From 01b37ad704d22508b37e416870ca9dd2eae693dd Mon Sep 17 00:00:00 2001 From: Anupam K Date: Tue, 25 Aug 2020 00:46:35 +0530 Subject: [PATCH 001/369] fix: multiselect recipients in Email Digest --- erpnext/patches.txt | 1 + .../v13_0/update_recipient_email_digest.py | 20 + .../doctype/email_digest/email_digest.js | 97 +- .../doctype/email_digest/email_digest.json | 1716 +++-------------- .../doctype/email_digest/email_digest.py | 32 +- .../email_digest_recipient/__init__.py | 0 .../email_digest_recipient.json | 33 + .../email_digest_recipient.py | 10 + 8 files changed, 384 insertions(+), 1525 deletions(-) create mode 100644 erpnext/patches/v13_0/update_recipient_email_digest.py create mode 100644 erpnext/setup/doctype/email_digest_recipient/__init__.py create mode 100644 erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.json create mode 100644 erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e17e949b3bc..049fef65eba 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -722,3 +722,4 @@ erpnext.patches.v13_0.stock_entry_enhancements erpnext.patches.v12_0.update_state_code_for_daman_and_diu erpnext.patches.v12_0.rename_lost_reason_detail erpnext.patches.v13_0.update_start_end_date_for_old_shift_assignment +erpnext.patches.v13_0.update_recipient_email_digest diff --git a/erpnext/patches/v13_0/update_recipient_email_digest.py b/erpnext/patches/v13_0/update_recipient_email_digest.py new file mode 100644 index 00000000000..583a04d03c2 --- /dev/null +++ b/erpnext/patches/v13_0/update_recipient_email_digest.py @@ -0,0 +1,20 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("setup", "doctype", "Email Digest") + email_digests = frappe.db.get_list('Email Digest', fields=['name', 'recipient_list']) + for email_digest in email_digests: + if email_digest.recipient_list: + for recipient in email_digest.recipient_list.split("\n"): + doc = frappe.get_doc({ + 'doctype': 'Email Digest Recipient', + 'parenttype': 'Email Digest', + 'parentfield': 'recipients', + 'parent': email_digest.name, + 'recipient': recipient + }) + doc.insert() diff --git a/erpnext/setup/doctype/email_digest/email_digest.js b/erpnext/setup/doctype/email_digest/email_digest.js index 1071ea20209..73fce454292 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.js +++ b/erpnext/setup/doctype/email_digest/email_digest.js @@ -1,78 +1,31 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -cur_frm.cscript.refresh = function(doc, dt, dn) { - doc = locals[dt][dn]; - cur_frm.add_custom_button(__('View Now'), function() { - frappe.call({ - method: 'erpnext.setup.doctype.email_digest.email_digest.get_digest_msg', - args: { - name: doc.name - }, - callback: function(r) { - var d = new frappe.ui.Dialog({ - title: __('Email Digest: ') + dn, - width: 800 - }); - $(d.body).html(r.message); - d.show(); - } - }); - }, "fa fa-eye-open", "btn-default"); - - if (!cur_frm.is_new()) { - cur_frm.add_custom_button(__('Send Now'), function() { - return cur_frm.call('send', null, (r) => { - frappe.show_alert(__('Message Sent')); +frappe.ui.form.on("Email Digest", { + refresh: function(frm) { + frm.add_custom_button(__('View Now'), function() { + frappe.call({ + method: 'erpnext.setup.doctype.email_digest.email_digest.get_digest_msg', + args: { + name: frm.doc.name + }, + callback: function(r) { + var d = new frappe.ui.Dialog({ + title: __('Email Digest: ') + frm.doc.name, + width: 800 + }); + $(d.body).html(r.message); + d.show(); + } }); }); + + if (!frm.is_new()) { + frm.add_custom_button(__('Send Now'), function() { + return frm.call('send', null, (r) => { + frappe.show_alert({message:__("Message Sent"), indicator:'green'}); + }); + }); + } } -}; - -cur_frm.cscript.addremove_recipients = function(doc, dt, dn) { - // Get user list - - return cur_frm.call('get_users', null, function(r) { - // Open a dialog and display checkboxes against email addresses - doc = locals[dt][dn]; - var d = new frappe.ui.Dialog({ - title: __('Add/Remove Recipients'), - width: 400 - }); - - $.each(r.user_list, function(i, v) { - var fullname = frappe.user.full_name(v.name); - if(fullname !== v.name) fullname = fullname + " <" + v.name + ">"; - - if(v.enabled==0) { - fullname = repl(" %(name)s (" + __("disabled user") + ")", {name: v.name}); - } - - $('
').appendTo(d.body); - }); - - // Display add recipients button - d.set_primary_action("Update", function() { - cur_frm.cscript.add_to_rec_list(doc, d.body, r.user_list.length); - }); - - cur_frm.rec_dialog = d; - d.show(); - }); -} - -cur_frm.cscript.add_to_rec_list = function(doc, dialog, length) { - // add checked users to list of recipients - var rec_list = []; - $(dialog).find('input:checked').each(function(i, input) { - rec_list.push($(input).attr('data-id')); - }); - - doc.recipient_list = rec_list.join('\n'); - cur_frm.rec_dialog.hide(); - cur_frm.save(); - cur_frm.refresh_fields(); -} +}); \ No newline at end of file diff --git a/erpnext/setup/doctype/email_digest/email_digest.json b/erpnext/setup/doctype/email_digest/email_digest.json index 125aca17c75..06c98e5ff94 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.json +++ b/erpnext/setup/doctype/email_digest/email_digest.json @@ -1,1482 +1,338 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-09-16 22:00:00", - "custom": 0, - "description": "Send regular summary reports via Email.", - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "editable_grid": 0, + "actions": [], + "autoname": "Prompt", + "creation": "2018-09-16 22:00:00", + "description": "Send regular summary reports via Email.", + "doctype": "DocType", + "document_type": "System", + "engine": "InnoDB", + "field_order": [ + "settings", + "column_break0", + "enabled", + "company", + "frequency", + "next_send", + "column_break1", + "recipients", + "accounts", + "accounts_module", + "income", + "expenses_booked", + "income_year_to_date", + "expense_year_to_date", + "column_break_16", + "bank_balance", + "credit_balance", + "invoiced_amount", + "payables", + "work_in_progress", + "sales_orders_to_bill", + "purchase_orders_to_bill", + "operation", + "column_break_21", + "sales_order", + "purchase_order", + "sales_orders_to_deliver", + "purchase_orders_to_receive", + "sales_invoice", + "purchase_invoice", + "column_break_operation", + "new_quotations", + "pending_quotations", + "issue", + "project", + "purchase_orders_items_overdue", + "other", + "tools", + "calendar_events", + "todo_list", + "notifications", + "column_break_32", + "add_quote" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "settings", - "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, - "label": "Email Digest Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "fieldname": "settings", + "fieldtype": "Section Break", + "label": "Email Digest Settings" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break0", - "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, - "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 - }, + "fieldname": "column_break0", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "enabled", - "fieldtype": "Check", - "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": "Enabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Enabled" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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": 1, - "in_standard_filter": 1, - "label": "For Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "For Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "frequency", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "How frequently?", - "length": 0, - "no_copy": 0, - "options": "Daily\nWeekly\nMonthly", - "permlevel": 0, - "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 - }, + "fieldname": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "How frequently?", + "options": "Daily\nWeekly\nMonthly", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.enabled", - "fieldname": "next_send", - "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": "Next email will be sent on:", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "depends_on": "eval:doc.enabled", + "fieldname": "next_send", + "fieldtype": "Data", + "label": "Next email will be sent on:", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "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, - "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 - }, + "fieldname": "column_break1", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Note: Email will not be sent to disabled users", - "fieldname": "recipient_list", - "fieldtype": "Code", - "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": "Recipients", - "length": 0, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "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 - }, + "fieldname": "accounts", + "fieldtype": "Section Break", + "label": "Accounts" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "addremove_recipients", - "fieldtype": "Button", - "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": "Add/Remove Recipients", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "fieldname": "accounts_module", + "fieldtype": "Column Break", + "hidden": 1, + "label": "Profit & Loss" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "accounts", - "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, - "label": "Accounts", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "default": "0", + "fieldname": "income", + "fieldtype": "Check", + "label": "New Income" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "accounts_module", - "fieldtype": "Column Break", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Profit & Loss", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "default": "0", + "fieldname": "expenses_booked", + "fieldtype": "Check", + "label": "New Expenses" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "income", - "fieldtype": "Check", - "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": "New Income", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "default": "0", + "fieldname": "income_year_to_date", + "fieldtype": "Check", + "label": "Annual Income" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "expenses_booked", - "fieldtype": "Check", - "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": "New Expenses", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "default": "0", + "fieldname": "expense_year_to_date", + "fieldtype": "Check", + "label": "Annual Expenses" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "income_year_to_date", - "fieldtype": "Check", - "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": "Annual Income", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "fieldname": "column_break_16", + "fieldtype": "Column Break", + "label": "Balance Sheet" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expense_year_to_date", - "fieldtype": "Check", - "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": "Annual Expenses", - "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 - }, + "default": "0", + "fieldname": "bank_balance", + "fieldtype": "Check", + "label": "Bank Balance" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "column_break_16", - "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, - "label": "Balance Sheet", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "default": "0", + "fieldname": "credit_balance", + "fieldtype": "Check", + "label": "Bank Credit Balance" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "bank_balance", - "fieldtype": "Check", - "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": "Bank Balance", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "default": "0", + "fieldname": "invoiced_amount", + "fieldtype": "Check", + "label": "Receivables" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "credit_balance", - "fieldtype": "Check", - "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": "Bank Credit Balance", - "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 - }, + "default": "0", + "fieldname": "payables", + "fieldtype": "Check", + "label": "Payables" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "invoiced_amount", - "fieldtype": "Check", - "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": "Receivables", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "fieldname": "work_in_progress", + "fieldtype": "Column Break", + "label": "Work in Progress" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "payables", - "fieldtype": "Check", - "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": "Payables", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "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 - }, + "default": "0", + "fieldname": "sales_orders_to_bill", + "fieldtype": "Check", + "label": "Sales Orders to Bill" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "work_in_progress", - "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, - "label": "Work in Progress", - "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 - }, + "default": "0", + "fieldname": "purchase_orders_to_bill", + "fieldtype": "Check", + "label": "Purchase Orders to Bill" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_orders_to_bill", - "fieldtype": "Check", - "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": "Sales Orders to Bill", - "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 - }, + "fieldname": "operation", + "fieldtype": "Section Break", + "label": "Operations" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "purchase_orders_to_bill", - "fieldtype": "Check", - "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": "Purchase Orders to Bill", - "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 - }, + "fieldname": "column_break_21", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "operation", - "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, - "label": "Operations", - "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 - }, + "default": "0", + "fieldname": "sales_order", + "fieldtype": "Check", + "label": "New Sales Orders" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_21", - "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 - }, + "default": "0", + "fieldname": "purchase_order", + "fieldtype": "Check", + "label": "New Purchase Orders" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_order", - "fieldtype": "Check", - "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": "New Sales Orders", - "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 - }, + "default": "0", + "fieldname": "sales_orders_to_deliver", + "fieldtype": "Check", + "label": "Sales Orders to Deliver" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "purchase_order", - "fieldtype": "Check", - "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": "New Purchase Orders", - "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 - }, + "default": "0", + "fieldname": "purchase_orders_to_receive", + "fieldtype": "Check", + "label": "Purchase Orders to Receive" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_orders_to_deliver", - "fieldtype": "Check", - "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": "Sales Orders to Deliver", - "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 - }, + "default": "0", + "fieldname": "sales_invoice", + "fieldtype": "Check", + "label": "New Sales Invoice" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "purchase_orders_to_receive", - "fieldtype": "Check", - "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": "Purchase Orders to Receive", - "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 - }, + "default": "0", + "fieldname": "purchase_invoice", + "fieldtype": "Check", + "label": "New Purchase Invoice" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_invoice", - "fieldtype": "Check", - "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": "New Sales Invoice", - "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 - }, + "fieldname": "column_break_operation", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "purchase_invoice", - "fieldtype": "Check", - "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": "New Purchase Invoice", - "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 - }, + "default": "0", + "fieldname": "new_quotations", + "fieldtype": "Check", + "label": "New Quotations" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_operation", - "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, - "label": "", - "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 - }, + "default": "0", + "fieldname": "pending_quotations", + "fieldtype": "Check", + "label": "Open Quotations" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "new_quotations", - "fieldtype": "Check", - "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": "New Quotations", - "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 - }, + "default": "0", + "fieldname": "issue", + "fieldtype": "Check", + "label": "Open Issues" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pending_quotations", - "fieldtype": "Check", - "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": "Open Quotations", - "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 - }, + "default": "0", + "fieldname": "project", + "fieldtype": "Check", + "label": "Open Projects" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "issue", - "fieldtype": "Check", - "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": "Open Issues", - "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 - }, + "default": "0", + "fieldname": "purchase_orders_items_overdue", + "fieldtype": "Check", + "label": "Purchase Orders Items Overdue" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "project", - "fieldtype": "Check", - "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": "Open Projects", - "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 - }, + "fieldname": "other", + "fieldtype": "Section Break", + "label": "Other" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "purchase_orders_items_overdue", - "fieldtype": "Check", - "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": "Purchase Orders Items Overdue", - "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 - }, + "fieldname": "tools", + "fieldtype": "Column Break", + "label": "Tools" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "other", - "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, - "label": "Other", - "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 - }, + "default": "0", + "fieldname": "calendar_events", + "fieldtype": "Check", + "label": "Upcoming Calendar Events" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tools", - "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, - "label": "Tools", - "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 - }, + "default": "0", + "fieldname": "todo_list", + "fieldtype": "Check", + "label": "Open To Do" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "calendar_events", - "fieldtype": "Check", - "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": "Upcoming Calendar Events", - "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 - }, + "default": "0", + "fieldname": "notifications", + "fieldtype": "Check", + "label": "Open Notifications" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "todo_list", - "fieldtype": "Check", - "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": "Open To Do", - "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 - }, + "fieldname": "column_break_32", + "fieldtype": "Column Break", + "label": " " + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "notifications", - "fieldtype": "Check", - "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": "Open Notifications", - "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 - }, + "default": "0", + "fieldname": "add_quote", + "fieldtype": "Check", + "label": "Add Quote" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_32", - "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, - "label": " ", - "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": "add_quote", - "fieldtype": "Check", - "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": "Add Quote", - "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 + "description": "Note: Email will not be sent to disabled users", + "fieldname": "recipients", + "fieldtype": "Table MultiSelect", + "label": "Recipients", + "options": "Email Digest Recipient", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-envelope", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-01-16 09:52:15.149908", - "modified_by": "Administrator", - "module": "Setup", - "name": "Email Digest", - "owner": "Administrator", + ], + "icon": "fa fa-envelope", + "idx": 1, + "index_web_pages_for_search": 1, + "links": [], + "modified": "2020-08-24 23:49:00.081695", + "modified_by": "Administrator", + "module": "Setup", + "name": "Email Digest", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 1, - "print": 0, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "permlevel": 1, + "read": 1, + "role": "System Manager" } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index b30bd7814b6..90f90913ba4 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -24,40 +24,26 @@ class EmailDigest(Document): self._accounts = {} self.currency = frappe.db.get_value('Company', self.company, "default_currency") - def get_users(self): - """get list of users""" - user_list = frappe.db.sql(""" - select name, enabled from tabUser - where name not in ({}) - and user_type != "Website User" - order by enabled desc, name asc""".format(", ".join(["%s"]*len(STANDARD_USERS))), STANDARD_USERS, as_dict=1) - - if self.recipient_list: - recipient_list = self.recipient_list.split("\n") - else: - recipient_list = [] - for p in user_list: - p["checked"] = p["name"] in recipient_list and 1 or 0 - - frappe.response['user_list'] = user_list - def send(self): # send email only to enabled users valid_users = [p[0] for p in frappe.db.sql("""select name from `tabUser` where enabled=1""")] - recipients = list(filter(lambda r: r in valid_users, - self.recipient_list.split("\n"))) + recipients = frappe.db.get_list('Email Digest Recipient', + filters={ + 'parent': self.name + }, + fields=['recipient']) original_user = frappe.session.user if recipients: - for user_id in recipients: - frappe.set_user(user_id) - frappe.set_user_lang(user_id) + for user in recipients: + frappe.set_user(user.recipient) + frappe.set_user_lang(user.recipient) msg_for_this_recipient = self.get_msg_html() if msg_for_this_recipient: frappe.sendmail( - recipients=user_id, + recipients=user.recipient, subject=_("{0} Digest").format(self.frequency), message=msg_for_this_recipient, reference_doctype = self.doctype, diff --git a/erpnext/setup/doctype/email_digest_recipient/__init__.py b/erpnext/setup/doctype/email_digest_recipient/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.json b/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.json new file mode 100644 index 00000000000..8b2a6dcf49f --- /dev/null +++ b/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.json @@ -0,0 +1,33 @@ +{ + "actions": [], + "creation": "2020-06-08 12:19:40.428949", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "recipient" + ], + "fields": [ + { + "fieldname": "recipient", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Recipient", + "options": "User", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-08-24 23:10:23.217572", + "modified_by": "Administrator", + "module": "Setup", + "name": "Email Digest Recipient", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.py b/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.py new file mode 100644 index 00000000000..968c51c345b --- /dev/null +++ b/erpnext/setup/doctype/email_digest_recipient/email_digest_recipient.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class EmailDigestRecipient(Document): + pass From ddfc1042396a0c71a0cab74954451dd73a1604cf Mon Sep 17 00:00:00 2001 From: Anupam K Date: Mon, 31 Aug 2020 16:54:16 +0530 Subject: [PATCH 002/369] fix: patch --- erpnext/patches/v13_0/update_recipient_email_digest.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v13_0/update_recipient_email_digest.py b/erpnext/patches/v13_0/update_recipient_email_digest.py index 583a04d03c2..d9aa03f0fd6 100644 --- a/erpnext/patches/v13_0/update_recipient_email_digest.py +++ b/erpnext/patches/v13_0/update_recipient_email_digest.py @@ -6,6 +6,7 @@ import frappe def execute(): frappe.reload_doc("setup", "doctype", "Email Digest") + frappe.reload_doc("setup", "doctype", "Email Digest Recipient") email_digests = frappe.db.get_list('Email Digest', fields=['name', 'recipient_list']) for email_digest in email_digests: if email_digest.recipient_list: From d4e2a3324f6fea0dd32b472acb2a9e3e94c9e081 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 13 Oct 2020 12:26:25 +0530 Subject: [PATCH 003/369] fix: review fixes --- .../doctype/email_digest/email_digest.py | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/erpnext/setup/doctype/email_digest/email_digest.py b/erpnext/setup/doctype/email_digest/email_digest.py index 90f90913ba4..6c66d030289 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -28,27 +28,22 @@ class EmailDigest(Document): # send email only to enabled users valid_users = [p[0] for p in frappe.db.sql("""select name from `tabUser` where enabled=1""")] - recipients = frappe.db.get_list('Email Digest Recipient', - filters={ - 'parent': self.name - }, - fields=['recipient']) - original_user = frappe.session.user - if recipients: - for user in recipients: - frappe.set_user(user.recipient) - frappe.set_user_lang(user.recipient) - msg_for_this_recipient = self.get_msg_html() - if msg_for_this_recipient: - frappe.sendmail( - recipients=user.recipient, - subject=_("{0} Digest").format(self.frequency), - message=msg_for_this_recipient, - reference_doctype = self.doctype, - reference_name = self.name, - unsubscribe_message = _("Unsubscribe from this Email Digest")) + if self.recipients: + for user in self.recipients: + if user.recipient in valid_users: + frappe.set_user(user.recipient) + frappe.set_user_lang(user.recipient) + msg_for_this_recipient = self.get_msg_html() + if msg_for_this_recipient: + frappe.sendmail( + recipients=user.recipient, + subject=_("{0} Digest").format(self.frequency), + message=msg_for_this_recipient, + reference_doctype = self.doctype, + reference_name = self.name, + unsubscribe_message = _("Unsubscribe from this Email Digest")) frappe.set_user(original_user) frappe.set_user_lang(original_user) From c1ba35b25b056053c886b732cd59ab6c08ba1484 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 20 Jun 2021 12:42:06 +0530 Subject: [PATCH 004/369] feat: Organizational Chart --- erpnext/hr/doctype/employee/employee.py | 5 +- .../hr/page/organizational_chart/__init__.py | 0 .../page/organizational_chart/node_card.html | 27 ++ .../organizational_chart.js | 409 ++++++++++++++++++ .../organizational_chart.json | 26 ++ .../organizational_chart.py | 49 +++ erpnext/public/build.json | 3 +- erpnext/public/scss/organizational_chart.scss | 209 +++++++++ 8 files changed, 725 insertions(+), 3 deletions(-) create mode 100644 erpnext/hr/page/organizational_chart/__init__.py create mode 100644 erpnext/hr/page/organizational_chart/node_card.html create mode 100644 erpnext/hr/page/organizational_chart/organizational_chart.js create mode 100644 erpnext/hr/page/organizational_chart/organizational_chart.json create mode 100644 erpnext/hr/page/organizational_chart/organizational_chart.py create mode 100644 erpnext/public/scss/organizational_chart.scss diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index ed7d5884347..7917e3abf59 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -476,13 +476,14 @@ def get_employee_emails(employee_list): return employee_emails @frappe.whitelist() -def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False): +def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False, fields=None): filters = [['status', '!=', 'Left']] if company and company != 'All Companies': filters.append(['company', '=', company]) - fields = ['name as value', 'employee_name as title'] + if not fields: + fields = ['name as value', 'employee_name as title'] if is_root: parent = '' diff --git a/erpnext/hr/page/organizational_chart/__init__.py b/erpnext/hr/page/organizational_chart/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/page/organizational_chart/node_card.html b/erpnext/hr/page/organizational_chart/node_card.html new file mode 100644 index 00000000000..057c45ee864 --- /dev/null +++ b/erpnext/hr/page/organizational_chart/node_card.html @@ -0,0 +1,27 @@ +
+
+
+ + + +
+
+
+ {{ name }} +
+ {{ frappe.utils.icon("edit", "xs") }} + {{ __("Edit") }} +
+
+
+
{{ title }}
+ + {% if connections == 1 %} +
· {{ connections }} {{ __("Connection") }}
+ {% else %} +
· {{ connections }} {{ __("Connections") }}
+ {% endif %} +
+
+
+
\ No newline at end of file diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js new file mode 100644 index 00000000000..04bd9422bd9 --- /dev/null +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -0,0 +1,409 @@ +frappe.pages['organizational-chart'].on_page_load = function(wrapper) { + frappe.ui.make_app_page({ + parent: wrapper, + title: __('Organizational Chart'), + single_column: true + }); + + let organizational_chart = new OrganizationalChart(wrapper); + $(wrapper).bind('show', ()=> { + organizational_chart.show(); + }); +}; + +class OrganizationalChart { + + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = wrapper.page; + + this.page.main.css({ + 'min-height': '300px', + 'max-height': '600px', + 'overflow': 'auto', + 'position': 'relative' + }); + this.page.main.addClass('frappe-card'); + + this.nodes = {}; + this.setup_node_class(); + } + + setup_node_class() { + let me = this; + this.Node = class { + constructor({ + id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line + }) { + // to setup values passed via constructor + $.extend(this, arguments[0]); + + this.expanded = 0; + + me.nodes[this.id] = this; + me.make_node_element(this); + me.setup_node_click_action(this); + } + } + } + + make_node_element(node) { + let node_card = frappe.render_template('node_card', { + id: node.id, + name: node.name, + title: node.title, + image: node.image, + parent: node.parent_id, + connections: node.connections + }); + + node.parent.append(node_card); + node.$link = $(`#${node.id}`); + } + + show() { + frappe.breadcrumbs.add('HR'); + + let me = this; + let company = this.page.add_field({ + fieldtype: 'Link', + options: 'Company', + fieldname: 'company', + placeholder: __('Select Company'), + default: frappe.defaults.get_default('company'), + only_select: true, + reqd: 1, + change: () => { + me.company = undefined; + + if (company.get_value() && me.company != company.get_value()) { + me.company = company.get_value(); + + // svg for connectors + me.make_svg_markers() + + if (me.$hierarchy) + me.$hierarchy.remove(); + + // setup hierarchy + me.$hierarchy = $( + `
    +
  • +
`); + + me.page.main.append(me.$hierarchy); + me.render_root_node(); + } + } + }); + + company.refresh(); + $(`[data-fieldname="company"]`).trigger('change'); + } + + make_svg_markers() { + $('#arrows').remove(); + + this.page.main.prepend(` + + + + + + + + + + + + + + + + + + + `); + } + + render_root_node() { + this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; + + let me = this; + + frappe.call({ + method: me.method, + args: { + company: me.company + }, + callback: function(r) { + if (r.message.length) { + let data = r.message[0]; + + let root_node = new me.Node({ + id: data.name, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: true, + connections: data.connections, + is_root: true, + }); + + me.expand_node(root_node); + } + } + }) + } + + expand_node(node) { + let is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id; + this.set_selected_node(node); + this.show_active_path(node); + this.collapse_previous_level_nodes(node); + + // since the previous node collapses, all connections to that node need to be rebuilt + // if a sibling node is clicked, connections don't need to be rebuilt + if (!is_sibling) { + // rebuild outgoing connections + this.refresh_connectors(node.parent_id); + + // rebuild incoming connections + let grandparent = $(`#${node.parent_id}`).attr('data-parent'); + this.refresh_connectors(grandparent) + } + + if (node.expandable && !node.expanded) { + return this.load_children(node); + } + } + + collapse_node() { + if (this.selected_node.expandable) { + this.selected_node.$children.hide(); + $(`path[data-parent="${this.selected_node.id}"]`).hide(); + this.selected_node.expanded = false; + } + } + + show_active_path(node) { + // mark node parent on active path + $(`#${node.parent_id}`).addClass('active-path'); + } + + load_children(node) { + frappe.run_serially([ + () => this.get_child_nodes(node.id), + (child_nodes) => this.render_child_nodes(node, child_nodes) + ]); + } + + get_child_nodes(node_id) { + let me = this; + return new Promise(resolve => { + frappe.call({ + method: this.method, + args: { + parent: node_id, + company: me.company + }, + callback: (r) => { + resolve(r.message); + } + }); + }); + } + + render_child_nodes(node, child_nodes) { + const last_level = this.$hierarchy.find('.level:last').index(); + const current_level = $(`#${node.id}`).parent().parent().parent().index(); + + if (last_level === current_level) { + this.$hierarchy.append(` +
  • + `); + } + + if (!node.$children) { + node.$children = $('
      ') + .hide() + .appendTo(this.$hierarchy.find('.level:last')); + + node.$children.empty(); + + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_node(node, data); + + setTimeout(() => { + this.add_connector(node.id, data.name); + }, 250); + }); + } + } + + node.$children.show(); + $(`path[data-parent="${node.id}"]`).show(); + node.expanded = true; + } + + add_node(node, data) { + var $li = $('
    • '); + + return new this.Node({ + id: data.name, + parent: $li.appendTo(node.$children), + parent_id: node.id, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: data.expandable, + connections: data.connections, + children: undefined + }); + } + + add_connector(parent_id, child_id) { + let parent_node = document.querySelector(`#${parent_id}`); + let child_node = document.querySelector(`#${child_id}`); + + // variable for the namespace + const svgns = 'http://www.w3.org/2000/svg'; + let path = document.createElementNS(svgns, 'path'); + + // we need to connect right side of the parent to the left side of the child node + let pos_parent_right = { + x: parent_node.offsetLeft + parent_node.offsetWidth, + y: parent_node.offsetTop + parent_node.offsetHeight / 2 + }; + let pos_child_left = { + x: child_node.offsetLeft - 5, + y: child_node.offsetTop + child_node.offsetHeight / 2 + }; + + let connector = + "M" + + (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + + "C" + + (pos_parent_right.x + 100) + "," + (pos_parent_right.y) + " " + + (pos_child_left.x - 100) + "," + (pos_child_left.y) + " " + + (pos_child_left.x) + "," + (pos_child_left.y); + + path.setAttribute("d", connector); + path.setAttribute("data-parent", parent_id); + path.setAttribute("data-child", child_id); + + if ($(`#${parent_id}`).hasClass('active')) { + path.setAttribute("class", "active-connector"); + path.setAttribute("marker-start", "url(#arrowstart-active)"); + path.setAttribute("marker-end", "url(#arrowhead-active)"); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + path.setAttribute("class", "collapsed-connector"); + path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); + path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); + } + + $('#connectors').append(path); + } + + set_selected_node(node) { + // remove .active class from the current node + $('.active').removeClass('active'); + + // add active class to the newly selected node + this.selected_node = node; + node.$link.addClass('active'); + } + + collapse_previous_level_nodes(node) { + let node_parent = $(`#${node.parent_id}`); + + let previous_level_nodes = node_parent.parent().parent().children('li'); + if (node_parent.parent().hasClass('root-level')) { + previous_level_nodes = node_parent.parent().children('li'); + } + + let node_card = undefined; + + previous_level_nodes.each(function() { + node_card = $(this).find('.node-card'); + + if (!node_card.hasClass('active-path')) { + node_card.addClass('collapsed'); + } + }); + } + + refresh_connectors(node_parent) { + if (!node_parent) return; + + $(`path[data-parent="${node_parent}"]`).remove(); + + frappe.run_serially([ + () => this.get_child_nodes(node_parent), + (child_nodes) => { + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_connector(node_parent, data.name); + }); + } + } + ]); + } + + setup_node_click_action(node) { + let me = this; + let node_element = $(`#${node.id}`); + + node_element.click(function() { + let is_sibling = me.selected_node.parent_id === node.parent_id; + + if (is_sibling) { + me.collapse_node(); + } else if (node_element.is(':visible') + && (node_element.hasClass('collapsed') || node_element.hasClass('active-path'))) { + me.remove_levels_after_node(node); + me.remove_orphaned_connectors(); + } + + me.expand_node(node); + }); + } + + remove_levels_after_node(node) { + let level = $(`#${node.id}`).parent().parent().parent(); + + if ($(`#${node.id}`).parent().hasClass('root-level')) { + level = $(`#${node.id}`).parent(); + } + + level = $('.hierarchy > li:eq('+ level.index() + ')'); + level.nextAll('li').remove(); + + let nodes = level.find('.node-card'); + let node_object = undefined; + + $.each(nodes, (_i, element) => { + node_object = this.nodes[element.id]; + node_object.expanded = 0; + node_object.$children = undefined; + }); + + nodes.removeClass('collapsed active-path'); + } + + remove_orphaned_connectors() { + let paths = $('#connectors > path'); + $.each(paths, (_i, path) => { + let parent = $(path).data('parent'); + let child = $(path).data('child'); + + if ($(parent).length || $(child).length) + return; + + $(path).remove(); + }) + } +} diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.json b/erpnext/hr/page/organizational_chart/organizational_chart.json new file mode 100644 index 00000000000..d802781320a --- /dev/null +++ b/erpnext/hr/page/organizational_chart/organizational_chart.json @@ -0,0 +1,26 @@ +{ + "content": null, + "creation": "2021-05-25 10:53:10.107241", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2021-05-25 10:53:18.201931", + "modified_by": "Administrator", + "module": "HR", + "name": "organizational-chart", + "owner": "Administrator", + "page_name": "Organizational Chart", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "Organizational Chart" +} \ No newline at end of file diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py new file mode 100644 index 00000000000..be2964530b8 --- /dev/null +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -0,0 +1,49 @@ +from __future__ import unicode_literals +import frappe + +@frappe.whitelist() +def get_children(parent=None, company=None, is_root=False, is_tree=False, fields=None): + + filters = [['status', '!=', 'Left']] + if company and company != 'All Companies': + filters.append(['company', '=', company]) + + if not fields: + fields = ['employee_name', 'name', 'reports_to', 'image', 'designation'] + + if is_root: + parent = '' + if parent and company and parent!=company: + filters.append(['reports_to', '=', parent]) + else: + filters.append(['reports_to', '=', '']) + + employees = frappe.get_list('Employee', fields=fields, + filters=filters, order_by='name') + + for employee in employees: + is_expandable = frappe.get_all('Employee', filters=[ + ['reports_to', '=', employee.get('name')] + ]) + employee.connections = get_connections(employee.name) + employee.expandable = 1 if is_expandable else 0 + + return employees + + +def get_connections(employee): + num_connections = 0 + + connections = frappe.get_list('Employee', filters=[ + ['reports_to', '=', employee] + ]) + num_connections += len(connections) + + while connections: + for entry in connections: + connections = frappe.get_list('Employee', filters=[ + ['reports_to', '=', entry.name] + ]) + num_connections += len(connections) + + return num_connections \ No newline at end of file diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 7a3cb838a99..d3ebcdf7e7b 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -3,7 +3,8 @@ "public/less/erpnext.less", "public/less/hub.less", "public/scss/call_popup.scss", - "public/scss/point-of-sale.scss" + "public/scss/point-of-sale.scss", + "public/scss/organizational_chart.scss" ], "css/marketplace.css": [ "public/less/hub.less" diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/organizational_chart.scss new file mode 100644 index 00000000000..62f6ddcb6e0 --- /dev/null +++ b/erpnext/public/scss/organizational_chart.scss @@ -0,0 +1,209 @@ +.node-card { + background: white; + stroke: 1px solid var(--gray-200); + box-shadow: var(--shadow-base); + border-radius: 0.5rem; + padding: 0.75rem; + margin-left: 3rem; + width: 18rem; + + .btn-edit-node { + display: none; + } + + .edit-chart-node { + display: none; + } + + .node-edit-icon { + display: none; + } +} + +.node-image { + width: 3.0rem; + height: 3.0rem; +} + +.node-name { + font-size: 1rem; + line-height: 1.72; +} + +.node-title { + font-size: 0.75rem; + line-height: 1.35; +} + +.node-connections { + font-size: 0.75rem; + line-height: 1.35; +} + +.node-card.active { + background: var(--blue-50); + border: 1px solid var(--blue-500); + box-shadow: var(--shadow-md); + border-radius: 0.5rem; + padding: 0.75rem; + width: 18rem; + height: 5rem; + + .btn-edit-node { + display: flex; + background: var(--blue-100); + color: var(--blue-500); + padding: .25rem .5rem; + font-size: .75rem; + justify-content: center; + box-shadow: var(--shadow-sm); + } + + .edit-chart-node { + display: block; + } + + .node-edit-icon { + display: block; + } + + .edit-chart-node { + margin-right: 0.25rem; + } + + .node-edit-icon > .icon{ + stroke: var(--blue-500); + } + + .node-name { + align-items: center; + justify-content: space-between; + margin-bottom: 2px; + } +} + +.node-card.active-path { + background: var(--blue-100); + border: 1px solid var(--blue-300); + box-shadow: var(--shadow-sm); + border-radius: 0.5rem; + padding: 0.75rem; + width: 15rem; + height: 3.0rem; + + .btn-edit-node { + display: none !important; + } + + .edit-chart-node { + display: none; + } + + .node-edit-icon { + display: none; + } + + .node-info { + display: none; + } + + .node-title { + display: none; + } + + .node-connections { + display: none; + } + + .node-name { + font-size: 0.85rem; + line-height: 1.35; + } + + .node-image { + width: 1.5rem; + height: 1.5rem; + } + + .node-meta { + align-items: baseline; + } +} + +.node-card.collapsed { + background: white; + stroke: 1px solid var(--gray-200); + box-shadow: var(--shadow-sm); + border-radius: 0.5rem; + padding: 0.75rem; + width: 15rem; + height: 3.0rem; + + .btn-edit-node { + display: none !important; + } + + .edit-chart-node { + display: none; + } + + .node-edit-icon { + display: none; + } + + .node-info { + display: none; + } + + .node-title { + display: none; + } + + .node-connections { + display: none; + } + + .node-name { + font-size: 0.85rem; + line-height: 1.35; + } + + .node-image { + width: 1.5rem; + height: 1.5rem; + } + + .node-meta { + align-items: baseline; + } +} + +// horizontal hierarchy tree view +.hierarchy { + display: flex; + padding-top: 30px; +} + +.hierarchy li { + list-style-type: none; +} + +.child-node { + margin: 0px 0px 16px 0px; +} + +.level { + margin-right: 8px; +} + +#arrows { + position: absolute; +} + +.active-connector { + stroke: var(--blue-500); +} + +.collapsed-connector { + stroke: var(--blue-300); +} From cce19db826be54b401a9514cb6517810c98e6f7c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 21 Jun 2021 21:55:50 +0530 Subject: [PATCH 005/369] feat: org chart mobile interactions --- .../page/organizational_chart/node_card.html | 4 +- .../organizational_chart.js | 329 +++++++++++++++++- .../organizational_chart.py | 6 +- erpnext/public/scss/organizational_chart.scss | 70 ++++ 4 files changed, 404 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/node_card.html b/erpnext/hr/page/organizational_chart/node_card.html index 057c45ee864..e42e54f690c 100644 --- a/erpnext/hr/page/organizational_chart/node_card.html +++ b/erpnext/hr/page/organizational_chart/node_card.html @@ -17,9 +17,9 @@
      {{ title }}
      {% if connections == 1 %} -
      · {{ connections }} {{ __("Connection") }}
      +
      · {{ connections }}
      {% else %} -
      · {{ connections }} {{ __("Connections") }}
      +
      · {{ connections }}
      {% endif %} diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index 04bd9422bd9..5739a112de9 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -5,13 +5,20 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { single_column: true }); - let organizational_chart = new OrganizationalChart(wrapper); + // let organizational_chart = undefined; + // if (frappe.is_mobile()) { + // organizational_chart = new OrgChartMobile(wrapper); + // } else { + // organizational_chart = new OrgChart(wrapper); + // } + + let organizational_chart = new OrgChartMobile(wrapper); $(wrapper).bind('show', ()=> { organizational_chart.show(); }); }; -class OrganizationalChart { +class OrgChart { constructor(wrapper) { this.wrapper = $(wrapper); @@ -407,3 +414,321 @@ class OrganizationalChart { }) } } + + +class OrgChartMobile { + + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = wrapper.page; + + this.page.main.css({ + 'min-height': '300px', + 'max-height': '600px', + 'overflow': 'auto', + 'position': 'relative' + }); + this.page.main.addClass('frappe-card'); + + this.nodes = {}; + this.setup_node_class(); + } + + setup_node_class() { + let me = this; + this.Node = class { + constructor({ + id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line + }) { + // to setup values passed via constructor + $.extend(this, arguments[0]); + + this.expanded = 0; + + me.nodes[this.id] = this; + me.make_node_element(this); + me.setup_node_click_action(this); + } + } + } + + make_node_element(node) { + let node_card = frappe.render_template('node_card', { + id: node.id, + name: node.name, + title: node.title, + image: node.image, + parent: node.parent_id, + connections: node.connections, + is_mobile: 1 + }); + + node.parent.append(node_card); + node.$link = $(`#${node.id}`); + node.$link.addClass('mobile-node'); + } + + show() { + frappe.breadcrumbs.add('HR'); + + let me = this; + let company = this.page.add_field({ + fieldtype: 'Link', + options: 'Company', + fieldname: 'company', + placeholder: __('Select Company'), + default: frappe.defaults.get_default('company'), + only_select: true, + reqd: 1, + change: () => { + me.company = undefined; + + if (company.get_value() && me.company != company.get_value()) { + me.company = company.get_value(); + + if (me.$hierarchy) + me.$hierarchy.remove(); + + // setup hierarchy + me.$hierarchy = $( + `
        +
      • +
      `); + + me.page.main.append(me.$hierarchy); + me.render_root_node(); + } + } + }); + + company.refresh(); + $(`[data-fieldname="company"]`).trigger('change'); + } + + render_root_node() { + this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; + + let me = this; + + frappe.call({ + method: me.method, + args: { + company: me.company + }, + callback: function(r) { + if (r.message.length) { + let data = r.message[0]; + + let root_node = new me.Node({ + id: data.name, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: true, + connections: data.connections, + is_root: true, + }); + + me.expand_node(root_node); + } + } + }) + } + + expand_node(node) { + this.set_selected_node(node); + this.show_active_path(node); + + if (node.expandable && !node.expanded) { + return this.load_children(node); + } + } + + collapse_node() { + let node = this.selected_node; + if (node.expandable) { + node.$children.hide(); + node.expanded = false; + + // add a collapsed level to show the collapsed parent + // and a button beside it to move to that level + let node_parent = node.$link.parent(); + node_parent.prepend( + `
      ` + ); + + node_parent + .find('.collapsed-level') + .append(node.$link); + + frappe.run_serially([ + () => this.get_child_nodes(node.parent_id, node.id), + (child_nodes) => this.get_node_group(child_nodes, node.id), + (node_group) => { + node_parent.find('.collapsed-level') + .append(node_group); + } + ]); + } + } + + show_active_path(node) { + // mark node parent on active path + $(`#${node.parent_id}`).addClass('active-path'); + } + + load_children(node) { + frappe.run_serially([ + () => this.get_child_nodes(node.id), + (child_nodes) => this.render_child_nodes(node, child_nodes) + ]); + } + + get_child_nodes(node_id, exclude_node=null) { + let me = this; + return new Promise(resolve => { + frappe.call({ + method: this.method, + args: { + parent: node_id, + company: me.company, + exclude_node: exclude_node + }, + callback: (r) => { + resolve(r.message); + } + }); + }); + } + + render_child_nodes(node, child_nodes) { + if (!node.$children) { + node.$children = $('
        ') + .hide() + .appendTo(node.$link.parent()); + + node.$children.empty(); + + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_node(node, data); + $(`#${data.name}`).addClass('active-child'); + }); + } + } + + node.$children.show(); + node.expanded = true; + } + + add_node(node, data) { + var $li = $('
      • '); + + return new this.Node({ + id: data.name, + parent: $li.appendTo(node.$children), + parent_id: node.id, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: data.expandable, + connections: data.connections, + children: undefined + }); + } + + set_selected_node(node) { + // remove .active class from the current node + $('.active').removeClass('active'); + + // add active class to the newly selected node + this.selected_node = node; + node.$link.addClass('active'); + } + + setup_node_click_action(node) { + let me = this; + let node_element = $(`#${node.id}`); + let node_object = null; + + node_element.click(function() { + if (node_element.is(':visible') && node_element.hasClass('active-path')) { + me.remove_levels_after_node(node); + } else { + me.add_node_to_hierarchy(node, true); + me.collapse_node(); + } + + me.expand_node(node); + }); + } + + add_node_to_hierarchy(node) { + this.$hierarchy.append(` +
      • +
        +
        +
      • + `); + + node.$link.appendTo(this.$hierarchy.find('.level:last')); + } + + get_node_group(nodes, sibling) { + let limit = 2; + const display_nodes = nodes.slice(0, limit); + const extra_nodes = nodes.slice(limit); + + let html = display_nodes.map(node => + this.get_avatar(node) + ).join(''); + + if (extra_nodes.length === 1) { + let node = extra_nodes[0]; + html += this.get_avatar(node); + } else if (extra_nodes.length > 1) { + html = ` + ${html} + +
        + +${extra_nodes.length} +
        +
        + `; + } + + const $node_group = + $(`
        +
        + ${html} +
        +
        `); + + return $node_group; + } + + get_avatar(node) { + return ` + + ` + } + + remove_levels_after_node(node) { + let level = $(`#${node.id}`).parent().parent(); + + level = $('.hierarchy-mobile > li:eq('+ (level.index()) + ')'); + level.nextAll('li').remove(); + + let current_node = level.find(`#${node.id}`); + let node_object = this.nodes[node.id]; + + current_node.removeClass('active-child active-path'); + node_object.expanded = 0; + node_object.$children = undefined; + + level.empty().append(current_node); + } +} \ No newline at end of file diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index be2964530b8..ae91a919b2b 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe @frappe.whitelist() -def get_children(parent=None, company=None, is_root=False, is_tree=False, fields=None): +def get_children(parent=None, company=None, exclude_node=None, is_root=False, is_tree=False, fields=None): filters = [['status', '!=', 'Left']] if company and company != 'All Companies': @@ -13,6 +13,10 @@ def get_children(parent=None, company=None, is_root=False, is_tree=False, fields if is_root: parent = '' + + if exclude_node: + filters.append(['name', '!=', exclude_node]) + if parent and company and parent!=company: filters.append(['reports_to', '=', parent]) else: diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/organizational_chart.scss index 62f6ddcb6e0..02446be11a1 100644 --- a/erpnext/public/scss/organizational_chart.scss +++ b/erpnext/public/scss/organizational_chart.scss @@ -6,6 +6,7 @@ padding: 0.75rem; margin-left: 3rem; width: 18rem; + overflow: hidden; .btn-edit-node { display: none; @@ -207,3 +208,72 @@ .collapsed-connector { stroke: var(--blue-300); } + +// mobile + +.hierarchy-mobile { + display: flex; + flex-direction: column; + align-items: center; + padding-top: 30px; + padding-left: 0px; +} + +.hierarchy-mobile li { + list-style-type: none; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.mobile-node { + margin-left: 0; +} + +.mobile-node.active-path { + width: 12.25rem; +} + +.active-child { + width: 15.5rem; +} + +.mobile-node .node-connections { + max-width: 80px; +} + +.hierarchy-mobile .node-children { + margin-top: 16px; +} + +// node group + +.collapsed-level { + margin-bottom: 16px; +} + +.node-group { + background: white; + border: 1px solid var(--gray-300); + box-shadow: var(--shadow-sm); + border-radius: 0.5rem; + padding: 0.75rem; + margin-left: 12px; + width: 5rem; + height: 3rem; + overflow: hidden; +} + +.node-group .avatar-group { + margin-left: 0px; +} + +.node-group .avatar-extra-count { + background-color: var(--blue-100); + color: var(--blue-500); +} + +.node-group .avatar-frame { + width: 1.5rem; + height: 1.5rem; +} \ No newline at end of file From 6e3a7b4a751d6fa53b8659695753cf17518f1579 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 11:12:47 +0530 Subject: [PATCH 006/369] feat(mobile): sibling node group expansion and rendering --- .../organizational_chart.js | 64 +++++++++++++++---- erpnext/public/scss/organizational_chart.scss | 9 ++- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index 5739a112de9..edaf46162e2 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -565,11 +565,12 @@ class OrgChartMobile { frappe.run_serially([ () => this.get_child_nodes(node.parent_id, node.id), - (child_nodes) => this.get_node_group(child_nodes, node.id), + (child_nodes) => this.get_node_group(child_nodes, node.parent_id), (node_group) => { node_parent.find('.collapsed-level') .append(node_group); - } + }, + () => this.setup_node_group_action() ]); } } @@ -651,7 +652,6 @@ class OrgChartMobile { setup_node_click_action(node) { let me = this; let node_element = $(`#${node.id}`); - let node_object = null; node_element.click(function() { if (node_element.is(':visible') && node_element.hasClass('active-path')) { @@ -665,6 +665,15 @@ class OrgChartMobile { }); } + setup_node_group_action() { + let me = this; + + $('.node-group').on('click', function() { + let parent = $(this).attr('data-parent'); + me.expand_sibling_group_node(parent); + }); + } + add_node_to_hierarchy(node) { this.$hierarchy.append(`
      • @@ -676,7 +685,7 @@ class OrgChartMobile { node.$link.appendTo(this.$hierarchy.find('.level:last')); } - get_node_group(nodes, sibling) { + get_node_group(nodes, parent, collapsed=true) { let limit = 2; const display_nodes = nodes.slice(0, limit); const extra_nodes = nodes.slice(limit); @@ -700,14 +709,23 @@ class OrgChartMobile { `; } - const $node_group = - $(`
        -
        - ${html} -
        -
        `); + if (html) { + const $node_group = + $(`
        +
        + ${html} +
        +
        `); - return $node_group; + if (collapsed) + $node_group.addClass('collapsed'); + else + $node_group.addClass('mb-4'); + + return $node_group; + } + + return null; } get_avatar(node) { @@ -716,6 +734,30 @@ class OrgChartMobile { ` } + expand_sibling_group_node(parent) { + let node_object = this.nodes[parent]; + let node = node_object.$link; + node.removeClass('active-child active-path'); + node_object.expanded = 0; + node_object.$children = undefined; + + // show parent's siblings and expand parent node + frappe.run_serially([ + () => this.get_child_nodes(node_object.parent_id, node_object.id), + (child_nodes) => this.get_node_group(child_nodes, node_object.parent_id, false), + (node_group) => { + this.$hierarchy.empty().append(node_group) }, + () => this.setup_node_group_action(), + () => { + this.$hierarchy.append(` +
      • + `); + this.$hierarchy.append(node); + this.expand_node(node_object); + } + ]); + } + remove_levels_after_node(node) { let level = $(`#${node.id}`).parent().parent(); diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/organizational_chart.scss index 02446be11a1..b6d50a0470a 100644 --- a/erpnext/public/scss/organizational_chart.scss +++ b/erpnext/public/scss/organizational_chart.scss @@ -258,10 +258,10 @@ box-shadow: var(--shadow-sm); border-radius: 0.5rem; padding: 0.75rem; - margin-left: 12px; - width: 5rem; + width: 18rem; height: 3rem; overflow: hidden; + align-items: center; } .node-group .avatar-group { @@ -276,4 +276,9 @@ .node-group .avatar-frame { width: 1.5rem; height: 1.5rem; +} + +.node-group.collapsed { + width: 5rem; + margin-left: 12px; } \ No newline at end of file From 25e8723032aec69e7347ece7e714331cc2a35815 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 15:06:09 +0530 Subject: [PATCH 007/369] fix: expanded node group interactions and visibility --- .../organizational_chart.js | 23 +++++++++++++++---- erpnext/public/scss/organizational_chart.scss | 9 +++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index edaf46162e2..f693cf6ba67 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -486,6 +486,13 @@ class OrgChartMobile { if (company.get_value() && me.company != company.get_value()) { me.company = company.get_value(); + if (me.$sibling_group) + me.$sibling_group.remove(); + + // setup sibling group wrapper + me.$sibling_group = $(`
        `); + me.page.main.append(me.$sibling_group); + if (me.$hierarchy) me.$hierarchy.remove(); @@ -541,6 +548,12 @@ class OrgChartMobile { this.set_selected_node(node); this.show_active_path(node); + if (this.$sibling_group) { + const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent'); + if (node.parent_id !== sibling_parent) + this.$sibling_group.empty(); + } + if (node.expandable && !node.expanded) { return this.load_children(node); } @@ -719,8 +732,6 @@ class OrgChartMobile { if (collapsed) $node_group.addClass('collapsed'); - else - $node_group.addClass('mb-4'); return $node_group; } @@ -746,13 +757,15 @@ class OrgChartMobile { () => this.get_child_nodes(node_object.parent_id, node_object.id), (child_nodes) => this.get_node_group(child_nodes, node_object.parent_id, false), (node_group) => { - this.$hierarchy.empty().append(node_group) }, + if (node_group) + this.$sibling_group.empty().append(node_group); + }, () => this.setup_node_group_action(), () => { - this.$hierarchy.append(` + this.$hierarchy.empty().append(`
      • `); - this.$hierarchy.append(node); + this.$hierarchy.find('.level').append(node); this.expand_node(node_object); } ]); diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/organizational_chart.scss index b6d50a0470a..6012c015733 100644 --- a/erpnext/public/scss/organizational_chart.scss +++ b/erpnext/public/scss/organizational_chart.scss @@ -215,7 +215,7 @@ display: flex; flex-direction: column; align-items: center; - padding-top: 30px; + padding-top: 10px; padding-left: 0px; } @@ -250,6 +250,7 @@ .collapsed-level { margin-bottom: 16px; + width: 18rem; } .node-group { @@ -281,4 +282,10 @@ .node-group.collapsed { width: 5rem; margin-left: 12px; +} + +.sibling-group { + display: flex; + flex-direction: column; + align-items: center; } \ No newline at end of file From f5314293c6215841e87dab7462fc44d3c777804d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 17:48:44 +0530 Subject: [PATCH 008/369] feat: connectors for mobile node cards --- .../organizational_chart.js | 138 ++++++++++++++++++ erpnext/public/scss/organizational_chart.scss | 1 + 2 files changed, 139 insertions(+) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index f693cf6ba67..15334bd4ca5 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -486,6 +486,9 @@ class OrgChartMobile { if (company.get_value() && me.company != company.get_value()) { me.company = company.get_value(); + // svg for connectors + me.make_svg_markers() + if (me.$sibling_group) me.$sibling_group.remove(); @@ -512,6 +515,31 @@ class OrgChartMobile { $(`[data-fieldname="company"]`).trigger('change'); } + make_svg_markers() { + $('#arrows').remove(); + + this.page.main.prepend(` + + + + + + + + + + + + + + + + + + + `); + } + render_root_node() { this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; @@ -554,6 +582,14 @@ class OrgChartMobile { this.$sibling_group.empty(); } + // since the previous/parent node collapses, all connections to that node need to be rebuilt + // rebuild outgoing connections of parent + this.refresh_connectors(node.parent_id, node.id); + + // rebuild incoming connections of parent + let grandparent = $(`#${node.parent_id}`).attr('data-parent'); + this.refresh_connectors(grandparent, node.parent_id); + if (node.expandable && !node.expanded) { return this.load_children(node); } @@ -629,6 +665,10 @@ class OrgChartMobile { $.each(child_nodes, (_i, data) => { this.add_node(node, data); $(`#${data.name}`).addClass('active-child'); + + setTimeout(() => { + this.add_connector(node.id, data.name); + }, 250); }); } } @@ -653,6 +693,83 @@ class OrgChartMobile { }); } + add_connector(parent_id, child_id) { + let parent_node = document.querySelector(`#${parent_id}`); + let child_node = document.querySelector(`#${child_id}`); + + // variable for the namespace + const svgns = 'http://www.w3.org/2000/svg'; + let path = document.createElementNS(svgns, 'path'); + + let connector = undefined; + + if ($(`#${parent_id}`).hasClass('active')) { + connector = this.get_connector_for_active_node(parent_node, child_node); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + connector = this.get_connector_for_collapsed_node(parent_node, child_node); + } + + path.setAttribute("d", connector); + this.set_path_attributes(path, parent_id, child_id); + + $('#connectors').append(path); + } + + get_connector_for_active_node(parent_node, child_node) { + // we need to connect the bottom left of the parent to the left side of the child node + let pos_parent_bottom = { + x: parent_node.offsetLeft + 20, + y: parent_node.offsetTop + parent_node.offsetHeight + }; + let pos_child_left = { + x: child_node.offsetLeft - 5, + y: child_node.offsetTop + child_node.offsetHeight / 2 + }; + + let connector = + "M" + + (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + + "L" + + (pos_parent_bottom.x) + "," + (pos_child_left.y) + " " + + "L" + + (pos_child_left.x) + "," + (pos_child_left.y); + + return connector; + } + + get_connector_for_collapsed_node(parent_node, child_node) { + // we need to connect the bottom left of the parent to the top left of the child node + let pos_parent_bottom = { + x: parent_node.offsetLeft + 20, + y: parent_node.offsetTop + parent_node.offsetHeight + }; + let pos_child_top = { + x: child_node.offsetLeft + 20, + y: child_node.offsetTop + }; + + let connector = + "M" + + (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + + "L" + + (pos_child_top.x) + "," + (pos_child_top.y); + + return connector; + } + + set_path_attributes(path, parent_id, child_id) { + path.setAttribute("data-parent", parent_id); + path.setAttribute("data-child", child_id); + + if ($(`#${parent_id}`).hasClass('active')) { + path.setAttribute("class", "active-connector"); + path.setAttribute("marker-start", "url(#arrowstart-active)"); + path.setAttribute("marker-end", "url(#arrowhead-active)"); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + path.setAttribute("class", "collapsed-connector"); + } + } + set_selected_node(node) { // remove .active class from the current node $('.active').removeClass('active'); @@ -669,6 +786,7 @@ class OrgChartMobile { node_element.click(function() { if (node_element.is(':visible') && node_element.hasClass('active-path')) { me.remove_levels_after_node(node); + me.remove_orphaned_connectors(); } else { me.add_node_to_hierarchy(node, true); me.collapse_node(); @@ -786,4 +904,24 @@ class OrgChartMobile { level.empty().append(current_node); } + + remove_orphaned_connectors() { + let paths = $('#connectors > path'); + $.each(paths, (_i, path) => { + let parent = $(path).data('parent'); + let child = $(path).data('child'); + + if ($(parent).length || $(child).length) + return; + + $(path).remove(); + }) + } + + refresh_connectors(node_parent, node_id) { + if (!node_parent) return; + + $(`path[data-parent="${node_parent}"]`).remove(); + this.add_connector(node_parent, node_id); + } } \ No newline at end of file diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/organizational_chart.scss index 6012c015733..16b8792432e 100644 --- a/erpnext/public/scss/organizational_chart.scss +++ b/erpnext/public/scss/organizational_chart.scss @@ -199,6 +199,7 @@ #arrows { position: absolute; + overflow: visible; } .active-connector { From b7c61ff6510b4c6afcda7f315388c61068c13765 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 18:21:42 +0530 Subject: [PATCH 009/369] fix: don't refresh connections for same node - remove all connectors while expanding a group node --- .../organizational_chart/organizational_chart.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index 15334bd4ca5..efb367ad44c 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -573,6 +573,7 @@ class OrgChartMobile { } expand_node(node) { + const is_same_node = (this.selected_node && this.selected_node.id === node.id); this.set_selected_node(node); this.show_active_path(node); @@ -582,13 +583,15 @@ class OrgChartMobile { this.$sibling_group.empty(); } - // since the previous/parent node collapses, all connections to that node need to be rebuilt - // rebuild outgoing connections of parent - this.refresh_connectors(node.parent_id, node.id); + if (!is_same_node) { + // since the previous/parent node collapses, all connections to that node need to be rebuilt + // rebuild outgoing connections of parent + this.refresh_connectors(node.parent_id, node.id); - // rebuild incoming connections of parent - let grandparent = $(`#${node.parent_id}`).attr('data-parent'); - this.refresh_connectors(grandparent, node.parent_id); + // rebuild incoming connections of parent + let grandparent = $(`#${node.parent_id}`).attr('data-parent'); + this.refresh_connectors(grandparent, node.parent_id); + } if (node.expandable && !node.expanded) { return this.load_children(node); @@ -884,6 +887,7 @@ class OrgChartMobile {
      • `); this.$hierarchy.find('.level').append(node); + $(`#connectors`).empty(); this.expand_node(node_object); } ]); From bcc998e8c236766f4a8eadd5f0080050dfc7f160 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 19:15:08 +0530 Subject: [PATCH 010/369] chore: create separate files for Desktop and Mobile view and bundle assets --- .../organizational_chart.js | 930 +----------------- erpnext/public/build.json | 9 +- .../hierarchy_chart_desktop.js | 396 ++++++++ .../hierarchy_chart/hierarchy_chart_mobile.js | 513 ++++++++++ .../js/templates}/node_card.html | 0 ...tional_chart.scss => hierarchy_chart.scss} | 0 6 files changed, 925 insertions(+), 923 deletions(-) create mode 100644 erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js create mode 100644 erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js rename erpnext/{hr/page/organizational_chart => public/js/templates}/node_card.html (100%) rename erpnext/public/scss/{organizational_chart.scss => hierarchy_chart.scss} (100%) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index efb367ad44c..0fe724c78e4 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -5,927 +5,15 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { single_column: true }); - // let organizational_chart = undefined; - // if (frappe.is_mobile()) { - // organizational_chart = new OrgChartMobile(wrapper); - // } else { - // organizational_chart = new OrgChart(wrapper); - // } - - let organizational_chart = new OrgChartMobile(wrapper); - $(wrapper).bind('show', ()=> { - organizational_chart.show(); - }); -}; - -class OrgChart { - - constructor(wrapper) { - this.wrapper = $(wrapper); - this.page = wrapper.page; - - this.page.main.css({ - 'min-height': '300px', - 'max-height': '600px', - 'overflow': 'auto', - 'position': 'relative' - }); - this.page.main.addClass('frappe-card'); - - this.nodes = {}; - this.setup_node_class(); - } - - setup_node_class() { - let me = this; - this.Node = class { - constructor({ - id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line - }) { - // to setup values passed via constructor - $.extend(this, arguments[0]); - - this.expanded = 0; - - me.nodes[this.id] = this; - me.make_node_element(this); - me.setup_node_click_action(this); - } - } - } - - make_node_element(node) { - let node_card = frappe.render_template('node_card', { - id: node.id, - name: node.name, - title: node.title, - image: node.image, - parent: node.parent_id, - connections: node.connections - }); - - node.parent.append(node_card); - node.$link = $(`#${node.id}`); - } - - show() { - frappe.breadcrumbs.add('HR'); - - let me = this; - let company = this.page.add_field({ - fieldtype: 'Link', - options: 'Company', - fieldname: 'company', - placeholder: __('Select Company'), - default: frappe.defaults.get_default('company'), - only_select: true, - reqd: 1, - change: () => { - me.company = undefined; - - if (company.get_value() && me.company != company.get_value()) { - me.company = company.get_value(); - - // svg for connectors - me.make_svg_markers() - - if (me.$hierarchy) - me.$hierarchy.remove(); - - // setup hierarchy - me.$hierarchy = $( - `
          -
        • -
        `); - - me.page.main.append(me.$hierarchy); - me.render_root_node(); - } - } - }); - - company.refresh(); - $(`[data-fieldname="company"]`).trigger('change'); - } - - make_svg_markers() { - $('#arrows').remove(); - - this.page.main.prepend(` - - - - - - - - - - - - - - - - - - - `); - } - - render_root_node() { - this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; - - let me = this; - - frappe.call({ - method: me.method, - args: { - company: me.company - }, - callback: function(r) { - if (r.message.length) { - let data = r.message[0]; - - let root_node = new me.Node({ - id: data.name, - parent: me.$hierarchy.find('.root-level'), - parent_id: undefined, - image: data.image, - name: data.employee_name, - title: data.designation, - expandable: true, - connections: data.connections, - is_root: true, - }); - - me.expand_node(root_node); - } - } - }) - } - - expand_node(node) { - let is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id; - this.set_selected_node(node); - this.show_active_path(node); - this.collapse_previous_level_nodes(node); - - // since the previous node collapses, all connections to that node need to be rebuilt - // if a sibling node is clicked, connections don't need to be rebuilt - if (!is_sibling) { - // rebuild outgoing connections - this.refresh_connectors(node.parent_id); - - // rebuild incoming connections - let grandparent = $(`#${node.parent_id}`).attr('data-parent'); - this.refresh_connectors(grandparent) - } - - if (node.expandable && !node.expanded) { - return this.load_children(node); - } - } - - collapse_node() { - if (this.selected_node.expandable) { - this.selected_node.$children.hide(); - $(`path[data-parent="${this.selected_node.id}"]`).hide(); - this.selected_node.expanded = false; - } - } - - show_active_path(node) { - // mark node parent on active path - $(`#${node.parent_id}`).addClass('active-path'); - } - - load_children(node) { - frappe.run_serially([ - () => this.get_child_nodes(node.id), - (child_nodes) => this.render_child_nodes(node, child_nodes) - ]); - } - - get_child_nodes(node_id) { - let me = this; - return new Promise(resolve => { - frappe.call({ - method: this.method, - args: { - parent: node_id, - company: me.company - }, - callback: (r) => { - resolve(r.message); - } - }); - }); - } - - render_child_nodes(node, child_nodes) { - const last_level = this.$hierarchy.find('.level:last').index(); - const current_level = $(`#${node.id}`).parent().parent().parent().index(); - - if (last_level === current_level) { - this.$hierarchy.append(` -
      • - `); - } - - if (!node.$children) { - node.$children = $('
          ') - .hide() - .appendTo(this.$hierarchy.find('.level:last')); - - node.$children.empty(); - - if (child_nodes) { - $.each(child_nodes, (_i, data) => { - this.add_node(node, data); - - setTimeout(() => { - this.add_connector(node.id, data.name); - }, 250); - }); - } - } - - node.$children.show(); - $(`path[data-parent="${node.id}"]`).show(); - node.expanded = true; - } - - add_node(node, data) { - var $li = $('
        • '); - - return new this.Node({ - id: data.name, - parent: $li.appendTo(node.$children), - parent_id: node.id, - image: data.image, - name: data.employee_name, - title: data.designation, - expandable: data.expandable, - connections: data.connections, - children: undefined - }); - } - - add_connector(parent_id, child_id) { - let parent_node = document.querySelector(`#${parent_id}`); - let child_node = document.querySelector(`#${child_id}`); - - // variable for the namespace - const svgns = 'http://www.w3.org/2000/svg'; - let path = document.createElementNS(svgns, 'path'); - - // we need to connect right side of the parent to the left side of the child node - let pos_parent_right = { - x: parent_node.offsetLeft + parent_node.offsetWidth, - y: parent_node.offsetTop + parent_node.offsetHeight / 2 - }; - let pos_child_left = { - x: child_node.offsetLeft - 5, - y: child_node.offsetTop + child_node.offsetHeight / 2 - }; - - let connector = - "M" + - (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + - "C" + - (pos_parent_right.x + 100) + "," + (pos_parent_right.y) + " " + - (pos_child_left.x - 100) + "," + (pos_child_left.y) + " " + - (pos_child_left.x) + "," + (pos_child_left.y); - - path.setAttribute("d", connector); - path.setAttribute("data-parent", parent_id); - path.setAttribute("data-child", child_id); - - if ($(`#${parent_id}`).hasClass('active')) { - path.setAttribute("class", "active-connector"); - path.setAttribute("marker-start", "url(#arrowstart-active)"); - path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if ($(`#${parent_id}`).hasClass('active-path')) { - path.setAttribute("class", "collapsed-connector"); - path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); - path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); - } - - $('#connectors').append(path); - } - - set_selected_node(node) { - // remove .active class from the current node - $('.active').removeClass('active'); - - // add active class to the newly selected node - this.selected_node = node; - node.$link.addClass('active'); - } - - collapse_previous_level_nodes(node) { - let node_parent = $(`#${node.parent_id}`); - - let previous_level_nodes = node_parent.parent().parent().children('li'); - if (node_parent.parent().hasClass('root-level')) { - previous_level_nodes = node_parent.parent().children('li'); - } - - let node_card = undefined; - - previous_level_nodes.each(function() { - node_card = $(this).find('.node-card'); - - if (!node_card.hasClass('active-path')) { - node_card.addClass('collapsed'); - } - }); - } - - refresh_connectors(node_parent) { - if (!node_parent) return; - - $(`path[data-parent="${node_parent}"]`).remove(); - - frappe.run_serially([ - () => this.get_child_nodes(node_parent), - (child_nodes) => { - if (child_nodes) { - $.each(child_nodes, (_i, data) => { - this.add_connector(node_parent, data.name); - }); - } - } - ]); - } - - setup_node_click_action(node) { - let me = this; - let node_element = $(`#${node.id}`); - - node_element.click(function() { - let is_sibling = me.selected_node.parent_id === node.parent_id; - - if (is_sibling) { - me.collapse_node(); - } else if (node_element.is(':visible') - && (node_element.hasClass('collapsed') || node_element.hasClass('active-path'))) { - me.remove_levels_after_node(node); - me.remove_orphaned_connectors(); - } - - me.expand_node(node); - }); - } - - remove_levels_after_node(node) { - let level = $(`#${node.id}`).parent().parent().parent(); - - if ($(`#${node.id}`).parent().hasClass('root-level')) { - level = $(`#${node.id}`).parent(); - } - - level = $('.hierarchy > li:eq('+ level.index() + ')'); - level.nextAll('li').remove(); - - let nodes = level.find('.node-card'); - let node_object = undefined; - - $.each(nodes, (_i, element) => { - node_object = this.nodes[element.id]; - node_object.expanded = 0; - node_object.$children = undefined; - }); - - nodes.removeClass('collapsed active-path'); - } - - remove_orphaned_connectors() { - let paths = $('#connectors > path'); - $.each(paths, (_i, path) => { - let parent = $(path).data('parent'); - let child = $(path).data('child'); - - if ($(parent).length || $(child).length) - return; - - $(path).remove(); - }) - } -} - - -class OrgChartMobile { - - constructor(wrapper) { - this.wrapper = $(wrapper); - this.page = wrapper.page; - - this.page.main.css({ - 'min-height': '300px', - 'max-height': '600px', - 'overflow': 'auto', - 'position': 'relative' - }); - this.page.main.addClass('frappe-card'); - - this.nodes = {}; - this.setup_node_class(); - } - - setup_node_class() { - let me = this; - this.Node = class { - constructor({ - id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line - }) { - // to setup values passed via constructor - $.extend(this, arguments[0]); - - this.expanded = 0; - - me.nodes[this.id] = this; - me.make_node_element(this); - me.setup_node_click_action(this); - } - } - } - - make_node_element(node) { - let node_card = frappe.render_template('node_card', { - id: node.id, - name: node.name, - title: node.title, - image: node.image, - parent: node.parent_id, - connections: node.connections, - is_mobile: 1 - }); - - node.parent.append(node_card); - node.$link = $(`#${node.id}`); - node.$link.addClass('mobile-node'); - } - - show() { - frappe.breadcrumbs.add('HR'); - - let me = this; - let company = this.page.add_field({ - fieldtype: 'Link', - options: 'Company', - fieldname: 'company', - placeholder: __('Select Company'), - default: frappe.defaults.get_default('company'), - only_select: true, - reqd: 1, - change: () => { - me.company = undefined; - - if (company.get_value() && me.company != company.get_value()) { - me.company = company.get_value(); - - // svg for connectors - me.make_svg_markers() - - if (me.$sibling_group) - me.$sibling_group.remove(); - - // setup sibling group wrapper - me.$sibling_group = $(`
          `); - me.page.main.append(me.$sibling_group); - - if (me.$hierarchy) - me.$hierarchy.remove(); - - // setup hierarchy - me.$hierarchy = $( - `
            -
          • -
          `); - - me.page.main.append(me.$hierarchy); - me.render_root_node(); - } - } - }); - - company.refresh(); - $(`[data-fieldname="company"]`).trigger('change'); - } - - make_svg_markers() { - $('#arrows').remove(); - - this.page.main.prepend(` - - - - - - - - - - - - - - - - - - - `); - } - - render_root_node() { - this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; - - let me = this; - - frappe.call({ - method: me.method, - args: { - company: me.company - }, - callback: function(r) { - if (r.message.length) { - let data = r.message[0]; - - let root_node = new me.Node({ - id: data.name, - parent: me.$hierarchy.find('.root-level'), - parent_id: undefined, - image: data.image, - name: data.employee_name, - title: data.designation, - expandable: true, - connections: data.connections, - is_root: true, - }); - - me.expand_node(root_node); - } - } - }) - } - - expand_node(node) { - const is_same_node = (this.selected_node && this.selected_node.id === node.id); - this.set_selected_node(node); - this.show_active_path(node); - - if (this.$sibling_group) { - const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent'); - if (node.parent_id !== sibling_parent) - this.$sibling_group.empty(); - } - - if (!is_same_node) { - // since the previous/parent node collapses, all connections to that node need to be rebuilt - // rebuild outgoing connections of parent - this.refresh_connectors(node.parent_id, node.id); - - // rebuild incoming connections of parent - let grandparent = $(`#${node.parent_id}`).attr('data-parent'); - this.refresh_connectors(grandparent, node.parent_id); - } - - if (node.expandable && !node.expanded) { - return this.load_children(node); - } - } - - collapse_node() { - let node = this.selected_node; - if (node.expandable) { - node.$children.hide(); - node.expanded = false; - - // add a collapsed level to show the collapsed parent - // and a button beside it to move to that level - let node_parent = node.$link.parent(); - node_parent.prepend( - `
          ` - ); - - node_parent - .find('.collapsed-level') - .append(node.$link); - - frappe.run_serially([ - () => this.get_child_nodes(node.parent_id, node.id), - (child_nodes) => this.get_node_group(child_nodes, node.parent_id), - (node_group) => { - node_parent.find('.collapsed-level') - .append(node_group); - }, - () => this.setup_node_group_action() - ]); - } - } - - show_active_path(node) { - // mark node parent on active path - $(`#${node.parent_id}`).addClass('active-path'); - } - - load_children(node) { - frappe.run_serially([ - () => this.get_child_nodes(node.id), - (child_nodes) => this.render_child_nodes(node, child_nodes) - ]); - } - - get_child_nodes(node_id, exclude_node=null) { - let me = this; - return new Promise(resolve => { - frappe.call({ - method: this.method, - args: { - parent: node_id, - company: me.company, - exclude_node: exclude_node - }, - callback: (r) => { - resolve(r.message); - } - }); - }); - } - - render_child_nodes(node, child_nodes) { - if (!node.$children) { - node.$children = $('
            ') - .hide() - .appendTo(node.$link.parent()); - - node.$children.empty(); - - if (child_nodes) { - $.each(child_nodes, (_i, data) => { - this.add_node(node, data); - $(`#${data.name}`).addClass('active-child'); - - setTimeout(() => { - this.add_connector(node.id, data.name); - }, 250); - }); - } - } - - node.$children.show(); - node.expanded = true; - } - - add_node(node, data) { - var $li = $('
          • '); - - return new this.Node({ - id: data.name, - parent: $li.appendTo(node.$children), - parent_id: node.id, - image: data.image, - name: data.employee_name, - title: data.designation, - expandable: data.expandable, - connections: data.connections, - children: undefined - }); - } - - add_connector(parent_id, child_id) { - let parent_node = document.querySelector(`#${parent_id}`); - let child_node = document.querySelector(`#${child_id}`); - - // variable for the namespace - const svgns = 'http://www.w3.org/2000/svg'; - let path = document.createElementNS(svgns, 'path'); - - let connector = undefined; - - if ($(`#${parent_id}`).hasClass('active')) { - connector = this.get_connector_for_active_node(parent_node, child_node); - } else if ($(`#${parent_id}`).hasClass('active-path')) { - connector = this.get_connector_for_collapsed_node(parent_node, child_node); - } - - path.setAttribute("d", connector); - this.set_path_attributes(path, parent_id, child_id); - - $('#connectors').append(path); - } - - get_connector_for_active_node(parent_node, child_node) { - // we need to connect the bottom left of the parent to the left side of the child node - let pos_parent_bottom = { - x: parent_node.offsetLeft + 20, - y: parent_node.offsetTop + parent_node.offsetHeight - }; - let pos_child_left = { - x: child_node.offsetLeft - 5, - y: child_node.offsetTop + child_node.offsetHeight / 2 - }; - - let connector = - "M" + - (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + - "L" + - (pos_parent_bottom.x) + "," + (pos_child_left.y) + " " + - "L" + - (pos_child_left.x) + "," + (pos_child_left.y); - - return connector; - } - - get_connector_for_collapsed_node(parent_node, child_node) { - // we need to connect the bottom left of the parent to the top left of the child node - let pos_parent_bottom = { - x: parent_node.offsetLeft + 20, - y: parent_node.offsetTop + parent_node.offsetHeight - }; - let pos_child_top = { - x: child_node.offsetLeft + 20, - y: child_node.offsetTop - }; - - let connector = - "M" + - (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + - "L" + - (pos_child_top.x) + "," + (pos_child_top.y); - - return connector; - } - - set_path_attributes(path, parent_id, child_id) { - path.setAttribute("data-parent", parent_id); - path.setAttribute("data-child", child_id); - - if ($(`#${parent_id}`).hasClass('active')) { - path.setAttribute("class", "active-connector"); - path.setAttribute("marker-start", "url(#arrowstart-active)"); - path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if ($(`#${parent_id}`).hasClass('active-path')) { - path.setAttribute("class", "collapsed-connector"); - } - } - - set_selected_node(node) { - // remove .active class from the current node - $('.active').removeClass('active'); - - // add active class to the newly selected node - this.selected_node = node; - node.$link.addClass('active'); - } - - setup_node_click_action(node) { - let me = this; - let node_element = $(`#${node.id}`); - - node_element.click(function() { - if (node_element.is(':visible') && node_element.hasClass('active-path')) { - me.remove_levels_after_node(node); - me.remove_orphaned_connectors(); + $(wrapper).bind('show', () => { + frappe.require('/assets/js/hierarchy-chart.min.js', () => { + let organizational_chart = undefined; + if (frappe.is_mobile()) { + organizational_chart = new erpnext.HierarchyChartMobile(wrapper); } else { - me.add_node_to_hierarchy(node, true); - me.collapse_node(); + organizational_chart = new erpnext.HierarchyChart(wrapper); } - - me.expand_node(node); + organizational_chart.show(); }); - } - - setup_node_group_action() { - let me = this; - - $('.node-group').on('click', function() { - let parent = $(this).attr('data-parent'); - me.expand_sibling_group_node(parent); - }); - } - - add_node_to_hierarchy(node) { - this.$hierarchy.append(` -
          • -
            -
            -
          • - `); - - node.$link.appendTo(this.$hierarchy.find('.level:last')); - } - - get_node_group(nodes, parent, collapsed=true) { - let limit = 2; - const display_nodes = nodes.slice(0, limit); - const extra_nodes = nodes.slice(limit); - - let html = display_nodes.map(node => - this.get_avatar(node) - ).join(''); - - if (extra_nodes.length === 1) { - let node = extra_nodes[0]; - html += this.get_avatar(node); - } else if (extra_nodes.length > 1) { - html = ` - ${html} - -
            - +${extra_nodes.length} -
            -
            - `; - } - - if (html) { - const $node_group = - $(`
            -
            - ${html} -
            -
            `); - - if (collapsed) - $node_group.addClass('collapsed'); - - return $node_group; - } - - return null; - } - - get_avatar(node) { - return ` - - ` - } - - expand_sibling_group_node(parent) { - let node_object = this.nodes[parent]; - let node = node_object.$link; - node.removeClass('active-child active-path'); - node_object.expanded = 0; - node_object.$children = undefined; - - // show parent's siblings and expand parent node - frappe.run_serially([ - () => this.get_child_nodes(node_object.parent_id, node_object.id), - (child_nodes) => this.get_node_group(child_nodes, node_object.parent_id, false), - (node_group) => { - if (node_group) - this.$sibling_group.empty().append(node_group); - }, - () => this.setup_node_group_action(), - () => { - this.$hierarchy.empty().append(` -
          • - `); - this.$hierarchy.find('.level').append(node); - $(`#connectors`).empty(); - this.expand_node(node_object); - } - ]); - } - - remove_levels_after_node(node) { - let level = $(`#${node.id}`).parent().parent(); - - level = $('.hierarchy-mobile > li:eq('+ (level.index()) + ')'); - level.nextAll('li').remove(); - - let current_node = level.find(`#${node.id}`); - let node_object = this.nodes[node.id]; - - current_node.removeClass('active-child active-path'); - node_object.expanded = 0; - node_object.$children = undefined; - - level.empty().append(current_node); - } - - remove_orphaned_connectors() { - let paths = $('#connectors > path'); - $.each(paths, (_i, path) => { - let parent = $(path).data('parent'); - let child = $(path).data('child'); - - if ($(parent).length || $(child).length) - return; - - $(path).remove(); - }) - } - - refresh_connectors(node_parent, node_id) { - if (!node_parent) return; - - $(`path[data-parent="${node_parent}"]`).remove(); - this.add_connector(node_parent, node_id); - } -} \ No newline at end of file + }); +}; \ No newline at end of file diff --git a/erpnext/public/build.json b/erpnext/public/build.json index d3ebcdf7e7b..3c60e3ee500 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -4,7 +4,7 @@ "public/less/hub.less", "public/scss/call_popup.scss", "public/scss/point-of-sale.scss", - "public/scss/organizational_chart.scss" + "public/scss/hierarchy_chart.scss" ], "css/marketplace.css": [ "public/less/hub.less" @@ -44,7 +44,8 @@ "public/js/call_popup/call_popup.js", "public/js/utils/dimension_tree_filter.js", "public/js/telephony.js", - "public/js/templates/call_link.html" + "public/js/templates/call_link.html", + "public/js/templates/node_card.html" ], "js/item-dashboard.min.js": [ "stock/dashboard/item_dashboard.html", @@ -67,5 +68,9 @@ "public/js/bank_reconciliation_tool/data_table_manager.js", "public/js/bank_reconciliation_tool/number_card.js", "public/js/bank_reconciliation_tool/dialog_manager.js" + ], + "js/hierarchy-chart.min.js": [ + "public/js/hierarchy_chart/hierarchy_chart_desktop.js", + "public/js/hierarchy_chart/hierarchy_chart_mobile.js" ] } diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js new file mode 100644 index 00000000000..fd84d4ea5ca --- /dev/null +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -0,0 +1,396 @@ +erpnext.HierarchyChart = class { + + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = wrapper.page; + + this.page.main.css({ + 'min-height': '300px', + 'max-height': '600px', + 'overflow': 'auto', + 'position': 'relative' + }); + this.page.main.addClass('frappe-card'); + + this.nodes = {}; + this.setup_node_class(); + } + + setup_node_class() { + let me = this; + this.Node = class { + constructor({ + id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line + }) { + // to setup values passed via constructor + $.extend(this, arguments[0]); + + this.expanded = 0; + + me.nodes[this.id] = this; + me.make_node_element(this); + me.setup_node_click_action(this); + } + } + } + + make_node_element(node) { + let node_card = frappe.render_template('node_card', { + id: node.id, + name: node.name, + title: node.title, + image: node.image, + parent: node.parent_id, + connections: node.connections + }); + + node.parent.append(node_card); + node.$link = $(`#${node.id}`); + } + + show() { + frappe.breadcrumbs.add('HR'); + + let me = this; + let company = this.page.add_field({ + fieldtype: 'Link', + options: 'Company', + fieldname: 'company', + placeholder: __('Select Company'), + default: frappe.defaults.get_default('company'), + only_select: true, + reqd: 1, + change: () => { + me.company = undefined; + + if (company.get_value() && me.company != company.get_value()) { + me.company = company.get_value(); + + // svg for connectors + me.make_svg_markers() + + if (me.$hierarchy) + me.$hierarchy.remove(); + + // setup hierarchy + me.$hierarchy = $( + `
              +
            • +
            `); + + me.page.main.append(me.$hierarchy); + me.render_root_node(); + } + } + }); + + company.refresh(); + $(`[data-fieldname="company"]`).trigger('change'); + } + + make_svg_markers() { + $('#arrows').remove(); + + this.page.main.prepend(` + + + + + + + + + + + + + + + + + + + `); + } + + render_root_node() { + this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; + + let me = this; + + frappe.call({ + method: me.method, + args: { + company: me.company + }, + callback: function(r) { + if (r.message.length) { + let data = r.message[0]; + + let root_node = new me.Node({ + id: data.name, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: true, + connections: data.connections, + is_root: true, + }); + + me.expand_node(root_node); + } + } + }) + } + + expand_node(node) { + let is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id; + this.set_selected_node(node); + this.show_active_path(node); + this.collapse_previous_level_nodes(node); + + // since the previous node collapses, all connections to that node need to be rebuilt + // if a sibling node is clicked, connections don't need to be rebuilt + if (!is_sibling) { + // rebuild outgoing connections + this.refresh_connectors(node.parent_id); + + // rebuild incoming connections + let grandparent = $(`#${node.parent_id}`).attr('data-parent'); + this.refresh_connectors(grandparent) + } + + if (node.expandable && !node.expanded) { + return this.load_children(node); + } + } + + collapse_node() { + if (this.selected_node.expandable) { + this.selected_node.$children.hide(); + $(`path[data-parent="${this.selected_node.id}"]`).hide(); + this.selected_node.expanded = false; + } + } + + show_active_path(node) { + // mark node parent on active path + $(`#${node.parent_id}`).addClass('active-path'); + } + + load_children(node) { + frappe.run_serially([ + () => this.get_child_nodes(node.id), + (child_nodes) => this.render_child_nodes(node, child_nodes) + ]); + } + + get_child_nodes(node_id) { + let me = this; + return new Promise(resolve => { + frappe.call({ + method: this.method, + args: { + parent: node_id, + company: me.company + }, + callback: (r) => { + resolve(r.message); + } + }); + }); + } + + render_child_nodes(node, child_nodes) { + const last_level = this.$hierarchy.find('.level:last').index(); + const current_level = $(`#${node.id}`).parent().parent().parent().index(); + + if (last_level === current_level) { + this.$hierarchy.append(` +
          • + `); + } + + if (!node.$children) { + node.$children = $('
              ') + .hide() + .appendTo(this.$hierarchy.find('.level:last')); + + node.$children.empty(); + + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_node(node, data); + + setTimeout(() => { + this.add_connector(node.id, data.name); + }, 250); + }); + } + } + + node.$children.show(); + $(`path[data-parent="${node.id}"]`).show(); + node.expanded = true; + } + + add_node(node, data) { + var $li = $('
            • '); + + return new this.Node({ + id: data.name, + parent: $li.appendTo(node.$children), + parent_id: node.id, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: data.expandable, + connections: data.connections, + children: undefined + }); + } + + add_connector(parent_id, child_id) { + let parent_node = document.querySelector(`#${parent_id}`); + let child_node = document.querySelector(`#${child_id}`); + + // variable for the namespace + const svgns = 'http://www.w3.org/2000/svg'; + let path = document.createElementNS(svgns, 'path'); + + // we need to connect right side of the parent to the left side of the child node + let pos_parent_right = { + x: parent_node.offsetLeft + parent_node.offsetWidth, + y: parent_node.offsetTop + parent_node.offsetHeight / 2 + }; + let pos_child_left = { + x: child_node.offsetLeft - 5, + y: child_node.offsetTop + child_node.offsetHeight / 2 + }; + + let connector = + "M" + + (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + + "C" + + (pos_parent_right.x + 100) + "," + (pos_parent_right.y) + " " + + (pos_child_left.x - 100) + "," + (pos_child_left.y) + " " + + (pos_child_left.x) + "," + (pos_child_left.y); + + path.setAttribute("d", connector); + path.setAttribute("data-parent", parent_id); + path.setAttribute("data-child", child_id); + + if ($(`#${parent_id}`).hasClass('active')) { + path.setAttribute("class", "active-connector"); + path.setAttribute("marker-start", "url(#arrowstart-active)"); + path.setAttribute("marker-end", "url(#arrowhead-active)"); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + path.setAttribute("class", "collapsed-connector"); + path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); + path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); + } + + $('#connectors').append(path); + } + + set_selected_node(node) { + // remove .active class from the current node + $('.active').removeClass('active'); + + // add active class to the newly selected node + this.selected_node = node; + node.$link.addClass('active'); + } + + collapse_previous_level_nodes(node) { + let node_parent = $(`#${node.parent_id}`); + + let previous_level_nodes = node_parent.parent().parent().children('li'); + if (node_parent.parent().hasClass('root-level')) { + previous_level_nodes = node_parent.parent().children('li'); + } + + let node_card = undefined; + + previous_level_nodes.each(function() { + node_card = $(this).find('.node-card'); + + if (!node_card.hasClass('active-path')) { + node_card.addClass('collapsed'); + } + }); + } + + refresh_connectors(node_parent) { + if (!node_parent) return; + + $(`path[data-parent="${node_parent}"]`).remove(); + + frappe.run_serially([ + () => this.get_child_nodes(node_parent), + (child_nodes) => { + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_connector(node_parent, data.name); + }); + } + } + ]); + } + + setup_node_click_action(node) { + let me = this; + let node_element = $(`#${node.id}`); + + node_element.click(function() { + let is_sibling = me.selected_node.parent_id === node.parent_id; + + if (is_sibling) { + me.collapse_node(); + } else if (node_element.is(':visible') + && (node_element.hasClass('collapsed') || node_element.hasClass('active-path'))) { + me.remove_levels_after_node(node); + me.remove_orphaned_connectors(); + } + + me.expand_node(node); + }); + } + + remove_levels_after_node(node) { + let level = $(`#${node.id}`).parent().parent().parent(); + + if ($(`#${node.id}`).parent().hasClass('root-level')) { + level = $(`#${node.id}`).parent(); + } + + level = $('.hierarchy > li:eq('+ level.index() + ')'); + level.nextAll('li').remove(); + + let nodes = level.find('.node-card'); + let node_object = undefined; + + $.each(nodes, (_i, element) => { + node_object = this.nodes[element.id]; + node_object.expanded = 0; + node_object.$children = undefined; + }); + + nodes.removeClass('collapsed active-path'); + } + + remove_orphaned_connectors() { + let paths = $('#connectors > path'); + $.each(paths, (_i, path) => { + let parent = $(path).data('parent'); + let child = $(path).data('child'); + + if ($(parent).length || $(child).length) + return; + + $(path).remove(); + }) + } +} \ No newline at end of file diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js new file mode 100644 index 00000000000..c705681438d --- /dev/null +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -0,0 +1,513 @@ +erpnext.HierarchyChartMobile = class { + + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = wrapper.page; + + this.page.main.css({ + 'min-height': '300px', + 'max-height': '600px', + 'overflow': 'auto', + 'position': 'relative' + }); + this.page.main.addClass('frappe-card'); + + this.nodes = {}; + this.setup_node_class(); + } + + setup_node_class() { + let me = this; + this.Node = class { + constructor({ + id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line + }) { + // to setup values passed via constructor + $.extend(this, arguments[0]); + + this.expanded = 0; + + me.nodes[this.id] = this; + me.make_node_element(this); + me.setup_node_click_action(this); + } + } + } + + make_node_element(node) { + let node_card = frappe.render_template('node_card', { + id: node.id, + name: node.name, + title: node.title, + image: node.image, + parent: node.parent_id, + connections: node.connections, + is_mobile: 1 + }); + + node.parent.append(node_card); + node.$link = $(`#${node.id}`); + node.$link.addClass('mobile-node'); + } + + show() { + frappe.breadcrumbs.add('HR'); + + let me = this; + let company = this.page.add_field({ + fieldtype: 'Link', + options: 'Company', + fieldname: 'company', + placeholder: __('Select Company'), + default: frappe.defaults.get_default('company'), + only_select: true, + reqd: 1, + change: () => { + me.company = undefined; + + if (company.get_value() && me.company != company.get_value()) { + me.company = company.get_value(); + + // svg for connectors + me.make_svg_markers() + + if (me.$sibling_group) + me.$sibling_group.remove(); + + // setup sibling group wrapper + me.$sibling_group = $(`
              `); + me.page.main.append(me.$sibling_group); + + if (me.$hierarchy) + me.$hierarchy.remove(); + + // setup hierarchy + me.$hierarchy = $( + `
                +
              • +
              `); + + me.page.main.append(me.$hierarchy); + me.render_root_node(); + } + } + }); + + company.refresh(); + $(`[data-fieldname="company"]`).trigger('change'); + } + + make_svg_markers() { + $('#arrows').remove(); + + this.page.main.prepend(` + + + + + + + + + + + + + + + + + + + `); + } + + render_root_node() { + this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; + + let me = this; + + frappe.call({ + method: me.method, + args: { + company: me.company + }, + callback: function(r) { + if (r.message.length) { + let data = r.message[0]; + + let root_node = new me.Node({ + id: data.name, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: true, + connections: data.connections, + is_root: true, + }); + + me.expand_node(root_node); + } + } + }) + } + + expand_node(node) { + const is_same_node = (this.selected_node && this.selected_node.id === node.id); + this.set_selected_node(node); + this.show_active_path(node); + + if (this.$sibling_group) { + const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent'); + if (node.parent_id !== sibling_parent) + this.$sibling_group.empty(); + } + + if (!is_same_node) { + // since the previous/parent node collapses, all connections to that node need to be rebuilt + // rebuild outgoing connections of parent + this.refresh_connectors(node.parent_id, node.id); + + // rebuild incoming connections of parent + let grandparent = $(`#${node.parent_id}`).attr('data-parent'); + this.refresh_connectors(grandparent, node.parent_id); + } + + if (node.expandable && !node.expanded) { + return this.load_children(node); + } + } + + collapse_node() { + let node = this.selected_node; + if (node.expandable) { + node.$children.hide(); + node.expanded = false; + + // add a collapsed level to show the collapsed parent + // and a button beside it to move to that level + let node_parent = node.$link.parent(); + node_parent.prepend( + `
              ` + ); + + node_parent + .find('.collapsed-level') + .append(node.$link); + + frappe.run_serially([ + () => this.get_child_nodes(node.parent_id, node.id), + (child_nodes) => this.get_node_group(child_nodes, node.parent_id), + (node_group) => { + node_parent.find('.collapsed-level') + .append(node_group); + }, + () => this.setup_node_group_action() + ]); + } + } + + show_active_path(node) { + // mark node parent on active path + $(`#${node.parent_id}`).addClass('active-path'); + } + + load_children(node) { + frappe.run_serially([ + () => this.get_child_nodes(node.id), + (child_nodes) => this.render_child_nodes(node, child_nodes) + ]); + } + + get_child_nodes(node_id, exclude_node=null) { + let me = this; + return new Promise(resolve => { + frappe.call({ + method: this.method, + args: { + parent: node_id, + company: me.company, + exclude_node: exclude_node + }, + callback: (r) => { + resolve(r.message); + } + }); + }); + } + + render_child_nodes(node, child_nodes) { + if (!node.$children) { + node.$children = $('
                ') + .hide() + .appendTo(node.$link.parent()); + + node.$children.empty(); + + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_node(node, data); + $(`#${data.name}`).addClass('active-child'); + + setTimeout(() => { + this.add_connector(node.id, data.name); + }, 250); + }); + } + } + + node.$children.show(); + node.expanded = true; + } + + add_node(node, data) { + var $li = $('
              • '); + + return new this.Node({ + id: data.name, + parent: $li.appendTo(node.$children), + parent_id: node.id, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: data.expandable, + connections: data.connections, + children: undefined + }); + } + + add_connector(parent_id, child_id) { + let parent_node = document.querySelector(`#${parent_id}`); + let child_node = document.querySelector(`#${child_id}`); + + // variable for the namespace + const svgns = 'http://www.w3.org/2000/svg'; + let path = document.createElementNS(svgns, 'path'); + + let connector = undefined; + + if ($(`#${parent_id}`).hasClass('active')) { + connector = this.get_connector_for_active_node(parent_node, child_node); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + connector = this.get_connector_for_collapsed_node(parent_node, child_node); + } + + path.setAttribute("d", connector); + this.set_path_attributes(path, parent_id, child_id); + + $('#connectors').append(path); + } + + get_connector_for_active_node(parent_node, child_node) { + // we need to connect the bottom left of the parent to the left side of the child node + let pos_parent_bottom = { + x: parent_node.offsetLeft + 20, + y: parent_node.offsetTop + parent_node.offsetHeight + }; + let pos_child_left = { + x: child_node.offsetLeft - 5, + y: child_node.offsetTop + child_node.offsetHeight / 2 + }; + + let connector = + "M" + + (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + + "L" + + (pos_parent_bottom.x) + "," + (pos_child_left.y) + " " + + "L" + + (pos_child_left.x) + "," + (pos_child_left.y); + + return connector; + } + + get_connector_for_collapsed_node(parent_node, child_node) { + // we need to connect the bottom left of the parent to the top left of the child node + let pos_parent_bottom = { + x: parent_node.offsetLeft + 20, + y: parent_node.offsetTop + parent_node.offsetHeight + }; + let pos_child_top = { + x: child_node.offsetLeft + 20, + y: child_node.offsetTop + }; + + let connector = + "M" + + (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + + "L" + + (pos_child_top.x) + "," + (pos_child_top.y); + + return connector; + } + + set_path_attributes(path, parent_id, child_id) { + path.setAttribute("data-parent", parent_id); + path.setAttribute("data-child", child_id); + + if ($(`#${parent_id}`).hasClass('active')) { + path.setAttribute("class", "active-connector"); + path.setAttribute("marker-start", "url(#arrowstart-active)"); + path.setAttribute("marker-end", "url(#arrowhead-active)"); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + path.setAttribute("class", "collapsed-connector"); + } + } + + set_selected_node(node) { + // remove .active class from the current node + $('.active').removeClass('active'); + + // add active class to the newly selected node + this.selected_node = node; + node.$link.addClass('active'); + } + + setup_node_click_action(node) { + let me = this; + let node_element = $(`#${node.id}`); + + node_element.click(function() { + if (node_element.is(':visible') && node_element.hasClass('active-path')) { + me.remove_levels_after_node(node); + me.remove_orphaned_connectors(); + } else { + me.add_node_to_hierarchy(node, true); + me.collapse_node(); + } + + me.expand_node(node); + }); + } + + setup_node_group_action() { + let me = this; + + $('.node-group').on('click', function() { + let parent = $(this).attr('data-parent'); + me.expand_sibling_group_node(parent); + }); + } + + add_node_to_hierarchy(node) { + this.$hierarchy.append(` +
              • +
                +
                +
              • + `); + + node.$link.appendTo(this.$hierarchy.find('.level:last')); + } + + get_node_group(nodes, parent, collapsed=true) { + let limit = 2; + const display_nodes = nodes.slice(0, limit); + const extra_nodes = nodes.slice(limit); + + let html = display_nodes.map(node => + this.get_avatar(node) + ).join(''); + + if (extra_nodes.length === 1) { + let node = extra_nodes[0]; + html += this.get_avatar(node); + } else if (extra_nodes.length > 1) { + html = ` + ${html} + +
                + +${extra_nodes.length} +
                +
                + `; + } + + if (html) { + const $node_group = + $(`
                +
                + ${html} +
                +
                `); + + if (collapsed) + $node_group.addClass('collapsed'); + + return $node_group; + } + + return null; + } + + get_avatar(node) { + return ` + + ` + } + + expand_sibling_group_node(parent) { + let node_object = this.nodes[parent]; + let node = node_object.$link; + node.removeClass('active-child active-path'); + node_object.expanded = 0; + node_object.$children = undefined; + + // show parent's siblings and expand parent node + frappe.run_serially([ + () => this.get_child_nodes(node_object.parent_id, node_object.id), + (child_nodes) => this.get_node_group(child_nodes, node_object.parent_id, false), + (node_group) => { + if (node_group) + this.$sibling_group.empty().append(node_group); + }, + () => this.setup_node_group_action(), + () => { + this.$hierarchy.empty().append(` +
              • + `); + this.$hierarchy.find('.level').append(node); + $(`#connectors`).empty(); + this.expand_node(node_object); + } + ]); + } + + remove_levels_after_node(node) { + let level = $(`#${node.id}`).parent().parent(); + + level = $('.hierarchy-mobile > li:eq('+ (level.index()) + ')'); + level.nextAll('li').remove(); + + let current_node = level.find(`#${node.id}`); + let node_object = this.nodes[node.id]; + + current_node.removeClass('active-child active-path'); + node_object.expanded = 0; + node_object.$children = undefined; + + level.empty().append(current_node); + } + + remove_orphaned_connectors() { + let paths = $('#connectors > path'); + $.each(paths, (_i, path) => { + let parent = $(path).data('parent'); + let child = $(path).data('child'); + + if ($(parent).length || $(child).length) + return; + + $(path).remove(); + }) + } + + refresh_connectors(node_parent, node_id) { + if (!node_parent) return; + + $(`path[data-parent="${node_parent}"]`).remove(); + this.add_connector(node_parent, node_id); + } +} \ No newline at end of file diff --git a/erpnext/hr/page/organizational_chart/node_card.html b/erpnext/public/js/templates/node_card.html similarity index 100% rename from erpnext/hr/page/organizational_chart/node_card.html rename to erpnext/public/js/templates/node_card.html diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/hierarchy_chart.scss similarity index 100% rename from erpnext/public/scss/organizational_chart.scss rename to erpnext/public/scss/hierarchy_chart.scss From f15e8b7f5a50cc686e3139b4aee63726a01f6413 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 21:26:47 +0530 Subject: [PATCH 011/369] refactor: add options to chart - method to return the node data - wrapper for showing the hierarchy --- .../organizational_chart.js | 6 ++-- .../organizational_chart.py | 6 ++-- .../hierarchy_chart_desktop.js | 28 +++++++++------- .../hierarchy_chart/hierarchy_chart_mobile.js | 32 +++++++++++-------- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index 0fe724c78e4..ca9855286ca 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -8,10 +8,12 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { $(wrapper).bind('show', () => { frappe.require('/assets/js/hierarchy-chart.min.js', () => { let organizational_chart = undefined; + let method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; + if (frappe.is_mobile()) { - organizational_chart = new erpnext.HierarchyChartMobile(wrapper); + organizational_chart = new erpnext.HierarchyChartMobile(wrapper, method); } else { - organizational_chart = new erpnext.HierarchyChart(wrapper); + organizational_chart = new erpnext.HierarchyChart(wrapper, method); } organizational_chart.show(); }); diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index ae91a919b2b..f3aa13897d5 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -9,7 +9,7 @@ def get_children(parent=None, company=None, exclude_node=None, is_root=False, is filters.append(['company', '=', company]) if not fields: - fields = ['employee_name', 'name', 'reports_to', 'image', 'designation'] + fields = ['employee_name as name', 'name as id', 'reports_to', 'image', 'designation as title'] if is_root: parent = '' @@ -27,9 +27,9 @@ def get_children(parent=None, company=None, exclude_node=None, is_root=False, is for employee in employees: is_expandable = frappe.get_all('Employee', filters=[ - ['reports_to', '=', employee.get('name')] + ['reports_to', '=', employee.get('id')] ]) - employee.connections = get_connections(employee.name) + employee.connections = get_connections(employee.id) employee.expandable = 1 if is_expandable else 0 return employees diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index fd84d4ea5ca..052f140c139 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -1,8 +1,14 @@ erpnext.HierarchyChart = class { - - constructor(wrapper) { + /* Options: + - wrapper: wrapper for the hierarchy view + - method: + - to get the data for each node + - this method should return id, name, title, image, and connections for each node + */ + constructor(wrapper, method) { this.wrapper = $(wrapper); this.page = wrapper.page; + this.method = method; this.page.main.css({ 'min-height': '300px', @@ -114,8 +120,6 @@ erpnext.HierarchyChart = class { } render_root_node() { - this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; - let me = this; frappe.call({ @@ -128,12 +132,12 @@ erpnext.HierarchyChart = class { let data = r.message[0]; let root_node = new me.Node({ - id: data.name, + id: data.id, parent: me.$hierarchy.find('.root-level'), parent_id: undefined, image: data.image, - name: data.employee_name, - title: data.designation, + name: data.name, + title: data.title, expandable: true, connections: data.connections, is_root: true, @@ -225,7 +229,7 @@ erpnext.HierarchyChart = class { this.add_node(node, data); setTimeout(() => { - this.add_connector(node.id, data.name); + this.add_connector(node.id, data.id); }, 250); }); } @@ -240,12 +244,12 @@ erpnext.HierarchyChart = class { var $li = $('
              • '); return new this.Node({ - id: data.name, + id: data.id, parent: $li.appendTo(node.$children), parent_id: node.id, image: data.image, - name: data.employee_name, - title: data.designation, + name: data.name, + title: data.title, expandable: data.expandable, connections: data.connections, children: undefined @@ -333,7 +337,7 @@ erpnext.HierarchyChart = class { (child_nodes) => { if (child_nodes) { $.each(child_nodes, (_i, data) => { - this.add_connector(node_parent, data.name); + this.add_connector(node_parent, data.id); }); } } diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index c705681438d..1b8bc2e8e0f 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -1,8 +1,14 @@ erpnext.HierarchyChartMobile = class { - - constructor(wrapper) { + /* Options: + - wrapper: wrapper for the hierarchy view + - method: + - to get the data for each node + - this method should return id, name, title, image, and connections for each node + */ + constructor(wrapper, method) { this.wrapper = $(wrapper); this.page = wrapper.page; + this.method = method; this.page.main.css({ 'min-height': '300px', @@ -123,8 +129,6 @@ erpnext.HierarchyChartMobile = class { } render_root_node() { - this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; - let me = this; frappe.call({ @@ -137,12 +141,12 @@ erpnext.HierarchyChartMobile = class { let data = r.message[0]; let root_node = new me.Node({ - id: data.name, + id: data.id, parent: me.$hierarchy.find('.root-level'), parent_id: undefined, image: data.image, - name: data.employee_name, - title: data.designation, + name: data.name, + title: data.title, expandable: true, connections: data.connections, is_root: true, @@ -249,10 +253,10 @@ erpnext.HierarchyChartMobile = class { if (child_nodes) { $.each(child_nodes, (_i, data) => { this.add_node(node, data); - $(`#${data.name}`).addClass('active-child'); + $(`#${data.id}`).addClass('active-child'); setTimeout(() => { - this.add_connector(node.id, data.name); + this.add_connector(node.id, data.id); }, 250); }); } @@ -266,12 +270,12 @@ erpnext.HierarchyChartMobile = class { var $li = $('
              • '); return new this.Node({ - id: data.name, + id: data.id, parent: $li.appendTo(node.$children), parent_id: node.id, image: data.image, - name: data.employee_name, - title: data.designation, + name: data.name, + title: data.title, expandable: data.expandable, connections: data.connections, children: undefined @@ -418,7 +422,7 @@ erpnext.HierarchyChartMobile = class { ${html}
                + title="${extra_nodes.map(node => node.name).join(', ')}"> +${extra_nodes.length}
                @@ -443,7 +447,7 @@ erpnext.HierarchyChartMobile = class { } get_avatar(node) { - return ` + return ` ` } From c40b9d276e04cb521dd33601a84acb53100f40c0 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 21:51:21 +0530 Subject: [PATCH 012/369] feat: setup node edit action --- .../organizational_chart/organizational_chart.js | 4 ++-- .../js/hierarchy_chart/hierarchy_chart_desktop.js | 14 +++++++++++++- .../js/hierarchy_chart/hierarchy_chart_mobile.js | 14 +++++++++++++- erpnext/public/js/templates/node_card.html | 4 ++-- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index ca9855286ca..a1388867687 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -11,9 +11,9 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { let method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; if (frappe.is_mobile()) { - organizational_chart = new erpnext.HierarchyChartMobile(wrapper, method); + organizational_chart = new erpnext.HierarchyChartMobile('Employee', wrapper, method); } else { - organizational_chart = new erpnext.HierarchyChart(wrapper, method); + organizational_chart = new erpnext.HierarchyChart('Employee', wrapper, method); } organizational_chart.show(); }); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 052f140c139..0823ec77a80 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -1,14 +1,16 @@ erpnext.HierarchyChart = class { /* Options: + - doctype - wrapper: wrapper for the hierarchy view - method: - to get the data for each node - this method should return id, name, title, image, and connections for each node */ - constructor(wrapper, method) { + constructor(doctype, wrapper, method) { this.wrapper = $(wrapper); this.page = wrapper.page; this.method = method; + this.doctype = doctype; this.page.main.css({ 'min-height': '300px', @@ -36,6 +38,7 @@ erpnext.HierarchyChart = class { me.nodes[this.id] = this; me.make_node_element(this); me.setup_node_click_action(this); + me.setup_edit_node_action(this); } } } @@ -363,6 +366,15 @@ erpnext.HierarchyChart = class { }); } + setup_edit_node_action(node) { + let node_element = $(`#${node.id}`); + let me = this; + + node_element.find('.btn-edit-node').click(function() { + frappe.set_route('Form', me.doctype, node.id); + }); + } + remove_levels_after_node(node) { let level = $(`#${node.id}`).parent().parent().parent(); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 1b8bc2e8e0f..4b09714d0ad 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -1,14 +1,16 @@ erpnext.HierarchyChartMobile = class { /* Options: + - doctype - wrapper: wrapper for the hierarchy view - method: - to get the data for each node - this method should return id, name, title, image, and connections for each node */ - constructor(wrapper, method) { + constructor(doctype, wrapper, method) { this.wrapper = $(wrapper); this.page = wrapper.page; this.method = method; + this.doctype = doctype this.page.main.css({ 'min-height': '300px', @@ -36,6 +38,7 @@ erpnext.HierarchyChartMobile = class { me.nodes[this.id] = this; me.make_node_element(this); me.setup_node_click_action(this); + me.setup_edit_node_action(this); } } } @@ -385,6 +388,15 @@ erpnext.HierarchyChartMobile = class { }); } + setup_edit_node_action(node) { + let node_element = $(`#${node.id}`); + let me = this; + + node_element.find('.btn-edit-node').click(function() { + frappe.set_route('Form', me.doctype, node.id); + }); + } + setup_node_group_action() { let me = this; diff --git a/erpnext/public/js/templates/node_card.html b/erpnext/public/js/templates/node_card.html index e42e54f690c..30aedab4bb7 100644 --- a/erpnext/public/js/templates/node_card.html +++ b/erpnext/public/js/templates/node_card.html @@ -17,9 +17,9 @@
                {{ title }}
                {% if connections == 1 %} -
                · {{ connections }}
                +
                · {{ connections }} Connection
                {% else %} -
                · {{ connections }}
                +
                · {{ connections }} Connections
                {% endif %} From 7558b28b794f09826bcf8f42385605dcdc819086 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 21:57:27 +0530 Subject: [PATCH 013/369] fix: revert changes in employee descendants query --- erpnext/hr/doctype/employee/employee.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 7917e3abf59..ed7d5884347 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -476,14 +476,13 @@ def get_employee_emails(employee_list): return employee_emails @frappe.whitelist() -def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False, fields=None): +def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False): filters = [['status', '!=', 'Left']] if company and company != 'All Companies': filters.append(['company', '=', company]) - if not fields: - fields = ['name as value', 'employee_name as title'] + fields = ['name as value', 'employee_name as title'] if is_root: parent = '' From a7507f7af63c259fd61164c39a5dbd5d6e3c2df1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 30 Jun 2021 01:46:10 +0530 Subject: [PATCH 014/369] refactor: use arcs instead of bezier curves for cleaner connectors --- .../hierarchy_chart_desktop.js | 54 +++++++++++++++---- erpnext/public/scss/hierarchy_chart.scss | 2 +- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 0823ec77a80..ba811be5869 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -277,15 +277,53 @@ erpnext.HierarchyChart = class { y: child_node.offsetTop + child_node.offsetHeight / 2 }; - let connector = - "M" + - (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + - "C" + - (pos_parent_right.x + 100) + "," + (pos_parent_right.y) + " " + - (pos_child_left.x - 100) + "," + (pos_child_left.y) + " " + - (pos_child_left.x) + "," + (pos_child_left.y); + let connector = this.get_connector(pos_parent_right, pos_child_left); path.setAttribute("d", connector); + this.set_path_attributes(path, parent_id, child_id); + + $('#connectors').append(path); + } + + get_connector(pos_parent_right, pos_child_left) { + if (pos_parent_right.y === pos_child_left.y) { + // don't add arcs if it's a straight line + return "M" + + (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + + "L"+ + (pos_child_left.x) + "," + (pos_child_left.y); + } else { + let arc_1 = ""; + let arc_2 = ""; + let offset = 0; + + if (pos_parent_right.y > pos_child_left.y) { + // if child is above parent on Y axis 1st arc is anticlocwise + // second arc is clockwise + arc_1 = "a10,10 1 0 0 10,-10 "; + arc_2 = "a10,10 0 0 1 10,-10 "; + offset = 10; + } else { + // if child is below parent on Y axis 1st arc is clockwise + // second arc is anticlockwise + arc_1 = "a10,10 0 0 1 10,10 "; + arc_2 = "a10,10 1 0 0 10,10 "; + offset = -10; + } + + return "M" + (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + + "L" + + (pos_parent_right.x + 40) + "," + (pos_parent_right.y) + " " + + arc_1 + + "L" + + (pos_parent_right.x + 50) + "," + (pos_child_left.y + offset) + " " + + arc_2 + + "L"+ + (pos_child_left.x) + "," + (pos_child_left.y); + } + } + + set_path_attributes(path, parent_id, child_id) { path.setAttribute("data-parent", parent_id); path.setAttribute("data-child", child_id); @@ -298,8 +336,6 @@ erpnext.HierarchyChart = class { path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); } - - $('#connectors').append(path); } set_selected_node(node) { diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 16b8792432e..16137fdb5f0 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -48,7 +48,6 @@ border-radius: 0.5rem; padding: 0.75rem; width: 18rem; - height: 5rem; .btn-edit-node { display: flex; @@ -195,6 +194,7 @@ .level { margin-right: 8px; + align-items: flex-start; } #arrows { From b8a18bfef1f3c86758d63bc3cde172ecc1758f43 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 30 Jun 2021 01:57:43 +0530 Subject: [PATCH 015/369] feat: add arc to connectors in mobile view --- erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 4b09714d0ad..b09b428b306 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -322,7 +322,8 @@ erpnext.HierarchyChartMobile = class { "M" + (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + "L" + - (pos_parent_bottom.x) + "," + (pos_child_left.y) + " " + + (pos_parent_bottom.x) + "," + (pos_child_left.y - 10) + " " + + "a10,10 1 0 0 10,10 " + "L" + (pos_child_left.x) + "," + (pos_child_left.y); From 77b0b8a877108062818b5e0f72995966a85f9af2 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 30 Jun 2021 02:29:16 +0530 Subject: [PATCH 016/369] fix: edit node button overflowing --- erpnext/public/js/templates/node_card.html | 2 +- erpnext/public/scss/hierarchy_chart.scss | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/templates/node_card.html b/erpnext/public/js/templates/node_card.html index 30aedab4bb7..c3d8e010b53 100644 --- a/erpnext/public/js/templates/node_card.html +++ b/erpnext/public/js/templates/node_card.html @@ -8,7 +8,7 @@
                {{ name }} -
                + diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 16137fdb5f0..eefc14d6797 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -57,6 +57,7 @@ font-size: .75rem; justify-content: center; box-shadow: var(--shadow-sm); + margin-left: auto; } .edit-chart-node { @@ -79,6 +80,7 @@ align-items: center; justify-content: space-between; margin-bottom: 2px; + width: 12.2rem; } } From 2fcd05aa82042456b50c9dad42d1e97d57afb80f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 2 Jul 2021 18:15:18 +0530 Subject: [PATCH 017/369] fix: sider --- .../hierarchy_chart_desktop.js | 25 +++++++++---------- .../hierarchy_chart/hierarchy_chart_mobile.js | 18 ++++++------- erpnext/public/scss/hierarchy_chart.scss | 5 +--- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index ba811be5869..9e82fb20024 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -40,7 +40,7 @@ erpnext.HierarchyChart = class { me.setup_node_click_action(this); me.setup_edit_node_action(this); } - } + }; } make_node_element(node) { @@ -76,7 +76,7 @@ erpnext.HierarchyChart = class { me.company = company.get_value(); // svg for connectors - me.make_svg_markers() + me.make_svg_markers(); if (me.$hierarchy) me.$hierarchy.remove(); @@ -149,7 +149,7 @@ erpnext.HierarchyChart = class { me.expand_node(root_node); } } - }) + }); } expand_node(node) { @@ -166,7 +166,7 @@ erpnext.HierarchyChart = class { // rebuild incoming connections let grandparent = $(`#${node.parent_id}`).attr('data-parent'); - this.refresh_connectors(grandparent) + this.refresh_connectors(grandparent); } if (node.expandable && !node.expanded) { @@ -176,8 +176,8 @@ erpnext.HierarchyChart = class { collapse_node() { if (this.selected_node.expandable) { - this.selected_node.$children.hide(); - $(`path[data-parent="${this.selected_node.id}"]`).hide(); + this.selected_node.$children.hide('fast'); + $(`path[data-parent="${this.selected_node.id}"]`).hide('fast'); this.selected_node.expanded = false; } } @@ -222,15 +222,14 @@ erpnext.HierarchyChart = class { if (!node.$children) { node.$children = $('
                  ') - .hide() - .appendTo(this.$hierarchy.find('.level:last')); + .hide() + .appendTo(this.$hierarchy.find('.level:last')); node.$children.empty(); if (child_nodes) { $.each(child_nodes, (_i, data) => { this.add_node(node, data); - setTimeout(() => { this.add_connector(node.id, data.id); }, 250); @@ -238,8 +237,8 @@ erpnext.HierarchyChart = class { } } - node.$children.show(); - $(`path[data-parent="${node.id}"]`).show(); + node.$children.show('fast'); + $(`path[data-parent="${node.id}"]`).show('fast'); node.expanded = true; } @@ -443,6 +442,6 @@ erpnext.HierarchyChart = class { return; $(path).remove(); - }) + }); } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index b09b428b306..2ff00baa7c6 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -10,7 +10,7 @@ erpnext.HierarchyChartMobile = class { this.wrapper = $(wrapper); this.page = wrapper.page; this.method = method; - this.doctype = doctype + this.doctype = doctype; this.page.main.css({ 'min-height': '300px', @@ -40,7 +40,7 @@ erpnext.HierarchyChartMobile = class { me.setup_node_click_action(this); me.setup_edit_node_action(this); } - } + }; } make_node_element(node) { @@ -78,7 +78,7 @@ erpnext.HierarchyChartMobile = class { me.company = company.get_value(); // svg for connectors - me.make_svg_markers() + me.make_svg_markers(); if (me.$sibling_group) me.$sibling_group.remove(); @@ -158,7 +158,7 @@ erpnext.HierarchyChartMobile = class { me.expand_node(root_node); } } - }) + }); } expand_node(node) { @@ -248,8 +248,8 @@ erpnext.HierarchyChartMobile = class { render_child_nodes(node, child_nodes) { if (!node.$children) { node.$children = $('
                    ') - .hide() - .appendTo(node.$link.parent()); + .hide() + .appendTo(node.$link.parent()); node.$children.empty(); @@ -462,7 +462,7 @@ erpnext.HierarchyChartMobile = class { get_avatar(node) { return ` - ` + `; } expand_sibling_group_node(parent) { @@ -518,7 +518,7 @@ erpnext.HierarchyChartMobile = class { return; $(path).remove(); - }) + }); } refresh_connectors(node_parent, node_id) { @@ -527,4 +527,4 @@ erpnext.HierarchyChartMobile = class { $(`path[data-parent="${node_parent}"]`).remove(); this.add_connector(node_parent, node_id); } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index eefc14d6797..a54bf6f332e 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -62,16 +62,13 @@ .edit-chart-node { display: block; + margin-right: 0.25rem; } .node-edit-icon { display: block; } - .edit-chart-node { - margin-right: 0.25rem; - } - .node-edit-icon > .icon{ stroke: var(--blue-500); } From ad066033925ed8f8e5bd6c2e8b7f9c3cc54e5e27 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 6 Jul 2021 18:16:49 +0530 Subject: [PATCH 018/369] fix: removing orphaned connectors --- .../js/hierarchy_chart/hierarchy_chart_desktop.js | 14 ++++++-------- .../js/hierarchy_chart/hierarchy_chart_mobile.js | 6 ++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 9e82fb20024..1896ac7c39e 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -176,8 +176,8 @@ erpnext.HierarchyChart = class { collapse_node() { if (this.selected_node.expandable) { - this.selected_node.$children.hide('fast'); - $(`path[data-parent="${this.selected_node.id}"]`).hide('fast'); + this.selected_node.$children.hide(); + $(`path[data-parent="${this.selected_node.id}"]`).hide(); this.selected_node.expanded = false; } } @@ -237,8 +237,8 @@ erpnext.HierarchyChart = class { } } - node.$children.show('fast'); - $(`path[data-parent="${node.id}"]`).show('fast'); + node.$children.show(); + $(`path[data-parent="${node.id}"]`).show(); node.expanded = true; } @@ -262,9 +262,7 @@ erpnext.HierarchyChart = class { let parent_node = document.querySelector(`#${parent_id}`); let child_node = document.querySelector(`#${child_id}`); - // variable for the namespace - const svgns = 'http://www.w3.org/2000/svg'; - let path = document.createElementNS(svgns, 'path'); + let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); // we need to connect right side of the parent to the left side of the child node let pos_parent_right = { @@ -438,7 +436,7 @@ erpnext.HierarchyChart = class { let parent = $(path).data('parent'); let child = $(path).data('child'); - if ($(parent).length || $(child).length) + if ($(`#${parent}`).length && $(`#${child}`).length) return; $(path).remove(); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 2ff00baa7c6..102cbb03b0a 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -289,9 +289,7 @@ erpnext.HierarchyChartMobile = class { let parent_node = document.querySelector(`#${parent_id}`); let child_node = document.querySelector(`#${child_id}`); - // variable for the namespace - const svgns = 'http://www.w3.org/2000/svg'; - let path = document.createElementNS(svgns, 'path'); + let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); let connector = undefined; @@ -514,7 +512,7 @@ erpnext.HierarchyChartMobile = class { let parent = $(path).data('parent'); let child = $(path).data('child'); - if ($(parent).length || $(child).length) + if ($(`#${parent}`).length && $(`#${child}`).length) return; $(path).remove(); From 6d5ee25bba0222b122d873340784cc82ee8309fc Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 7 Jul 2021 09:43:28 +0530 Subject: [PATCH 019/369] fix: unnecessary variables --- .../js/hierarchy_chart/hierarchy_chart_desktop.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 1896ac7c39e..8d0685f80d5 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -153,7 +153,7 @@ erpnext.HierarchyChart = class { } expand_node(node) { - let is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id; + const is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id; this.set_selected_node(node); this.show_active_path(node); this.collapse_previous_level_nodes(node); @@ -243,11 +243,9 @@ erpnext.HierarchyChart = class { } add_node(node, data) { - var $li = $('
                  • '); - return new this.Node({ id: data.id, - parent: $li.appendTo(node.$children), + parent: $('
                  • ').appendTo(node.$children), parent_id: node.id, image: data.image, name: data.name, @@ -259,8 +257,8 @@ erpnext.HierarchyChart = class { } add_connector(parent_id, child_id) { - let parent_node = document.querySelector(`#${parent_id}`); - let child_node = document.querySelector(`#${child_id}`); + const parent_node = document.querySelector(`#${parent_id}`); + const child_node = document.querySelector(`#${child_id}`); let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); @@ -276,7 +274,7 @@ erpnext.HierarchyChart = class { let connector = this.get_connector(pos_parent_right, pos_child_left); - path.setAttribute("d", connector); + path.setAttribute('d', connector); this.set_path_attributes(path, parent_id, child_id); $('#connectors').append(path); @@ -385,7 +383,7 @@ erpnext.HierarchyChart = class { let node_element = $(`#${node.id}`); node_element.click(function() { - let is_sibling = me.selected_node.parent_id === node.parent_id; + const is_sibling = me.selected_node.parent_id === node.parent_id; if (is_sibling) { me.collapse_node(); From 6eec25127369e2b3af3658d11a80132facfee29e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 7 Jul 2021 12:05:50 +0530 Subject: [PATCH 020/369] feat: handle multiple root / orphan nodes --- .../organizational_chart.py | 3 +- .../hierarchy_chart_desktop.js | 48 +++++++++---------- .../hierarchy_chart/hierarchy_chart_mobile.js | 35 +++++++------- erpnext/public/scss/hierarchy_chart.scss | 4 ++ 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index f3aa13897d5..77b8df7520b 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -17,7 +17,7 @@ def get_children(parent=None, company=None, exclude_node=None, is_root=False, is if exclude_node: filters.append(['name', '!=', exclude_node]) - if parent and company and parent!=company: + if parent and company and parent != company: filters.append(['reports_to', '=', parent]) else: filters.append(['reports_to', '=', '']) @@ -32,6 +32,7 @@ def get_children(parent=None, company=None, exclude_node=None, is_root=False, is employee.connections = get_connections(employee.id) employee.expandable = 1 if is_expandable else 0 + employees.sort(key=lambda x: x['connections'], reverse=True) return employees diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 8d0685f80d5..e89a98ac4f8 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -84,11 +84,13 @@ erpnext.HierarchyChart = class { // setup hierarchy me.$hierarchy = $( `
                      -
                    • +
                    • +
                        +
                      `); me.page.main.append(me.$hierarchy); - me.render_root_node(); + me.render_root_nodes(); } } }); @@ -122,7 +124,7 @@ erpnext.HierarchyChart = class { `); } - render_root_node() { + render_root_nodes() { let me = this; frappe.call({ @@ -132,21 +134,28 @@ erpnext.HierarchyChart = class { }, callback: function(r) { if (r.message.length) { - let data = r.message[0]; + let nodes = r.message; + let node = undefined; + let first_root = undefined; - let root_node = new me.Node({ - id: data.id, - parent: me.$hierarchy.find('.root-level'), - parent_id: undefined, - image: data.image, - name: data.name, - title: data.title, - expandable: true, - connections: data.connections, - is_root: true, + $.each(nodes, (i, data) => { + node = new me.Node({ + id: data.id, + parent: $('
                    • ').appendTo(me.$hierarchy.find('.node-children')), + parent_id: undefined, + image: data.image, + name: data.name, + title: data.title, + expandable: true, + connections: data.connections, + is_root: true + }); + + if (i == 0) + first_root = node; }); - me.expand_node(root_node); + me.expand_node(first_root); } } }); @@ -344,12 +353,7 @@ erpnext.HierarchyChart = class { collapse_previous_level_nodes(node) { let node_parent = $(`#${node.parent_id}`); - let previous_level_nodes = node_parent.parent().parent().children('li'); - if (node_parent.parent().hasClass('root-level')) { - previous_level_nodes = node_parent.parent().children('li'); - } - let node_card = undefined; previous_level_nodes.each(function() { @@ -409,10 +413,6 @@ erpnext.HierarchyChart = class { remove_levels_after_node(node) { let level = $(`#${node.id}`).parent().parent().parent(); - if ($(`#${node.id}`).parent().hasClass('root-level')) { - level = $(`#${node.id}`).parent(); - } - level = $('.hierarchy > li:eq('+ level.index() + ')'); level.nextAll('li').remove(); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 102cbb03b0a..5eee27b5fc3 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -97,7 +97,7 @@ erpnext.HierarchyChartMobile = class { `); me.page.main.append(me.$hierarchy); - me.render_root_node(); + me.render_root_nodes(); } } }); @@ -131,7 +131,7 @@ erpnext.HierarchyChartMobile = class { `); } - render_root_node() { + render_root_nodes() { let me = this; frappe.call({ @@ -141,21 +141,21 @@ erpnext.HierarchyChartMobile = class { }, callback: function(r) { if (r.message.length) { - let data = r.message[0]; + let nodes = r.message; - let root_node = new me.Node({ - id: data.id, - parent: me.$hierarchy.find('.root-level'), - parent_id: undefined, - image: data.image, - name: data.name, - title: data.title, - expandable: true, - connections: data.connections, - is_root: true, + $.each(nodes, (_i, data) => { + return new me.Node({ + id: data.id, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.name, + title: data.title, + expandable: true, + connections: data.connections, + is_root: true + }); }); - - me.expand_node(root_node); } } }); @@ -375,7 +375,10 @@ erpnext.HierarchyChartMobile = class { let node_element = $(`#${node.id}`); node_element.click(function() { - if (node_element.is(':visible') && node_element.hasClass('active-path')) { + if (node.is_root) { + me.$hierarchy.empty(); + me.add_node_to_hierarchy(node, true); + } else if (node_element.is(':visible') && node_element.hasClass('active-path')) { me.remove_levels_after_node(node); me.remove_orphaned_connectors(); } else { diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index a54bf6f332e..dd523c34439 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -246,6 +246,10 @@ margin-top: 16px; } +.root-level .node-card { + margin: 0 0 16px; +} + // node group .collapsed-level { From df3bb9ea8caffe47e3696c10f711a393d78e6630 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 8 Jul 2021 09:53:31 +0530 Subject: [PATCH 021/369] perf: Optimise Rendering - optimise get_children function - use promises instead of callbacks - optimise selectors - use const wherever possible - use pure js instead of jquery for connectors for faster rendering --- .../organizational_chart.py | 22 ++--- .../hierarchy_chart_desktop.js | 82 +++++++++---------- .../hierarchy_chart/hierarchy_chart_mobile.js | 65 +++++++-------- 3 files changed, 78 insertions(+), 91 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 77b8df7520b..46578f3aaf7 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,33 +2,25 @@ from __future__ import unicode_literals import frappe @frappe.whitelist() -def get_children(parent=None, company=None, exclude_node=None, is_root=False, is_tree=False, fields=None): +def get_children(parent=None, company=None): filters = [['status', '!=', 'Left']] if company and company != 'All Companies': filters.append(['company', '=', company]) - if not fields: - fields = ['employee_name as name', 'name as id', 'reports_to', 'image', 'designation as title'] - - if is_root: - parent = '' - - if exclude_node: - filters.append(['name', '!=', exclude_node]) - if parent and company and parent != company: filters.append(['reports_to', '=', parent]) else: filters.append(['reports_to', '=', '']) - employees = frappe.get_list('Employee', fields=fields, - filters=filters, order_by='name') + employees = frappe.get_list('Employee', + fields=['employee_name as name', 'name as id', 'reports_to', 'image', 'designation as title'], + filters=filters, + order_by='name' + ) for employee in employees: - is_expandable = frappe.get_all('Employee', filters=[ - ['reports_to', '=', employee.get('id')] - ]) + is_expandable = frappe.db.count('Employee', filters={'reports_to': employee.get('id')}) employee.connections = get_connections(employee.id) employee.expandable = 1 if is_expandable else 0 diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index e89a98ac4f8..bf366792a9c 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -7,7 +7,6 @@ erpnext.HierarchyChart = class { - this method should return id, name, title, image, and connections for each node */ constructor(doctype, wrapper, method) { - this.wrapper = $(wrapper); this.page = wrapper.page; this.method = method; this.doctype = doctype; @@ -61,6 +60,8 @@ erpnext.HierarchyChart = class { frappe.breadcrumbs.add('HR'); let me = this; + if ($(`[data-fieldname="company"]`).length) return; + let company = this.page.add_field({ fieldtype: 'Link', options: 'Company', @@ -131,32 +132,30 @@ erpnext.HierarchyChart = class { method: me.method, args: { company: me.company - }, - callback: function(r) { - if (r.message.length) { - let nodes = r.message; - let node = undefined; - let first_root = undefined; + } + }).then(r => { + if (r.message.length) { + let node = undefined; + let first_root = undefined; - $.each(nodes, (i, data) => { - node = new me.Node({ - id: data.id, - parent: $('
                    • ').appendTo(me.$hierarchy.find('.node-children')), - parent_id: undefined, - image: data.image, - name: data.name, - title: data.title, - expandable: true, - connections: data.connections, - is_root: true - }); - - if (i == 0) - first_root = node; + $.each(r.message, (i, data) => { + node = new me.Node({ + id: data.id, + parent: $('
                    • ').appendTo(me.$hierarchy.find('.node-children')), + parent_id: undefined, + image: data.image, + name: data.name, + title: data.title, + expandable: true, + connections: data.connections, + is_root: true }); - me.expand_node(first_root); - } + if (i == 0) + first_root = node; + }); + + me.expand_node(first_root); } }); } @@ -204,18 +203,14 @@ erpnext.HierarchyChart = class { } get_child_nodes(node_id) { - let me = this; return new Promise(resolve => { frappe.call({ method: this.method, args: { parent: node_id, - company: me.company - }, - callback: (r) => { - resolve(r.message); + company: this.company } - }); + }).then(r => resolve(r.message)); }); } @@ -266,27 +261,28 @@ erpnext.HierarchyChart = class { } add_connector(parent_id, child_id) { + // using pure javascript for better performance const parent_node = document.querySelector(`#${parent_id}`); const child_node = document.querySelector(`#${child_id}`); let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); // we need to connect right side of the parent to the left side of the child node - let pos_parent_right = { + const pos_parent_right = { x: parent_node.offsetLeft + parent_node.offsetWidth, y: parent_node.offsetTop + parent_node.offsetHeight / 2 }; - let pos_child_left = { + const pos_child_left = { x: child_node.offsetLeft - 5, y: child_node.offsetTop + child_node.offsetHeight / 2 }; - let connector = this.get_connector(pos_parent_right, pos_child_left); + const connector = this.get_connector(pos_parent_right, pos_child_left); path.setAttribute('d', connector); this.set_path_attributes(path, parent_id, child_id); - $('#connectors').append(path); + document.getElementById('connectors').appendChild(path); } get_connector(pos_parent_right, pos_child_left) { @@ -330,12 +326,13 @@ erpnext.HierarchyChart = class { set_path_attributes(path, parent_id, child_id) { path.setAttribute("data-parent", parent_id); path.setAttribute("data-child", child_id); + const parent = $(`#${parent_id}`); - if ($(`#${parent_id}`).hasClass('active')) { + if (parent.hasClass('active')) { path.setAttribute("class", "active-connector"); path.setAttribute("marker-start", "url(#arrowstart-active)"); path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if ($(`#${parent_id}`).hasClass('active-path')) { + } else if (parent.hasClass('active-path')) { path.setAttribute("class", "collapsed-connector"); path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); @@ -343,8 +340,9 @@ erpnext.HierarchyChart = class { } set_selected_node(node) { - // remove .active class from the current node - $('.active').removeClass('active'); + // remove active class from the current node + if (this.selected_node) + this.selected_node.$link.removeClass('active'); // add active class to the newly selected node this.selected_node = node; @@ -411,9 +409,9 @@ erpnext.HierarchyChart = class { } remove_levels_after_node(node) { - let level = $(`#${node.id}`).parent().parent().parent(); + let level = $(`#${node.id}`).parent().parent().parent().index(); - level = $('.hierarchy > li:eq('+ level.index() + ')'); + level = $('.hierarchy > li:eq('+ level + ')'); level.nextAll('li').remove(); let nodes = level.find('.node-card'); @@ -431,8 +429,8 @@ erpnext.HierarchyChart = class { remove_orphaned_connectors() { let paths = $('#connectors > path'); $.each(paths, (_i, path) => { - let parent = $(path).data('parent'); - let child = $(path).data('child'); + const parent = $(path).data('parent'); + const child = $(path).data('child'); if ($(`#${parent}`).length && $(`#${child}`).length) return; diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 5eee27b5fc3..17062e25857 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -7,7 +7,6 @@ erpnext.HierarchyChartMobile = class { - this method should return id, name, title, image, and connections for each node */ constructor(doctype, wrapper, method) { - this.wrapper = $(wrapper); this.page = wrapper.page; this.method = method; this.doctype = doctype; @@ -63,6 +62,8 @@ erpnext.HierarchyChartMobile = class { frappe.breadcrumbs.add('HR'); let me = this; + if ($(`[data-fieldname="company"]`).length) return; + let company = this.page.add_field({ fieldtype: 'Link', options: 'Company', @@ -139,24 +140,21 @@ erpnext.HierarchyChartMobile = class { args: { company: me.company }, - callback: function(r) { - if (r.message.length) { - let nodes = r.message; - - $.each(nodes, (_i, data) => { - return new me.Node({ - id: data.id, - parent: me.$hierarchy.find('.root-level'), - parent_id: undefined, - image: data.image, - name: data.name, - title: data.title, - expandable: true, - connections: data.connections, - is_root: true - }); + }).then(r => { + if (r.message.length) { + $.each(r.message, (_i, data) => { + return new me.Node({ + id: data.id, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.name, + title: data.title, + expandable: true, + connections: data.connections, + is_root: true }); - } + }); } }); } @@ -237,11 +235,8 @@ erpnext.HierarchyChartMobile = class { parent: node_id, company: me.company, exclude_node: exclude_node - }, - callback: (r) => { - resolve(r.message); } - }); + }).then(r => resolve(r.message)); }); } @@ -286,10 +281,10 @@ erpnext.HierarchyChartMobile = class { } add_connector(parent_id, child_id) { - let parent_node = document.querySelector(`#${parent_id}`); - let child_node = document.querySelector(`#${child_id}`); + const parent_node = document.querySelector(`#${parent_id}`); + const child_node = document.querySelector(`#${child_id}`); - let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); let connector = undefined; @@ -299,10 +294,10 @@ erpnext.HierarchyChartMobile = class { connector = this.get_connector_for_collapsed_node(parent_node, child_node); } - path.setAttribute("d", connector); + path.setAttribute('d', connector); this.set_path_attributes(path, parent_id, child_id); - $('#connectors').append(path); + document.getElementById('connectors').appendChild(path); } get_connector_for_active_node(parent_node, child_node) { @@ -351,19 +346,21 @@ erpnext.HierarchyChartMobile = class { set_path_attributes(path, parent_id, child_id) { path.setAttribute("data-parent", parent_id); path.setAttribute("data-child", child_id); + const parent = $(`#${parent_id}`); - if ($(`#${parent_id}`).hasClass('active')) { + if (parent.hasClass('active')) { path.setAttribute("class", "active-connector"); path.setAttribute("marker-start", "url(#arrowstart-active)"); path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if ($(`#${parent_id}`).hasClass('active-path')) { + } else if (parent.hasClass('active-path')) { path.setAttribute("class", "collapsed-connector"); } } set_selected_node(node) { // remove .active class from the current node - $('.active').removeClass('active'); + if (this.selected_node) + this.selected_node.$link.removeClass('active'); // add active class to the newly selected node this.selected_node = node; @@ -494,9 +491,9 @@ erpnext.HierarchyChartMobile = class { } remove_levels_after_node(node) { - let level = $(`#${node.id}`).parent().parent(); + let level = $(`#${node.id}`).parent().parent().index(); - level = $('.hierarchy-mobile > li:eq('+ (level.index()) + ')'); + level = $('.hierarchy-mobile > li:eq('+ level + ')'); level.nextAll('li').remove(); let current_node = level.find(`#${node.id}`); @@ -512,8 +509,8 @@ erpnext.HierarchyChartMobile = class { remove_orphaned_connectors() { let paths = $('#connectors > path'); $.each(paths, (_i, path) => { - let parent = $(path).data('parent'); - let child = $(path).data('child'); + const parent = $(path).data('parent'); + const child = $(path).data('child'); if ($(`#${parent}`).length && $(`#${child}`).length) return; From 48018b8d8c50caee0e8b6ff6bd5a81a35806dcdb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 8 Jul 2021 11:23:50 +0530 Subject: [PATCH 022/369] fix: do not sort by number of connections --- .../hr/page/organizational_chart/organizational_chart.py | 1 - .../public/js/hierarchy_chart/hierarchy_chart_desktop.js | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 46578f3aaf7..ce84b3c7444 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -24,7 +24,6 @@ def get_children(parent=None, company=None): employee.connections = get_connections(employee.id) employee.expandable = 1 if is_expandable else 0 - employees.sort(key=lambda x: x['connections'], reverse=True) return employees diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index bf366792a9c..374787c6ef2 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -135,8 +135,8 @@ erpnext.HierarchyChart = class { } }).then(r => { if (r.message.length) { + let expand_node = undefined; let node = undefined; - let first_root = undefined; $.each(r.message, (i, data) => { node = new me.Node({ @@ -151,11 +151,11 @@ erpnext.HierarchyChart = class { is_root: true }); - if (i == 0) - first_root = node; + if (!expand_node && data.connections) + expand_node = node; }); - me.expand_node(first_root); + me.expand_node(expand_node); } }); } From 05ffc0d3e0fc061639494df2978557c63f7d2d25 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 8 Jul 2021 16:55:42 +0530 Subject: [PATCH 023/369] feat: use icon for connections on mobile view --- .../js/hierarchy_chart/hierarchy_chart_mobile.js | 2 +- erpnext/public/js/templates/node_card.html | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 17062e25857..19852993789 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -50,7 +50,7 @@ erpnext.HierarchyChartMobile = class { image: node.image, parent: node.parent_id, connections: node.connections, - is_mobile: 1 + is_mobile: true }); node.parent.append(node_card); diff --git a/erpnext/public/js/templates/node_card.html b/erpnext/public/js/templates/node_card.html index c3d8e010b53..fb94df85ed8 100644 --- a/erpnext/public/js/templates/node_card.html +++ b/erpnext/public/js/templates/node_card.html @@ -16,10 +16,16 @@
                      {{ title }}
                      - {% if connections == 1 %} -
                      · {{ connections }} Connection
                      + {% if is_mobile %} +
                      + · {{ connections }} +
                      {% else %} -
                      · {{ connections }} Connections
                      + {% if connections == 1 %} +
                      · {{ connections }} Connection
                      + {% else %} +
                      · {{ connections }} Connections
                      + {% endif %} {% endif %}
                      From 09c24c79496e942d749ff8e5a4ef502453064557 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 8 Jul 2021 17:05:40 +0530 Subject: [PATCH 024/369] fix: exclude active node while fetching sibling group --- .../hr/page/organizational_chart/organizational_chart.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index ce84b3c7444..1e03e3d06ad 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,8 +2,7 @@ from __future__ import unicode_literals import frappe @frappe.whitelist() -def get_children(parent=None, company=None): - +def get_children(parent=None, company=None, exclude_node=None): filters = [['status', '!=', 'Left']] if company and company != 'All Companies': filters.append(['company', '=', company]) @@ -13,6 +12,9 @@ def get_children(parent=None, company=None): else: filters.append(['reports_to', '=', '']) + if exclude_node: + filters.append(['name', '!=', exclude_node]) + employees = frappe.get_list('Employee', fields=['employee_name as name', 'name as id', 'reports_to', 'image', 'designation as title'], filters=filters, From 06fc9e7847622cf0443c166d3514d6a2288e4902 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 8 Jul 2021 18:44:53 +0530 Subject: [PATCH 025/369] fix: sibling group expansion not working for root nodes --- .../hierarchy_chart/hierarchy_chart_mobile.js | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 19852993789..d48b4c8f362 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -88,16 +88,7 @@ erpnext.HierarchyChartMobile = class { me.$sibling_group = $(`
                      `); me.page.main.append(me.$sibling_group); - if (me.$hierarchy) - me.$hierarchy.remove(); - - // setup hierarchy - me.$hierarchy = $( - `
                        -
                      • -
                      `); - - me.page.main.append(me.$hierarchy); + me.setup_hierarchy() me.render_root_nodes(); } } @@ -132,6 +123,19 @@ erpnext.HierarchyChartMobile = class { `); } + setup_hierarchy() { + $(`#connectors`).empty(); + if (this.$hierarchy) + this.$hierarchy.remove(); + + this.$hierarchy = $( + `
                        +
                      • +
                      `); + + this.page.main.append(this.$hierarchy); + } + render_root_nodes() { let me = this; @@ -142,10 +146,13 @@ erpnext.HierarchyChartMobile = class { }, }).then(r => { if (r.message.length) { + let root_level = me.$hierarchy.find('.root-level'); + root_level.empty(); + $.each(r.message, (_i, data) => { return new me.Node({ id: data.id, - parent: me.$hierarchy.find('.root-level'), + parent: root_level, parent_id: undefined, image: data.image, name: data.name, @@ -401,7 +408,12 @@ erpnext.HierarchyChartMobile = class { $('.node-group').on('click', function() { let parent = $(this).attr('data-parent'); - me.expand_sibling_group_node(parent); + if (parent === 'undefined') { + me.setup_hierarchy(); + me.render_root_nodes(); + } else { + me.expand_sibling_group_node(parent); + } }); } From 24b31c0bf93a53c4f98fe5020b9575288b2a9aab Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 9 Jul 2021 01:03:02 +0530 Subject: [PATCH 026/369] fix(mobile): collapsed nodes not expanding --- .../hierarchy_chart/hierarchy_chart_mobile.js | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index d48b4c8f362..58530eaaf91 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -88,7 +88,7 @@ erpnext.HierarchyChartMobile = class { me.$sibling_group = $(`
                      `); me.page.main.append(me.$sibling_group); - me.setup_hierarchy() + me.setup_hierarchy(); me.render_root_nodes(); } } @@ -194,9 +194,9 @@ erpnext.HierarchyChartMobile = class { collapse_node() { let node = this.selected_node; - if (node.expandable) { + if (node.expandable && node.$children) { node.$children.hide(); - node.expanded = false; + node.expanded = 0; // add a collapsed level to show the collapsed parent // and a button beside it to move to that level @@ -212,10 +212,7 @@ erpnext.HierarchyChartMobile = class { frappe.run_serially([ () => this.get_child_nodes(node.parent_id, node.id), (child_nodes) => this.get_node_group(child_nodes, node.parent_id), - (node_group) => { - node_parent.find('.collapsed-level') - .append(node_group); - }, + (node_group) => node_parent.find('.collapsed-level').append(node_group), () => this.setup_node_group_action() ]); } @@ -268,7 +265,7 @@ erpnext.HierarchyChartMobile = class { } node.$children.show(); - node.expanded = true; + node.expanded = 1; } add_node(node, data) { @@ -380,13 +377,16 @@ erpnext.HierarchyChartMobile = class { node_element.click(function() { if (node.is_root) { + var el = $(this).detach(); me.$hierarchy.empty(); - me.add_node_to_hierarchy(node, true); + $(`#connectors`).empty(); + me.add_node_to_hierarchy(el, node); } else if (node_element.is(':visible') && node_element.hasClass('active-path')) { me.remove_levels_after_node(node); me.remove_orphaned_connectors(); } else { - me.add_node_to_hierarchy(node, true); + var el = $(this).detach(); + me.add_node_to_hierarchy(el, node); me.collapse_node(); } @@ -417,15 +417,15 @@ erpnext.HierarchyChartMobile = class { }); } - add_node_to_hierarchy(node) { - this.$hierarchy.append(` -
                    • -
                      -
                      -
                    • - `); + add_node_to_hierarchy(node_element, node) { + this.$hierarchy.append(`
                    • `); + node_element.removeClass('active-child active-path'); + this.$hierarchy.find('.level:last').append(node_element); - node.$link.appendTo(this.$hierarchy.find('.level:last')); + let node_object = this.nodes[node.id]; + node_object.expanded = 0; + node_object.$children = undefined; + this.nodes[node.id] = node_object; } get_node_group(nodes, parent, collapsed=true) { @@ -478,9 +478,11 @@ erpnext.HierarchyChartMobile = class { expand_sibling_group_node(parent) { let node_object = this.nodes[parent]; let node = node_object.$link; + node.removeClass('active-child active-path'); node_object.expanded = 0; node_object.$children = undefined; + this.nodes[node.id] = node_object; // show parent's siblings and expand parent node frappe.run_serially([ @@ -491,17 +493,21 @@ erpnext.HierarchyChartMobile = class { this.$sibling_group.empty().append(node_group); }, () => this.setup_node_group_action(), - () => { - this.$hierarchy.empty().append(` -
                    • - `); - this.$hierarchy.find('.level').append(node); - $(`#connectors`).empty(); - this.expand_node(node_object); - } + () => this.reattach_and_expand_node(node, node_object) ]); } + reattach_and_expand_node(node, node_object) { + var el = node.detach(); + + this.$hierarchy.empty().append(` +
                    • + `); + this.$hierarchy.find('.level').append(el); + $(`#connectors`).empty(); + this.expand_node(node_object); + } + remove_levels_after_node(node) { let level = $(`#${node.id}`).parent().parent().index(); From 4582f28d0d072feddc7cbfcab4aac063fdfaad36 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 9 Jul 2021 01:25:26 +0530 Subject: [PATCH 027/369] fix: sider --- erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 58530eaaf91..5a6f1688767 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -376,8 +376,9 @@ erpnext.HierarchyChartMobile = class { let node_element = $(`#${node.id}`); node_element.click(function() { + let el = $(this).detach(); + if (node.is_root) { - var el = $(this).detach(); me.$hierarchy.empty(); $(`#connectors`).empty(); me.add_node_to_hierarchy(el, node); @@ -385,7 +386,6 @@ erpnext.HierarchyChartMobile = class { me.remove_levels_after_node(node); me.remove_orphaned_connectors(); } else { - var el = $(this).detach(); me.add_node_to_hierarchy(el, node); me.collapse_node(); } From 9e26f2d797ee17b332463e9aace65b516c0c2deb Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 9 Jul 2021 22:08:56 +0530 Subject: [PATCH 028/369] fix: Organize buttons --- erpnext/assets/doctype/asset/asset.js | 67 ++++++++++++------- erpnext/assets/doctype/asset/asset.py | 11 ++- .../doctype/asset_repair/asset_repair.json | 42 +++++------- 3 files changed, 70 insertions(+), 50 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 6f1bb28f370..2a57183a802 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -82,24 +82,46 @@ frappe.ui.form.on('Asset', { if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) { frm.add_custom_button("Transfer Asset", function() { erpnext.asset.transfer_asset(frm); - }); + }, __("Manage")); frm.add_custom_button("Scrap Asset", function() { erpnext.asset.scrap_asset(frm); - }); + }, __("Manage")); frm.add_custom_button("Sell Asset", function() { frm.trigger("make_sales_invoice"); - }); + }, __("Manage")); } else if (frm.doc.status=='Scrapped') { frm.add_custom_button("Restore Asset", function() { erpnext.asset.restore_asset(frm); - }); + }, __("Manage")); + } + + if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) { + frm.add_custom_button(__("Maintain Asset"), function() { + frm.trigger("create_asset_maintenance"); + }, __("Manage")); + } + + frm.add_custom_button(__("Repair Asset"), function() { + frm.trigger("create_asset_repair"); + }, __("Manage")); + + if (frm.doc.status != 'Fully Depreciated') { + frm.add_custom_button(__("Adjust Asset Value"), function() { + frm.trigger("create_asset_adjustment"); + }, __("Manage")); + } + + if (!frm.doc.calculate_depreciation) { + frm.add_custom_button(__("Create Depreciation Entry"), function() { + frm.trigger("make_journal_entry"); + }, __("Manage")); } if (frm.doc.purchase_receipt || !frm.doc.is_existing_asset) { - frm.add_custom_button("General Ledger", function() { + frm.add_custom_button("View General Ledger", function() { frappe.route_options = { "voucher_no": frm.doc.name, "from_date": frm.doc.available_for_use_date, @@ -107,27 +129,10 @@ frappe.ui.form.on('Asset', { "company": frm.doc.company }; frappe.set_route("query-report", "General Ledger"); - }); + }, __("Manage")); } - if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) { - frm.add_custom_button(__("Asset Maintenance"), function() { - frm.trigger("create_asset_maintenance"); - }, __('Create')); - } - if (frm.doc.status != 'Fully Depreciated') { - frm.add_custom_button(__("Asset Value Adjustment"), function() { - frm.trigger("create_asset_adjustment"); - }, __('Create')); - } - - if (!frm.doc.calculate_depreciation) { - frm.add_custom_button(__("Depreciation Entry"), function() { - frm.trigger("make_journal_entry"); - }, __('Create')); - } - - frm.page.set_inner_btn_group_as_primary(__('Create')); + frm.page.set_inner_btn_group_as_primary(__("Manage")); frm.trigger("setup_chart"); } @@ -304,6 +309,20 @@ frappe.ui.form.on('Asset', { }) }, + create_asset_repair: function(frm) { + frappe.call({ + args: { + "asset": frm.doc.name, + "asset_name": frm.doc.asset_name + }, + method: "erpnext.assets.doctype.asset.asset.create_asset_repair", + callback: function(r) { + var doclist = frappe.model.sync(r.message); + frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + } + }) + }, + create_asset_adjustment: function(frm) { frappe.call({ args: { diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 8799275fc4e..456649fa07e 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -625,9 +625,18 @@ def create_asset_maintenance(asset, item_code, item_name, asset_category, compan }) return asset_maintenance +@frappe.whitelist() +def create_asset_repair(asset, asset_name): + asset_repair = frappe.new_doc("Asset Repair") + asset_repair.update({ + "asset": asset, + "asset_name": asset_name + }) + return asset_repair + @frappe.whitelist() def create_asset_adjustment(asset, asset_category, company): - asset_maintenance = frappe.new_doc("Asset Value Adjustment") + asset_maintenance = frappe.get_doc("Asset Value Adjustment") asset_maintenance.update({ "asset": asset, "company": company, diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index d338fc0fb79..853534eb319 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -8,10 +8,9 @@ "engine": "InnoDB", "field_order": [ "naming_series", - "asset_name", "column_break_2", - "item_code", - "item_name", + "asset", + "asset_name", "section_break_5", "failure_date", "assign_to", @@ -30,15 +29,6 @@ "amended_from" ], "fields": [ - { - "columns": 1, - "fieldname": "asset_name", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Asset", - "options": "Asset", - "reqd": 1 - }, { "fieldname": "naming_series", "fieldtype": "Select", @@ -50,18 +40,6 @@ "fieldname": "column_break_2", "fieldtype": "Column Break" }, - { - "fetch_from": "asset_name.item_code", - "fieldname": "item_code", - "fieldtype": "Read Only", - "label": "Item Code" - }, - { - "fetch_from": "asset_name.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "label": "Item Name" - }, { "fieldname": "section_break_5", "fieldtype": "Section Break", @@ -159,12 +137,26 @@ "options": "Asset Repair", "print_hide": 1, "read_only": 1 + }, + { + "columns": 1, + "fieldname": "asset", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset", + "options": "Asset", + "reqd": 1 + }, + { + "fieldname": "asset_name", + "fieldtype": "Read Only", + "label": "Asset Name" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-01-22 15:08:12.495850", + "modified": "2021-05-10 22:48:42.165513", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", From 58bc967073255a6e28eac76d4776810553ea0aa1 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 9 Jul 2021 22:10:35 +0530 Subject: [PATCH 029/369] fix: Rename 'Fixed Asset Depreciation Settings' to 'Fixed Asset Deafults' --- .../doctype/asset_repair/asset_repair.js | 4 ++++ .../doctype/asset_repair/asset_repair.json | 22 +++++++++++++++++-- .../doctype/asset_repair/asset_repair.py | 4 ++++ erpnext/setup/doctype/company/company.json | 16 +++++++------- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index 4ba2b4474a9..f5eeeda5fb1 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -2,6 +2,10 @@ // For license information, please see license.txt frappe.ui.form.on('Asset Repair', { + refresh: function(frm) { + frm.toggle_display(['completion_date', 'repair_status'], !(frm.doc.__islocal)); + }, + repair_status: (frm) => { if (frm.doc.completion_date && frm.doc.repair_status == "Completed") { frappe.call ({ diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 853534eb319..4ed9916392a 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -7,9 +7,9 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "asset", "naming_series", "column_break_2", - "asset", "asset_name", "section_break_5", "failure_date", @@ -18,7 +18,10 @@ "column_break_6", "completion_date", "repair_status", + "section_break_7", "repair_cost", + "column_break_8", + "payable_account", "section_break_9", "description", "column_break_9", @@ -151,12 +154,27 @@ "fieldname": "asset_name", "fieldtype": "Read Only", "label": "Asset Name" + }, + { + "fieldname": "payable_account", + "fieldtype": "Link", + "label": "Payable Account", + "options": "Account" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break", + "label": "Accounting Details" + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-10 22:48:42.165513", + "modified": "2021-05-11 05:11:58.330860", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 049b931b5e8..884dc195882 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -13,6 +13,10 @@ class AssetRepair(Document): if self.repair_status == "Completed" and not self.completion_date: frappe.throw(_("Please select Completion Date for Completed Repair")) + if self.repair_status == 'Pending': + frappe.db.set_value('Asset', self.asset, 'status', 'Out of Order') + else: + frappe.db.set_value('Asset', self.asset, 'status', 'Submitted') @frappe.whitelist() def get_downtime(failure_date, completion_date): diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 061986d92d7..2d2f336e932 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -74,7 +74,7 @@ "stock_received_but_not_billed", "service_received_but_not_billed", "expenses_included_in_valuation", - "fixed_asset_depreciation_settings", + "fixed_asset_defaults", "accumulated_depreciation_account", "depreciation_expense_account", "series_for_depreciation_entry", @@ -519,12 +519,6 @@ "no_copy": 1, "options": "Account" }, - { - "collapsible": 1, - "fieldname": "fixed_asset_depreciation_settings", - "fieldtype": "Section Break", - "label": "Fixed Asset Depreciation Settings" - }, { "fieldname": "accumulated_depreciation_account", "fieldtype": "Link", @@ -734,6 +728,12 @@ "fieldtype": "Link", "label": "Default Payment Discount Account", "options": "Account" + }, + { + "collapsible": 1, + "fieldname": "fixed_asset_defaults", + "fieldtype": "Section Break", + "label": "Fixed Asset Defaults" } ], "icon": "fa fa-building", @@ -741,7 +741,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2021-05-07 03:11:28.189740", + "modified": "2021-05-11 21:45:22.803065", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 42c70fba3c7c645b308d1f6f84123037a8466712 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 9 Jul 2021 22:11:50 +0530 Subject: [PATCH 030/369] fix: Modify depreciation schedule when increase_in_asset_life is not a multiple of frequency_of_depreciation) --- erpnext/assets/doctype/asset/asset.js | 1 - erpnext/assets/doctype/asset/asset.json | 23 ++- erpnext/assets/doctype/asset/asset.py | 31 +++- .../asset_maintenance/asset_maintenance.js | 3 + .../asset_maintenance/asset_maintenance.json | 31 +++- .../asset_maintenance/asset_maintenance.py | 37 ++++- .../doctype/asset_repair/asset_repair.js | 11 +- .../doctype/asset_repair/asset_repair.json | 127 ++++++++++---- .../doctype/asset_repair/asset_repair.py | 157 +++++++++++++++++- erpnext/assets/doctype/stock_item/__init__.py | 0 .../assets/doctype/stock_item/stock_item.json | 55 ++++++ .../assets/doctype/stock_item/stock_item.py | 8 + erpnext/setup/doctype/company/company.json | 9 +- 13 files changed, 444 insertions(+), 49 deletions(-) create mode 100644 erpnext/assets/doctype/stock_item/__init__.py create mode 100644 erpnext/assets/doctype/stock_item/stock_item.json create mode 100644 erpnext/assets/doctype/stock_item/stock_item.py diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 2a57183a802..1e67ec816b6 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -132,7 +132,6 @@ frappe.ui.form.on('Asset', { }, __("Manage")); } - frm.page.set_inner_btn_group_as_primary(__("Manage")); frm.trigger("setup_chart"); } diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 421b9a6c378..8a0e3ad2a66 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -23,6 +23,7 @@ "asset_name", "asset_category", "location", + "asset_value", "custodian", "department", "disposal_date", @@ -53,6 +54,8 @@ "next_depreciation_date", "section_break_14", "schedules", + "to_date", + "edit_dates", "insurance_details", "policy_number", "insurer", @@ -480,6 +483,24 @@ "fieldname": "section_break_36", "fieldtype": "Section Break", "label": "Finance Books" + }, + { + "fieldname": "asset_value", + "fieldtype": "Currency", + "label": "Asset Value", + "read_only": 1 + }, + { + "fieldname": "to_date", + "fieldtype": "Date", + "hidden": 1, + "label": "To Date" + }, + { + "fieldname": "edit_dates", + "fieldtype": "Data", + "hidden": 1, + "label": "Edit Dates" } ], "idx": 72, @@ -502,7 +523,7 @@ "link_fieldname": "asset" } ], - "modified": "2021-01-22 12:38:59.091510", + "modified": "2021-05-21 12:05:29.424083", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 456649fa07e..e8cfe0ae17f 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -96,6 +96,9 @@ class Asset(AccountsController): finance_books = get_item_details(self.item_code, self.asset_category) self.set('finance_books', finance_books) + if not(self.asset_value): + self.asset_value = self.gross_purchase_amount + def validate_asset_values(self): if not self.asset_category: self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category") @@ -168,15 +171,23 @@ class Asset(AccountsController): d.precision("rate_of_depreciation")) def make_depreciation_schedule(self): - if 'Manual' not in [d.depreciation_method for d in self.finance_books]: + if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.schedules: self.schedules = [] - if self.get("schedules") or not self.available_for_use_date: + if not self.available_for_use_date: return for d in self.get('finance_books'): self.validate_asset_finance_books(d) + start = 0 + for n in range (len(self.schedules)): + if not self.schedules[n].journal_entry: + print("*"*100) + del self.schedules[n:] + start = n + break + value_after_depreciation = (flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)) @@ -189,9 +200,9 @@ class Asset(AccountsController): if has_pro_rata: number_of_pending_depreciations += 1 - + skip_row = False - for n in range(number_of_pending_depreciations): + for n in range(start, number_of_pending_depreciations): # If depreciation is already completed (for double declining balance) if skip_row: continue @@ -216,11 +227,12 @@ class Asset(AccountsController): # For last row elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1: - to_date = add_months(self.available_for_use_date, - n * cint(d.frequency_of_depreciation)) + if not self.edit_dates: + self.to_date = add_months(self.available_for_use_date, + n * cint(d.frequency_of_depreciation)) - depreciation_amount, days, months = self.get_pro_rata_amt(d, - depreciation_amount, schedule_date, to_date) + depreciation_amount, days, months = get_pro_rata_amt(d, + depreciation_amount, schedule_date, self.to_date) monthly_schedule_date = add_months(schedule_date, 1) @@ -346,11 +358,12 @@ class Asset(AccountsController): if d.finance_book_id not in finance_books: accumulated_depreciation = flt(self.opening_accumulated_depreciation) value_after_depreciation = flt(self.get_value_after_depreciation(d.finance_book_id)) - finance_books.append(d.finance_book_id) + finance_books.append(int(d.finance_book_id)) depreciation_amount = flt(d.depreciation_amount, d.precision("depreciation_amount")) value_after_depreciation -= flt(depreciation_amount) + # for the last row, if depreciation method = Straight Line if straight_line_idx and i == max(straight_line_idx) - 1: book = self.get('finance_books')[cint(d.finance_book_id) - 1] depreciation_amount += flt(value_after_depreciation - diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js index 70b8654509f..3830d1168c4 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js @@ -30,7 +30,10 @@ frappe.ui.form.on('Asset Maintenance', { if(!frm.is_new()) { frm.trigger('make_dashboard'); } + + frm.toggle_display(['stock_consumption_details_section'], frm.doc.stock_consumption) }, + make_dashboard: (frm) => { if(!frm.is_new()) { frappe.call({ diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json index c0c2566fe23..da2fd754512 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json @@ -12,13 +12,17 @@ "column_break_3", "item_code", "item_name", + "stock_consumption", "section_break_6", "maintenance_team", "column_break_9", "maintenance_manager", "maintenance_manager_name", "section_break_8", - "asset_maintenance_tasks" + "asset_maintenance_tasks", + "stock_consumption_details_section", + "warehouse", + "stock_items" ], "fields": [ { @@ -100,10 +104,33 @@ "label": "Maintenance Tasks", "options": "Asset Maintenance Task", "reqd": 1 + }, + { + "default": "0", + "fieldname": "stock_consumption", + "fieldtype": "Check", + "label": "Stock Consumed During Maintenance" + }, + { + "fieldname": "stock_consumption_details_section", + "fieldtype": "Section Break", + "label": "Stock Consumption Details" + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse" + }, + { + "fieldname": "stock_items", + "fieldtype": "Table", + "label": "Stock Items", + "options": "Stock Item" } ], "links": [], - "modified": "2020-05-28 20:28:32.993823", + "modified": "2021-05-13 05:24:58.480132", "modified_by": "Administrator", "module": "Assets", "name": "Asset Maintenance", diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index a506deec93e..e3e654c398b 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -19,10 +19,45 @@ class AssetMaintenance(Document): if not task.assign_to and self.docstatus == 0: throw(_("Row #{}: Please asign task to a member.").format(task.idx)) + if self.stock_consumption: + self.check_for_stock_items_and_warehouse() + self.increase_asset_value() + self.decrease_stock_quantity() + def on_update(self): for task in self.get('asset_maintenance_tasks'): assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date) - self.sync_maintenance_tasks() + self.sync_maintenance_tasks() + + def check_for_stock_items_and_warehouse(self): + if self.stock_consumption: + if not self.stock_items: + frappe.throw(_("Please enter Stock Items consumed during Asset Maintenance.")) + if not self.warehouse: + frappe.throw(_("Please enter Warehouse from which Stock Items consumed during Asset Maintenance were taken.")) + + def increase_asset_value(self): + asset_value = frappe.db.get_value('Asset', self.asset_name, 'asset_value') + for item in self.stock_items: + asset_value += item.total_value + + frappe.db.set_value('Asset', self.asset_name, 'asset_value', asset_value) + + def decrease_stock_quantity(self): + stock_entry = frappe.get_doc({ + "doctype": "Stock Entry", + "stock_entry_type": "Material Issue" + }) + + for stock_item in self.stock_items: + stock_entry.append('items', { + "s_warehouse": self.warehouse, + "item_code": stock_item.item, + "qty": stock_item.consumed_quantity + }) + + stock_entry.insert() + stock_entry.submit() def sync_maintenance_tasks(self): tasks_names = [] diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index f5eeeda5fb1..7633a595a2f 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -3,7 +3,16 @@ frappe.ui.form.on('Asset Repair', { refresh: function(frm) { - frm.toggle_display(['completion_date', 'repair_status'], !(frm.doc.__islocal)); + frm.toggle_display(['completion_date', 'repair_status', 'accounting_details', 'accounting_dimensions_section'], !(frm.doc.__islocal)); + + if (frm.doc.docstatus) { + frm.add_custom_button("View General Ledger", function() { + frappe.route_options = { + "voucher_no": frm.doc.name + }; + frappe.set_route("query-report", "General Ledger"); + }); + } }, repair_status: (frm) => { diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 4ed9916392a..522f2874d99 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -13,20 +13,30 @@ "asset_name", "section_break_5", "failure_date", - "assign_to", - "assign_to_name", + "repair_status", "column_break_6", "completion_date", - "repair_status", - "section_break_7", + "accounting_dimensions_section", + "cost_center", + "column_break_14", + "project", + "accounting_details", "repair_cost", + "capitalize_repair_cost", + "stock_consumption", "column_break_8", - "payable_account", + "total_repair_cost", + "purchase_invoice", + "stock_consumption_details_section", + "warehouse", + "stock_items", + "asset_depreciation_details_section", + "increase_in_asset_life", "section_break_9", "description", "column_break_9", "actions_performed", - "section_break_17", + "section_break_23", "downtime", "column_break_19", "amended_from" @@ -55,20 +65,6 @@ "label": "Failure Date", "reqd": 1 }, - { - "allow_on_submit": 1, - "fieldname": "assign_to", - "fieldtype": "Link", - "label": "Assign To", - "options": "User" - }, - { - "allow_on_submit": 1, - "fetch_from": "assign_to.full_name", - "fieldname": "assign_to_name", - "fieldtype": "Read Only", - "label": "Assign To Name" - }, { "fieldname": "column_break_6", "fieldtype": "Column Break" @@ -110,10 +106,6 @@ "fieldtype": "Long Text", "label": "Actions performed" }, - { - "fieldname": "section_break_17", - "fieldtype": "Section Break" - }, { "allow_on_submit": 1, "fieldname": "downtime", @@ -151,30 +143,103 @@ "reqd": 1 }, { + "fetch_from": "asset.asset_name", "fieldname": "asset_name", "fieldtype": "Read Only", "label": "Asset Name" }, { - "fieldname": "payable_account", - "fieldtype": "Link", - "label": "Payable Account", - "options": "Account" + "fieldname": "column_break_8", + "fieldtype": "Column Break" }, { - "fieldname": "section_break_7", + "default": "0", + "fieldname": "capitalize_repair_cost", + "fieldtype": "Check", + "label": "Capitalize Repair Cost" + }, + { + "fieldname": "accounting_details", "fieldtype": "Section Break", "label": "Accounting Details" }, { - "fieldname": "column_break_8", + "fieldname": "stock_items", + "fieldtype": "Table", + "label": "Stock Items", + "options": "Stock Item" + }, + { + "fieldname": "section_break_23", + "fieldtype": "Section Break" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "fieldname": "column_break_14", "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "stock_consumption", + "fieldtype": "Check", + "label": "Stock Consumed During Repair" + }, + { + "depends_on": "stock_consumption", + "fieldname": "stock_consumption_details_section", + "fieldtype": "Section Break", + "label": "Stock Consumption Details" + }, + { + "depends_on": "stock_consumption", + "fieldname": "total_repair_cost", + "fieldtype": "Currency", + "label": "Total Repair Cost" + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse" + }, + { + "depends_on": "capitalize_repair_cost", + "fieldname": "asset_depreciation_details_section", + "fieldtype": "Section Break", + "label": "Asset Depreciation Details" + }, + { + "fieldname": "increase_in_asset_life", + "fieldtype": "Int", + "label": "Increase In Asset Life(Months)" + }, + { + "fieldname": "purchase_invoice", + "fieldtype": "Link", + "label": "Purchase Invoice", + "options": "Purchase Invoice" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-11 05:11:58.330860", + "modified": "2021-05-21 10:37:35.002238", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 884dc195882..8fd019febd0 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -5,19 +5,172 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import time_diff_in_hours +from frappe.utils import time_diff_in_hours, getdate, add_days, date_diff, add_months, flt, cint from frappe.model.document import Document +from erpnext.accounts.general_ledger import make_gl_entries class AssetRepair(Document): def validate(self): if self.repair_status == "Completed" and not self.completion_date: frappe.throw(_("Please select Completion Date for Completed Repair")) + self.update_status() + self.set_total_value() # change later + self.calculate_total_repair_cost() + + def update_status(self): if self.repair_status == 'Pending': frappe.db.set_value('Asset', self.asset, 'status', 'Out of Order') else: - frappe.db.set_value('Asset', self.asset, 'status', 'Submitted') + asset = frappe.get_doc('Asset', self.asset) + asset.set_status() + def set_total_value(self): + for item in self.stock_items: + item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity) + + def calculate_total_repair_cost(self): + self.total_repair_cost = self.repair_cost + if self.stock_consumption: + for item in self.stock_items: + self.total_repair_cost += item.total_value + + def on_submit(self): + self.check_repair_status() + self.check_for_cost_center() + + if self.stock_consumption or self.capitalize_repair_cost: + self.increase_asset_value() + if self.stock_consumption: + self.check_for_stock_items_and_warehouse() + self.decrease_stock_quantity() + if self.capitalize_repair_cost: + self.check_for_purchase_invoice() + self.make_gl_entries() + self.modify_depreciation_schedule() + + def check_repair_status(self): + if self.repair_status == "Pending": + frappe.throw(_("Please update Repair Status.")) + + def check_for_stock_items_and_warehouse(self): + if not self.stock_items: + frappe.throw(_("Please enter Stock Items consumed during Asset Repair.")) + if not self.warehouse: + frappe.throw(_("Please enter Warehouse from which Stock Items consumed during Asset Repair were taken.")) + + def check_for_cost_center(self): + if not self.cost_center: + frappe.throw(_("Please enter Cost Center.")) + + def increase_asset_value(self): + asset_value = frappe.db.get_value('Asset', self.asset, 'asset_value') + for item in self.stock_items: + asset_value += item.total_value + + if self.capitalize_repair_cost: + asset_value += self.repair_cost + frappe.db.set_value('Asset', self.asset, 'asset_value', asset_value) + + def decrease_stock_quantity(self): + stock_entry = frappe.get_doc({ + "doctype": "Stock Entry", + "stock_entry_type": "Material Issue" + }) + + for stock_item in self.stock_items: + stock_entry.append('items', { + "s_warehouse": self.warehouse, + "item_code": stock_item.item, + "qty": stock_item.consumed_quantity + }) + + stock_entry.insert() + stock_entry.submit() + + def check_for_purchase_invoice(self): + if not self.purchase_invoice: + frappe.throw(_("Please link Purchase Invoice.")) + + def on_cancel(self): + self.make_gl_entries(cancel=True) + + def make_gl_entries(self, cancel=False): + if flt(self.repair_cost) > 0: + gl_entries = self.get_gl_entries() + make_gl_entries(gl_entries, cancel) + + def get_gl_entries(self): + gl_entry = [] + company = frappe.db.get_value('Asset', self.asset, 'company') + repair_and_maintenance_account = frappe.db.get_value('Company', company, 'repair_and_maintenance_account') + fixed_asset_account = self.get_fixed_asset_account() + expense_account = frappe.get_doc('Purchase Invoice', self.purchase_invoice).items[0].expense_account + + gl_entry = frappe.get_doc({ + "doctype": "GL Entry", + "account": expense_account, + "credit": self.total_repair_cost, + "credit_in_account_currency": self.total_repair_cost, + "against": repair_and_maintenance_account, + "voucher_type": self.doctype, + "voucher_no": self.name, + "cost_center": self.cost_center, + "posting_date": getdate() + }) + gl_entry.insert() + gl_entry = frappe.get_doc({ + "doctype": "GL Entry", + "account": fixed_asset_account, + "debit": self.total_repair_cost, + "debit_in_account_currency": self.total_repair_cost, + "against": expense_account, + "voucher_type": self.doctype, + "voucher_no": self.name, + "cost_center": self.cost_center, + "posting_date": getdate(), + "against_voucher_type": "Purchase Invoice", + "against_voucher": self.purchase_invoice + }) + gl_entry.insert() + + def get_fixed_asset_account(self): + asset_category = frappe.get_doc('Asset Category', frappe.db.get_value('Asset', self.asset, 'asset_category')) + company = frappe.db.get_value('Asset', self.asset, 'company') + for account in asset_category.accounts: + if account.company_name == company: + return account.fixed_asset_account + + def modify_depreciation_schedule(self): + if self.increase_in_asset_life: + asset = frappe.get_doc('Asset', self.asset) + asset.flags.ignore_validate_update_after_submit = True + for row in asset.finance_books: + row.total_number_of_depreciations += self.increase_in_asset_life/row.frequency_of_depreciation + + asset.edit_dates = "" + extra_months = self.increase_in_asset_life % row.frequency_of_depreciation + if extra_months != 0: + self.calculate_last_schedule_date(asset, row, extra_months) + # fix depreciation amount + + asset.prepare_depreciation_data() + asset.save() + + # to help modify depreciation schedule when increase_in_asset_life is not a multiple of frequency_of_depreciation + def calculate_last_schedule_date(self, asset, row, extra_months): + asset.edit_dates = "Don't Edit" + number_of_pending_depreciations = cint(row.total_number_of_depreciations) - \ + cint(asset.number_of_depreciations_booked) + last_schedule_date = asset.schedules[len(asset.schedules)-1].schedule_date + asset.to_date = add_months(last_schedule_date, extra_months) + schedule_date = add_months(row.depreciation_start_date, + number_of_pending_depreciations * cint(row.frequency_of_depreciation)) + + if asset.to_date > schedule_date: + row.total_number_of_depreciations += 1 + + @frappe.whitelist() def get_downtime(failure_date, completion_date): downtime = time_diff_in_hours(completion_date, failure_date) diff --git a/erpnext/assets/doctype/stock_item/__init__.py b/erpnext/assets/doctype/stock_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/assets/doctype/stock_item/stock_item.json b/erpnext/assets/doctype/stock_item/stock_item.json new file mode 100644 index 00000000000..b1f05db3950 --- /dev/null +++ b/erpnext/assets/doctype/stock_item/stock_item.json @@ -0,0 +1,55 @@ +{ + "actions": [], + "creation": "2021-05-12 02:41:54.161024", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item", + "valuation_rate", + "consumed_quantity", + "total_value" + ], + "fields": [ + { + "fieldname": "item", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item", + "options": "Item" + }, + { + "fetch_from": "item.valuation_rate", + "fieldname": "valuation_rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Valuation Rate", + "read_only": 1 + }, + { + "fieldname": "consumed_quantity", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Consumed Quantity" + }, + { + "fieldname": "total_value", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Value", + "read_only": 1 + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-12 03:19:55.006300", + "modified_by": "Administrator", + "module": "Assets", + "name": "Stock Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/assets/doctype/stock_item/stock_item.py b/erpnext/assets/doctype/stock_item/stock_item.py new file mode 100644 index 00000000000..0e3cc3f8ba7 --- /dev/null +++ b/erpnext/assets/doctype/stock_item/stock_item.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class StockItem(Document): + pass diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 2d2f336e932..e6ec496a65e 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -83,6 +83,7 @@ "disposal_account", "depreciation_cost_center", "capital_work_in_progress_account", + "repair_and_maintenance_account", "asset_received_but_not_billed", "budget_detail", "exception_budget_approver_role", @@ -734,6 +735,12 @@ "fieldname": "fixed_asset_defaults", "fieldtype": "Section Break", "label": "Fixed Asset Defaults" + }, + { + "fieldname": "repair_and_maintenance_account", + "fieldtype": "Link", + "label": "Repair and Maintenance Account", + "options": "Account" } ], "icon": "fa fa-building", @@ -741,7 +748,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2021-05-11 21:45:22.803065", + "modified": "2021-05-12 16:51:08.187233", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 4e284433d17e7830c8c4a33885c2d82db689b926 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 9 Jul 2021 22:12:46 +0530 Subject: [PATCH 031/369] fix: Fix depreciation_amount calculation --- erpnext/assets/doctype/asset/asset.py | 15 +++++++++------ .../assets/doctype/asset_repair/asset_repair.py | 1 - 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index e8cfe0ae17f..3bd20023abf 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -183,13 +183,12 @@ class Asset(AccountsController): start = 0 for n in range (len(self.schedules)): if not self.schedules[n].journal_entry: - print("*"*100) del self.schedules[n:] start = n break - value_after_depreciation = (flt(self.gross_purchase_amount) - - flt(self.opening_accumulated_depreciation)) + value_after_depreciation = (flt(self.asset_value) - + flt(self.opening_accumulated_depreciation)) - flt(d.expected_value_after_useful_life) d.value_after_depreciation = value_after_depreciation @@ -779,9 +778,13 @@ def get_depreciation_amount(asset, depreciable_value, row): depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) if row.depreciation_method in ("Straight Line", "Manual"): - depreciation_amount = (flt(row.value_after_depreciation) - - flt(row.expected_value_after_useful_life)) / depreciation_left + if not asset.to_date: + depreciation_amount = (flt(row.value_after_depreciation) - + flt(row.expected_value_after_useful_life)) / depreciation_left + else: + depreciation_amount = (flt(row.value_after_depreciation) - + flt(row.expected_value_after_useful_life)) / (date_diff(asset.to_date, asset.available_for_use_date) / 365) else: depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100)) - return depreciation_amount \ No newline at end of file + return depreciation_amount diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 8fd019febd0..9973afd80ad 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -152,7 +152,6 @@ class AssetRepair(Document): extra_months = self.increase_in_asset_life % row.frequency_of_depreciation if extra_months != 0: self.calculate_last_schedule_date(asset, row, extra_months) - # fix depreciation amount asset.prepare_depreciation_data() asset.save() From 97193a46324dd0c10650e4f51111eea323555602 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 9 Jul 2021 22:15:10 +0530 Subject: [PATCH 032/369] fix: Sider issues --- erpnext/assets/doctype/asset/asset.js | 2 +- erpnext/assets/doctype/asset/asset.py | 2 +- erpnext/assets/doctype/asset_maintenance/asset_maintenance.js | 2 +- erpnext/assets/doctype/asset_repair/asset_repair.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 1e67ec816b6..922cc4a7b26 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -319,7 +319,7 @@ frappe.ui.form.on('Asset', { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); } - }) + }); }, create_asset_adjustment: function(frm) { diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 3bd20023abf..b3d3a198c33 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -181,7 +181,7 @@ class Asset(AccountsController): self.validate_asset_finance_books(d) start = 0 - for n in range (len(self.schedules)): + for n in range(len(self.schedules)): if not self.schedules[n].journal_entry: del self.schedules[n:] start = n diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js index 3830d1168c4..19393b7e9db 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js @@ -31,7 +31,7 @@ frappe.ui.form.on('Asset Maintenance', { frm.trigger('make_dashboard'); } - frm.toggle_display(['stock_consumption_details_section'], frm.doc.stock_consumption) + frm.toggle_display(['stock_consumption_details_section'], frm.doc.stock_consumption); }, make_dashboard: (frm) => { diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 9973afd80ad..3e81ba55b00 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import time_diff_in_hours, getdate, add_days, date_diff, add_months, flt, cint +from frappe.utils import time_diff_in_hours, getdate, add_months, flt, cint from frappe.model.document import Document from erpnext.accounts.general_ledger import make_gl_entries From 794807ecc3bb709a0cfdb0bb5ee7aa50c0399698 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 3 Jun 2021 04:55:49 +0530 Subject: [PATCH 033/369] fix(Asset Repair): Only modify depreciation schedule if calculate_depreciation is checked --- erpnext/assets/doctype/asset_repair/asset_repair.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 3e81ba55b00..e3093df7fb0 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -47,7 +47,8 @@ class AssetRepair(Document): if self.capitalize_repair_cost: self.check_for_purchase_invoice() self.make_gl_entries() - self.modify_depreciation_schedule() + if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation'): + self.modify_depreciation_schedule() def check_repair_status(self): if self.repair_status == "Pending": From c6ed66ec5b54b96a455452f33c927989fb9ab586 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 3 Jun 2021 21:07:07 +0530 Subject: [PATCH 034/369] fix(Asset Repair): Remove unnecessary condition --- erpnext/assets/doctype/asset_repair/asset_repair.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index e3093df7fb0..070e7b0a9b8 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -72,7 +72,7 @@ class AssetRepair(Document): if self.capitalize_repair_cost: asset_value += self.repair_cost frappe.db.set_value('Asset', self.asset, 'asset_value', asset_value) - + def decrease_stock_quantity(self): stock_entry = frappe.get_doc({ "doctype": "Stock Entry", From 3f9f0ffdfe504646b09e76fe7554bf31816ca0d4 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 3 Jun 2021 21:28:58 +0530 Subject: [PATCH 035/369] fix(Asset Repair): Add Company in GL Entries --- erpnext/assets/doctype/asset_repair/asset_repair.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 070e7b0a9b8..9f096438642 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -117,7 +117,8 @@ class AssetRepair(Document): "voucher_type": self.doctype, "voucher_no": self.name, "cost_center": self.cost_center, - "posting_date": getdate() + "posting_date": getdate(), + "company": company }) gl_entry.insert() gl_entry = frappe.get_doc({ @@ -131,7 +132,8 @@ class AssetRepair(Document): "cost_center": self.cost_center, "posting_date": getdate(), "against_voucher_type": "Purchase Invoice", - "against_voucher": self.purchase_invoice + "against_voucher": self.purchase_invoice, + "company": company }) gl_entry.insert() From 70de9744965045c6302ac63f0ec1eb3004c4efe5 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 3 Jun 2021 22:28:30 +0530 Subject: [PATCH 036/369] fix(Asset): Add depreciation schedule details in create_asset() --- erpnext/assets/doctype/asset/test_asset.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 8845f24d104..29fbc9f15d6 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -699,7 +699,7 @@ def create_asset(**args): "item_code": args.item_code or "Macbook Pro", "company": args.company or"_Test Company", "purchase_date": "2015-01-01", - "calculate_depreciation": 0, + "calculate_depreciation": args.calculate_depreciation or 0, "gross_purchase_amount": 100000, "purchase_receipt_amount": 100000, "expected_value_after_useful_life": 10000, @@ -710,6 +710,13 @@ def create_asset(**args): "is_existing_asset": args.is_existing_asset or 0 }) + if asset.calculate_depreciation: + asset.append("finance_books", { + "depreciation_method": "Straight Line", + "frequency_of_depreciation": 12, + "total_number_of_depreciations": 5 + }) + try: asset.save() except frappe.DuplicateEntryError: From 2833903ce570abd3a2d5a68adf6dd5c4afac42b6 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 3 Jun 2021 22:28:57 +0530 Subject: [PATCH 037/369] fix(Asset Repair): Add tests --- .../doctype/asset_repair/asset_repair.py | 1 - .../doctype/asset_repair/test_asset_repair.py | 166 +++++++++++++++++- 2 files changed, 164 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 9f096438642..4ca6fbc5d04 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -172,7 +172,6 @@ class AssetRepair(Document): if asset.to_date > schedule_date: row.total_number_of_depreciations += 1 - @frappe.whitelist() def get_downtime(failure_date, completion_date): downtime = time_diff_in_hours(completion_date, failure_date) diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 3d325a9683c..9c9dd449713 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -2,8 +2,170 @@ # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt from __future__ import unicode_literals - +import frappe +from frappe.utils import nowdate, flt import unittest +from erpnext.assets.doctype.asset.test_asset import create_asset_data, create_asset, set_depreciation_settings_in_company class TestAssetRepair(unittest.TestCase): - pass + def setUp(self): + set_depreciation_settings_in_company() + create_asset_data() + frappe.db.sql("delete from `tabTax Rule`") + + def test_completion_date(self): + asset_repair = create_asset_repair() + asset_repair.repair_status = "Completed" + asset_repair.save() + self.assertTrue(asset_repair.completion_date) + + def test_update_status(self): + asset = create_asset() + initial_status = asset.status + asset_repair = create_asset_repair(asset = asset) + + if asset_repair.repair_status == "Pending": + asset.reload() + self.assertEqual(asset.status, "Out of Order") + + asset_repair.repair_status = "Completed" + asset_repair.save() + asset_status = frappe.db.get_value("Asset", asset_repair.asset, "status") + self.assertEqual(asset_status, initial_status) + + def test_stock_item_total_value(self): + asset_repair = create_asset_repair(stock_consumption = 1) + + for item in asset_repair.stock_items: + total_value = flt(item.valuation_rate) * flt(item.consumed_quantity) + self.assertEqual(item.total_value, total_value) + + def test_total_repair_cost(self): + asset_repair = create_asset_repair(stock_consumption = 1) + + total_repair_cost = asset_repair.repair_cost + self.assertEqual(total_repair_cost, asset_repair.repair_cost) + for item in asset_repair.stock_items: + total_repair_cost += item.total_value + + self.assertEqual(total_repair_cost, asset_repair.total_repair_cost) + + def test_repair_status_after_submit(self): + asset_repair = create_asset_repair(submit = 1) + self.assertNotEqual(asset_repair.repair_status, "Pending") + + def test_stock_items(self): + asset_repair = create_asset_repair(stock_consumption = 1) + self.assertTrue(asset_repair.stock_consumption) + self.assertTrue(asset_repair.stock_items) + + def test_warehouse(self): + asset_repair = create_asset_repair(stock_consumption = 1) + self.assertTrue(asset_repair.stock_consumption) + self.assertTrue(asset_repair.warehouse) + + def test_decrease_stock_quantity(self): + asset_repair = create_asset_repair(stock_consumption = 1, submit = 1) + stock_entry = frappe.get_last_doc('Stock Entry') + + self.assertEqual(stock_entry.stock_entry_type, "Material Issue") + self.assertEqual(stock_entry.items[0].s_warehouse, asset_repair.warehouse) + self.assertEqual(stock_entry.items[0].item_code, asset_repair.stock_items[0].item) + self.assertEqual(stock_entry.items[0].qty, asset_repair.stock_items[0].consumed_quantity) + + def test_increase_in_asset_value_due_to_stock_consumption(self): + asset = create_asset() + initial_asset_value = asset.asset_value + asset_repair = create_asset_repair(asset= asset, stock_consumption = 1, submit = 1) + asset.reload() + + increase_in_asset_value = asset.asset_value - initial_asset_value + self.assertEqual(asset_repair.stock_items[0].total_value, increase_in_asset_value) + + def test_increase_in_asset_value_due_to_repair_cost_capitalisation(self): + asset = create_asset() + initial_asset_value = asset.asset_value + asset_repair = create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1) + asset.reload() + + increase_in_asset_value = asset.asset_value - initial_asset_value + self.assertEqual(asset_repair.repair_cost, increase_in_asset_value) + + def test_purchase_invoice(self): + asset_repair = create_asset_repair(capitalize_repair_cost = 1, submit = 1) + self.assertTrue(asset_repair.purchase_invoice) + + def test_gl_entries(self): + asset_repair = create_asset_repair(capitalize_repair_cost = 1, submit = 1) + gl_entry = frappe.get_last_doc('GL Entry') + self.assertEqual(asset_repair.name, gl_entry.voucher_no) + + def test_increase_in_asset_life(self): + asset = create_asset(calculate_depreciation = 1) + initial_num_of_depreciations = num_of_depreciations(asset) + create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1) + asset.reload() + self.assertEqual((initial_num_of_depreciations + 1), num_of_depreciations(asset)) + +def num_of_depreciations(asset): + return asset.finance_books[0].total_number_of_depreciations + +def create_asset_repair(**args): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice + + args = frappe._dict(args) + + if args.asset: + asset = args.asset + else: + asset = create_asset(is_existing_asset = 1) + asset_repair = frappe.new_doc("Asset Repair") + asset_repair.update({ + "asset": asset.name, + "asset_name": asset.asset_name, + "failure_date": nowdate(), + "description": "Test Description", + "repair_cost": 0 + }) + + if args.stock_consumption: + asset_repair.stock_consumption = 1 + asset_repair.warehouse = create_warehouse("Test Warehouse", company = asset.company) + asset_repair.append("stock_items", { + "item": args.item or args.item_code or "_Test Item", + "valuation_rate": args.rate if args.get("rate") is not None else 100, + "consumed_quantity": args.qty or 1 + }) + + try: + asset_repair.save() + except frappe.DuplicateEntryError: + pass + + if args.submit: + asset_repair.repair_status = "Completed" + asset_repair.cost_center = "_Test Cost Center - _TC" + + if args.stock_consumption: + stock_entry = frappe.get_doc({ + "doctype": "Stock Entry", + "stock_entry_type": "Material Receipt", + "company": asset.company + }) + stock_entry.append('items', { + "t_warehouse": asset_repair.warehouse, + "item_code": asset_repair.stock_items[0].item, + "qty": asset_repair.stock_items[0].consumed_quantity + }) + stock_entry.submit() + + if args.capitalize_repair_cost: + asset_repair.capitalize_repair_cost = 1 + asset_repair.repair_cost = 1000 + if asset.calculate_depreciation: + asset_repair.increase_in_asset_life = 12 + asset_repair.purchase_invoice = make_purchase_invoice().name + + asset_repair.submit() + return asset_repair \ No newline at end of file From 65b2f9234b46f8e6442292bb0e54d56dd6841a46 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 3 Jun 2021 01:54:44 +0530 Subject: [PATCH 038/369] fix(Asset Repair): Set company when creating Stock Entry --- erpnext/assets/doctype/asset_repair/asset_repair.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 4ca6fbc5d04..6a5776a8357 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -76,7 +76,8 @@ class AssetRepair(Document): def decrease_stock_quantity(self): stock_entry = frappe.get_doc({ "doctype": "Stock Entry", - "stock_entry_type": "Material Issue" + "stock_entry_type": "Material Issue", + "company": frappe.get_value('Asset', self.asset, "company") }) for stock_item in self.stock_items: From 4e620c3b32381b97fb44c50f0608bd6522d10668 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 15 Jun 2021 12:02:07 +0530 Subject: [PATCH 039/369] fix: Set asset_name as title --- erpnext/assets/doctype/asset_repair/asset_repair.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 522f2874d99..640762d82c0 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -239,7 +239,8 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-21 10:37:35.002238", + "modified": "2021-06-15 10:34:00.839353", + "title_field": "asset_name", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", From fd272569aa1bb8e35c65d982691c32402cf6cdd2 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 07:03:32 +0530 Subject: [PATCH 040/369] fix(Asset Repair): Display fields according to the state of the doc --- erpnext/assets/doctype/asset_repair/asset_repair.js | 2 -- erpnext/assets/doctype/asset_repair/asset_repair.json | 9 +++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index 7633a595a2f..fdb8d0af67f 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -3,8 +3,6 @@ frappe.ui.form.on('Asset Repair', { refresh: function(frm) { - frm.toggle_display(['completion_date', 'repair_status', 'accounting_details', 'accounting_dimensions_section'], !(frm.doc.__islocal)); - if (frm.doc.docstatus) { frm.add_custom_button("View General Ledger", function() { frappe.route_options = { diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 640762d82c0..baae93d468e 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -71,6 +71,7 @@ }, { "allow_on_submit": 1, + "depends_on": "eval:!doc.__islocal", "fieldname": "completion_date", "fieldtype": "Datetime", "label": "Completion Date" @@ -78,6 +79,7 @@ { "allow_on_submit": 1, "default": "Pending", + "depends_on": "eval:!doc.__islocal", "fieldname": "repair_status", "fieldtype": "Select", "label": "Repair Status", @@ -154,6 +156,7 @@ }, { "default": "0", + "depends_on": "eval:!doc.__islocal", "fieldname": "capitalize_repair_cost", "fieldtype": "Check", "label": "Capitalize Repair Cost" @@ -196,6 +199,7 @@ }, { "default": "0", + "depends_on": "eval:!doc.__islocal", "fieldname": "stock_consumption", "fieldtype": "Check", "label": "Stock Consumed During Repair" @@ -230,6 +234,7 @@ "label": "Increase In Asset Life(Months)" }, { + "depends_on": "eval:!doc.__islocal", "fieldname": "purchase_invoice", "fieldtype": "Link", "label": "Purchase Invoice", @@ -239,8 +244,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-15 10:34:00.839353", - "title_field": "asset_name", + "modified": "2021-06-16 07:01:28.217619", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", @@ -279,6 +283,7 @@ ], "sort_field": "modified", "sort_order": "DESC", + "title_field": "asset_name", "track_changes": 1, "track_seen": 1 } \ No newline at end of file From 654074ad7a3e55a4bf073e7c6f9e11195a12bfcd Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 07:50:03 +0530 Subject: [PATCH 041/369] fix(Asset Repair): Add title to error messages --- erpnext/assets/doctype/asset_repair/asset_repair.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 6a5776a8357..6565f356aef 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -56,9 +56,9 @@ class AssetRepair(Document): def check_for_stock_items_and_warehouse(self): if not self.stock_items: - frappe.throw(_("Please enter Stock Items consumed during Asset Repair.")) + frappe.throw(_("Please enter Stock Items consumed during the Repair."), title=_("Missing Items")) if not self.warehouse: - frappe.throw(_("Please enter Warehouse from which Stock Items consumed during Asset Repair were taken.")) + frappe.throw(_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."), title=_("Missing Warehouse")) def check_for_cost_center(self): if not self.cost_center: From 5ab0cabf911581fe27cff997b326c76c6c90d777 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 07:56:40 +0530 Subject: [PATCH 042/369] fix(Asset Repair): Make Stock Items and Warehouse mandatory if stock_consumption is checked --- erpnext/assets/doctype/asset_repair/asset_repair.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index baae93d468e..17d33c41ecc 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -170,6 +170,7 @@ "fieldname": "stock_items", "fieldtype": "Table", "label": "Stock Items", + "mandatory_depends_on": "stock_consumption", "options": "Stock Item" }, { @@ -217,6 +218,7 @@ "label": "Total Repair Cost" }, { + "depends_on": "stock_consumption", "fieldname": "warehouse", "fieldtype": "Link", "label": "Warehouse", @@ -244,7 +246,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-16 07:01:28.217619", + "modified": "2021-06-16 07:52:49.438800", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", From 867fd02b2dfb1d54a823cabc1f0ebaef295ccc6f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 08:07:06 +0530 Subject: [PATCH 043/369] fix(Asset Repair): Add Company field --- .../assets/doctype/asset_repair/asset_repair.json | 10 +++++++++- erpnext/assets/doctype/asset_repair/asset_repair.py | 12 +++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 17d33c41ecc..840589ca2ed 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -11,6 +11,7 @@ "naming_series", "column_break_2", "asset_name", + "company", "section_break_5", "failure_date", "repair_status", @@ -241,12 +242,19 @@ "fieldtype": "Link", "label": "Purchase Invoice", "options": "Purchase Invoice" + }, + { + "fetch_from": "asset.company", + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company" } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-16 07:52:49.438800", + "modified": "2021-06-16 08:02:34.782990", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 6565f356aef..bd5e462f132 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -77,7 +77,7 @@ class AssetRepair(Document): stock_entry = frappe.get_doc({ "doctype": "Stock Entry", "stock_entry_type": "Material Issue", - "company": frappe.get_value('Asset', self.asset, "company") + "company": self.company }) for stock_item in self.stock_items: @@ -104,8 +104,7 @@ class AssetRepair(Document): def get_gl_entries(self): gl_entry = [] - company = frappe.db.get_value('Asset', self.asset, 'company') - repair_and_maintenance_account = frappe.db.get_value('Company', company, 'repair_and_maintenance_account') + repair_and_maintenance_account = frappe.db.get_value('Company', self.company, 'repair_and_maintenance_account') fixed_asset_account = self.get_fixed_asset_account() expense_account = frappe.get_doc('Purchase Invoice', self.purchase_invoice).items[0].expense_account @@ -119,7 +118,7 @@ class AssetRepair(Document): "voucher_no": self.name, "cost_center": self.cost_center, "posting_date": getdate(), - "company": company + "company": self.company }) gl_entry.insert() gl_entry = frappe.get_doc({ @@ -134,15 +133,14 @@ class AssetRepair(Document): "posting_date": getdate(), "against_voucher_type": "Purchase Invoice", "against_voucher": self.purchase_invoice, - "company": company + "company": self.company }) gl_entry.insert() def get_fixed_asset_account(self): asset_category = frappe.get_doc('Asset Category', frappe.db.get_value('Asset', self.asset, 'asset_category')) - company = frappe.db.get_value('Asset', self.asset, 'company') for account in asset_category.accounts: - if account.company_name == company: + if account.company_name == self.company: return account.fixed_asset_account def modify_depreciation_schedule(self): From 17fa1217792bb06554d4c2f60e45768091da2bac Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 08:13:36 +0530 Subject: [PATCH 044/369] fix(Asset Repair): Filter Cost Center and Project by Company --- .../doctype/asset_repair/asset_repair.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index fdb8d0af67f..1e87722179d 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -30,3 +30,21 @@ frappe.ui.form.on('Asset Repair', { } } }); + +cur_frm.fields_dict.cost_center.get_query = function(doc) { + return{ + filters:{ + 'is_group': 0, + 'company': doc.company + } + } +} + +cur_frm.fields_dict.project.get_query = function(doc) { + return{ + filters:{ + 'is_group': 0, + 'company': doc.company + } + } +} \ No newline at end of file From aff97095252aa2807f4ea4c0bf1a35c6e7d8e912 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 08:18:45 +0530 Subject: [PATCH 045/369] fix(Asset Repair): Add mandatory_depends_on condition for Purchase Invoice --- erpnext/assets/doctype/asset_repair/asset_repair.json | 3 ++- erpnext/assets/doctype/asset_repair/asset_repair.py | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 840589ca2ed..d3335c5afba 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -241,6 +241,7 @@ "fieldname": "purchase_invoice", "fieldtype": "Link", "label": "Purchase Invoice", + "mandatory_depends_on": "eval: doc.repair_status == 'Completed' && doc.repair_cost > 0", "options": "Purchase Invoice" }, { @@ -254,7 +255,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-16 08:02:34.782990", + "modified": "2021-06-16 08:16:07.581813", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index bd5e462f132..01eeb36e3a9 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -45,7 +45,6 @@ class AssetRepair(Document): self.check_for_stock_items_and_warehouse() self.decrease_stock_quantity() if self.capitalize_repair_cost: - self.check_for_purchase_invoice() self.make_gl_entries() if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation'): self.modify_depreciation_schedule() @@ -90,10 +89,6 @@ class AssetRepair(Document): stock_entry.insert() stock_entry.submit() - def check_for_purchase_invoice(self): - if not self.purchase_invoice: - frappe.throw(_("Please link Purchase Invoice.")) - def on_cancel(self): self.make_gl_entries(cancel=True) From 9779aa11fbd7390bc08a9f025f6893eb4890a5b9 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 08:26:02 +0530 Subject: [PATCH 046/369] fix(Asset Repair): Use existing function from asset.py for fetching fixed_asset_account --- erpnext/assets/doctype/asset_repair/asset_repair.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 01eeb36e3a9..63cdaf5cdeb 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -8,6 +8,7 @@ from frappe import _ from frappe.utils import time_diff_in_hours, getdate, add_months, flt, cint from frappe.model.document import Document from erpnext.accounts.general_ledger import make_gl_entries +from erpnext.assets.doctype.asset.asset import get_asset_account class AssetRepair(Document): def validate(self): @@ -100,7 +101,7 @@ class AssetRepair(Document): def get_gl_entries(self): gl_entry = [] repair_and_maintenance_account = frappe.db.get_value('Company', self.company, 'repair_and_maintenance_account') - fixed_asset_account = self.get_fixed_asset_account() + fixed_asset_account = get_asset_account("fixed_asset_account", asset=self.asset, company=self.company) expense_account = frappe.get_doc('Purchase Invoice', self.purchase_invoice).items[0].expense_account gl_entry = frappe.get_doc({ @@ -132,12 +133,6 @@ class AssetRepair(Document): }) gl_entry.insert() - def get_fixed_asset_account(self): - asset_category = frappe.get_doc('Asset Category', frappe.db.get_value('Asset', self.asset, 'asset_category')) - for account in asset_category.accounts: - if account.company_name == self.company: - return account.fixed_asset_account - def modify_depreciation_schedule(self): if self.increase_in_asset_life: asset = frappe.get_doc('Asset', self.asset) From 0aaf88cc0aadd37f1e19a79829c767fb3a69265c Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 08:33:05 +0530 Subject: [PATCH 047/369] fix(Asset Repair): Uncheck allow_on_submit for all fields --- erpnext/assets/doctype/asset_repair/asset_repair.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index d3335c5afba..88e75a168fe 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -71,14 +71,12 @@ "fieldtype": "Column Break" }, { - "allow_on_submit": 1, "depends_on": "eval:!doc.__islocal", "fieldname": "completion_date", "fieldtype": "Datetime", "label": "Completion Date" }, { - "allow_on_submit": 1, "default": "Pending", "depends_on": "eval:!doc.__islocal", "fieldname": "repair_status", @@ -104,13 +102,11 @@ "fieldtype": "Column Break" }, { - "allow_on_submit": 1, "fieldname": "actions_performed", "fieldtype": "Long Text", "label": "Actions performed" }, { - "allow_on_submit": 1, "fieldname": "downtime", "fieldtype": "Data", "in_list_view": 1, @@ -122,7 +118,6 @@ "fieldtype": "Column Break" }, { - "allow_on_submit": 1, "fieldname": "repair_cost", "fieldtype": "Currency", "label": "Repair Cost" @@ -255,7 +250,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-16 08:16:07.581813", + "modified": "2021-06-16 08:32:06.160615", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", From 75fbda10ad1c51c00a02123e63b62c4d6a066d36 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 08:35:50 +0530 Subject: [PATCH 048/369] fix(Asset Repair): Make Cost Center non-mandatory --- erpnext/assets/doctype/asset_repair/asset_repair.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 63cdaf5cdeb..5f4c382079c 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -38,7 +38,6 @@ class AssetRepair(Document): def on_submit(self): self.check_repair_status() - self.check_for_cost_center() if self.stock_consumption or self.capitalize_repair_cost: self.increase_asset_value() @@ -60,10 +59,6 @@ class AssetRepair(Document): if not self.warehouse: frappe.throw(_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."), title=_("Missing Warehouse")) - def check_for_cost_center(self): - if not self.cost_center: - frappe.throw(_("Please enter Cost Center.")) - def increase_asset_value(self): asset_value = frappe.db.get_value('Asset', self.asset, 'asset_value') for item in self.stock_items: From 71eaf3dbd8bffed9f1e499170729d84884944d02 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 08:45:54 +0530 Subject: [PATCH 049/369] fix(Asset Repair): Fix Sider issues --- .../assets/doctype/asset_repair/asset_repair.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index 1e87722179d..2319b069b0c 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -32,19 +32,19 @@ frappe.ui.form.on('Asset Repair', { }); cur_frm.fields_dict.cost_center.get_query = function(doc) { - return{ - filters:{ + return { + filters: { 'is_group': 0, 'company': doc.company } - } -} + }; +}; cur_frm.fields_dict.project.get_query = function(doc) { - return{ - filters:{ + return { + filters: { 'is_group': 0, 'company': doc.company } - } -} \ No newline at end of file + }; +}; \ No newline at end of file From 96de4fdf1fd8b3047ede42ff10b20ecc443e3026 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 10:42:37 +0530 Subject: [PATCH 050/369] fix(Asset Repair): Fix GL Entry creation --- .../doctype/asset_repair/asset_repair.py | 78 ++++++++++++------- 1 file changed, 48 insertions(+), 30 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 5f4c382079c..92f7408ba75 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -9,8 +9,9 @@ from frappe.utils import time_diff_in_hours, getdate, add_months, flt, cint from frappe.model.document import Document from erpnext.accounts.general_ledger import make_gl_entries from erpnext.assets.doctype.asset.asset import get_asset_account +from erpnext.controllers.accounts_controller import AccountsController -class AssetRepair(Document): +class AssetRepair(AccountsController): def validate(self): if self.repair_status == "Completed" and not self.completion_date: frappe.throw(_("Please select Completion Date for Completed Repair")) @@ -94,39 +95,56 @@ class AssetRepair(Document): make_gl_entries(gl_entries, cancel) def get_gl_entries(self): - gl_entry = [] + gl_entries = [] repair_and_maintenance_account = frappe.db.get_value('Company', self.company, 'repair_and_maintenance_account') fixed_asset_account = get_asset_account("fixed_asset_account", asset=self.asset, company=self.company) expense_account = frappe.get_doc('Purchase Invoice', self.purchase_invoice).items[0].expense_account - gl_entry = frappe.get_doc({ - "doctype": "GL Entry", - "account": expense_account, - "credit": self.total_repair_cost, - "credit_in_account_currency": self.total_repair_cost, - "against": repair_and_maintenance_account, - "voucher_type": self.doctype, - "voucher_no": self.name, - "cost_center": self.cost_center, - "posting_date": getdate(), - "company": self.company - }) - gl_entry.insert() - gl_entry = frappe.get_doc({ - "doctype": "GL Entry", - "account": fixed_asset_account, - "debit": self.total_repair_cost, - "debit_in_account_currency": self.total_repair_cost, - "against": expense_account, - "voucher_type": self.doctype, - "voucher_no": self.name, - "cost_center": self.cost_center, - "posting_date": getdate(), - "against_voucher_type": "Purchase Invoice", - "against_voucher": self.purchase_invoice, - "company": self.company - }) - gl_entry.insert() + gl_entries.append( + self.get_gl_dict({ + "account": expense_account, + "credit": self.repair_cost, + "credit_in_account_currency": self.repair_cost, + "against": repair_and_maintenance_account, + "voucher_type": self.doctype, + "voucher_no": self.name, + "cost_center": self.cost_center, + "posting_date": getdate(), + "company": self.company + }, item=self) + ) + + gl_entries.append( + self.get_gl_dict({ + "account": expense_account, + "credit": self.total_repair_cost - self.repair_cost, + "credit_in_account_currency": self.total_repair_cost - self.repair_cost, + "against": repair_and_maintenance_account, + "voucher_type": self.doctype, + "voucher_no": self.name, + "cost_center": self.cost_center, + "posting_date": getdate(), + "company": self.company + }, item=self) + ) + + gl_entries.append( + self.get_gl_dict({ + "account": fixed_asset_account, + "debit": self.total_repair_cost, + "debit_in_account_currency": self.total_repair_cost, + "against": expense_account, + "voucher_type": self.doctype, + "voucher_no": self.name, + "cost_center": self.cost_center, + "posting_date": getdate(), + "against_voucher_type": "Purchase Invoice", + "against_voucher": self.purchase_invoice, + "company": self.company + }, item=self) + ) + + return gl_entries def modify_depreciation_schedule(self): if self.increase_in_asset_life: From 9520efb941097765fefce9754beb158699386a25 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 16 Jun 2021 10:48:07 +0530 Subject: [PATCH 051/369] fix(Asset Repair): Filter Warehouse by Company --- erpnext/assets/doctype/asset_repair/asset_repair.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index 2319b069b0c..efa6a9d4949 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -41,6 +41,14 @@ cur_frm.fields_dict.cost_center.get_query = function(doc) { }; cur_frm.fields_dict.project.get_query = function(doc) { + return { + filters: { + 'company': doc.company + } + }; +}; + +cur_frm.fields_dict.warehouse.get_query = function(doc) { return { filters: { 'is_group': 0, From d354a301cbc0fd77a5283ca7690b5c82ba053c47 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 17 Jun 2021 08:28:19 +0530 Subject: [PATCH 052/369] fix(Asset Repair): Display value_after_depreciation in Finance Books --- .../assets/doctype/asset_finance_book/asset_finance_book.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index d9b7b695f7f..ee3a2072f0f 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -67,7 +67,6 @@ { "fieldname": "value_after_depreciation", "fieldtype": "Currency", - "hidden": 1, "label": "Value After Depreciation", "no_copy": 1, "options": "Company:company:default_currency", @@ -85,7 +84,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-11-05 16:30:09.213479", + "modified": "2021-06-17 08:02:32.650738", "modified_by": "Administrator", "module": "Assets", "name": "Asset Finance Book", From 50826f16ee1c16454cbfa24af316d2bf32e8c1e9 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 17 Jun 2021 13:02:02 +0530 Subject: [PATCH 053/369] fix(Asset): Replace asset_value with value_after_depreciation in Finance Books --- erpnext/assets/doctype/asset/asset.json | 9 +-------- erpnext/assets/doctype/asset/asset.py | 11 ++++++----- .../asset_finance_book/asset_finance_book.json | 2 +- .../assets/doctype/asset_repair/asset_repair.py | 16 +++++++++++----- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 8a0e3ad2a66..d55258c8f67 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -23,7 +23,6 @@ "asset_name", "asset_category", "location", - "asset_value", "custodian", "department", "disposal_date", @@ -484,12 +483,6 @@ "fieldtype": "Section Break", "label": "Finance Books" }, - { - "fieldname": "asset_value", - "fieldtype": "Currency", - "label": "Asset Value", - "read_only": 1 - }, { "fieldname": "to_date", "fieldtype": "Date", @@ -523,7 +516,7 @@ "link_fieldname": "asset" } ], - "modified": "2021-05-21 12:05:29.424083", + "modified": "2021-06-17 12:59:39.189106", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index b3d3a198c33..4820f8b4879 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -96,9 +96,6 @@ class Asset(AccountsController): finance_books = get_item_details(self.item_code, self.asset_category) self.set('finance_books', finance_books) - if not(self.asset_value): - self.asset_value = self.gross_purchase_amount - def validate_asset_values(self): if not self.asset_category: self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category") @@ -187,8 +184,12 @@ class Asset(AccountsController): start = n break - value_after_depreciation = (flt(self.asset_value) - - flt(self.opening_accumulated_depreciation)) - flt(d.expected_value_after_useful_life) + if d.value_after_depreciation: + value_after_depreciation = (flt(d.value_after_depreciation) - + flt(self.opening_accumulated_depreciation)) - flt(d.expected_value_after_useful_life) + else: + value_after_depreciation = (flt(self.gross_purchase_amount) - + flt(self.opening_accumulated_depreciation)) - flt(d.expected_value_after_useful_life) d.value_after_depreciation = value_after_depreciation diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index ee3a2072f0f..e5a5f194c1b 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -84,7 +84,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-06-17 08:02:32.650738", + "modified": "2021-06-17 12:59:05.743683", "modified_by": "Administrator", "module": "Assets", "name": "Asset Finance Book", diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 92f7408ba75..2d039190e36 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -61,13 +61,19 @@ class AssetRepair(AccountsController): frappe.throw(_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."), title=_("Missing Warehouse")) def increase_asset_value(self): - asset_value = frappe.db.get_value('Asset', self.asset, 'asset_value') + total_value_of_stock_consumed = 0 for item in self.stock_items: - asset_value += item.total_value + total_value_of_stock_consumed += item.total_value - if self.capitalize_repair_cost: - asset_value += self.repair_cost - frappe.db.set_value('Asset', self.asset, 'asset_value', asset_value) + asset = frappe.get_doc('Asset', self.asset) + asset.flags.ignore_validate_update_after_submit = True + if asset.calculate_depreciation: + for row in asset.finance_books: + row.value_after_depreciation += total_value_of_stock_consumed + + if self.capitalize_repair_cost: + row.value_after_depreciation += self.repair_cost + asset.save() def decrease_stock_quantity(self): stock_entry = frappe.get_doc({ From 54cbc8324acac8bdff796d72f7f128fe076eae3f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 18 Jun 2021 09:53:18 +0530 Subject: [PATCH 054/369] fix(Asset Repair): Create GL Entries for each item in Stock Items --- .../doctype/asset_repair/asset_repair.py | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 2d039190e36..7864cb70a5e 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -120,19 +120,23 @@ class AssetRepair(AccountsController): }, item=self) ) - gl_entries.append( - self.get_gl_dict({ - "account": expense_account, - "credit": self.total_repair_cost - self.repair_cost, - "credit_in_account_currency": self.total_repair_cost - self.repair_cost, - "against": repair_and_maintenance_account, - "voucher_type": self.doctype, - "voucher_no": self.name, - "cost_center": self.cost_center, - "posting_date": getdate(), - "company": self.company - }, item=self) - ) + if self.stock_consumption: + # creating GL Entries for each row in Stock Items based on the Stock Entry created for it + stock_entry = frappe.get_last_doc('Stock Entry') + for item in stock_entry.items: + gl_entries.append( + self.get_gl_dict({ + "account": item.expense_account, + "credit": item.amount, + "credit_in_account_currency": item.amount, + "against": repair_and_maintenance_account, + "voucher_type": self.doctype, + "voucher_no": self.name, + "cost_center": self.cost_center, + "posting_date": getdate(), + "company": self.company + }, item=self) + ) gl_entries.append( self.get_gl_dict({ From bd336c7d8e4d4053fb69c12a669dfc1efcf4250c Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 18 Jun 2021 09:59:45 +0530 Subject: [PATCH 055/369] fix(Asset): Add function to clear old depreciation schedule --- erpnext/assets/doctype/asset/asset.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 4820f8b4879..9273f01da75 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -176,13 +176,8 @@ class Asset(AccountsController): for d in self.get('finance_books'): self.validate_asset_finance_books(d) - - start = 0 - for n in range(len(self.schedules)): - if not self.schedules[n].journal_entry: - del self.schedules[n:] - start = n - break + + start = self.clear_depreciation_schedule() if d.value_after_depreciation: value_after_depreciation = (flt(d.value_after_depreciation) - @@ -296,6 +291,15 @@ class Asset(AccountsController): "finance_book_id": d.idx }) + def clear_depreciation_schedule(self): + start = 0 + for n in range(len(self.schedules)): + if not self.schedules[n].journal_entry: + del self.schedules[n:] + start = n + break + return start + def check_is_pro_rata(self, row): has_pro_rata = False From be536040df75293c2eb6de9b084b1fa6d10cf495 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 19 Jun 2021 13:06:27 +0530 Subject: [PATCH 056/369] fix: Add comments --- erpnext/assets/doctype/asset/asset.py | 12 ++++++-- .../doctype/asset_repair/asset_repair.py | 30 +++++++++++-------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 9273f01da75..18e3ffc8a6f 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -179,7 +179,8 @@ class Asset(AccountsController): start = self.clear_depreciation_schedule() - if d.value_after_depreciation: + # value_after_depreciation - current Asset value + if d.value_after_depreciation: value_after_depreciation = (flt(d.value_after_depreciation) - flt(self.opening_accumulated_depreciation)) - flt(d.expected_value_after_useful_life) else: @@ -291,6 +292,7 @@ class Asset(AccountsController): "finance_book_id": d.idx }) + # used when depreciation schedule needs to be modified due to increase in asset life def clear_depreciation_schedule(self): start = 0 for n in range(len(self.schedules)): @@ -300,10 +302,13 @@ class Asset(AccountsController): break return start + + # if it returns True, depreciation_amount will not be equal for the first and last rows def check_is_pro_rata(self, row): has_pro_rata = False - days = date_diff(row.depreciation_start_date, self.available_for_use_date) + 1 + + # if frequency_of_depreciation is 12 months, total_days = 365 total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation) if days < total_days: @@ -783,9 +788,12 @@ def get_depreciation_amount(asset, depreciable_value, row): depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) if row.depreciation_method in ("Straight Line", "Manual"): + # if the Depreciation Schedule is being prepared for the first time if not asset.to_date: depreciation_amount = (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / depreciation_left + + # if the Depreciation Schedule is being modified after Asset Repair else: depreciation_amount = (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / (date_diff(asset.to_date, asset.available_for_use_date) / 365) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 7864cb70a5e..0befee70cb9 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -47,7 +47,7 @@ class AssetRepair(AccountsController): self.decrease_stock_quantity() if self.capitalize_repair_cost: self.make_gl_entries() - if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation'): + if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation') and self.increase_in_asset_life: self.modify_depreciation_schedule() def check_repair_status(self): @@ -157,27 +157,33 @@ class AssetRepair(AccountsController): return gl_entries def modify_depreciation_schedule(self): - if self.increase_in_asset_life: - asset = frappe.get_doc('Asset', self.asset) - asset.flags.ignore_validate_update_after_submit = True - for row in asset.finance_books: - row.total_number_of_depreciations += self.increase_in_asset_life/row.frequency_of_depreciation + asset = frappe.get_doc('Asset', self.asset) + asset.flags.ignore_validate_update_after_submit = True + for row in asset.finance_books: + row.total_number_of_depreciations += self.increase_in_asset_life/row.frequency_of_depreciation - asset.edit_dates = "" - extra_months = self.increase_in_asset_life % row.frequency_of_depreciation - if extra_months != 0: - self.calculate_last_schedule_date(asset, row, extra_months) + asset.edit_dates = "" + extra_months = self.increase_in_asset_life % row.frequency_of_depreciation + if extra_months != 0: + self.calculate_last_schedule_date(asset, row, extra_months) - asset.prepare_depreciation_data() - asset.save() + asset.prepare_depreciation_data() + asset.save() # to help modify depreciation schedule when increase_in_asset_life is not a multiple of frequency_of_depreciation def calculate_last_schedule_date(self, asset, row, extra_months): asset.edit_dates = "Don't Edit" number_of_pending_depreciations = cint(row.total_number_of_depreciations) - \ cint(asset.number_of_depreciations_booked) + + # the Schedule Date in the final row of the old Depreciation Schedule last_schedule_date = asset.schedules[len(asset.schedules)-1].schedule_date + + # the Schedule Date in the final row of the new Depreciation Schedule asset.to_date = add_months(last_schedule_date, extra_months) + + # the latest possible date at which the depreciation can occur, without increasing the Total Number of Depreciations + # if depreciations happen yearly and the Depreciation Posting Date is 01-01-2020, this could be 01-01-2021, 01-01-2022... schedule_date = add_months(row.depreciation_start_date, number_of_pending_depreciations * cint(row.frequency_of_depreciation)) From ae8cb335b6fb458dd7fc405e71c5dfabb272f94a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 19 Jun 2021 13:45:37 +0530 Subject: [PATCH 057/369] fix(Asset Repair): Fix depreciation_amount calculation --- erpnext/assets/doctype/asset/asset.py | 2 +- erpnext/regional/india/utils.py | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 18e3ffc8a6f..93b05ebd5c8 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -789,7 +789,7 @@ def get_depreciation_amount(asset, depreciable_value, row): if row.depreciation_method in ("Straight Line", "Manual"): # if the Depreciation Schedule is being prepared for the first time - if not asset.to_date: + if not asset.edit_dates: depreciation_amount = (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / depreciation_left diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index a4466e78f28..11b19ae6968 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -834,8 +834,16 @@ def get_depreciation_amount(asset, depreciable_value, row): depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked) if row.depreciation_method in ("Straight Line", "Manual"): - depreciation_amount = (flt(row.value_after_depreciation) - - flt(row.expected_value_after_useful_life)) / depreciation_left + # if the Depreciation Schedule is being prepared for the first time + if not asset.edit_dates: + depreciation_amount = (flt(row.value_after_depreciation) - + flt(row.expected_value_after_useful_life)) / depreciation_left + + # if the Depreciation Schedule is being modified after Asset Repair + else: + depreciation_amount = (flt(row.value_after_depreciation) - + flt(row.expected_value_after_useful_life)) / (date_diff(asset.to_date, asset.available_for_use_date) / 365) + else: rate_of_depreciation = row.rate_of_depreciation # if its the first depreciation From bd1796cbb61ff261ee2f5d699e44eeaedbe6638a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 19 Jun 2021 14:00:26 +0530 Subject: [PATCH 058/369] fix: Replace edit_dates with flags.increase_in_asset_life --- erpnext/assets/doctype/asset/asset.json | 9 +-------- erpnext/assets/doctype/asset/asset.py | 4 ++-- erpnext/assets/doctype/asset_repair/asset_repair.py | 4 ++-- erpnext/regional/india/utils.py | 2 +- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index d55258c8f67..d77eb104184 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -54,7 +54,6 @@ "section_break_14", "schedules", "to_date", - "edit_dates", "insurance_details", "policy_number", "insurer", @@ -488,12 +487,6 @@ "fieldtype": "Date", "hidden": 1, "label": "To Date" - }, - { - "fieldname": "edit_dates", - "fieldtype": "Data", - "hidden": 1, - "label": "Edit Dates" } ], "idx": 72, @@ -516,7 +509,7 @@ "link_fieldname": "asset" } ], - "modified": "2021-06-17 12:59:39.189106", + "modified": "2021-06-19 13:56:58.450182", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 93b05ebd5c8..63b70f66132 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -223,7 +223,7 @@ class Asset(AccountsController): # For last row elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1: - if not self.edit_dates: + if not self.flags.increase_in_asset_life: self.to_date = add_months(self.available_for_use_date, n * cint(d.frequency_of_depreciation)) @@ -789,7 +789,7 @@ def get_depreciation_amount(asset, depreciable_value, row): if row.depreciation_method in ("Straight Line", "Manual"): # if the Depreciation Schedule is being prepared for the first time - if not asset.edit_dates: + if not asset.flags.increase_in_asset_life: depreciation_amount = (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / depreciation_left diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 0befee70cb9..da237f09f07 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -162,7 +162,7 @@ class AssetRepair(AccountsController): for row in asset.finance_books: row.total_number_of_depreciations += self.increase_in_asset_life/row.frequency_of_depreciation - asset.edit_dates = "" + asset.flags.increase_in_asset_life = False extra_months = self.increase_in_asset_life % row.frequency_of_depreciation if extra_months != 0: self.calculate_last_schedule_date(asset, row, extra_months) @@ -172,7 +172,7 @@ class AssetRepair(AccountsController): # to help modify depreciation schedule when increase_in_asset_life is not a multiple of frequency_of_depreciation def calculate_last_schedule_date(self, asset, row, extra_months): - asset.edit_dates = "Don't Edit" + asset.flags.increase_in_asset_life = True number_of_pending_depreciations = cint(row.total_number_of_depreciations) - \ cint(asset.number_of_depreciations_booked) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 11b19ae6968..81c0918b99a 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -835,7 +835,7 @@ def get_depreciation_amount(asset, depreciable_value, row): if row.depreciation_method in ("Straight Line", "Manual"): # if the Depreciation Schedule is being prepared for the first time - if not asset.edit_dates: + if not asset.flags.increase_in_asset_life: depreciation_amount = (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / depreciation_left From 4004bcd4362e2cec8e39977768024ee256c378c7 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 19 Jun 2021 14:06:45 +0530 Subject: [PATCH 059/369] fix(Asset Repair): Move Total Repair Cost to the Stock Consumption Details section --- erpnext/assets/doctype/asset_repair/asset_repair.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 88e75a168fe..89f7fa3bca8 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -26,11 +26,11 @@ "capitalize_repair_cost", "stock_consumption", "column_break_8", - "total_repair_cost", "purchase_invoice", "stock_consumption_details_section", "warehouse", "stock_items", + "total_repair_cost", "asset_depreciation_details_section", "increase_in_asset_life", "section_break_9", @@ -209,6 +209,7 @@ }, { "depends_on": "stock_consumption", + "description": "Sum of Repair Cost and the total value of all Stock Items consumed during the repair.", "fieldname": "total_repair_cost", "fieldtype": "Currency", "label": "Total Repair Cost" @@ -250,7 +251,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-16 08:32:06.160615", + "modified": "2021-06-19 14:04:35.423111", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", From e755c74a60b84b0c8399196db4a5cabc8c91ae4b Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 19 Jun 2021 14:54:30 +0530 Subject: [PATCH 060/369] fix(Asset Repair): Add Stock Entry field --- .../doctype/asset_repair/asset_repair.json | 16 +++++++++++++--- .../assets/doctype/asset_repair/asset_repair.py | 4 +++- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 89f7fa3bca8..ee18c4bdc4d 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -31,6 +31,7 @@ "warehouse", "stock_items", "total_repair_cost", + "stock_entry", "asset_depreciation_details_section", "increase_in_asset_life", "section_break_9", @@ -118,6 +119,7 @@ "fieldtype": "Column Break" }, { + "default": "0", "fieldname": "repair_cost", "fieldtype": "Currency", "label": "Repair Cost" @@ -208,11 +210,12 @@ "label": "Stock Consumption Details" }, { - "depends_on": "stock_consumption", + "depends_on": "eval: doc.stock_consumption && doc.total_repair_cost > 0", "description": "Sum of Repair Cost and the total value of all Stock Items consumed during the repair.", "fieldname": "total_repair_cost", "fieldtype": "Currency", - "label": "Total Repair Cost" + "label": "Total Repair Cost", + "read_only": 1 }, { "depends_on": "stock_consumption", @@ -246,12 +249,19 @@ "fieldtype": "Link", "label": "Company", "options": "Company" + }, + { + "fieldname": "stock_entry", + "fieldtype": "Link", + "label": "Stock Entry", + "options": "Stock Entry", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-19 14:04:35.423111", + "modified": "2021-06-19 14:47:25.875814", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index da237f09f07..c074cc930e6 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -92,6 +92,8 @@ class AssetRepair(AccountsController): stock_entry.insert() stock_entry.submit() + self.stock_entry = stock_entry.name + def on_cancel(self): self.make_gl_entries(cancel=True) @@ -122,7 +124,7 @@ class AssetRepair(AccountsController): if self.stock_consumption: # creating GL Entries for each row in Stock Items based on the Stock Entry created for it - stock_entry = frappe.get_last_doc('Stock Entry') + stock_entry = frappe.get_doc('Stock Entry', self.stock_entry) for item in stock_entry.items: gl_entries.append( self.get_gl_dict({ From 399d17e40efde2ef674becd036ce3ffd5cb7fc0f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 19 Jun 2021 15:18:54 +0530 Subject: [PATCH 061/369] fix(Asset Repair): Make Error Description non-mandatory --- erpnext/assets/doctype/asset_repair/asset_repair.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index ee18c4bdc4d..cfa084e6063 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -95,8 +95,7 @@ { "fieldname": "description", "fieldtype": "Long Text", - "label": "Error Description", - "reqd": 1 + "label": "Error Description" }, { "fieldname": "column_break_9", @@ -261,7 +260,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-19 14:47:25.875814", + "modified": "2021-06-19 15:18:10.625833", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", From 68e0c96c0366a18da4215b2834644db44f35171c Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 19 Jun 2021 15:23:06 +0530 Subject: [PATCH 062/369] fix(Asset Repair): Prevent some fields from being copied on duplicating the doc --- erpnext/assets/doctype/asset_repair/asset_repair.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index cfa084e6063..a0fe6328027 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -75,7 +75,8 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "completion_date", "fieldtype": "Datetime", - "label": "Completion Date" + "label": "Completion Date", + "no_copy": 1 }, { "default": "Pending", @@ -232,7 +233,8 @@ { "fieldname": "increase_in_asset_life", "fieldtype": "Int", - "label": "Increase In Asset Life(Months)" + "label": "Increase In Asset Life(Months)", + "no_copy": 1 }, { "depends_on": "eval:!doc.__islocal", @@ -240,6 +242,7 @@ "fieldtype": "Link", "label": "Purchase Invoice", "mandatory_depends_on": "eval: doc.repair_status == 'Completed' && doc.repair_cost > 0", + "no_copy": 1, "options": "Purchase Invoice" }, { @@ -260,7 +263,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-19 15:18:10.625833", + "modified": "2021-06-19 15:20:24.056706", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", From 42fd7ffbc01c59b901b46953ead07096da957887 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sun, 20 Jun 2021 17:44:35 +0530 Subject: [PATCH 063/369] fix(Asset Repair): Set completion_date on changing repair_status to 'Completed' --- erpnext/assets/doctype/asset_repair/asset_repair.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index efa6a9d4949..ced3dad1e5f 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -28,6 +28,10 @@ frappe.ui.form.on('Asset Repair', { } }); } + + if (frm.doc.repair_status == "Completed") { + frm.set_value('completion_date', frappe.datetime.now_datetime()); + } } }); From 852881e33e5bbe887b801e11cee3282928dc3a7b Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 21 Jun 2021 14:52:00 +0530 Subject: [PATCH 064/369] fix(Asset Repair): Fix tests --- .../doctype/asset_repair/asset_repair.json | 2 +- .../doctype/asset_repair/asset_repair.py | 55 ++++++++----------- .../doctype/asset_repair/test_asset_repair.py | 7 ++- 3 files changed, 30 insertions(+), 34 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index a0fe6328027..6bcddbfb946 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -263,7 +263,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-19 15:20:24.056706", + "modified": "2021-06-20 17:35:51.075537", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index c074cc930e6..5fccfb76a52 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -6,74 +6,72 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.utils import time_diff_in_hours, getdate, add_months, flt, cint -from frappe.model.document import Document from erpnext.accounts.general_ledger import make_gl_entries from erpnext.assets.doctype.asset.asset import get_asset_account from erpnext.controllers.accounts_controller import AccountsController class AssetRepair(AccountsController): def validate(self): - if self.repair_status == "Completed" and not self.completion_date: - frappe.throw(_("Please select Completion Date for Completed Repair")) - + self.asset_doc = frappe.get_doc('Asset', self.asset) self.update_status() - self.set_total_value() # change later + if self.get('stock_items'): + self.set_total_value() # change later self.calculate_total_repair_cost() def update_status(self): if self.repair_status == 'Pending': frappe.db.set_value('Asset', self.asset, 'status', 'Out of Order') else: - asset = frappe.get_doc('Asset', self.asset) - asset.set_status() + self.asset_doc.set_status() def set_total_value(self): - for item in self.stock_items: + for item in self.get('stock_items'): item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity) def calculate_total_repair_cost(self): self.total_repair_cost = self.repair_cost - if self.stock_consumption: - for item in self.stock_items: + if self.get('stock_items'): + for item in self.get('stock_items'): self.total_repair_cost += item.total_value def on_submit(self): self.check_repair_status() - if self.stock_consumption or self.capitalize_repair_cost: + if self.get('stock_consumption') or self.get('capitalize_repair_cost'): self.increase_asset_value() - if self.stock_consumption: + if self.get('stock_consumption'): self.check_for_stock_items_and_warehouse() self.decrease_stock_quantity() - if self.capitalize_repair_cost: + if self.get('capitalize_repair_cost'): self.make_gl_entries() if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation') and self.increase_in_asset_life: self.modify_depreciation_schedule() + self.asset_doc.flags.ignore_validate_update_after_submit = True + self.asset_doc.save() + def check_repair_status(self): if self.repair_status == "Pending": frappe.throw(_("Please update Repair Status.")) def check_for_stock_items_and_warehouse(self): - if not self.stock_items: + if not self.get('stock_items'): frappe.throw(_("Please enter Stock Items consumed during the Repair."), title=_("Missing Items")) if not self.warehouse: frappe.throw(_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."), title=_("Missing Warehouse")) def increase_asset_value(self): total_value_of_stock_consumed = 0 - for item in self.stock_items: - total_value_of_stock_consumed += item.total_value + if self.get('stock_consumption'): + for item in self.get('stock_items'): + total_value_of_stock_consumed += item.total_value - asset = frappe.get_doc('Asset', self.asset) - asset.flags.ignore_validate_update_after_submit = True - if asset.calculate_depreciation: - for row in asset.finance_books: + if self.asset_doc.calculate_depreciation: + for row in self.asset_doc.finance_books: row.value_after_depreciation += total_value_of_stock_consumed if self.capitalize_repair_cost: row.value_after_depreciation += self.repair_cost - asset.save() def decrease_stock_quantity(self): stock_entry = frappe.get_doc({ @@ -82,7 +80,7 @@ class AssetRepair(AccountsController): "company": self.company }) - for stock_item in self.stock_items: + for stock_item in self.get('stock_items'): stock_entry.append('items', { "s_warehouse": self.warehouse, "item_code": stock_item.item, @@ -122,7 +120,7 @@ class AssetRepair(AccountsController): }, item=self) ) - if self.stock_consumption: + if self.get('stock_consumption'): # creating GL Entries for each row in Stock Items based on the Stock Entry created for it stock_entry = frappe.get_doc('Stock Entry', self.stock_entry) for item in stock_entry.items: @@ -159,18 +157,13 @@ class AssetRepair(AccountsController): return gl_entries def modify_depreciation_schedule(self): - asset = frappe.get_doc('Asset', self.asset) - asset.flags.ignore_validate_update_after_submit = True - for row in asset.finance_books: + for row in self.asset_doc.finance_books: row.total_number_of_depreciations += self.increase_in_asset_life/row.frequency_of_depreciation - asset.flags.increase_in_asset_life = False + self.asset_doc.flags.increase_in_asset_life = False extra_months = self.increase_in_asset_life % row.frequency_of_depreciation if extra_months != 0: - self.calculate_last_schedule_date(asset, row, extra_months) - - asset.prepare_depreciation_data() - asset.save() + self.calculate_last_schedule_date(self.asset_doc, row, extra_months) # to help modify depreciation schedule when increase_in_asset_life is not a multiple of frequency_of_depreciation def calculate_last_schedule_date(self, asset, row, extra_months): diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 9c9dd449713..d1b417fd384 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -105,7 +105,9 @@ class TestAssetRepair(unittest.TestCase): initial_num_of_depreciations = num_of_depreciations(asset) create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1) asset.reload() + self.assertEqual((initial_num_of_depreciations + 1), num_of_depreciations(asset)) + self.assertEqual(asset.schedules[-1].accumulated_depreciation_amount, asset.finance_books[0].value_after_depreciation) def num_of_depreciations(asset): return asset.finance_books[0].total_number_of_depreciations @@ -126,7 +128,8 @@ def create_asset_repair(**args): "asset_name": asset.asset_name, "failure_date": nowdate(), "description": "Test Description", - "repair_cost": 0 + "repair_cost": 0, + "company": asset.company }) if args.stock_consumption: @@ -142,7 +145,7 @@ def create_asset_repair(**args): asset_repair.save() except frappe.DuplicateEntryError: pass - + if args.submit: asset_repair.repair_status = "Completed" asset_repair.cost_center = "_Test Cost Center - _TC" From e92187863347ff5421fb209942b36a26cc9115b8 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 21 Jun 2021 14:55:34 +0530 Subject: [PATCH 065/369] fix: Rename 'Stock Item' to 'Asset Repair Consumed Item' --- .../assets/doctype/asset_maintenance/asset_maintenance.json | 4 ++-- erpnext/assets/doctype/asset_repair/asset_repair.json | 4 ++-- .../{stock_item => asset_repair_consumed_item}/__init__.py | 0 .../asset_repair_consumed_item.json} | 2 +- .../asset_repair_consumed_item.py} | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename erpnext/assets/doctype/{stock_item => asset_repair_consumed_item}/__init__.py (100%) rename erpnext/assets/doctype/{stock_item/stock_item.json => asset_repair_consumed_item/asset_repair_consumed_item.json} (96%) rename erpnext/assets/doctype/{stock_item/stock_item.py => asset_repair_consumed_item/asset_repair_consumed_item.py} (81%) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json index da2fd754512..63a55389d81 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json @@ -126,11 +126,11 @@ "fieldname": "stock_items", "fieldtype": "Table", "label": "Stock Items", - "options": "Stock Item" + "options": "Asset Repair Consumed Item" } ], "links": [], - "modified": "2021-05-13 05:24:58.480132", + "modified": "2021-06-21 14:53:46.041123", "modified_by": "Administrator", "module": "Assets", "name": "Asset Maintenance", diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 6bcddbfb946..14f18b5309c 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -169,7 +169,7 @@ "fieldtype": "Table", "label": "Stock Items", "mandatory_depends_on": "stock_consumption", - "options": "Stock Item" + "options": "Asset Repair Consumed Item" }, { "fieldname": "section_break_23", @@ -263,7 +263,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-20 17:35:51.075537", + "modified": "2021-06-21 14:53:46.665576", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", diff --git a/erpnext/assets/doctype/stock_item/__init__.py b/erpnext/assets/doctype/asset_repair_consumed_item/__init__.py similarity index 100% rename from erpnext/assets/doctype/stock_item/__init__.py rename to erpnext/assets/doctype/asset_repair_consumed_item/__init__.py diff --git a/erpnext/assets/doctype/stock_item/stock_item.json b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json similarity index 96% rename from erpnext/assets/doctype/stock_item/stock_item.json rename to erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json index b1f05db3950..528f0ec986a 100644 --- a/erpnext/assets/doctype/stock_item/stock_item.json +++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json @@ -46,7 +46,7 @@ "modified": "2021-05-12 03:19:55.006300", "modified_by": "Administrator", "module": "Assets", - "name": "Stock Item", + "name": "Asset Repair Consumed Item", "owner": "Administrator", "permissions": [], "sort_field": "modified", diff --git a/erpnext/assets/doctype/stock_item/stock_item.py b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py similarity index 81% rename from erpnext/assets/doctype/stock_item/stock_item.py rename to erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py index 0e3cc3f8ba7..fa22a5712f4 100644 --- a/erpnext/assets/doctype/stock_item/stock_item.py +++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py @@ -4,5 +4,5 @@ # import frappe from frappe.model.document import Document -class StockItem(Document): +class AssetRepairConsumedItem(Document): pass From ad78888c867335a8ececa2e307a3740a4a234283 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 21 Jun 2021 15:02:40 +0530 Subject: [PATCH 066/369] fix(Asset Repair): Compute total_value instantly --- erpnext/assets/doctype/asset_repair/asset_repair.js | 7 +++++++ erpnext/assets/doctype/asset_repair/asset_repair.py | 6 ------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index ced3dad1e5f..91bed4fdd03 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -35,6 +35,13 @@ frappe.ui.form.on('Asset Repair', { } }); +frappe.ui.form.on('Asset Repair Consumed Item', { + consumed_quantity: function(frm, cdt, cdn) { + var row = locals[cdt][cdn]; + frappe.model.set_value(cdt, cdn, 'total_value', row.consumed_quantity * row.valuation_rate); + }, +}); + cur_frm.fields_dict.cost_center.get_query = function(doc) { return { filters: { diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 5fccfb76a52..79b9a6a2b57 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -14,8 +14,6 @@ class AssetRepair(AccountsController): def validate(self): self.asset_doc = frappe.get_doc('Asset', self.asset) self.update_status() - if self.get('stock_items'): - self.set_total_value() # change later self.calculate_total_repair_cost() def update_status(self): @@ -24,10 +22,6 @@ class AssetRepair(AccountsController): else: self.asset_doc.set_status() - def set_total_value(self): - for item in self.get('stock_items'): - item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity) - def calculate_total_repair_cost(self): self.total_repair_cost = self.repair_cost if self.get('stock_items'): From 6c2f4ce6a5b12d3fbfd2675c6fa23e12f7a8285a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 21 Jun 2021 15:22:02 +0530 Subject: [PATCH 067/369] fix(Asset Repair): Increase stock quantity and decrease asset value on cancellation --- .../doctype/asset_repair/asset_repair.py | 55 ++++++++++++++++--- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 79b9a6a2b57..e7b8b45b7ee 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -24,9 +24,9 @@ class AssetRepair(AccountsController): def calculate_total_repair_cost(self): self.total_repair_cost = self.repair_cost - if self.get('stock_items'): - for item in self.get('stock_items'): - self.total_repair_cost += item.total_value + + total_value_of_stock_consumed = self.get_total_value_of_stock_consumed() + self.total_repair_cost += total_value_of_stock_consumed def on_submit(self): self.check_repair_status() @@ -44,6 +44,14 @@ class AssetRepair(AccountsController): self.asset_doc.flags.ignore_validate_update_after_submit = True self.asset_doc.save() + def on_cancel(self): + if self.get('stock_consumption') or self.get('capitalize_repair_cost'): + self.decrease_asset_value() + if self.get('stock_consumption'): + self.increase_stock_quantity() + if self.get('capitalize_repair_cost'): + self.make_gl_entries(cancel=True) + def check_repair_status(self): if self.repair_status == "Pending": frappe.throw(_("Please update Repair Status.")) @@ -55,10 +63,7 @@ class AssetRepair(AccountsController): frappe.throw(_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."), title=_("Missing Warehouse")) def increase_asset_value(self): - total_value_of_stock_consumed = 0 - if self.get('stock_consumption'): - for item in self.get('stock_items'): - total_value_of_stock_consumed += item.total_value + total_value_of_stock_consumed = self.get_total_value_of_stock_consumed() if self.asset_doc.calculate_depreciation: for row in self.asset_doc.finance_books: @@ -67,6 +72,24 @@ class AssetRepair(AccountsController): if self.capitalize_repair_cost: row.value_after_depreciation += self.repair_cost + def decrease_asset_value(self): + total_value_of_stock_consumed = self.get_total_value_of_stock_consumed() + + if self.asset_doc.calculate_depreciation: + for row in self.asset_doc.finance_books: + row.value_after_depreciation -= total_value_of_stock_consumed + + if self.capitalize_repair_cost: + row.value_after_depreciation -= self.repair_cost + + def get_total_value_of_stock_consumed(self): + total_value_of_stock_consumed = 0 + if self.get('stock_consumption'): + for item in self.get('stock_items'): + total_value_of_stock_consumed += item.total_value + + return total_value_of_stock_consumed + def decrease_stock_quantity(self): stock_entry = frappe.get_doc({ "doctype": "Stock Entry", @@ -86,8 +109,22 @@ class AssetRepair(AccountsController): self.stock_entry = stock_entry.name - def on_cancel(self): - self.make_gl_entries(cancel=True) + def increase_stock_quantity(self): + stock_entry = frappe.get_doc({ + "doctype": "Stock Entry", + "stock_entry_type": "Material Receipt", + "company": self.company + }) + + for stock_item in self.get('stock_items'): + stock_entry.append('items', { + "s_warehouse": self.warehouse, + "item_code": stock_item.item, + "qty": stock_item.consumed_quantity + }) + + stock_entry.insert() + stock_entry.submit() def make_gl_entries(self, cancel=False): if flt(self.repair_cost) > 0: From ba9558527d78b75b0bb22356a290c37cd8f1d273 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 21 Jun 2021 18:57:11 +0530 Subject: [PATCH 068/369] fix(Asset Repair): Return Depreciation Schedule to original state on cancellation --- .../doctype/asset_repair/asset_repair.py | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index e7b8b45b7ee..01b36880be0 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -42,15 +42,25 @@ class AssetRepair(AccountsController): self.modify_depreciation_schedule() self.asset_doc.flags.ignore_validate_update_after_submit = True + self.asset_doc.prepare_depreciation_data() self.asset_doc.save() def on_cancel(self): + self.asset_doc = frappe.get_doc('Asset', self.asset) + if self.get('stock_consumption') or self.get('capitalize_repair_cost'): self.decrease_asset_value() if self.get('stock_consumption'): self.increase_stock_quantity() if self.get('capitalize_repair_cost'): + self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry') self.make_gl_entries(cancel=True) + if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation') and self.increase_in_asset_life: + self.revert_depreciation_schedule_on_cancellation() + + self.asset_doc.flags.ignore_validate_update_after_submit = True + self.asset_doc.prepare_depreciation_data() + self.asset_doc.save() def check_repair_status(self): if self.repair_status == "Pending": @@ -101,7 +111,8 @@ class AssetRepair(AccountsController): stock_entry.append('items', { "s_warehouse": self.warehouse, "item_code": stock_item.item, - "qty": stock_item.consumed_quantity + "qty": stock_item.consumed_quantity, + "basic_rate": stock_item.valuation_rate }) stock_entry.insert() @@ -118,7 +129,7 @@ class AssetRepair(AccountsController): for stock_item in self.get('stock_items'): stock_entry.append('items', { - "s_warehouse": self.warehouse, + "t_warehouse": self.warehouse, "item_code": stock_item.item, "qty": stock_item.consumed_quantity }) @@ -126,6 +137,8 @@ class AssetRepair(AccountsController): stock_entry.insert() stock_entry.submit() + self.stock_entry = stock_entry.name + def make_gl_entries(self, cancel=False): if flt(self.repair_cost) > 0: gl_entries = self.get_gl_entries() @@ -216,6 +229,34 @@ class AssetRepair(AccountsController): if asset.to_date > schedule_date: row.total_number_of_depreciations += 1 + def revert_depreciation_schedule_on_cancellation(self): + for row in self.asset_doc.finance_books: + row.total_number_of_depreciations -= self.increase_in_asset_life/row.frequency_of_depreciation + + self.asset_doc.flags.increase_in_asset_life = False + extra_months = self.increase_in_asset_life % row.frequency_of_depreciation + if extra_months != 0: + self.calculate_last_schedule_date_before_modification(self.asset_doc, row, extra_months) + + def calculate_last_schedule_date_before_modification(self, asset, row, extra_months): + asset.flags.increase_in_asset_life = True + number_of_pending_depreciations = cint(row.total_number_of_depreciations) - \ + cint(asset.number_of_depreciations_booked) + + # the Schedule Date in the final row of the modified Depreciation Schedule + last_schedule_date = asset.schedules[len(asset.schedules)-1].schedule_date + + # the Schedule Date in the final row of the original Depreciation Schedule + asset.to_date = add_months(last_schedule_date, -extra_months) + + # the latest possible date at which the depreciation can occur, without decreasing the Total Number of Depreciations + # if depreciations happen yearly and the Depreciation Posting Date is 01-01-2020, this could be 01-01-2021, 01-01-2022... + schedule_date = add_months(row.depreciation_start_date, + (number_of_pending_depreciations - 1) * cint(row.frequency_of_depreciation)) + + if asset.to_date < schedule_date: + row.total_number_of_depreciations -= 1 + @frappe.whitelist() def get_downtime(failure_date, completion_date): downtime = time_diff_in_hours(completion_date, failure_date) From c34e6b1889dfc49f4a40e2157f13f037feb88c9a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 22 Jun 2021 16:28:29 +0530 Subject: [PATCH 069/369] fix(Asset): Fix tests for Asset Repair --- erpnext/assets/doctype/asset/test_asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 29fbc9f15d6..f3667c7b95f 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -707,7 +707,7 @@ def create_asset(**args): "available_for_use_date": "2020-06-06", "location": "Test Location", "asset_owner": "Company", - "is_existing_asset": args.is_existing_asset or 0 + "is_existing_asset": 1 }) if asset.calculate_depreciation: From 55bca4cbc704217294f9fb1fc71ced01f58fb8c8 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 22 Jun 2021 16:33:10 +0530 Subject: [PATCH 070/369] fix(Asset Repair): Revert Stock Entry on cancellation --- .../doctype/asset_repair/asset_repair.py | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 01b36880be0..0049dcaded5 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -118,26 +118,11 @@ class AssetRepair(AccountsController): stock_entry.insert() stock_entry.submit() - self.stock_entry = stock_entry.name + self.db_set('stock_entry', stock_entry.name) def increase_stock_quantity(self): - stock_entry = frappe.get_doc({ - "doctype": "Stock Entry", - "stock_entry_type": "Material Receipt", - "company": self.company - }) - - for stock_item in self.get('stock_items'): - stock_entry.append('items', { - "t_warehouse": self.warehouse, - "item_code": stock_item.item, - "qty": stock_item.consumed_quantity - }) - - stock_entry.insert() - stock_entry.submit() - - self.stock_entry = stock_entry.name + stock_entry = frappe.get_doc('Stock Entry', self.stock_entry) + stock_entry.cancel() def make_gl_entries(self, cancel=False): if flt(self.repair_cost) > 0: From 307fe43e08919252c983935779b92f5679c5a307 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 22 Jun 2021 17:16:12 +0530 Subject: [PATCH 071/369] fix: Remove changes made to Asset Maintenance --- .../asset_maintenance/asset_maintenance.js | 3 -- .../asset_maintenance/asset_maintenance.json | 31 +--------------- .../asset_maintenance/asset_maintenance.py | 37 +------------------ 3 files changed, 3 insertions(+), 68 deletions(-) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js index 19393b7e9db..70b8654509f 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js @@ -30,10 +30,7 @@ frappe.ui.form.on('Asset Maintenance', { if(!frm.is_new()) { frm.trigger('make_dashboard'); } - - frm.toggle_display(['stock_consumption_details_section'], frm.doc.stock_consumption); }, - make_dashboard: (frm) => { if(!frm.is_new()) { frappe.call({ diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json index 63a55389d81..c0c2566fe23 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.json @@ -12,17 +12,13 @@ "column_break_3", "item_code", "item_name", - "stock_consumption", "section_break_6", "maintenance_team", "column_break_9", "maintenance_manager", "maintenance_manager_name", "section_break_8", - "asset_maintenance_tasks", - "stock_consumption_details_section", - "warehouse", - "stock_items" + "asset_maintenance_tasks" ], "fields": [ { @@ -104,33 +100,10 @@ "label": "Maintenance Tasks", "options": "Asset Maintenance Task", "reqd": 1 - }, - { - "default": "0", - "fieldname": "stock_consumption", - "fieldtype": "Check", - "label": "Stock Consumed During Maintenance" - }, - { - "fieldname": "stock_consumption_details_section", - "fieldtype": "Section Break", - "label": "Stock Consumption Details" - }, - { - "fieldname": "warehouse", - "fieldtype": "Link", - "label": "Warehouse", - "options": "Warehouse" - }, - { - "fieldname": "stock_items", - "fieldtype": "Table", - "label": "Stock Items", - "options": "Asset Repair Consumed Item" } ], "links": [], - "modified": "2021-06-21 14:53:46.041123", + "modified": "2020-05-28 20:28:32.993823", "modified_by": "Administrator", "module": "Assets", "name": "Asset Maintenance", diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index e3e654c398b..a506deec93e 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -19,45 +19,10 @@ class AssetMaintenance(Document): if not task.assign_to and self.docstatus == 0: throw(_("Row #{}: Please asign task to a member.").format(task.idx)) - if self.stock_consumption: - self.check_for_stock_items_and_warehouse() - self.increase_asset_value() - self.decrease_stock_quantity() - def on_update(self): for task in self.get('asset_maintenance_tasks'): assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date) - self.sync_maintenance_tasks() - - def check_for_stock_items_and_warehouse(self): - if self.stock_consumption: - if not self.stock_items: - frappe.throw(_("Please enter Stock Items consumed during Asset Maintenance.")) - if not self.warehouse: - frappe.throw(_("Please enter Warehouse from which Stock Items consumed during Asset Maintenance were taken.")) - - def increase_asset_value(self): - asset_value = frappe.db.get_value('Asset', self.asset_name, 'asset_value') - for item in self.stock_items: - asset_value += item.total_value - - frappe.db.set_value('Asset', self.asset_name, 'asset_value', asset_value) - - def decrease_stock_quantity(self): - stock_entry = frappe.get_doc({ - "doctype": "Stock Entry", - "stock_entry_type": "Material Issue" - }) - - for stock_item in self.stock_items: - stock_entry.append('items', { - "s_warehouse": self.warehouse, - "item_code": stock_item.item, - "qty": stock_item.consumed_quantity - }) - - stock_entry.insert() - stock_entry.submit() + self.sync_maintenance_tasks() def sync_maintenance_tasks(self): tasks_names = [] From 72ea64f6ac3de658758dd9249d361f28337053e5 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 22 Jun 2021 17:24:14 +0530 Subject: [PATCH 072/369] fix: Sider issues --- erpnext/assets/doctype/asset_repair/asset_repair.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 0049dcaded5..6054258ea69 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -14,6 +14,9 @@ class AssetRepair(AccountsController): def validate(self): self.asset_doc = frappe.get_doc('Asset', self.asset) self.update_status() + + if self.get('stock_items'): + self.set_total_value() self.calculate_total_repair_cost() def update_status(self): @@ -22,6 +25,10 @@ class AssetRepair(AccountsController): else: self.asset_doc.set_status() + def set_total_value(self): + for item in self.get('stock_items'): + item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity) + def calculate_total_repair_cost(self): self.total_repair_cost = self.repair_cost From 3ba9fb32de38fbaaa3109938308b84ab3c9b31e6 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 23 Jun 2021 13:26:47 +0530 Subject: [PATCH 073/369] fix(Asset Repair): Replace asset_value with value_after_depreciation in tests --- .../doctype/asset_repair/test_asset_repair.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index d1b417fd384..52a960e8509 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -74,21 +74,21 @@ class TestAssetRepair(unittest.TestCase): self.assertEqual(stock_entry.items[0].qty, asset_repair.stock_items[0].consumed_quantity) def test_increase_in_asset_value_due_to_stock_consumption(self): - asset = create_asset() - initial_asset_value = asset.asset_value + asset = create_asset(calculate_depreciation = 1) + initial_asset_value = get_asset_value(asset) asset_repair = create_asset_repair(asset= asset, stock_consumption = 1, submit = 1) asset.reload() - increase_in_asset_value = asset.asset_value - initial_asset_value + increase_in_asset_value = get_asset_value(asset) - initial_asset_value self.assertEqual(asset_repair.stock_items[0].total_value, increase_in_asset_value) def test_increase_in_asset_value_due_to_repair_cost_capitalisation(self): - asset = create_asset() - initial_asset_value = asset.asset_value + asset = create_asset(calculate_depreciation = 1) + initial_asset_value = get_asset_value(asset) asset_repair = create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1) asset.reload() - increase_in_asset_value = asset.asset_value - initial_asset_value + increase_in_asset_value = get_asset_value(asset) - initial_asset_value self.assertEqual(asset_repair.repair_cost, increase_in_asset_value) def test_purchase_invoice(self): @@ -109,6 +109,9 @@ class TestAssetRepair(unittest.TestCase): self.assertEqual((initial_num_of_depreciations + 1), num_of_depreciations(asset)) self.assertEqual(asset.schedules[-1].accumulated_depreciation_amount, asset.finance_books[0].value_after_depreciation) +def get_asset_value(asset): + return asset.finance_books[0].value_after_depreciation + def num_of_depreciations(asset): return asset.finance_books[0].total_number_of_depreciations From 39dba43b87423d82821b2da3dc87cf79c62025ca Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 23 Jun 2021 22:19:05 +0530 Subject: [PATCH 074/369] fix(Asset): Fix value_after_depreciation calculation --- erpnext/assets/doctype/asset/asset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 63b70f66132..110922e66b9 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -182,10 +182,10 @@ class Asset(AccountsController): # value_after_depreciation - current Asset value if d.value_after_depreciation: value_after_depreciation = (flt(d.value_after_depreciation) - - flt(self.opening_accumulated_depreciation)) - flt(d.expected_value_after_useful_life) + flt(self.opening_accumulated_depreciation)) else: value_after_depreciation = (flt(self.gross_purchase_amount) - - flt(self.opening_accumulated_depreciation)) - flt(d.expected_value_after_useful_life) + flt(self.opening_accumulated_depreciation)) d.value_after_depreciation = value_after_depreciation From 18bbfdf343cca9bfed4dd648c5394cf6ced81bc9 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 23 Jun 2021 22:26:45 +0530 Subject: [PATCH 075/369] fix(Asset Repair): Remove test that's no longer necessary --- erpnext/assets/doctype/asset_repair/test_asset_repair.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 52a960e8509..b3d78b3bfbb 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -13,12 +13,6 @@ class TestAssetRepair(unittest.TestCase): create_asset_data() frappe.db.sql("delete from `tabTax Rule`") - def test_completion_date(self): - asset_repair = create_asset_repair() - asset_repair.repair_status = "Completed" - asset_repair.save() - self.assertTrue(asset_repair.completion_date) - def test_update_status(self): asset = create_asset() initial_status = asset.status From f3ae1dd23b05aed611c657931583474f2b5070c2 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 24 Jun 2021 12:44:13 +0530 Subject: [PATCH 076/369] fix(Asset): Fix test --- erpnext/assets/doctype/asset/test_asset.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index f3667c7b95f..32bdb5224a7 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -125,7 +125,6 @@ class TestAsset(unittest.TestCase): "frequency_of_depreciation": 12, "depreciation_start_date": "2030-12-31" }) - asset.insert() self.assertEqual(asset.status, "Draft") asset.save() expected_schedules = [ From 81bcae7433206d99d6f5cffbe857b96478a14909 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 24 Jun 2021 13:22:26 +0530 Subject: [PATCH 077/369] fix(Asset): Remove redundant code --- erpnext/assets/doctype/asset/test_asset.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 32bdb5224a7..59fbe3b0301 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -153,9 +153,8 @@ class TestAsset(unittest.TestCase): "frequency_of_depreciation": 12, "depreciation_start_date": '2030-12-31' }) - asset.insert() - self.assertEqual(asset.status, "Draft") asset.save() + self.assertEqual(asset.status, "Draft") expected_schedules = [ ['2030-12-31', 66667.00, 66667.00], @@ -184,7 +183,7 @@ class TestAsset(unittest.TestCase): "frequency_of_depreciation": 12, "depreciation_start_date": "2030-12-31" }) - asset.insert() + asset.save() self.assertEqual(asset.status, "Draft") expected_schedules = [ @@ -215,7 +214,6 @@ class TestAsset(unittest.TestCase): "depreciation_start_date": "2030-12-31" }) - asset.insert() asset.save() expected_schedules = [ @@ -246,7 +244,6 @@ class TestAsset(unittest.TestCase): "frequency_of_depreciation": 10, "depreciation_start_date": "2020-12-31" }) - asset.insert() asset.submit() asset.load_from_db() self.assertEqual(asset.status, "Submitted") @@ -349,7 +346,6 @@ class TestAsset(unittest.TestCase): "frequency_of_depreciation": 10, "depreciation_start_date": "2020-12-31" }) - asset.insert() asset.submit() post_depreciation_entries(date="2021-01-01") @@ -379,7 +375,6 @@ class TestAsset(unittest.TestCase): "total_number_of_depreciations": 10, "frequency_of_depreciation": 1 }) - asset.insert() asset.submit() post_depreciation_entries(date=add_months('2020-01-01', 4)) @@ -423,7 +418,6 @@ class TestAsset(unittest.TestCase): "frequency_of_depreciation": 10, "depreciation_start_date": "2020-12-31" }) - asset.insert() asset.submit() post_depreciation_entries(date="2021-01-01") @@ -467,7 +461,7 @@ class TestAsset(unittest.TestCase): "total_number_of_depreciations": 3, "frequency_of_depreciation": 10 }) - asset.insert() + asset.save() accumulated_depreciation_after_full_schedule = \ max(d.accumulated_depreciation_amount for d in asset.get("schedules")) From cba0966ec59e719ae52c10e38b234f4dd4958525 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 24 Jun 2021 14:56:34 +0530 Subject: [PATCH 078/369] fix(Asset Repair): Change controller hooks --- erpnext/assets/doctype/asset_repair/asset_repair.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 6054258ea69..64c51fd8c3e 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -35,7 +35,7 @@ class AssetRepair(AccountsController): total_value_of_stock_consumed = self.get_total_value_of_stock_consumed() self.total_repair_cost += total_value_of_stock_consumed - def on_submit(self): + def before_submit(self): self.check_repair_status() if self.get('stock_consumption') or self.get('capitalize_repair_cost'): @@ -52,7 +52,7 @@ class AssetRepair(AccountsController): self.asset_doc.prepare_depreciation_data() self.asset_doc.save() - def on_cancel(self): + def before_cancel(self): self.asset_doc = frappe.get_doc('Asset', self.asset) if self.get('stock_consumption') or self.get('capitalize_repair_cost'): From 7c37e83535b0c2a7ddc7ff50718fb1ed3e69409c Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 24 Jun 2021 15:04:44 +0530 Subject: [PATCH 079/369] fix(Asset): Remove to_date field --- erpnext/assets/doctype/asset/asset.json | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index d77eb104184..de060757e2e 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -53,7 +53,6 @@ "next_depreciation_date", "section_break_14", "schedules", - "to_date", "insurance_details", "policy_number", "insurer", @@ -481,12 +480,6 @@ "fieldname": "section_break_36", "fieldtype": "Section Break", "label": "Finance Books" - }, - { - "fieldname": "to_date", - "fieldtype": "Date", - "hidden": 1, - "label": "To Date" } ], "idx": 72, @@ -509,7 +502,7 @@ "link_fieldname": "asset" } ], - "modified": "2021-06-19 13:56:58.450182", + "modified": "2021-06-24 14:58:51.097908", "modified_by": "Administrator", "module": "Assets", "name": "Asset", From 597016bb34942170354994f7b90eeb6b529c60f5 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 24 Jun 2021 22:18:59 +0530 Subject: [PATCH 080/369] fix(Asset): Remove extra tabs --- erpnext/assets/doctype/asset/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 110922e66b9..0e1bba63621 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -196,7 +196,7 @@ class Asset(AccountsController): if has_pro_rata: number_of_pending_depreciations += 1 - + skip_row = False for n in range(start, number_of_pending_depreciations): # If depreciation is already completed (for double declining balance) From 267fed2d239b240e12f3d4526e1db79c3ce8dd92 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 24 Jun 2021 22:21:08 +0530 Subject: [PATCH 081/369] fix(Asset): Add comment --- erpnext/assets/doctype/asset/asset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 0e1bba63621..2fe71672b37 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -224,6 +224,7 @@ class Asset(AccountsController): # For last row elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1: if not self.flags.increase_in_asset_life: + # In case of increase_in_asset_life, the self.to_date is already set on asset_repair submission self.to_date = add_months(self.available_for_use_date, n * cint(d.frequency_of_depreciation)) From c8caafa680edeeea0f16655bed200187be4010b8 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 24 Jun 2021 22:25:45 +0530 Subject: [PATCH 082/369] fix(Asset Repair): Move filters for cost_center, warehouse and project to setup --- .../doctype/asset_repair/asset_repair.js | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index 91bed4fdd03..1cebfff66e5 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -2,6 +2,34 @@ // For license information, please see license.txt frappe.ui.form.on('Asset Repair', { + setup: function(frm) { + frm.fields_dict.cost_center.get_query = function(doc) { + return { + filters: { + 'is_group': 0, + 'company': doc.company + } + }; + }; + + frm.fields_dict.project.get_query = function(doc) { + return { + filters: { + 'company': doc.company + } + }; + }; + + frm.fields_dict.warehouse.get_query = function(doc) { + return { + filters: { + 'is_group': 0, + 'company': doc.company + } + }; + }; + }, + refresh: function(frm) { if (frm.doc.docstatus) { frm.add_custom_button("View General Ledger", function() { @@ -40,30 +68,4 @@ frappe.ui.form.on('Asset Repair Consumed Item', { var row = locals[cdt][cdn]; frappe.model.set_value(cdt, cdn, 'total_value', row.consumed_quantity * row.valuation_rate); }, -}); - -cur_frm.fields_dict.cost_center.get_query = function(doc) { - return { - filters: { - 'is_group': 0, - 'company': doc.company - } - }; -}; - -cur_frm.fields_dict.project.get_query = function(doc) { - return { - filters: { - 'company': doc.company - } - }; -}; - -cur_frm.fields_dict.warehouse.get_query = function(doc) { - return { - filters: { - 'is_group': 0, - 'company': doc.company - } - }; -}; \ No newline at end of file +}); \ No newline at end of file From e328e3b48a51fb44b2fd744d80421bb555f777d1 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 24 Jun 2021 22:27:30 +0530 Subject: [PATCH 083/369] fix(Asset Repair): Edit description for total_repair_cost --- erpnext/assets/doctype/asset_repair/asset_repair.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 14f18b5309c..3f62443bda4 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -211,7 +211,7 @@ }, { "depends_on": "eval: doc.stock_consumption && doc.total_repair_cost > 0", - "description": "Sum of Repair Cost and the total value of all Stock Items consumed during the repair.", + "description": "Sum of Repair Cost and Value of Consumed Stock Items.", "fieldname": "total_repair_cost", "fieldtype": "Currency", "label": "Total Repair Cost", From fd7fb37697a7720f9e752588799514f917416648 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 24 Jun 2021 22:30:08 +0530 Subject: [PATCH 084/369] fix(Asset Repair): Simplify code for Asset Repair creation in tests --- erpnext/assets/doctype/asset_repair/test_asset_repair.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index b3d78b3bfbb..30bbb37851e 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -138,10 +138,7 @@ def create_asset_repair(**args): "consumed_quantity": args.qty or 1 }) - try: - asset_repair.save() - except frappe.DuplicateEntryError: - pass + asset_repair.insert(ignore_if_duplicate=True) if args.submit: asset_repair.repair_status = "Completed" From 073b50f7fd0a6c89f86d09bb5b497d7e3a2b188d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 26 Jun 2021 01:32:17 +0530 Subject: [PATCH 085/369] fix(Asset Repair): Rearrange fields --- erpnext/assets/doctype/asset_repair/asset_repair.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json index 3f62443bda4..19528a26ccd 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.json +++ b/erpnext/assets/doctype/asset_repair/asset_repair.json @@ -8,10 +8,10 @@ "engine": "InnoDB", "field_order": [ "asset", - "naming_series", + "company", "column_break_2", "asset_name", - "company", + "naming_series", "section_break_5", "failure_date", "repair_status", @@ -263,7 +263,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-21 14:53:46.665576", + "modified": "2021-06-25 13:14:38.307723", "modified_by": "Administrator", "module": "Assets", "name": "Asset Repair", From 2e507b47a8f82b62cf4674fe962cf6cc9e035da1 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 28 Jun 2021 11:42:28 +0530 Subject: [PATCH 086/369] fix(Asset Repair): cancellation --- erpnext/assets/doctype/asset_repair/asset_repair.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 64c51fd8c3e..d32fdf7054f 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -30,7 +30,7 @@ class AssetRepair(AccountsController): item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity) def calculate_total_repair_cost(self): - self.total_repair_cost = self.repair_cost + self.total_repair_cost = flt(self.repair_cost) total_value_of_stock_consumed = self.get_total_value_of_stock_consumed() self.total_repair_cost += total_value_of_stock_consumed @@ -129,6 +129,7 @@ class AssetRepair(AccountsController): def increase_stock_quantity(self): stock_entry = frappe.get_doc('Stock Entry', self.stock_entry) + stock_entry.flags.ignore_links = True stock_entry.cancel() def make_gl_entries(self, cancel=False): @@ -252,4 +253,4 @@ class AssetRepair(AccountsController): @frappe.whitelist() def get_downtime(failure_date, completion_date): downtime = time_diff_in_hours(completion_date, failure_date) - return round(downtime, 2) \ No newline at end of file + return round(downtime, 2) From 40793f4a18c4a23d1414654116657323d7ea800c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 13 Jul 2021 13:17:19 +0530 Subject: [PATCH 087/369] test: introduce cypress tests Co-authored-by: Ankush Co-authored-by: Nabin Hait --- .eslintrc | 6 +- .github/helper/install.sh | 2 +- .github/workflows/ui-tests.yml | 108 +++++++++++++++++++++++++++ cypress.json | 11 +++ cypress/fixtures/example.json | 5 ++ cypress/integration/test_customer.js | 13 ++++ cypress/plugins/index.js | 17 +++++ cypress/support/commands.js | 31 ++++++++ cypress/support/index.js | 26 +++++++ cypress/tsconfig.json | 12 +++ 10 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/ui-tests.yml create mode 100644 cypress.json create mode 100644 cypress/fixtures/example.json create mode 100644 cypress/integration/test_customer.js create mode 100644 cypress/plugins/index.js create mode 100644 cypress/support/commands.js create mode 100644 cypress/support/index.js create mode 100644 cypress/tsconfig.json diff --git a/.eslintrc b/.eslintrc index 3b6ab7498d9..ecfaab23eed 100644 --- a/.eslintrc +++ b/.eslintrc @@ -147,10 +147,14 @@ "Chart": true, "Cypress": true, "cy": true, + "describe": true, + "expect": true, "it": true, "context": true, "before": true, "beforeEach": true, - "onScan": true + "onScan": true, + "extend_cscript": true, + "localforage": true } } diff --git a/.github/helper/install.sh b/.github/helper/install.sh index 7b0f944c669..a6a6069d358 100644 --- a/.github/helper/install.sh +++ b/.github/helper/install.sh @@ -42,5 +42,5 @@ sed -i 's/socketio:/# socketio:/g' Procfile sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile bench get-app erpnext "${GITHUB_WORKSPACE}" -bench start & +bench start &> bench_run_logs.txt & bench --site test_site reinstall --yes diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml new file mode 100644 index 00000000000..412a05b0a15 --- /dev/null +++ b/.github/workflows/ui-tests.yml @@ -0,0 +1,108 @@ +name: UI + +on: + pull_request: + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-18.04 + + strategy: + fail-fast: false + + name: UI Tests (Cypress) + + services: + mysql: + image: mariadb:10.3 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: YES + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3 + + steps: + - name: Clone + uses: actions/checkout@v2 + + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.7 + + - uses: actions/setup-node@v2 + with: + node-version: 14 + check-latest: true + + - name: Add to Hosts + run: | + echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + ${{ runner.os }}- + + - name: Cache node modules + uses: actions/cache@v2 + env: + cache-name: cache-node-modules + with: + path: ~/.npm + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn cache dir)" + + - uses: actions/cache@v2 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + + - name: Cache cypress binary + uses: actions/cache@v2 + with: + path: ~/.cache + key: ${{ runner.os }}-cypress- + restore-keys: | + ${{ runner.os }}-cypress- + ${{ runner.os }}- + + - name: Install + run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh + env: + DB: mariadb + TYPE: ui + + - name: Site Setup + run: cd ~/frappe-bench/ && bench --site test_site execute erpnext.setup.utils.before_tests + + - name: cypress pre-requisites + run: cd ~/frappe-bench/apps/frappe && yarn add cypress-file-upload@^5 --no-lockfile + + + - name: Build Assets + run: cd ~/frappe-bench/ && bench build + + - name: UI Tests + run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless + env: + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + + - name: Show bench console if tests failed + if: ${{ failure() }} + run: cat ~/frappe-bench/bench_run_logs.txt diff --git a/cypress.json b/cypress.json new file mode 100644 index 00000000000..2f5026f62cc --- /dev/null +++ b/cypress.json @@ -0,0 +1,11 @@ +{ + "baseUrl": "http://test_site:8000/", + "projectId": "da59y9", + "adminPassword": "admin", + "defaultCommandTimeout": 20000, + "pageLoadTimeout": 15000, + "retries": { + "runMode": 2, + "openMode": 2 + } +} \ No newline at end of file diff --git a/cypress/fixtures/example.json b/cypress/fixtures/example.json new file mode 100644 index 00000000000..da18d9352a1 --- /dev/null +++ b/cypress/fixtures/example.json @@ -0,0 +1,5 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io", + "body": "Fixtures are a great way to mock data for responses to routes" +} \ No newline at end of file diff --git a/cypress/integration/test_customer.js b/cypress/integration/test_customer.js new file mode 100644 index 00000000000..3d6ed5d0d89 --- /dev/null +++ b/cypress/integration/test_customer.js @@ -0,0 +1,13 @@ + +context('Customer', () => { + before(() => { + cy.login(); + }); + it('Check Customer Group', () => { + cy.visit(`app/customer/`); + cy.get('.primary-action').click(); + cy.wait(500); + cy.get('.custom-actions > .btn').click(); + cy.get_field('customer_group', 'Link').should('have.value', 'All Customer Groups'); + }); +}); diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js new file mode 100644 index 00000000000..07d9804a733 --- /dev/null +++ b/cypress/plugins/index.js @@ -0,0 +1,17 @@ +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +module.exports = () => { + // `on` is used to hook into various events Cypress emits + // `config` is the resolved Cypress config +}; diff --git a/cypress/support/commands.js b/cypress/support/commands.js new file mode 100644 index 00000000000..7ddc80ab8dd --- /dev/null +++ b/cypress/support/commands.js @@ -0,0 +1,31 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** +// +// +// -- This is a parent command -- +// Cypress.Commands.add("login", (email, password) => { ... }); +// +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }); +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }); +// +// +// -- This is will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }); + +const slug = (name) => name.toLowerCase().replace(" ", "-"); + +Cypress.Commands.add("go_to_doc", (doctype, name) => { + cy.visit(`/app/${slug(doctype)}/${encodeURIComponent(name)}`); +}); diff --git a/cypress/support/index.js b/cypress/support/index.js new file mode 100644 index 00000000000..72070cc81c4 --- /dev/null +++ b/cypress/support/index.js @@ -0,0 +1,26 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; +import '../../../frappe/cypress/support/commands' // eslint-disable-line + + +// Alternatively you can use CommonJS syntax: +// require('./commands') + +Cypress.Cookies.defaults({ + preserve: 'sid' +}); diff --git a/cypress/tsconfig.json b/cypress/tsconfig.json new file mode 100644 index 00000000000..d90ebf6856d --- /dev/null +++ b/cypress/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "allowJs": true, + "baseUrl": "../node_modules", + "types": [ + "cypress" + ] + }, + "include": [ + "**/*.*" + ] +} \ No newline at end of file From f004b404d1ed344790600d3a1d2e038a53370031 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 14 Jul 2021 23:50:54 +0530 Subject: [PATCH 088/369] test: UI tests for org chart desktop --- .../test_organizational_chart_desktop.js | 102 ++++++++++++++++++ .../hierarchy_chart_desktop.js | 3 +- erpnext/tests/ui_test_helpers.py | 53 +++++++++ 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 cypress/integration/test_organizational_chart_desktop.js create mode 100644 erpnext/tests/ui_test_helpers.py diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js new file mode 100644 index 00000000000..d50d5513301 --- /dev/null +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -0,0 +1,102 @@ +context('Organizational Chart', () => { + before(() => { + cy.login(); + cy.visit('/app/website'); + + cy.visit(`app/organizational-chart`); + cy.fill_field('company', 'Test Org Chart'); + cy.get('body').click(); + cy.wait(500); + }); + + beforeEach(() => { + cy.window().its('frappe').then(frappe => { + return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); + }).as('employee_records'); + }); + + it('renders root nodes and loads children for the first expandable node', () => { + // check rendered root nodes and the node name, title, connections + cy.get('.hierarchy').find('.root-level ul.node-children').children() + .should('have.length', 2) + .first() + .as('first-child'); + + cy.get('@first-child').get('.node-name').contains('Test Employee 1'); + cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO'); + cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2 Connections'); + + // check children of first node + cy.get('@employee_records').then(employee_records => { + // children of 1st root visible + cy.get(`[data-parent="${employee_records.message[0]}"]`).as('child-node') + cy.get('@child-node') + .should('have.length', 1) + .should('be.visible'); + cy.get('@child-node').get('.node-name').contains('Test Employee 3'); + + // connectors between first root node and immediate child + cy.get(`path[data-parent="${employee_records.message[0]}"]`) + .should('be.visible') + .invoke('attr', 'data-child') + .should('equal', employee_records.message[2]); + }); + }); + + it('hides active nodes children and connectors on expanding sibling node', () => { + cy.get('@employee_records').then(employee_records => { + // click sibling + cy.get(`#${employee_records.message[1]}`) + .click() + .should('have.class', 'active'); + + // child nodes and connectors hidden + cy.get(`[data-parent="${employee_records.message[0]}"]`).should('not.be.visible'); + cy.get(`path[data-parent="${employee_records.message[0]}"]`).should('not.be.visible'); + }); + }); + + it('collapses previous level nodes and refreshes connectors on expanding child node', () => { + cy.get('@employee_records').then(employee_records => { + // click child node + cy.get(`#${employee_records.message[3]}`) + .click() + .should('have.class', 'active'); + + // previous level nodes: parent should be on active-path; other nodes should be collapsed + cy.get(`#${employee_records.message[0]}`).should('have.class', 'collapsed'); + cy.get(`#${employee_records.message[1]}`).should('have.class', 'active-path'); + + // previous level connectors refreshed + cy.get(`path[data-parent="${employee_records.message[1]}"]`) + .should('have.class', 'collapsed-connector'); + + // child node's children and connectors rendered + cy.get(`[data-parent="${employee_records.message[3]}"]`).should('be.visible'); + cy.get(`path[data-parent="${employee_records.message[3]}"]`).should('be.visible'); + }); + }); + + it('expands previous level nodes', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[0]}`) + .click() + .should('have.class', 'active'); + + cy.get(`[data-parent="${employee_records.message[0]}"]`) + .should('be.visible'); + + cy.get('ul.hierarchy').children().should('have.length', 2); + cy.get(`#connectors`).children().should('have.length', 1); + }); + }); + + it('edit node navigates to employee master', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node') + .click(); + + cy.url().should('include', `/employee/${employee_records.message[0]}`); + }); + }); +}); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 374787c6ef2..fe4d17c2102 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -49,7 +49,8 @@ erpnext.HierarchyChart = class { title: node.title, image: node.image, parent: node.parent_id, - connections: node.connections + connections: node.connections, + is_mobile: false }); node.parent.append(node_card); diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py new file mode 100644 index 00000000000..8e67b1cd346 --- /dev/null +++ b/erpnext/tests/ui_test_helpers.py @@ -0,0 +1,53 @@ +import frappe +from frappe import _ +from frappe.utils import getdate + +@frappe.whitelist() +def create_employee_records(): + company = create_company() + create_missing_designation() + + emp1 = create_employee('Test Employee 1', 'CEO') + emp2 = create_employee('Test Employee 2', 'CTO') + emp3 = create_employee('Test Employee 3', 'Head of Marketing and Sales', emp1) + emp4 = create_employee('Test Employee 4', 'Project Manager', emp2) + emp5 = create_employee('Test Employee 5', 'Analyst', emp3) + emp6 = create_employee('Test Employee 6', 'Software Developer', emp4) + + employees = [emp1, emp2, emp3, emp4, emp5, emp6] + return employees + +def create_company(): + company = frappe.db.exists('Company', 'Test Org Chart') + if not company: + company = frappe.get_doc({ + 'doctype': 'Company', + 'company_name': 'Test Org Chart', + 'country': 'India', + 'default_currency': 'INR' + }).insert().name + + return company + +def create_employee(first_name, designation, reports_to=None): + employee = frappe.db.exists('Employee', {'first_name': first_name, 'designation': designation}) + if not employee: + employee = frappe.get_doc({ + 'doctype': 'Employee', + 'first_name': first_name, + 'company': 'Test Org Chart', + 'gender': 'Female', + 'date_of_birth': getdate('08-12-1998'), + 'date_of_joining': getdate('01-01-2021'), + 'designation': designation, + 'reports_to': reports_to + }).insert().name + + return employee + +def create_missing_designation(): + if not frappe.db.exists('Designation', 'CTO'): + frappe.get_doc({ + 'doctype': 'Designation', + 'designation_name': 'CTO' + }).insert() \ No newline at end of file From ee7eaf9c70484dec3db6f282ca2f74a4a4ac2a2d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 15 Jul 2021 19:19:09 +0530 Subject: [PATCH 089/369] test: UI tests for org chart mobile fix(mobile): detach node before emptying hierarchy fix(mobile): sibling group not rendering for first level --- .../test_organizational_chart_mobile.js | 182 ++++++++++++++++++ .../hierarchy_chart/hierarchy_chart_mobile.js | 13 +- erpnext/tests/ui_test_helpers.py | 7 +- 3 files changed, 195 insertions(+), 7 deletions(-) create mode 100644 cypress/integration/test_organizational_chart_mobile.js diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js new file mode 100644 index 00000000000..656051289f1 --- /dev/null +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -0,0 +1,182 @@ +context('Organizational Chart Mobile', () => { + before(() => { + cy.login(); + cy.viewport(375, 667); + cy.visit('/app/website'); + + cy.visit(`app/organizational-chart`); + cy.wait(500); + cy.fill_field('company', 'Test Org Chart'); + cy.get('body').click(); + cy.wait(500); + }); + + beforeEach(() => { + cy.viewport(375, 667); + cy.wait(500); + + cy.window().its('frappe').then(frappe => { + return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); + }).as('employee_records'); + }); + + it('renders root nodes', () => { + // check rendered root nodes and the node name, title, connections + cy.get('.hierarchy-mobile').find('.root-level').children() + .should('have.length', 2) + .first() + .as('first-child'); + + cy.get('@first-child').get('.node-name').contains('Test Employee 1'); + cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO'); + cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2'); + }); + + it('expands root node', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[1]}`) + .click() + .should('have.class', 'active'); + + // other root node removed + cy.get(`#${employee_records.message[0]}`).should('not.exist'); + + // children of active root node + cy.get('.hierarchy-mobile').find('.level').first().find('ul.node-children').children() + .should('have.length', 2) + + cy.get(`[data-parent="${employee_records.message[1]}"]`).first().as('child-node'); + cy.get('@child-node').should('be.visible'); + + cy.get('@child-node') + .get('.node-name') + .contains('Test Employee 4'); + + // connectors between root node and immediate children + cy.get(`path[data-parent="${employee_records.message[1]}"]`).as('connectors'); + cy.get('@connectors') + .should('have.length', 2) + .should('be.visible') + + cy.get('@connectors') + .first() + .invoke('attr', 'data-child') + .should('eq', employee_records.message[3]); + }); + }); + + it('expands child node', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[3]}`) + .click() + .should('have.class', 'active') + .as('expanded_node'); + + // 2 levels on screen; 1 on active path; 1 collapsed + cy.get('.hierarchy-mobile').children().should('have.length', 2); + cy.get(`#${employee_records.message[1]}`).should('have.class', 'active-path'); + + // children of expanded node visible + cy.get('@expanded_node') + .next() + .should('have.class', 'node-children') + .as('node-children'); + + cy.get('@node-children').children().should('have.length', 1); + cy.get('@node-children') + .first() + .get('.node-card') + .should('have.class', 'active-child') + .contains('Test Employee 7'); + + // orphan connectors removed + cy.get(`#connectors`).children().should('have.length', 2); + }); + }); + + it('renders sibling group', () => { + cy.get('@employee_records').then(employee_records => { + // sibling group visible for parent + cy.get(`#${employee_records.message[1]}`) + .next() + .as('sibling_group'); + + cy.get('@sibling_group') + .should('have.attr', 'data-parent', 'undefined') + .should('have.class', 'node-group') + .and('have.class', 'collapsed') + + cy.get('@sibling_group').get('.avatar-group').children().as('siblings'); + cy.get('@siblings').should('have.length', 1); + cy.get('@siblings') + .first() + .should('have.attr', 'title', 'Test Employee 1'); + + }); + }); + + it('expands previous level nodes', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[6]}`) + .click() + .should('have.class', 'active'); + + // clicking on previous level node should remove all the nodes ahead + // and expand that node + cy.get(`#${employee_records.message[3]}`).click(); + cy.get(`#${employee_records.message[3]}`) + .should('have.class', 'active') + .should('not.have.class', 'active-path'); + + cy.get(`#${employee_records.message[6]}`).should('have.class', 'active-child'); + cy.get('.hierarchy-mobile').children().should('have.length', 2); + cy.get(`#connectors`).children().should('have.length', 2); + }); + }); + + it('expands sibling group', () => { + cy.get('@employee_records').then(employee_records => { + // sibling group visible for parent + cy.get(`#${employee_records.message[6]}`).click() + + cy.get(`#${employee_records.message[3]}`) + .next() + .click(); + + // siblings of parent should be visible + cy.get('.hierarchy-mobile').prev().as('sibling_group'); + cy.get('@sibling_group') + .should('exist') + .should('have.class', 'sibling-group') + .should('not.have.class', 'collapsed'); + + cy.get(`#${employee_records.message[1]}`) + .should('be.visible') + .should('have.class', 'active'); + + cy.get(`[data-parent="${employee_records.message[1]}"]`) + .should('be.visible') + .should('have.length', 2) + .should('have.class', 'active-child'); + }); + }); + + it('goes to the respective level after clicking on non-collapsed sibling group', () => { + // click on non-collapsed sibling group + cy.get('.hierarchy-mobile') + .prev() + .click(); + + // should take you to that level + cy.get('.hierarchy-mobile').find('li.level .node-card').should('have.length', 2); + }); + + it('edit node navigates to employee master', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node') + .click(); + + cy.url().should('include', `/employee/${employee_records.message[0]}`); + }); + }); +}); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 5a6f1688767..bd7946a1e13 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -128,6 +128,9 @@ erpnext.HierarchyChartMobile = class { if (this.$hierarchy) this.$hierarchy.remove(); + if (this.$sibling_group) + this.$sibling_group.empty(); + this.$hierarchy = $( `
                      • @@ -173,7 +176,7 @@ erpnext.HierarchyChartMobile = class { if (this.$sibling_group) { const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent'); - if (node.parent_id !== sibling_parent) + if (node.parent_id !== undefined && node.parent_id != sibling_parent) this.$sibling_group.empty(); } @@ -376,9 +379,10 @@ erpnext.HierarchyChartMobile = class { let node_element = $(`#${node.id}`); node_element.click(function() { - let el = $(this).detach(); + let el = undefined; if (node.is_root) { + el = $(this).detach(); me.$hierarchy.empty(); $(`#connectors`).empty(); me.add_node_to_hierarchy(el, node); @@ -386,6 +390,7 @@ erpnext.HierarchyChartMobile = class { me.remove_levels_after_node(node); me.remove_orphaned_connectors(); } else { + el = $(this).detach(); me.add_node_to_hierarchy(el, node); me.collapse_node(); } @@ -514,10 +519,10 @@ erpnext.HierarchyChartMobile = class { level = $('.hierarchy-mobile > li:eq('+ level + ')'); level.nextAll('li').remove(); - let current_node = level.find(`#${node.id}`); let node_object = this.nodes[node.id]; - + let current_node = level.find(`#${node.id}`).detach(); current_node.removeClass('active-child active-path'); + node_object.expanded = 0; node_object.$children = undefined; diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py index 8e67b1cd346..99748dca02e 100644 --- a/erpnext/tests/ui_test_helpers.py +++ b/erpnext/tests/ui_test_helpers.py @@ -11,10 +11,11 @@ def create_employee_records(): emp2 = create_employee('Test Employee 2', 'CTO') emp3 = create_employee('Test Employee 3', 'Head of Marketing and Sales', emp1) emp4 = create_employee('Test Employee 4', 'Project Manager', emp2) - emp5 = create_employee('Test Employee 5', 'Analyst', emp3) - emp6 = create_employee('Test Employee 6', 'Software Developer', emp4) + emp5 = create_employee('Test Employee 5', 'Engineer', emp2) + emp6 = create_employee('Test Employee 6', 'Analyst', emp3) + emp7 = create_employee('Test Employee 7', 'Software Developer', emp4) - employees = [emp1, emp2, emp3, emp4, emp5, emp6] + employees = [emp1, emp2, emp3, emp4, emp5, emp6, emp7] return employees def create_company(): From 8961a267f649f487eb9f9e3fc63aa6d40765e193 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 15 Jul 2021 19:32:15 +0530 Subject: [PATCH 090/369] fix: sider --- cypress/integration/test_organizational_chart_desktop.js | 4 ++-- cypress/integration/test_organizational_chart_mobile.js | 8 ++++---- erpnext/tests/ui_test_helpers.py | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index d50d5513301..807ef5731a5 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -29,7 +29,7 @@ context('Organizational Chart', () => { // check children of first node cy.get('@employee_records').then(employee_records => { // children of 1st root visible - cy.get(`[data-parent="${employee_records.message[0]}"]`).as('child-node') + cy.get(`[data-parent="${employee_records.message[0]}"]`).as('child-node'); cy.get('@child-node') .should('have.length', 1) .should('be.visible'); @@ -39,7 +39,7 @@ context('Organizational Chart', () => { cy.get(`path[data-parent="${employee_records.message[0]}"]`) .should('be.visible') .invoke('attr', 'data-child') - .should('equal', employee_records.message[2]); + .should('equal', employee_records.message[2]); }); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 656051289f1..f48972bdc68 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -43,7 +43,7 @@ context('Organizational Chart Mobile', () => { // children of active root node cy.get('.hierarchy-mobile').find('.level').first().find('ul.node-children').children() - .should('have.length', 2) + .should('have.length', 2); cy.get(`[data-parent="${employee_records.message[1]}"]`).first().as('child-node'); cy.get('@child-node').should('be.visible'); @@ -56,7 +56,7 @@ context('Organizational Chart Mobile', () => { cy.get(`path[data-parent="${employee_records.message[1]}"]`).as('connectors'); cy.get('@connectors') .should('have.length', 2) - .should('be.visible') + .should('be.visible'); cy.get('@connectors') .first() @@ -104,7 +104,7 @@ context('Organizational Chart Mobile', () => { cy.get('@sibling_group') .should('have.attr', 'data-parent', 'undefined') .should('have.class', 'node-group') - .and('have.class', 'collapsed') + .and('have.class', 'collapsed'); cy.get('@sibling_group').get('.avatar-group').children().as('siblings'); cy.get('@siblings').should('have.length', 1); @@ -137,7 +137,7 @@ context('Organizational Chart Mobile', () => { it('expands sibling group', () => { cy.get('@employee_records').then(employee_records => { // sibling group visible for parent - cy.get(`#${employee_records.message[6]}`).click() + cy.get(`#${employee_records.message[6]}`).click(); cy.get(`#${employee_records.message[3]}`) .next() diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py index 99748dca02e..f66d69ba232 100644 --- a/erpnext/tests/ui_test_helpers.py +++ b/erpnext/tests/ui_test_helpers.py @@ -1,10 +1,9 @@ import frappe -from frappe import _ from frappe.utils import getdate @frappe.whitelist() def create_employee_records(): - company = create_company() + create_company() create_missing_designation() emp1 = create_employee('Test Employee 1', 'CEO') From 56cdcebbf4d4daa0bffeead320c1187507cc40f6 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 16 Jul 2021 02:23:55 +0530 Subject: [PATCH 091/369] fix: Sider issues --- erpnext/assets/doctype/asset/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 2fe71672b37..ecc35b05b35 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -228,7 +228,7 @@ class Asset(AccountsController): self.to_date = add_months(self.available_for_use_date, n * cint(d.frequency_of_depreciation)) - depreciation_amount, days, months = get_pro_rata_amt(d, + depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount, schedule_date, self.to_date) monthly_schedule_date = add_months(schedule_date, 1) From 2c3866a53ea29c20029b5dba75f5184a490b36ff Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 16 Jul 2021 10:32:38 +0530 Subject: [PATCH 092/369] ci(cypress): use env variable for key documentation ref: https://docs.cypress.io/guides/guides/command-line\#cypress-run --- .github/workflows/ui-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 412a05b0a15..0f13e653ecd 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -101,7 +101,7 @@ jobs: - name: UI Tests run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless env: - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + CYPRESS_RECORD_KEY: 60a8e3bf-08f5-45b1-9269-2b207d7d30cd - name: Show bench console if tests failed if: ${{ failure() }} From cad11707822064aa6fa85cebe4d6d54d53b186ee Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 19 Jul 2021 14:36:54 +0530 Subject: [PATCH 093/369] fix: Add missing cess amount in GSTR-3B report --- erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 641520437fb..6de228fbc7e 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -322,6 +322,9 @@ class GSTR3BReport(Document): inter_state_supply_details[(gst_category, place_of_supply)]['txval'] += taxable_value inter_state_supply_details[(gst_category, place_of_supply)]['iamt'] += (taxable_value * rate /100) + if self.invoice_cess.get(inv): + self.report_dict['sup_details']['osup_det']['csamt'] += flt(self.invoice_cess.get(inv), 2) + self.set_inter_state_supply(inter_state_supply_details) def set_supplies_liable_to_reverse_charge(self): From 9d89b2afcf2cb2cffa8857dc66b0289a165b711b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 15:47:31 +0530 Subject: [PATCH 094/369] fix: UI tests --- cypress.json | 2 +- cypress/integration/test_organizational_chart_desktop.js | 6 ++++-- cypress/integration/test_organizational_chart_mobile.js | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/cypress.json b/cypress.json index 2f5026f62cc..afcd657c53d 100644 --- a/cypress.json +++ b/cypress.json @@ -1,5 +1,5 @@ { - "baseUrl": "http://test_site:8000/", + "baseUrl": "http://test_site:8000", "projectId": "da59y9", "adminPassword": "admin", "defaultCommandTimeout": 20000, diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 807ef5731a5..b11b9ea6ab9 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -2,9 +2,11 @@ context('Organizational Chart', () => { before(() => { cy.login(); cy.visit('/app/website'); + cy.awesomebar('Organizational Chart'); + + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input').type('Test Org Chart'); - cy.visit(`app/organizational-chart`); - cy.fill_field('company', 'Test Org Chart'); cy.get('body').click(); cy.wait(500); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index f48972bdc68..a42562ff2e1 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -3,10 +3,11 @@ context('Organizational Chart Mobile', () => { cy.login(); cy.viewport(375, 667); cy.visit('/app/website'); + cy.awesomebar('Organizational Chart'); + + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input').type('Test Org Chart'); - cy.visit(`app/organizational-chart`); - cy.wait(500); - cy.fill_field('company', 'Test Org Chart'); cy.get('body').click(); cy.wait(500); }); From 7270ab5c2057ec21b2baf37daf937905f1886dc1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 16:26:17 +0530 Subject: [PATCH 095/369] fix(tests): clear filter before typing --- cypress/integration/test_organizational_chart_desktop.js | 2 +- cypress/integration/test_organizational_chart_mobile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index b11b9ea6ab9..516d254b813 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -5,7 +5,7 @@ context('Organizational Chart', () => { cy.awesomebar('Organizational Chart'); cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input').type('Test Org Chart'); + cy.get('@input').clear().type('Test Org Chart'); cy.get('body').click(); cy.wait(500); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index a42562ff2e1..503db68c18c 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -6,7 +6,7 @@ context('Organizational Chart Mobile', () => { cy.awesomebar('Organizational Chart'); cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input').type('Test Org Chart'); + cy.get('@input').clear().type('Test Org Chart'); cy.get('body').click(); cy.wait(500); From 6e46be5058be06ecbd180f507f8267c1bc44b07c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 17:03:17 +0530 Subject: [PATCH 096/369] fix(tests): apply filters correctly --- .../test_organizational_chart_desktop.js | 7 ++++--- .../test_organizational_chart_mobile.js | 21 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 516d254b813..cb12eb5c0c4 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -5,14 +5,15 @@ context('Organizational Chart', () => { cy.awesomebar('Organizational Chart'); cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input').clear().type('Test Org Chart'); + cy.get('@input').clear().wait(200).type('Test Org Chart'); + cy.get('@input').type('{enter}', { delay: 100 }); + cy.get('@input').blur(); - cy.get('body').click(); cy.wait(500); }); beforeEach(() => { - cy.window().its('frappe').then(frappe => { + return cy.window().its('frappe').then(frappe => { return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); }).as('employee_records'); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 503db68c18c..a1d3c0083ca 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -6,9 +6,10 @@ context('Organizational Chart Mobile', () => { cy.awesomebar('Organizational Chart'); cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input').clear().type('Test Org Chart'); + cy.get('@input').clear().wait(200).type('Test Org Chart'); + cy.get('@input').type('{enter}', { delay: 100 }); + cy.get('@input').blur(); - cy.get('body').click(); cy.wait(500); }); @@ -16,7 +17,7 @@ context('Organizational Chart Mobile', () => { cy.viewport(375, 667); cy.wait(500); - cy.window().its('frappe').then(frappe => { + return cy.window().its('frappe').then(frappe => { return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); }).as('employee_records'); }); @@ -163,13 +164,15 @@ context('Organizational Chart Mobile', () => { }); it('goes to the respective level after clicking on non-collapsed sibling group', () => { - // click on non-collapsed sibling group - cy.get('.hierarchy-mobile') - .prev() - .click(); + cy.get('@employee_records').then(() => { + // click on non-collapsed sibling group + cy.get('.hierarchy-mobile') + .prev() + .click(); - // should take you to that level - cy.get('.hierarchy-mobile').find('li.level .node-card').should('have.length', 2); + // should take you to that level + cy.get('.hierarchy-mobile').find('li.level .node-card').should('have.length', 2); + }); }); it('edit node navigates to employee master', () => { From e327148edf315dec5a284868a9013e2e8a1a04eb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 17:34:15 +0530 Subject: [PATCH 097/369] fix: tests --- cypress/integration/test_organizational_chart_desktop.js | 6 +++--- cypress/integration/test_organizational_chart_mobile.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index cb12eb5c0c4..95592e2f6a8 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -4,10 +4,10 @@ context('Organizational Chart', () => { cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input').clear().wait(200).type('Test Org Chart'); + cy.get('.frappe-control[data-fieldname=company] input').first().focus().as('input'); + cy.get('@input').clear().wait(200).type('Test Org Chart', { force: true }); cy.get('@input').type('{enter}', { delay: 100 }); - cy.get('@input').blur(); + cy.get('@input').blur({ force: true }); cy.wait(500); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index a1d3c0083ca..632d15ba6c8 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -5,10 +5,10 @@ context('Organizational Chart Mobile', () => { cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input').clear().wait(200).type('Test Org Chart'); + cy.get('.frappe-control[data-fieldname=company] input').first().focus().as('input'); + cy.get('@input').clear().wait(200).type('Test Org Chart', { force: true }); cy.get('@input').type('{enter}', { delay: 100 }); - cy.get('@input').blur(); + cy.get('@input').blur({ force: true }); cy.wait(500); }); From ff9d631f1525fe4f27caac4afc65eddb165cc27b Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 19 Jul 2021 20:09:37 +0530 Subject: [PATCH 098/369] fix:Ignore mandatory fields while creating payment reconciliation Journal Entry --- .../doctype/payment_reconciliation/payment_reconciliation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 6635128f9ef..d788d91855e 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -306,5 +306,5 @@ def reconcile_dr_cr_note(dr_cr_notes, company): } ] }) - + jv.flags.ignore_mandatory = True jv.submit() \ No newline at end of file From 89c5bb6066737418a01c18ec202c8fa78424a9b2 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 22:19:28 +0530 Subject: [PATCH 099/369] fix: tests --- .../test_organizational_chart_desktop.js | 31 +++++++--------- .../test_organizational_chart_mobile.js | 37 ++++++++----------- erpnext/tests/ui_test_helpers.py | 6 +++ 3 files changed, 34 insertions(+), 40 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 95592e2f6a8..0493732812f 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -1,21 +1,17 @@ context('Organizational Chart', () => { before(() => { cy.login(); - cy.visit('/app/website'); + cy.visit('/app'); + + cy.call('erpnext.tests.ui_test_helpers.create_employee_records'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').first().focus().as('input'); - cy.get('@input').clear().wait(200).type('Test Org Chart', { force: true }); - cy.get('@input').type('{enter}', { delay: 100 }); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart', { force: true }); + cy.wait(200); cy.get('@input').blur({ force: true }); - - cy.wait(500); - }); - - beforeEach(() => { - return cy.window().its('frappe').then(frappe => { - return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); - }).as('employee_records'); }); it('renders root nodes and loads children for the first expandable node', () => { @@ -29,8 +25,7 @@ context('Organizational Chart', () => { cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO'); cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2 Connections'); - // check children of first node - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // children of 1st root visible cy.get(`[data-parent="${employee_records.message[0]}"]`).as('child-node'); cy.get('@child-node') @@ -47,7 +42,7 @@ context('Organizational Chart', () => { }); it('hides active nodes children and connectors on expanding sibling node', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // click sibling cy.get(`#${employee_records.message[1]}`) .click() @@ -60,7 +55,7 @@ context('Organizational Chart', () => { }); it('collapses previous level nodes and refreshes connectors on expanding child node', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // click child node cy.get(`#${employee_records.message[3]}`) .click() @@ -81,7 +76,7 @@ context('Organizational Chart', () => { }); it('expands previous level nodes', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[0]}`) .click() .should('have.class', 'active'); @@ -95,7 +90,7 @@ context('Organizational Chart', () => { }); it('edit node navigates to employee master', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node') .click(); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 632d15ba6c8..1dcfbcfeb17 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -2,24 +2,17 @@ context('Organizational Chart Mobile', () => { before(() => { cy.login(); cy.viewport(375, 667); - cy.visit('/app/website'); + cy.visit('/app'); + + cy.call('erpnext.tests.ui_test_helpers.create_employee_records'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').first().focus().as('input'); - cy.get('@input').clear().wait(200).type('Test Org Chart', { force: true }); - cy.get('@input').type('{enter}', { delay: 100 }); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart', { force: true }); + cy.wait(200); cy.get('@input').blur({ force: true }); - - cy.wait(500); - }); - - beforeEach(() => { - cy.viewport(375, 667); - cy.wait(500); - - return cy.window().its('frappe').then(frappe => { - return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); - }).as('employee_records'); }); it('renders root nodes', () => { @@ -35,7 +28,7 @@ context('Organizational Chart Mobile', () => { }); it('expands root node', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[1]}`) .click() .should('have.class', 'active'); @@ -68,7 +61,7 @@ context('Organizational Chart Mobile', () => { }); it('expands child node', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[3]}`) .click() .should('have.class', 'active') @@ -97,7 +90,7 @@ context('Organizational Chart Mobile', () => { }); it('renders sibling group', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // sibling group visible for parent cy.get(`#${employee_records.message[1]}`) .next() @@ -118,7 +111,7 @@ context('Organizational Chart Mobile', () => { }); it('expands previous level nodes', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[6]}`) .click() .should('have.class', 'active'); @@ -137,7 +130,7 @@ context('Organizational Chart Mobile', () => { }); it('expands sibling group', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // sibling group visible for parent cy.get(`#${employee_records.message[6]}`).click(); @@ -164,7 +157,7 @@ context('Organizational Chart Mobile', () => { }); it('goes to the respective level after clicking on non-collapsed sibling group', () => { - cy.get('@employee_records').then(() => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(() => { // click on non-collapsed sibling group cy.get('.hierarchy-mobile') .prev() @@ -176,7 +169,7 @@ context('Organizational Chart Mobile', () => { }); it('edit node navigates to employee master', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node') .click(); diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py index f66d69ba232..fc3aa298242 100644 --- a/erpnext/tests/ui_test_helpers.py +++ b/erpnext/tests/ui_test_helpers.py @@ -17,6 +17,12 @@ def create_employee_records(): employees = [emp1, emp2, emp3, emp4, emp5, emp6, emp7] return employees +@frappe.whitelist() +def get_employee_records(): + return frappe.db.get_list('Employee', filters={ + 'company': 'Test Org Chart' + }, pluck='name', order_by='name') + def create_company(): company = frappe.db.exists('Company', 'Test Org Chart') if not company: From 7176c0847e6aeb10dd0f68e501707074cf3345e1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 23:34:02 +0530 Subject: [PATCH 100/369] fix: tests --- .../test_organizational_chart_desktop.js | 19 +++++++++---------- .../test_organizational_chart_mobile.js | 19 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 0493732812f..52863f18e05 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -1,17 +1,16 @@ context('Organizational Chart', () => { before(() => { cy.login(); - cy.visit('/app'); - - cy.call('erpnext.tests.ui_test_helpers.create_employee_records'); + cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart', { force: true }); - cy.wait(200); - cy.get('@input').blur({ force: true }); + cy.call('erpnext.tests.ui_test_helpers.create_employee_records').then(() => { + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); + }); }); it('renders root nodes and loads children for the first expandable node', () => { @@ -27,7 +26,7 @@ context('Organizational Chart', () => { cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // children of 1st root visible - cy.get(`[data-parent="${employee_records.message[0]}"]`).as('child-node'); + cy.get(`div[data-parent="${employee_records.message[0]}"]`).as('child-node'); cy.get('@child-node') .should('have.length', 1) .should('be.visible'); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 1dcfbcfeb17..2272a31046e 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -2,17 +2,16 @@ context('Organizational Chart Mobile', () => { before(() => { cy.login(); cy.viewport(375, 667); - cy.visit('/app'); - - cy.call('erpnext.tests.ui_test_helpers.create_employee_records'); + cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart', { force: true }); - cy.wait(200); - cy.get('@input').blur({ force: true }); + cy.call('erpnext.tests.ui_test_helpers.create_employee_records').then(() => { + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); + }); }); it('renders root nodes', () => { @@ -40,7 +39,7 @@ context('Organizational Chart Mobile', () => { cy.get('.hierarchy-mobile').find('.level').first().find('ul.node-children').children() .should('have.length', 2); - cy.get(`[data-parent="${employee_records.message[1]}"]`).first().as('child-node'); + cy.get(`div[data-parent="${employee_records.message[1]}"]`).first().as('child-node'); cy.get('@child-node').should('be.visible'); cy.get('@child-node') From eb65ce662a5bdde4bd8a54a8268363d2651a3c09 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 20 Jul 2021 10:23:52 +0530 Subject: [PATCH 101/369] fix(test): increase timeout for record creation --- .../test_organizational_chart_desktop.js | 27 ++++++++++++++----- .../test_organizational_chart_mobile.js | 27 ++++++++++++++----- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 52863f18e05..0da4e560a72 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -4,12 +4,27 @@ context('Organizational Chart', () => { cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.call('erpnext.tests.ui_test_helpers.create_employee_records').then(() => { - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart{enter}', { force: true }) - .blur({ force: true }); + cy.window().its('frappe.csrf_token').then(csrf_token => { + return cy.request({ + url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + }, + timeout: 60000 + }) + .then(res => { + expect(res.status).eq(200); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); + + cy.get('body').click(); + }); }); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 2272a31046e..0374678a1ad 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -5,12 +5,27 @@ context('Organizational Chart Mobile', () => { cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.call('erpnext.tests.ui_test_helpers.create_employee_records').then(() => { - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart{enter}', { force: true }) - .blur({ force: true }); + cy.window().its('frappe.csrf_token').then(csrf_token => { + return cy.request({ + url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + }, + timeout: 60000 + }) + .then(res => { + expect(res.status).eq(200); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); + + cy.get('body').click(); + }); }); }); From 41dd0c5a8a75d674f151f09fe9412fdd42347d82 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 20 Jul 2021 10:55:05 +0530 Subject: [PATCH 102/369] fix: sider --- .../test_organizational_chart_desktop.js | 3 +- .../test_organizational_chart_mobile.js | 36 +++++++++---------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 0da4e560a72..57b7f7dced2 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -14,8 +14,7 @@ context('Organizational Chart', () => { 'X-Frappe-CSRF-Token': csrf_token }, timeout: 60000 - }) - .then(res => { + }).then(res => { expect(res.status).eq(200); cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); cy.get('@input') diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 0374678a1ad..214229f6f6c 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -7,25 +7,25 @@ context('Organizational Chart Mobile', () => { cy.window().its('frappe.csrf_token').then(csrf_token => { return cy.request({ - url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - }, - timeout: 60000 - }) - .then(res => { - expect(res.status).eq(200); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart{enter}', { force: true }) - .blur({ force: true }); + url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + }, + timeout: 60000 + }) + .then(res => { + expect(res.status).eq(200); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); - cy.get('body').click(); - }); + cy.get('body').click(); + }); }); }); From 0222ee03580a37cc304b70a7e698d0fcc416c686 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 20 Jul 2021 12:19:44 +0530 Subject: [PATCH 103/369] fix: sider --- .../test_organizational_chart_desktop.js | 2 -- .../test_organizational_chart_mobile.js | 35 +++++++++---------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 57b7f7dced2..fb46bbb4331 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -21,8 +21,6 @@ context('Organizational Chart', () => { .clear({ force: true }) .type('Test Org Chart{enter}', { force: true }) .blur({ force: true }); - - cy.get('body').click(); }); }); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 214229f6f6c..df90dbfa22f 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -7,25 +7,22 @@ context('Organizational Chart Mobile', () => { cy.window().its('frappe.csrf_token').then(csrf_token => { return cy.request({ - url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - }, - timeout: 60000 - }) - .then(res => { - expect(res.status).eq(200); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart{enter}', { force: true }) - .blur({ force: true }); - - cy.get('body').click(); - }); + url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + }, + timeout: 60000 + }).then(res => { + expect(res.status).eq(200); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); + }); }); }); From 8a64a84d1afdfd18cefcc99a0860ed63fff34b84 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 13:25:53 +0530 Subject: [PATCH 104/369] fix: GST Reports timeout issue --- erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py | 5 ++--- erpnext/regional/report/gstr_1/gstr_1.py | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 641520437fb..6a61ae2b422 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -214,9 +214,8 @@ class GSTR3BReport(Document): for d in item_details: if d.item_code not in self.invoice_items.get(d.parent, {}): - self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, - sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in item_details - if i.item_code == d.item_code and i.parent == d.parent)) + self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0) + self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0) if d.is_nil_exempt and d.item_code not in self.is_nil_exempt: self.is_nil_exempt.append(d.item_code) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index cfcb8c3444f..b81fa810fe1 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -217,9 +217,8 @@ class Gstr1Report(object): for d in items: if d.item_code not in self.invoice_items.get(d.parent, {}): - self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, - sum((i.get('taxable_value', 0) or i.get('base_net_amount', 0)) for i in items - if i.item_code == d.item_code and i.parent == d.parent)) + self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, 0.0) + self.invoice_items[d.parent][d.item_code] += d.get('taxable_value', 0) or d.get('base_net_amount', 0) item_tax_rate = {} From e1dc6980d09266b3db115546403d8bbdc0946b1e Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 5 Jul 2021 14:58:32 +0530 Subject: [PATCH 105/369] feat(Accounts Settings): Add 'Enable Discount Accounting' checkbox --- .../doctype/accounts_settings/accounts_settings.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 703e93c0757..676c6a8b479 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -12,6 +12,7 @@ "role_allowed_to_over_bill", "make_payment_via_journal_entry", "column_break_11", + "enable_discount_accounting", "check_supplier_invoice_uniqueness", "unlink_payment_on_cancellation_of_invoice", "automatically_fetch_payment_terms", @@ -261,6 +262,12 @@ "fieldname": "post_change_gl_entries", "fieldtype": "Check", "label": "Create Ledger Entries for Change Amount" + }, + { + "default": "0", + "fieldname": "enable_discount_accounting", + "fieldtype": "Check", + "label": "Enable Discount Accounting" } ], "icon": "icon-cog", @@ -268,7 +275,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-06-17 20:26:03.721202", + "modified": "2021-07-05 14:56:19.820731", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", From a6690a8a3df1e467850f98c7a561d2b5b0b295eb Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 5 Jul 2021 15:09:05 +0530 Subject: [PATCH 106/369] feat(Sales Invoice): Add 'Discount Account' field in Items table --- .../doctype/sales_invoice_item/sales_invoice_item.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 8e6952a93c4..b65903bf5e8 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -63,6 +63,7 @@ "finance_book", "col_break4", "expense_account", + "discount_account", "deferred_revenue", "deferred_revenue_account", "service_stop_date", @@ -821,12 +822,18 @@ "no_copy": 1, "options": "currency", "read_only": 1 + }, + { + "fieldname": "discount_account", + "fieldtype": "Link", + "label": "Discount Account", + "options": "Account" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-02-23 01:05:22.123527", + "modified": "2021-07-05 15:07:22.857128", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", From f6a9374356658f6375b50312838620ae6e14de5a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 6 Jul 2021 00:36:06 +0530 Subject: [PATCH 107/369] feat: Create GL Entries for discount accounting --- .../doctype/sales_invoice/sales_invoice.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 6d1f6249c13..04c789d30a6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -846,6 +846,7 @@ class SalesInvoice(SellingController): self.allocate_advance_taxes(gl_entries) self.make_item_gl_entries(gl_entries) + self.make_discount_gl_entries(gl_entries) # merge gl entries before adding pos entries gl_entries = merge_similar_entries(gl_entries) @@ -959,6 +960,40 @@ class SalesInvoice(SellingController): erpnext.is_perpetual_inventory_enabled(self.company): gl_entries += super(SalesInvoice, self).get_gl_entries() + def make_discount_gl_entries(self, gl_entries): + enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + + if enable_discount_accounting: + for item in self.get("items"): + if item.get('discount_amount') and item.get('discount_account'): + account_currency = get_account_currency(item.discount_account) + gl_entries.append( + self.get_gl_dict({ + "account": item.discount_account, + "against": self.customer, + "debit": flt(item.discount_amount), + "debit_in_account_currency": flt(item.discount_amount), + "cost_center": self.cost_center, + "project": self.project + }, account_currency, item=self) + ) + + income_account = (item.income_account + if (not item.enable_deferred_revenue or self.is_return) + else item.deferred_revenue_account) + + account_currency = get_account_currency(income_account) + gl_entries.append( + self.get_gl_dict({ + "account": income_account, + "against": self.customer, + "credit": flt(item.discount_amount), + "credit_in_account_currency": flt(item.discount_amount), + "cost_center": item.cost_center, + "project": item.project or self.project + }, account_currency, item=item) + ) + def make_loyalty_point_redemption_gle(self, gl_entries): if cint(self.redeem_loyalty_points): gl_entries.append( From c4e54295601b4dd6da940212ed561cb5f3abb20c Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 6 Jul 2021 16:28:19 +0530 Subject: [PATCH 108/369] feat: Filter list for Discount Account field in Items table --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index f813425e6b5..dc0d9da97b6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -510,6 +510,14 @@ cur_frm.set_query("income_account", "items", function(doc) { } }); +// Discount Account in Details Table +// -------------------------------- +cur_frm.set_query("discount_account", "items", function(doc) { + return{ + query: "erpnext.controllers.queries.get_income_account", + filters: {'company': doc.company} + } +}); // Cost Center in Details Table // ----------------------------- From acb9e207ec96f642d5ce935157af4315717c0f57 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 6 Jul 2021 16:29:19 +0530 Subject: [PATCH 109/369] feat: Add Default Discount Account field --- erpnext/stock/doctype/item/item.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 6fed9efa63f..f1c413e00a0 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -91,6 +91,7 @@ "is_sales_item", "column_break3", "max_discount", + "default_discount_account", "deferred_revenue", "deferred_revenue_account", "enable_deferred_revenue", @@ -1058,6 +1059,12 @@ "fieldname": "website_image_alt", "fieldtype": "Data", "label": "Image Description" + }, + { + "fieldname": "default_discount_account", + "fieldtype": "Link", + "label": "Default Discount Account", + "options": "Account" } ], "has_web_view": 1, @@ -1067,7 +1074,7 @@ "index_web_pages_for_search": 1, "links": [], "max_attachments": 1, - "modified": "2021-03-18 14:04:38.575519", + "modified": "2021-07-06 00:46:15.878648", "modified_by": "Administrator", "module": "Stock", "name": "Item", @@ -1138,4 +1145,4 @@ "sort_order": "DESC", "title_field": "item_name", "track_changes": 1 -} +} \ No newline at end of file From cdfefa261e021dcba4d1f09b064f2b7baf6d72f1 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 6 Jul 2021 16:51:12 +0530 Subject: [PATCH 110/369] feat: Assign Item's Default Discount Account if present --- erpnext/stock/get_item_details.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index cf52803fca8..bf082f89be7 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -287,6 +287,7 @@ def get_basic_details(args, item, overwrite_warehouse=True): "warehouse": warehouse, "income_account": get_default_income_account(args, item_defaults, item_group_defaults, brand_defaults), "expense_account": expense_account or get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults) , + "discount_account": None or get_default_discount_account(args, item_defaults), "cost_center": get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults), 'has_serial_no': item.has_serial_no, 'has_batch_no': item.has_batch_no, @@ -589,6 +590,10 @@ def get_default_expense_account(args, item, item_group, brand): or brand.get("expense_account") or args.expense_account) +def get_default_discount_account(args, item_defaults): + return (item_defaults.default_discount_account + or args.discount_account) + def get_default_deferred_account(args, item, fieldname=None): if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"): return (item.get(fieldname) From f48fa2e7f3893020b9ccbd546ff83e53a2c1c581 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 6 Jul 2021 20:22:13 +0530 Subject: [PATCH 111/369] feat: Toggle display for discount accounting fields according to enable_discount_accounting --- .../doctype/accounts_settings/accounts_settings.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index ac4a2d6f16d..9e33eb395b7 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -21,6 +21,7 @@ class AccountsSettings(Document): self.validate_stale_days() self.enable_payment_schedule_in_print() + self.toggle_discount_accounting_fields() def validate_stale_days(self): if not self.allow_stale and cint(self.stale_days) <= 0: @@ -33,3 +34,9 @@ class AccountsSettings(Document): for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"): make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check", validate_fields_for_doctype=False) make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check", validate_fields_for_doctype=False) + + def toggle_discount_accounting_fields(self): + enable_discount_accounting = cint(self.enable_discount_accounting) + + make_property_setter("Sales Invoice Item", "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) + make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) \ No newline at end of file From d24b5f707a870e1e1c44ce0caadff9005c79f633 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 12 Jul 2021 18:56:06 +0530 Subject: [PATCH 112/369] fix: Add description for Enable Discount Accounting checkbox --- .../doctype/accounts_settings/accounts_settings.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 676c6a8b479..49a2afee85f 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -12,7 +12,6 @@ "role_allowed_to_over_bill", "make_payment_via_journal_entry", "column_break_11", - "enable_discount_accounting", "check_supplier_invoice_uniqueness", "unlink_payment_on_cancellation_of_invoice", "automatically_fetch_payment_terms", @@ -20,6 +19,7 @@ "book_asset_depreciation_entry_automatically", "unlink_advance_payment_on_cancelation_of_order", "post_change_gl_entries", + "enable_discount_accounting", "tax_settings_section", "determine_address_tax_category_from", "column_break_19", @@ -265,6 +265,7 @@ }, { "default": "0", + "description": "If enabled, additional ledger entries will be made for discounts in a separate Discount Account", "fieldname": "enable_discount_accounting", "fieldtype": "Check", "label": "Enable Discount Accounting" @@ -275,7 +276,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-07-05 14:56:19.820731", + "modified": "2021-07-12 18:54:29.084958", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", From cfb94175a2458b47ec54fb0ccabcb0a682004504 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 12 Jul 2021 19:07:54 +0530 Subject: [PATCH 113/369] fix: Filter Discount Account list --- .../doctype/sales_invoice/sales_invoice.js | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index dc0d9da97b6..17228a3c283 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -510,15 +510,6 @@ cur_frm.set_query("income_account", "items", function(doc) { } }); -// Discount Account in Details Table -// -------------------------------- -cur_frm.set_query("discount_account", "items", function(doc) { - return{ - query: "erpnext.controllers.queries.get_income_account", - filters: {'company': doc.company} - } -}); - // Cost Center in Details Table // ----------------------------- cur_frm.fields_dict["items"].grid.get_field("cost_center").get_query = function(doc) { @@ -626,6 +617,19 @@ frappe.ui.form.on('Sales Invoice', { } } + // discount account + frm.fields_dict['items'].grid.get_field('discount_account').get_query = function(doc) { + if (erpnext.is_perpetual_inventory_enabled(doc.company)) { + return { + filters: { + 'report_type': 'Profit and Loss', + 'company': doc.company, + "is_group": 0 + } + } + } + } + frm.fields_dict['items'].grid.get_field('deferred_revenue_account').get_query = function(doc) { return { filters: { From 613d08faad544cd0c6855e30bf082538c2c992cf Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 12 Jul 2021 22:32:38 +0530 Subject: [PATCH 114/369] fix: Copy discount account from first row to all Items --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 17228a3c283..2b48d66dca9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -347,8 +347,8 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte items_add: function(doc, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); - this.frm.script_manager.copy_from_first_row("items", row, ["income_account", "cost_center"]); - }, + this.frm.script_manager.copy_from_first_row("items", row, ["income_account", "discount_account", "cost_center"]); + } set_dynamic_labels: function() { this._super(); From 546c8d125c6563ffa57f31b08a79948c7ce0d8ba Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 01:37:30 +0530 Subject: [PATCH 115/369] fix: Move Default Discount Account field to Item Defaults --- erpnext/stock/doctype/item/item.json | 9 +- .../doctype/item_default/item_default.json | 548 ++++-------------- erpnext/stock/get_item_details.py | 4 +- 3 files changed, 104 insertions(+), 457 deletions(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index f1c413e00a0..f662bbd1c79 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -91,7 +91,6 @@ "is_sales_item", "column_break3", "max_discount", - "default_discount_account", "deferred_revenue", "deferred_revenue_account", "enable_deferred_revenue", @@ -1059,12 +1058,6 @@ "fieldname": "website_image_alt", "fieldtype": "Data", "label": "Image Description" - }, - { - "fieldname": "default_discount_account", - "fieldtype": "Link", - "label": "Default Discount Account", - "options": "Account" } ], "has_web_view": 1, @@ -1074,7 +1067,7 @@ "index_web_pages_for_search": 1, "links": [], "max_attachments": 1, - "modified": "2021-07-06 00:46:15.878648", + "modified": "2021-07-13 01:29:06.071827", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item_default/item_default.json b/erpnext/stock/doctype/item_default/item_default.json index 96b5dfdc8f7..bc171604f43 100644 --- a/erpnext/stock/doctype/item_default/item_default.json +++ b/erpnext/stock/doctype/item_default/item_default.json @@ -1,464 +1,118 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-05-03 02:29:24.444341", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2018-05-03 02:29:24.444341", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "default_warehouse", + "column_break_3", + "default_price_list", + "default_discount_account", + "purchase_defaults", + "buying_cost_center", + "default_supplier", + "column_break_8", + "expense_account", + "selling_defaults", + "selling_cost_center", + "column_break_12", + "income_account" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "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": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_warehouse", - "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": "Default Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "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 - }, + "fieldname": "default_warehouse", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Default Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "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 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_price_list", - "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": "Default Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "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 - }, + "fieldname": "default_price_list", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Default Price List", + "options": "Price List" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "purchase_defaults", - "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, - "label": "Purchase Defaults", - "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 - }, + "fieldname": "purchase_defaults", + "fieldtype": "Section Break", + "label": "Purchase Defaults" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "buying_cost_center", - "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": "Default Buying Cost Center", - "length": 0, - "no_copy": 0, - "options": "Cost Center", - "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 - }, + "fieldname": "buying_cost_center", + "fieldtype": "Link", + "label": "Default Buying Cost Center", + "options": "Cost Center" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_supplier", - "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": "Default Supplier", - "length": 0, - "no_copy": 0, - "options": "Supplier", - "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 - }, + "fieldname": "default_supplier", + "fieldtype": "Link", + "label": "Default Supplier", + "options": "Supplier" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "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 - }, + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expense_account", - "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": "Default Expense Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "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 - }, + "fieldname": "expense_account", + "fieldtype": "Link", + "label": "Default Expense Account", + "options": "Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "selling_defaults", - "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, - "label": "Sales Defaults", - "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 - }, + "fieldname": "selling_defaults", + "fieldtype": "Section Break", + "label": "Sales Defaults" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "selling_cost_center", - "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": "Default Selling Cost Center", - "length": 0, - "no_copy": 0, - "options": "Cost Center", - "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 - }, + "fieldname": "selling_cost_center", + "fieldtype": "Link", + "label": "Default Selling Cost Center", + "options": "Cost Center" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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 - }, + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "income_account", - "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": "Default Income Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "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 + "fieldname": "income_account", + "fieldtype": "Link", + "label": "Default Income Account", + "options": "Account" + }, + { + "fieldname": "default_discount_account", + "fieldtype": "Link", + "label": "Default Discount Account", + "options": "Account" } - ], - "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-12-07 11:48:07.638935", - "modified_by": "Administrator", - "module": "Stock", - "name": "Item Default", - "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 + ], + "istable": 1, + "links": [], + "modified": "2021-07-13 01:26:03.860065", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Default", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index bf082f89be7..cadb4aa97b1 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -590,8 +590,8 @@ def get_default_expense_account(args, item, item_group, brand): or brand.get("expense_account") or args.expense_account) -def get_default_discount_account(args, item_defaults): - return (item_defaults.default_discount_account +def get_default_discount_account(args, item): + return (item.get("default_discount_account") or args.discount_account) def get_default_deferred_account(args, item, fieldname=None): From ee025b501fbb15ab21ceaf8f3bf36f892279c4ed Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 01:43:41 +0530 Subject: [PATCH 116/369] fix: Filter options for Default Discount Account --- erpnext/stock/doctype/item/item.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 264baeaa472..2fee57f2d1b 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -274,6 +274,17 @@ $.extend(erpnext.item, { } } + frm.fields_dict["item_defaults"].grid.get_field("default_discount_account").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { + 'report_type': 'Profit and Loss', + 'company': row.company, + "is_group": 0 + } + } + } + frm.fields_dict["item_defaults"].grid.get_field("buying_cost_center").get_query = function(doc, cdt, cdn) { const row = locals[cdt][cdn]; return { From bde5c619db8e80796a923cc57af1d97cc235f125 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 02:06:03 +0530 Subject: [PATCH 117/369] fix: Add Discount Account field --- .../purchase_invoice_item/purchase_invoice_item.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 8a55ff87e39..922b567d152 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -73,6 +73,7 @@ "manufacturer_part_no", "accounting", "expense_account", + "discount_account", "col_break5", "is_fixed_asset", "asset_location", @@ -849,12 +850,18 @@ "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "discount_account", + "fieldtype": "Link", + "label": "Discount Account", + "options": "Account" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-06-16 19:57:03.101571", + "modified": "2021-07-13 02:04:37.787882", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", From e2c83c3bafe00951a66bb9405bd6da911539a875 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 02:07:46 +0530 Subject: [PATCH 118/369] fix: Display Discount Account only if Enable Discount Accounting is checked --- .../accounts/doctype/accounts_settings/accounts_settings.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 9e33eb395b7..d1abdba3792 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -38,5 +38,7 @@ class AccountsSettings(Document): def toggle_discount_accounting_fields(self): enable_discount_accounting = cint(self.enable_discount_accounting) - make_property_setter("Sales Invoice Item", "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) + for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]: + make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) + make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) \ No newline at end of file From 377775ad8ead56e000717ce769e4da06fb5e7855 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 02:09:40 +0530 Subject: [PATCH 119/369] fix: Copy Discount Account from first row --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index dc9094c3e91..81e3aaa8a58 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -365,7 +365,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ items_add: function(doc, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); this.frm.script_manager.copy_from_first_row("items", row, - ["expense_account", "cost_center", "project"]); + ["expense_account", "discount_account", "cost_center", "project"]); }, on_submit: function() { From e06eb8e6a9fe6f6c36bf83f49bb5f72f1203b794 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 02:14:18 +0530 Subject: [PATCH 120/369] fix: Filter options for Discount Account --- .../doctype/purchase_invoice/purchase_invoice.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 81e3aaa8a58..a1ee5d0076e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -508,6 +508,16 @@ frappe.ui.form.on("Purchase Invoice", { } } } + + frm.fields_dict['items'].grid.get_field('discount_account').get_query = function(doc) { + return { + filters: { + 'report_type': 'Profit and Loss', + 'company': doc.company, + "is_group": 0 + } + } + } }, refresh: function(frm) { From fd2852e87e3b03c019cff84669f908fdc70f3704 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 03:01:02 +0530 Subject: [PATCH 121/369] fix: Create common function for discount accounting --- .../purchase_invoice/purchase_invoice.py | 1 + .../doctype/sales_invoice/sales_invoice.py | 34 -------------- erpnext/controllers/accounts_controller.py | 45 +++++++++++++++++++ 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index f7992797ed4..78d1ee972f0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -446,6 +446,7 @@ class PurchaseInvoice(BuyingController): self.make_supplier_gl_entry(gl_entries) self.make_item_gl_entries(gl_entries) + self.make_discount_gl_entries(gl_entries) if self.check_asset_cwip_enabled(): self.get_asset_gl_entry(gl_entries) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 04c789d30a6..15fae84b799 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -960,40 +960,6 @@ class SalesInvoice(SellingController): erpnext.is_perpetual_inventory_enabled(self.company): gl_entries += super(SalesInvoice, self).get_gl_entries() - def make_discount_gl_entries(self, gl_entries): - enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) - - if enable_discount_accounting: - for item in self.get("items"): - if item.get('discount_amount') and item.get('discount_account'): - account_currency = get_account_currency(item.discount_account) - gl_entries.append( - self.get_gl_dict({ - "account": item.discount_account, - "against": self.customer, - "debit": flt(item.discount_amount), - "debit_in_account_currency": flt(item.discount_amount), - "cost_center": self.cost_center, - "project": self.project - }, account_currency, item=self) - ) - - income_account = (item.income_account - if (not item.enable_deferred_revenue or self.is_return) - else item.deferred_revenue_account) - - account_currency = get_account_currency(income_account) - gl_entries.append( - self.get_gl_dict({ - "account": income_account, - "against": self.customer, - "credit": flt(item.discount_amount), - "credit_in_account_currency": flt(item.discount_amount), - "cost_center": item.cost_center, - "project": item.project or self.project - }, account_currency, item=item) - ) - def make_loyalty_point_redemption_gle(self, gl_entries): if cint(self.redeem_loyalty_points): gl_entries.append( diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 4c313c43a72..0a115825086 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -808,6 +808,51 @@ class AccountsController(TransactionBase): tax_map[tax.account_head] -= allocated_amount allocated_tax_map[tax.account_head] -= allocated_amount + def make_discount_gl_entries(self, gl_entries): + enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + + if enable_discount_accounting: + for item in self.get("items"): + if item.get('discount_amount') and item.get('discount_account'): + if self.doctype == "Purchase Invoice": + dr_or_cr = "credit" + rev_dr_cr = "debit" + supplier_or_customer = self.supplier + income_or_expense_account = (item.expense_account + if (not item.enable_deferred_expense or self.is_return) + else item.deferred_expense_account) + else: + dr_or_cr = "debit" + rev_dr_cr = "credit" + supplier_or_customer = self.customer + income_or_expense_account = (item.income_account + if (not item.enable_deferred_revenue or self.is_return) + else item.deferred_revenue_account) + + account_currency = get_account_currency(item.discount_account) + gl_entries.append( + self.get_gl_dict({ + "account": item.discount_account, + "against": supplier_or_customer, + dr_or_cr: flt(item.discount_amount), + dr_or_cr + "_in_account_currency": flt(item.discount_amount), + "cost_center": self.cost_center, + "project": self.project + }, account_currency, item=self) + ) + + account_currency = get_account_currency(income_or_expense_account) + gl_entries.append( + self.get_gl_dict({ + "account": income_or_expense_account, + "against": supplier_or_customer, + rev_dr_cr: flt(item.discount_amount), + rev_dr_cr + "_in_account_currency": flt(item.discount_amount), + "cost_center": item.cost_center, + "project": item.project or self.project + }, account_currency, item=item) + ) + def allocate_advance_taxes(self, gl_entries): tax_map = self.get_tax_map() for pe in self.get("advances"): From 38e87fce099100d1db3386730140212bc8a42d51 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 17:41:29 +0530 Subject: [PATCH 122/369] fix: Add tests for discount accounting --- .../purchase_invoice/test_purchase_invoice.py | 20 ++++++++++++++++++- .../sales_invoice/test_sales_invoice.py | 15 ++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index ca4d009956d..bd7ded269c6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -230,6 +230,16 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(expected_values[gle.account][1], gle.debit) self.assertEqual(expected_values[gle.account][2], gle.credit) + def test_purchase_invoice_with_discount_accounting_enabled(self): + enable_discount_accounting() + + discount_account = create_account(account_name="Discount Account", + parent_account="Indirect Expenses - _TC", company="_Test Company") + pi = make_purchase_invoice(discount_account=discount_account, discount_amount=100) + + discount_amount = frappe.db.get_value('GL Entry', {'account': discount_account, 'voucher_no': pi.name}, 'credit') + self.assertEqual(discount_amount, 100) + def test_purchase_invoice_change_naming_series(self): pi = frappe.copy_doc(test_records[1]) pi.insert() @@ -1170,6 +1180,11 @@ def unlink_payment_on_cancel_of_invoice(enable=1): accounts_settings.unlink_payment_on_cancellation_of_invoice = enable accounts_settings.save() +def enable_discount_accounting(enable=1): + accounts_settings = frappe.get_doc("Accounts Settings") + accounts_settings.enable_discount_accounting = enable + accounts_settings.save() + def make_purchase_invoice(**args): pi = frappe.new_doc("Purchase Invoice") args = frappe._dict(args) @@ -1192,6 +1207,7 @@ def make_purchase_invoice(**args): pi.return_against = args.return_against pi.is_subcontracted = args.is_subcontracted or "No" pi.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC" + pi.cost_center = args.cost_center or "_Test Cost Center - _TC" pi.append("items", { "item_code": args.item or args.item_code or "_Test Item", @@ -1200,7 +1216,9 @@ def make_purchase_invoice(**args): "received_qty": args.received_qty or 0, "rejected_qty": args.rejected_qty or 0, "rate": args.rate or 50, - 'expense_account': args.expense_account or '_Test Account Cost for Goods Sold - _TC', + "expense_account": args.expense_account or '_Test Account Cost for Goods Sold - _TC', + "discount_account": args.discount_account or None, + "discount_amount": args.discount_amount or 0, "conversion_factor": 1.0, "serial_no": args.serial_no, "stock_uom": args.uom or "_Test UOM", diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index dbc7f8632fc..d03a874c286 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1984,6 +1984,18 @@ class TestSalesInvoice(unittest.TestCase): sales_invoice.save() self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC") + def test_sales_invoice_with_discount_accounting_enabled(self): + from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import enable_discount_accounting + + enable_discount_accounting() + + discount_account = create_account(account_name="Discount Account", + parent_account="Indirect Expenses - _TC", company="_Test Company") + si = create_sales_invoice(discount_account=discount_account, discount_amount=100) + + discount_amount = frappe.db.get_value('GL Entry', {'account': discount_account, 'voucher_no': si.name}, 'debit') + self.assertEqual(discount_amount, 100) + def get_sales_invoice_for_e_invoice(): si = make_sales_invoice_for_ewaybill() si.naming_series = 'INV-2020-.#####' @@ -2152,6 +2164,7 @@ def create_sales_invoice(**args): si.currency=args.currency or "INR" si.conversion_rate = args.conversion_rate or 1 si.naming_series = args.naming_series or "T-SINV-" + si.cost_center = args.cost_center or "_Test Cost Center - _TC" si.append("items", { "item_code": args.item or args.item_code or "_Test Item", @@ -2165,6 +2178,8 @@ def create_sales_invoice(**args): "rate": args.rate if args.get("rate") is not None else 100, "income_account": args.income_account or "Sales - _TC", "expense_account": args.expense_account or "Cost of Goods Sold - _TC", + "discount_account": args.discount_account or None, + "discount_amount": args.discount_amount or 0, "cost_center": args.cost_center or "_Test Cost Center - _TC", "serial_no": args.serial_no, "conversion_factor": 1 From 9e788cfdcd866780b16195fff411a4dc4bfd822d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 15 Jul 2021 22:01:02 +0530 Subject: [PATCH 123/369] fix: Add Additional Discount Account field --- .../accounts/doctype/sales_invoice/sales_invoice.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index e7dd6b8a606..5c09b71cf35 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -104,6 +104,7 @@ "section_break_49", "apply_discount_on", "base_discount_amount", + "additional_discount_account", "column_break_51", "additional_discount_percentage", "discount_amount", @@ -1966,6 +1967,12 @@ "fieldname": "disable_rounded_total", "fieldtype": "Check", "label": "Disable Rounded Total" + }, + { + "fieldname": "additional_discount_account", + "fieldtype": "Link", + "label": "Additional Discount Account", + "options": "Account" } ], "icon": "fa fa-file-text", @@ -1978,7 +1985,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-05-20 22:48:33.988881", + "modified": "2021-07-15 21:57:17.544279", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From c1d65cfa8d987c734b412026a7fbc5aec427359a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 15 Jul 2021 22:01:38 +0530 Subject: [PATCH 124/369] fix: Filter options for Additional Discount Account --- .../accounts/doctype/sales_invoice/sales_invoice.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 2b48d66dca9..fab0e113240 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -591,6 +591,16 @@ frappe.ui.form.on('Sales Invoice', { }; }); + frm.set_query("additional_discount_account", function() { + return { + filters: { + company: frm.doc.company, + is_group: 0, + root_type: "Profit and Loss", + } + }; + }); + frm.custom_make_buttons = { 'Delivery Note': 'Delivery', 'Sales Invoice': 'Return / Credit Note', From b64787ee9ca81aa18904419e072296c1932ed15a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 15 Jul 2021 22:02:43 +0530 Subject: [PATCH 125/369] fix: Only display Additional Discount Account if Enable Discount Accounting is checked --- .../accounts/doctype/accounts_settings/accounts_settings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index d1abdba3792..053f061acc7 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -41,4 +41,5 @@ class AccountsSettings(Document): for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]: make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) - make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) \ No newline at end of file + make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) + make_property_setter("Sales Invoice", "additional_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) \ No newline at end of file From 38327fc17764516a5844af6ee4fcf36986e715b6 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 15 Jul 2021 22:03:46 +0530 Subject: [PATCH 126/369] fix: Make additional GL Entries for discount applied on taxes --- erpnext/controllers/accounts_controller.py | 36 ++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 0a115825086..8fc4023f2e2 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -852,6 +852,42 @@ class AccountsController(TransactionBase): "project": item.project or self.project }, account_currency, item=item) ) + + if self.get('discount_amount') and self.get('additional_discount_account'): + self.make_gle_for_additional_discount_applied_on_taxes(gl_entries) + + def make_gle_for_additional_discount_applied_on_taxes(self, gl_entries): + for tax in self.get("taxes"): + if flt(tax.base_tax_amount_after_discount_amount) and flt(tax.base_tax_amount): + account_currency = get_account_currency(tax.account_head) + additional_discount_applied_on_taxes = flt(tax.base_tax_amount) - flt(tax.base_tax_amount_after_discount_amount) + + gl_entries.append( + self.get_gl_dict({ + "account": tax.account_head, + "against": self.customer, + "credit": flt(additional_discount_applied_on_taxes, + tax.precision("tax_amount_after_discount_amount")), + "credit_in_account_currency": (flt(additional_discount_applied_on_taxes, + tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else + flt(additional_discount_applied_on_taxes, tax.precision("tax_amount_after_discount_amount"))), + "cost_center": tax.cost_center + }, account_currency, item=tax) + ) + + gl_entries.append( + self.get_gl_dict({ + "account": self.additional_discount_account, + "against": self.customer, + "debit": flt(additional_discount_applied_on_taxes, + tax.precision("tax_amount_after_discount_amount")), + "debit_in_account_currency": (flt(additional_discount_applied_on_taxes, + tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else + flt(additional_discount_applied_on_taxes, tax.precision("tax_amount_after_discount_amount"))), + "cost_center": tax.cost_center + }, account_currency, item=tax) + ) + def allocate_advance_taxes(self, gl_entries): tax_map = self.get_tax_map() From d6f409addcedaff16339000374b74a5d93be31db Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 16 Jul 2021 02:18:45 +0530 Subject: [PATCH 127/369] fix: Sider issues --- erpnext/stock/doctype/item/item.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 2fee57f2d1b..45666183856 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -282,8 +282,8 @@ $.extend(erpnext.item, { 'company': row.company, "is_group": 0 } - } - } + }; + }; frm.fields_dict["item_defaults"].grid.get_field("buying_cost_center").get_query = function(doc, cdt, cdn) { const row = locals[cdt][cdn]; From d0d9e83ad202f22d1cc3c6c21f20f9b934ad3700 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:34:50 +0530 Subject: [PATCH 128/369] fix: Check all expected GL Entries --- .../doctype/purchase_invoice/test_purchase_invoice.py | 11 +++++++++-- .../doctype/sales_invoice/test_sales_invoice.py | 9 +++++++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index bd7ded269c6..36fbc7e3513 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -231,14 +231,21 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(expected_values[gle.account][2], gle.credit) def test_purchase_invoice_with_discount_accounting_enabled(self): + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import check_gl_entries + enable_discount_accounting() discount_account = create_account(account_name="Discount Account", parent_account="Indirect Expenses - _TC", company="_Test Company") pi = make_purchase_invoice(discount_account=discount_account, discount_amount=100) - discount_amount = frappe.db.get_value('GL Entry', {'account': discount_account, 'voucher_no': pi.name}, 'credit') - self.assertEqual(discount_amount, 100) + expected_gle = [ + ["Discount Account - _TC", 0.0, 100.0, nowdate()], + ["_Test Account Cost for Goods Sold - _TC", 350.0, 0.0, nowdate()], + ["Creditors - _TC", 0.0, 250.0, nowdate()] + ] + + check_gl_entries(self, pi.name, expected_gle, nowdate()) def test_purchase_invoice_change_naming_series(self): pi = frappe.copy_doc(test_records[1]) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index d03a874c286..2ddfad9c310 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1992,9 +1992,14 @@ class TestSalesInvoice(unittest.TestCase): discount_account = create_account(account_name="Discount Account", parent_account="Indirect Expenses - _TC", company="_Test Company") si = create_sales_invoice(discount_account=discount_account, discount_amount=100) + + expected_gle = [ + ["Discount Account - _TC", 100.0, 0.0, nowdate()], + ["Sales - _TC", 0.0, 200.0, nowdate()], + ["Debtors - _TC", 100.0, 0.0, nowdate()] + ] - discount_amount = frappe.db.get_value('GL Entry', {'account': discount_account, 'voucher_no': si.name}, 'debit') - self.assertEqual(discount_amount, 100) + check_gl_entries(self, si.name, expected_gle, nowdate()) def get_sales_invoice_for_e_invoice(): si = make_sales_invoice_for_ewaybill() From 03f270697194c81f50a66d3318cbf2928ddfb9e8 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:40:43 +0530 Subject: [PATCH 129/369] fix: Add Additional Discount Account field --- .../doctype/purchase_invoice/purchase_invoice.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 00ef7d5c184..96ae828f464 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -96,6 +96,7 @@ "section_break_44", "apply_discount_on", "base_discount_amount", + "additional_discount_account", "column_break_46", "additional_discount_percentage", "discount_amount", @@ -1377,13 +1378,19 @@ "no_copy": 1, "print_hide": 1, "read_only": 1 + }, + { + "fieldname": "additional_discount_account", + "fieldtype": "Link", + "label": "Additional Discount Account", + "options": "Account" } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2021-06-15 18:20:56.806195", + "modified": "2021-07-17 17:37:50.570595", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", From ff25683378769b5869f19dc49f8165d18938a8fd Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:41:06 +0530 Subject: [PATCH 130/369] fix: Filter options for Additional Discount Account --- .../doctype/purchase_invoice/purchase_invoice.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index a1ee5d0076e..4ed31cc2272 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -499,6 +499,16 @@ frappe.ui.form.on("Purchase Invoice", { 'Payment Entry': 'Payment' } + frm.set_query("additional_discount_account", function() { + return { + filters: { + company: frm.doc.company, + is_group: 0, + root_type: "Profit and Loss", + } + }; + }); + frm.fields_dict['items'].grid.get_field('deferred_expense_account').get_query = function(doc) { return { filters: { From 9dd2a9e8973fe4e5c70dc4aa65683870281e4091 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:42:36 +0530 Subject: [PATCH 131/369] fix: Create ledger entries for discount applied on taxes in make_tax_gl_entries --- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 5 +++++ erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 5 +++++ erpnext/controllers/accounts_controller.py | 3 --- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 78d1ee972f0..006f5bb0e86 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -888,6 +888,11 @@ class PurchaseInvoice(BuyingController): "remarks": self.remarks or "Accounting Entry for Stock" }, item=tax)) + enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + + if enable_discount_accounting and self.get('discount_amount') and self.get('additional_discount_account'): + self.make_gle_for_additional_discount_applied_on_taxes(gl_entries) + def make_internal_transfer_gl_entries(self, gl_entries): if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges): account_currency = get_account_currency(self.unrealized_profit_loss_account) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 15fae84b799..ee9b59e769d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -902,6 +902,11 @@ class SalesInvoice(SellingController): }, account_currency, item=tax) ) + enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + + if enable_discount_accounting and self.get('discount_amount') and self.get('additional_discount_account'): + self.make_gle_for_additional_discount_applied_on_taxes(gl_entries) + def make_internal_transfer_gl_entries(self, gl_entries): if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges): account_currency = get_account_currency(self.unrealized_profit_loss_account) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 8fc4023f2e2..f4593c2a762 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -852,9 +852,6 @@ class AccountsController(TransactionBase): "project": item.project or self.project }, account_currency, item=item) ) - - if self.get('discount_amount') and self.get('additional_discount_account'): - self.make_gle_for_additional_discount_applied_on_taxes(gl_entries) def make_gle_for_additional_discount_applied_on_taxes(self, gl_entries): for tax in self.get("taxes"): From 4da7c5882ba8a05c42a013c3b0690b1701da14f9 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:45:35 +0530 Subject: [PATCH 132/369] fix: Only display Additional Discount Account if Enable Discount Accounting is checked --- .../accounts/doctype/accounts_settings/accounts_settings.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 053f061acc7..24b0ec4d4a8 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -41,5 +41,7 @@ class AccountsSettings(Document): for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]: make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) - make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) - make_property_setter("Sales Invoice", "additional_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) \ No newline at end of file + for doctype in ["Sales Invoice", "Purchase Invoice"]: + make_property_setter(doctype, "additional_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) + + make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) \ No newline at end of file From d62af77ca8e88b83a921e892fffd583ffd21f6c6 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:47:20 +0530 Subject: [PATCH 133/369] fix: Remove unnecessary condition --- .../accounts/doctype/sales_invoice/sales_invoice.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index fab0e113240..a5341df2e38 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -629,13 +629,11 @@ frappe.ui.form.on('Sales Invoice', { // discount account frm.fields_dict['items'].grid.get_field('discount_account').get_query = function(doc) { - if (erpnext.is_perpetual_inventory_enabled(doc.company)) { - return { - filters: { - 'report_type': 'Profit and Loss', - 'company': doc.company, - "is_group": 0 - } + return { + filters: { + 'report_type': 'Profit and Loss', + 'company': doc.company, + "is_group": 0 } } } From 99652876d090781916ce8af600e619ea63be0d86 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 18:45:21 +0530 Subject: [PATCH 134/369] fix: Add test for additional discount applied on taxes --- .../sales_invoice/test_sales_invoice.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 2ddfad9c310..1e2e92f6093 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2001,6 +2001,35 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, si.name, expected_gle, nowdate()) + def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled(self): + from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import enable_discount_accounting + + enable_discount_accounting() + additional_discount_account = create_account(account_name="Discount Account", + parent_account="Indirect Expenses - _TC", company="_Test Company") + + si = create_sales_invoice(rate=75000, do_not_save=1) + si.apply_discount_on = "Grand Total" + si.additional_discount_account = additional_discount_account + si.additional_discount_percentage = 10 + si.append("taxes", { + "charge_type": "On Net Total", + "account_head": "CGST - _TC", + "cost_center": "Main - _TC", + "description": "CGST @ 9.0", + "rate": 9 + }) + si.submit() + + expected_gle = [ + ["Sales - _TC", 0.0, 67500.0, nowdate()], + ["Discount Account - _TC", 675.0, 0.0, nowdate()], + ["CGST - _TC", 0.0, 6750.0, nowdate()], + ["Debtors - _TC", 73575.0, 0.0, nowdate()] + ] + + check_gl_entries(self, si.name, expected_gle, nowdate()) + def get_sales_invoice_for_e_invoice(): si = make_sales_invoice_for_ewaybill() si.naming_series = 'INV-2020-.#####' From 99cb89f449b6013457f538cd027b72556e18ebf8 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 19:49:16 +0530 Subject: [PATCH 135/369] fix: Switch debit and credit for ledger entries for discount applied on taxes for Purchase Invoice --- erpnext/controllers/accounts_controller.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index f4593c2a762..aa2fe29bc69 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -858,14 +858,22 @@ class AccountsController(TransactionBase): if flt(tax.base_tax_amount_after_discount_amount) and flt(tax.base_tax_amount): account_currency = get_account_currency(tax.account_head) additional_discount_applied_on_taxes = flt(tax.base_tax_amount) - flt(tax.base_tax_amount_after_discount_amount) + if self.doctype == 'Purchase Invoice': + against = self.supplier + dr_or_cr = "debit" + rev_dr_cr = "credit" + else: + against = self.customer + dr_or_cr = "credit" + rev_dr_cr = "debit" gl_entries.append( self.get_gl_dict({ "account": tax.account_head, - "against": self.customer, - "credit": flt(additional_discount_applied_on_taxes, + "against": against, + dr_or_cr: flt(additional_discount_applied_on_taxes, tax.precision("tax_amount_after_discount_amount")), - "credit_in_account_currency": (flt(additional_discount_applied_on_taxes, + dr_or_cr + "_in_account_currency": (flt(additional_discount_applied_on_taxes, tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else flt(additional_discount_applied_on_taxes, tax.precision("tax_amount_after_discount_amount"))), "cost_center": tax.cost_center @@ -875,17 +883,16 @@ class AccountsController(TransactionBase): gl_entries.append( self.get_gl_dict({ "account": self.additional_discount_account, - "against": self.customer, - "debit": flt(additional_discount_applied_on_taxes, + "against": against, + rev_dr_cr: flt(additional_discount_applied_on_taxes, tax.precision("tax_amount_after_discount_amount")), - "debit_in_account_currency": (flt(additional_discount_applied_on_taxes, + rev_dr_cr + "_in_account_currency": (flt(additional_discount_applied_on_taxes, tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else flt(additional_discount_applied_on_taxes, tax.precision("tax_amount_after_discount_amount"))), "cost_center": tax.cost_center }, account_currency, item=tax) ) - def allocate_advance_taxes(self, gl_entries): tax_map = self.get_tax_map() for pe in self.get("advances"): From 251f2296018a996737da6eadddcb650994b3f728 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 19:49:42 +0530 Subject: [PATCH 136/369] fix: Add test for additional discount applied on taxes --- .../purchase_invoice/test_purchase_invoice.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 36fbc7e3513..4eb71f8e704 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -247,6 +247,35 @@ class TestPurchaseInvoice(unittest.TestCase): check_gl_entries(self, pi.name, expected_gle, nowdate()) + def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabled(self): + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import check_gl_entries + + enable_discount_accounting() + additional_discount_account = create_account(account_name="Discount Account", + parent_account="Indirect Expenses - _TC", company="_Test Company") + + pi = make_purchase_invoice(qty=1, rate=75000, do_not_save=1) + pi.apply_discount_on = "Grand Total" + pi.additional_discount_account = additional_discount_account + pi.additional_discount_percentage = 10 + pi.append("taxes", { + "charge_type": "On Net Total", + "account_head": "CGST - _TC", + "cost_center": "Main - _TC", + "description": "CGST @ 9.0", + "rate": 9 + }) + pi.submit() + + expected_gle = [ + ["Discount Account - _TC", 0.0, 675.0, nowdate()], + ["CGST - _TC", 6750.0, 0.0, nowdate()], + ["_Test Account Cost for Goods Sold - _TC", 67500.0, 0.0, nowdate()], + ["Creditors - _TC", 0.0, 73575.0, nowdate()] + ] + + check_gl_entries(self, pi.name, expected_gle, nowdate()) + def test_purchase_invoice_change_naming_series(self): pi = frappe.copy_doc(test_records[1]) pi.insert() From b4a8bc8e4cbacb80e077b450195c1be346524387 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 19 Jul 2021 23:43:36 +0530 Subject: [PATCH 137/369] fix: Use the item's cost centre instead of the Invoice's Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index aa2fe29bc69..65dbe17ec1d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -836,7 +836,7 @@ class AccountsController(TransactionBase): "against": supplier_or_customer, dr_or_cr: flt(item.discount_amount), dr_or_cr + "_in_account_currency": flt(item.discount_amount), - "cost_center": self.cost_center, + "cost_center": item.cost_center, "project": self.project }, account_currency, item=self) ) From e7e9bda1235f2b9d35b1f8a9d3b525a026bf4dd5 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 19 Jul 2021 23:44:21 +0530 Subject: [PATCH 138/369] fix: Use the item's project instead of the invoice's Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 65dbe17ec1d..2aac4968a28 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -837,7 +837,7 @@ class AccountsController(TransactionBase): dr_or_cr: flt(item.discount_amount), dr_or_cr + "_in_account_currency": flt(item.discount_amount), "cost_center": item.cost_center, - "project": self.project + "project": item.project }, account_currency, item=self) ) From f421dc7ca74f97794c8eca5bf52fcfabef178cfe Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 19 Jul 2021 23:44:55 +0530 Subject: [PATCH 139/369] fix: GL Entry creation Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 2aac4968a28..9b3336cde50 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -838,7 +838,7 @@ class AccountsController(TransactionBase): dr_or_cr + "_in_account_currency": flt(item.discount_amount), "cost_center": item.cost_center, "project": item.project - }, account_currency, item=self) + }, account_currency, item=item) ) account_currency = get_account_currency(income_or_expense_account) From 821b75f1b119b5ef9be2278889882db3b73af33c Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 19 Jul 2021 23:46:38 +0530 Subject: [PATCH 140/369] fix: Filter for additional_discount_account Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index a5341df2e38..02562278863 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -596,7 +596,7 @@ frappe.ui.form.on('Sales Invoice', { filters: { company: frm.doc.company, is_group: 0, - root_type: "Profit and Loss", + report_type: "Profit and Loss", } }; }); From ed6ebdf5ed297352ac7120bb910662d8c4a41183 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 19 Jul 2021 23:47:58 +0530 Subject: [PATCH 141/369] fix: Filter for additional_discount_account Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 4ed31cc2272..bf78c028c81 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -504,7 +504,7 @@ frappe.ui.form.on("Purchase Invoice", { filters: { company: frm.doc.company, is_group: 0, - root_type: "Profit and Loss", + report_type: "Profit and Loss", } }; }); From 566e8f849903644cad157fb8ae9a790d304e84e4 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 20 Jul 2021 03:46:02 +0530 Subject: [PATCH 142/369] fix: Create GL Entries for Additional Discount Account --- .../purchase_invoice/purchase_invoice.py | 11 ++-- .../doctype/sales_invoice/sales_invoice.py | 31 +++++++--- erpnext/controllers/accounts_controller.py | 60 ++++++------------- 3 files changed, 45 insertions(+), 57 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 006f5bb0e86..feae213d92d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -609,7 +609,11 @@ class PurchaseInvoice(BuyingController): if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) if not item.is_fixed_asset: - amount = flt(item.base_net_amount, item.precision("base_net_amount")) + if frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'): + amount = flt(item.base_amount, item.precision("base_amount")) + else: + amount = flt(item.base_net_amount, item.precision("base_net_amount")) + else: amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount")) @@ -888,11 +892,6 @@ class PurchaseInvoice(BuyingController): "remarks": self.remarks or "Accounting Entry for Stock" }, item=tax)) - enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) - - if enable_discount_accounting and self.get('discount_amount') and self.get('additional_discount_account'): - self.make_gle_for_additional_discount_applied_on_taxes(gl_entries) - def make_internal_transfer_gl_entries(self, gl_entries): if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges): account_currency = get_account_currency(self.unrealized_profit_loss_account) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ee9b59e769d..52c2a9c42c3 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -902,11 +902,6 @@ class SalesInvoice(SellingController): }, account_currency, item=tax) ) - enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) - - if enable_discount_accounting and self.get('discount_amount') and self.get('additional_discount_account'): - self.make_gle_for_additional_discount_applied_on_taxes(gl_entries) - def make_internal_transfer_gl_entries(self, gl_entries): if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges): account_currency = get_account_currency(self.unrealized_profit_loss_account) @@ -946,15 +941,17 @@ class SalesInvoice(SellingController): income_account = (item.income_account if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account) + amount, base_amount = self.get_amount_and_base_amount(item) + account_currency = get_account_currency(income_account) gl_entries.append( self.get_gl_dict({ "account": income_account, "against": self.customer, - "credit": flt(item.base_net_amount, item.precision("base_net_amount")), - "credit_in_account_currency": (flt(item.base_net_amount, item.precision("base_net_amount")) + "credit": flt(base_amount, item.precision("base_net_amount")), + "credit_in_account_currency": (flt(base_amount, item.precision("base_net_amount")) if account_currency==self.company_currency - else flt(item.net_amount, item.precision("net_amount"))), + else flt(amount, item.precision("net_amount"))), "cost_center": item.cost_center, "project": item.project or self.project }, account_currency, item=item) @@ -965,6 +962,24 @@ class SalesInvoice(SellingController): erpnext.is_perpetual_inventory_enabled(self.company): gl_entries += super(SalesInvoice, self).get_gl_entries() + def get_amount_and_base_amount(self, item): + amount = item.net_amount + base_amount = item.base_net_amount + + enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + + if enable_discount_accounting and self.get('discount_amount') and self.get('additional_discount_account'): + amount = item.amount + base_amount = item.base_amount + + return amount, base_amount + + def set_asset_status(self, asset): + if self.is_return: + asset.set_status() + else: + asset.set_status("Sold" if self.docstatus==1 else None) + def make_loyalty_point_redemption_gle(self, gl_entries): if cint(self.redeem_loyalty_points): gl_entries.append( diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 9b3336cde50..59879e0df58 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -812,19 +812,23 @@ class AccountsController(TransactionBase): enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) if enable_discount_accounting: + if self.doctype == "Purchase Invoice": + dr_or_cr = "credit" + rev_dr_cr = "debit" + supplier_or_customer = self.supplier + + else: + dr_or_cr = "debit" + rev_dr_cr = "credit" + supplier_or_customer = self.customer + for item in self.get("items"): if item.get('discount_amount') and item.get('discount_account'): if self.doctype == "Purchase Invoice": - dr_or_cr = "credit" - rev_dr_cr = "debit" - supplier_or_customer = self.supplier income_or_expense_account = (item.expense_account if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) else: - dr_or_cr = "debit" - rev_dr_cr = "credit" - supplier_or_customer = self.customer income_or_expense_account = (item.income_account if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account) @@ -853,46 +857,16 @@ class AccountsController(TransactionBase): }, account_currency, item=item) ) - def make_gle_for_additional_discount_applied_on_taxes(self, gl_entries): - for tax in self.get("taxes"): - if flt(tax.base_tax_amount_after_discount_amount) and flt(tax.base_tax_amount): - account_currency = get_account_currency(tax.account_head) - additional_discount_applied_on_taxes = flt(tax.base_tax_amount) - flt(tax.base_tax_amount_after_discount_amount) - if self.doctype == 'Purchase Invoice': - against = self.supplier - dr_or_cr = "debit" - rev_dr_cr = "credit" - else: - against = self.customer - dr_or_cr = "credit" - rev_dr_cr = "debit" - - gl_entries.append( - self.get_gl_dict({ - "account": tax.account_head, - "against": against, - dr_or_cr: flt(additional_discount_applied_on_taxes, - tax.precision("tax_amount_after_discount_amount")), - dr_or_cr + "_in_account_currency": (flt(additional_discount_applied_on_taxes, - tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else - flt(additional_discount_applied_on_taxes, tax.precision("tax_amount_after_discount_amount"))), - "cost_center": tax.cost_center - }, account_currency, item=tax) - ) - + if self.get('discount_amount') and self.get('additional_discount_account'): gl_entries.append( self.get_gl_dict({ "account": self.additional_discount_account, - "against": against, - rev_dr_cr: flt(additional_discount_applied_on_taxes, - tax.precision("tax_amount_after_discount_amount")), - rev_dr_cr + "_in_account_currency": (flt(additional_discount_applied_on_taxes, - tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else - flt(additional_discount_applied_on_taxes, tax.precision("tax_amount_after_discount_amount"))), - "cost_center": tax.cost_center - }, account_currency, item=tax) - ) - + "against": supplier_or_customer, + dr_or_cr: self.discount_amount, + "cost_center": self.cost_center + }, item=self) + ) + def allocate_advance_taxes(self, gl_entries): tax_map = self.get_tax_map() for pe in self.get("advances"): From 1f6c05f0139a8ad9f1d22c4c35552e51e767180f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 20 Jul 2021 03:52:39 +0530 Subject: [PATCH 143/369] fix: Make discount_account mandatory if discount accounting is enabled --- erpnext/accounts/doctype/accounts_settings/accounts_settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 24b0ec4d4a8..a3a32d5e975 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -40,6 +40,7 @@ class AccountsSettings(Document): for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]: make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) + make_property_setter(doctype, "discount_account", "mandatory", enable_discount_accounting, "Check", validate_fields_for_doctype=False) for doctype in ["Sales Invoice", "Purchase Invoice"]: make_property_setter(doctype, "additional_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) From 45327e04db1a35f439e874e2e5175c87683af959 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 20 Jul 2021 05:16:33 +0530 Subject: [PATCH 144/369] fix: Tests --- .../purchase_invoice/test_purchase_invoice.py | 43 +++++++++++++------ .../sales_invoice/test_sales_invoice.py | 25 +++++------ 2 files changed, 43 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 4eb71f8e704..0487ef64443 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -231,8 +231,6 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(expected_values[gle.account][2], gle.credit) def test_purchase_invoice_with_discount_accounting_enabled(self): - from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import check_gl_entries - enable_discount_accounting() discount_account = create_account(account_name="Discount Account", @@ -240,38 +238,45 @@ class TestPurchaseInvoice(unittest.TestCase): pi = make_purchase_invoice(discount_account=discount_account, discount_amount=100) expected_gle = [ - ["Discount Account - _TC", 0.0, 100.0, nowdate()], ["_Test Account Cost for Goods Sold - _TC", 350.0, 0.0, nowdate()], - ["Creditors - _TC", 0.0, 250.0, nowdate()] + ["Creditors - _TC", 0.0, 250.0, nowdate()], + ["Discount Account - _TC", 0.0, 100.0, nowdate()] ] check_gl_entries(self, pi.name, expected_gle, nowdate()) def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabled(self): - from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import check_gl_entries - enable_discount_accounting() additional_discount_account = create_account(account_name="Discount Account", parent_account="Indirect Expenses - _TC", company="_Test Company") - pi = make_purchase_invoice(qty=1, rate=75000, do_not_save=1) + pi = make_purchase_invoice(qty=1, rate=100, do_not_save=1) pi.apply_discount_on = "Grand Total" pi.additional_discount_account = additional_discount_account - pi.additional_discount_percentage = 10 + pi.additional_discount_percentage = 20 pi.append("taxes", { "charge_type": "On Net Total", "account_head": "CGST - _TC", "cost_center": "Main - _TC", "description": "CGST @ 9.0", - "rate": 9 + "base_tax_amount": 20, + "base_tax_amount_after_discount_amount": 20 }) pi.submit() + # gle = frappe.get_all( + # "GL Entry", + # fields = ['account', 'debit', 'credit', 'posting_date'], + # filters = {'voucher_no': pi.name} + # ) + # for gl in gle: + # print(gl, "\n") + expected_gle = [ - ["Discount Account - _TC", 0.0, 675.0, nowdate()], - ["CGST - _TC", 6750.0, 0.0, nowdate()], - ["_Test Account Cost for Goods Sold - _TC", 67500.0, 0.0, nowdate()], - ["Creditors - _TC", 0.0, 73575.0, nowdate()] + ["CGST - _TC", 20.0, 0.0, nowdate()], + ["Creditors - _TC", 0.0, 96.0, nowdate()], + ["Discount Account - _TC", 0.0, 24.0, nowdate()], + ["_Test Account Cost for Goods Sold - _TC", 100.0, 0.0, nowdate()] ] check_gl_entries(self, pi.name, expected_gle, nowdate()) @@ -1186,6 +1191,18 @@ class TestPurchaseInvoice(unittest.TestCase): self.assertEqual(expected_gle[i][0], gle.account) self.assertEqual(expected_gle[i][1], gle.amount) +def check_gl_entries(doc, voucher_no, expected_gle, posting_date): + gl_entries = frappe.db.sql("""select account, debit, credit, posting_date + from `tabGL Entry` + where voucher_type='Purchase Invoice' and voucher_no=%s and posting_date >= %s + order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1) + + for i, gle in enumerate(gl_entries): + doc.assertEqual(expected_gle[i][0], gle.account) + doc.assertEqual(expected_gle[i][1], gle.debit) + doc.assertEqual(expected_gle[i][2], gle.credit) + doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date) + def update_tax_witholding_category(company, account, date): from erpnext.accounts.utils import get_fiscal_year diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 1e2e92f6093..9f7d18bdb4a 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1994,12 +1994,12 @@ class TestSalesInvoice(unittest.TestCase): si = create_sales_invoice(discount_account=discount_account, discount_amount=100) expected_gle = [ + ["Debtors - _TC", 100.0, 0.0, nowdate()], ["Discount Account - _TC", 100.0, 0.0, nowdate()], - ["Sales - _TC", 0.0, 200.0, nowdate()], - ["Debtors - _TC", 100.0, 0.0, nowdate()] + ["Sales - _TC", 0.0, 200.0, nowdate()] ] - check_gl_entries(self, si.name, expected_gle, nowdate()) + check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled(self): from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import enable_discount_accounting @@ -2008,27 +2008,28 @@ class TestSalesInvoice(unittest.TestCase): additional_discount_account = create_account(account_name="Discount Account", parent_account="Indirect Expenses - _TC", company="_Test Company") - si = create_sales_invoice(rate=75000, do_not_save=1) + si = create_sales_invoice(rate=100, do_not_save=1) si.apply_discount_on = "Grand Total" si.additional_discount_account = additional_discount_account - si.additional_discount_percentage = 10 + si.additional_discount_percentage = 20 si.append("taxes", { - "charge_type": "On Net Total", + "charge_type": "Actual", "account_head": "CGST - _TC", "cost_center": "Main - _TC", "description": "CGST @ 9.0", - "rate": 9 + "rate": 0, + "tax_amount": 20 }) si.submit() expected_gle = [ - ["Sales - _TC", 0.0, 67500.0, nowdate()], - ["Discount Account - _TC", 675.0, 0.0, nowdate()], - ["CGST - _TC", 0.0, 6750.0, nowdate()], - ["Debtors - _TC", 73575.0, 0.0, nowdate()] + ["CGST - _TC", 0.0, 20.0, nowdate()], + ["Debtors - _TC", 96.0, 0.0, nowdate()], + ["Discount Account - _TC", 24.0, 0.0, nowdate()], + ["Sales - _TC", 0.0, 100.0, nowdate()] ] - check_gl_entries(self, si.name, expected_gle, nowdate()) + check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) def get_sales_invoice_for_e_invoice(): si = make_sales_invoice_for_ewaybill() From 4fa409e0194fde280647b31c96a404099cf99eb1 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 20 Jul 2021 22:03:44 +0530 Subject: [PATCH 145/369] fix: Add mandatory_depends_on property for Discount Account --- .../doctype/accounts_settings/accounts_settings.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index a3a32d5e975..55449132928 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -40,9 +40,16 @@ class AccountsSettings(Document): for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]: make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) - make_property_setter(doctype, "discount_account", "mandatory", enable_discount_accounting, "Check", validate_fields_for_doctype=False) + if enable_discount_accounting: + make_property_setter(doctype, "discount_account", "mandatory_depends_on", "eval: doc.discount_amount", "Code", validate_fields_for_doctype=False) + else: + make_property_setter(doctype, "discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False) for doctype in ["Sales Invoice", "Purchase Invoice"]: make_property_setter(doctype, "additional_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) + if enable_discount_accounting: + make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "eval: doc.discount_amount", "Code", validate_fields_for_doctype=False) + else: + make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False) make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) \ No newline at end of file From fc09d845c5b38dd60783dfd287a71f7ab0dcbad9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 15:25:09 +0530 Subject: [PATCH 146/369] fix: Syntax Error --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 02562278863..56f11650ffa 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -348,7 +348,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte items_add: function(doc, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); this.frm.script_manager.copy_from_first_row("items", row, ["income_account", "discount_account", "cost_center"]); - } + }, set_dynamic_labels: function() { this._super(); From 5a06019440f80b2c3719f195cdf5619d3715b759 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 15:25:40 +0530 Subject: [PATCH 147/369] fix: GL For taxes if discount applied on Grand Total --- .../doctype/sales_invoice/sales_invoice.py | 26 +++++++------------ erpnext/controllers/accounts_controller.py | 21 +++++++++++++++ 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 52c2a9c42c3..2539de7b124 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -886,18 +886,22 @@ class SalesInvoice(SellingController): ) def make_tax_gl_entries(self, gl_entries): + enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + for tax in self.get("taxes"): + amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting) + if flt(tax.base_tax_amount_after_discount_amount): account_currency = get_account_currency(tax.account_head) gl_entries.append( self.get_gl_dict({ "account": tax.account_head, "against": self.customer, - "credit": flt(tax.base_tax_amount_after_discount_amount, + "credit": flt(base_amount, tax.precision("tax_amount_after_discount_amount")), - "credit_in_account_currency": (flt(tax.base_tax_amount_after_discount_amount, + "credit_in_account_currency": (flt(base_amount, tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else - flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))), + flt(amount, tax.precision("tax_amount_after_discount_amount"))), "cost_center": tax.cost_center }, account_currency, item=tax) ) @@ -916,6 +920,8 @@ class SalesInvoice(SellingController): def make_item_gl_entries(self, gl_entries): # income account gl entries + enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + for item in self.get("items"): if flt(item.base_net_amount, item.precision("base_net_amount")): if item.is_fixed_asset: @@ -941,7 +947,7 @@ class SalesInvoice(SellingController): income_account = (item.income_account if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account) - amount, base_amount = self.get_amount_and_base_amount(item) + amount, base_amount = self.get_amount_and_base_amount(item, enable_discount_accounting) account_currency = get_account_currency(income_account) gl_entries.append( @@ -962,18 +968,6 @@ class SalesInvoice(SellingController): erpnext.is_perpetual_inventory_enabled(self.company): gl_entries += super(SalesInvoice, self).get_gl_entries() - def get_amount_and_base_amount(self, item): - amount = item.net_amount - base_amount = item.base_net_amount - - enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) - - if enable_discount_accounting and self.get('discount_amount') and self.get('additional_discount_account'): - amount = item.amount - base_amount = item.base_amount - - return amount, base_amount - def set_asset_status(self, asset): if self.is_return: asset.set_status() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 59879e0df58..3d048c36865 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -808,6 +808,27 @@ class AccountsController(TransactionBase): tax_map[tax.account_head] -= allocated_amount allocated_tax_map[tax.account_head] -= allocated_amount + def get_amount_and_base_amount(self, item, enable_discount_accounting): + amount = item.net_amount + base_amount = item.base_net_amount + + if enable_discount_accounting and self.get('discount_amount') and self.get('additional_discount_account'): + amount = item.amount + base_amount = item.base_amount + + return amount, base_amount + + def get_tax_amounts(self, tax, enable_discount_accounting): + amount = tax.tax_amount_after_discount_amount + base_amount = tax.base_tax_amount_after_discount_amount + + if enable_discount_accounting and self.get('discount_amount') and self.get('additional_discount_account') \ + and self.get('apply_discount_on') == 'Grand Total': + amount = tax.tax_amount + base_amount = tax.base_tax_amount + + return amount, base_amount + def make_discount_gl_entries(self, gl_entries): enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) From c765073c2a56688835b04d72fc335f654e2ef86a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 22:28:32 +0530 Subject: [PATCH 148/369] fix: Tests --- .../purchase_invoice/test_purchase_invoice.py | 20 +++++++++---------- .../sales_invoice/test_sales_invoice.py | 9 +++++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 0487ef64443..e20e385f63c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -250,17 +250,16 @@ class TestPurchaseInvoice(unittest.TestCase): additional_discount_account = create_account(account_name="Discount Account", parent_account="Indirect Expenses - _TC", company="_Test Company") - pi = make_purchase_invoice(qty=1, rate=100, do_not_save=1) + pi = make_purchase_invoice(qty=1, rate=100, do_not_save=1, parent_cost_center="Main - _TC") pi.apply_discount_on = "Grand Total" pi.additional_discount_account = additional_discount_account pi.additional_discount_percentage = 20 pi.append("taxes", { - "charge_type": "On Net Total", - "account_head": "CGST - _TC", + "charge_type": "Actual", + "account_head": "_Test Account VAT - _TC", "cost_center": "Main - _TC", - "description": "CGST @ 9.0", - "base_tax_amount": 20, - "base_tax_amount_after_discount_amount": 20 + "description": "Test", + "tax_amount": 20 }) pi.submit() @@ -273,10 +272,10 @@ class TestPurchaseInvoice(unittest.TestCase): # print(gl, "\n") expected_gle = [ - ["CGST - _TC", 20.0, 0.0, nowdate()], + ["_Test Account Cost for Goods Sold - _TC", 100.0, 0.0, nowdate()], + ["_Test Account VAT - _TC", 20.0, 0.0, nowdate()], ["Creditors - _TC", 0.0, 96.0, nowdate()], - ["Discount Account - _TC", 0.0, 24.0, nowdate()], - ["_Test Account Cost for Goods Sold - _TC", 100.0, 0.0, nowdate()] + ["Discount Account - _TC", 0.0, 24.0, nowdate()] ] check_gl_entries(self, pi.name, expected_gle, nowdate()) @@ -1197,6 +1196,7 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): where voucher_type='Purchase Invoice' and voucher_no=%s and posting_date >= %s order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1) + print(gl_entries) for i, gle in enumerate(gl_entries): doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) @@ -1260,7 +1260,7 @@ def make_purchase_invoice(**args): pi.return_against = args.return_against pi.is_subcontracted = args.is_subcontracted or "No" pi.supplier_warehouse = args.supplier_warehouse or "_Test Warehouse 1 - _TC" - pi.cost_center = args.cost_center or "_Test Cost Center - _TC" + pi.cost_center = args.parent_cost_center pi.append("items", { "item_code": args.item or args.item_code or "_Test Item", diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 9f7d18bdb4a..1c0177d3d08 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2014,16 +2014,17 @@ class TestSalesInvoice(unittest.TestCase): si.additional_discount_percentage = 20 si.append("taxes", { "charge_type": "Actual", - "account_head": "CGST - _TC", + "account_head": "_Test Account VAT - _TC", "cost_center": "Main - _TC", - "description": "CGST @ 9.0", + "parent_cost_center": "Main - _TC", + "description": "Test", "rate": 0, "tax_amount": 20 }) si.submit() expected_gle = [ - ["CGST - _TC", 0.0, 20.0, nowdate()], + ["_Test Account VAT - _TC", 0.0, 20.0, nowdate()], ["Debtors - _TC", 96.0, 0.0, nowdate()], ["Discount Account - _TC", 24.0, 0.0, nowdate()], ["Sales - _TC", 0.0, 100.0, nowdate()] @@ -2199,7 +2200,7 @@ def create_sales_invoice(**args): si.currency=args.currency or "INR" si.conversion_rate = args.conversion_rate or 1 si.naming_series = args.naming_series or "T-SINV-" - si.cost_center = args.cost_center or "_Test Cost Center - _TC" + si.cost_center = args.parent_cost_center si.append("items", { "item_code": args.item or args.item_code or "_Test Item", From 117676175728e3f34edbfea9b989da26ba9a6e84 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 21 Jul 2021 23:19:47 +0530 Subject: [PATCH 149/369] feat: Expand All nodes option in Desktop view --- .../hierarchy_chart_desktop.js | 152 +++++++++++++++--- erpnext/public/scss/hierarchy_chart.scss | 1 + erpnext/utilities/hierarchy_chart.py | 29 ++++ 3 files changed, 159 insertions(+), 23 deletions(-) create mode 100644 erpnext/utilities/hierarchy_chart.py diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index fe4d17c2102..694c26567aa 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -36,7 +36,11 @@ erpnext.HierarchyChart = class { me.nodes[this.id] = this; me.make_node_element(this); - me.setup_node_click_action(this); + + if (!me.all_nodes_expanded) { + me.setup_node_click_action(this); + } + me.setup_edit_node_action(this); } }; @@ -60,8 +64,9 @@ erpnext.HierarchyChart = class { show() { frappe.breadcrumbs.add('HR'); - let me = this; + this.setup_actions(); if ($(`[data-fieldname="company"]`).length) return; + let me = this; let company = this.page.add_field({ fieldtype: 'Link', @@ -79,20 +84,9 @@ erpnext.HierarchyChart = class { // svg for connectors me.make_svg_markers(); - - if (me.$hierarchy) - me.$hierarchy.remove(); - - // setup hierarchy - me.$hierarchy = $( - `
                          -
                        • -
                            -
                          • -
                          `); - - me.page.main.append(me.$hierarchy); + me.setup_hierarchy() me.render_root_nodes(); + me.all_nodes_expanded = false; } } }); @@ -101,6 +95,42 @@ erpnext.HierarchyChart = class { $(`[data-fieldname="company"]`).trigger('change'); } + setup_actions() { + let me = this; + this.page.add_inner_button(__('Expand All'), function() { + me.load_children(me.root_node, true); + me.all_nodes_expanded = true; + + me.page.remove_inner_button(__('Expand All')); + me.page.add_inner_button(__('Collapse All'), function() { + me.setup_hierarchy(); + me.render_root_nodes(); + me.all_nodes_expanded = false; + + me.page.remove_inner_button(__('Collapse All')); + me.setup_actions(); + }); + }); + } + + setup_hierarchy() { + if (this.$hierarchy) + this.$hierarchy.remove(); + + $(`#connectors`).empty(); + + // setup hierarchy + this.$hierarchy = $( + `
                            +
                          • +
                              +
                            • +
                            `); + + this.page.main.append(this.$hierarchy); + this.nodes = {}; + } + make_svg_markers() { $('#arrows').remove(); @@ -126,7 +156,7 @@ erpnext.HierarchyChart = class { `); } - render_root_nodes() { + render_root_nodes(expanded_view=false) { let me = this; frappe.call({ @@ -156,7 +186,10 @@ erpnext.HierarchyChart = class { expand_node = node; }); - me.expand_node(expand_node); + if (!expanded_view) { + me.root_node = expand_node; + me.expand_node(expand_node); + } } }); } @@ -196,11 +229,20 @@ erpnext.HierarchyChart = class { $(`#${node.parent_id}`).addClass('active-path'); } - load_children(node) { - frappe.run_serially([ - () => this.get_child_nodes(node.id), - (child_nodes) => this.render_child_nodes(node, child_nodes) - ]); + load_children(node, deep=false) { + if (!deep) { + frappe.run_serially([ + () => this.get_child_nodes(node.id), + (child_nodes) => this.render_child_nodes(node, child_nodes) + ]); + } else { + frappe.run_serially([ + () => this.setup_hierarchy(), + () => this.render_root_nodes(true), + () => this.get_all_nodes(node.id, node.name), + (data_list) => this.render_children_of_all_nodes(data_list) + ]); + } } get_child_nodes(node_id) { @@ -247,6 +289,70 @@ erpnext.HierarchyChart = class { node.expanded = true; } + get_all_nodes(node_id, node_name) { + return new Promise(resolve => { + frappe.call({ + method: 'erpnext.utilities.hierarchy_chart.get_all_nodes', + args: { + method: this.method, + company: this.company, + parent: node_id, + parent_name: node_name + }, + callback: (r) => { + resolve(r.message); + } + }); + }); + } + + render_children_of_all_nodes(data_list) { + let entry = undefined; + let node = undefined; + + while(data_list.length) { + // to avoid overlapping connectors + entry = data_list.shift(); + node = this.nodes[entry.parent]; + if (node) { + this.render_child_nodes_for_expanded_view(node, entry.data); + } else { + data_list.push(entry); + } + } + } + + render_child_nodes_for_expanded_view(node, child_nodes) { + node.$children = $('
                              ') + + const last_level = this.$hierarchy.find('.level:last').index(); + const node_level = $(`#${node.id}`).parent().parent().parent().index(); + + if (last_level === node_level) { + this.$hierarchy.append(` +
                            • + `); + node.$children.appendTo(this.$hierarchy.find('.level:last')); + } else { + node.$children.appendTo(this.$hierarchy.find('.level:eq(' + (node_level + 1) + ')')); + } + + node.$children.hide().empty(); + + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_node(node, data); + setTimeout(() => { + this.add_connector(node.id, data.id); + }, 250); + }); + } + + node.$children.show(); + $(`path[data-parent="${node.id}"]`).show(); + node.expanded = true; + } + add_node(node, data) { return new this.Node({ id: data.id, @@ -333,7 +439,7 @@ erpnext.HierarchyChart = class { path.setAttribute("class", "active-connector"); path.setAttribute("marker-start", "url(#arrowstart-active)"); path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if (parent.hasClass('active-path')) { + } else { path.setAttribute("class", "collapsed-connector"); path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index dd523c34439..1c2f9421faa 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -194,6 +194,7 @@ .level { margin-right: 8px; align-items: flex-start; + flex-direction: column; } #arrows { diff --git a/erpnext/utilities/hierarchy_chart.py b/erpnext/utilities/hierarchy_chart.py new file mode 100644 index 00000000000..9b0279351f2 --- /dev/null +++ b/erpnext/utilities/hierarchy_chart.py @@ -0,0 +1,29 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +@frappe.whitelist() +def get_all_nodes(parent, parent_name, method, company): + '''Recursively gets all data from nodes''' + method = frappe.get_attr(method) + + if not method in frappe.whitelisted: + frappe.throw(_('Not Permitted'), frappe.PermissionError) + + data = method(parent, company) + result = [dict(parent=parent, parent_name=parent_name, data=data)] + + nodes_to_expand = [{'id': d.get('id'), 'name': d.get('name')} for d in data if d.get('expandable')] + + while nodes_to_expand: + parent = nodes_to_expand.pop(0) + data = method(parent.get('id'), company) + result.append(dict(parent=parent.get('id'), parent_name=parent.get('name'), data=data)) + for d in data: + if d.get('expandable'): + nodes_to_expand.append({'id': d.get('id'), 'name': d.get('name')}) + + return result \ No newline at end of file From 2ff0d3e0ebf5424ad8a50f65ad64b531a13f4b40 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jul 2021 10:43:16 +0530 Subject: [PATCH 150/369] fix: Test Cases --- .../doctype/purchase_invoice/test_purchase_invoice.py | 9 --------- .../accounts/doctype/sales_invoice/test_sales_invoice.py | 3 +-- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index e20e385f63c..9d2acdc9779 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -263,14 +263,6 @@ class TestPurchaseInvoice(unittest.TestCase): }) pi.submit() - # gle = frappe.get_all( - # "GL Entry", - # fields = ['account', 'debit', 'credit', 'posting_date'], - # filters = {'voucher_no': pi.name} - # ) - # for gl in gle: - # print(gl, "\n") - expected_gle = [ ["_Test Account Cost for Goods Sold - _TC", 100.0, 0.0, nowdate()], ["_Test Account VAT - _TC", 20.0, 0.0, nowdate()], @@ -1196,7 +1188,6 @@ def check_gl_entries(doc, voucher_no, expected_gle, posting_date): where voucher_type='Purchase Invoice' and voucher_no=%s and posting_date >= %s order by posting_date asc, account asc""", (voucher_no, posting_date), as_dict=1) - print(gl_entries) for i, gle in enumerate(gl_entries): doc.assertEqual(expected_gle[i][0], gle.account) doc.assertEqual(expected_gle[i][1], gle.debit) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 1c0177d3d08..a7fbbdd5caa 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2008,7 +2008,7 @@ class TestSalesInvoice(unittest.TestCase): additional_discount_account = create_account(account_name="Discount Account", parent_account="Indirect Expenses - _TC", company="_Test Company") - si = create_sales_invoice(rate=100, do_not_save=1) + si = create_sales_invoice(rate=100, parent_cost_center='Main - _TC', do_not_save=1) si.apply_discount_on = "Grand Total" si.additional_discount_account = additional_discount_account si.additional_discount_percentage = 20 @@ -2016,7 +2016,6 @@ class TestSalesInvoice(unittest.TestCase): "charge_type": "Actual", "account_head": "_Test Account VAT - _TC", "cost_center": "Main - _TC", - "parent_cost_center": "Main - _TC", "description": "Test", "rate": 0, "tax_amount": 20 From 328444b530afea225103660a0c5fea9124e0f014 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 23 Jul 2021 20:28:02 +0530 Subject: [PATCH 151/369] fix(India): Default value for export type --- erpnext/patches.txt | 1 + .../v13_0/update_export_type_for_gst.py | 24 +++++++++++++++++++ erpnext/regional/india/setup.py | 2 -- 3 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v13_0/update_export_type_for_gst.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 2a836351177..b891719b02d 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -293,3 +293,4 @@ erpnext.patches.v13_0.update_job_card_details erpnext.patches.v13_0.update_level_in_bom #1234sswef erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry erpnext.patches.v13_0.update_subscription_status_in_memberships +erpnext.patches.v13_0.update_export_type_for_gst diff --git a/erpnext/patches/v13_0/update_export_type_for_gst.py b/erpnext/patches/v13_0/update_export_type_for_gst.py new file mode 100644 index 00000000000..478a2a6c806 --- /dev/null +++ b/erpnext/patches/v13_0/update_export_type_for_gst.py @@ -0,0 +1,24 @@ +import frappe + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + # Update custom fields + fieldname = frappe.db.get_value('Custom Field', {'dt': 'Customer', 'fieldname': 'export_type'}) + if fieldname: + frappe.db.set_value('Custom Field', fieldname, 'default', '') + + fieldname = frappe.db.get_value('Custom Field', {'dt': 'Supplier', 'fieldname': 'export_type'}) + if fieldname: + frappe.db.set_value('Custom Field', fieldname, 'default', '') + + # Update Customer/Supplier Masters + frappe.db.sql(""" + UPDATE `tabCustomer` set export_type = '' WHERE gst_category NOT IN ('SEZ', 'Overseas', 'Deemed Export') + """) + + frappe.db.sql(""" + UPDATE `tabSupplier` set export_type = '' WHERE gst_category NOT IN ('SEZ', 'Overseas') + """) \ No newline at end of file diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 92654608da5..e9372f9b8fc 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -641,7 +641,6 @@ def make_custom_fields(update=True): 'label': 'Export Type', 'fieldtype': 'Select', 'insert_after': 'gst_category', - 'default': 'Without Payment of Tax', 'depends_on':'eval:in_list(["SEZ", "Overseas"], doc.gst_category)', 'options': '\nWith Payment of Tax\nWithout Payment of Tax' } @@ -660,7 +659,6 @@ def make_custom_fields(update=True): 'label': 'Export Type', 'fieldtype': 'Select', 'insert_after': 'gst_category', - 'default': 'Without Payment of Tax', 'depends_on':'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)', 'options': '\nWith Payment of Tax\nWithout Payment of Tax' } From 57cb3ac023544c618b2c7c9fa5927a81cd31a848 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 20:23:20 +0530 Subject: [PATCH 152/369] feat: add html2canvas for easily exporting html to images using canvas --- .eslintrc | 1 + package.json | 3 ++- yarn.lock | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index ecfaab23eed..46fb354c11c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -154,6 +154,7 @@ "before": true, "beforeEach": true, "onScan": true, + "html2canvas": true, "extend_cscript": true, "localforage": true } diff --git a/package.json b/package.json index c9ee7a622c4..5bc1e56a210 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "snyk": "^1.518.0" }, "dependencies": { - "onscan.js": "^1.5.2" + "onscan.js": "^1.5.2", + "html2canvas": "^1.1.4" }, "scripts": { "snyk-protect": "snyk protect", diff --git a/yarn.lock b/yarn.lock index 0a2ac1affc8..cc01d89344a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -688,6 +688,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-arraybuffer@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45" + integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -997,6 +1002,13 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== +css-line-break@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-1.1.1.tgz#d5e9bdd297840099eb0503c7310fd34927a026ef" + integrity sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA== + dependencies: + base64-arraybuffer "^0.2.0" + debug@^3.1.0, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -1472,6 +1484,13 @@ hosted-git-info@^3.0.4, hosted-git-info@^3.0.7: dependencies: lru-cache "^6.0.0" +html2canvas@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.1.4.tgz#53ae91cd26e9e9e623c56533cccb2e3f57c8124c" + integrity sha512-uHgQDwrXsRmFdnlOVFvHin9R7mdjjZvoBoXxicPR+NnucngkaLa5zIDW9fzMkiip0jSffyTyWedE8iVogYOeWg== + dependencies: + css-line-break "1.1.1" + http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" From 37198159aaaecb86b5bbcb4528b935922eb11f3c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 20:28:01 +0530 Subject: [PATCH 153/369] feat: Export chart option in desktop view --- .../hierarchy_chart_desktop.js | 97 +++++++++++++------ erpnext/public/scss/hierarchy_chart.scss | 10 +- erpnext/utilities/hierarchy_chart.py | 4 +- 3 files changed, 82 insertions(+), 29 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 694c26567aa..57d34d82252 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -1,3 +1,4 @@ +import html2canvas from 'html2canvas'; erpnext.HierarchyChart = class { /* Options: - doctype @@ -11,16 +12,20 @@ erpnext.HierarchyChart = class { this.method = method; this.doctype = doctype; + this.setup_page_style(); + this.page.main.addClass('frappe-card'); + + this.nodes = {}; + this.setup_node_class(); + } + + setup_page_style() { this.page.main.css({ 'min-height': '300px', 'max-height': '600px', 'overflow': 'auto', 'position': 'relative' }); - this.page.main.addClass('frappe-card'); - - this.nodes = {}; - this.setup_node_class(); } setup_node_class() { @@ -84,7 +89,7 @@ erpnext.HierarchyChart = class { // svg for connectors me.make_svg_markers(); - me.setup_hierarchy() + me.setup_hierarchy(); me.render_root_nodes(); me.all_nodes_expanded = false; } @@ -97,6 +102,10 @@ erpnext.HierarchyChart = class { setup_actions() { let me = this; + this.page.add_inner_button(__('Export'), function() { + me.export_chart(); + }); + this.page.add_inner_button(__('Expand All'), function() { me.load_children(me.root_node, true); me.all_nodes_expanded = true; @@ -113,6 +122,36 @@ erpnext.HierarchyChart = class { }); } + export_chart() { + this.page.main.css({ + 'min-height': '', + 'max-height': '', + 'overflow': 'visible', + 'position': 'fixed', + 'left': '0', + 'top': '0' + }); + + $('.node-card').addClass('exported'); + + html2canvas(document.querySelector('#hierarchy-chart-wrapper'), { + scrollY: -window.scrollY, + scrollX: 0 + }).then(function(canvas) { + // Export the canvas to its data URI representation + let dataURL = canvas.toDataURL('image/png'); + + // download the image + let a = document.createElement('a'); + a.href = dataURL; + a.download = 'hierarchy_chart'; + a.click(); + }); + + this.setup_page_style(); + $('.node-card').removeClass('exported'); + } + setup_hierarchy() { if (this.$hierarchy) this.$hierarchy.remove(); @@ -127,33 +166,37 @@ erpnext.HierarchyChart = class {
                            `); - this.page.main.append(this.$hierarchy); + this.page.main + .find('#hierarchy-chart-wrapper') + .append(this.$hierarchy); this.nodes = {}; } make_svg_markers() { $('#arrows').remove(); - this.page.main.prepend(` - - - - - - - - + this.page.main.append(` +
                            + + + + + + + + - - - - - - - - - - `); + + + + + + + + + + +
                            `); } render_root_nodes(expanded_view=false) { @@ -310,7 +353,7 @@ erpnext.HierarchyChart = class { let entry = undefined; let node = undefined; - while(data_list.length) { + while (data_list.length) { // to avoid overlapping connectors entry = data_list.shift(); node = this.nodes[entry.parent]; @@ -323,7 +366,7 @@ erpnext.HierarchyChart = class { } render_child_nodes_for_expanded_view(node, child_nodes) { - node.$children = $('
                              ') + node.$children = $('
                                '); const last_level = this.$hierarchy.find('.level:last').index(); const node_level = $(`#${node.id}`).parent().parent().parent().index(); diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 1c2f9421faa..44288fe1552 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -21,6 +21,10 @@ } } +.node-card.exported { + box-shadow: none +} + .node-image { width: 3.0rem; height: 3.0rem; @@ -178,9 +182,12 @@ } // horizontal hierarchy tree view +#hierarchy-chart-wrapper { + padding-top: 30px; +} + .hierarchy { display: flex; - padding-top: 30px; } .hierarchy li { @@ -200,6 +207,7 @@ #arrows { position: absolute; overflow: visible; + margin-top: -80px; } .active-connector { diff --git a/erpnext/utilities/hierarchy_chart.py b/erpnext/utilities/hierarchy_chart.py index 9b0279351f2..22d3f28faa7 100644 --- a/erpnext/utilities/hierarchy_chart.py +++ b/erpnext/utilities/hierarchy_chart.py @@ -3,14 +3,16 @@ from __future__ import unicode_literals import frappe +import os from frappe import _ +from frappe.utils.pdf import get_pdf @frappe.whitelist() def get_all_nodes(parent, parent_name, method, company): '''Recursively gets all data from nodes''' method = frappe.get_attr(method) - if not method in frappe.whitelisted: + if method not in frappe.whitelisted: frappe.throw(_('Not Permitted'), frappe.PermissionError) data = method(parent, company) From 475d856d6681bebd2586754b0c081735952e841e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 20:39:51 +0530 Subject: [PATCH 154/369] fix(style): longer titles overflowing --- erpnext/public/scss/hierarchy_chart.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 44288fe1552..7f1077dbbd2 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -40,6 +40,10 @@ line-height: 1.35; } +.node-info { + width: 12.7rem; +} + .node-connections { font-size: 0.75rem; line-height: 1.35; From 6bca87ddb9c39cd18abb30bc1b9f4cb5f07b451c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 21:34:51 +0530 Subject: [PATCH 155/369] fix: remove unnecessary imports --- erpnext/utilities/hierarchy_chart.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/utilities/hierarchy_chart.py b/erpnext/utilities/hierarchy_chart.py index 22d3f28faa7..fb58a5d5867 100644 --- a/erpnext/utilities/hierarchy_chart.py +++ b/erpnext/utilities/hierarchy_chart.py @@ -3,9 +3,7 @@ from __future__ import unicode_literals import frappe -import os from frappe import _ -from frappe.utils.pdf import get_pdf @frappe.whitelist() def get_all_nodes(parent, parent_name, method, company): From c676eaae57d353ff9ecd34fe28158e8216e94c9f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 23:11:18 +0530 Subject: [PATCH 156/369] fix: test --- erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 57d34d82252..89fb8d57925 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -173,7 +173,7 @@ erpnext.HierarchyChart = class { } make_svg_markers() { - $('#arrows').remove(); + $('#hierarchy-chart-wrapper').remove(); this.page.main.append(`
                                From 4209b3bda9dce91efa816d7dd43a655210ef02a5 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 26 Jul 2021 13:00:32 +0530 Subject: [PATCH 157/369] fix: included company in Link Document Type filters for contact (#26576) (#26634) (cherry picked from commit cbddedab7bf2fc7637b861214c3373a742da830b) Co-authored-by: Noah Jacob --- erpnext/hooks.py | 3 ++- erpnext/public/js/contact.js | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 erpnext/public/js/contact.js diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 52daec91805..1ba752a1464 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -24,7 +24,8 @@ doctype_js = { "Address": "public/js/address.js", "Communication": "public/js/communication.js", "Event": "public/js/event.js", - "Newsletter": "public/js/newsletter.js" + "Newsletter": "public/js/newsletter.js", + "Contact": "public/js/contact.js" } override_doctype_class = { diff --git a/erpnext/public/js/contact.js b/erpnext/public/js/contact.js new file mode 100644 index 00000000000..41a0e8a9f99 --- /dev/null +++ b/erpnext/public/js/contact.js @@ -0,0 +1,16 @@ + + +frappe.ui.form.on("Contact", { + refresh(frm) { + frm.set_query('link_doctype', "links", function() { + return { + query: "frappe.contacts.address_and_contact.filter_dynamic_link_doctypes", + filters: { + fieldtype: ["in", ["HTML", "Text Editor"]], + fieldname: ["in", ["contact_html", "company_description"]], + } + }; + }); + frm.refresh_field("links"); + } +}); From 06fb0b93b5905ab0ba7acd6939a817ecebe717e9 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 26 Jul 2021 16:46:47 +0530 Subject: [PATCH 158/369] fix: Supplier invoice importer fix v13 (#26633) * fix: Supplier Invoice Importer fix Co-authored-by: Subin Tom --- erpnext/controllers/accounts_controller.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 4c313c43a72..cdd865ac4ac 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1112,8 +1112,11 @@ class AccountsController(TransactionBase): for d in self.get("payment_schedule"): if d.invoice_portion: d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount')) - d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount')) + d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount')) d.outstanding = d.payment_amount + elif not d.invoice_portion: + d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount')) + def set_due_date(self): due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date] From d066eab6cd15ded671cbb5c34d8f4a8847a7ba64 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 26 Jul 2021 18:38:50 +0530 Subject: [PATCH 159/369] fix: Test case for GSTR-3b report --- .../regional/doctype/gstr_3b_report/gstr_3b_report.py | 10 ++++++++-- erpnext/regional/report/gstr_1/gstr_1.py | 3 ++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 641520437fb..6fd135d5606 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -281,9 +281,15 @@ class GSTR3BReport(Document): if self.get('invoice_items'): # Build itemised tax for export invoices, nil and exempted where tax table is blank for invoice, items in iteritems(self.invoice_items): - if invoice not in self.items_based_on_tax_rate and (self.invoice_detail_map.get(invoice, {}).get('export_type') - == "Without Payment of Tax"): + if invoice not in self.items_based_on_tax_rate and self.invoice_detail_map.get(invoice, {}).get('export_type') \ + == "Without Payment of Tax" and self.invoice_detail_map.get(invoice, {}).get('gst_category') == "Overseas": self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys()) + else: + for item in items.keys(): + if item in self.is_nil_exempt + self.is_non_gst and \ + item not in self.items_based_on_tax_rate.get(invoice, {}).get(0, []): + self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, []) + self.items_based_on_tax_rate[invoice][0].append(item) def set_outward_taxable_supplies(self): inter_state_supply_details = {} diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index cfcb8c3444f..f9de2d527ba 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -287,7 +287,8 @@ class Gstr1Report(object): # Build itemised tax for export invoices where tax table is blank for invoice, items in iteritems(self.invoice_items): if invoice not in self.items_based_on_tax_rate and invoice not in unidentified_gst_accounts_invoice \ - and frappe.db.get_value(self.doctype, invoice, "export_type") == "Without Payment of Tax": + and self.invoices.get(invoice, {}).get('export_type') == "Without Payment of Tax" \ + and self.invoices.get(invoice, {}).get('gst_category') == "Overseas": self.items_based_on_tax_rate.setdefault(invoice, {}).setdefault(0, items.keys()) def get_columns(self): From 8d52a2270999f8fa37139aa02ec9bf839f95a343 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 26 Jul 2021 19:00:57 +0530 Subject: [PATCH 160/369] fix: Additional discount calculations in Invoices (#26553) * fix: Additional discount calculations in Invoices * revert: Client side handling for Dynamic GST Rates * fix: Add update item tax template method back * fix: Revert refresh field * fix: add company change trigger --- .../public/js/controllers/taxes_and_totals.js | 69 +++---------------- erpnext/public/js/controllers/transaction.js | 44 +++++++++++- 2 files changed, 53 insertions(+), 60 deletions(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 52efbb5f6cd..53d5278bbf4 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -65,28 +65,23 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ this.frm.refresh_fields(); }, - calculate_discount_amount: function(){ + calculate_discount_amount: function() { if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount")) { - this.calculate_item_values(); - this.calculate_net_total(); this.set_discount_amount(); this.apply_discount_amount(); } }, _calculate_taxes_and_totals: function() { - frappe.run_serially([ - () => this.validate_conversion_rate(), - () => this.calculate_item_values(), - () => this.update_item_tax_map(), - () => this.initialize_taxes(), - () => this.determine_exclusive_rate(), - () => this.calculate_net_total(), - () => this.calculate_taxes(), - () => this.manipulate_grand_total_for_inclusive_tax(), - () => this.calculate_totals(), - () => this._cleanup() - ]); + this.validate_conversion_rate(); + this.calculate_item_values(); + this.initialize_taxes(); + this.determine_exclusive_rate(); + this.calculate_net_total(); + this.calculate_taxes(); + this.manipulate_grand_total_for_inclusive_tax(); + this.calculate_totals(); + this._cleanup(); }, validate_conversion_rate: function() { @@ -107,7 +102,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }, calculate_item_values: function() { - var me = this; + let me = this; if (!this.discount_amount_applied) { $.each(this.frm.doc["items"] || [], function(i, item) { frappe.model.round_floats_in(item); @@ -268,46 +263,6 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ frappe.model.round_floats_in(this.frm.doc, ["total", "base_total", "net_total", "base_net_total"]); }, - update_item_tax_map: function() { - let me = this; - let item_codes = []; - let item_rates = {}; - let item_tax_templates = {}; - - $.each(this.frm.doc.items || [], function(i, item) { - if (item.item_code) { - // Use combination of name and item code in case same item is added multiple times - item_codes.push([item.item_code, item.name]); - item_rates[item.name] = item.net_rate; - item_tax_templates[item.name] = item.item_tax_template; - } - }); - - if (item_codes.length) { - return this.frm.call({ - method: "erpnext.stock.get_item_details.get_item_tax_info", - args: { - company: me.frm.doc.company, - tax_category: cstr(me.frm.doc.tax_category), - item_codes: item_codes, - item_rates: item_rates, - item_tax_templates: item_tax_templates - }, - callback: function(r) { - if (!r.exc) { - $.each(me.frm.doc.items || [], function(i, item) { - if (item.name && r.message.hasOwnProperty(item.name) && r.message[item.name].item_tax_template) { - item.item_tax_template = r.message[item.name].item_tax_template; - item.item_tax_rate = r.message[item.name].item_tax_rate; - me.add_taxes_from_item_tax_template(item.item_tax_rate); - } - }); - } - } - }); - } - }, - add_taxes_from_item_tax_template: function(item_tax_map) { let me = this; @@ -632,8 +587,6 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ tax.item_wise_tax_detail = JSON.stringify(tax.item_wise_tax_detail); }); } - - this.frm.refresh_fields(); }, set_discount_amount: function() { diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index b3af3d67eaa..5475383759f 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -826,9 +826,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ frappe.run_serially([ () => me.frm.script_manager.trigger("currency"), + () => me.update_item_tax_map(), () => me.apply_default_taxes(), - () => me.apply_pricing_rule(), - () => me.calculate_taxes_and_totals() + () => me.apply_pricing_rule() ]); } } @@ -1787,6 +1787,46 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ ]); }, + update_item_tax_map: function() { + let me = this; + let item_codes = []; + let item_rates = {}; + let item_tax_templates = {}; + + $.each(this.frm.doc.items || [], function(i, item) { + if (item.item_code) { + // Use combination of name and item code in case same item is added multiple times + item_codes.push([item.item_code, item.name]); + item_rates[item.name] = item.net_rate; + item_tax_templates[item.name] = item.item_tax_template; + } + }); + + if (item_codes.length) { + return this.frm.call({ + method: "erpnext.stock.get_item_details.get_item_tax_info", + args: { + company: me.frm.doc.company, + tax_category: cstr(me.frm.doc.tax_category), + item_codes: item_codes, + item_rates: item_rates, + item_tax_templates: item_tax_templates + }, + callback: function(r) { + if (!r.exc) { + $.each(me.frm.doc.items || [], function(i, item) { + if (item.name && r.message.hasOwnProperty(item.name) && r.message[item.name].item_tax_template) { + item.item_tax_template = r.message[item.name].item_tax_template; + item.item_tax_rate = r.message[item.name].item_tax_rate; + me.add_taxes_from_item_tax_template(item.item_tax_rate); + } + }); + } + } + }); + } + }, + item_tax_template: function(doc, cdt, cdn) { var me = this; if(me.frm.updating_party_details) return; From fb72df7dce3124d36b4e86553fc509928a3585b2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 25 Jul 2021 19:46:20 +0530 Subject: [PATCH 161/369] fix: Exchange rate revaluation posting date and precision fixes --- .../exchange_rate_revaluation.py | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index 56193216a22..e94875f2d72 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -99,10 +99,12 @@ class ExchangeRateRevaluation(Document): sum(debit) - sum(credit) as balance from `tabGL Entry` where account in (%s) + and posting_date <= %s + and is_cancelled = 0 group by account, party_type, party having sum(debit) != sum(credit) order by account - """ % ', '.join(['%s']*len(accounts)), tuple(accounts), as_dict=1) + """ % (', '.join(['%s']*len(accounts)), '%s'), tuple(accounts + [self.posting_date]), as_dict=1) return account_details @@ -143,9 +145,9 @@ class ExchangeRateRevaluation(Document): "party_type": d.get("party_type"), "party": d.get("party"), "account_currency": d.get("account_currency"), - "balance": d.get("balance_in_account_currency"), - dr_or_cr: abs(d.get("balance_in_account_currency")), - "exchange_rate":d.get("new_exchange_rate"), + "balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")), + dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")), + "exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")), "reference_type": "Exchange Rate Revaluation", "reference_name": self.name, }) @@ -154,9 +156,9 @@ class ExchangeRateRevaluation(Document): "party_type": d.get("party_type"), "party": d.get("party"), "account_currency": d.get("account_currency"), - "balance": d.get("balance_in_account_currency"), - reverse_dr_or_cr: abs(d.get("balance_in_account_currency")), - "exchange_rate": d.get("current_exchange_rate"), + "balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")), + reverse_dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")), + "exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")), "reference_type": "Exchange Rate Revaluation", "reference_name": self.name }) @@ -185,9 +187,9 @@ def get_account_details(account, company, posting_date, party_type=None, party=N account_details = {} company_currency = erpnext.get_company_currency(company) - balance = get_balance_on(account, party_type=party_type, party=party, in_account_currency=False) + balance = get_balance_on(account, date=posting_date, party_type=party_type, party=party, in_account_currency=False) if balance: - balance_in_account_currency = get_balance_on(account, party_type=party_type, party=party) + balance_in_account_currency = get_balance_on(account, date=posting_date, party_type=party_type, party=party) current_exchange_rate = balance / balance_in_account_currency if balance_in_account_currency else 0 new_exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date) new_balance_in_base_currency = balance_in_account_currency * new_exchange_rate From 5749e52bf6f4a67d4ee899b07edc2c2ac8434bf6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 25 Jul 2021 19:46:50 +0530 Subject: [PATCH 162/369] fix: Ignore GL Entry on cancel --- .../exchange_rate_revaluation/exchange_rate_revaluation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index e94875f2d72..c8d5737d753 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -27,6 +27,9 @@ class ExchangeRateRevaluation(Document): if not (self.company and self.posting_date): frappe.throw(_("Please select Company and Posting Date to getting entries")) + def on_cancel(self): + self.ignore_linked_doctypes = ('GL Entry') + @frappe.whitelist() def check_journal_entry_condition(self): total_debit = frappe.db.get_value("Journal Entry Account", { From dc2cd35b933ee038940578325efbec7c2f522906 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 25 Jul 2021 21:26:22 +0530 Subject: [PATCH 163/369] fix: Convert null values to empty string on grouping --- .../exchange_rate_revaluation/exchange_rate_revaluation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index c8d5737d753..f2b0a8c08a6 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -104,7 +104,7 @@ class ExchangeRateRevaluation(Document): where account in (%s) and posting_date <= %s and is_cancelled = 0 - group by account, party_type, party + group by account, NULLIF(party_type,''), NULLIF(party,'') having sum(debit) != sum(credit) order by account """ % (', '.join(['%s']*len(accounts)), '%s'), tuple(accounts + [self.posting_date]), as_dict=1) From cfd73ed554b21550592dd57abc561bd8eb72f3a9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 26 Jul 2021 19:42:19 +0530 Subject: [PATCH 164/369] ci: auto backport squashed commits based on labels (#26622) (#26640) (cherry picked from commit 057a0a98428b138b20646b820e7ab27c99bc5f22) Co-authored-by: Ankush --- .github/workflows/backport.yml | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 7c6b8432b87..cc98f4544f8 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -1,16 +1,25 @@ name: Backport on: - pull_request: + pull_request_target: types: - closed - labeled jobs: - backport: - runs-on: ubuntu-18.04 - name: Backport + main: + runs-on: ubuntu-latest steps: - - name: Backport - uses: tibdex/backport@v1 + - name: Checkout Actions + uses: actions/checkout@v2 with: - github_token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + repository: "ankush/backport" + path: ./actions + ref: develop + - name: Install Actions + run: npm install --production --prefix ./actions + - name: Run backport + uses: ./actions/backport + with: + token: ${{secrets.BACKPORT_BOT_TOKEN}} + labelsToAdd: "backport" + title: "{{originalTitle}}" From aaea5edbdb2d14ce3599e9e5d47048cf032e3f6c Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Tue, 27 Jul 2021 09:39:33 +0530 Subject: [PATCH 165/369] fix: Salary component account filter (#26605) * fix: salary component account filter * fix: cleanup --- .../doctype/salary_component/salary_component.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/payroll/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js index dbf75140ac1..e9e6f81862c 100644 --- a/erpnext/payroll/doctype/salary_component/salary_component.js +++ b/erpnext/payroll/doctype/salary_component/salary_component.js @@ -4,11 +4,18 @@ frappe.ui.form.on('Salary Component', { setup: function(frm) { frm.set_query("account", "accounts", function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + let d = frappe.get_doc(cdt, cdn); + + let root_type = "Liability"; + if (frm.doc.type == "Deduction") { + root_type = "Expense"; + } + return { filters: { "is_group": 0, - "company": d.company + "company": d.company, + "root_type": root_type } }; }); From 6c48a2ed7949e33401dc6efe8682540bc3ba9c1c Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Tue, 27 Jul 2021 09:41:55 +0530 Subject: [PATCH 166/369] fix: Removed set_status after cancel from Expense Claim and Employee Advance (#25901) * fix: removed set_status after cancel from hr doctypes * fix: semgrep on_submit issue * fix: sider * fix: spaces * fix: update flag for db_set --- .../employee_advance/employee_advance.py | 3 +-- .../hr/doctype/expense_claim/expense_claim.py | 23 +++++++++++-------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index ece627c50de..cbb3cc813b4 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -24,7 +24,6 @@ class EmployeeAdvance(Document): def on_cancel(self): self.ignore_linked_doctypes = ('GL Entry') - self.set_status() def set_status(self): if self.docstatus == 0: @@ -231,4 +230,4 @@ def get_voucher_type(mode_of_payment=None): if mode_of_payment_type == "Bank": voucher_type = "Bank Entry" - return voucher_type \ No newline at end of file + return voucher_type diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index 8f8dbb22245..95e2806aedc 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -36,8 +36,8 @@ class ExpenseClaim(AccountsController): if self.task and not self.project: self.project = frappe.db.get_value("Task", self.task, "project") - def set_status(self): - self.status = { + def set_status(self, update=False): + status = { "0": "Draft", "1": "Submitted", "2": "Cancelled" @@ -45,14 +45,18 @@ class ExpenseClaim(AccountsController): paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount) precision = self.precision("grand_total") - if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 - and flt(self.grand_total, precision) == flt(paid_amount, precision))) \ - and self.docstatus == 1 and self.approval_status == 'Approved': - self.status = "Paid" + if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 + and flt(self.grand_total, precision) == flt(paid_amount, precision))) and self.approval_status == 'Approved': + status = "Paid" elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved': - self.status = "Unpaid" + status = "Unpaid" elif self.docstatus == 1 and self.approval_status == 'Rejected': - self.status = 'Rejected' + status = 'Rejected' + + if update: + self.db_set("status", status) + else: + self.status = status def on_update(self): share_doc_with_approver(self, self.expense_approver) @@ -75,7 +79,7 @@ class ExpenseClaim(AccountsController): if self.is_paid: update_reimbursed_amount(self) - self.set_status() + self.set_status(update=True) self.update_claimed_amount_in_employee_advance() def on_cancel(self): @@ -87,7 +91,6 @@ class ExpenseClaim(AccountsController): if self.is_paid: update_reimbursed_amount(self) - self.set_status() self.update_claimed_amount_in_employee_advance() def update_claimed_amount_in_employee_advance(self): From 5448aa25e71dca107d4868c05297a2030a2d4089 Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Tue, 27 Jul 2021 10:18:53 +0530 Subject: [PATCH 167/369] chore: code owners updated (#26659) --- CODEOWNERS | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 219b6bb7821..a4a14de1b8e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -21,13 +21,13 @@ erpnext/quality_management/ @marination @rohitwaghchaure erpnext/shopping_cart/ @marination erpnext/stock/ @marination @rohitwaghchaure @ankush -erpnext/crm/ @ruchamahabal -erpnext/education/ @ruchamahabal -erpnext/healthcare/ @ruchamahabal -erpnext/hr/ @ruchamahabal +erpnext/crm/ @ruchamahabal @pateljannat +erpnext/education/ @ruchamahabal @pateljannat +erpnext/healthcare/ @ruchamahabal @pateljannat @chillaranand +erpnext/hr/ @ruchamahabal @pateljannat erpnext/non_profit/ @ruchamahabal -erpnext/payroll @ruchamahabal -erpnext/projects/ @ruchamahabal +erpnext/payroll @ruchamahabal @pateljannat +erpnext/projects/ @ruchamahabal @pateljannat erpnext/controllers @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination From 6e6823c3aa5555d9fa3f4a3061031c2e6ec59da4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 27 Jul 2021 10:59:25 +0530 Subject: [PATCH 168/369] feat: Enhancements in TDS --- .../tax_withholding_category.json | 350 ++++++------------ .../tax_withholding_category.py | 24 +- .../test_tax_withholding_category.py | 47 ++- 3 files changed, 184 insertions(+), 237 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json index f9160e281da..331770fbe84 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json @@ -1,263 +1,151 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-04-13 18:42:06.431683", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2018-04-13 18:42:06.431683", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "category_details_section", + "category_name", + "round_off_tax_amount", + "column_break_2", + "consider_party_ledger_amount", + "tax_on_excess_amount", + "section_break_8", + "rates", + "section_break_7", + "accounts" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "category_name", "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": "Category Name", - "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 - }, + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_8", "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, "label": "Tax Withholding Rates", - "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 + "show_days": 1, + "show_seconds": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "rates", "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": "Rates", - "length": 0, - "no_copy": 0, "options": "Tax Withholding Rate", - "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 + "show_days": 1, + "show_seconds": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "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, + "fieldname": "section_break_7", + "fieldtype": "Section Break", "label": "Account Details", - "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 - }, + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "accounts", - "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": "Accounts", - "length": 0, - "no_copy": 0, - "options": "Tax Withholding Account", - "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 + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Tax Withholding Account", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "category_details_section", + "fieldtype": "Section Break", + "label": "Category Details", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "description": "Even invoices with apply tax withholding unchecked will be considered for checking cumulative threshold breach", + "fieldname": "consider_party_ledger_amount", + "fieldtype": "Check", + "label": "Consider Entire Party Ledger Amount", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "description": "Tax will be withheld only for amount exceeding the cumulative threshold", + "fieldname": "tax_on_excess_amount", + "fieldtype": "Check", + "label": "Only Deduct Tax On Excess Amount ", + "show_days": 1, + "show_seconds": 1 + }, + { + "description": "Checking this will round off the tax amount to the nearest integer", + "fieldname": "round_off_tax_amount", + "fieldtype": "Data", + "label": "Round Off Tax Amount", + "show_days": 1, + "show_seconds": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-17 22:53:26.193179", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Tax Withholding Category", - "name_case": "", - "owner": "Administrator", + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-07-26 21:47:34.396071", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Tax Withholding Category", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "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 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index b9ee4a0963f..45c8e1b49fc 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import flt, getdate +from frappe.utils import flt, getdate, cint from erpnext.accounts.utils import get_fiscal_year class TaxWithholdingCategory(Document): @@ -86,7 +86,10 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company): "rate": tax_rate_detail.tax_withholding_rate, "threshold": tax_rate_detail.single_threshold, "cumulative_threshold": tax_rate_detail.cumulative_threshold, - "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category + "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category, + "consider_party_ledger_amount": tax_withholding.consider_party_ledger_amount, + "tax_on_excess_amount": tax_withholding.tax_on_excess_amount, + "round_off_tax_amount": tax_withholding.round_off_tax_amount }) def get_tax_withholding_rates(tax_withholding, fiscal_year): @@ -235,10 +238,15 @@ def get_deducted_tax(taxable_vouchers, fiscal_year, tax_details): def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers): tds_amount = 0 + invoice_filters = { + 'name': ('in', vouchers), + 'docstatus': 1 + } - supp_credit_amt = frappe.db.get_value('Purchase Invoice', { - 'name': ('in', vouchers), 'docstatus': 1, 'apply_tds': 1 - }, 'sum(net_total)') or 0.0 + if not cint(tax_details.consider_party_ledger_amount): + invoice_filters.update({'apply_tds': 1}) + + supp_credit_amt = frappe.db.get_value('Purchase Invoice', invoice_filters, 'sum(net_total)') or 0.0 supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', { 'parent': ('in', vouchers), 'docstatus': 1, @@ -255,6 +263,9 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu cumulative_threshold = tax_details.get('cumulative_threshold', 0) if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)): + if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(tax_details.tax_on_excess_amount): + supp_credit_amt -= cumulative_threshold + if ldc and is_valid_certificate( ldc.valid_from, ldc.valid_upto, inv.get('posting_date') or inv.get('transaction_date'), tax_deducted, @@ -263,6 +274,9 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details) else: tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0 + + if cint(tax_details.round_off_tax_amount): + tds_amount = round(tds_amount) return tds_amount diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index dd26be7c992..2ba22ca4353 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -87,6 +87,31 @@ class TestTaxWithholdingCategory(unittest.TestCase): for d in invoices: d.cancel() + def test_tax_withholding_category_checks(self): + invoices = [] + frappe.db.set_value("Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category") + + # First Invoice with no tds check + pi = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000, do_not_save=True) + pi.apply_tds = 0 + pi.save() + pi.submit() + invoices.append(pi) + + # Second Invoice will apply TDS checked + pi1 = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000) + pi1.submit() + invoices.append(pi1) + + # Cumulative threshold is 30000 + # Threshold calculation should be on both the invoices + # TDS should be applied only on 1000 + self.assertEqual(pi1.taxes[0].tax_amount, 1000) + + for d in invoices: + d.cancel() + + def test_cumulative_threshold_tcs(self): frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS") invoices = [] @@ -195,7 +220,7 @@ def create_sales_invoice(**args): def create_records(): # create a new suppliers - for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']: + for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3']: if frappe.db.exists('Supplier', name): continue @@ -311,3 +336,23 @@ def create_tax_with_holding_category(): 'account': 'TDS - _TC' }] }).insert() + + if not frappe.db.exists("Tax Withholding Category", "New TDS Category"): + frappe.get_doc({ + "doctype": "Tax Withholding Category", + "name": "New TDS Category", + "category_name": "New TDS Category", + "round_off_tax_amount": 1, + "consider_party_ledger_amount": 1, + "tax_on_excess_amount": 1, + "rates": [{ + 'fiscal_year': fiscal_year, + 'tax_withholding_rate': 10, + 'single_threshold': 0, + 'cumulative_threshold': 30000 + }], + "accounts": [{ + 'company': '_Test Company', + 'account': 'TDS - _TC' + }] + }).insert() From b122a1eaa094c6e320dddd6c5b5f6716932f8028 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 27 Jul 2021 16:59:06 +0530 Subject: [PATCH 169/369] fix: reload manufacturing setting before patch (#26641) (#26670) (cherry picked from commit c8d7a8c781f6c448fd872427d611ffab70c136db) Co-authored-by: Ankush --- erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py index 48999e6f993..d7ad1fc6962 100644 --- a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py +++ b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py @@ -10,6 +10,7 @@ def execute(): if not frappe.db.has_column('Work Order', 'has_batch_no'): return + frappe.reload_doc('manufacturing', 'doctype', 'manufacturing_settings') if cint(frappe.db.get_single_value('Manufacturing Settings', 'make_serial_no_batch_from_work_order')): return @@ -107,4 +108,4 @@ def repost_future_sle_and_gle(doc): "company": doc.company }) - create_repost_item_valuation_entry(args) \ No newline at end of file + create_repost_item_valuation_entry(args) From af58ac9e104b0a278835880bcdebc47a4eaa38c7 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 27 Jul 2021 18:43:20 +0530 Subject: [PATCH 170/369] fix: not able to add employee in the job card --- erpnext/manufacturing/doctype/job_card/job_card.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 420bb008039..69c7f5c614b 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -192,11 +192,11 @@ class JobCard(Document): "completed_qty": args.get("completed_qty") or 0.0 }) elif args.get("start_time"): - new_args = { + new_args = frappe._dict({ "from_time": get_datetime(args.get("start_time")), "operation": args.get("sub_operation"), "completed_qty": 0.0 - } + }) if employees: for name in employees: From 56b81565fa3f2b1503c98678846372275635ce90 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 23 Jul 2021 15:19:53 +0530 Subject: [PATCH 171/369] fix: added progress bar in repost item valuation --- .../repost_item_valuation.js | 37 ++++++++++ .../repost_item_valuation.json | 34 ++++++++- .../repost_item_valuation.py | 2 +- erpnext/stock/stock_ledger.py | 71 ++++++++++++++----- 4 files changed, 124 insertions(+), 20 deletions(-) diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js index b3e4286bccb..4cd40bf38ec 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js @@ -29,13 +29,50 @@ frappe.ui.form.on('Repost Item Valuation', { }; }); } + + frm.trigger('setup_realtime_progress'); }, + + setup_realtime_progress: function(frm) { + frappe.realtime.on('item_reposting_progress', data => { + if (frm.doc.name !== data.name) { + return; + } + + if (frm.doc.status == 'In Progress') { + frm.doc.current_index = data.current_index; + frm.doc.items_to_be_repost = data.items_to_be_repost; + + frm.dashboard.reset(); + frm.trigger('show_reposting_progress'); + } + }); + }, + refresh: function(frm) { if (frm.doc.status == "Failed" && frm.doc.docstatus==1) { frm.add_custom_button(__('Restart'), function () { frm.trigger("restart_reposting"); }).addClass("btn-primary"); } + + frm.trigger('show_reposting_progress'); + }, + + show_reposting_progress: function(frm) { + var bars = []; + + let total_count = frm.doc.items_to_be_repost ? JSON.parse(frm.doc.items_to_be_repost).length : 0; + let progress = flt(cint(frm.doc.current_index) / total_count * 100, 2) || 0.5; + var title = __('Reposting Completed {0}%', [progress]); + + bars.push({ + 'title': title, + 'width': progress + '%', + 'progress_class': 'progress-bar-success' + }); + + frm.dashboard.add_progress(__('Reposting Progress'), bars); }, restart_reposting: function(frm) { diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json index 071fc86d9b3..a800bf87013 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json @@ -21,7 +21,10 @@ "allow_zero_rate", "amended_from", "error_section", - "error_log" + "error_log", + "items_to_be_repost", + "distinct_item_and_warehouse", + "current_index" ], "fields": [ { @@ -142,12 +145,39 @@ "fieldname": "allow_zero_rate", "fieldtype": "Check", "label": "Allow Zero Rate" + }, + { + "fieldname": "items_to_be_repost", + "fieldtype": "Code", + "hidden": 1, + "label": "Items to Be Repost", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "distinct_item_and_warehouse", + "fieldtype": "Code", + "hidden": 1, + "label": "Distinct Item and Warehouse", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "current_index", + "fieldtype": "Int", + "hidden": 1, + "label": "Current Index", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-12-10 07:52:12.476589", + "modified": "2021-07-22 18:59:43.057878", "modified_by": "Administrator", "module": "Stock", "name": "Repost Item Valuation", diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 5f31d9caf0d..2e454a51596 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -80,7 +80,7 @@ def repost(doc): def repost_sl_entries(doc): if doc.based_on == 'Transaction': - repost_future_sle(voucher_type=doc.voucher_type, voucher_no=doc.voucher_no, + repost_future_sle(doc=doc, voucher_type=doc.voucher_type, voucher_no=doc.voucher_no, allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher) else: repost_future_sle(args=[frappe._dict({ diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index c15d1eda7dc..8f9ec465e5d 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -127,30 +127,24 @@ def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False): sle.submit() return sle -def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=None, via_landed_cost_voucher=False): +def repost_future_sle(args=None, doc=None, voucher_type=None, voucher_no=None, allow_negative_stock=None, via_landed_cost_voucher=False): if not args and voucher_type and voucher_no: - args = get_args_for_voucher(voucher_type, voucher_no) + args = get_items_to_be_repost(voucher_type, voucher_no, doc) - distinct_item_warehouses = {} - for i, d in enumerate(args): - distinct_item_warehouses.setdefault((d.item_code, d.warehouse), frappe._dict({ - "reposting_status": False, - "sle": d, - "args_idx": i - })) + distinct_item_warehouses = get_distinct_item_warehouse(args, doc) - i = 0 + i = get_current_index(doc) or 0 while i < len(args): obj = update_entries_after({ - "item_code": args[i].item_code, - "warehouse": args[i].warehouse, - "posting_date": args[i].posting_date, - "posting_time": args[i].posting_time, + "item_code": args[i].get('item_code'), + "warehouse": args[i].get('warehouse'), + "posting_date": args[i].get('posting_date'), + "posting_time": args[i].get('posting_time'), "creation": args[i].get("creation"), "distinct_item_warehouses": distinct_item_warehouses }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher) - distinct_item_warehouses[(args[i].item_code, args[i].warehouse)].reposting_status = True + distinct_item_warehouses[(args[i].get('item_code'), args[i].get('warehouse'))].reposting_status = True if obj.new_items_found: for item_wh, data in iteritems(distinct_item_warehouses): @@ -159,11 +153,35 @@ def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negat args.append(data.sle) elif data.sle_changed and not data.reposting_status: args[data.args_idx] = data.sle - + data.sle_changed = False i += 1 -def get_args_for_voucher(voucher_type, voucher_no): + if doc and i % 2 == 0: + update_args_in_repost_item_valuation(doc, i, args, distinct_item_warehouses) + + if doc and args: + update_args_in_repost_item_valuation(doc, i, args, distinct_item_warehouses) + +def update_args_in_repost_item_valuation(doc, index, args, distinct_item_warehouses): + frappe.db.set_value(doc.doctype, doc.name, { + 'items_to_be_repost': json.dumps(args, default=str), + 'distinct_item_and_warehouse': json.dumps({str(k): v for k,v in distinct_item_warehouses.items()}, default=str), + 'current_index': index + }) + + frappe.db.commit() + + frappe.publish_realtime('item_reposting_progress', { + 'name': doc.name, + 'items_to_be_repost': json.dumps(args, default=str), + 'current_index': index + }) + +def get_items_to_be_repost(voucher_type, voucher_no, doc=None): + if doc and doc.items_to_be_repost: + return json.loads(doc.items_to_be_repost) or [] + return frappe.db.get_all("Stock Ledger Entry", filters={"voucher_type": voucher_type, "voucher_no": voucher_no}, fields=["item_code", "warehouse", "posting_date", "posting_time", "creation"], @@ -171,6 +189,25 @@ def get_args_for_voucher(voucher_type, voucher_no): group_by="item_code, warehouse" ) +def get_distinct_item_warehouse(args=None, doc=None): + distinct_item_warehouses = {} + if doc and doc.distinct_item_and_warehouse: + distinct_item_warehouses = json.loads(doc.distinct_item_and_warehouse) + distinct_item_warehouses = {frappe.safe_eval(k): frappe._dict(v) for k, v in distinct_item_warehouses.items()} + else: + for i, d in enumerate(args): + distinct_item_warehouses.setdefault((d.item_code, d.warehouse), frappe._dict({ + "reposting_status": False, + "sle": d, + "args_idx": i + })) + + return distinct_item_warehouses + +def get_current_index(doc=None): + if doc and doc.current_index: + return doc.current_index + class update_entries_after(object): """ update valution rate and qty after transaction From ac2e139d5bdfe58fb03ee73ef88cf70d855b8caf Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Tue, 27 Jul 2021 20:44:59 +0200 Subject: [PATCH 172/369] fix: force reload of Opportunity in patch (#26668) --- erpnext/patches/v13_0/rename_issue_doctype_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/rename_issue_doctype_fields.py b/erpnext/patches/v13_0/rename_issue_doctype_fields.py index fa1dfed6435..41c51c36dcb 100644 --- a/erpnext/patches/v13_0/rename_issue_doctype_fields.py +++ b/erpnext/patches/v13_0/rename_issue_doctype_fields.py @@ -37,7 +37,7 @@ def execute(): if frappe.db.exists('DocType', 'Opportunity'): opportunities = frappe.db.get_all('Opportunity', fields=['name', 'mins_to_first_response'], order_by='creation desc') - frappe.reload_doc('crm', 'doctype', 'opportunity') + frappe.reload_doctype('Opportunity', force=True) rename_field('Opportunity', 'mins_to_first_response', 'first_response_time') # change fieldtype to duration From 64af124f88f2133790cd4d7f160f1fac9afbc409 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Jul 2021 10:43:02 +0530 Subject: [PATCH 173/369] fix(minor): Consider grand total for threshold check --- .../tax_withholding_category/tax_withholding_category.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 45c8e1b49fc..020de3c3f34 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -243,10 +243,13 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu 'docstatus': 1 } + field = 'sum(net_total)' + if not cint(tax_details.consider_party_ledger_amount): invoice_filters.update({'apply_tds': 1}) + field = 'sum(grand_total)' - supp_credit_amt = frappe.db.get_value('Purchase Invoice', invoice_filters, 'sum(net_total)') or 0.0 + supp_credit_amt = frappe.db.get_value('Purchase Invoice', invoice_filters, field) or 0.0 supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', { 'parent': ('in', vouchers), 'docstatus': 1, From 1c9e516092e415ae27441b26e0a632427cd81f31 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Jul 2021 11:38:44 +0530 Subject: [PATCH 174/369] fix: GL Entries for discount amount with item qty greater than 1 --- erpnext/controllers/accounts_controller.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3d048c36865..8199b1040f0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -845,6 +845,7 @@ class AccountsController(TransactionBase): for item in self.get("items"): if item.get('discount_amount') and item.get('discount_account'): + discount_amount = item.discount_amount * item.qty if self.doctype == "Purchase Invoice": income_or_expense_account = (item.expense_account if (not item.enable_deferred_expense or self.is_return) @@ -859,8 +860,9 @@ class AccountsController(TransactionBase): self.get_gl_dict({ "account": item.discount_account, "against": supplier_or_customer, - dr_or_cr: flt(item.discount_amount), - dr_or_cr + "_in_account_currency": flt(item.discount_amount), + dr_or_cr: flt(discount_amount, item.precision('discount_amount')), + dr_or_cr + "_in_account_currency": flt(discount_amount * self.get('conversion_rate'), + item.precision('discount_amount')), "cost_center": item.cost_center, "project": item.project }, account_currency, item=item) @@ -871,8 +873,9 @@ class AccountsController(TransactionBase): self.get_gl_dict({ "account": income_or_expense_account, "against": supplier_or_customer, - rev_dr_cr: flt(item.discount_amount), - rev_dr_cr + "_in_account_currency": flt(item.discount_amount), + rev_dr_cr: flt(discount_amount, item.precision('discount_amount')), + rev_dr_cr + "_in_account_currency": flt(discount_amount * self.get('conversion_rate'), + item.precision('discount_amount')), "cost_center": item.cost_center, "project": item.project or self.project }, account_currency, item=item) From 93502499412e6a268f5d570b33b131af74b328f1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Jul 2021 12:57:59 +0530 Subject: [PATCH 175/369] fix: Chnage fieldtype from data to check --- .../tax_withholding_category.json | 4 ++-- erpnext/patches.txt | 1 + erpnext/patches/v13_0/update_tds_check_field.py | 8 ++++++++ 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v13_0/update_tds_check_field.py diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json index 331770fbe84..153906ffe97 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json @@ -94,7 +94,7 @@ { "description": "Checking this will round off the tax amount to the nearest integer", "fieldname": "round_off_tax_amount", - "fieldtype": "Data", + "fieldtype": "Check", "label": "Round Off Tax Amount", "show_days": 1, "show_seconds": 1 @@ -102,7 +102,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2021-07-26 21:47:34.396071", + "modified": "2021-07-27 21:47:34.396071", "modified_by": "Administrator", "module": "Accounts", "name": "Tax Withholding Category", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b891719b02d..32763754d22 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -294,3 +294,4 @@ erpnext.patches.v13_0.update_level_in_bom #1234sswef erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry erpnext.patches.v13_0.update_subscription_status_in_memberships erpnext.patches.v13_0.update_export_type_for_gst +erpnext.patches.v13_0.update_tds_check_field #3 diff --git a/erpnext/patches/v13_0/update_tds_check_field.py b/erpnext/patches/v13_0/update_tds_check_field.py new file mode 100644 index 00000000000..16bf76d530c --- /dev/null +++ b/erpnext/patches/v13_0/update_tds_check_field.py @@ -0,0 +1,8 @@ +import frappe + +def execute(): + if frappe.db.has_column("Tax Withholding Category", "round_off_tax_amount"): + frappe.db.sql(""" + UPDATE `tabTax Withholding Category` set round_off_tax_amount = 0 + WHERE round_off_tax_amount IS NULL + """) \ No newline at end of file From 90c5cb0a3106c173c0607b9193d31d7cd4749e9f Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 28 Jul 2021 13:42:33 +0530 Subject: [PATCH 176/369] fix: documentation link for E Invoicing (#26685) --- .../regional/doctype/e_invoice_settings/e_invoice_settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js index cc2d9f06d2d..54e488610df 100644 --- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js +++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js @@ -3,7 +3,7 @@ frappe.ui.form.on('E Invoice Settings', { refresh(frm) { - const docs_link = 'https://docs.erpnext.com/docs/user/manual/en/regional/india/setup-e-invoicing'; + const docs_link = 'https://docs.erpnext.com/docs/v13/user/manual/en/regional/india/setup-e-invoicing'; frm.dashboard.set_headline( __("Read {0} for more information on E Invoicing features.", [`documentation`]) ); From fa8e6ac7cd604fffaeecb4a4e73f4b08a20ad2b2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Jul 2021 15:30:05 +0530 Subject: [PATCH 177/369] fix: Patch --- erpnext/patches/v13_0/update_tds_check_field.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_tds_check_field.py b/erpnext/patches/v13_0/update_tds_check_field.py index 16bf76d530c..3d149586a04 100644 --- a/erpnext/patches/v13_0/update_tds_check_field.py +++ b/erpnext/patches/v13_0/update_tds_check_field.py @@ -1,7 +1,8 @@ import frappe def execute(): - if frappe.db.has_column("Tax Withholding Category", "round_off_tax_amount"): + if frappe.db.has_table("Tax Withholding Category") \ + and frappe.db.has_column("Tax Withholding Category", "round_off_tax_amount"): frappe.db.sql(""" UPDATE `tabTax Withholding Category` set round_off_tax_amount = 0 WHERE round_off_tax_amount IS NULL From 1b6dd84c0a25e143ec3ac92b3f439d6d90ca6573 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 28 Jul 2021 18:11:11 +0530 Subject: [PATCH 178/369] fix(bom): remove manual permission checking (#26689) (#26690) get_list does the permission checking. (cherry picked from commit d95f16ac8fb084e33ab936545fc60acd6a4ff618) Co-authored-by: Ankush --- erpnext/manufacturing/doctype/bom/bom.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index af081c449c6..ebd9ae2dc54 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -1069,13 +1069,6 @@ def item_query(doctype, txt, searchfield, start, page_len, filters): if barcodes: or_cond_filters["name"] = ("in", barcodes) - for cond in get_match_cond(doctype, as_condition=False): - for key, value in cond.items(): - if key == doctype: - key = "name" - - query_filters[key] = ("in", value) - if filters and filters.get("item_code"): has_variants = frappe.get_cached_value("Item", filters.get("item_code"), "has_variants") if not has_variants: @@ -1084,7 +1077,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters): if filters and filters.get("is_stock_item"): query_filters["is_stock_item"] = 1 - return frappe.get_all("Item", + return frappe.get_list("Item", fields = fields, filters=query_filters, or_filters = or_cond_filters, order_by=order_by, limit_start=start, limit_page_length=page_len, as_list=1) From b1350af1f64868244ec7dc9838fb81fa4bbec1b4 Mon Sep 17 00:00:00 2001 From: Anupam Date: Wed, 28 Jul 2021 18:51:19 +0530 Subject: [PATCH 179/369] fix: setup wizard --- .../setup/setup_wizard/operations/company_setup.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/setup_wizard/operations/company_setup.py b/erpnext/setup/setup_wizard/operations/company_setup.py index 4edf9485dc1..4833d93c4a7 100644 --- a/erpnext/setup/setup_wizard/operations/company_setup.py +++ b/erpnext/setup/setup_wizard/operations/company_setup.py @@ -45,9 +45,16 @@ def enable_shopping_cart(args): def create_email_digest(): from frappe.utils.user import get_system_managers system_managers = get_system_managers(only_name=True) + if not system_managers: return + recipients = [] + for d in system_managers: + recipients.append({ + 'recipient': d + }) + companies = frappe.db.sql_list("select name FROM `tabCompany`") for company in companies: if not frappe.db.exists("Email Digest", "Default Weekly Digest - " + company): @@ -56,7 +63,7 @@ def create_email_digest(): "name": "Default Weekly Digest - " + company, "company": company, "frequency": "Weekly", - "recipient_list": "\n".join(system_managers) + "recipients": recipients }) for df in edigest.meta.get("fields", {"fieldtype": "Check"}): @@ -72,7 +79,7 @@ def create_email_digest(): "name": "Scheduler Errors", "company": companies[0], "frequency": "Daily", - "recipient_list": "\n".join(system_managers), + "recipients": recipients, "scheduler_errors": 1, "enabled": 1 }) From e5fea372afb1c1295811ff18a570c9acf1506c50 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 29 Jul 2021 11:05:38 +0530 Subject: [PATCH 180/369] fix: frappe linter --- erpnext/setup/doctype/email_digest/email_digest.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/email_digest/email_digest.js b/erpnext/setup/doctype/email_digest/email_digest.js index 84e2d0d52a2..2e415af2828 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.js +++ b/erpnext/setup/doctype/email_digest/email_digest.js @@ -11,8 +11,8 @@ frappe.ui.form.on("Email Digest", { name: frm.doc.name }, callback: function(r) { - var d = new frappe.ui.Dialog({ - title: __('Email Digest: ') + frm.doc.name, + let d = new frappe.ui.Dialog({ + title: __('Email Digest: {0}', [frm.doc.name]), width: 800 }); $(d.body).html(r.message); From 8859574aab0553406964859250e0e4bbe470ee8a Mon Sep 17 00:00:00 2001 From: Ankush Date: Thu, 29 Jul 2021 11:09:22 +0530 Subject: [PATCH 181/369] feat: don't recompute taxes (#26694) --- .../sales_taxes_and_charges.json | 14 ++++++++++++-- erpnext/controllers/taxes_and_totals.py | 7 ++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index 1b7a0fe562e..cfdb167bbca 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -27,7 +27,8 @@ "base_tax_amount", "base_total", "base_tax_amount_after_discount_amount", - "item_wise_tax_detail" + "item_wise_tax_detail", + "dont_recompute_tax" ], "fields": [ { @@ -200,13 +201,22 @@ "fieldname": "included_in_paid_amount", "fieldtype": "Check", "label": "Considered In Paid Amount" + }, + { + "default": "0", + "fieldname": "dont_recompute_tax", + "fieldtype": "Check", + "hidden": 1, + "label": "Dont Recompute tax", + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-06-14 01:44:36.899147", + "modified": "2021-07-27 12:40:59.051803", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 56da5b71da0..099c7d43463 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -152,7 +152,7 @@ class calculate_taxes_and_totals(object): validate_taxes_and_charges(tax) validate_inclusive_tax(tax, self.doc) - if not self.doc.get('is_consolidated'): + if not (self.doc.get('is_consolidated') or tax.get("dont_recompute_tax")): tax.item_wise_tax_detail = {} tax_fields = ["total", "tax_amount_after_discount_amount", @@ -347,7 +347,7 @@ class calculate_taxes_and_totals(object): elif tax.charge_type == "On Item Quantity": current_tax_amount = tax_rate * item.qty - if not self.doc.get("is_consolidated"): + if not (self.doc.get("is_consolidated") or tax.get("dont_recompute_tax")): self.set_item_wise_tax(item, tax, tax_rate, current_tax_amount) return current_tax_amount @@ -455,7 +455,8 @@ class calculate_taxes_and_totals(object): def _cleanup(self): if not self.doc.get('is_consolidated'): for tax in self.doc.get("taxes"): - tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':')) + if not tax.get("dont_recompute_tax"): + tax.item_wise_tax_detail = json.dumps(tax.item_wise_tax_detail, separators=(',', ':')) def set_discount_amount(self): if self.doc.additional_discount_percentage: From 6a71955b998f30e3477b9847cd8fec4b806b69bf Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 29 Jul 2021 14:28:13 +0530 Subject: [PATCH 182/369] chore: change location of backport action (#26705) (#26707) (cherry picked from commit e906acdc49a5131680301eb056f16c7821b0b539) Co-authored-by: Ankush --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index cc98f4544f8..1d180f251e1 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -12,7 +12,7 @@ jobs: - name: Checkout Actions uses: actions/checkout@v2 with: - repository: "ankush/backport" + repository: "frappe/backport" path: ./actions ref: develop - name: Install Actions From 4c681592bf9d7933f872dbec460cb8942535a934 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 29 Jul 2021 15:26:19 +0530 Subject: [PATCH 183/369] fix: TDS calculation for first threshold breach for TDS category 194Q --- .../tax_withholding_category/tax_withholding_category.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 020de3c3f34..481ef285e72 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -148,6 +148,7 @@ def get_lower_deduction_certificate(fiscal_year, pan_no): def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, pan_no=None): fiscal_year = fiscal_year_details[0] + vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type) advance_vouchers = get_advance_vouchers(parties, fiscal_year, inv.company, party_type=party_type) taxable_vouchers = vouchers + advance_vouchers @@ -267,7 +268,11 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)): if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(tax_details.tax_on_excess_amount): - supp_credit_amt -= cumulative_threshold + # Get net total again as TDS is calculated on net total + # Grand is used to just check for threshold breach + net_total = frappe.db.get_value('Purchase Invoice', invoice_filters, 'sum(net_total)') or 0.0 + net_total += inv.net_total + supp_credit_amt = net_total - cumulative_threshold if ldc and is_valid_certificate( ldc.valid_from, ldc.valid_upto, From e39bbc85e11d848a2dd248c546b723f914d3bede Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 29 Jul 2021 15:46:25 +0530 Subject: [PATCH 184/369] fix: cannot cancel payment entry if linked with invoices (#26703) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index d3ac3a66760..439b1edbce6 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -7,6 +7,8 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges"; frappe.ui.form.on('Payment Entry', { onload: function(frm) { + frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice']; + if(frm.doc.__islocal) { if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null); if (!frm.doc.paid_to) frm.set_value("paid_to_account_currency", null); From 379ce70126205d3e489680549aec51cc6722aa6a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 29 Jul 2021 17:02:06 +0530 Subject: [PATCH 185/369] fix: remove cancelled entries from Stock and Account Value comparison report --- .../stock_and_account_value_comparison.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index 14d543b1740..bfc4471b9af 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -22,6 +22,7 @@ def get_data(report_filters): data = [] filters = { + "is_cancelled": 0, "company": report_filters.company, "posting_date": ("<=", report_filters.as_on_date) } @@ -34,7 +35,7 @@ def get_data(report_filters): key = (d.voucher_type, d.voucher_no) gl_data = voucher_wise_gl_data.get(key) or {} d.account_value = gl_data.get("account_value", 0) - d.difference_value = (d.stock_value - d.account_value) + d.difference_value = abs(d.stock_value - d.account_value) if abs(d.difference_value) > 0.1: data.append(d) From 19a6d809272ab4efeb6ef57d45d47b71f6d8fec6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 29 Jul 2021 18:47:16 +0530 Subject: [PATCH 186/369] fix: Parent condition in pricing rules --- erpnext/accounts/doctype/pricing_rule/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index b54d0e73a8c..94abf3b3c06 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -168,7 +168,7 @@ def _get_tree_conditions(args, parenttype, table, allow_blank=True): frappe.throw(_("Invalid {0}").format(args.get(field))) parent_groups = frappe.db.sql_list("""select name from `tab%s` - where lft>=%s and rgt<=%s""" % (parenttype, '%s', '%s'), (lft, rgt)) + where lft<=%s and rgt>=%s""" % (parenttype, '%s', '%s'), (lft, rgt)) if parenttype in ["Customer Group", "Item Group", "Territory"]: parent_field = "parent_{0}".format(frappe.scrub(parenttype)) From 1011c1b01acf5f606de2f9936bf9ea7411f1f53e Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 9 Jul 2021 01:44:34 +0530 Subject: [PATCH 187/369] fix: Clear Payment Schedule if PI has default Payment Schedule, but linked PO doensn't --- .../purchase_invoice/purchase_invoice.py | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index f7992797ed4..147785a0806 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1142,6 +1142,79 @@ class PurchaseInvoice(BuyingController): if update: self.db_set('status', self.status, update_modified = update_modified) +@frappe.whitelist() +def set_payment_terms_from_po(doc): + if isinstance(doc, six.string_types): + doc = json.loads(doc) + + purchase_order = doc.get('items')[0].get('purchase_order') + + if purchase_order and all_items_have_same_po(doc, purchase_order): + purchase_order = frappe.get_cached_doc('Purchase Order', purchase_order) + else: + return + + if has_default_payment_terms(doc) and not has_default_payment_terms(purchase_order): + doc['payment_schedule'] = [] + doc['payment_terms_template'] = purchase_order.payment_terms_template + + for schedule in purchase_order.payment_schedule: + payment_schedule = { + 'payment_term': schedule.payment_term, + 'due_date': schedule.due_date, + 'invoice_portion': schedule.invoice_portion, + 'discount_type': schedule.discount_type, + 'discount': schedule.discount, + 'base_payment_amount': schedule.base_payment_amount, + 'payment_amount': schedule.payment_amount, + 'outstanding': schedule.outstanding + } + doc['payment_schedule'].append(payment_schedule) + + return doc + +def all_items_have_same_po(doc, purchase_order): + for item in doc.get('items'): + if item.get('purchase_order') != purchase_order: + return False + + return True + +def has_default_payment_terms(doc): + if doc.get('payment_schedule')[0].get('invoice_portion') == 100: + return True + return False + +# to get details of purchase invoice/receipt from which this doc was created for exchange rate difference handling +def get_purchase_document_details(doc): + if doc.doctype == 'Purchase Invoice': + doc_reference = 'purchase_receipt' + items_reference = 'pr_detail' + parent_doctype = 'Purchase Receipt' + child_doctype = 'Purchase Receipt Item' + else: + doc_reference = 'purchase_invoice' + items_reference = 'purchase_invoice_item' + parent_doctype = 'Purchase Invoice' + child_doctype = 'Purchase Invoice Item' + + purchase_receipts_or_invoices = [] + items = [] + + for item in doc.get('items'): + if item.get(doc_reference): + purchase_receipts_or_invoices.append(item.get(doc_reference)) + if item.get(items_reference): + items.append(item.get(items_reference)) + + exchange_rate_map = frappe._dict(frappe.get_all(parent_doctype, filters={'name': ('in', + purchase_receipts_or_invoices)}, fields=['name', 'conversion_rate'], as_list=1)) + + net_rate_map = frappe._dict(frappe.get_all(child_doctype, filters={'name': ('in', + items)}, fields=['name', 'net_rate'], as_list=1)) + + return exchange_rate_map, net_rate_map + def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) From b389c9e3759d8e62b2c7a7721c4d6d8a24c8fb77 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 22:53:21 +0530 Subject: [PATCH 188/369] fix: Fetch Payment Terms from Sales/Purchase Orders --- .../purchase_invoice/purchase_invoice.py | 43 ------------------- .../doctype/purchase_order/purchase_order.py | 12 +++--- erpnext/controllers/accounts_controller.py | 43 +++++++++++++++++++ .../doctype/sales_order/sales_order.py | 6 +++ .../doctype/delivery_note/delivery_note.py | 6 +++ .../purchase_receipt/purchase_receipt.py | 5 +++ 6 files changed, 66 insertions(+), 49 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 147785a0806..28a9bddc420 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1142,49 +1142,6 @@ class PurchaseInvoice(BuyingController): if update: self.db_set('status', self.status, update_modified = update_modified) -@frappe.whitelist() -def set_payment_terms_from_po(doc): - if isinstance(doc, six.string_types): - doc = json.loads(doc) - - purchase_order = doc.get('items')[0].get('purchase_order') - - if purchase_order and all_items_have_same_po(doc, purchase_order): - purchase_order = frappe.get_cached_doc('Purchase Order', purchase_order) - else: - return - - if has_default_payment_terms(doc) and not has_default_payment_terms(purchase_order): - doc['payment_schedule'] = [] - doc['payment_terms_template'] = purchase_order.payment_terms_template - - for schedule in purchase_order.payment_schedule: - payment_schedule = { - 'payment_term': schedule.payment_term, - 'due_date': schedule.due_date, - 'invoice_portion': schedule.invoice_portion, - 'discount_type': schedule.discount_type, - 'discount': schedule.discount, - 'base_payment_amount': schedule.base_payment_amount, - 'payment_amount': schedule.payment_amount, - 'outstanding': schedule.outstanding - } - doc['payment_schedule'].append(payment_schedule) - - return doc - -def all_items_have_same_po(doc, purchase_order): - for item in doc.get('items'): - if item.get('purchase_order') != purchase_order: - return False - - return True - -def has_default_payment_terms(doc): - if doc.get('payment_schedule')[0].get('invoice_portion') == 100: - return True - return False - # to get details of purchase invoice/receipt from which this doc was created for exchange rate difference handling def get_purchase_document_details(doc): if doc.doctype == 'Purchase Invoice': diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index eaa502ff7f0..a0bac51046f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -443,6 +443,8 @@ def make_purchase_invoice_from_portal(purchase_order_name): frappe.response.location = '/purchase-invoices/' + doc.name def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions=False): + from erpnext.controllers.accounts_controller import fetch_payment_terms_from_order + def postprocess(source, target): target.flags.ignore_permissions = ignore_permissions set_missing_values(source, target) @@ -489,15 +491,13 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions }, } - if frappe.get_single("Accounts Settings").automatically_fetch_payment_terms == 1: - fields["Payment Schedule"] = { - "doctype": "Payment Schedule", - "add_if_empty": True - } - doc = get_mapped_doc("Purchase Order", source_name, fields, target_doc, postprocess, ignore_permissions=ignore_permissions) + automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) + if automatically_fetch_payment_terms: + fetch_payment_terms_from_order(doc) + return doc @frappe.whitelist() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index cdd865ac4ac..f7ea77bae44 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1803,3 +1803,46 @@ def validate_regional(doc): @erpnext.allow_regional def validate_einvoice_fields(doc): pass + +def fetch_payment_terms_from_order(doc): + """ + Fetch Payment Terms from Purchase/Sales Order on creating a new Purchase/Sales Invoice. + """ + + if doc.doctype == "Sales Invoice": + po_or_so = doc.get('items')[0].get('sales_order') + po_or_so_doctype = "Sales Order" + po_or_so_doctype_name = "sales_order" + else: + po_or_so = doc.get('items')[0].get('purchase_order') + po_or_so_doctype = "Purchase Order" + po_or_so_doctype_name = "purchase_order" + + if po_or_so and all_items_have_same_po_or_so(doc, po_or_so, po_or_so_doctype_name): + po_or_so = frappe.get_cached_doc(po_or_so_doctype, po_or_so) + else: + doc.set_payment_schedule() + return + + doc.payment_schedule = [] + doc.payment_terms_template = po_or_so.payment_terms_template + + for schedule in po_or_so.payment_schedule: + payment_schedule = { + 'payment_term': schedule.payment_term, + 'due_date': schedule.due_date, + 'invoice_portion': schedule.invoice_portion, + 'discount_type': schedule.discount_type, + 'discount': schedule.discount, + 'base_payment_amount': schedule.base_payment_amount, + 'payment_amount': schedule.payment_amount, + 'outstanding': schedule.outstanding + } + doc.append("payment_schedule", payment_schedule) + +def all_items_have_same_po_or_so(doc, po_or_so, po_or_so_fieldname): + for item in doc.get('items'): + if item.get(po_or_so_fieldname) != po_or_so: + return False + + return True \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 41f57a34d3d..a58c381df35 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -621,6 +621,8 @@ def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False): @frappe.whitelist() def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): + from erpnext.controllers.accounts_controller import fetch_payment_terms_from_order + def postprocess(source, target): set_missing_values(source, target) #Get the advance paid Journal Entries in Sales Invoice Advance @@ -693,6 +695,10 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): } }, target_doc, postprocess, ignore_permissions=ignore_permissions) + automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) + if automatically_fetch_payment_terms: + fetch_payment_terms_from_order(doclist) + return doclist @frappe.whitelist() diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 4808e948fc6..1628f930191 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -414,6 +414,8 @@ def get_returned_qty_map(delivery_note): @frappe.whitelist() def make_sales_invoice(source_name, target_doc=None): + from erpnext.controllers.accounts_controller import fetch_payment_terms_from_order + doc = frappe.get_doc('Delivery Note', source_name) to_make_invoice_qty_map = {} @@ -503,6 +505,10 @@ def make_sales_invoice(source_name, target_doc=None): } }, target_doc, set_missing_values) + automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) + if automatically_fetch_payment_terms: + fetch_payment_terms_from_order(doc) + return doc @frappe.whitelist() diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 82c87a83a50..cf6cac273f0 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -583,6 +583,7 @@ def update_billing_percentage(pr_doc, update_modified=True): @frappe.whitelist() def make_purchase_invoice(source_name, target_doc=None): from erpnext.accounts.party import get_payment_terms_template + from erpnext.controllers.accounts_controller import fetch_payment_terms_from_order doc = frappe.get_doc('Purchase Receipt', source_name) returned_qty_map = get_returned_qty_map(source_name) @@ -654,6 +655,10 @@ def make_purchase_invoice(source_name, target_doc=None): } }, target_doc, set_missing_values) + automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) + if automatically_fetch_payment_terms: + fetch_payment_terms_from_order(doclist) + return doclist def get_invoiced_qty_map(purchase_receipt): From def7cc6cb3584ce32e8351e8b7faeb49a955a052 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 22 Jul 2021 05:57:42 +0530 Subject: [PATCH 190/369] fix: Modify set_payment_schedule() to include fetch_payment_terms_from_order() --- .../doctype/purchase_order/purchase_order.py | 4 +- erpnext/controllers/accounts_controller.py | 111 +++++++++++------- .../doctype/sales_order/sales_order.py | 4 +- .../doctype/delivery_note/delivery_note.py | 4 +- .../purchase_receipt/purchase_receipt.py | 3 +- 5 files changed, 70 insertions(+), 56 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index a0bac51046f..f68d81909a3 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -443,8 +443,6 @@ def make_purchase_invoice_from_portal(purchase_order_name): frappe.response.location = '/purchase-invoices/' + doc.name def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions=False): - from erpnext.controllers.accounts_controller import fetch_payment_terms_from_order - def postprocess(source, target): target.flags.ignore_permissions = ignore_permissions set_missing_values(source, target) @@ -496,7 +494,7 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) if automatically_fetch_payment_terms: - fetch_payment_terms_from_order(doc) + doc.set_payment_schedule() return doc diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index f7ea77bae44..2bf5c77e61c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1105,7 +1105,14 @@ class AccountsController(TransactionBase): data = get_payment_terms(self.payment_terms_template, posting_date, grand_total, base_grand_total) for item in data: self.append("payment_schedule", item) - else: + + elif self.doctype in ["Sales Invoice", "Purchase Invoice"]: + po_or_so, doctype, fieldname = self.get_order_details() + + if self.linked_order_has_payment_terms(po_or_so, fieldname): + self.fetch_payment_terms_from_order(po_or_so, doctype) + + elif self.doctype not in ["Purchase Receipt"]: data = dict(due_date=due_date, invoice_portion=100, payment_amount=grand_total, base_payment_amount=base_grand_total) self.append("payment_schedule", data) else: @@ -1118,6 +1125,63 @@ class AccountsController(TransactionBase): d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount')) + def get_order_details(self): + if self.doctype == "Sales Invoice": + po_or_so = self.get('items')[0].get('sales_order') + po_or_so_doctype = "Sales Order" + po_or_so_doctype_name = "sales_order" + + else: + po_or_so = self.get('items')[0].get('purchase_order') + po_or_so_doctype = "Purchase Order" + po_or_so_doctype_name = "purchase_order" + + return po_or_so, po_or_so_doctype, po_or_so_doctype_name + + def linked_order_has_payment_terms(self, po_or_so, fieldname): + if po_or_so and self.all_items_have_same_po_or_so(po_or_so, fieldname): + if self.linked_order_has_payment_terms_template(po_or_so): + return True + elif self.linked_order_has_payment_schedule(po_or_so): + return True + + return False + + def all_items_have_same_po_or_so(self, po_or_so, fieldname): + for item in self.get('items'): + if item.get(fieldname) != po_or_so: + return False + + return True + + def linked_order_has_payment_terms_template(self, po_or_so): + return frappe.get_value('Sales Order', po_or_so, 'payment_terms_template') + + def linked_order_has_payment_schedule(self, po_or_so): + return frappe.get_all('Payment Schedule', filters={'parent': po_or_so}) + + def fetch_payment_terms_from_order(self, po_or_so, po_or_so_doctype): + """ + Fetch Payment Terms from Purchase/Sales Order on creating a new Purchase/Sales Invoice. + """ + po_or_so = frappe.get_cached_doc(po_or_so_doctype, po_or_so) + + self.payment_schedule = [] + self.payment_terms_template = po_or_so.payment_terms_template + + for schedule in po_or_so.payment_schedule: + payment_schedule = { + 'payment_term': schedule.payment_term, + 'due_date': schedule.due_date, + 'invoice_portion': schedule.invoice_portion, + 'discount_type': schedule.discount_type, + 'discount': schedule.discount, + 'base_payment_amount': schedule.base_payment_amount, + 'payment_amount': schedule.payment_amount, + 'outstanding': schedule.outstanding + } + self.append("payment_schedule", payment_schedule) + def set_due_date(self): due_dates = [d.due_date for d in self.get("payment_schedule") if d.due_date] if due_dates: @@ -1802,47 +1866,4 @@ def validate_regional(doc): @erpnext.allow_regional def validate_einvoice_fields(doc): - pass - -def fetch_payment_terms_from_order(doc): - """ - Fetch Payment Terms from Purchase/Sales Order on creating a new Purchase/Sales Invoice. - """ - - if doc.doctype == "Sales Invoice": - po_or_so = doc.get('items')[0].get('sales_order') - po_or_so_doctype = "Sales Order" - po_or_so_doctype_name = "sales_order" - else: - po_or_so = doc.get('items')[0].get('purchase_order') - po_or_so_doctype = "Purchase Order" - po_or_so_doctype_name = "purchase_order" - - if po_or_so and all_items_have_same_po_or_so(doc, po_or_so, po_or_so_doctype_name): - po_or_so = frappe.get_cached_doc(po_or_so_doctype, po_or_so) - else: - doc.set_payment_schedule() - return - - doc.payment_schedule = [] - doc.payment_terms_template = po_or_so.payment_terms_template - - for schedule in po_or_so.payment_schedule: - payment_schedule = { - 'payment_term': schedule.payment_term, - 'due_date': schedule.due_date, - 'invoice_portion': schedule.invoice_portion, - 'discount_type': schedule.discount_type, - 'discount': schedule.discount, - 'base_payment_amount': schedule.base_payment_amount, - 'payment_amount': schedule.payment_amount, - 'outstanding': schedule.outstanding - } - doc.append("payment_schedule", payment_schedule) - -def all_items_have_same_po_or_so(doc, po_or_so, po_or_so_fieldname): - for item in doc.get('items'): - if item.get(po_or_so_fieldname) != po_or_so: - return False - - return True \ No newline at end of file + pass \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index a58c381df35..2b9d516e217 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -621,8 +621,6 @@ def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False): @frappe.whitelist() def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): - from erpnext.controllers.accounts_controller import fetch_payment_terms_from_order - def postprocess(source, target): set_missing_values(source, target) #Get the advance paid Journal Entries in Sales Invoice Advance @@ -697,7 +695,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) if automatically_fetch_payment_terms: - fetch_payment_terms_from_order(doclist) + doclist.set_payment_schedule() return doclist diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 1628f930191..f99a01b8202 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -414,8 +414,6 @@ def get_returned_qty_map(delivery_note): @frappe.whitelist() def make_sales_invoice(source_name, target_doc=None): - from erpnext.controllers.accounts_controller import fetch_payment_terms_from_order - doc = frappe.get_doc('Delivery Note', source_name) to_make_invoice_qty_map = {} @@ -507,7 +505,7 @@ def make_sales_invoice(source_name, target_doc=None): automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) if automatically_fetch_payment_terms: - fetch_payment_terms_from_order(doc) + doc.set_payment_schedule() return doc diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index cf6cac273f0..b05cc7875cf 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -583,7 +583,6 @@ def update_billing_percentage(pr_doc, update_modified=True): @frappe.whitelist() def make_purchase_invoice(source_name, target_doc=None): from erpnext.accounts.party import get_payment_terms_template - from erpnext.controllers.accounts_controller import fetch_payment_terms_from_order doc = frappe.get_doc('Purchase Receipt', source_name) returned_qty_map = get_returned_qty_map(source_name) @@ -657,7 +656,7 @@ def make_purchase_invoice(source_name, target_doc=None): automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) if automatically_fetch_payment_terms: - fetch_payment_terms_from_order(doclist) + doc.set_payment_schedule() return doclist From 59d1cc02c525cb6710d2a38a5b9c40031f8bf9a8 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 22 Jul 2021 22:58:45 +0530 Subject: [PATCH 191/369] fix: Add test to check if payment terms are fetched when creating a Sales Invoice --- .../doctype/sales_order/test_sales_order.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 974648d6d44..bf6925473ad 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1229,7 +1229,42 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.ValidationError, so.cancel) + def test_payment_terms_are_fetched_when_creating_invoice(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice + automatically_fetch_payment_terms() + + so = make_sales_order(uom="Nos", do_not_save=1) + create_payment_terms_template() + so.payment_terms_template = 'Test Receivable Template' + so.submit() + + si = create_sales_invoice(qty=10, do_not_save=1) + si.items[0].sales_order = so.name + si.items[0].so_detail = so.items[0].name + si.insert() + + self.assertEqual(so.payment_terms_template, si.payment_terms_template) + compare_payment_schedules(self, so, si) + +def automatically_fetch_payment_terms(enable=1): + accounts_settings = frappe.get_doc("Accounts Settings") + accounts_settings.automatically_fetch_payment_terms = enable + accounts_settings.save() + +def compare_payment_schedules(doc, doc1, doc2): + payment_schedule1 = frappe.db.sql("""select payment_term, description, due_date, mode_of_payment, invoice_portion, payment_amount + from `tabPayment Schedule` + where parenttype=%s and parent=%s + order by payment_term asc""", (doc1.doctype, doc1.name), as_dict=1) + + payment_schedule2 = frappe.db.sql("""select payment_term, description, due_date, mode_of_payment, invoice_portion, payment_amount + from `tabPayment Schedule` + where parenttype=%s and parent=%s + order by payment_term asc""", (doc2.doctype, doc2.name), as_dict=1) + + doc.assertEqual(payment_schedule1, payment_schedule2) def make_sales_order(**args): so = frappe.new_doc("Sales Order") From 293c5e10c3fa122715ca5f3ca67f9051a87bf01f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 23 Jul 2021 03:23:29 +0530 Subject: [PATCH 192/369] fix: Add test to check if payment terms are fetched when creating a Sales Invoice --- .../delivery_note/test_delivery_note.py | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index f981aeb13bb..8b1245eb403 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -17,7 +17,8 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry \ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, SerialNoWarehouseError from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation \ import create_stock_reconciliation, set_valuation_method -from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order, create_dn_against_so +from erpnext.selling.doctype.sales_order.test_sales_order \ + import make_sales_order, create_dn_against_so, automatically_fetch_payment_terms, compare_payment_schedules from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.stock.doctype.warehouse.test_warehouse import get_warehouse from erpnext.stock.doctype.item.test_item import make_item @@ -759,6 +760,30 @@ class TestDeliveryNote(unittest.TestCase): self.assertTrue("TESTBATCH" in dn.packed_items[0].batch_no, "Batch number not added in packed item") + def test_payment_terms_are_fetched_when_creating_invoice(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice + + automatically_fetch_payment_terms() + + so = make_sales_order(uom="Nos", do_not_save=1) + create_payment_terms_template() + so.payment_terms_template = 'Test Receivable Template' + so.submit() + + dn = create_dn_against_so(so.name, delivered_qty=10) + + si = create_sales_invoice(qty=10, do_not_save=1) + si.items[0].delivery_note= dn.name + si.items[0].dn_detail = dn.items[0].name + si.items[0].sales_order = so.name + si.items[0].so_detail = so.items[0].name + + si.insert() + si.submit() + + self.assertEqual(so.payment_terms_template, si.payment_terms_template) + compare_payment_schedules(self, so, si) def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") From e94604f517ea77f6e82fe8e8c14cdb569742eee2 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 23 Jul 2021 03:36:37 +0530 Subject: [PATCH 193/369] fix: Add test to check if payment terms are fetched when creating a Purchase Invoice --- .../purchase_order/test_purchase_order.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 8563b97ab74..11cf39e5e2d 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -968,8 +968,25 @@ class TestPurchaseOrder(unittest.TestCase): # To test if the PO does NOT have a Blanket Order self.assertEqual(po_doc.items[0].blanket_order, None) + def test_payment_terms_are_fetched_when_creating_invoice(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template + from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice + from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules + automatically_fetch_payment_terms() + po = create_purchase_order(qty=10, rate=100, do_not_save=1) + create_payment_terms_template() + po.payment_terms_template = 'Test Receivable Template' + po.submit() + + pi = make_purchase_invoice(qty=10, rate=100, do_not_save=1) + pi.items[0].purchase_order = po.name + pi.items[0].po_detail = po.items[0].name + pi.insert() + + # self.assertEqual(po.payment_terms_template, pi.payment_terms_template) + compare_payment_schedules(self, po, pi) def make_pr_against_po(po, received_qty=0): pr = make_purchase_receipt(po) From 0413a5aafdc2ef05ce6aabfc275081aac8a737af Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 23 Jul 2021 03:44:56 +0530 Subject: [PATCH 194/369] fix: Add test to check if payment terms are fetched when creating a Purchase Invoice --- .../purchase_receipt/test_purchase_receipt.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 2eb8bfd5d2f..44e3e1e70e0 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1052,6 +1052,58 @@ class TestPurchaseReceipt(unittest.TestCase): frappe.db.set_value('Company', company, 'enable_perpetual_inventory_for_non_stock_items', before_test_value) + def test_purchase_receipt_with_exchange_rate_difference(self): + from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice as create_purchase_invoice + from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_purchase_receipt as create_purchase_receipt + + pi = create_purchase_invoice(company="_Test Company with perpetual inventory", + cost_center = "Main - TCP1", + warehouse = "Stores - TCP1", + expense_account ="_Test Account Cost for Goods Sold - TCP1", + currency = "USD", conversion_rate = 70) + + pr = create_purchase_receipt(pi.name) + pr.conversion_rate = 80 + pr.items[0].purchase_invoice = pi.name + pr.items[0].purchase_invoice_item = pi.items[0].name + + pr.save() + pr.submit() + + # Get exchnage gain and loss account + exchange_gain_loss_account = frappe.db.get_value('Company', pr.company, 'exchange_gain_loss_account') + + # fetching the latest GL Entry with exchange gain and loss account account + amount = frappe.db.get_value('GL Entry', {'account': exchange_gain_loss_account, 'voucher_no': pr.name}, 'credit') + discrepancy_caused_by_exchange_rate_diff = abs(pi.items[0].base_net_amount - pr.items[0].base_net_amount) + + self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount) + + def test_payment_terms_are_fetched_when_creating_invoice(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template + from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice + from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, make_pr_against_po + from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules + + automatically_fetch_payment_terms() + + po = create_purchase_order(qty=10, rate=100, do_not_save=1) + create_payment_terms_template() + po.payment_terms_template = 'Test Receivable Template' + po.submit() + + pr = make_pr_against_po(po.name, received_qty=10) + + pi = make_purchase_invoice(qty=10, rate=100, do_not_save=1) + pi.items[0].purchase_receipt = pr.name + pi.items[0].pr_detail = pr.items[0].name + pi.items[0].purchase_order = po.name + pi.items[0].po_detail = po.items[0].name + pi.insert() + + # self.assertEqual(po.payment_terms_template, pi.payment_terms_template) + compare_payment_schedules(self, po, pi) + def get_sl_entries(voucher_type, voucher_no): return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s From 57df4a3aa1c5432eb889c7045de3d570fe35e90c Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 23 Jul 2021 03:45:59 +0530 Subject: [PATCH 195/369] fix: Rename tests --- erpnext/buying/doctype/purchase_order/test_purchase_order.py | 2 +- erpnext/selling/doctype/sales_order/test_sales_order.py | 2 +- erpnext/stock/doctype/delivery_note/test_delivery_note.py | 2 +- erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 11cf39e5e2d..474c9cf3df9 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -968,7 +968,7 @@ class TestPurchaseOrder(unittest.TestCase): # To test if the PO does NOT have a Blanket Order self.assertEqual(po_doc.items[0].blanket_order, None) - def test_payment_terms_are_fetched_when_creating_invoice(self): + def test_payment_terms_are_fetched_when_creating_purchase_invoice(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index bf6925473ad..3fbe0afd10e 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1229,7 +1229,7 @@ class TestSalesOrder(unittest.TestCase): self.assertRaises(frappe.ValidationError, so.cancel) - def test_payment_terms_are_fetched_when_creating_invoice(self): + def test_payment_terms_are_fetched_when_creating_sales_invoice(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 8b1245eb403..ca8d8b99d31 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -760,7 +760,7 @@ class TestDeliveryNote(unittest.TestCase): self.assertTrue("TESTBATCH" in dn.packed_items[0].batch_no, "Batch number not added in packed item") - def test_payment_terms_are_fetched_when_creating_invoice(self): + def test_payment_terms_are_fetched_when_creating_sales_invoice(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 44e3e1e70e0..9a5064fcc71 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1079,7 +1079,7 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount) - def test_payment_terms_are_fetched_when_creating_invoice(self): + def test_payment_terms_are_fetched_when_creating_purchase_invoice(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order, make_pr_against_po From 6343950d82e77ccffa0d444829bd65a8b23fc441 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 23 Jul 2021 03:58:27 +0530 Subject: [PATCH 196/369] fix: Sider issues --- erpnext/selling/doctype/sales_order/test_sales_order.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 3fbe0afd10e..f4a089bcef2 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1249,9 +1249,9 @@ class TestSalesOrder(unittest.TestCase): compare_payment_schedules(self, so, si) def automatically_fetch_payment_terms(enable=1): - accounts_settings = frappe.get_doc("Accounts Settings") - accounts_settings.automatically_fetch_payment_terms = enable - accounts_settings.save() + accounts_settings = frappe.get_doc("Accounts Settings") + accounts_settings.automatically_fetch_payment_terms = enable + accounts_settings.save() def compare_payment_schedules(doc, doc1, doc2): payment_schedule1 = frappe.db.sql("""select payment_term, description, due_date, mode_of_payment, invoice_portion, payment_amount From 01ab63189a70788c7c542bcd6a69b56280a46761 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 29 Jul 2021 19:18:35 +0530 Subject: [PATCH 197/369] fix: Check if Purchase Order has Payment Terms Template --- erpnext/controllers/accounts_controller.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 2bf5c77e61c..913e70b30c5 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1109,7 +1109,7 @@ class AccountsController(TransactionBase): elif self.doctype in ["Sales Invoice", "Purchase Invoice"]: po_or_so, doctype, fieldname = self.get_order_details() - if self.linked_order_has_payment_terms(po_or_so, fieldname): + if self.linked_order_has_payment_terms(po_or_so, fieldname, doctype): self.fetch_payment_terms_from_order(po_or_so, doctype) elif self.doctype not in ["Purchase Receipt"]: @@ -1138,9 +1138,9 @@ class AccountsController(TransactionBase): return po_or_so, po_or_so_doctype, po_or_so_doctype_name - def linked_order_has_payment_terms(self, po_or_so, fieldname): + def linked_order_has_payment_terms(self, po_or_so, fieldname, doctype): if po_or_so and self.all_items_have_same_po_or_so(po_or_so, fieldname): - if self.linked_order_has_payment_terms_template(po_or_so): + if self.linked_order_has_payment_terms_template(po_or_so, doctype): return True elif self.linked_order_has_payment_schedule(po_or_so): return True @@ -1154,8 +1154,8 @@ class AccountsController(TransactionBase): return True - def linked_order_has_payment_terms_template(self, po_or_so): - return frappe.get_value('Sales Order', po_or_so, 'payment_terms_template') + def linked_order_has_payment_terms_template(self, po_or_so, doctype): + return frappe.get_value(doctype, po_or_so, 'payment_terms_template') def linked_order_has_payment_schedule(self, po_or_so): return frappe.get_all('Payment Schedule', filters={'parent': po_or_so}) From c7df7593248be42c1a0a3bd15a1905d8864cc1b5 Mon Sep 17 00:00:00 2001 From: Ankush Date: Thu, 29 Jul 2021 19:49:12 +0530 Subject: [PATCH 198/369] fix: empty "against account" in Purchase Receipt GLE bp #26712 (#26718) * fix: correct field for GLE against account in PR * fix: remove incorrect field check from reposting --- erpnext/accounts/utils.py | 2 +- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 1cdbd8d38a6..9afe365f747 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -966,7 +966,7 @@ def compare_existing_and_expected_gle(existing_gle, expected_gle, precision): for e in existing_gle: if entry.account == e.account: account_existed = True - if (entry.account == e.account and entry.against_account == e.against_account + if (entry.account == e.account and (not entry.cost_center or not e.cost_center or entry.cost_center == e.cost_center) and ( flt(entry.debit, precision) != flt(e.debit, precision) or flt(entry.credit, precision) != flt(e.credit, precision))): diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 82c87a83a50..26ea11e01d9 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -415,7 +415,7 @@ class PurchaseReceipt(BuyingController): "cost_center": cost_center, "debit": debit, "credit": credit, - "against_account": against_account, + "against": against_account, "remarks": remarks, } From 2d6f2fea5b42286e4a12d26d219448f9a298c0ed Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 30 Jul 2021 10:55:53 +0530 Subject: [PATCH 199/369] fix: gl entries for exchange gain loss (#26728) --- .../purchase_invoice/test_purchase_invoice.py | 8 ++++---- erpnext/controllers/accounts_controller.py | 17 +++++++++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index ca4d009956d..4bc22a544d1 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -997,8 +997,8 @@ class TestPurchaseInvoice(unittest.TestCase): expected_gle = [ ["_Test Account Cost for Goods Sold - _TC", 37500.0], - ["_Test Payable USD - _TC", -40000.0], - ["Exchange Gain/Loss - _TC", 2500.0] + ["_Test Payable USD - _TC", -35000.0], + ["Exchange Gain/Loss - _TC", -2500.0] ] gl_entries = frappe.db.sql(""" @@ -1028,8 +1028,8 @@ class TestPurchaseInvoice(unittest.TestCase): expected_gle = [ ["_Test Account Cost for Goods Sold - _TC", 36500.0], - ["_Test Payable USD - _TC", -38000.0], - ["Exchange Gain/Loss - _TC", 1500.0] + ["_Test Payable USD - _TC", -35000.0], + ["Exchange Gain/Loss - _TC", -1500.0] ] gl_entries = frappe.db.sql(""" diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index cdd865ac4ac..a9b7efbe980 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -674,19 +674,24 @@ class AccountsController(TransactionBase): if self.get('doctype') in ['Purchase Invoice', 'Sales Invoice']: for d in self.get("advances"): if d.exchange_gain_loss: - party = self.supplier if self.get('doctype') == 'Purchase Invoice' else self.customer - party_account = self.credit_to if self.get('doctype') == 'Purchase Invoice' else self.debit_to - party_type = "Supplier" if self.get('doctype') == 'Purchase Invoice' else "Customer" + is_purchase_invoice = self.get('doctype') == 'Purchase Invoice' + party = self.supplier if is_purchase_invoice else self.customer + party_account = self.credit_to if is_purchase_invoice else self.debit_to + party_type = "Supplier" if is_purchase_invoice else "Customer" gain_loss_account = frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account') + if not gain_loss_account: + frappe.throw(_("Please set Default Exchange Gain/Loss Account in Company {}") + .format(self.get('company'))) account_currency = get_account_currency(gain_loss_account) if account_currency != self.company_currency: - frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency)) + frappe.throw(_("Currency for {0} must be {1}").format(gain_loss_account, self.company_currency)) # for purchase dr_or_cr = 'debit' if d.exchange_gain_loss > 0 else 'credit' - # just reverse for sales? - dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit' + if not is_purchase_invoice: + # just reverse for sales? + dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit' gl_entries.append( self.get_gl_dict({ From b8845b9ed986a8cd2e2e30360d12336b23b4c5bc Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Fri, 30 Jul 2021 16:30:18 +0530 Subject: [PATCH 200/369] Fix: Payment Entry party validation issue --- erpnext/accounts/party.py | 5 +++++ erpnext/accounts/utils.py | 2 ++ 2 files changed, 7 insertions(+) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index b97dc401e6a..c3b78a4f482 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -286,6 +286,7 @@ def validate_party_gle_currency(party_type, party, company, party_account_curren .format(frappe.bold(party_type), frappe.bold(party), frappe.bold(existing_gle_currency), frappe.bold(company)), InvalidAccountCurrency) def validate_party_accounts(doc): + companies = [] for account in doc.get("accounts"): @@ -446,6 +447,10 @@ def get_payment_terms_template(party_name, party_type, company=None): return template def validate_party_frozen_disabled(party_type, party_name): + + if frappe.flags.ignore_party_validation: + return + if party_type and party_name: if party_type in ("Customer", "Supplier"): party = frappe.get_cached_value(party_type, party_name, ["is_frozen", "disabled"], as_dict=True) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 1cdbd8d38a6..d569e1cbca2 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -350,6 +350,7 @@ def reconcile_against_document(args): # cancel advance entry doc = frappe.get_doc(d.voucher_type, d.voucher_no) + frappe.flags.ignore_party_validation = True doc.make_gl_entries(cancel=1, adv_adj=1) # update ref in advance entry @@ -361,6 +362,7 @@ def reconcile_against_document(args): # re-submit advance entry doc = frappe.get_doc(d.voucher_type, d.voucher_no) doc.make_gl_entries(cancel = 0, adv_adj =1) + frappe.flags.ignore_party_validation = False if d.voucher_type in ('Payment Entry', 'Journal Entry'): doc.update_expense_claim() From b5c7fce68922a846be2b644980b9dfad8a596e92 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 30 Jul 2021 18:54:35 +0530 Subject: [PATCH 201/369] fix: student category mapping from the program enrollment tool (#26716) (#26739) Co-authored-by: Jannat Patel <31363128+pateljannat@users.noreply.github.com> (cherry picked from commit 1a2332a81cd7999e1f9266337fb48462ebfcbcc4) Co-authored-by: Rucha Mahabal --- erpnext/education/api.py | 7 +- .../program_enrollment_tool_student.json | 245 +++++------------- 2 files changed, 64 insertions(+), 188 deletions(-) diff --git a/erpnext/education/api.py b/erpnext/education/api.py index afa0be9b9f3..4493a3fef17 100644 --- a/erpnext/education/api.py +++ b/erpnext/education/api.py @@ -34,11 +34,14 @@ def enroll_student(source_name): } }}, ignore_permissions=True) student.save() + + student_applicant = frappe.db.get_value("Student Applicant", source_name, + ["student_category", "program"], as_dict=True) program_enrollment = frappe.new_doc("Program Enrollment") program_enrollment.student = student.name - program_enrollment.student_category = student.student_category + program_enrollment.student_category = student_applicant.student_category program_enrollment.student_name = student.title - program_enrollment.program = frappe.db.get_value("Student Applicant", source_name, "program") + program_enrollment.program = student_applicant.program frappe.publish_realtime('enroll_student_progress', {"progress": [2, 4]}, user=frappe.session.user) return program_enrollment diff --git a/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json b/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json index 9be292b65ea..1d7497387f9 100644 --- a/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json +++ b/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json @@ -1,195 +1,68 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-06-10 03:29:02.539914", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2016-06-10 03:29:02.539914", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "student_applicant", + "student", + "student_name", + "column_break_3", + "student_batch_name", + "student_category" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "student_applicant", - "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": "Student Applicant", - "length": 0, - "no_copy": 0, - "options": "Student Applicant", - "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, - "unique": 0 - }, + "fieldname": "student_applicant", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Student Applicant", + "options": "Student Applicant" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "student", - "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": "Student", - "length": 0, - "no_copy": 0, - "options": "Student", - "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, - "unique": 0 - }, + "fieldname": "student", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Student", + "options": "Student" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "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, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "student_name", - "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": "Student 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, - "unique": 0 - }, + "fieldname": "student_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Student Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "student_batch_name", - "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": "Student Batch Name", - "length": 0, - "no_copy": 0, - "options": "Student Batch Name", - "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, - "unique": 0 + "fieldname": "student_batch_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Student Batch Name", + "options": "Student Batch Name" + }, + { + "fieldname": "student_category", + "fieldtype": "Link", + "label": "Student Category", + "options": "Student Category", + "read_only": 1 } - ], - "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-01-02 12:03:53.890741", - "modified_by": "Administrator", - "module": "Education", - "name": "Program Enrollment Tool Student", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2021-07-29 18:19:54.471594", + "modified_by": "Administrator", + "module": "Education", + "name": "Program Enrollment Tool Student", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "restrict_to_domain": "Education", + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file From 74bb55bfd26583b8e12b5dc2dd89eb8f51dbd45d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sun, 1 Aug 2021 20:03:38 +0530 Subject: [PATCH 202/369] Revert "fix: Tax calculation for Recurring additional salary (#24206)" This reverts commit adfdc71844a94a68884c50f45c5f90f3d1640a95. --- .../additional_salary/additional_salary.py | 6 +++--- .../doctype/salary_detail/salary_detail.json | 11 +--------- .../doctype/salary_slip/salary_slip.py | 21 +++++-------------- 3 files changed, 9 insertions(+), 29 deletions(-) diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py index b978cbe2b57..381f399e9fa 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py @@ -112,11 +112,11 @@ class AdditionalSalary(Document): no_of_days = date_diff(getdate(end_date), getdate(start_date)) + 1 return amount_per_day * no_of_days -@frappe.whitelist() def get_additional_salaries(employee, start_date, end_date, component_type): additional_salary_list = frappe.db.sql(""" - select name, salary_component as component, type, amount, overwrite_salary_structure_amount as overwrite, - deduct_full_tax_on_selected_payroll_date, is_recurring + select name, salary_component as component, type, amount, + overwrite_salary_structure_amount as overwrite, + deduct_full_tax_on_selected_payroll_date from `tabAdditional Salary` where employee=%(employee)s and docstatus = 1 diff --git a/erpnext/payroll/doctype/salary_detail/salary_detail.json b/erpnext/payroll/doctype/salary_detail/salary_detail.json index 97608d72f3e..393f647cc88 100644 --- a/erpnext/payroll/doctype/salary_detail/salary_detail.json +++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json @@ -12,7 +12,6 @@ "year_to_date", "section_break_5", "additional_salary", - "is_recurring_additional_salary", "statistical_component", "depends_on_payment_days", "exempted_from_income_tax", @@ -236,19 +235,11 @@ "label": "Year To Date", "options": "currency", "read_only": 1 - }, - { - "default": "0", - "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='earnings' && doc.additional_salary", - "fieldname": "is_recurring_additional_salary", - "fieldtype": "Check", - "label": "Is Recurring Additional Salary", - "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2021-03-14 13:39:15.847158", + "modified": "2021-01-14 13:39:15.847158", "modified_by": "Administrator", "module": "Payroll", "name": "Salary Detail", diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 3e82c0d428a..7e1fb0616d0 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -7,12 +7,12 @@ import datetime, math from frappe.utils import add_days, cint, cstr, flt, getdate, rounded, date_diff, money_in_words, formatdate, get_first_day from frappe.model.naming import make_autoname -from frappe.utils.background_jobs import enqueue from frappe import msgprint, _ from erpnext.payroll.doctype.payroll_entry.payroll_entry import get_start_end_dates from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.utilities.transaction_base import TransactionBase +from frappe.utils.background_jobs import enqueue from erpnext.payroll.doctype.additional_salary.additional_salary import get_additional_salaries from erpnext.payroll.doctype.payroll_period.payroll_period import get_period_factor, get_payroll_period from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_benefit_component_amount @@ -618,8 +618,7 @@ class SalarySlip(TransactionBase): get_salary_component_data(additional_salary.component), additional_salary.amount, component_type, - additional_salary, - is_recurring = additional_salary.is_recurring + additional_salary ) def add_tax_components(self, payroll_period): @@ -640,7 +639,7 @@ class SalarySlip(TransactionBase): tax_row = get_salary_component_data(d) self.update_component_row(tax_row, tax_amount, "deductions") - def update_component_row(self, component_data, amount, component_type, additional_salary=None, is_recurring = 0): + def update_component_row(self, component_data, amount, component_type, additional_salary=None): component_row = None for d in self.get(component_type): if d.salary_component != component_data.salary_component: @@ -681,7 +680,6 @@ class SalarySlip(TransactionBase): component_row.set('abbr', abbr) if additional_salary: - component_row.is_recurring_additional_salary = is_recurring component_row.default_amount = 0 component_row.additional_amount = amount component_row.additional_salary = additional_salary.name @@ -715,7 +713,6 @@ class SalarySlip(TransactionBase): # get remaining numbers of sub-period (period for which one salary is processed) remaining_sub_periods = get_period_factor(self.employee, self.start_date, self.end_date, self.payroll_frequency, payroll_period)[1] - # get taxable_earnings, paid_taxes for previous period previous_taxable_earnings = self.get_taxable_earnings_for_prev_period(payroll_period.start_date, self.start_date, tax_slab.allow_tax_exemption) @@ -875,16 +872,8 @@ class SalarySlip(TransactionBase): if earning.is_tax_applicable: if additional_amount: - if not earning.is_recurring_additional_salary: - taxable_earnings += (amount - additional_amount) - additional_income += additional_amount - else: - to_date = frappe.db.get_value("Additional Salary", earning.additional_salary, 'to_date') - period = (getdate(to_date).month - getdate(self.start_date).month) + 1 - if period > 0: - taxable_earnings += (amount - additional_amount) * period - additional_income += additional_amount * period - + taxable_earnings += (amount - additional_amount) + additional_income += additional_amount if earning.deduct_full_tax_on_selected_payroll_date: additional_income_with_full_tax += additional_amount continue From a75c7c48d8c0710b6c36bc8c720f722cefdc2cd8 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 2 Aug 2021 11:34:28 +0530 Subject: [PATCH 203/369] fix: missing QR Code in auto email attachment (#26599) --- erpnext/regional/india/e_invoice/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index ea600d90973..cd85f6b026f 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -966,7 +966,7 @@ class GSPConnector(): "attached_to_doctype": doctype, "attached_to_name": docname, "attached_to_field": "qrcode_image", - "is_private": 1, + "is_private": 0, "content": qr_image.getvalue()}) _file.save() frappe.db.commit() From 4597f151f5e3f91f34a620d87b4ad805b0498a12 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 2 Aug 2021 11:38:31 +0530 Subject: [PATCH 204/369] fix: POS Item Cart non-stop scroll issue (#26693) --- erpnext/selling/page/point_of_sale/pos_item_cart.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index 6e36d2809ae..a4a4b0e0ed2 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -564,7 +564,6 @@ erpnext.PointOfSale.ItemCart = class { ) set_dynamic_rate_header_width(); - this.scroll_to_item($item_to_update); function set_dynamic_rate_header_width() { const rate_cols = Array.from(me.$cart_items_wrapper.find(".item-rate-amount")); @@ -639,12 +638,6 @@ erpnext.PointOfSale.ItemCart = class { $($img).parent().replaceWith(`
                                ${item_abbr}
                                `); } - scroll_to_item($item) { - if ($item.length === 0) return; - const scrollTop = $item.offset().top - this.$cart_items_wrapper.offset().top + this.$cart_items_wrapper.scrollTop(); - this.$cart_items_wrapper.animate({ scrollTop }); - } - update_selector_value_in_cart_item(selector, value, item) { const $item_to_update = this.get_cart_item(item); $item_to_update.attr(`data-${selector}`, escape(value)); From a9474a9fbd407806ae7cd5f06169501c0c75bdfa Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 2 Aug 2021 12:25:27 +0530 Subject: [PATCH 205/369] fix: POS Invoice consolidated Sales Invoice field set to no copy (#26768) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 7459c11d4d9..33c3e0432bc 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -1545,6 +1545,7 @@ "fieldname": "consolidated_invoice", "fieldtype": "Link", "label": "Consolidated Sales Invoice", + "no_copy": 1, "options": "Sales Invoice", "read_only": 1 } @@ -1552,7 +1553,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2021-02-01 15:03:33.800707", + "modified": "2021-07-29 13:37:20.636171", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", From e43bdf76a5b8a76e590dd1c15605b06c534187fb Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 2 Aug 2021 18:57:48 +0530 Subject: [PATCH 206/369] chore: warning for shopify integration deprecation (#26701) * chore: warning for shopify integration deprecation * fix: warn deprecation during patch for sysadmins --- .../doctype/shopify_log/shopify_log.js | 3 +++ .../doctype/shopify_settings/shopify_settings.js | 4 ++++ erpnext/patches.txt | 1 + .../patches/v13_0/shopify_deprecation_warning.py | 15 +++++++++++++++ 4 files changed, 23 insertions(+) create mode 100644 erpnext/patches/v13_0/shopify_deprecation_warning.py diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js index d3fe7d2b4d6..12faeecc87f 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js +++ b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js @@ -18,5 +18,8 @@ frappe.ui.form.on('Shopify Log', { }) }).addClass('btn-primary'); } + + let app_link = "Ecommerce Integrations" + frm.dashboard.add_comment(__("Shopify Integration will be removed from ERPNext in Version 14. Please install {0} app to continue using it.", [app_link]), "yellow", true); } }); diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js index 1574795dfad..a926a7e52a5 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js @@ -36,6 +36,10 @@ frappe.ui.form.on("Shopify Settings", "refresh", function(frm){ frm.toggle_reqd("delivery_note_series", frm.doc.sync_delivery_note); } + + let app_link = "Ecommerce Integrations" + frm.dashboard.add_comment(__("Shopify Integration will be removed from ERPNext in Version 14. Please install {0} app to continue using it.", [app_link]), "yellow", true); + }) $.extend(erpnext_integrations.shopify_settings, { diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b2597470d41..0012641b663 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -296,3 +296,4 @@ erpnext.patches.v13_0.update_subscription_status_in_memberships erpnext.patches.v13_0.update_export_type_for_gst erpnext.patches.v13_0.update_tds_check_field #3 erpnext.patches.v13_0.update_recipient_email_digest +erpnext.patches.v13_0.shopify_deprecation_warning \ No newline at end of file diff --git a/erpnext/patches/v13_0/shopify_deprecation_warning.py b/erpnext/patches/v13_0/shopify_deprecation_warning.py new file mode 100644 index 00000000000..8b0f1935cfb --- /dev/null +++ b/erpnext/patches/v13_0/shopify_deprecation_warning.py @@ -0,0 +1,15 @@ +import click +import frappe + + +def execute(): + + frappe.reload_doc("erpnext_integrations", "doctype", "shopify_settings") + if not frappe.db.get_single_value("Shopify Settings", "enable_shopify"): + return + + click.secho( + "Shopify Integration is moved to a separate app and will be removed from ERPNext in version-14.\n" + "Please install the app to continue using the integration: https://github.com/frappe/ecommerce_integrations", + fg="yellow", + ) From 8800aaaee7e9928ddc0148aad47c2d50d5f83030 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Tue, 13 Jul 2021 14:58:17 +0530 Subject: [PATCH 207/369] feat: Added fields for dispatch address in Sales Order, Sales Invoice, Delivery Note for Eway Bill --- .../doctype/sales_invoice/sales_invoice.json | 19 +++++++++++++++++- erpnext/regional/india/utils.py | 10 ++++++---- .../doctype/sales_order/sales_order.json | 20 ++++++++++++++++++- .../doctype/delivery_note/delivery_note.json | 19 +++++++++++++++++- 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index e7dd6b8a606..0a9a105b7ca 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -48,6 +48,8 @@ "shipping_address", "company_address", "company_address_display", + "dispatch_address_name", + "dispatch_address", "currency_and_price_list", "currency", "conversion_rate", @@ -1966,6 +1968,21 @@ "fieldname": "disable_rounded_total", "fieldtype": "Check", "label": "Disable Rounded Total" + }, + { + "allow_on_submit": 1, + "fieldname": "dispatch_address_name", + "fieldtype": "Link", + "label": "Dispatch Address Name", + "options": "Address", + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "dispatch_address", + "fieldtype": "Small Text", + "label": "Dispatch Address", + "read_only": 1 } ], "icon": "fa fa-file-text", @@ -1978,7 +1995,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-05-20 22:48:33.988881", + "modified": "2021-07-08 14:03:55.502522", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index a4466e78f28..657fd380b49 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -431,9 +431,11 @@ def get_ewb_data(dt, dn): company_address = frappe.get_doc('Address', doc.company_address) billing_address = frappe.get_doc('Address', doc.customer_address) + #added dispatch address + dispatch_address = frappe.get_doc('Address', doc.dispatch_address_name) shipping_address = frappe.get_doc('Address', doc.shipping_address_name) - data = get_address_details(data, doc, company_address, billing_address) + data = get_address_details(data, doc, company_address, billing_address, dispatch_address) data.itemList = [] data.totalValue = doc.total @@ -519,10 +521,10 @@ def get_gstins_for_company(company): `tabDynamic Link`.link_name = %(company)s""", {"company": company}) return company_gstins -def get_address_details(data, doc, company_address, billing_address): +def get_address_details(data, doc, company_address, billing_address, dispatch_address): data.fromPincode = validate_pincode(company_address.pincode, 'Company Address') - data.fromStateCode = data.actualFromStateCode = validate_state_code( - company_address.gst_state_number, 'Company Address') + data.fromStateCode = validate_state_code(company_address.gst_state_number, 'Company Address') + data.actualFromStateCode = validate_state_code(dispatch_address.gst_state_number, 'Company Address') if not doc.billing_address_gstin or len(doc.billing_address_gstin) < 15: data.toGstin = 'URP' diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 762b6f1d6c9..d31db820abc 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -38,6 +38,8 @@ "col_break46", "shipping_address_name", "shipping_address", + "dispatch_address_name", + "dispatch_address", "customer_group", "territory", "currency_and_price_list", @@ -1486,13 +1488,29 @@ "fieldname": "disable_rounded_total", "fieldtype": "Check", "label": "Disable Rounded Total" + }, + { + "allow_on_submit": 1, + "fieldname": "dispatch_address_name", + "fieldtype": "Link", + "label": "Dispatch Address Name", + "options": "Address", + "print_hide": 1 + }, + { + "allow_on_submit": 1, + "depends_on": "dispatch_address_name", + "fieldname": "dispatch_address", + "fieldtype": "Small Text", + "label": "Dispatch Address", + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2021-04-15 23:55:13.439068", + "modified": "2021-07-08 21:37:44.177493", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index f20e76f5bf6..dbfeb4a10b7 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -32,6 +32,8 @@ "contact_info", "shipping_address_name", "shipping_address", + "dispatch_address_name", + "dispatch_address", "contact_person", "contact_display", "contact_mobile", @@ -1282,13 +1284,28 @@ "fieldname": "disable_rounded_total", "fieldtype": "Check", "label": "Disable Rounded Total" + }, + { + "fieldname": "dispatch_address_name", + "fieldtype": "Link", + "label": "Dispatch Address Name", + "options": "Address", + "print_hide": 1 + }, + { + "depends_on": "dispatch_address_name", + "fieldname": "dispatch_address", + "fieldtype": "Small Text", + "label": "Dispatch Address", + "print_hide": 1, + "read_only": 1 } ], "icon": "fa fa-truck", "idx": 146, "is_submittable": 1, "links": [], - "modified": "2021-06-11 19:27:30.901112", + "modified": "2021-07-08 21:37:20.802652", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", From 6ba11a382a75232166109e7ffe2adefbe6181c66 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 19 Jul 2021 14:37:12 +0530 Subject: [PATCH 208/369] test: Updated test case for Eway bill --- .../sales_invoice/test_sales_invoice.py | 27 +++++++++++++++++++ erpnext/regional/india/utils.py | 4 +-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index dbc7f8632fc..70bccd7166e 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1908,6 +1908,8 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(data['billLists'][0]['sgstValue'], 5400) self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234') self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000) + self.assertEqual(data['billLists'][0]['actualFromStateCode'],7) + self.assertEqual(data['billLists'][0]['fromStateCode'],27) def test_einvoice_submission_without_irn(self): # init @@ -2062,6 +2064,30 @@ def make_test_address_for_ewaybill(): address.save() + if not frappe.db.exists('Address', '_Test Dispatch-Address for Eway bill-Shipping'): + address = frappe.get_doc({ + "address_line1": "_Test Dispatch Address Line 1", + "address_title": "_Test Dispatch-Address for Eway bill", + "address_type": "Shipping", + "city": "_Test City", + "state": "Test State", + "country": "India", + "doctype": "Address", + "is_primary_address": 0, + "phone": "+910000000000", + "gstin": "07AAACC1206D1ZI", + "gst_state": "Delhi", + "gst_state_number": "07", + "pincode": "1100101" + }).insert() + + address.append("links", { + "link_doctype": "Company", + "link_name": "_Test Company" + }) + + address.save() + def make_test_transporter_for_ewaybill(): if not frappe.db.exists('Supplier', '_Test Transporter'): frappe.get_doc({ @@ -2100,6 +2126,7 @@ def make_sales_invoice_for_ewaybill(): si.distance = 2000 si.company_address = "_Test Address for Eway bill-Billing" si.customer_address = "_Test Customer-Address for Eway bill-Shipping" + si.dispatch_address_name = "_Test Dispatch-Address for Eway bill-Shipping" si.vehicle_no = "KA12KA1234" si.gst_category = "Registered Regular" si.mode_of_transport = 'Road' diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 657fd380b49..04db9b3abec 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -432,7 +432,7 @@ def get_ewb_data(dt, dn): billing_address = frappe.get_doc('Address', doc.customer_address) #added dispatch address - dispatch_address = frappe.get_doc('Address', doc.dispatch_address_name) + dispatch_address = frappe.get_doc('Address', doc.dispatch_address_name) if doc.dispatch_address_name else company_address shipping_address = frappe.get_doc('Address', doc.shipping_address_name) data = get_address_details(data, doc, company_address, billing_address, dispatch_address) @@ -524,7 +524,7 @@ def get_gstins_for_company(company): def get_address_details(data, doc, company_address, billing_address, dispatch_address): data.fromPincode = validate_pincode(company_address.pincode, 'Company Address') data.fromStateCode = validate_state_code(company_address.gst_state_number, 'Company Address') - data.actualFromStateCode = validate_state_code(dispatch_address.gst_state_number, 'Company Address') + data.actualFromStateCode = validate_state_code(dispatch_address.gst_state_number, 'Dispatch Address') if not doc.billing_address_gstin or len(doc.billing_address_gstin) < 15: data.toGstin = 'URP' From f99696b75d3819214b3849bdc29d43d16142e4ce Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 2 Aug 2021 23:15:44 +0530 Subject: [PATCH 209/369] fix: Condition for fetching Payment Terms from Sales/Purchase Orders --- erpnext/controllers/accounts_controller.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 913e70b30c5..f475bc2fe2d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1101,16 +1101,16 @@ class AccountsController(TransactionBase): base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total")) if not self.get("payment_schedule"): + if self.doctype in ["Sales Invoice", "Purchase Invoice"] and not self.get("payment_terms_template"): + po_or_so, doctype, fieldname = self.get_order_details() + if self.get("payment_terms_template"): data = get_payment_terms(self.payment_terms_template, posting_date, grand_total, base_grand_total) for item in data: self.append("payment_schedule", item) - elif self.doctype in ["Sales Invoice", "Purchase Invoice"]: - po_or_so, doctype, fieldname = self.get_order_details() - - if self.linked_order_has_payment_terms(po_or_so, fieldname, doctype): - self.fetch_payment_terms_from_order(po_or_so, doctype) + elif self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype): + self.fetch_payment_terms_from_order(po_or_so, doctype) elif self.doctype not in ["Purchase Receipt"]: data = dict(due_date=due_date, invoice_portion=100, payment_amount=grand_total, base_payment_amount=base_grand_total) From 373ed1f65c3b6fc66ae03c829a562cb884c37b6c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 3 Aug 2021 13:28:50 +0530 Subject: [PATCH 210/369] fix: remove limit from stock balance report (#26773) (#26779) (cherry picked from commit b3740e9afc375624dc478c97a97f757e06de084f) Co-authored-by: Ankush --- erpnext/stock/report/stock_balance/stock_balance.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index b6a80631892..9e56ad41306 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -16,8 +16,6 @@ def execute(filters=None): is_reposting_item_valuation_in_progress() if not filters: filters = {} - validate_filters(filters) - from_date = filters.get('from_date') to_date = filters.get('to_date') @@ -295,12 +293,6 @@ def get_item_reorder_details(items): return dict((d.parent + d.warehouse, d) for d in item_reorder_details) -def validate_filters(filters): - if not (filters.get("item_code") or filters.get("warehouse")): - sle_count = flt(frappe.db.sql("""select count(name) from `tabStock Ledger Entry`""")[0][0]) - if sle_count > 500000: - frappe.throw(_("Please set filter based on Item or Warehouse due to a large amount of entries.")) - def get_variants_attributes(): '''Return all item variant attributes.''' return [i.name for i in frappe.get_all('Item Attribute')] From 5a442f1bce2de9eaccd82f39aca86118680c3b57 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 3 Aug 2021 13:29:10 +0530 Subject: [PATCH 211/369] fix: change format string to percent string interpolation (#26774) (#26778) (cherry picked from commit 7fe588e236051b9e03cd1b1934fa0a88379716b7) Co-authored-by: Alan <2.alan.tom@gmail.com> --- .../patches/v13_0/add_missing_fg_item_for_stock_entry.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py index d7ad1fc6962..0d8109c41ad 100644 --- a/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py +++ b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py @@ -30,19 +30,20 @@ def execute(): return repost_stock_entries = [] + stock_entries = frappe.db.sql_list(''' SELECT se.name FROM `tabStock Entry` se WHERE - se.purpose = 'Manufacture' and se.docstatus < 2 and se.work_order in {work_orders} + se.purpose = 'Manufacture' and se.docstatus < 2 and se.work_order in %s and not exists( select name from `tabStock Entry Detail` sed where sed.parent = se.name and sed.is_finished_item = 1 ) - Order BY + ORDER BY se.posting_date, se.posting_time - '''.format(work_orders=tuple(work_orders))) + ''', (work_orders,)) if stock_entries: print('Length of stock entries', len(stock_entries)) From 85815f989c6ab5fd5a89bf4d5ea71ffdb8c4eb0d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 3 Aug 2021 20:06:07 +0530 Subject: [PATCH 212/369] fix: Reset weight_per_unit on replacing Item (#26619) (#26791) * fix: Assign Item's default weight_per_unit as its weight_per_unit in get_item_details * fix: Set weight_uom in get_item_details as Item's default weight_uom (cherry picked from commit 471f48f64db0f8da9d4703f4b82b6f9517fbacae) Co-authored-by: Ganga Manoj --- erpnext/stock/get_item_details.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index cf52803fca8..2ed7a04ba80 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -312,8 +312,8 @@ def get_basic_details(args, item, overwrite_warehouse=True): "transaction_date": args.get("transaction_date"), "against_blanket_order": args.get("against_blanket_order"), "bom_no": item.get("default_bom"), - "weight_per_unit": args.get("weight_per_unit") or item.get("weight_per_unit"), - "weight_uom": args.get("weight_uom") or item.get("weight_uom") + "weight_per_unit": item.get("weight_per_unit"), + "weight_uom": item.get("weight_uom") }) if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"): From 1b9a5c851d372ea2dd5119a0d8e95a79d583bbf5 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 3 Aug 2021 20:06:36 +0530 Subject: [PATCH 213/369] fix: incorrect amount in work order required items table. (#26585) (#26623) * fix: amount in work order not equal to rate * qty * fix: patch for amount in work order required items (cherry picked from commit cd12d95a246bcf5c49eda78fe8ce4fa9c90e6b76) Co-authored-by: Ankush --- erpnext/manufacturing/doctype/bom/bom.py | 2 +- erpnext/manufacturing/doctype/work_order/work_order.py | 2 +- erpnext/patches.txt | 1 + .../v13_0/update_amt_in_work_order_required_items.py | 10 ++++++++++ 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v13_0/update_amt_in_work_order_required_items.py diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index ebd9ae2dc54..4e93fc67997 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -774,7 +774,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite item.image, bom.project, bom_item.rate, - bom_item.amount, + sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * bom_item.rate * %(qty)s as amount, item.stock_uom, item.item_group, item.allow_alternative_item, diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 69812c7452c..282b5d0afe4 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -655,7 +655,7 @@ class WorkOrder(Document): for item in sorted(item_dict.values(), key=lambda d: d['idx'] or 9999): self.append('required_items', { 'rate': item.rate, - 'amount': item.amount, + 'amount': item.rate * item.qty, 'operation': item.operation or operation, 'item_code': item.item_code, 'item_name': item.item_name, diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 0012641b663..ae01496f02a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -293,6 +293,7 @@ erpnext.patches.v13_0.update_job_card_details erpnext.patches.v13_0.update_level_in_bom #1234sswef erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry erpnext.patches.v13_0.update_subscription_status_in_memberships +erpnext.patches.v13_0.update_amt_in_work_order_required_items erpnext.patches.v13_0.update_export_type_for_gst erpnext.patches.v13_0.update_tds_check_field #3 erpnext.patches.v13_0.update_recipient_email_digest diff --git a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py new file mode 100644 index 00000000000..eae5ff60b90 --- /dev/null +++ b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py @@ -0,0 +1,10 @@ +import frappe + +def execute(): + """ Correct amount in child table of required items table.""" + + frappe.reload_doc("manufacturing", "doctype", "work_order") + frappe.reload_doc("manufacturing", "doctype", "work_order_item") + + frappe.db.sql("""UPDATE `tabWork Order Item` SET amount = rate * required_qty""") + From 9f94c19752dbca0f72d497e47b751ee5104e1e24 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Wed, 4 Aug 2021 10:01:44 +0530 Subject: [PATCH 214/369] fix: ignore permission to update call log (#26797) Backport of #26112 #no-docs --- erpnext/telephony/doctype/call_log/call_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index 4d553df08b8..c00dfa90566 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -142,7 +142,7 @@ def link_existing_conversations(doc, state): for log in logs: call_log = frappe.get_doc('Call Log', log) call_log.add_link(link_type=doc.doctype, link_name=doc.name) - call_log.save() + call_log.save(ignore_permissions=True) frappe.db.commit() except Exception: frappe.log_error(title=_('Error during caller information update')) From df477dcae6de059c7a05bcf16f81867469cd1ff1 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 4 Aug 2021 14:02:43 +0530 Subject: [PATCH 215/369] fix: bank remittance report issue (#26398) (#26766) --- erpnext/payroll/report/bank_remittance/bank_remittance.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.py b/erpnext/payroll/report/bank_remittance/bank_remittance.py index 500543ceb02..05a5366a5c1 100644 --- a/erpnext/payroll/report/bank_remittance/bank_remittance.py +++ b/erpnext/payroll/report/bank_remittance/bank_remittance.py @@ -95,6 +95,7 @@ def execute(filters=None): "amount": salary.net_pay, } data.append(row) + return columns, data def get_bank_accounts(): @@ -116,7 +117,7 @@ def get_payroll_entries(accounts, filters): entries = get_all("Payroll Entry", payroll_filter, ["name", "payment_account"]) payment_accounts = [d.payment_account for d in entries] - set_company_account(payment_accounts, entries) + entries = set_company_account(payment_accounts, entries) return entries def get_salary_slips(payroll_entries): From 13192e1db1606c2db289360d6e0de1c535875bc3 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 4 Aug 2021 17:07:22 +0530 Subject: [PATCH 216/369] fix: trigger lost reason dialog when status is changed to lost (#26811) (#26812) (cherry picked from commit e5d8ba65ca1c982ff4a8db0352e81939ae64665f) Co-authored-by: Mohammed Yusuf Shaikh <49878143+mohammedyusufshaikh@users.noreply.github.com> --- erpnext/crm/doctype/opportunity/opportunity.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index ac374a95f4e..089a63fc1cd 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -53,6 +53,13 @@ frappe.ui.form.on("Opportunity", { frm.get_field("items").grid.set_multiple_add("item_code", "qty"); }, + status:function(frm){ + if (frm.doc.status == "Lost"){ + frm.trigger('set_as_lost_dialog'); + } + + }, + customer_address: function(frm, cdt, cdn) { erpnext.utils.get_address_display(frm, 'customer_address', 'address_display', false); }, @@ -91,11 +98,6 @@ frappe.ui.form.on("Opportunity", { frm.add_custom_button(__('Quotation'), cur_frm.cscript.create_quotation, __('Create')); - if(doc.status!=="Quotation") { - frm.add_custom_button(__('Lost'), () => { - frm.trigger('set_as_lost_dialog'); - }); - } } if(!frm.doc.__islocal && frm.perm[0].write && frm.doc.docstatus==0) { From 8328f4523021e2c187fa6ec17beae20ffe500f04 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 4 Aug 2021 03:15:13 +0530 Subject: [PATCH 217/369] fix: Rename test to reflect changes in code --- erpnext/buying/doctype/purchase_order/test_purchase_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 474c9cf3df9..d7db27cb54c 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -632,7 +632,7 @@ class TestPurchaseOrder(unittest.TestCase): else: raise Exception - def test_terms_does_not_copy(self): + def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked(self): po = create_purchase_order() self.assertTrue(po.get('payment_schedule')) From 8fced95f8cad1a3e1d2561f2beb800b416b535ba Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Mon, 28 Jun 2021 16:50:20 +0530 Subject: [PATCH 218/369] feat: over transfer allowance for material transfers --- .../doctype/material_request/material_request.py | 9 ++++++++- .../stock/doctype/stock_settings/stock_settings.json | 11 +++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 3ad9909ad07..026b85e26d2 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -162,8 +162,15 @@ class MaterialRequest(BuyingController): from `tabStock Entry Detail` where material_request = %s and material_request_item = %s and docstatus = 1""", (self.name, d.name))[0][0]) + mr_qty_allowance = frappe.db.get_single_value('Stock Settings', 'mr_qty_allowance') - if d.ordered_qty and d.ordered_qty > d.stock_qty: + if mr_qty_allowance: + allowed_qty = d.qty + (d.qty * (mr_qty_allowance/100)) + if d.ordered_qty and d.ordered_qty > allowed_qty: + frappe.throw(_("The total Issue / Transfer quantity {0} in Material Request {1} \ + cannot be greater than allowed requested quantity {2} for Item {3}").format(d.ordered_qty, d.parent, allowed_qty, d.item_code)) + + elif d.ordered_qty and d.ordered_qty > d.stock_qty: frappe.throw(_("The total Issue / Transfer quantity {0} in Material Request {1} \ cannot be greater than requested quantity {2} for Item {3}").format(d.ordered_qty, d.parent, d.qty, d.item_code)) diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index 2a9dcfb67ed..f75cb561385 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -18,6 +18,7 @@ "section_break_9", "over_delivery_receipt_allowance", "role_allowed_to_over_deliver_receive", + "mr_qty_allowance", "column_break_12", "auto_insert_price_list_rate_if_missing", "allow_negative_stock", @@ -283,6 +284,12 @@ "fieldtype": "Select", "label": "Action If Quality Inspection Is Rejected", "options": "Stop\nWarn" + }, + { + "description": "The percentage you are allowed to transfer more against the quantity ordered. For example, if you have ordered 100 units, and your Allowance is 10%, then you are allowed transfer 110 units.", + "fieldname": "mr_qty_allowance", + "fieldtype": "Float", + "label": "Over Transfer Allowance" } ], "icon": "icon-cog", @@ -290,7 +297,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-07-10 16:17:42.159829", + "modified": "2021-06-28 17:02:26.683002", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", @@ -310,4 +317,4 @@ "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 -} \ No newline at end of file +} From 673bc58193354adf0973b474ff3ff9072918d279 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Wed, 30 Jun 2021 20:16:50 +0530 Subject: [PATCH 219/369] test: test case for over transfer of materials --- .../material_request/test_material_request.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 72a3a5e67c7..b4776ba2492 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -329,6 +329,58 @@ class TestMaterialRequest(unittest.TestCase): self.assertEqual(current_requested_qty_item1, existing_requested_qty_item1 + 54.0) self.assertEqual(current_requested_qty_item2, existing_requested_qty_item2 + 3.0) + def test_over_transfer_qty_allowance(self): + mr = frappe.new_doc('Material Request') + mr.company = "_Test Company" + mr.scheduled_date = today() + mr.append('items',{ + "item_code": "_Test FG Item", + "item_name": "_Test FG Item", + "qty": 10, + "schedule_date": today(), + "uom": "_Test UOM 1", + "warehouse": "_Test Warehouse - _TC" + }) + + mr.material_request_type = "Material Transfer" + mr.insert() + mr.submit() + + frappe.db.set_value('Stock Settings', None, 'mr_qty_allowance', 20) + + # map a stock entry + + se_doc = make_stock_entry(mr.name) + se_doc.update({ + "posting_date": today(), + "posting_time": "00:00", + }) + se_doc.get("items")[0].update({ + "qty": 13, + "transfer_qty": 12.0, + "s_warehouse": "_Test Warehouse - _TC", + "t_warehouse": "_Test Warehouse 1 - _TC", + "basic_rate": 1.0 + }) + + # make available the qty in _Test Warehouse 1 before transfer + sr = frappe.new_doc("Stock Reconciliation") + sr.company = "_Test Company" + sr.purpose = "Opening Stock" + sr.append('items', { + "item_code": "_Test FG Item", + "warehouse": "_Test Warehouse - _TC", + "qty": 20, + "valuation_rate": 0.01 + }) + sr.insert() + sr.submit() + se = frappe.copy_doc(se_doc) + se.insert() + self.assertRaises(frappe.ValidationError) + se.items[0].qty = 12 + se.submit() + def test_completed_qty_for_over_transfer(self): existing_requested_qty_item1 = self._get_requested_qty("_Test Item Home Desktop 100", "_Test Warehouse - _TC") existing_requested_qty_item2 = self._get_requested_qty("_Test Item Home Desktop 200", "_Test Warehouse - _TC") From bf8d0c256df51dd4495f8a0bbf80b8ce3b5253ab Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 4 Aug 2021 22:01:17 +0530 Subject: [PATCH 220/369] fix: typo in error message (#26816) (#26817) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 005291e6dd4088fca23fa36b85f1755510939823) Co-authored-by: François de Ryckel --- erpnext/accounts/doctype/account/account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 1be2fbf5c81..f763df0852b 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -230,7 +230,7 @@ class Account(NestedSet): if self.check_gle_exists(): throw(_("Account with existing transaction can not be converted to group.")) elif self.account_type and not self.flags.exclude_account_type_check: - throw(_("Cannot covert to Group because Account Type is selected.")) + throw(_("Cannot convert to Group because Account Type is selected.")) else: self.is_group = 1 self.save() From 23c104555b33d35d381543adce7d6bf15be91460 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 00:28:42 +0530 Subject: [PATCH 221/369] fix: Remove irrelevant code --- .../purchase_invoice/purchase_invoice.py | 30 ------------------- .../purchase_receipt/test_purchase_receipt.py | 27 ----------------- 2 files changed, 57 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 28a9bddc420..f7992797ed4 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1142,36 +1142,6 @@ class PurchaseInvoice(BuyingController): if update: self.db_set('status', self.status, update_modified = update_modified) -# to get details of purchase invoice/receipt from which this doc was created for exchange rate difference handling -def get_purchase_document_details(doc): - if doc.doctype == 'Purchase Invoice': - doc_reference = 'purchase_receipt' - items_reference = 'pr_detail' - parent_doctype = 'Purchase Receipt' - child_doctype = 'Purchase Receipt Item' - else: - doc_reference = 'purchase_invoice' - items_reference = 'purchase_invoice_item' - parent_doctype = 'Purchase Invoice' - child_doctype = 'Purchase Invoice Item' - - purchase_receipts_or_invoices = [] - items = [] - - for item in doc.get('items'): - if item.get(doc_reference): - purchase_receipts_or_invoices.append(item.get(doc_reference)) - if item.get(items_reference): - items.append(item.get(items_reference)) - - exchange_rate_map = frappe._dict(frappe.get_all(parent_doctype, filters={'name': ('in', - purchase_receipts_or_invoices)}, fields=['name', 'conversion_rate'], as_list=1)) - - net_rate_map = frappe._dict(frappe.get_all(child_doctype, filters={'name': ('in', - items)}, fields=['name', 'net_rate'], as_list=1)) - - return exchange_rate_map, net_rate_map - def get_list_context(context=None): from erpnext.controllers.website_list_for_contact import get_list_context list_context = get_list_context(context) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 9a5064fcc71..fd6ac16fa31 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1052,33 +1052,6 @@ class TestPurchaseReceipt(unittest.TestCase): frappe.db.set_value('Company', company, 'enable_perpetual_inventory_for_non_stock_items', before_test_value) - def test_purchase_receipt_with_exchange_rate_difference(self): - from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice as create_purchase_invoice - from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_purchase_receipt as create_purchase_receipt - - pi = create_purchase_invoice(company="_Test Company with perpetual inventory", - cost_center = "Main - TCP1", - warehouse = "Stores - TCP1", - expense_account ="_Test Account Cost for Goods Sold - TCP1", - currency = "USD", conversion_rate = 70) - - pr = create_purchase_receipt(pi.name) - pr.conversion_rate = 80 - pr.items[0].purchase_invoice = pi.name - pr.items[0].purchase_invoice_item = pi.items[0].name - - pr.save() - pr.submit() - - # Get exchnage gain and loss account - exchange_gain_loss_account = frappe.db.get_value('Company', pr.company, 'exchange_gain_loss_account') - - # fetching the latest GL Entry with exchange gain and loss account account - amount = frappe.db.get_value('GL Entry', {'account': exchange_gain_loss_account, 'voucher_no': pr.name}, 'credit') - discrepancy_caused_by_exchange_rate_diff = abs(pi.items[0].base_net_amount - pr.items[0].base_net_amount) - - self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount) - def test_payment_terms_are_fetched_when_creating_purchase_invoice(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice From 0b11420147066cd11616e474554dd474171e9078 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 00:35:45 +0530 Subject: [PATCH 222/369] fix: Disable automcatically_fetch_payment_terms after running its associated tests --- erpnext/buying/doctype/purchase_order/test_purchase_order.py | 2 ++ erpnext/selling/doctype/sales_order/test_sales_order.py | 2 ++ erpnext/stock/doctype/delivery_note/test_delivery_note.py | 2 ++ erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py | 2 ++ 4 files changed, 8 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index d7db27cb54c..0db54e42068 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -988,6 +988,8 @@ class TestPurchaseOrder(unittest.TestCase): # self.assertEqual(po.payment_terms_template, pi.payment_terms_template) compare_payment_schedules(self, po, pi) + automatically_fetch_payment_terms(enable=0) + def make_pr_against_po(po, received_qty=0): pr = make_purchase_receipt(po) pr.get("items")[0].qty = received_qty or 5 diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index f4a089bcef2..5639ee8069e 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1248,6 +1248,8 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.payment_terms_template, si.payment_terms_template) compare_payment_schedules(self, so, si) + automatically_fetch_payment_terms(enable=0) + def automatically_fetch_payment_terms(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") accounts_settings.automatically_fetch_payment_terms = enable diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index ca8d8b99d31..756825e826d 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -785,6 +785,8 @@ class TestDeliveryNote(unittest.TestCase): self.assertEqual(so.payment_terms_template, si.payment_terms_template) compare_payment_schedules(self, so, si) + automatically_fetch_payment_terms(enable=0) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") args = frappe._dict(args) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index fd6ac16fa31..34ea3257d57 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1077,6 +1077,8 @@ class TestPurchaseReceipt(unittest.TestCase): # self.assertEqual(po.payment_terms_template, pi.payment_terms_template) compare_payment_schedules(self, po, pi) + automatically_fetch_payment_terms(enable=0) + def get_sl_entries(voucher_type, voucher_no): return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s From 4a6ef9ab0f5d02cf86286e905a93261530be5d86 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 00:52:55 +0530 Subject: [PATCH 223/369] fix: Compare Payment Schedules --- .../doctype/sales_order/test_sales_order.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 5639ee8069e..a226da75cd8 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -5,7 +5,7 @@ import json import unittest import frappe import frappe.permissions -from frappe.utils import flt, add_days, nowdate +from frappe.utils import flt, add_days, nowdate, getdate from frappe.core.doctype.user_permission.test_user_permission import create_user from erpnext.selling.doctype.sales_order.sales_order \ import make_material_request, make_delivery_note, make_sales_invoice, WarehouseRequired @@ -1256,17 +1256,11 @@ def automatically_fetch_payment_terms(enable=1): accounts_settings.save() def compare_payment_schedules(doc, doc1, doc2): - payment_schedule1 = frappe.db.sql("""select payment_term, description, due_date, mode_of_payment, invoice_portion, payment_amount - from `tabPayment Schedule` - where parenttype=%s and parent=%s - order by payment_term asc""", (doc1.doctype, doc1.name), as_dict=1) - - payment_schedule2 = frappe.db.sql("""select payment_term, description, due_date, mode_of_payment, invoice_portion, payment_amount - from `tabPayment Schedule` - where parenttype=%s and parent=%s - order by payment_term asc""", (doc2.doctype, doc2.name), as_dict=1) - - doc.assertEqual(payment_schedule1, payment_schedule2) + for index, schedule in enumerate(doc1.get('payment_schedule')): + doc.assertEqual(schedule.payment_term, doc2.payment_schedule[index].payment_term) + doc.assertEqual(getdate(schedule.due_date), doc2.payment_schedule[index].due_date) + doc.assertEqual(schedule.invoice_portion, doc2.payment_schedule[index].invoice_portion) + doc.assertEqual(schedule.payment_amount, doc2.payment_schedule[index].payment_amount) def make_sales_order(**args): so = frappe.new_doc("Sales Order") From 5b5a365aafead7d9d26fee0591765da41b85e581 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 5 Aug 2021 11:15:16 +0530 Subject: [PATCH 224/369] fix: POS payment modes displayed wrong total (#26808) --- erpnext/selling/page/point_of_sale/pos_payment.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index f1a166b5230..63306adc6fa 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -198,6 +198,7 @@ erpnext.PointOfSale.Payment = class { const is_cash_shortcuts_invisible = !this.$payment_modes.find('.cash-shortcuts').is(':visible'); this.attach_cash_shortcuts(frm.doc); !is_cash_shortcuts_invisible && this.$payment_modes.find('.cash-shortcuts').css('display', 'grid'); + this.render_payment_mode_dom(); }); frappe.ui.form.on('POS Invoice', 'loyalty_amount', (frm) => { From cf4078756dea4ac073af278e86510b2f94cee08b Mon Sep 17 00:00:00 2001 From: noahjacob Date: Tue, 25 May 2021 16:40:39 +0530 Subject: [PATCH 225/369] fix: fixed fetching sales order of item variant in production plan --- .../production_plan/production_plan.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 6a024f275aa..99b69263e05 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -118,16 +118,20 @@ class ProductionPlan(Document): item_condition = "" if self.item_code: + if frappe.db.exists('Item', self.item_code): + is_variant = frappe.db.get_value('Item', self.item_code, ['variant_of']) + if is_variant: + variant_of = is_variant item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code)) - + items = frappe.db.sql("""select distinct parent, item_code, warehouse, (qty - work_order_qty) * conversion_factor as pending_qty, description, name from `tabSales Order Item` so_item where parent in (%s) and docstatus = 1 and qty > work_order_qty - and exists (select name from `tabBOM` bom where bom.item=so_item.item_code + and exists (select name from `tabBOM` bom where bom.item= "%s" and bom.is_active = 1) %s""" % \ - (", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1) - + (", ".join(["%s"] * len(so_list)), variant_of or "so_items.item_code", item_condition), tuple(so_list), as_dict=1) + if self.item_code: item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code)) @@ -695,6 +699,10 @@ def get_sales_orders(self): so_filter += "and so.status = %(sales_order_status)s" if self.item_code: + if frappe.db.exists('Item', self.item_code): + is_variant = frappe.db.get_value('Item', self.item_code, ['variant_of']) + if is_variant: + variant_of = is_variant item_filter += " and so_item.item_code = %(item)s" open_so = frappe.db.sql(""" @@ -704,7 +712,7 @@ def get_sales_orders(self): and so.docstatus = 1 and so.status not in ("Stopped", "Closed") and so.company = %(company)s and so_item.qty > so_item.work_order_qty {0} {1} - and (exists (select name from `tabBOM` bom where bom.item=so_item.item_code + and (exists (select name from `tabBOM` bom where bom.item=%(item_code)s and bom.is_active = 1) or exists (select name from `tabPacked Item` pi where pi.parent = so.name and pi.parent_item = so_item.item_code @@ -715,6 +723,7 @@ def get_sales_orders(self): "to_date": self.to_date, "customer": self.customer, "project": self.project, + "item_code": variant_of or "so_item.item_code", "item": self.item_code, "company": self.company, "sales_order_status": self.sales_order_status From 9b0b2daf4a049991dd47e93fa1202189c676110b Mon Sep 17 00:00:00 2001 From: noahjacob Date: Wed, 26 May 2021 17:52:08 +0530 Subject: [PATCH 226/369] refactor: updated sql query for item variants --- .../production_plan/production_plan.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 99b69263e05..efb2d9235fd 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -116,21 +116,21 @@ class ProductionPlan(Document): so_list = self.get_so_mr_list("sales_order", "sales_orders") - item_condition = "" + item_condition = variant_of_bom = "" if self.item_code: if frappe.db.exists('Item', self.item_code): - is_variant = frappe.db.get_value('Item', self.item_code, ['variant_of']) - if is_variant: - variant_of = is_variant + has_variant_bom = frappe.db.exists({'doctype': 'BOM', 'item': self.item_code}) + if not has_variant_bom: + variant_of_bom = "'%s'" % frappe.db.get_value('Item', self.item_code, ['variant_of']) item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code)) - + items = frappe.db.sql("""select distinct parent, item_code, warehouse, (qty - work_order_qty) * conversion_factor as pending_qty, description, name from `tabSales Order Item` so_item where parent in (%s) and docstatus = 1 and qty > work_order_qty - and exists (select name from `tabBOM` bom where bom.item= "%s" + and exists (select name from `tabBOM` bom where bom.item= %s and bom.is_active = 1) %s""" % \ - (", ".join(["%s"] * len(so_list)), variant_of or "so_items.item_code", item_condition), tuple(so_list), as_dict=1) + (", ".join(["%s"] * len(so_list)), variant_of_bom or "so_item.item_code", item_condition), tuple(so_list), as_dict=1) if self.item_code: item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code)) @@ -686,7 +686,7 @@ def get_material_request_items(row, sales_order, company, } def get_sales_orders(self): - so_filter = item_filter = "" + so_filter = item_filter = variant_of_bom = "" if self.from_date: so_filter += " and so.transaction_date >= %(from_date)s" if self.to_date: @@ -700,9 +700,9 @@ def get_sales_orders(self): if self.item_code: if frappe.db.exists('Item', self.item_code): - is_variant = frappe.db.get_value('Item', self.item_code, ['variant_of']) - if is_variant: - variant_of = is_variant + has_variant_bom = frappe.db.exists({'doctype': 'BOM', 'item': self.item_code}) + if not has_variant_bom: + variant_of_bom = "'%s'" % frappe.db.get_value('Item', self.item_code, ['variant_of']) item_filter += " and so_item.item_code = %(item)s" open_so = frappe.db.sql(""" @@ -712,18 +712,17 @@ def get_sales_orders(self): and so.docstatus = 1 and so.status not in ("Stopped", "Closed") and so.company = %(company)s and so_item.qty > so_item.work_order_qty {0} {1} - and (exists (select name from `tabBOM` bom where bom.item=%(item_code)s + and (exists (select name from `tabBOM` bom where bom.item= {2} and bom.is_active = 1) or exists (select name from `tabPacked Item` pi where pi.parent = so.name and pi.parent_item = so_item.item_code and exists (select name from `tabBOM` bom where bom.item=pi.item_code and bom.is_active = 1))) - """.format(so_filter, item_filter), { + """.format(so_filter, item_filter, variant_of_bom or "so_item.item_code"), { "from_date": self.from_date, "to_date": self.to_date, "customer": self.customer, "project": self.project, - "item_code": variant_of or "so_item.item_code", "item": self.item_code, "company": self.company, "sales_order_status": self.sales_order_status From b10465eebe4ef6839ad6b9c12bc0536c3abb7a91 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 3 Jun 2021 17:57:23 +0530 Subject: [PATCH 227/369] refactor: created function to get bom_item for query --- .../production_plan/production_plan.py | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index efb2d9235fd..c2a365adb1d 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -109,6 +109,15 @@ class ProductionPlan(Document): so_mr_list = [d.get(field) for d in self.get(table) if d.get(field)] return so_mr_list + def get_bom_item(self): + """Check if Item or if its Template has a BOM.""" + bom_item = None + has_bom = frappe.db.exists({'doctype': 'BOM', 'item': self.item_code, 'docstatus': 1}) + if not has_bom: + template_item = frappe.db.get_value('Item', self.item_code, ['variant_of']) + bom_item = "bom.item = {0}".format(frappe.db.escape(template_item)) if template_item else bom_item + return bom_item + def get_so_items(self): # Check for empty table or empty rows if not self.get("sales_orders") or not self.get_so_mr_list("sales_order", "sales_orders"): @@ -116,21 +125,20 @@ class ProductionPlan(Document): so_list = self.get_so_mr_list("sales_order", "sales_orders") - item_condition = variant_of_bom = "" - if self.item_code: - if frappe.db.exists('Item', self.item_code): - has_variant_bom = frappe.db.exists({'doctype': 'BOM', 'item': self.item_code}) - if not has_variant_bom: - variant_of_bom = "'%s'" % frappe.db.get_value('Item', self.item_code, ['variant_of']) + item_condition = "" + bom_item = "bom.item = so_item.item_code" + if self.item_code and frappe.db.exists('Item', self.item_code): + bom_item = self.get_bom_item() or bom_item item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code)) items = frappe.db.sql("""select distinct parent, item_code, warehouse, - (qty - work_order_qty) * conversion_factor as pending_qty, description, name - from `tabSales Order Item` so_item - where parent in (%s) and docstatus = 1 and qty > work_order_qty - and exists (select name from `tabBOM` bom where bom.item= %s - and bom.is_active = 1) %s""" % \ - (", ".join(["%s"] * len(so_list)), variant_of_bom or "so_item.item_code", item_condition), tuple(so_list), as_dict=1) + (qty - work_order_qty) * conversion_factor as pending_qty, description, name + from `tabSales Order Item` so_item + where + parent in (%s) and docstatus = 1 and qty > work_order_qty + and exists (select name from `tabBOM` bom where %s + and bom.is_active = 1) %s""" % \ + (", ".join(["%s"] * len(so_list)), bom_item, item_condition), tuple(so_list), as_dict=1) if self.item_code: item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code)) @@ -686,7 +694,8 @@ def get_material_request_items(row, sales_order, company, } def get_sales_orders(self): - so_filter = item_filter = variant_of_bom = "" + so_filter = item_filter = "" + bom_item = "bom.item = so_item.item_code" if self.from_date: so_filter += " and so.transaction_date >= %(from_date)s" if self.to_date: @@ -698,11 +707,8 @@ def get_sales_orders(self): if self.sales_order_status: so_filter += "and so.status = %(sales_order_status)s" - if self.item_code: - if frappe.db.exists('Item', self.item_code): - has_variant_bom = frappe.db.exists({'doctype': 'BOM', 'item': self.item_code}) - if not has_variant_bom: - variant_of_bom = "'%s'" % frappe.db.get_value('Item', self.item_code, ['variant_of']) + if self.item_code and frappe.db.exists('Item', self.item_code): + bom_item = self.get_bom_item() or bom_item item_filter += " and so_item.item_code = %(item)s" open_so = frappe.db.sql(""" @@ -712,13 +718,13 @@ def get_sales_orders(self): and so.docstatus = 1 and so.status not in ("Stopped", "Closed") and so.company = %(company)s and so_item.qty > so_item.work_order_qty {0} {1} - and (exists (select name from `tabBOM` bom where bom.item= {2} + and (exists (select name from `tabBOM` bom where {2} and bom.is_active = 1) or exists (select name from `tabPacked Item` pi where pi.parent = so.name and pi.parent_item = so_item.item_code and exists (select name from `tabBOM` bom where bom.item=pi.item_code and bom.is_active = 1))) - """.format(so_filter, item_filter, variant_of_bom or "so_item.item_code"), { + """.format(so_filter, item_filter, bom_item), { "from_date": self.from_date, "to_date": self.to_date, "customer": self.customer, From 041ac339b1ba0ac04724b888fad6748b7a2f47b3 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Sun, 6 Jun 2021 18:08:39 +0530 Subject: [PATCH 228/369] style: improved formatting of sql query --- .../production_plan/production_plan.py | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index c2a365adb1d..8c27d6ccc96 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -131,15 +131,22 @@ class ProductionPlan(Document): bom_item = self.get_bom_item() or bom_item item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code)) - items = frappe.db.sql("""select distinct parent, item_code, warehouse, - (qty - work_order_qty) * conversion_factor as pending_qty, description, name - from `tabSales Order Item` so_item - where + items = frappe.db.sql(""" + select + distinct parent, item_code, warehouse, + (qty - work_order_qty) * conversion_factor as pending_qty, + description, name + from + `tabSales Order Item` so_item + where parent in (%s) and docstatus = 1 and qty > work_order_qty and exists (select name from `tabBOM` bom where %s - and bom.is_active = 1) %s""" % \ - (", ".join(["%s"] * len(so_list)), bom_item, item_condition), tuple(so_list), as_dict=1) - + and bom.is_active = 1) %s""" % + (", ".join(["%s"] * len(so_list)), + bom_item, + item_condition), + tuple(so_list), as_dict=1) + if self.item_code: item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code)) From 6e07ec26173ae3e18f155fcf6013bd01b035c074 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 5 Aug 2021 20:13:28 +0530 Subject: [PATCH 229/369] fix: Do not fetch fully return issued purchase receipts (#26809) (#26825) (cherry picked from commit 1d90f7684e0fce8227fb566fa4110b96003d9ee5) Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index dc9094c3e91..c588d45a9fd 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -134,7 +134,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ }, get_query_filters: { docstatus: 1, - status: ["not in", ["Closed", "Completed"]], + status: ["not in", ["Closed", "Completed", "Return Issued"]], company: me.frm.doc.company, is_return: 0 } From e4a07d8ff349907129eccd1fdad48efa4429900d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 21:42:09 +0530 Subject: [PATCH 230/369] fix: Stop fetching amount while fetching Payment Terms --- erpnext/controllers/accounts_controller.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index f475bc2fe2d..7f389cc32a3 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1175,10 +1175,7 @@ class AccountsController(TransactionBase): 'due_date': schedule.due_date, 'invoice_portion': schedule.invoice_portion, 'discount_type': schedule.discount_type, - 'discount': schedule.discount, - 'base_payment_amount': schedule.base_payment_amount, - 'payment_amount': schedule.payment_amount, - 'outstanding': schedule.outstanding + 'discount': schedule.discount } self.append("payment_schedule", payment_schedule) From 5c21eea13d865103ee4d62f4922ae49b76382c4f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 21:50:09 +0530 Subject: [PATCH 231/369] fix: Fetch discount details from Payment Terms only if Discount Type = Percentage --- erpnext/controllers/accounts_controller.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 7f389cc32a3..78e4ad31add 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1174,9 +1174,14 @@ class AccountsController(TransactionBase): 'payment_term': schedule.payment_term, 'due_date': schedule.due_date, 'invoice_portion': schedule.invoice_portion, - 'discount_type': schedule.discount_type, - 'discount': schedule.discount + 'mode_of_payment': schedule.mode_of_payment, + 'description': schedule.description } + + if schedule.discount_type == 'Percentage': + payment_schedule['discount_type'] = schedule.discount_type + payment_schedule['discount'] = schedule.discount + self.append("payment_schedule", payment_schedule) def set_due_date(self): From fb80ca9e06ae57dbb61e1a3907b2cfc19b1fd925 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 22:04:11 +0530 Subject: [PATCH 232/369] fix: Only fetch default Payment Terms Template if present --- erpnext/accounts/party.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index b97dc401e6a..19a394f7439 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals +from re import template import frappe, erpnext from frappe import _, msgprint, scrub @@ -59,7 +60,10 @@ def _get_party_details(party=None, account=None, party_type="Customer", company= billing_address=party_address, shipping_address=shipping_address) if fetch_payment_terms_template: - party_details["payment_terms_template"] = get_payment_terms_template(party.name, party_type, company) + payment_terms_template = get_payment_terms_template(party.name, party_type, company) + + if payment_terms_template: + party_details["payment_terms_template"] = payment_terms_template if not party_details.get("currency"): party_details["currency"] = currency From 232c7286364220e1286d593605e9401b35573486 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 23:04:58 +0530 Subject: [PATCH 233/369] Revert "fix: Only fetch default Payment Terms Template if present" This reverts commit fb80ca9e06ae57dbb61e1a3907b2cfc19b1fd925. --- erpnext/accounts/party.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 19a394f7439..b97dc401e6a 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -2,7 +2,6 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -from re import template import frappe, erpnext from frappe import _, msgprint, scrub @@ -60,10 +59,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company= billing_address=party_address, shipping_address=shipping_address) if fetch_payment_terms_template: - payment_terms_template = get_payment_terms_template(party.name, party_type, company) - - if payment_terms_template: - party_details["payment_terms_template"] = payment_terms_template + party_details["payment_terms_template"] = get_payment_terms_template(party.name, party_type, company) if not party_details.get("currency"): party_details["currency"] = currency From b13e46071a3b2118611557560ae626036ceb3b21 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 6 Aug 2021 10:39:58 +0530 Subject: [PATCH 234/369] fix: Let all System Managers be able to delete Company transactions (#26815) (#26819) (cherry picked from commit 884d8cf0653794af68b377e2f02ac60968a7ead1) Co-authored-by: Ganga Manoj --- .../transaction_deletion_record.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json index 9313f955167..23e59472a6d 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json @@ -54,7 +54,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-08 23:13:48.049879", + "modified": "2021-08-04 20:15:59.071493", "modified_by": "Administrator", "module": "Setup", "name": "Transaction Deletion Record", @@ -70,6 +70,7 @@ "report": 1, "role": "System Manager", "share": 1, + "submit": 1, "write": 1 } ], From 6871c076852d460e02158c06fc9e0f09f8c4dfaa Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 6 Aug 2021 10:53:38 +0530 Subject: [PATCH 235/369] test: Failing budget test due to project naming --- erpnext/accounts/doctype/budget/test_budget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py index 603e21ea248..6c25f0024d5 100644 --- a/erpnext/accounts/doctype/budget/test_budget.py +++ b/erpnext/accounts/doctype/budget/test_budget.py @@ -249,7 +249,7 @@ class TestBudget(unittest.TestCase): def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None): if budget_against_field == "project": - budget_against = "_Test Project" + budget_against = frappe.db.get_value("Project", {"project_name": "_Test Project"}) else: budget_against = budget_against_CC or "_Test Cost Center - _TC" @@ -275,7 +275,7 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True) elif budget_against_field == "project": make_journal_entry("_Test Account Cost for Goods Sold - _TC", - "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date=nowdate()) + "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project=budget_against, posting_date=nowdate()) def make_budget(**args): args = frappe._dict(args) From 2e352834a223a05f31528e57c530a318aa3ff6e6 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 6 Aug 2021 10:59:29 +0530 Subject: [PATCH 236/369] fix: fetching of item tax from hsn code (#26736) (#26792) * fix: fetching of item tax from hsn code (cherry picked from commit 3a50490c04398c30663cab77f722153ec0b220e1) Co-authored-by: Saqib --- erpnext/hooks.py | 3 ++- erpnext/regional/india/utils.py | 13 ++++++++++++- erpnext/stock/doctype/item/item.js | 14 -------------- erpnext/stock/doctype/item/item.py | 5 +++++ erpnext/stock/doctype/item/regional/india.js | 15 +++++++++++++++ 5 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 erpnext/stock/doctype/item/regional/india.js diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 1ba752a1464..e6b6cc4a8a0 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -430,7 +430,8 @@ regional_overrides = { 'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption', 'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period', 'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields', - 'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount' + 'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount', + 'erpnext.stock.doctype.item.item.set_item_tax_from_hsn_code': 'erpnext.regional.india.utils.set_item_tax_from_hsn_code' }, 'United Arab Emirates': { 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data', diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 04db9b3abec..ac195a6c762 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -851,4 +851,15 @@ def get_depreciation_amount(asset, depreciable_value, row): depreciation_amount = flt(depreciable_value * (flt(rate_of_depreciation) / 100)) - return depreciation_amount \ No newline at end of file + return depreciation_amount + +def set_item_tax_from_hsn_code(item): + if not item.taxes and item.gst_hsn_code: + hsn_doc = frappe.get_doc("GST HSN Code", item.gst_hsn_code) + + for tax in hsn_doc.taxes: + item.append('taxes', { + 'item_tax_template': tax.item_tax_template, + 'tax_category': tax.tax_category, + 'valid_from': tax.valid_from + }) \ No newline at end of file diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 264baeaa472..3ca98738cb9 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -138,20 +138,6 @@ frappe.ui.form.on("Item", { frm.toggle_reqd('customer', frm.doc.is_customer_provided_item ? 1:0); }, - gst_hsn_code: function(frm) { - if((!frm.doc.taxes || !frm.doc.taxes.length) && frm.doc.gst_hsn_code) { - frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc => { - $.each(hsn_doc.taxes || [], function(i, tax) { - let a = frappe.model.add_child(cur_frm.doc, 'Item Tax', 'taxes'); - a.item_tax_template = tax.item_tax_template; - a.tax_category = tax.tax_category; - a.valid_from = tax.valid_from; - frm.refresh_field('taxes'); - }); - }); - } - }, - is_fixed_asset: function(frm) { // set serial no to false & toggles its visibility frm.set_value('has_serial_no', 0); diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index fbd30cf6c78..9bf4dbf2e85 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -123,6 +123,7 @@ class Item(WebsiteGenerator): self.cant_change() self.update_show_in_website() self.validate_item_tax_net_rate_range() + set_item_tax_from_hsn_code(self) if not self.is_new(): self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") @@ -1305,3 +1306,7 @@ def update_variants(variants, template, publish_progress=True): def on_doctype_update(): # since route is a Text column, it needs a length for indexing frappe.db.add_index("Item", ["route(500)"]) + +@erpnext.allow_regional +def set_item_tax_from_hsn_code(item): + pass \ No newline at end of file diff --git a/erpnext/stock/doctype/item/regional/india.js b/erpnext/stock/doctype/item/regional/india.js new file mode 100644 index 00000000000..77ae51fa341 --- /dev/null +++ b/erpnext/stock/doctype/item/regional/india.js @@ -0,0 +1,15 @@ +frappe.ui.form.on('Item', { + gst_hsn_code: function(frm) { + if ((!frm.doc.taxes || !frm.doc.taxes.length) && frm.doc.gst_hsn_code) { + frappe.db.get_doc("GST HSN Code", frm.doc.gst_hsn_code).then(hsn_doc => { + $.each(hsn_doc.taxes || [], function(i, tax) { + let a = frappe.model.add_child(cur_frm.doc, 'Item Tax', 'taxes'); + a.item_tax_template = tax.item_tax_template; + a.tax_category = tax.tax_category; + a.valid_from = tax.valid_from; + frm.refresh_field('taxes'); + }); + }); + } + }, +}); \ No newline at end of file From cb44aed78b3e8a427bd8d1387664e9c3037745ce Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 5 Aug 2021 16:11:36 +0530 Subject: [PATCH 237/369] test: get sales order with variant --- .../production_plan/test_production_plan.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 93e6d7a97f4..af8de8ee0e3 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -11,6 +11,7 @@ from erpnext.manufacturing.doctype.production_plan.production_plan import get_sa from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests, get_warehouse_list +from erpnext.controllers.item_variant import create_variant class TestProductionPlan(unittest.TestCase): def setUp(self): @@ -271,6 +272,60 @@ class TestProductionPlan(unittest.TestCase): self.assertEqual(warehouses, expected_warehouses) + def test_get_sales_order_with_variant(self): + if not frappe.db.exists('Item', {"item_code": 'PIV'}): + item = create_item('PIV', valuation_rate = 100) + variant_settings = { + "attributes": [ + { + "attribute": "Colour" + }, + ], + "has_variants": 1 + } + item.update(variant_settings) + item.save() + parent_bom = make_bom(item = 'PIV', raw_materials = ['PIV']) + if not frappe.db.exists('BOM', {"item": 'PIV'}): + parent_bom = make_bom(item = 'PIV', raw_materials = ['PIV']) + else: + parent_bom = frappe.get_doc('BOM', {"item": 'PIV'}) + + if not frappe.db.exists('Item', {"item_code": 'PIV-RED'}): + variant = create_variant("PIV", {"Colour": "Red"}) + variant.save() + variant_bom = make_bom(item = variant.item_code, raw_materials = [variant.item_code]) + else: + variant = frappe.get_doc('Item', 'PIV-RED') + if not frappe.db.exists('BOM', {"item": 'PIV-RED'}): + variant_bom = make_bom(item = variant.item_code, raw_materials = [variant.item_code]) + + """Testing when item variant has a BOM""" + so = make_sales_order(item_code="PIV-RED", qty=5) + pln = frappe.new_doc('Production Plan') + pln.company = so.company + pln.get_items_from = 'Sales Order' + pln.item_code = 'PIV-RED' + pln.get_open_sales_orders() + self.assertEqual(pln.sales_orders[0].sales_order, so.name) + pln.get_so_items() + self.assertEqual(pln.po_items[0].item_code, 'PIV-RED') + self.assertEqual(pln.po_items[0].bom_no, variant_bom.name) + so.cancel() + frappe.delete_doc('Sales Order', so.name) + variant_bom.cancel() + frappe.delete_doc('BOM', variant_bom.name) + + """Testing when item variant doesn't have a BOM""" + so = make_sales_order(item_code="PIV-RED", qty=5) + pln.get_open_sales_orders() + self.assertEqual(pln.sales_orders[0].sales_order, so.name) + pln.po_items = [] + pln.get_so_items() + self.assertEqual(pln.po_items[0].item_code, 'PIV-RED') + self.assertEqual(pln.po_items[0].bom_no, parent_bom.name) + + frappe.db.rollback() def create_production_plan(**args): args = frappe._dict(args) From c8e6c070328d11e2c8db68c366c53f703f53388a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 6 Aug 2021 12:33:18 +0530 Subject: [PATCH 238/369] fix(e-invoicing): cannot generate IRNs for standalone credit notes (#26824) (#26833) --- erpnext/regional/india/e_invoice/utils.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index cd85f6b026f..e65442dbff4 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -316,10 +316,6 @@ def get_payment_details(invoice): )) def get_return_doc_reference(invoice): - if not invoice.return_against: - frappe.throw(_('For generating IRN, reference to the original invoice is mandatory for a credit note. Please set {} field to generate e-invoice.') - .format(frappe.bold('Return Against')), title=_('Missing Field')) - invoice_date = frappe.db.get_value('Sales Invoice', invoice.return_against, 'posting_date') return frappe._dict(dict( invoice_name=invoice.return_against, invoice_date=format_date(invoice_date, 'dd/mm/yyyy') @@ -435,7 +431,7 @@ def make_einvoice(invoice): if invoice.is_pos and invoice.base_paid_amount: payment_details = get_payment_details(invoice) - if invoice.is_return: + if invoice.is_return and invoice.return_against: prev_doc_details = get_return_doc_reference(invoice) if invoice.transporter and not invoice.is_return: From 9ea24db20a215e637ef497d1f4594474f8adde6a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 6 Aug 2021 21:14:40 +0530 Subject: [PATCH 239/369] test: use item that allows fractional UOM in test (#26837) (#26838) (cherry picked from commit 614336fe1d2f2d136809e29d394800994e7ae4c9) Co-authored-by: Ankush --- .../stock/doctype/purchase_receipt/test_purchase_receipt.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 2eb8bfd5d2f..ca6e61fe6b0 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -23,9 +23,7 @@ class TestPurchaseReceipt(unittest.TestCase): def test_reverse_purchase_receipt_sle(self): - frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 0) - - pr = make_purchase_receipt(qty=0.5) + pr = make_purchase_receipt(qty=0.5, item_code="_Test Item Home Desktop 200") sl_entry = frappe.db.get_all("Stock Ledger Entry", {"voucher_type": "Purchase Receipt", "voucher_no": pr.name}, ['actual_qty']) @@ -41,8 +39,6 @@ class TestPurchaseReceipt(unittest.TestCase): self.assertEqual(len(sl_entry_cancelled), 2) self.assertEqual(sl_entry_cancelled[1].actual_qty, -0.5) - frappe.db.set_value('UOM', '_Test UOM', 'must_be_whole_number', 1) - def test_make_purchase_invoice(self): if not frappe.db.exists('Payment Terms Template', '_Test Payment Terms Template For Purchase Invoice'): frappe.get_doc({ From aec784640796a73261abf6d0511483a1b7031de9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 6 Aug 2021 21:55:01 +0530 Subject: [PATCH 240/369] test: fix pricelist tests (#26839) (#26840) problem: exchange rate API is returning exchange rates for "_Test currency". These tests were relying on failure of that function. (cherry picked from commit 27a29eb6bc67fc8dce482c0b38239929cf3fc6fb) Co-authored-by: Ankush --- erpnext/stock/doctype/batch/test_batch.py | 9 ++++++--- erpnext/stock/doctype/item/test_item.py | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py index cbd272df4b8..a85a0222b55 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -269,11 +269,14 @@ class TestBatch(unittest.TestCase): batch2 = create_batch('_Test Batch Price Item', 300, 1) batch3 = create_batch('_Test Batch Price Item', 400, 0) + company = "_Test Company with perpetual inventory" + currency = frappe.get_cached_value("Company", company, "default_currency") + args = frappe._dict({ "item_code": "_Test Batch Price Item", - "company": "_Test Company with perpetual inventory", + "company": company, "price_list": "_Test Price List", - "currency": "_Test Currency", + "currency": currency, "doctype": "Sales Invoice", "conversion_rate": 1, "price_list_currency": "_Test Currency", @@ -333,4 +336,4 @@ def make_new_batch(**args): except frappe.DuplicateEntryError: batch = frappe.get_doc("Batch", args.batch_id) - return batch \ No newline at end of file + return batch diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index c7467a5a0f5..9ec44d2e2e4 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -83,14 +83,17 @@ class TestItem(unittest.TestCase): make_test_objects("Item Price") + company = "_Test Company" + currency = frappe.get_cached_value("Company", company, "default_currency") + details = get_item_details({ "item_code": "_Test Item", - "company": "_Test Company", + "company": company, "price_list": "_Test Price List", - "currency": "_Test Currency", + "currency": currency, "doctype": "Sales Order", "conversion_rate": 1, - "price_list_currency": "_Test Currency", + "price_list_currency": currency, "plc_conversion_rate": 1, "order_type": "Sales", "customer": "_Test Customer", From 0bba425fe300305c7d55a543dd42f98549d9b021 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 6 Aug 2021 23:53:16 +0530 Subject: [PATCH 241/369] fix: Ignore default payment term templates when coping payment terms from orders --- .../purchase_invoice/purchase_invoice.js | 5 +- .../purchase_invoice/purchase_invoice.json | 635 +++++++++++---- .../doctype/sales_invoice/sales_invoice.json | 731 +++++++++++++----- erpnext/accounts/party.py | 4 +- .../doctype/purchase_order/purchase_order.py | 7 +- erpnext/controllers/accounts_controller.py | 31 +- erpnext/controllers/buying_controller.py | 3 +- erpnext/public/js/utils/party.js | 1 + .../purchase_receipt/purchase_receipt.py | 5 +- 9 files changed, 1060 insertions(+), 362 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index dc9094c3e91..299531d9f39 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -275,7 +275,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ // Do not update if inter company reference is there as the details will already be updated if(this.frm.updating_party_details || this.frm.doc.inter_company_invoice_reference) return; - + erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details", { posting_date: this.frm.doc.posting_date, @@ -283,7 +283,8 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ party: this.frm.doc.supplier, party_type: "Supplier", account: this.frm.doc.credit_to, - price_list: this.frm.doc.buying_price_list + price_list: this.frm.doc.buying_price_list, + fetch_payment_terms_template: cint(!this.frm.doc.ignore_default_payment_terms_template) }, function() { me.apply_pricing_rule(); me.frm.doc.apply_tds = me.frm.supplier_tds ? 1 : 0; diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 00ef7d5c184..f1bf595a394 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -131,6 +131,7 @@ "advances", "payment_schedule_section", "payment_terms_template", + "ignore_default_payment_terms_template", "payment_schedule", "terms_section_break", "tc_name", @@ -175,7 +176,9 @@ "hidden": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "naming_series", @@ -187,7 +190,9 @@ "options": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier", @@ -199,7 +204,9 @@ "options": "Supplier", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -211,7 +218,9 @@ "label": "Supplier Name", "oldfieldname": "supplier_name", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "supplier.tax_id", @@ -219,21 +228,27 @@ "fieldtype": "Read Only", "label": "Tax Id", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "due_date", "fieldtype": "Date", "label": "Due Date", "oldfieldname": "due_date", - "oldfieldtype": "Date" + "oldfieldtype": "Date", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_paid", "fieldtype": "Check", "label": "Is Paid", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -241,19 +256,25 @@ "fieldtype": "Check", "label": "Is Return (Debit Note)", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "apply_tds", "fieldtype": "Check", "label": "Apply Tax Withholding Amount", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -263,13 +284,17 @@ "label": "Company", "options": "Company", "print_hide": 1, - "remember_last_selected_value": 1 + "remember_last_selected_value": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cost_center", "fieldtype": "Link", "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "default": "Today", @@ -281,7 +306,9 @@ "oldfieldtype": "Date", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "posting_time", @@ -290,6 +317,8 @@ "no_copy": 1, "print_hide": 1, "print_width": "100px", + "show_days": 1, + "show_seconds": 1, "width": "100px" }, { @@ -298,7 +327,9 @@ "fieldname": "set_posting_time", "fieldtype": "Check", "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -310,44 +341,58 @@ "oldfieldtype": "Link", "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:doc.on_hold", "fieldname": "sb_14", "fieldtype": "Section Break", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "on_hold", "fieldtype": "Check", - "label": "Hold Invoice" + "label": "Hold Invoice", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "description": "Once set, this invoice will be on hold till the set date", "fieldname": "release_date", "fieldtype": "Date", - "label": "Release Date" + "label": "Release Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cb_17", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.on_hold", "fieldname": "hold_comment", "fieldtype": "Small Text", - "label": "Reason For Putting On Hold" + "label": "Reason For Putting On Hold", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "bill_no", "fieldname": "supplier_invoice_details", "fieldtype": "Section Break", - "label": "Supplier Invoice Details" + "label": "Supplier Invoice Details", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_no", @@ -355,11 +400,15 @@ "label": "Supplier Invoice No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_15", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "bill_date", @@ -368,13 +417,17 @@ "no_copy": 1, "oldfieldname": "bill_date", "oldfieldtype": "Date", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", "fieldname": "returns", "fieldtype": "Section Break", - "label": "Returns" + "label": "Returns", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "return_against", @@ -384,26 +437,34 @@ "no_copy": 1, "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "supplier_address", "fieldtype": "Link", "label": "Select Supplier Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", "fieldtype": "Small Text", "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", @@ -411,51 +472,67 @@ "in_global_search": 1, "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", "fieldtype": "Small Text", "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", "fieldtype": "Small Text", "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", "fieldtype": "Small Text", "label": "Contact Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_address", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", "fieldtype": "Link", "label": "Select Shipping Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_display", "fieldtype": "Small Text", "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "currency_and_price_list", "fieldtype": "Section Break", "label": "Currency and Price List", - "options": "fa fa-tag" + "options": "fa fa-tag", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -464,7 +541,9 @@ "oldfieldname": "currency", "oldfieldtype": "Select", "options": "Currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "conversion_rate", @@ -473,18 +552,24 @@ "oldfieldname": "conversion_rate", "oldfieldtype": "Currency", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "buying_price_list", "fieldtype": "Link", "label": "Price List", "options": "Price List", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -492,14 +577,18 @@ "label": "Price List Currency", "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "plc_conversion_rate", "fieldtype": "Float", "label": "Price List Exchange Rate", "precision": "9", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -508,11 +597,15 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -521,7 +614,9 @@ "fieldtype": "Link", "label": "Set Accepted Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -531,11 +626,15 @@ "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break_warehouse", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -543,25 +642,33 @@ "fieldtype": "Select", "label": "Raw Materials Supplied", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "items_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "update_stock", "fieldtype": "Check", "label": "Update Stock", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -571,25 +678,33 @@ "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Purchase Invoice Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", "fieldtype": "Table", "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible_depends_on": "supplied_items", "fieldname": "raw_materials_supplied", "fieldtype": "Section Break", - "label": "Raw Materials Supplied" + "label": "Raw Materials Supplied", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -597,17 +712,23 @@ "fieldtype": "Table", "label": "Supplied Items", "no_copy": 1, - "options": "Purchase Receipt Item Supplied" + "options": "Purchase Receipt Item Supplied", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_26", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", "fieldtype": "Float", "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -615,7 +736,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -625,18 +748,24 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_28", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", "fieldtype": "Currency", "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -646,42 +775,56 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_49", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", "fieldtype": "Link", "label": "Shipping Rule", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_51", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -690,7 +833,9 @@ "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -698,13 +843,17 @@ "label": "Purchase Taxes and Charges", "oldfieldname": "purchase_tax_details", "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges" + "options": "Purchase Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "sec_tax_breakup", "fieldtype": "Section Break", - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -713,13 +862,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", "fieldtype": "Section Break", "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_added", @@ -729,7 +882,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_taxes_and_charges_deducted", @@ -739,7 +894,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -749,11 +906,15 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_40", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_added", @@ -763,7 +924,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges_deducted", @@ -773,7 +936,9 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -781,14 +946,18 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -796,7 +965,9 @@ "fieldtype": "Select", "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -804,28 +975,38 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_46", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", "fieldtype": "Float", "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", "fieldtype": "Currency", "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_49", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -835,7 +1016,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -845,7 +1028,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -855,7 +1040,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_in_words", @@ -865,13 +1052,17 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break8", "fieldtype": "Column Break", "oldfieldtype": "Column Break", "print_hide": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -882,7 +1073,9 @@ "oldfieldname": "grand_total_import", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -892,7 +1085,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -902,7 +1097,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -912,7 +1109,9 @@ "oldfieldname": "in_words_import", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_advance", @@ -923,7 +1122,9 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "outstanding_amount", @@ -934,14 +1135,18 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "depends_on": "grand_total", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -949,20 +1154,26 @@ "depends_on": "eval:doc.is_paid===1||(doc.advances && doc.advances.length>0)", "fieldname": "payments_section", "fieldtype": "Section Break", - "label": "Payments" + "label": "Payments", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "mode_of_payment", "fieldtype": "Link", "label": "Mode of Payment", "options": "Mode of Payment", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cash_bank_account", "fieldtype": "Link", "label": "Cash/Bank Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "clearance_date", @@ -970,11 +1181,15 @@ "label": "Clearance Date", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_br_payments", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_paid", @@ -983,7 +1198,9 @@ "label": "Paid Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_paid_amount", @@ -992,7 +1209,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1000,7 +1219,9 @@ "depends_on": "grand_total", "fieldname": "write_off", "fieldtype": "Section Break", - "label": "Write Off" + "label": "Write Off", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "write_off_amount", @@ -1008,7 +1229,9 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_write_off_amount", @@ -1017,11 +1240,15 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_61", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1029,7 +1256,9 @@ "fieldtype": "Link", "label": "Write Off Account", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:flt(doc.write_off_amount)!=0", @@ -1037,7 +1266,9 @@ "fieldtype": "Link", "label": "Write Off Cost Center", "options": "Cost Center", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1047,13 +1278,17 @@ "label": "Advance Payments", "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "allocate_advances_automatically", "fieldtype": "Check", - "label": "Set Advances and Allocate (FIFO)" + "label": "Set Advances and Allocate (FIFO)", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.allocate_advances_automatically", @@ -1061,7 +1296,9 @@ "fieldtype": "Button", "label": "Get Advances Paid", "oldfieldtype": "Button", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advances", @@ -1071,20 +1308,26 @@ "oldfieldname": "advance_allocation_details", "oldfieldtype": "Table", "options": "Purchase Invoice Advance", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "collapsible_depends_on": "eval:(!doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_terms_template", "fieldtype": "Link", "label": "Payment Terms Template", - "options": "Payment Terms Template" + "options": "Payment Terms Template", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "payment_schedule", @@ -1092,7 +1335,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1100,25 +1345,33 @@ "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", - "options": "fa fa-legal" + "options": "fa fa-legal", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", "fieldtype": "Link", "label": "Terms", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", "fieldtype": "Text Editor", - "label": "Terms and Conditions1" + "label": "Terms and Conditions1", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1126,7 +1379,9 @@ "fieldtype": "Link", "label": "Letter Head", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1134,11 +1389,15 @@ "fieldname": "group_same_items", "fieldtype": "Check", "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_112", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1150,14 +1409,18 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", "fieldtype": "Data", "label": "Print Language", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1166,7 +1429,9 @@ "label": "More Information", "oldfieldtype": "Section Break", "options": "fa fa-file-text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "credit_to", @@ -1177,7 +1442,9 @@ "options": "Account", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -1187,7 +1454,9 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -1197,7 +1466,9 @@ "oldfieldname": "is_opening", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "against_expense_account", @@ -1207,11 +1478,15 @@ "no_copy": 1, "oldfieldname": "against_expense_account", "oldfieldtype": "Small Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_63", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "Draft", @@ -1220,7 +1495,9 @@ "in_standard_filter": 1, "label": "Status", "options": "\nDraft\nReturn\nDebit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled\nInternal Transfer", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_invoice_reference", @@ -1229,7 +1506,9 @@ "no_copy": 1, "options": "Sales Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", @@ -1238,14 +1517,18 @@ "no_copy": 1, "oldfieldname": "remarks", "oldfieldtype": "Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", "label": "Subscription Section", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1254,7 +1537,9 @@ "fieldtype": "Date", "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1263,11 +1548,15 @@ "fieldtype": "Date", "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_114", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "auto_repeat", @@ -1276,24 +1565,32 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, "depends_on": "eval: doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", - "label": "Accounting Dimensions " + "label": "Accounting Dimensions ", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "dimension_col_break", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1301,7 +1598,9 @@ "fieldname": "is_internal_supplier", "fieldtype": "Check", "label": "Is Internal Supplier", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_withholding_category", @@ -1309,25 +1608,33 @@ "hidden": 1, "label": "Tax Withholding Category", "options": "Tax Withholding Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "billing_address", "fieldtype": "Link", "label": "Select Billing Address", - "options": "Address" + "options": "Address", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "billing_address_display", "fieldtype": "Small Text", "label": "Billing Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "project", "fieldtype": "Link", "label": "Project", - "options": "Project" + "options": "Project", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_internal_supplier", @@ -1335,7 +1642,9 @@ "fieldname": "unrealized_profit_loss_account", "fieldtype": "Link", "label": "Unrealized Profit / Loss Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_internal_supplier", @@ -1344,7 +1653,9 @@ "fieldname": "represents_company", "fieldtype": "Link", "label": "Represents Company", - "options": "Company" + "options": "Company", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.update_stock && doc.is_internal_supplier", @@ -1356,6 +1667,8 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { @@ -1367,6 +1680,8 @@ "options": "Warehouse", "print_hide": 1, "print_width": "50px", + "show_days": 1, + "show_seconds": 1, "width": "50px" }, { @@ -1376,14 +1691,26 @@ "label": "Per Received", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "fieldname": "ignore_default_payment_terms_template", + "fieldtype": "Check", + "hidden": 1, + "label": "Ignore Default Payment Terms Template", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2021-06-15 18:20:56.806195", + "modified": "2021-08-07 17:53:14.351439", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index e7dd6b8a606..23e8091761c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -125,6 +125,7 @@ "get_advances", "advances", "payment_schedule_section", + "ignore_default_payment_terms_template", "payment_terms_template", "payment_schedule", "payments_section", @@ -195,7 +196,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "options": "fa fa-user" + "options": "fa fa-user", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -207,7 +210,9 @@ "hide_seconds": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -222,7 +227,9 @@ "options": "ACC-SINV-.YYYY.-\nACC-SINV-RET-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1 + "set_only_once": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -236,7 +243,9 @@ "oldfieldtype": "Link", "options": "Customer", "print_hide": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -250,7 +259,9 @@ "label": "Customer Name", "oldfieldname": "customer_name", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_id", @@ -259,7 +270,9 @@ "hide_seconds": 1, "label": "Tax Id", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "project", @@ -271,7 +284,9 @@ "oldfieldname": "project_name", "oldfieldtype": "Link", "options": "Project", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -282,7 +297,9 @@ "label": "Include Payment (POS)", "oldfieldname": "is_pos", "oldfieldtype": "Check", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_pos", @@ -292,7 +309,9 @@ "hide_seconds": 1, "label": "POS Profile", "options": "POS Profile", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -302,14 +321,18 @@ "hide_seconds": 1, "label": "Is Return (Credit Note)", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "hide_days": 1, "hide_seconds": 1, - "oldfieldtype": "Column Break" + "oldfieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company", @@ -323,7 +346,9 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cost_center", @@ -331,7 +356,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -345,7 +372,9 @@ "oldfieldname": "posting_date", "oldfieldtype": "Date", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "posting_time", @@ -356,7 +385,9 @@ "no_copy": 1, "oldfieldname": "posting_time", "oldfieldtype": "Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -366,7 +397,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Edit Posting Date and Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "due_date", @@ -376,7 +409,9 @@ "label": "Payment Due Date", "no_copy": 1, "oldfieldname": "due_date", - "oldfieldtype": "Date" + "oldfieldtype": "Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -390,7 +425,9 @@ "oldfieldtype": "Link", "options": "Sales Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.return_against || doc.is_debit_note", @@ -403,7 +440,9 @@ "options": "Sales Invoice", "print_hide": 1, "read_only_depends_on": "eval:doc.is_return", - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -412,7 +451,9 @@ "fieldtype": "Check", "hide_days": 1, "hide_seconds": 1, - "label": "Update Billed Amount in Sales Order" + "label": "Update Billed Amount in Sales Order", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -421,7 +462,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Customer PO Details" + "label": "Customer PO Details", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -431,13 +474,17 @@ "hide_seconds": 1, "label": "Customer's Purchase Order", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_23", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -445,7 +492,9 @@ "fieldtype": "Date", "hide_days": 1, "hide_seconds": 1, - "label": "Customer's Purchase Order Date" + "label": "Customer's Purchase Order Date", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -453,7 +502,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Address and Contact" + "label": "Address and Contact", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_address", @@ -462,7 +513,9 @@ "hide_seconds": 1, "label": "Customer Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", @@ -470,7 +523,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", @@ -480,7 +535,9 @@ "in_global_search": 1, "label": "Contact Person", "options": "Contact", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_display", @@ -488,7 +545,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", @@ -497,7 +556,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Mobile No", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_email", @@ -508,7 +569,9 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "territory", @@ -517,13 +580,17 @@ "hide_seconds": 1, "label": "Territory", "options": "Territory", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break4", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address_name", @@ -532,7 +599,9 @@ "hide_seconds": 1, "label": "Shipping Address Name", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_address", @@ -541,7 +610,9 @@ "hide_seconds": 1, "label": "Shipping Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company_address", @@ -550,7 +621,9 @@ "hide_seconds": 1, "label": "Company Address Name", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "company_address_display", @@ -560,7 +633,9 @@ "hide_seconds": 1, "label": "Company Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -569,7 +644,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Currency and Price List" + "label": "Currency and Price List", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "currency", @@ -581,7 +658,9 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Rate at which Customer Currency is converted to customer's base currency", @@ -594,13 +673,17 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break2", "fieldtype": "Column Break", "hide_days": 1, "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -613,7 +696,9 @@ "oldfieldtype": "Select", "options": "Price List", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -624,7 +709,9 @@ "options": "Currency", "print_hide": 1, "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "Rate at which Price list currency is converted to customer's base currency", @@ -635,7 +722,9 @@ "label": "Price List Exchange Rate", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -646,14 +735,18 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sec_warehouse", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Warehouse" + "label": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "update_stock", @@ -663,7 +756,9 @@ "hide_seconds": 1, "label": "Source Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "items_section", @@ -672,7 +767,9 @@ "hide_seconds": 1, "label": "Items", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart" + "options": "fa fa-shopping-cart", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -683,14 +780,18 @@ "label": "Update Stock", "oldfieldname": "update_stock", "oldfieldtype": "Check", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", "hide_days": 1, "hide_seconds": 1, - "label": "Scan Barcode" + "label": "Scan Barcode", + "show_days": 1, + "show_seconds": 1 }, { "allow_bulk_edit": 1, @@ -702,14 +803,18 @@ "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Sales Invoice Item", - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Pricing Rules" + "label": "Pricing Rules", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "pricing_rules", @@ -718,7 +823,9 @@ "hide_seconds": 1, "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "packing_list", @@ -727,7 +834,9 @@ "hide_seconds": 1, "label": "Packing List", "options": "fa fa-suitcase", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "packed_items", @@ -736,7 +845,9 @@ "hide_seconds": 1, "label": "Packed Items", "options": "Packed Item", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "product_bundle_help", @@ -744,7 +855,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Product Bundle Help", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -754,7 +867,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Time Sheet List" + "label": "Time Sheet List", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "timesheets", @@ -763,7 +878,9 @@ "hide_seconds": 1, "label": "Time Sheets", "options": "Sales Invoice Timesheet", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -774,13 +891,17 @@ "label": "Total Billing Amount", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_30", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_qty", @@ -788,7 +909,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Total Quantity", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total", @@ -798,7 +921,9 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_net_total", @@ -811,13 +936,17 @@ "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_32", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total", @@ -826,7 +955,9 @@ "hide_seconds": 1, "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -836,7 +967,9 @@ "label": "Net Total", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_net_weight", @@ -845,7 +978,9 @@ "hide_seconds": 1, "label": "Total Net Weight", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_section", @@ -853,7 +988,9 @@ "hide_days": 1, "hide_seconds": 1, "oldfieldtype": "Section Break", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes_and_charges", @@ -864,13 +1001,17 @@ "oldfieldname": "charge", "oldfieldtype": "Link", "options": "Sales Taxes and Charges Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_38", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "shipping_rule", @@ -880,7 +1021,9 @@ "label": "Shipping Rule", "oldfieldtype": "Button", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", @@ -889,13 +1032,17 @@ "hide_seconds": 1, "label": "Tax Category", "options": "Tax Category", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_40", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "taxes", @@ -905,7 +1052,9 @@ "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", - "options": "Sales Taxes and Charges" + "options": "Sales Taxes and Charges", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -913,7 +1062,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Tax Breakup" + "label": "Tax Breakup", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "other_charges_calculation", @@ -924,13 +1075,17 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_43", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -942,13 +1097,17 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_47", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -958,7 +1117,9 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -966,7 +1127,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Loyalty Points Redemption" + "label": "Loyalty Points Redemption", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "redeem_loyalty_points", @@ -976,7 +1139,9 @@ "hide_seconds": 1, "label": "Loyalty Points", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "redeem_loyalty_points", @@ -988,7 +1153,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -998,13 +1165,17 @@ "hide_seconds": 1, "label": "Redeem Loyalty Points", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_77", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "customer.loyalty_program", @@ -1016,7 +1187,9 @@ "no_copy": 1, "options": "Loyalty Program", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "redeem_loyalty_points", @@ -1026,7 +1199,9 @@ "hide_seconds": 1, "label": "Redemption Account", "no_copy": 1, - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "redeem_loyalty_points", @@ -1036,7 +1211,9 @@ "hide_seconds": 1, "label": "Redemption Cost Center", "no_copy": 1, - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1045,7 +1222,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Additional Discount" + "label": "Additional Discount", + "show_days": 1, + "show_seconds": 1 }, { "default": "Grand Total", @@ -1055,7 +1234,9 @@ "hide_seconds": 1, "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_discount_amount", @@ -1065,13 +1246,17 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_51", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "additional_discount_percentage", @@ -1079,7 +1264,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Additional Discount Percentage", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "discount_amount", @@ -1088,7 +1275,9 @@ "hide_seconds": 1, "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", @@ -1097,7 +1286,9 @@ "hide_seconds": 1, "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_grand_total", @@ -1110,7 +1301,9 @@ "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -1122,7 +1315,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -1135,7 +1330,9 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "description": "In Words will be visible once you save the Sales Invoice.", @@ -1148,7 +1345,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break5", @@ -1157,6 +1356,8 @@ "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1171,7 +1372,9 @@ "oldfieldtype": "Currency", "options": "currency", "read_only": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -1183,7 +1386,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -1196,7 +1401,9 @@ "oldfieldname": "rounded_total_export", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -1208,7 +1415,9 @@ "oldfieldname": "in_words_export", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_advance", @@ -1220,7 +1429,9 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "outstanding_amount", @@ -1233,7 +1444,9 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1245,7 +1458,9 @@ "label": "Advance Payments", "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1253,7 +1468,9 @@ "fieldtype": "Check", "hide_days": 1, "hide_seconds": 1, - "label": "Allocate Advances Automatically (FIFO)" + "label": "Allocate Advances Automatically (FIFO)", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:!doc.allocate_advances_automatically", @@ -1262,7 +1479,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Get Advances Received", - "options": "set_advances" + "options": "set_advances", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "advances", @@ -1273,7 +1492,9 @@ "oldfieldname": "advance_adjustment_details", "oldfieldtype": "Table", "options": "Sales Invoice Advance", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1282,7 +1503,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Payment Terms" + "label": "Payment Terms", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:(!doc.is_pos && !doc.is_return)", @@ -1293,7 +1516,9 @@ "label": "Payment Terms Template", "no_copy": 1, "options": "Payment Terms Template", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:(!doc.is_pos && !doc.is_return)", @@ -1304,7 +1529,9 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)", @@ -1313,7 +1540,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Payments", - "options": "fa fa-money" + "options": "fa fa-money", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_pos", @@ -1326,7 +1555,9 @@ "oldfieldname": "cash_bank_account", "oldfieldtype": "Link", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_pos===1", @@ -1336,13 +1567,17 @@ "hide_seconds": 1, "label": "Sales Invoice Payment", "options": "Sales Invoice Payment", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_84", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_paid_amount", @@ -1353,13 +1588,17 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_86", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points", @@ -1373,13 +1612,17 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "section_break_88", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_pos", @@ -1391,13 +1634,17 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_90", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_pos", @@ -1408,7 +1655,9 @@ "label": "Change Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "is_pos", @@ -1418,7 +1667,9 @@ "hide_seconds": 1, "label": "Account for Change Amount", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1429,6 +1680,8 @@ "hide_days": 1, "hide_seconds": 1, "label": "Write Off", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1439,7 +1692,9 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "base_write_off_amount", @@ -1450,7 +1705,9 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1460,13 +1717,17 @@ "hide_days": 1, "hide_seconds": 1, "label": "Write Off Outstanding Amount", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_74", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "write_off_account", @@ -1475,7 +1736,9 @@ "hide_seconds": 1, "label": "Write Off Account", "options": "Account", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "write_off_cost_center", @@ -1484,7 +1747,9 @@ "hide_seconds": 1, "label": "Write Off Cost Center", "options": "Cost Center", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1494,7 +1759,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Terms and Conditions", - "oldfieldtype": "Section Break" + "oldfieldtype": "Section Break", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tc_name", @@ -1505,7 +1772,9 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", @@ -1514,7 +1783,9 @@ "hide_seconds": 1, "label": "Terms and Conditions Details", "oldfieldname": "terms", - "oldfieldtype": "Text Editor" + "oldfieldtype": "Text Editor", + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1522,7 +1793,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Printing Settings" + "label": "Printing Settings", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1534,7 +1807,9 @@ "oldfieldname": "letter_head", "oldfieldtype": "Select", "options": "Letter Head", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1544,7 +1819,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Group same items", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "language", @@ -1553,13 +1830,17 @@ "hide_seconds": 1, "label": "Print Language", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_84", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1573,7 +1854,9 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1582,7 +1865,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "More Information" + "label": "More Information", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "inter_company_invoice_reference", @@ -1591,7 +1876,9 @@ "hide_seconds": 1, "label": "Inter Company Invoice Reference", "options": "Purchase Invoice", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "customer_group", @@ -1601,7 +1888,9 @@ "hide_seconds": 1, "label": "Customer Group", "options": "Customer Group", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "campaign", @@ -1612,7 +1901,9 @@ "oldfieldname": "campaign", "oldfieldtype": "Link", "options": "Campaign", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1622,13 +1913,17 @@ "hide_seconds": 1, "label": "Is Discounted", "no_copy": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "col_break23", "fieldtype": "Column Break", "hide_days": 1, "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1642,7 +1937,9 @@ "no_copy": 1, "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "source", @@ -1653,7 +1950,9 @@ "oldfieldname": "source", "oldfieldtype": "Select", "options": "Lead Source", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1664,7 +1963,9 @@ "label": "Accounting Details", "oldfieldtype": "Section Break", "options": "fa fa-file-text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "debit_to", @@ -1677,7 +1978,9 @@ "options": "Account", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -1689,7 +1992,9 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -1701,7 +2006,9 @@ "oldfieldname": "is_opening", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "c_form_applicable", @@ -1711,7 +2018,9 @@ "label": "C-Form Applicable", "no_copy": 1, "options": "No\nYes", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "c_form_no", @@ -1722,7 +2031,9 @@ "no_copy": 1, "options": "C-Form", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break8", @@ -1730,7 +2041,9 @@ "hide_days": 1, "hide_seconds": 1, "oldfieldtype": "Column Break", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", @@ -1741,7 +2054,9 @@ "no_copy": 1, "oldfieldname": "remarks", "oldfieldtype": "Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1753,7 +2068,9 @@ "label": "Commission", "oldfieldtype": "Section Break", "options": "fa fa-group", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "sales_partner", @@ -1764,7 +2081,9 @@ "oldfieldname": "sales_partner", "oldfieldtype": "Link", "options": "Sales Partner", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break10", @@ -1773,6 +2092,8 @@ "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1783,7 +2104,9 @@ "label": "Commission Rate (%)", "oldfieldname": "commission_rate", "oldfieldtype": "Currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_commission", @@ -1794,7 +2117,9 @@ "oldfieldname": "total_commission", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1804,7 +2129,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Sales Team", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1816,7 +2143,9 @@ "oldfieldname": "sales_team", "oldfieldtype": "Table", "options": "Sales Team", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1824,7 +2153,9 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Subscription Section" + "label": "Subscription Section", + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1834,7 +2165,9 @@ "hide_seconds": 1, "label": "From Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1844,13 +2177,17 @@ "hide_seconds": 1, "label": "To Date", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_140", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1862,7 +2199,9 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "allow_on_submit": 1, @@ -1871,7 +2210,9 @@ "fieldtype": "Button", "hide_days": 1, "hide_seconds": 1, - "label": "Update Auto Repeat Reference" + "label": "Update Auto Repeat Reference", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "against_income_account", @@ -1884,7 +2225,9 @@ "oldfieldname": "against_income_account", "oldfieldtype": "Small Text", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1892,13 +2235,17 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Accounting Dimensions" + "label": "Accounting Dimensions", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "dimension_col_break", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1906,7 +2253,9 @@ "fieldname": "is_consolidated", "fieldtype": "Check", "label": "Is Consolidated", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1916,14 +2265,18 @@ "hide_days": 1, "hide_seconds": 1, "label": "Is Internal Customer", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fetch_from": "company.tax_id", "fieldname": "company_tax_id", "fieldtype": "Data", "label": "Company Tax ID", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_internal_customer", @@ -1931,7 +2284,9 @@ "fieldname": "unrealized_profit_loss_account", "fieldtype": "Link", "label": "Unrealized Profit / Loss Account", - "options": "Account" + "options": "Account", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval:doc.is_internal_customer", @@ -1941,31 +2296,51 @@ "fieldtype": "Link", "label": "Represents Company", "options": "Company", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break_55", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 }, { "depends_on": "eval: doc.is_internal_customer && doc.update_stock", "fieldname": "set_target_warehouse", "fieldtype": "Link", "label": "Set Target Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "fieldname": "is_debit_note", "fieldtype": "Check", - "label": "Is Debit Note" + "label": "Is Debit Note", + "show_days": 1, + "show_seconds": 1 }, { "default": "0", "depends_on": "grand_total", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total" + "label": "Disable Rounded Total", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "fieldname": "ignore_default_payment_terms_template", + "fieldtype": "Check", + "hidden": 1, + "label": "Ignore Default Payment Terms Template", + "read_only": 1, + "show_days": 1, + "show_seconds": 1 } ], "icon": "fa fa-file-text", @@ -1978,7 +2353,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-05-20 22:48:33.988881", + "modified": "2021-08-06 23:02:20.445127", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index b97dc401e6a..329f9a97b86 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -8,7 +8,7 @@ from frappe import _, msgprint, scrub from frappe.core.doctype.user_permission.user_permission import get_permitted_documents from frappe.model.utils import get_fetch_values from frappe.utils import (add_days, getdate, formatdate, date_diff, - add_years, get_timestamp, nowdate, flt, cstr, add_months, get_last_day) + add_years, get_timestamp, nowdate, flt, cstr, add_months, get_last_day, cint) from frappe.contacts.doctype.address.address import (get_address_display, get_default_address, get_company_address) from frappe.contacts.doctype.contact.contact import get_contact_details @@ -58,7 +58,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company= customer_group=party_details.customer_group, supplier_group=party_details.supplier_group, tax_category=party_details.tax_category, billing_address=party_address, shipping_address=shipping_address) - if fetch_payment_terms_template: + if cint(fetch_payment_terms_template): party_details["payment_terms_template"] = get_payment_terms_template(party.name, party_type, company) if not party_details.get("currency"): diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index f68d81909a3..94684bff1cd 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -447,10 +447,11 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions target.flags.ignore_permissions = ignore_permissions set_missing_values(source, target) #Get the advance paid Journal Entries in Purchase Invoice Advance - if target.get("allocate_advances_automatically"): target.set_advances() + target.set_payment_schedule() + def update_item(obj, target, source_parent): target.amount = flt(obj.amount) - flt(obj.billed_amt) target.base_amount = target.amount * flt(source_parent.conversion_rate) @@ -492,10 +493,6 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions doc = get_mapped_doc("Purchase Order", source_name, fields, target_doc, postprocess, ignore_permissions=ignore_permissions) - automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) - if automatically_fetch_payment_terms: - doc.set_payment_schedule() - return doc @frappe.whitelist() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 78e4ad31add..00f40cda2c6 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1091,6 +1091,8 @@ class AccountsController(TransactionBase): if self.doctype in ("Sales Invoice", "Purchase Invoice"): base_grand_total = base_grand_total - flt(self.base_write_off_amount) grand_total = grand_total - flt(self.write_off_amount) + po_or_so, doctype, fieldname = self.get_order_details() + automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) if self.get("total_advance"): if party_account_currency == self.company_currency: @@ -1101,28 +1103,25 @@ class AccountsController(TransactionBase): base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total")) if not self.get("payment_schedule"): - if self.doctype in ["Sales Invoice", "Purchase Invoice"] and not self.get("payment_terms_template"): - po_or_so, doctype, fieldname = self.get_order_details() - - if self.get("payment_terms_template"): + if self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype): + self.fetch_payment_terms_from_order(po_or_so, doctype) + if self.get('payment_terms_template'): + self.ignore_default_payment_terms_template = 1 + elif self.get("payment_terms_template"): data = get_payment_terms(self.payment_terms_template, posting_date, grand_total, base_grand_total) for item in data: self.append("payment_schedule", item) - - elif self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype): - self.fetch_payment_terms_from_order(po_or_so, doctype) - elif self.doctype not in ["Purchase Receipt"]: data = dict(due_date=due_date, invoice_portion=100, payment_amount=grand_total, base_payment_amount=base_grand_total) self.append("payment_schedule", data) - else: - for d in self.get("payment_schedule"): - if d.invoice_portion: - d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount')) - d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount')) - d.outstanding = d.payment_amount - elif not d.invoice_portion: - d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount')) + + for d in self.get("payment_schedule"): + if d.invoice_portion: + d.payment_amount = flt(grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount')) + d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('base_payment_amount')) + d.outstanding = d.payment_amount + elif not d.invoice_portion: + d.base_payment_amount = flt(base_grand_total * self.get("conversion_rate"), d.precision('base_payment_amount')) def get_order_details(self): diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 6a550e0e975..974ade35849 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -72,7 +72,8 @@ class BuyingController(StockController, Subcontracting): # set contact and address details for supplier, if they are not mentioned if getattr(self, "supplier", None): self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions, - doctype=self.doctype, company=self.company, party_address=self.supplier_address, shipping_address=self.get('shipping_address'))) + doctype=self.doctype, company=self.company, party_address=self.supplier_address, shipping_address=self.get('shipping_address'), + fetch_payment_terms_template= not self.get('ignore_default_payment_terms_template'))) self.set_missing_item_details(for_validate) diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index a79eadc7619..54df0d63cba 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -76,6 +76,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { if (args) { args.posting_date = frm.doc.posting_date || frm.doc.transaction_date; + args.fetch_payment_terms_template = cint(!frm.doc.ignore_default_payment_terms_template) } } if (!args || !args.party) return; diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index b05cc7875cf..d071f010478 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -598,6 +598,7 @@ def make_purchase_invoice(source_name, target_doc=None): doc.run_method("onload") doc.run_method("set_missing_values") doc.run_method("calculate_taxes_and_totals") + doc.set_payment_schedule() def update_item(source_doc, target_doc, source_parent): target_doc.qty, returned_qty = get_pending_qty(source_doc) @@ -654,10 +655,6 @@ def make_purchase_invoice(source_name, target_doc=None): } }, target_doc, set_missing_values) - automatically_fetch_payment_terms = cint(frappe.db.get_single_value('Accounts Settings', 'automatically_fetch_payment_terms')) - if automatically_fetch_payment_terms: - doc.set_payment_schedule() - return doclist def get_invoiced_qty_map(purchase_receipt): From a27ef14db63b3492c48b0c7352e01df98fd18d5d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 7 Aug 2021 00:12:57 +0530 Subject: [PATCH 242/369] fix: Override template only if setting is enabled --- erpnext/controllers/accounts_controller.py | 3 ++- erpnext/public/js/utils/party.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 405216bf6fe..d967100806b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1108,7 +1108,8 @@ class AccountsController(TransactionBase): base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total")) if not self.get("payment_schedule"): - if self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype): + if self.doctype in ["Sales Invoice", "Purchase Invoice"] and automatically_fetch_payment_terms \ + and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype): self.fetch_payment_terms_from_order(po_or_so, doctype) if self.get('payment_terms_template'): self.ignore_default_payment_terms_template = 1 diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 54df0d63cba..4d432e3d5cc 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -76,7 +76,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { if (args) { args.posting_date = frm.doc.posting_date || frm.doc.transaction_date; - args.fetch_payment_terms_template = cint(!frm.doc.ignore_default_payment_terms_template) + args.fetch_payment_terms_template = cint(!frm.doc.ignore_default_payment_terms_template); } } if (!args || !args.party) return; From 25d131a39ffa7624938ccb8bf4742913bf376c52 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 7 Aug 2021 17:39:40 +0530 Subject: [PATCH 243/369] test: Improve test case for not coping payment terms --- .../buying/doctype/purchase_order/purchase_order.py | 1 + .../doctype/purchase_order/test_purchase_order.py | 12 ++++++++---- erpnext/selling/doctype/sales_order/sales_order.py | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 94684bff1cd..a0b1e073cc6 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -471,6 +471,7 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions "party_account_currency": "party_account_currency", "supplier_warehouse":"supplier_warehouse" }, + "field_no_map" : ["payment_terms_template"], "validation": { "docstatus": ["=", 1], } diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 0db54e42068..d06f9d20274 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -633,13 +633,17 @@ class TestPurchaseOrder(unittest.TestCase): raise Exception def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked(self): - po = create_purchase_order() - - self.assertTrue(po.get('payment_schedule')) + po = create_purchase_order(do_not_save=1) + po.payment_terms_template = '_Test Payment Term Template' + po.save() + po.submit() + company = frappe.get_doc('Company', '_Test Company', 'payment_terms', '_Test Payment Term Template 1') pi = make_pi_from_po(po.name) + pi.save() - self.assertFalse(pi.get('payment_schedule')) + self.assertEqual(pi.get('payment_terms_template'), '_Test Payment Term Template 1') + frappe.db.set_value('Company', '_Test Company', 'payment_terms', '') def test_terms_copied(self): po = create_purchase_order(do_not_save=1) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 2b9d516e217..bba54018aef 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -670,6 +670,7 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): "party_account_currency": "party_account_currency", "payment_terms_template": "payment_terms_template" }, + "field_no_map": ["payment_terms_template"], "validation": { "docstatus": ["=", 1] } From 5ace2767afac395e94991d1563d03eff1f53e9e9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 8 Aug 2021 19:17:38 +0530 Subject: [PATCH 244/369] test: Fix test cases for payment terms fetch --- .../buying/doctype/purchase_order/test_purchase_order.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index d06f9d20274..d668c76b6b9 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -484,6 +484,9 @@ class TestPurchaseOrder(unittest.TestCase): def test_make_purchase_invoice_with_terms(self): + from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules + + automatically_fetch_payment_terms() po = create_purchase_order(do_not_save=True) self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name) @@ -509,6 +512,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date)) self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0) self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30)) + automatically_fetch_payment_terms(enable=0) def test_subcontracting(self): po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes") @@ -638,7 +642,7 @@ class TestPurchaseOrder(unittest.TestCase): po.save() po.submit() - company = frappe.get_doc('Company', '_Test Company', 'payment_terms', '_Test Payment Term Template 1') + frappe.db.set_value('Company', '_Test Company', 'payment_terms', '_Test Payment Term Template 1') pi = make_pi_from_po(po.name) pi.save() @@ -992,7 +996,7 @@ class TestPurchaseOrder(unittest.TestCase): # self.assertEqual(po.payment_terms_template, pi.payment_terms_template) compare_payment_schedules(self, po, pi) - automatically_fetch_payment_terms(enable=0) + automatically_fetch_payment_terms(enable=0) def make_pr_against_po(po, received_qty=0): pr = make_purchase_receipt(po) From bba9aac9c0fd3d5c047ec6eccca098654e240bb0 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 9 Aug 2021 10:58:39 +0530 Subject: [PATCH 245/369] feat: add french address template (bp #26316) * add french address template Co-authored-by: HENRY Florian (cherry picked from commit 07e65ab5895f166b6dceb38dfbea1cb90f6015b9) --- erpnext/regional/address_template/templates/france.html | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 erpnext/regional/address_template/templates/france.html diff --git a/erpnext/regional/address_template/templates/france.html b/erpnext/regional/address_template/templates/france.html new file mode 100644 index 00000000000..752331eeec9 --- /dev/null +++ b/erpnext/regional/address_template/templates/france.html @@ -0,0 +1,5 @@ +{% if address_line1 %}{{ address_line1 }}{% endif -%} +{% if address_line2 %}
                                {{ address_line2 }}{% endif -%} +{% if pincode %}
                                {{ pincode }}{% endif -%} +{% if city %} {{ city }}{% endif -%} +{% if country %}
                                {{ country }}{% endif -%} From 3dfbf19e8f6ee97be7a1a727b98bfc0db1ec3db7 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 9 Aug 2021 11:33:55 +0530 Subject: [PATCH 246/369] fix: allow alternative items when using job card (bp #26724) (cherry picked from commit 7e0c57fa3fe62417ad3be75412e0c031d6486bb8) Co-authored-by: Ankush --- erpnext/manufacturing/doctype/job_card/job_card.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 69c7f5c614b..66e2394b847 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -608,6 +608,11 @@ def make_stock_entry(source_name, target_doc=None): target.set_missing_values() target.set_stock_entry_type() + wo_allows_alternate_item = frappe.db.get_value("Work Order", target.work_order, "allow_alternative_item") + for item in target.items: + item.allow_alternative_item = int(wo_allows_alternate_item and + frappe.get_cached_value("Item", item.item_code, "allow_alternative_item")) + doclist = get_mapped_doc("Job Card", source_name, { "Job Card": { "doctype": "Stock Entry", @@ -698,4 +703,4 @@ def make_corrective_job_card(source_name, operation=None, for_operation=None, ta } }, target_doc, set_missing_values) - return doclist \ No newline at end of file + return doclist From 210441d9b59f58f450bad4d61879e61549f5ac5c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 9 Aug 2021 11:34:33 +0530 Subject: [PATCH 247/369] fix: price list with 0 value are ignored (bp #26655) * fix: price list with 0 value are ignored Steps to reproduce: 1. Create 2 item price for two different supplier. One of them should be zero. 2. Create PO 3. Add supplier with non-zero price and add item. 4. change supplier. Price won't change. If price was non-zero it would've changed. Root cause: falsiness check instead of null value check is used for checking if price list value exists. 0 is evaluated as false. * refactor: make get_price_list_rate function pure (cherry picked from commit 16d4de5130097bc2dfdc7e073f1e13f0a22481d1) Co-authored-by: Ankush --- erpnext/manufacturing/doctype/bom/bom.py | 5 ++--- erpnext/stock/get_item_details.py | 21 ++++++++++++--------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 4e93fc67997..0ba85078ead 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -717,9 +717,8 @@ def get_bom_item_rate(args, bom_doc): "ignore_conversion_rate": True }) item_doc = frappe.get_cached_doc("Item", args.get("item_code")) - out = frappe._dict() - get_price_list_rate(bom_args, item_doc, out) - rate = out.price_list_rate + price_list_data = get_price_list_rate(bom_args, item_doc) + rate = price_list_data.price_list_rate return rate diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 2ed7a04ba80..be8508a000b 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -74,8 +74,7 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru update_party_blanket_order(args, out) - - get_price_list_rate(args, item, out) + out.update(get_price_list_rate(args, item)) if args.customer and cint(args.is_pos): out.update(get_pos_profile_item_details(args.company, args, update_data=True)) @@ -638,7 +637,10 @@ def get_default_supplier(args, item, item_group, brand): or item_group.get("default_supplier") or brand.get("default_supplier")) -def get_price_list_rate(args, item_doc, out): +def get_price_list_rate(args, item_doc, out=None): + if out is None: + out = frappe._dict() + meta = frappe.get_meta(args.parenttype or args.doctype) if meta.get_field("currency") or args.get('currency'): @@ -651,17 +653,17 @@ def get_price_list_rate(args, item_doc, out): if meta.get_field("currency"): validate_conversion_rate(args, meta) - price_list_rate = get_price_list_rate_for(args, item_doc.name) or 0 + price_list_rate = get_price_list_rate_for(args, item_doc.name) # variant - if not price_list_rate and item_doc.variant_of: + if price_list_rate is None and item_doc.variant_of: price_list_rate = get_price_list_rate_for(args, item_doc.variant_of) # insert in database - if not price_list_rate: + if price_list_rate is None: if args.price_list and args.rate: insert_item_price(args) - return {} + return out out.price_list_rate = flt(price_list_rate) * flt(args.plc_conversion_rate) \ / flt(args.conversion_rate) @@ -671,6 +673,8 @@ def get_price_list_rate(args, item_doc, out): out.update(get_last_purchase_details(item_doc.name, args.name, args.conversion_rate)) + return out + def insert_item_price(args): """Insert Item Price if Price List and Price List Rate are specified and currency is the same""" if frappe.db.get_value("Price List", args.price_list, "currency", cache=True) == args.currency \ @@ -1073,9 +1077,8 @@ def apply_price_list(args, as_doc=False): } def apply_price_list_on_item(args): - item_details = frappe._dict() item_doc = frappe.get_doc("Item", args.item_code) - get_price_list_rate(args, item_doc, item_details) + item_details = get_price_list_rate(args, item_doc) item_details.update(get_pricing_rule_for_item(args, item_details.price_list_rate)) From a8166c06c7e471cfcf67d4ec6cacdc2d67e12485 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 9 Aug 2021 12:24:04 +0530 Subject: [PATCH 248/369] fix: Faulty Gl Entry for Asset LCVs (#26803) * fix: Faulty Gl Entry for Asset LCVs - Both Gl entries were crediting in their respective accounts - Asset Account must be debited into * fix: Use keyword arguments instead of positional for better readability * chore: Test for LCV for draft asset created via Purchase Receipt --- .../test_landed_cost_voucher.py | 35 +++- .../purchase_receipt/purchase_receipt.py | 151 +++++++++++++++--- 2 files changed, 159 insertions(+), 27 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index 32b08f60c4a..128a2ab62ff 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -11,6 +11,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.account.test_account import create_account +from erpnext.assets.doctype.asset.test_asset import create_asset_category, create_fixed_asset_item class TestLandedCostVoucher(unittest.TestCase): def test_landed_cost_voucher(self): @@ -250,6 +251,38 @@ class TestLandedCostVoucher(unittest.TestCase): self.assertEqual(entry.credit, amounts[0]) self.assertEqual(entry.credit_in_account_currency, amounts[1]) + def test_asset_lcv(self): + "Check if LCV for an Asset updates the Assets Gross Purchase Amount correctly." + if not frappe.db.exists("Asset Category", "Computers"): + create_asset_category() + + if not frappe.db.exists("Item", "Macbook Pro"): + create_fixed_asset_item() + + pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=50000) + + # check if draft asset was created + assets = frappe.db.get_all('Asset', filters={'purchase_receipt': pr.name}) + self.assertEqual(len(assets), 1) + + frappe.db.set_value("Company", pr.company, "capital_work_in_progress_account", "CWIP Account - _TC") + lcv = make_landed_cost_voucher( + company = pr.company, + receipt_document_type = "Purchase Receipt", + receipt_document=pr.name, + charges=80, + expense_account="Expenses Included In Valuation - _TC") + + lcv.save() + lcv.submit() + + # lcv updates amount in draft asset + self.assertEqual(frappe.db.get_value("Asset", assets[0].name, "gross_purchase_amount"), 50080) + + # tear down + lcv.cancel() + pr.cancel() + def make_landed_cost_voucher(** args): args = frappe._dict(args) ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document) @@ -268,7 +301,7 @@ def make_landed_cost_voucher(** args): lcv.set("taxes", [{ "description": "Shipping Charges", - "expense_account": "Expenses Included In Valuation - TCP1", + "expense_account": args.expense_account or "Expenses Included In Valuation - TCP1", "amount": args.charges }]) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 26ea11e01d9..96e14ef759a 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -286,8 +286,16 @@ class PurchaseReceipt(BuyingController): and warehouse_account_name == supplier_warehouse_account: continue - self.add_gl_entry(gl_entries, warehouse_account_name, d.cost_center, stock_value_diff, 0.0, remarks, - stock_rbnb, account_currency=warehouse_account_currency, item=d) + self.add_gl_entry( + gl_entries=gl_entries, + account=warehouse_account_name, + cost_center=d.cost_center, + debit=stock_value_diff, + credit=0.0, + remarks=remarks, + against_account=stock_rbnb, + account_currency=warehouse_account_currency, + item=d) # GL Entry for from warehouse or Stock Received but not billed # Intentionally passed negative debit amount to avoid incorrect GL Entry validation @@ -300,9 +308,17 @@ class PurchaseReceipt(BuyingController): account = warehouse_account[d.from_warehouse]['account'] \ if d.from_warehouse else stock_rbnb - self.add_gl_entry(gl_entries, account, d.cost_center, - -1 * flt(d.base_net_amount, d.precision("base_net_amount")), 0.0, remarks, warehouse_account_name, - debit_in_account_currency=-1 * credit_amount, account_currency=credit_currency, item=d) + self.add_gl_entry( + gl_entries=gl_entries, + account=account, + cost_center=d.cost_center, + debit=-1 * flt(d.base_net_amount, d.precision("base_net_amount")), + credit=0.0, + remarks=remarks, + against_account=warehouse_account_name, + debit_in_account_currency=-1 * credit_amount, + account_currency=credit_currency, + item=d) # Amount added through landed-cos-voucher if d.landed_cost_voucher_amount and landed_cost_entries: @@ -311,14 +327,31 @@ class PurchaseReceipt(BuyingController): credit_amount = (flt(amount["base_amount"]) if (amount["base_amount"] or account_currency!=self.company_currency) else flt(amount["amount"])) - self.add_gl_entry(gl_entries, account, d.cost_center, 0.0, credit_amount, remarks, - warehouse_account_name, credit_in_account_currency=flt(amount["amount"]), - account_currency=account_currency, project=d.project, item=d) + self.add_gl_entry( + gl_entries=gl_entries, + account=account, + cost_center=d.cost_center, + debit=0.0, + credit=credit_amount, + remarks=remarks, + against_account=warehouse_account_name, + credit_in_account_currency=flt(amount["amount"]), + account_currency=account_currency, + project=d.project, + item=d) # sub-contracting warehouse if flt(d.rm_supp_cost) and warehouse_account.get(self.supplier_warehouse): - self.add_gl_entry(gl_entries, supplier_warehouse_account, d.cost_center, 0.0, flt(d.rm_supp_cost), - remarks, warehouse_account_name, account_currency=supplier_warehouse_account_currency, item=d) + self.add_gl_entry( + gl_entries=gl_entries, + account=supplier_warehouse_account, + cost_center=d.cost_center, + debit=0.0, + credit=flt(d.rm_supp_cost), + remarks=remarks, + against_account=warehouse_account_name, + account_currency=supplier_warehouse_account_currency, + item=d) # divisional loss adjustment valuation_amount_as_per_doc = flt(d.base_net_amount, d.precision("base_net_amount")) + \ @@ -335,8 +368,17 @@ class PurchaseReceipt(BuyingController): cost_center = d.cost_center or frappe.get_cached_value("Company", self.company, "cost_center") - self.add_gl_entry(gl_entries, loss_account, cost_center, divisional_loss, 0.0, remarks, - warehouse_account_name, account_currency=credit_currency, project=d.project, item=d) + self.add_gl_entry( + gl_entries=gl_entries, + account=loss_account, + cost_center=cost_center, + debit=divisional_loss, + credit=0.0, + remarks=remarks, + against_account=warehouse_account_name, + account_currency=credit_currency, + project=d.project, + item=d) elif d.warehouse not in warehouse_with_no_account or \ d.rejected_warehouse not in warehouse_with_no_account: @@ -347,12 +389,30 @@ class PurchaseReceipt(BuyingController): debit_currency = get_account_currency(d.expense_account) remarks = self.get("remarks") or _("Accounting Entry for Service") - self.add_gl_entry(gl_entries, service_received_but_not_billed_account, d.cost_center, 0.0, d.amount, - remarks, d.expense_account, account_currency=credit_currency, project=d.project, + self.add_gl_entry( + gl_entries=gl_entries, + account=service_received_but_not_billed_account, + cost_center=d.cost_center, + debit=0.0, + credit=d.amount, + remarks=remarks, + against_account=d.expense_account, + account_currency=credit_currency, + project=d.project, voucher_detail_no=d.name, item=d) - self.add_gl_entry(gl_entries, d.expense_account, d.cost_center, d.amount, 0.0, remarks, service_received_but_not_billed_account, - account_currency = debit_currency, project=d.project, voucher_detail_no=d.name, item=d) + self.add_gl_entry( + gl_entries=gl_entries, + account=d.expense_account, + cost_center=d.cost_center, + debit=d.amount, + credit=0.0, + remarks=remarks, + against_account=service_received_but_not_billed_account, + account_currency = debit_currency, + project=d.project, + voucher_detail_no=d.name, + item=d) if warehouse_with_no_account: frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" + @@ -402,8 +462,15 @@ class PurchaseReceipt(BuyingController): applicable_amount = negative_expense_to_be_booked * (valuation_tax[tax.name] / total_valuation_amount) amount_including_divisional_loss -= applicable_amount - self.add_gl_entry(gl_entries, account, tax.cost_center, 0.0, applicable_amount, self.remarks or _("Accounting Entry for Stock"), - against_account, item=tax) + self.add_gl_entry( + gl_entries=gl_entries, + account=account, + cost_center=tax.cost_center, + debit=0.0, + credit=applicable_amount, + remarks=self.remarks or _("Accounting Entry for Stock"), + against_account=against_account, + item=tax) i += 1 @@ -456,15 +523,31 @@ class PurchaseReceipt(BuyingController): # debit cwip account debit_in_account_currency = (base_asset_amount if cwip_account_currency == self.company_currency else asset_amount) - self.add_gl_entry(gl_entries, cwip_account, item.cost_center, base_asset_amount, 0.0, remarks, - arbnb_account, debit_in_account_currency=debit_in_account_currency, item=item) + self.add_gl_entry( + gl_entries=gl_entries, + account=cwip_account, + cost_center=item.cost_center, + debit=base_asset_amount, + credit=0.0, + remarks=remarks, + against_account=arbnb_account, + debit_in_account_currency=debit_in_account_currency, + item=item) asset_rbnb_currency = get_account_currency(arbnb_account) # credit arbnb account credit_in_account_currency = (base_asset_amount if asset_rbnb_currency == self.company_currency else asset_amount) - self.add_gl_entry(gl_entries, arbnb_account, item.cost_center, 0.0, base_asset_amount, remarks, - cwip_account, credit_in_account_currency=credit_in_account_currency, item=item) + self.add_gl_entry( + gl_entries=gl_entries, + account=arbnb_account, + cost_center=item.cost_center, + debit=0.0, + credit=base_asset_amount, + remarks=remarks, + against_account=cwip_account, + credit_in_account_currency=credit_in_account_currency, + item=item) def add_lcv_gl_entries(self, item, gl_entries): expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation") @@ -477,11 +560,27 @@ class PurchaseReceipt(BuyingController): remarks = self.get("remarks") or _("Accounting Entry for Stock") - self.add_gl_entry(gl_entries, expenses_included_in_asset_valuation, item.cost_center, 0.0, flt(item.landed_cost_voucher_amount), - remarks, asset_account, project=item.project, item=item) + self.add_gl_entry( + gl_entries=gl_entries, + account=expenses_included_in_asset_valuation, + cost_center=item.cost_center, + debit=0.0, + credit=flt(item.landed_cost_voucher_amount), + remarks=remarks, + against_account=asset_account, + project=item.project, + item=item) - self.add_gl_entry(gl_entries, asset_account, item.cost_center, 0.0, flt(item.landed_cost_voucher_amount), - remarks, expenses_included_in_asset_valuation, project=item.project, item=item) + self.add_gl_entry( + gl_entries=gl_entries, + account=asset_account, + cost_center=item.cost_center, + debit=flt(item.landed_cost_voucher_amount), + credit=0.0, + remarks=remarks, + against_account=expenses_included_in_asset_valuation, + project=item.project, + item=item) def update_assets(self, item, valuation_rate): assets = frappe.db.get_all('Asset', From ea83e2b45fb4219bf643e844ba85d682b6c44556 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 9 Aug 2021 14:48:59 +0530 Subject: [PATCH 249/369] fix: validate python expressions (#26835) (#26856) (cherry picked from commit 07337d5c78c14119ae9fc5d010080a1db61d0bdd) Co-authored-by: Ankush --- erpnext/accounts/doctype/pricing_rule/pricing_rule.json | 5 +++-- .../item_quality_inspection_parameter.json | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 428989aa965..0be41b40635 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -558,7 +558,8 @@ "description": "Simple Python Expression, Example: territory != 'All Territories'", "fieldname": "condition", "fieldtype": "Code", - "label": "Condition" + "label": "Condition", + "options": "PythonExpression" }, { "fieldname": "column_break_42", @@ -575,7 +576,7 @@ "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2021-03-06 22:01:24.840422", + "modified": "2021-08-06 15:10:04.219321", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json index 9b1a47eed6c..5de45cbcad9 100644 --- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json +++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.json @@ -47,7 +47,8 @@ "description": "Simple Python formula applied on Reading fields.
                                Numeric eg. 1: reading_1 > 0.2 and reading_1 < 0.5
                                \nNumeric eg. 2: mean > 3.5 (mean of populated fields)
                                \nValue based eg.: reading_value in (\"A\", \"B\", \"C\")", "fieldname": "acceptance_formula", "fieldtype": "Code", - "label": "Acceptance Criteria Formula" + "label": "Acceptance Criteria Formula", + "options": "PythonExpression" }, { "default": "0", @@ -89,7 +90,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-02-04 18:50:02.056173", + "modified": "2021-08-06 15:08:20.911338", "modified_by": "Administrator", "module": "Stock", "name": "Item Quality Inspection Parameter", From 9f5111809d2264c555b2990a2dbe4340dc2fbad5 Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 9 Aug 2021 15:35:26 +0530 Subject: [PATCH 250/369] test: fix flaky purchase receipt test (#26859) (#26860) # Conflicts: # erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py --- .../purchase_receipt/test_purchase_receipt.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index ca6e61fe6b0..b4abeff94f1 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -324,18 +324,7 @@ class TestPurchaseReceipt(unittest.TestCase): pr1.submit() self.assertRaises(frappe.ValidationError, pr2.submit) - - pr1.cancel() - se.cancel() - se1.cancel() - se2.cancel() - se3.cancel() - po.reload() - pr2.load_from_db() - pr2.cancel() - - po.load_from_db() - po.cancel() + frappe.db.rollback() def test_serial_no_supplier(self): pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1) @@ -1040,7 +1029,7 @@ class TestPurchaseReceipt(unittest.TestCase): 'account': srbnb_account, 'voucher_detail_no': pr.items[1].name }, pluck="name") - + # check if the entries are not merged into one # seperate entries should be made since voucher_detail_no is different self.assertEqual(len(item_one_gl_entry), 1) From 0ff9ef673c0ec99667f6ade2e3de41bf61224ad0 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 9 Aug 2021 17:58:18 +0530 Subject: [PATCH 251/369] fix: add parameter for db insert while adding item tax (#26855) (#26858) (cherry picked from commit b3bbebd27c73827d5a88ff47d0d16fb73dcf6de2) Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com> --- erpnext/controllers/accounts_controller.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a9b7efbe980..498f3cf0e97 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1507,7 +1507,7 @@ def set_child_tax_template_and_map(item, child_item, parent_doc): if child_item.get("item_tax_template"): child_item.item_tax_rate = get_item_tax_map(parent_doc.get('company'), child_item.item_tax_template, as_json=True) -def add_taxes_from_tax_template(child_item, parent_doc): +def add_taxes_from_tax_template(child_item, parent_doc, db_insert=True): add_taxes_from_item_tax_template = frappe.db.get_single_value("Accounts Settings", "add_taxes_from_item_tax_template") if child_item.get("item_tax_rate") and add_taxes_from_item_tax_template: @@ -1530,7 +1530,8 @@ def add_taxes_from_tax_template(child_item, parent_doc): "category" : "Total", "add_deduct_tax" : "Add" }) - tax_row.db_insert() + if db_insert: + tax_row.db_insert() def set_order_defaults(parent_doctype, parent_doctype_name, child_doctype, child_docname, trans_item): """ From 18bd182f615ab8894ab505184ad9f3e604913977 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 9 Aug 2021 18:34:51 +0530 Subject: [PATCH 252/369] patch: delete all orphaned tables docs (#26863) --- erpnext/patches.txt | 1 + .../patches/v13_0/delete_orphaned_tables.py | 69 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 erpnext/patches/v13_0/delete_orphaned_tables.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ae01496f02a..ada3badd7c7 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -294,6 +294,7 @@ erpnext.patches.v13_0.update_level_in_bom #1234sswef erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry erpnext.patches.v13_0.update_subscription_status_in_memberships erpnext.patches.v13_0.update_amt_in_work_order_required_items +erpnext.patches.v13_0.delete_orphaned_tables erpnext.patches.v13_0.update_export_type_for_gst erpnext.patches.v13_0.update_tds_check_field #3 erpnext.patches.v13_0.update_recipient_email_digest diff --git a/erpnext/patches/v13_0/delete_orphaned_tables.py b/erpnext/patches/v13_0/delete_orphaned_tables.py new file mode 100644 index 00000000000..1d6eebe0398 --- /dev/null +++ b/erpnext/patches/v13_0/delete_orphaned_tables.py @@ -0,0 +1,69 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe +from frappe.utils import getdate + +def execute(): + frappe.reload_doc('setup', 'doctype', 'transaction_deletion_record') + + if has_deleted_company_transactions(): + child_doctypes = get_child_doctypes_whose_parent_doctypes_were_affected() + + for doctype in child_doctypes: + docs = frappe.get_all(doctype, fields=['name', 'parent', 'parenttype', 'creation']) + + for doc in docs: + if not frappe.db.exists(doc['parenttype'], doc['parent']): + frappe.db.delete(doctype, {'name': doc['name']}) + + elif check_for_new_doc_with_same_name_as_deleted_parent(doc): + frappe.db.delete(doctype, {'name': doc['name']}) + +def has_deleted_company_transactions(): + return frappe.get_all('Transaction Deletion Record') + +def get_child_doctypes_whose_parent_doctypes_were_affected(): + parent_doctypes = get_affected_doctypes() + child_doctypes = frappe.get_all( + 'DocField', + filters={ + 'fieldtype': 'Table', + 'parent':['in', parent_doctypes] + }, pluck='options') + + return child_doctypes + +def get_affected_doctypes(): + affected_doctypes = [] + tdr_docs = frappe.get_all('Transaction Deletion Record', pluck="name") + + for tdr in tdr_docs: + tdr_doc = frappe.get_doc("Transaction Deletion Record", tdr) + + for doctype in tdr_doc.doctypes: + if is_not_child_table(doctype.doctype_name): + affected_doctypes.append(doctype.doctype_name) + + affected_doctypes = remove_duplicate_items(affected_doctypes) + return affected_doctypes + +def is_not_child_table(doctype): + return not bool(frappe.get_value('DocType', doctype, 'istable')) + +def remove_duplicate_items(affected_doctypes): + return list(set(affected_doctypes)) + +def check_for_new_doc_with_same_name_as_deleted_parent(doc): + """ + Compares creation times of parent and child docs. + Since Transaction Deletion Record resets the naming series after deletion, + it allows the creation of new docs with the same names as the deleted ones. + """ + + parent_creation_time = frappe.db.get_value(doc['parenttype'], doc['parent'], 'creation') + child_creation_time = doc['creation'] + + return getdate(parent_creation_time) > getdate(child_creation_time) \ No newline at end of file From 0a90302170e499b468ced0a437ed1c2a7fab6dbe Mon Sep 17 00:00:00 2001 From: Francisco Roldan Date: Mon, 9 Aug 2021 11:41:56 -0300 Subject: [PATCH 253/369] fix: depends_on in price list field --- .../accounts/doctype/subscription_plan/subscription_plan.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json index 46ce0939e4f..771611a7860 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json @@ -78,7 +78,7 @@ "label": "Cost" }, { - "depends_on": "eval:doc.price_determination==\"Based on price list\"", + "depends_on": "eval:doc.price_determination==\"Based On Price List\"", "fieldname": "price_list", "fieldtype": "Link", "label": "Price List", @@ -147,7 +147,7 @@ } ], "links": [], - "modified": "2020-06-25 10:53:44.205774", + "modified": "2021-08-09 10:53:44.205774", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription Plan", From b73bb3e5013e08276f7af4f83321cc600b081ad0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 5 Aug 2021 22:38:00 +0530 Subject: [PATCH 254/369] fix: Add discount account handling in Purchase Invoice --- .../purchase_invoice/purchase_invoice.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index feae213d92d..d0f4e965414 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -519,6 +519,10 @@ class PurchaseInvoice(BuyingController): if d.category in ('Valuation', 'Total and Valuation') and flt(d.base_tax_amount_after_discount_amount)] + exchange_rate_map, net_rate_map = get_purchase_document_details(self) + + enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + for item in self.get("items"): if flt(item.base_net_amount): account_currency = get_account_currency(item.expense_account) @@ -609,11 +613,7 @@ class PurchaseInvoice(BuyingController): if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) if not item.is_fixed_asset: - if frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'): - amount = flt(item.base_amount, item.precision("base_amount")) - else: - amount = flt(item.base_net_amount, item.precision("base_net_amount")) - + dummy, amount = self.get_amount_and_base_amount(item, enable_discount_accounting) else: amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount")) @@ -827,8 +827,11 @@ class PurchaseInvoice(BuyingController): def make_tax_gl_entries(self, gl_entries): # tax table gl entries valuation_tax = {} + enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + for tax in self.get("taxes"): - if tax.category in ("Total", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount): + amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting) + if tax.category in ("Total", "Valuation and Total") and flt(base_amount): account_currency = get_account_currency(tax.account_head) dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" @@ -837,21 +840,21 @@ class PurchaseInvoice(BuyingController): self.get_gl_dict({ "account": tax.account_head, "against": self.supplier, - dr_or_cr: tax.base_tax_amount_after_discount_amount, - dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \ + dr_or_cr: base_amount, + dr_or_cr + "_in_account_currency": base_amount \ if account_currency==self.company_currency \ - else tax.tax_amount_after_discount_amount, + else amount, "cost_center": tax.cost_center }, account_currency, item=tax) ) # accumulate valuation tax - if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount) \ + if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(base_amount) \ and not self.is_internal_transfer(): if self.auto_accounting_for_stock and not tax.cost_center: frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category))) valuation_tax.setdefault(tax.name, 0) valuation_tax[tax.name] += \ - (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount) + (tax.add_deduct_tax == "Add" and 1 or -1) * flt(base_amount) if self.is_opening == "No" and self.negative_expense_to_be_booked and valuation_tax: # credit valuation tax amount in "Expenses Included In Valuation" From 428ccebad95a82ca0a38c35924fe5756ac35311b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 5 Aug 2021 22:38:24 +0530 Subject: [PATCH 255/369] test: Update test cases for discount accounting --- .../purchase_invoice/test_purchase_invoice.py | 27 ++++++++++--------- .../sales_invoice/test_sales_invoice.py | 24 +++++++++-------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index d60f0a40b15..f0f5a58d153 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -235,39 +235,41 @@ class TestPurchaseInvoice(unittest.TestCase): discount_account = create_account(account_name="Discount Account", parent_account="Indirect Expenses - _TC", company="_Test Company") - pi = make_purchase_invoice(discount_account=discount_account, discount_amount=100) + pi = make_purchase_invoice(discount_account=discount_account, rate=45) expected_gle = [ - ["_Test Account Cost for Goods Sold - _TC", 350.0, 0.0, nowdate()], - ["Creditors - _TC", 0.0, 250.0, nowdate()], - ["Discount Account - _TC", 0.0, 100.0, nowdate()] + ["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, nowdate()], + ["Creditors - _TC", 0.0, 225.0, nowdate()], + ["Discount Account - _TC", 0.0, 25.0, nowdate()] ] check_gl_entries(self, pi.name, expected_gle, nowdate()) + enable_discount_accounting(enable=0) def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabled(self): enable_discount_accounting() additional_discount_account = create_account(account_name="Discount Account", parent_account="Indirect Expenses - _TC", company="_Test Company") - pi = make_purchase_invoice(qty=1, rate=100, do_not_save=1, parent_cost_center="Main - _TC") + pi = make_purchase_invoice(do_not_save=1, parent_cost_center="Main - _TC") pi.apply_discount_on = "Grand Total" pi.additional_discount_account = additional_discount_account - pi.additional_discount_percentage = 20 + pi.additional_discount_percentage = 10 + pi.disable_rounded_total = 1 pi.append("taxes", { - "charge_type": "Actual", + "charge_type": "On Net Total", "account_head": "_Test Account VAT - _TC", "cost_center": "Main - _TC", "description": "Test", - "tax_amount": 20 + "rate": 10 }) pi.submit() expected_gle = [ - ["_Test Account Cost for Goods Sold - _TC", 100.0, 0.0, nowdate()], - ["_Test Account VAT - _TC", 20.0, 0.0, nowdate()], - ["Creditors - _TC", 0.0, 96.0, nowdate()], - ["Discount Account - _TC", 0.0, 24.0, nowdate()] + ["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, nowdate()], + ["_Test Account VAT - _TC", 25.0, 0.0, nowdate()], + ["Creditors - _TC", 0.0, 247.5, nowdate()], + ["Discount Account - _TC", 0.0, 27.5, nowdate()] ] check_gl_entries(self, pi.name, expected_gle, nowdate()) @@ -1260,6 +1262,7 @@ def make_purchase_invoice(**args): "received_qty": args.received_qty or 0, "rejected_qty": args.rejected_qty or 0, "rate": args.rate or 50, + "price_list_rate": args.price_list_rate or 50, "expense_account": args.expense_account or '_Test Account Cost for Goods Sold - _TC', "discount_account": args.discount_account or None, "discount_amount": args.discount_amount or 0, diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index af317db7124..12f03be2881 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1993,15 +1993,16 @@ class TestSalesInvoice(unittest.TestCase): discount_account = create_account(account_name="Discount Account", parent_account="Indirect Expenses - _TC", company="_Test Company") - si = create_sales_invoice(discount_account=discount_account, discount_amount=100) + si = create_sales_invoice(discount_account=discount_account, discount_percentage=10, rate=90) expected_gle = [ - ["Debtors - _TC", 100.0, 0.0, nowdate()], - ["Discount Account - _TC", 100.0, 0.0, nowdate()], - ["Sales - _TC", 0.0, 200.0, nowdate()] + ["Debtors - _TC", 90.0, 0.0, nowdate()], + ["Discount Account - _TC", 10.0, 0.0, nowdate()], + ["Sales - _TC", 0.0, 100.0, nowdate()] ] check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) + enable_discount_accounting(enable=0) def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled(self): from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import enable_discount_accounting @@ -2010,28 +2011,28 @@ class TestSalesInvoice(unittest.TestCase): additional_discount_account = create_account(account_name="Discount Account", parent_account="Indirect Expenses - _TC", company="_Test Company") - si = create_sales_invoice(rate=100, parent_cost_center='Main - _TC', do_not_save=1) + si = create_sales_invoice(parent_cost_center='Main - _TC', do_not_save=1) si.apply_discount_on = "Grand Total" si.additional_discount_account = additional_discount_account si.additional_discount_percentage = 20 si.append("taxes", { - "charge_type": "Actual", + "charge_type": "On Net Total", "account_head": "_Test Account VAT - _TC", "cost_center": "Main - _TC", "description": "Test", - "rate": 0, - "tax_amount": 20 + "rate": 10 }) si.submit() expected_gle = [ - ["_Test Account VAT - _TC", 0.0, 20.0, nowdate()], - ["Debtors - _TC", 96.0, 0.0, nowdate()], - ["Discount Account - _TC", 24.0, 0.0, nowdate()], + ["_Test Account VAT - _TC", 0.0, 10.0, nowdate()], + ["Debtors - _TC", 88, 0.0, nowdate()], + ["Discount Account - _TC", 22.0, 0.0, nowdate()], ["Sales - _TC", 0.0, 100.0, nowdate()] ] check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1)) + enable_discount_accounting(enable=0) def get_sales_invoice_for_e_invoice(): si = make_sales_invoice_for_ewaybill() @@ -2238,6 +2239,7 @@ def create_sales_invoice(**args): "uom": args.uom or "Nos", "stock_uom": args.uom or "Nos", "rate": args.rate if args.get("rate") is not None else 100, + "price_list_rate": args.price_list_rate if args.get("price_list_rate") is not None else 100, "income_account": args.income_account or "Sales - _TC", "expense_account": args.expense_account or "Cost of Goods Sold - _TC", "discount_account": args.discount_account or None, From 797170e9138310da53ef33ecb2f8397f8b292882 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Aug 2021 11:02:21 +0530 Subject: [PATCH 256/369] fix: Linting issues --- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index d0f4e965414..d7d9a3886a5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -519,8 +519,6 @@ class PurchaseInvoice(BuyingController): if d.category in ('Valuation', 'Total and Valuation') and flt(d.base_tax_amount_after_discount_amount)] - exchange_rate_map, net_rate_map = get_purchase_document_details(self) - enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) for item in self.get("items"): @@ -841,8 +839,8 @@ class PurchaseInvoice(BuyingController): "account": tax.account_head, "against": self.supplier, dr_or_cr: base_amount, - dr_or_cr + "_in_account_currency": base_amount \ - if account_currency==self.company_currency \ + dr_or_cr + "_in_account_currency": base_amount + if account_currency==self.company_currency else amount, "cost_center": tax.cost_center }, account_currency, item=tax) From 415519af15244b985a79d91691f7f2ba9a9b691c Mon Sep 17 00:00:00 2001 From: noahjacob Date: Fri, 7 May 2021 13:00:46 +0530 Subject: [PATCH 257/369] feat: added multi-select fields to create multiple pricing rules. --- .../doctype/campaign_item/__init__.py | 0 .../doctype/campaign_item/campaign_item.json | 31 + .../doctype/campaign_item/campaign_item.py | 10 + .../doctype/customer_group_item/__init__.py | 0 .../customer_group_item.json | 31 + .../customer_group_item.py | 10 + .../doctype/customer_item/__init__.py | 0 .../doctype/customer_item/customer_item.json | 31 + .../doctype/customer_item/customer_item.py | 10 + .../promotional_scheme.json | 1226 ++--------------- .../promotional_scheme/promotional_scheme.py | 97 +- .../doctype/sales_partner_item/__init__.py | 0 .../sales_partner_item.json | 31 + .../sales_partner_item/sales_partner_item.py | 10 + .../doctype/supplier_group_item/__init__.py | 0 .../supplier_group_item.json | 31 + .../supplier_group_item.py | 10 + .../doctype/supplier_item/__init__.py | 0 .../doctype/supplier_item/supplier_item.json | 31 + .../doctype/supplier_item/supplier_item.py | 10 + .../doctype/territory_item/__init__.py | 0 .../territory_item/territory_item.json | 31 + .../doctype/territory_item/territory_item.py | 10 + 23 files changed, 443 insertions(+), 1167 deletions(-) create mode 100644 erpnext/accounts/doctype/campaign_item/__init__.py create mode 100644 erpnext/accounts/doctype/campaign_item/campaign_item.json create mode 100644 erpnext/accounts/doctype/campaign_item/campaign_item.py create mode 100644 erpnext/accounts/doctype/customer_group_item/__init__.py create mode 100644 erpnext/accounts/doctype/customer_group_item/customer_group_item.json create mode 100644 erpnext/accounts/doctype/customer_group_item/customer_group_item.py create mode 100644 erpnext/accounts/doctype/customer_item/__init__.py create mode 100644 erpnext/accounts/doctype/customer_item/customer_item.json create mode 100644 erpnext/accounts/doctype/customer_item/customer_item.py create mode 100644 erpnext/accounts/doctype/sales_partner_item/__init__.py create mode 100644 erpnext/accounts/doctype/sales_partner_item/sales_partner_item.json create mode 100644 erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py create mode 100644 erpnext/accounts/doctype/supplier_group_item/__init__.py create mode 100644 erpnext/accounts/doctype/supplier_group_item/supplier_group_item.json create mode 100644 erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py create mode 100644 erpnext/accounts/doctype/supplier_item/__init__.py create mode 100644 erpnext/accounts/doctype/supplier_item/supplier_item.json create mode 100644 erpnext/accounts/doctype/supplier_item/supplier_item.py create mode 100644 erpnext/accounts/doctype/territory_item/__init__.py create mode 100644 erpnext/accounts/doctype/territory_item/territory_item.json create mode 100644 erpnext/accounts/doctype/territory_item/territory_item.py diff --git a/erpnext/accounts/doctype/campaign_item/__init__.py b/erpnext/accounts/doctype/campaign_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/campaign_item/campaign_item.json b/erpnext/accounts/doctype/campaign_item/campaign_item.json new file mode 100644 index 00000000000..69383a482b4 --- /dev/null +++ b/erpnext/accounts/doctype/campaign_item/campaign_item.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2021-05-06 16:18:25.410476", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "campaign" + ], + "fields": [ + { + "fieldname": "campaign", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Campaign", + "options": "Campaign" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-07 10:43:49.717633", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Campaign Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/campaign_item/campaign_item.py b/erpnext/accounts/doctype/campaign_item/campaign_item.py new file mode 100644 index 00000000000..e5ca7e53684 --- /dev/null +++ b/erpnext/accounts/doctype/campaign_item/campaign_item.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class CampaignItem(Document): + pass diff --git a/erpnext/accounts/doctype/customer_group_item/__init__.py b/erpnext/accounts/doctype/customer_group_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/customer_group_item/customer_group_item.json b/erpnext/accounts/doctype/customer_group_item/customer_group_item.json new file mode 100644 index 00000000000..bd1229d4e09 --- /dev/null +++ b/erpnext/accounts/doctype/customer_group_item/customer_group_item.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2021-05-06 16:12:42.558878", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "customer_group" + ], + "fields": [ + { + "fieldname": "customer_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Customer Group", + "options": "Customer Group" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-07 10:39:21.563506", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Customer Group Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/customer_group_item/customer_group_item.py b/erpnext/accounts/doctype/customer_group_item/customer_group_item.py new file mode 100644 index 00000000000..ea24788ad56 --- /dev/null +++ b/erpnext/accounts/doctype/customer_group_item/customer_group_item.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class CustomerGroupItem(Document): + pass diff --git a/erpnext/accounts/doctype/customer_item/__init__.py b/erpnext/accounts/doctype/customer_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/customer_item/customer_item.json b/erpnext/accounts/doctype/customer_item/customer_item.json new file mode 100644 index 00000000000..f3dac02f942 --- /dev/null +++ b/erpnext/accounts/doctype/customer_item/customer_item.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2021-05-05 14:04:54.266353", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "customer" + ], + "fields": [ + { + "fieldname": "customer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Customer ", + "options": "Customer" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-06 10:02:32.967841", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Customer Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/customer_item/customer_item.py b/erpnext/accounts/doctype/customer_item/customer_item.py new file mode 100644 index 00000000000..4aad84ec140 --- /dev/null +++ b/erpnext/accounts/doctype/customer_item/customer_item.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class CustomerItem(Document): + pass diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json index cc71324dbc3..1d68b23d6c7 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.json @@ -1,1381 +1,339 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "Prompt", - "beta": 0, "creation": "2019-02-08 17:10:36.077402", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "section_break_1", + "apply_on", + "disable", + "column_break_3", + "items", + "item_groups", + "brands", + "mixed_conditions", + "is_cumulative", + "section_break_10", + "apply_rule_on_other", + "column_break_11", + "other_item_code", + "other_item_group", + "other_brand", + "section_break_8", + "selling", + "buying", + "column_break_12", + "applicable_for", + "customer", + "customer_group", + "territory", + "sales_partner", + "campaign", + "supplier", + "supplier_group", + "period_settings_section", + "valid_from", + "valid_upto", + "column_break_26", + "company", + "currency", + "section_break_14", + "price_discount_slabs", + "section_break_15", + "product_discount_slabs" + ], "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": "section_break_1", - "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, - "label": "", - "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 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Item Code", - "fetch_if_empty": 0, "fieldname": "apply_on", "fieldtype": "Select", - "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": "Apply On", - "length": 0, - "no_copy": 0, "options": "\nItem Code\nItem Group\nBrand\nTransaction", - "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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "disable", "fieldtype": "Check", - "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": "Disable", - "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 + "label": "Disable" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "column_break_3", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.apply_on == 'Item Code'", - "fetch_if_empty": 0, "fieldname": "items", "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": "Pricing Rule Item Code", - "length": 0, - "no_copy": 0, - "options": "Pricing Rule Item Code", - "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 + "options": "Pricing Rule Item Code" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.apply_on == 'Item Group'", - "fetch_if_empty": 0, "fieldname": "item_groups", "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": "Pricing Rule Item Group", - "length": 0, - "no_copy": 0, - "options": "Pricing Rule Item Group", - "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 + "options": "Pricing Rule Item Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.apply_on == 'Brand'", - "fetch_if_empty": 0, "fieldname": "brands", "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": "Pricing Rule Brand", - "length": 0, - "no_copy": 0, - "options": "Pricing Rule Brand", - "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 + "options": "Pricing Rule Brand" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "mixed_conditions", "fieldtype": "Check", - "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": "Mixed Conditions", - "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 + "label": "Mixed Conditions" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "is_cumulative", "fieldtype": "Check", - "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": "Is Cumulative", - "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 + "label": "Is Cumulative" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "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, - "label": "Discount on Other Item", - "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 + "label": "Discount on Other Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "apply_rule_on_other", "fieldtype": "Select", - "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": "Apply Rule On Other", - "length": 0, - "no_copy": 0, - "options": "\nItem Code\nItem Group\nBrand", - "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 + "options": "\nItem Code\nItem Group\nBrand" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "column_break_11", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.apply_rule_on_other == 'Item Code'", - "fetch_if_empty": 0, "fieldname": "other_item_code", "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": "Item Code", - "length": 0, - "no_copy": 0, - "options": "Item", - "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 + "options": "Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.apply_rule_on_other == 'Item Group'", - "fetch_if_empty": 0, "fieldname": "other_item_group", "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": "Item Group", - "length": 0, - "no_copy": 0, - "options": "Item Group", - "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 + "options": "Item Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.apply_rule_on_other == 'Brand'", - "fetch_if_empty": 0, "fieldname": "other_brand", "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": "Brand", - "length": 0, - "no_copy": 0, - "options": "Brand", - "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 + "options": "Brand" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "section_break_8", "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, - "label": "Party Information", - "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 + "label": "Party Information" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "selling", "fieldtype": "Check", - "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": "Selling", - "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 + "label": "Selling" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "buying", "fieldtype": "Check", - "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": "Buying", - "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 + "label": "Buying" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval: doc.buying || doc.selling", - "fetch_if_empty": 0, "fieldname": "applicable_for", "fieldtype": "Select", - "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": "Applicable For", - "length": 0, - "no_copy": 0, - "options": "\nCustomer\nCustomer Group\nTerritory\nSales Partner\nCampaign\nSupplier\nSupplier Group", - "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 + "options": "\nCustomer\nCustomer Group\nTerritory\nSales Partner\nCampaign\nSupplier\nSupplier Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.applicable_for=='Customer'", - "fetch_if_empty": 0, "fieldname": "customer", - "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, + "fieldtype": "Table MultiSelect", "label": "Customer", - "length": 0, - "no_copy": 0, - "options": "Customer", - "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 + "options": "Customer Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.applicable_for==\"Customer Group\"", - "fetch_if_empty": 0, "fieldname": "customer_group", - "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, + "fieldtype": "Table MultiSelect", "label": "Customer Group", - "length": 0, - "no_copy": 0, - "options": "Customer Group", - "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 + "options": "Customer Group Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.applicable_for==\"Territory\"", - "fetch_if_empty": 0, "fieldname": "territory", - "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, + "fieldtype": "Table MultiSelect", "label": "Territory", - "length": 0, - "no_copy": 0, - "options": "Territory", - "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 + "options": "Territory Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.applicable_for==\"Sales Partner\"", - "fetch_if_empty": 0, "fieldname": "sales_partner", - "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, + "fieldtype": "Table MultiSelect", "label": "Sales Partner", - "length": 0, - "no_copy": 0, - "options": "Sales Partner", - "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 + "options": "Sales Partner Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.applicable_for==\"Campaign\"", - "fetch_if_empty": 0, "fieldname": "campaign", - "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, + "fieldtype": "Table MultiSelect", "label": "Campaign", - "length": 0, - "no_copy": 0, - "options": "Campaign", - "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 + "options": "Campaign Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.applicable_for=='Supplier'", - "fetch_if_empty": 0, "fieldname": "supplier", - "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, + "fieldtype": "Table MultiSelect", "label": "Supplier", - "length": 0, - "no_copy": 0, - "options": "Supplier", - "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 + "options": "Supplier Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.applicable_for==\"Supplier Group\"", - "fetch_if_empty": 0, "fieldname": "supplier_group", - "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, + "fieldtype": "Table MultiSelect", "label": "Supplier Group", - "length": 0, - "no_copy": 0, - "options": "Supplier Group", - "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 + "options": "Supplier Group Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "period_settings_section", "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, - "label": "Period Settings", - "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 + "label": "Period Settings" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Today", - "fetch_if_empty": 0, "fieldname": "valid_from", "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": "Valid From", - "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 + "label": "Valid From" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "valid_upto", "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": "Valid Upto", - "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 + "label": "Valid Upto" }, { - "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_26", - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "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": 1, - "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": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "currency", "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": "Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "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 + "options": "Currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "section_break_14", "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, - "label": "Price Discount Slabs", - "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 + "label": "Price Discount Slabs" }, { "allow_bulk_edit": 1, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "price_discount_slabs", "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": "Promotional Scheme Price Discount", - "length": 0, - "no_copy": 0, - "options": "Promotional Scheme Price Discount", - "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 + "options": "Promotional Scheme Price Discount" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "section_break_15", "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, - "label": "Product Discount Slabs", - "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 + "label": "Product Discount Slabs" }, { "allow_bulk_edit": 1, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "product_discount_slabs", "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": "Promotional Scheme Product Discount", - "length": 0, - "no_copy": 0, - "options": "Promotional Scheme Product Discount", - "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 + "options": "Promotional Scheme Product Discount" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-25 12:14:27.486586", + "links": [], + "modified": "2021-05-06 16:20:22.039078", "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Accounts Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Sales Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Accounts User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "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 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 7d9302382f1..ec42eda63ac 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -30,17 +30,17 @@ class PromotionalScheme(Document): frappe.throw(_("Price or product discount slabs are required")) def on_update(self): - data = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"], - filters = {'promotional_scheme': self.name}) or {} - + data = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name", "creation"], order_by = 'creation asc', + filters = {'promotional_scheme': self.name, 'applicable_for': self.applicable_for}) or {} self.update_pricing_rules(data) def update_pricing_rules(self, data): rules = {} count = 0 - + names = [] for d in data: - rules[d.get('promotional_scheme_id')] = d.get('name') + names.append(d.name) + rules[d.get('promotional_scheme_id')] = names docs = get_pricing_rules(self, rules) @@ -73,42 +73,73 @@ def get_pricing_rules(doc, rules = {}): def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}): new_doc = [] args = get_args_for_pricing_rule(doc) + applicable_for = frappe.scrub(doc.get('applicable_for')) for d in doc.get(child_doc): if d.name in rules: - pr = frappe.get_doc('Pricing Rule', rules.get(d.name)) + for a in args.get(applicable_for): + + temp_args = args.copy() + docname = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name", applicable_for], + filters = {'promotional_scheme_id': d.name, applicable_for: a}) + + if docname: + pr = frappe.get_doc('Pricing Rule', docname[0].get('name')) + temp_args[applicable_for] = a + pr = set_args(temp_args,pr,doc,child_doc,discount_fields,d) + else: + pr = frappe.new_doc("Pricing Rule") + pr.title = make_autoname("{0}/.####".format(doc.name)) + temp_args[applicable_for] = a + pr = set_args(temp_args,pr,doc,child_doc,discount_fields,d) + + new_doc.append(pr) + else: - pr = frappe.new_doc("Pricing Rule") - pr.title = make_autoname("{0}/.####".format(doc.name)) + for i in range(len(args.get(applicable_for))) : - pr.update(args) - for field in (other_fields + discount_fields): - pr.set(field, d.get(field)) - - pr.promotional_scheme_id = d.name - pr.promotional_scheme = doc.name - pr.disable = d.disable if d.disable else doc.disable - pr.price_or_product_discount = ('Price' - if child_doc == 'price_discount_slabs' else 'Product') - - for field in ['items', 'item_groups', 'brands']: - if doc.get(field): - pr.set(field, []) - - apply_on = frappe.scrub(doc.get('apply_on')) - for d in doc.get(field): - pr.append(field, { - apply_on: d.get(apply_on), - 'uom': d.uom - }) - - new_doc.append(pr) + pr = frappe.new_doc("Pricing Rule") + pr.title = make_autoname("{0}/.####".format(doc.name)) + temp_args = args.copy() + temp_args[applicable_for] = args[applicable_for][i] + pr = set_args(temp_args,pr,doc,child_doc,discount_fields,d) + new_doc.append(pr) return new_doc +def set_args(args,pr,doc,child_doc,discount_fields,d): + pr.update(args) + for field in (other_fields + discount_fields): + pr.set(field, d.get(field)) + + pr.promotional_scheme_id = d.name + pr.promotional_scheme = doc.name + pr.disable = d.disable if d.disable else doc.disable + pr.price_or_product_discount = ('Price' + if child_doc == 'price_discount_slabs' else 'Product') + + for field in ['items', 'item_groups', 'brands']: + if doc.get(field): + pr.set(field, []) + + apply_on = frappe.scrub(doc.get('apply_on')) + for d in doc.get(field): + pr.append(field, { + apply_on: d.get(apply_on), + 'uom': d.uom + }) + return pr + def get_args_for_pricing_rule(doc): args = { 'promotional_scheme': doc.name } - + applicable_for = frappe.scrub(doc.get('applicable_for')) + for d in pricing_rule_fields: - args[d] = doc.get(d) - + if d == applicable_for: + items = [] + for i in doc.get(applicable_for): + items.append(i.get(applicable_for)) + args[d] = items + + else: + args[d] = doc.get(d) return args diff --git a/erpnext/accounts/doctype/sales_partner_item/__init__.py b/erpnext/accounts/doctype/sales_partner_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.json b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.json new file mode 100644 index 00000000000..c176e4d173d --- /dev/null +++ b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2021-05-06 16:17:44.329943", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "sales_partner" + ], + "fields": [ + { + "fieldname": "sales_partner", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Sales Partner ", + "options": "Sales Partner" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-07 10:43:37.532095", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Sales Partner Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py new file mode 100644 index 00000000000..9e6564845b8 --- /dev/null +++ b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class SalesPartnerItem(Document): + pass diff --git a/erpnext/accounts/doctype/supplier_group_item/__init__.py b/erpnext/accounts/doctype/supplier_group_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.json b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.json new file mode 100644 index 00000000000..67fac458456 --- /dev/null +++ b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2021-05-06 16:19:22.040795", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "supplier_group" + ], + "fields": [ + { + "fieldname": "supplier_group", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Supplier Group", + "options": "Supplier Group" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-07 10:43:59.877938", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Supplier Group Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py new file mode 100644 index 00000000000..5ea2635482d --- /dev/null +++ b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class SupplierGroupItem(Document): + pass diff --git a/erpnext/accounts/doctype/supplier_item/__init__.py b/erpnext/accounts/doctype/supplier_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/supplier_item/supplier_item.json b/erpnext/accounts/doctype/supplier_item/supplier_item.json new file mode 100644 index 00000000000..95c4dc6db36 --- /dev/null +++ b/erpnext/accounts/doctype/supplier_item/supplier_item.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2021-05-06 16:18:54.758468", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "supplier" + ], + "fields": [ + { + "fieldname": "supplier", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Supplier", + "options": "Supplier" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-07 10:44:09.707778", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Supplier Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/supplier_item/supplier_item.py b/erpnext/accounts/doctype/supplier_item/supplier_item.py new file mode 100644 index 00000000000..b58b5dc057b --- /dev/null +++ b/erpnext/accounts/doctype/supplier_item/supplier_item.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class SupplierItem(Document): + pass diff --git a/erpnext/accounts/doctype/territory_item/__init__.py b/erpnext/accounts/doctype/territory_item/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/territory_item/territory_item.json b/erpnext/accounts/doctype/territory_item/territory_item.json new file mode 100644 index 00000000000..0f0fdea8c01 --- /dev/null +++ b/erpnext/accounts/doctype/territory_item/territory_item.json @@ -0,0 +1,31 @@ +{ + "actions": [], + "creation": "2021-05-06 16:16:51.885441", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "territory" + ], + "fields": [ + { + "fieldname": "territory", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Territory", + "options": "Territory" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-05-07 10:43:26.641030", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Territory Item", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/territory_item/territory_item.py b/erpnext/accounts/doctype/territory_item/territory_item.py new file mode 100644 index 00000000000..9adfc005721 --- /dev/null +++ b/erpnext/accounts/doctype/territory_item/territory_item.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class TerritoryItem(Document): + pass From a9ef56a1077fcefd224b8382f892085f99eb2b5e Mon Sep 17 00:00:00 2001 From: noahjacob Date: Fri, 7 May 2021 16:03:59 +0530 Subject: [PATCH 258/369] test: added test case for creating and updating --- .../test_promotional_scheme.py | 57 ++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py index 8dc04997796..9c756258db3 100644 --- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py @@ -7,4 +7,59 @@ import frappe import unittest class TestPromotionalScheme(unittest.TestCase): - pass + def test_promotional_scheme(self): + ps = make_promotional_scheme() + price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name", "creation"], + filters = {'promotional_scheme': ps.name}) + self.assertTrue(len(price_rules),1) + price_doc_details = frappe.db.get_value('Pricing Rule',price_rules[0].name,['customer','min_qty','discount_percentage'],as_dict = 1) + self.assertTrue(price_doc_details.customer,'_Test Customer') + self.assertTrue(price_doc_details.min_qty,4) + self.assertTrue(price_doc_details.discount_percentage,20) + + ps.price_discount_slabs[0].min_qty = 6 + ps.append('customer',{ + 'customer': "_Test Customer 2" + }) + ps.save() + price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"], + filters = {'promotional_scheme': ps.name}) + self.assertTrue(len(price_rules),2) + + price_doc_details = frappe.db.get_value('Pricing Rule',price_rules[1].name,['customer','min_qty','discount_percentage'],as_dict = 1) + self.assertTrue(price_doc_details.customer,'_Test Customer 2') + self.assertTrue(price_doc_details.min_qty,6) + self.assertTrue(price_doc_details.discount_percentage,20) + + price_doc_details = frappe.db.get_value('Pricing Rule',price_rules[0].name,['customer','min_qty','discount_percentage'],as_dict = 1) + self.assertTrue(price_doc_details.customer,'_Test Customer') + self.assertTrue(price_doc_details.min_qty,6) + + frappe.delete_doc('Promotional Scheme',ps.name) + price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"], + filters = {'promotional_scheme': ps.name}) + self.assertEqual(price_rules,[]) + + + + + +def make_promotional_scheme(): + ps = frappe.new_doc('Promotional Scheme') + ps.name = '_Test Scheme' + ps.append('items',{ + 'item_code': 'Test Production Item 1' + }) + ps.selling = 1 + ps.append('price_discount_slabs',{ + 'min_qty': 4, + 'discount_percentage': 20, + 'rule_description': 'Test' + }) + ps.applicable_for = 'Customer' + ps.append('customer',{ + 'customer': "_Test Customer" + }) + ps.save() + + return ps \ No newline at end of file From c95d96e7ae7bd0a283e29d09346e590abb489640 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Fri, 7 May 2021 16:17:00 +0530 Subject: [PATCH 259/369] refactor: changed variable names --- .../doctype/promotional_scheme/promotional_scheme.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index ec42eda63ac..ff3f9c5638d 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -76,20 +76,20 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}): applicable_for = frappe.scrub(doc.get('applicable_for')) for d in doc.get(child_doc): if d.name in rules: - for a in args.get(applicable_for): + for applicable_for_value in args.get(applicable_for): temp_args = args.copy() docname = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name", applicable_for], - filters = {'promotional_scheme_id': d.name, applicable_for: a}) + filters = {'promotional_scheme_id': d.name, applicable_for: applicable_for_value}) if docname: pr = frappe.get_doc('Pricing Rule', docname[0].get('name')) - temp_args[applicable_for] = a + temp_args[applicable_for] = applicable_for_value pr = set_args(temp_args,pr,doc,child_doc,discount_fields,d) else: pr = frappe.new_doc("Pricing Rule") pr.title = make_autoname("{0}/.####".format(doc.name)) - temp_args[applicable_for] = a + temp_args[applicable_for] = applicable_for_value pr = set_args(temp_args,pr,doc,child_doc,discount_fields,d) new_doc.append(pr) @@ -136,8 +136,8 @@ def get_args_for_pricing_rule(doc): for d in pricing_rule_fields: if d == applicable_for: items = [] - for i in doc.get(applicable_for): - items.append(i.get(applicable_for)) + for applicable_for_values in doc.get(applicable_for): + items.append(applicable_for_values.get(applicable_for)) args[d] = items else: From 3a663ac77fc89fd7e95f8fe7b1fee4ad044dac93 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Fri, 7 May 2021 17:39:26 +0530 Subject: [PATCH 260/369] test: changed test item name --- .../doctype/promotional_scheme/test_promotional_scheme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py index 9c756258db3..940b76a9ade 100644 --- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py @@ -48,7 +48,7 @@ def make_promotional_scheme(): ps = frappe.new_doc('Promotional Scheme') ps.name = '_Test Scheme' ps.append('items',{ - 'item_code': 'Test Production Item 1' + 'item_code': '_Test Item' }) ps.selling = 1 ps.append('price_discount_slabs',{ From a4cea1e56d68dbb4139324629bba1730508b33a3 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Wed, 12 May 2021 16:50:41 +0530 Subject: [PATCH 261/369] refactor: variable names --- .../doctype/promotional_scheme/promotional_scheme.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index ff3f9c5638d..69ed94676dd 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -106,14 +106,14 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}): return new_doc -def set_args(args,pr,doc,child_doc,discount_fields,d): +def set_args(args, pr, doc, child_doc, discount_fields, child_doc_fields): pr.update(args) for field in (other_fields + discount_fields): - pr.set(field, d.get(field)) + pr.set(field, child_doc_fields.get(field)) - pr.promotional_scheme_id = d.name + pr.promotional_scheme_id = child_doc_fields.name pr.promotional_scheme = doc.name - pr.disable = d.disable if d.disable else doc.disable + pr.disable = child_doc_fields.disable if child_doc_fields.disable else doc.disable pr.price_or_product_discount = ('Price' if child_doc == 'price_discount_slabs' else 'Product') From 4fb1b6b80cb9ebeb249137eb7b24410547c745f9 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Wed, 12 May 2021 16:51:02 +0530 Subject: [PATCH 262/369] fix: Sider --- .../promotional_scheme/test_promotional_scheme.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py index 940b76a9ade..99f9ed47e4a 100644 --- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py @@ -18,9 +18,8 @@ class TestPromotionalScheme(unittest.TestCase): self.assertTrue(price_doc_details.discount_percentage,20) ps.price_discount_slabs[0].min_qty = 6 - ps.append('customer',{ - 'customer': "_Test Customer 2" - }) + ps.append('customer', { + 'customer': "_Test Customer 2"}) ps.save() price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"], filters = {'promotional_scheme': ps.name}) @@ -39,11 +38,7 @@ class TestPromotionalScheme(unittest.TestCase): price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"], filters = {'promotional_scheme': ps.name}) self.assertEqual(price_rules,[]) - - - - def make_promotional_scheme(): ps = frappe.new_doc('Promotional Scheme') ps.name = '_Test Scheme' From 74bcb987f30dad26d66dd94caa6a3ca66eb00311 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Tue, 18 May 2021 14:52:50 +0530 Subject: [PATCH 263/369] style: fixed formatting --- .../promotional_scheme/promotional_scheme.py | 6 ++-- .../test_promotional_scheme.py | 28 +++++++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 69ed94676dd..2b7f1566381 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -85,12 +85,12 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}): if docname: pr = frappe.get_doc('Pricing Rule', docname[0].get('name')) temp_args[applicable_for] = applicable_for_value - pr = set_args(temp_args,pr,doc,child_doc,discount_fields,d) + pr = set_args(temp_args, pr, doc, child_doc, discount_fields, d) else: pr = frappe.new_doc("Pricing Rule") pr.title = make_autoname("{0}/.####".format(doc.name)) temp_args[applicable_for] = applicable_for_value - pr = set_args(temp_args,pr,doc,child_doc,discount_fields,d) + pr = set_args(temp_args, pr, doc, child_doc, discount_fields, d) new_doc.append(pr) @@ -101,7 +101,7 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}): pr.title = make_autoname("{0}/.####".format(doc.name)) temp_args = args.copy() temp_args[applicable_for] = args[applicable_for][i] - pr = set_args(temp_args,pr,doc,child_doc,discount_fields,d) + pr = set_args(temp_args, pr, doc, child_doc, discount_fields, d) new_doc.append(pr) return new_doc diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py index 99f9ed47e4a..7354ef036c5 100644 --- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py @@ -12,10 +12,10 @@ class TestPromotionalScheme(unittest.TestCase): price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name", "creation"], filters = {'promotional_scheme': ps.name}) self.assertTrue(len(price_rules),1) - price_doc_details = frappe.db.get_value('Pricing Rule',price_rules[0].name,['customer','min_qty','discount_percentage'],as_dict = 1) - self.assertTrue(price_doc_details.customer,'_Test Customer') - self.assertTrue(price_doc_details.min_qty,4) - self.assertTrue(price_doc_details.discount_percentage,20) + price_doc_details = frappe.db.get_value('Pricing Rule', price_rules[0].name, ['customer', 'min_qty', 'discount_percentage'], as_dict = 1) + self.assertTrue(price_doc_details.customer, '_Test Customer') + self.assertTrue(price_doc_details.min_qty, 4) + self.assertTrue(price_doc_details.discount_percentage, 20) ps.price_discount_slabs[0].min_qty = 6 ps.append('customer', { @@ -23,21 +23,21 @@ class TestPromotionalScheme(unittest.TestCase): ps.save() price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"], filters = {'promotional_scheme': ps.name}) - self.assertTrue(len(price_rules),2) + self.assertTrue(len(price_rules), 2) - price_doc_details = frappe.db.get_value('Pricing Rule',price_rules[1].name,['customer','min_qty','discount_percentage'],as_dict = 1) - self.assertTrue(price_doc_details.customer,'_Test Customer 2') - self.assertTrue(price_doc_details.min_qty,6) - self.assertTrue(price_doc_details.discount_percentage,20) + price_doc_details = frappe.db.get_value('Pricing Rule', price_rules[1].name, ['customer', 'min_qty', 'discount_percentage'], as_dict = 1) + self.assertTrue(price_doc_details.customer, '_Test Customer 2') + self.assertTrue(price_doc_details.min_qty, 6) + self.assertTrue(price_doc_details.discount_percentage, 20) - price_doc_details = frappe.db.get_value('Pricing Rule',price_rules[0].name,['customer','min_qty','discount_percentage'],as_dict = 1) - self.assertTrue(price_doc_details.customer,'_Test Customer') - self.assertTrue(price_doc_details.min_qty,6) + price_doc_details = frappe.db.get_value('Pricing Rule', price_rules[0].name, ['customer', 'min_qty', 'discount_percentage'], as_dict = 1) + self.assertTrue(price_doc_details.customer, '_Test Customer') + self.assertTrue(price_doc_details.min_qty, 6) - frappe.delete_doc('Promotional Scheme',ps.name) + frappe.delete_doc('Promotional Scheme', ps.name) price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"], filters = {'promotional_scheme': ps.name}) - self.assertEqual(price_rules,[]) + self.assertEqual(price_rules, []) def make_promotional_scheme(): ps = frappe.new_doc('Promotional Scheme') From b214e624bb8dde5fb0713fa4920306d97ea2f178 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Wed, 19 May 2021 16:18:37 +0530 Subject: [PATCH 264/369] style: fixed formatting --- .../promotional_scheme/promotional_scheme.py | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 2b7f1566381..53239feb429 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -30,8 +30,15 @@ class PromotionalScheme(Document): frappe.throw(_("Price or product discount slabs are required")) def on_update(self): - data = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name", "creation"], order_by = 'creation asc', - filters = {'promotional_scheme': self.name, 'applicable_for': self.applicable_for}) or {} + data = frappe.get_all( + 'Pricing Rule', + fields = ["promotional_scheme_id", "name", "creation"], + filters = { + 'promotional_scheme': self.name, + 'applicable_for': self.applicable_for + }, + order_by = 'creation asc', + ) or {} self.update_pricing_rules(data) def update_pricing_rules(self, data): @@ -77,10 +84,15 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}): for d in doc.get(child_doc): if d.name in rules: for applicable_for_value in args.get(applicable_for): - temp_args = args.copy() - docname = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name", applicable_for], - filters = {'promotional_scheme_id': d.name, applicable_for: applicable_for_value}) + docname = frappe.get_all( + 'Pricing Rule', + fields = ["promotional_scheme_id", "name", applicable_for], + filters = { + 'promotional_scheme_id': d.name, + applicable_for: applicable_for_value + } + ) if docname: pr = frappe.get_doc('Pricing Rule', docname[0].get('name')) @@ -139,7 +151,6 @@ def get_args_for_pricing_rule(doc): for applicable_for_values in doc.get(applicable_for): items.append(applicable_for_values.get(applicable_for)) args[d] = items - else: args[d] = doc.get(d) return args From dd86642037cfff7efb52aadab57a6454175e5b67 Mon Sep 17 00:00:00 2001 From: noahjacob Date: Mon, 24 May 2021 16:52:55 +0530 Subject: [PATCH 265/369] refactor: removed py2 code --- erpnext/accounts/doctype/campaign_item/campaign_item.py | 2 -- .../accounts/doctype/customer_group_item/customer_group_item.py | 2 -- erpnext/accounts/doctype/customer_item/customer_item.py | 2 -- .../accounts/doctype/sales_partner_item/sales_partner_item.py | 2 -- .../accounts/doctype/supplier_group_item/supplier_group_item.py | 2 -- erpnext/accounts/doctype/supplier_item/supplier_item.py | 2 -- erpnext/accounts/doctype/territory_item/territory_item.py | 2 -- 7 files changed, 14 deletions(-) diff --git a/erpnext/accounts/doctype/campaign_item/campaign_item.py b/erpnext/accounts/doctype/campaign_item/campaign_item.py index e5ca7e53684..4f5fd7f7d78 100644 --- a/erpnext/accounts/doctype/campaign_item/campaign_item.py +++ b/erpnext/accounts/doctype/campaign_item/campaign_item.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/erpnext/accounts/doctype/customer_group_item/customer_group_item.py b/erpnext/accounts/doctype/customer_group_item/customer_group_item.py index ea24788ad56..df782ac9e0c 100644 --- a/erpnext/accounts/doctype/customer_group_item/customer_group_item.py +++ b/erpnext/accounts/doctype/customer_group_item/customer_group_item.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/erpnext/accounts/doctype/customer_item/customer_item.py b/erpnext/accounts/doctype/customer_item/customer_item.py index 4aad84ec140..a577145e4ee 100644 --- a/erpnext/accounts/doctype/customer_item/customer_item.py +++ b/erpnext/accounts/doctype/customer_item/customer_item.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py index 9e6564845b8..9796c7b0ccf 100644 --- a/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py +++ b/erpnext/accounts/doctype/sales_partner_item/sales_partner_item.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py index 5ea2635482d..de0444ee193 100644 --- a/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py +++ b/erpnext/accounts/doctype/supplier_group_item/supplier_group_item.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/erpnext/accounts/doctype/supplier_item/supplier_item.py b/erpnext/accounts/doctype/supplier_item/supplier_item.py index b58b5dc057b..ad66e230c80 100644 --- a/erpnext/accounts/doctype/supplier_item/supplier_item.py +++ b/erpnext/accounts/doctype/supplier_item/supplier_item.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document diff --git a/erpnext/accounts/doctype/territory_item/territory_item.py b/erpnext/accounts/doctype/territory_item/territory_item.py index 9adfc005721..d46edc9dca8 100644 --- a/erpnext/accounts/doctype/territory_item/territory_item.py +++ b/erpnext/accounts/doctype/territory_item/territory_item.py @@ -1,8 +1,6 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals # import frappe from frappe.model.document import Document From 10ce2f5d6ebcacf16604930e399659cb2eadb740 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Wed, 4 Aug 2021 21:10:25 +0530 Subject: [PATCH 266/369] refactor: added naming series for pricing rule --- .../doctype/pricing_rule/pricing_rule.json | 18 +++++++++++++----- .../promotional_scheme/promotional_scheme.py | 18 +++++++++--------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 0be41b40635..99c5b34fa35 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -2,12 +2,13 @@ "actions": [], "allow_import": 1, "allow_rename": 1, - "autoname": "field:title", + "autoname": "naming_series:", "creation": "2014-02-21 15:02:51", "doctype": "DocType", "engine": "InnoDB", "field_order": [ "applicability_section", + "naming_series", "title", "disable", "apply_on", @@ -95,8 +96,7 @@ "fieldtype": "Data", "label": "Title", "no_copy": 1, - "reqd": 1, - "unique": 1 + "reqd": 1 }, { "default": "0", @@ -571,6 +571,13 @@ "fieldname": "is_recursive", "fieldtype": "Check", "label": "Is Recursive" + }, + { + "default": "PRLE-.####", + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "PRLE-.####" } ], "icon": "fa fa-gift", @@ -634,5 +641,6 @@ ], "show_name_in_global_search": 1, "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file + "sort_order": "DESC", + "title_field": "title" +} diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 53239feb429..f4ee1887c49 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -81,7 +81,7 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}): new_doc = [] args = get_args_for_pricing_rule(doc) applicable_for = frappe.scrub(doc.get('applicable_for')) - for d in doc.get(child_doc): + for idx, d in enumerate(doc.get(child_doc)): if d.name in rules: for applicable_for_value in args.get(applicable_for): temp_args = args.copy() @@ -93,24 +93,24 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}): applicable_for: applicable_for_value } ) - + if docname: pr = frappe.get_doc('Pricing Rule', docname[0].get('name')) temp_args[applicable_for] = applicable_for_value pr = set_args(temp_args, pr, doc, child_doc, discount_fields, d) else: pr = frappe.new_doc("Pricing Rule") - pr.title = make_autoname("{0}/.####".format(doc.name)) + pr.title = doc.name temp_args[applicable_for] = applicable_for_value pr = set_args(temp_args, pr, doc, child_doc, discount_fields, d) - + new_doc.append(pr) - + else: for i in range(len(args.get(applicable_for))) : pr = frappe.new_doc("Pricing Rule") - pr.title = make_autoname("{0}/.####".format(doc.name)) + pr.title = doc.name temp_args = args.copy() temp_args[applicable_for] = args[applicable_for][i] pr = set_args(temp_args, pr, doc, child_doc, discount_fields, d) @@ -144,13 +144,13 @@ def set_args(args, pr, doc, child_doc, discount_fields, child_doc_fields): def get_args_for_pricing_rule(doc): args = { 'promotional_scheme': doc.name } applicable_for = frappe.scrub(doc.get('applicable_for')) - + for d in pricing_rule_fields: if d == applicable_for: items = [] for applicable_for_values in doc.get(applicable_for): - items.append(applicable_for_values.get(applicable_for)) - args[d] = items + items.append(applicable_for_values.get(applicable_for)) + args[d] = items else: args[d] = doc.get(d) return args From 2ab62a44845d6d9dcda249759b3ad3131cc50f3a Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 10 Aug 2021 11:43:59 +0530 Subject: [PATCH 267/369] fix: Missing method reset_issue_metrics added back to Issue doctype (#26574) --- erpnext/support/doctype/issue/issue.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 9c69deb6a48..e4c4af03651 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -222,6 +222,10 @@ class Issue(Document): }).insert(ignore_permissions=True) return replicated_issue.name + + def reset_issue_metrics(self): + self.db_set("resolution_time", None) + self.db_set("user_resolution_time", None) def before_insert(self): if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): From 06b6b7e3cca968abc18e7136b8567ade9192003a Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 10 Aug 2021 13:10:46 +0530 Subject: [PATCH 268/369] fix: Set CWIP Account in company at the start to avoid flaky test --- .../doctype/landed_cost_voucher/test_landed_cost_voucher.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py index 128a2ab62ff..cb09d933801 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/test_landed_cost_voucher.py @@ -253,6 +253,8 @@ class TestLandedCostVoucher(unittest.TestCase): def test_asset_lcv(self): "Check if LCV for an Asset updates the Assets Gross Purchase Amount correctly." + frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC") + if not frappe.db.exists("Asset Category", "Computers"): create_asset_category() @@ -265,7 +267,6 @@ class TestLandedCostVoucher(unittest.TestCase): assets = frappe.db.get_all('Asset', filters={'purchase_receipt': pr.name}) self.assertEqual(len(assets), 1) - frappe.db.set_value("Company", pr.company, "capital_work_in_progress_account", "CWIP Account - _TC") lcv = make_landed_cost_voucher( company = pr.company, receipt_document_type = "Purchase Receipt", From 24da00cada12c20973c9ea3c6914b8fb60c5754f Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 10 Aug 2021 13:17:41 +0530 Subject: [PATCH 269/369] fix: updating lead status while customer creation (#26607) * fix: updating lead status while customer creation * fix: changes requested --- erpnext/selling/doctype/customer/customer.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 818888c0c12..9785f6c7a9f 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -134,9 +134,7 @@ class Customer(TransactionBase): '''If Customer created from Lead, update lead status to "Converted" update Customer link in Quotation, Opportunity''' if self.lead_name: - lead = frappe.get_doc('Lead', self.lead_name) - lead.status = 'Converted' - lead.save() + frappe.db.set_value("Lead", self.lead_name, "status", "Converted") def create_lead_address_contact(self): if self.lead_name: From 57191985761b6963cf2dd7c44c8300c0a86f1fe8 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 10 Aug 2021 13:56:48 +0530 Subject: [PATCH 270/369] feat: dynamic conditions for applying SLA (#26806) --- erpnext/support/doctype/issue/issue.py | 6 +-- .../service_level_agreement.json | 28 ++++++++++- .../service_level_agreement.py | 48 +++++++++++++++---- 3 files changed, 68 insertions(+), 14 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index e4c4af03651..b48925d33ad 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -235,8 +235,7 @@ class Issue(Document): self.set_response_and_resolution_time() def set_response_and_resolution_time(self, priority=None, service_level_agreement=None): - service_level_agreement = get_active_service_level_agreement_for(priority=priority, - customer=self.customer, service_level_agreement=service_level_agreement) + service_level_agreement = get_active_service_level_agreement_for(self) if not service_level_agreement: if frappe.db.get_value("Issue", self.name, "service_level_agreement"): @@ -247,7 +246,8 @@ class Issue(Document): frappe.throw(_("This Service Level Agreement is specific to Customer {0}").format(service_level_agreement.customer)) self.service_level_agreement = service_level_agreement.name - self.priority = service_level_agreement.default_priority if not priority else priority + if not self.priority: + self.priority = service_level_agreement.default_priority priority = get_priority(self) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index 939c1999828..1678f04def8 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -18,6 +18,10 @@ "entity_type", "column_break_10", "entity", + "filters_section", + "condition", + "column_break_15", + "condition_description", "agreement_details_section", "start_date", "active", @@ -171,10 +175,30 @@ "fieldtype": "Table", "label": "Pause SLA On", "options": "Pause SLA On Status" + }, + { + "fieldname": "filters_section", + "fieldtype": "Section Break", + "label": "Assignment Condition" + }, + { + "fieldname": "column_break_15", + "fieldtype": "Column Break" + }, + { + "fieldname": "condition", + "fieldtype": "Code", + "label": "Condition", + "options": "Python" + }, + { + "fieldname": "condition_description", + "fieldtype": "HTML", + "options": "

                                Condition Examples:

                                \n
                                doc.status==\"Open\"
                                doc.due_date==nowdate()
                                doc.total > 40000\n
                                " } ], "links": [], - "modified": "2020-06-10 12:30:15.050785", + "modified": "2021-07-27 11:16:45.596579", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", @@ -208,4 +232,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 70c469663b7..ec0237e2eac 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -3,10 +3,12 @@ # For license information, please see license.txt from __future__ import unicode_literals + import frappe from frappe.model.document import Document from frappe import _ -from frappe.utils import getdate, get_weekdays, get_link_to_form +from frappe.utils import getdate, get_weekdays, get_link_to_form, nowdate +from frappe.utils.safe_exec import get_safe_globals class ServiceLevelAgreement(Document): @@ -14,6 +16,7 @@ class ServiceLevelAgreement(Document): self.validate_doc() self.check_priorities() self.check_support_and_resolution() + self.validate_condition() def check_priorities(self): default_priority = [] @@ -92,6 +95,14 @@ class ServiceLevelAgreement(Document): if frappe.db.exists("Service Level Agreement", {"entity_type": self.entity_type, "entity": self.entity, "name": ["!=", self.name]}): frappe.throw(_("Service Level Agreement with Entity Type {0} and Entity {1} already exists.").format(self.entity_type, self.entity)) + def validate_condition(self): + temp_doc = frappe.new_doc('Issue') + if self.condition: + try: + frappe.safe_eval(self.condition, None, get_context(temp_doc)) + except Exception: + frappe.throw(_("The Condition '{0}' is invalid").format(self.condition)) + def get_service_level_agreement_priority(self, priority): priority = frappe.get_doc("Service Level Priority", {"priority": priority, "parent": self.name}) @@ -112,7 +123,7 @@ def check_agreement_status(): if doc.end_date and getdate(doc.end_date) < getdate(frappe.utils.getdate()): frappe.db.set_value("Service Level Agreement", service_level_agreement.name, "active", 0) -def get_active_service_level_agreement_for(priority, customer=None, service_level_agreement=None): +def get_active_service_level_agreement_for(doc): if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): return @@ -121,23 +132,42 @@ def get_active_service_level_agreement_for(priority, customer=None, service_leve ["Service Level Agreement", "enable", "=", 1] ] - if priority: - filters.append(["Service Level Priority", "priority", "=", priority]) + if doc.get('priority'): + filters.append(["Service Level Priority", "priority", "=", doc.get('priority')]) + customer = doc.get('customer') or_filters = [ ["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer)]] ] + + service_level_agreement = doc.get('service_level_agreement') if service_level_agreement: or_filters = [ - ["Service Level Agreement", "name", "=", service_level_agreement], + ["Service Level Agreement", "name", "=", doc.get('service_level_agreement')], ] - or_filters.append(["Service Level Agreement", "default_service_level_agreement", "=", 1]) + default_sla_filter = filters + [["Service Level Agreement", "default_service_level_agreement", "=", 1]] + default_sla = frappe.get_all("Service Level Agreement", filters=default_sla_filter, + fields=["name", "default_priority", "condition"]) - agreement = frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters, - fields=["name", "default_priority"]) + filters += [["Service Level Agreement", "default_service_level_agreement", "=", 0]] + agreements = frappe.get_all("Service Level Agreement", filters=filters, or_filters=or_filters, + fields=["name", "default_priority", "condition"]) + + # check if the current document on which SLA is to be applied fulfills all the conditions + filtered_agreements = [] + for agreement in agreements: + condition = agreement.get('condition') + if not condition or (condition and frappe.safe_eval(condition, None, get_context(doc))): + filtered_agreements.append(agreement) - return agreement[0] if agreement else None + # if any default sla + filtered_agreements += default_sla + + return filtered_agreements[0] if filtered_agreements else None + +def get_context(doc): + return {"doc": doc.as_dict(), "nowdate": nowdate, "frappe": frappe._dict(utils=get_safe_globals().get("frappe").get("utils"))} def get_customer_group(customer): if customer: From f22b85825370fe75b8df694f3c8f86a572dc411b Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 22 Jul 2021 13:23:54 +0530 Subject: [PATCH 271/369] fix: Clean Serial No input on Server Side --- erpnext/controllers/stock_controller.py | 7 +++++++ erpnext/public/js/controllers/transaction.js | 2 +- erpnext/stock/doctype/serial_no/serial_no.py | 10 ++++++++-- erpnext/stock/doctype/stock_entry/stock_entry.py | 1 + .../stock_reconciliation/stock_reconciliation.py | 1 + 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 17bd7354f93..17707ecae7f 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -27,6 +27,7 @@ class StockController(AccountsController): if not self.get('is_return'): self.validate_inspection() self.validate_serialized_batch() + self.clean_serial_nos() self.validate_customer_provided_item() self.set_rate_of_stock_uom() self.validate_internal_transfer() @@ -72,6 +73,12 @@ class StockController(AccountsController): frappe.throw(_("Row #{0}: The batch {1} has already expired.") .format(d.idx, get_link_to_form("Batch", d.get("batch_no")))) + def clean_serial_nos(self): + for row in self.get("items"): + if hasattr(row, "serial_no") and row.serial_no: + # replace commas by linefeed and remove all spaces in string + row.serial_no = row.serial_no.replace(",", "\n").replace(" ", "") + def get_gl_entries(self, warehouse_account=None, default_expense_account=None, default_cost_center=None): diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 5475383759f..b9fa9b7ebbd 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -732,7 +732,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ this.frm.trigger("item_code", cdt, cdn); } else { - // Replacing all occurences of comma with carriage return + // Replace all occurences of comma with line feed item.serial_no = item.serial_no.replace(/,/g, '\n'); item.conversion_factor = item.conversion_factor || 1; refresh_field("serial_no", item.name, item.parentfield); diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index bad7b608acf..70312bc543b 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -165,8 +165,14 @@ class SerialNo(StockController): ) ORDER BY posting_date desc, posting_time desc, creation desc""", - (self.item_code, self.company, - serial_no, serial_no+'\n%', '%\n'+serial_no, '%\n'+serial_no+'\n%'), as_dict=1): + ( + self.item_code, self.company, + serial_no, + serial_no+'\n%', + '%\n'+serial_no, + '%\n'+serial_no+'\n%' + ), + as_dict=1): if serial_no.upper() in get_serial_nos(sle.serial_no): if cint(sle.actual_qty) > 0: sle_dict.setdefault("incoming", []).append(sle) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 654755ec2fb..3ff42bf9742 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -76,6 +76,7 @@ class StockEntry(StockController): self.validate_difference_account() self.set_job_card_data() self.set_purpose_for_stock_entry() + self.clean_serial_nos() self.validate_duplicate_serial_no() if not self.from_bom: diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 2e092865075..324bb7a62d9 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -31,6 +31,7 @@ class StockReconciliation(StockController): self.validate_expense_account() self.validate_customer_provided_item() self.set_zero_value_for_customer_provided_items() + self.clean_serial_nos() self.set_total_qty_and_amount() self.validate_putaway_capacity() From 510e31952d8f557530ba83d05dad11509447875d Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 10 Aug 2021 14:00:55 +0530 Subject: [PATCH 272/369] test: Serial no sanitation --- .../stock/doctype/serial_no/test_serial_no.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py index cde7fe07c63..b9a58cf43e4 100644 --- a/erpnext/stock/doctype/serial_no/test_serial_no.py +++ b/erpnext/stock/doctype/serial_no/test_serial_no.py @@ -174,5 +174,23 @@ class TestSerialNo(unittest.TestCase): self.assertEqual(sn_doc.warehouse, "_Test Warehouse - _TC") self.assertEqual(sn_doc.purchase_document_no, se.name) + def test_serial_no_sanitation(self): + "Test if Serial No input is sanitised before entering the DB." + item_code = "_Test Serialized Item" + test_records = frappe.get_test_records('Stock Entry') + + se = frappe.copy_doc(test_records[0]) + se.get("items")[0].item_code = item_code + se.get("items")[0].qty = 3 + se.get("items")[0].serial_no = " _TS1, _TS2 , _TS3 " + se.get("items")[0].transfer_qty = 3 + se.set_stock_entry_type() + se.insert() + se.submit() + + self.assertEqual(se.get("items")[0].serial_no, "_TS1\n_TS2\n_TS3") + + frappe.db.rollback() + def tearDown(self): frappe.db.rollback() \ No newline at end of file From 1ba04fdfe542455bd703a56a48f4f65c1ac5f3e9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Aug 2021 15:47:36 +0530 Subject: [PATCH 273/369] fix: pos profile not mandatory for Sales Invoice (#26876) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 2539de7b124..eba8ba830fc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -480,7 +480,7 @@ class SalesInvoice(SellingController): if not self.pos_profile: pos_profile = get_pos_profile(self.company) or {} if not pos_profile: - frappe.throw(_("No POS Profile found. Please create a New POS Profile first")) + return self.pos_profile = pos_profile.get('name') pos = {} From 793063bf4eb324d606be83e7424691148e4aa227 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 10 Aug 2021 15:57:30 +0530 Subject: [PATCH 274/369] fix: pos return payment mode issue (#26875) --- erpnext/public/js/controllers/taxes_and_totals.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 53d5278bbf4..9d8fcb64f7f 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -47,7 +47,10 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ if (in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) && this.frm.doc.is_pos && this.frm.doc.is_return) { - this.update_paid_amount_for_return(); + if (this.frm.doc.doctype == "Sales Invoice") { + this.set_total_amount_to_default_mop(); + } + this.calculate_paid_amount(); } // Sales person's commission @@ -730,7 +733,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ } }, - update_paid_amount_for_return: function() { + set_total_amount_to_default_mop: function() { var grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total; if(this.frm.doc.party_account_currency == this.frm.doc.currency) { @@ -743,17 +746,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ precision("base_grand_total") ); } - this.frm.doc.payments.find(pay => { if (pay.default) { pay.amount = total_amount_to_pay; } else { - pay.amount = 0.0 + pay.amount = 0.0; } }); this.frm.refresh_fields(); - - this.calculate_paid_amount(); }, set_default_payment: function(total_amount_to_pay, update_paid_amount) { From 363225d2ba4d411beeae1d68950412e5c15238ea Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 10 Aug 2021 16:37:23 +0530 Subject: [PATCH 275/369] fix(asset): incorrect date difference calculation (#26805) --- erpnext/assets/doctype/asset/test_asset.py | 10 +++++----- erpnext/regional/india/utils.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 59fbe3b0301..e23a7154524 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -639,7 +639,7 @@ class TestAsset(unittest.TestCase): asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') asset = frappe.get_doc('Asset', asset_name) asset.calculate_depreciation = 1 - asset.available_for_use_date = '2030-06-12' + asset.available_for_use_date = '2030-07-12' asset.purchase_date = '2030-01-01' asset.append("finance_books", { "expected_value_after_useful_life": 1000, @@ -653,10 +653,10 @@ class TestAsset(unittest.TestCase): self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0) expected_schedules = [ - ["2030-12-31", 1106.85, 1106.85], - ["2031-12-31", 3446.58, 4553.43], - ["2032-12-31", 1723.29, 6276.72], - ["2033-06-12", 723.28, 7000.00] + ["2030-12-31", 942.47, 942.47], + ["2031-12-31", 3528.77, 4471.24], + ["2032-12-31", 1764.38, 6235.62], + ["2033-07-12", 764.38, 7000.00] ] schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)] diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 88c350ac899..a152797a5d4 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -851,7 +851,7 @@ def get_depreciation_amount(asset, depreciable_value, row): # if its the first depreciation if depreciable_value == asset.gross_purchase_amount: # as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2 - diff = date_diff(asset.available_for_use_date, row.depreciation_start_date) + diff = date_diff(row.depreciation_start_date, asset.available_for_use_date) if diff <= 180: rate_of_depreciation = rate_of_depreciation / 2 frappe.msgprint( From a7e0805039c997caf15f777d8a0a12ae83c91243 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Aug 2021 17:16:15 +0530 Subject: [PATCH 276/369] fix(e-invoicing): cannot cancel invoice if IRN cancelled on portal (#26879) --- erpnext/patches.txt | 1 + .../v12_0/show_einvoice_irn_cancelled_field.py | 12 ++++++++++++ erpnext/regional/india/setup.py | 4 ++-- 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ada3badd7c7..a2b99dc934c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -294,6 +294,7 @@ erpnext.patches.v13_0.update_level_in_bom #1234sswef erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry erpnext.patches.v13_0.update_subscription_status_in_memberships erpnext.patches.v13_0.update_amt_in_work_order_required_items +erpnext.patches.v12_0.show_einvoice_irn_cancelled_field erpnext.patches.v13_0.delete_orphaned_tables erpnext.patches.v13_0.update_export_type_for_gst erpnext.patches.v13_0.update_tds_check_field #3 diff --git a/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py b/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py new file mode 100644 index 00000000000..2319c17b34c --- /dev/null +++ b/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + irn_cancelled_field = frappe.db.exists('Custom Field', {'dt': 'Sales Invoice', 'fieldname': 'irn_cancelled'}) + if irn_cancelled_field: + frappe.db.set_value('Custom Field', irn_cancelled_field, 'depends_on', 'eval: doc.irn') + frappe.db.set_value('Custom Field', irn_cancelled_field, 'read_only', 0) diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index e9372f9b8fc..b4f146ce57e 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -457,7 +457,7 @@ def make_custom_fields(update=True): depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'), dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1, - depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'), + depends_on='eval: doc.irn', allow_on_submit=1, insert_after='customer'), dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1, depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill'), @@ -985,4 +985,4 @@ def create_gratuity_rule(): def update_accounts_settings_for_taxes(): if frappe.db.count('Company') == 1: - frappe.db.set_value('Accounts Settings', None, "add_taxes_from_item_tax_template", 0) \ No newline at end of file + frappe.db.set_value('Accounts Settings', None, "add_taxes_from_item_tax_template", 0) From 506ecb421676d53c90819df021131d715845dae5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 20 Jun 2021 12:42:06 +0530 Subject: [PATCH 277/369] feat: Organizational Chart --- erpnext/hr/doctype/employee/employee.py | 5 +- .../hr/page/organizational_chart/__init__.py | 0 .../page/organizational_chart/node_card.html | 27 ++ .../organizational_chart.js | 409 ++++++++++++++++++ .../organizational_chart.json | 26 ++ .../organizational_chart.py | 49 +++ erpnext/public/build.json | 3 +- erpnext/public/scss/organizational_chart.scss | 209 +++++++++ 8 files changed, 725 insertions(+), 3 deletions(-) create mode 100644 erpnext/hr/page/organizational_chart/__init__.py create mode 100644 erpnext/hr/page/organizational_chart/node_card.html create mode 100644 erpnext/hr/page/organizational_chart/organizational_chart.js create mode 100644 erpnext/hr/page/organizational_chart/organizational_chart.json create mode 100644 erpnext/hr/page/organizational_chart/organizational_chart.py create mode 100644 erpnext/public/scss/organizational_chart.scss diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 5ca47560b10..96046fba817 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -477,13 +477,14 @@ def get_employee_emails(employee_list): return employee_emails @frappe.whitelist() -def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False): +def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False, fields=None): filters = [['status', '=', 'Active']] if company and company != 'All Companies': filters.append(['company', '=', company]) - fields = ['name as value', 'employee_name as title'] + if not fields: + fields = ['name as value', 'employee_name as title'] if is_root: parent = '' diff --git a/erpnext/hr/page/organizational_chart/__init__.py b/erpnext/hr/page/organizational_chart/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/page/organizational_chart/node_card.html b/erpnext/hr/page/organizational_chart/node_card.html new file mode 100644 index 00000000000..057c45ee864 --- /dev/null +++ b/erpnext/hr/page/organizational_chart/node_card.html @@ -0,0 +1,27 @@ +
                                +
                                +
                                + + + +
                                +
                                +
                                + {{ name }} +
                                + {{ frappe.utils.icon("edit", "xs") }} + {{ __("Edit") }} +
                                +
                                +
                                +
                                {{ title }}
                                + + {% if connections == 1 %} +
                                · {{ connections }} {{ __("Connection") }}
                                + {% else %} +
                                · {{ connections }} {{ __("Connections") }}
                                + {% endif %} +
                                +
                                +
                                +
                                \ No newline at end of file diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js new file mode 100644 index 00000000000..04bd9422bd9 --- /dev/null +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -0,0 +1,409 @@ +frappe.pages['organizational-chart'].on_page_load = function(wrapper) { + frappe.ui.make_app_page({ + parent: wrapper, + title: __('Organizational Chart'), + single_column: true + }); + + let organizational_chart = new OrganizationalChart(wrapper); + $(wrapper).bind('show', ()=> { + organizational_chart.show(); + }); +}; + +class OrganizationalChart { + + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = wrapper.page; + + this.page.main.css({ + 'min-height': '300px', + 'max-height': '600px', + 'overflow': 'auto', + 'position': 'relative' + }); + this.page.main.addClass('frappe-card'); + + this.nodes = {}; + this.setup_node_class(); + } + + setup_node_class() { + let me = this; + this.Node = class { + constructor({ + id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line + }) { + // to setup values passed via constructor + $.extend(this, arguments[0]); + + this.expanded = 0; + + me.nodes[this.id] = this; + me.make_node_element(this); + me.setup_node_click_action(this); + } + } + } + + make_node_element(node) { + let node_card = frappe.render_template('node_card', { + id: node.id, + name: node.name, + title: node.title, + image: node.image, + parent: node.parent_id, + connections: node.connections + }); + + node.parent.append(node_card); + node.$link = $(`#${node.id}`); + } + + show() { + frappe.breadcrumbs.add('HR'); + + let me = this; + let company = this.page.add_field({ + fieldtype: 'Link', + options: 'Company', + fieldname: 'company', + placeholder: __('Select Company'), + default: frappe.defaults.get_default('company'), + only_select: true, + reqd: 1, + change: () => { + me.company = undefined; + + if (company.get_value() && me.company != company.get_value()) { + me.company = company.get_value(); + + // svg for connectors + me.make_svg_markers() + + if (me.$hierarchy) + me.$hierarchy.remove(); + + // setup hierarchy + me.$hierarchy = $( + `
                                  +
                                • +
                                `); + + me.page.main.append(me.$hierarchy); + me.render_root_node(); + } + } + }); + + company.refresh(); + $(`[data-fieldname="company"]`).trigger('change'); + } + + make_svg_markers() { + $('#arrows').remove(); + + this.page.main.prepend(` + + + + + + + + + + + + + + + + + + + `); + } + + render_root_node() { + this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; + + let me = this; + + frappe.call({ + method: me.method, + args: { + company: me.company + }, + callback: function(r) { + if (r.message.length) { + let data = r.message[0]; + + let root_node = new me.Node({ + id: data.name, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: true, + connections: data.connections, + is_root: true, + }); + + me.expand_node(root_node); + } + } + }) + } + + expand_node(node) { + let is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id; + this.set_selected_node(node); + this.show_active_path(node); + this.collapse_previous_level_nodes(node); + + // since the previous node collapses, all connections to that node need to be rebuilt + // if a sibling node is clicked, connections don't need to be rebuilt + if (!is_sibling) { + // rebuild outgoing connections + this.refresh_connectors(node.parent_id); + + // rebuild incoming connections + let grandparent = $(`#${node.parent_id}`).attr('data-parent'); + this.refresh_connectors(grandparent) + } + + if (node.expandable && !node.expanded) { + return this.load_children(node); + } + } + + collapse_node() { + if (this.selected_node.expandable) { + this.selected_node.$children.hide(); + $(`path[data-parent="${this.selected_node.id}"]`).hide(); + this.selected_node.expanded = false; + } + } + + show_active_path(node) { + // mark node parent on active path + $(`#${node.parent_id}`).addClass('active-path'); + } + + load_children(node) { + frappe.run_serially([ + () => this.get_child_nodes(node.id), + (child_nodes) => this.render_child_nodes(node, child_nodes) + ]); + } + + get_child_nodes(node_id) { + let me = this; + return new Promise(resolve => { + frappe.call({ + method: this.method, + args: { + parent: node_id, + company: me.company + }, + callback: (r) => { + resolve(r.message); + } + }); + }); + } + + render_child_nodes(node, child_nodes) { + const last_level = this.$hierarchy.find('.level:last').index(); + const current_level = $(`#${node.id}`).parent().parent().parent().index(); + + if (last_level === current_level) { + this.$hierarchy.append(` +
                              • + `); + } + + if (!node.$children) { + node.$children = $('
                                  ') + .hide() + .appendTo(this.$hierarchy.find('.level:last')); + + node.$children.empty(); + + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_node(node, data); + + setTimeout(() => { + this.add_connector(node.id, data.name); + }, 250); + }); + } + } + + node.$children.show(); + $(`path[data-parent="${node.id}"]`).show(); + node.expanded = true; + } + + add_node(node, data) { + var $li = $('
                                • '); + + return new this.Node({ + id: data.name, + parent: $li.appendTo(node.$children), + parent_id: node.id, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: data.expandable, + connections: data.connections, + children: undefined + }); + } + + add_connector(parent_id, child_id) { + let parent_node = document.querySelector(`#${parent_id}`); + let child_node = document.querySelector(`#${child_id}`); + + // variable for the namespace + const svgns = 'http://www.w3.org/2000/svg'; + let path = document.createElementNS(svgns, 'path'); + + // we need to connect right side of the parent to the left side of the child node + let pos_parent_right = { + x: parent_node.offsetLeft + parent_node.offsetWidth, + y: parent_node.offsetTop + parent_node.offsetHeight / 2 + }; + let pos_child_left = { + x: child_node.offsetLeft - 5, + y: child_node.offsetTop + child_node.offsetHeight / 2 + }; + + let connector = + "M" + + (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + + "C" + + (pos_parent_right.x + 100) + "," + (pos_parent_right.y) + " " + + (pos_child_left.x - 100) + "," + (pos_child_left.y) + " " + + (pos_child_left.x) + "," + (pos_child_left.y); + + path.setAttribute("d", connector); + path.setAttribute("data-parent", parent_id); + path.setAttribute("data-child", child_id); + + if ($(`#${parent_id}`).hasClass('active')) { + path.setAttribute("class", "active-connector"); + path.setAttribute("marker-start", "url(#arrowstart-active)"); + path.setAttribute("marker-end", "url(#arrowhead-active)"); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + path.setAttribute("class", "collapsed-connector"); + path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); + path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); + } + + $('#connectors').append(path); + } + + set_selected_node(node) { + // remove .active class from the current node + $('.active').removeClass('active'); + + // add active class to the newly selected node + this.selected_node = node; + node.$link.addClass('active'); + } + + collapse_previous_level_nodes(node) { + let node_parent = $(`#${node.parent_id}`); + + let previous_level_nodes = node_parent.parent().parent().children('li'); + if (node_parent.parent().hasClass('root-level')) { + previous_level_nodes = node_parent.parent().children('li'); + } + + let node_card = undefined; + + previous_level_nodes.each(function() { + node_card = $(this).find('.node-card'); + + if (!node_card.hasClass('active-path')) { + node_card.addClass('collapsed'); + } + }); + } + + refresh_connectors(node_parent) { + if (!node_parent) return; + + $(`path[data-parent="${node_parent}"]`).remove(); + + frappe.run_serially([ + () => this.get_child_nodes(node_parent), + (child_nodes) => { + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_connector(node_parent, data.name); + }); + } + } + ]); + } + + setup_node_click_action(node) { + let me = this; + let node_element = $(`#${node.id}`); + + node_element.click(function() { + let is_sibling = me.selected_node.parent_id === node.parent_id; + + if (is_sibling) { + me.collapse_node(); + } else if (node_element.is(':visible') + && (node_element.hasClass('collapsed') || node_element.hasClass('active-path'))) { + me.remove_levels_after_node(node); + me.remove_orphaned_connectors(); + } + + me.expand_node(node); + }); + } + + remove_levels_after_node(node) { + let level = $(`#${node.id}`).parent().parent().parent(); + + if ($(`#${node.id}`).parent().hasClass('root-level')) { + level = $(`#${node.id}`).parent(); + } + + level = $('.hierarchy > li:eq('+ level.index() + ')'); + level.nextAll('li').remove(); + + let nodes = level.find('.node-card'); + let node_object = undefined; + + $.each(nodes, (_i, element) => { + node_object = this.nodes[element.id]; + node_object.expanded = 0; + node_object.$children = undefined; + }); + + nodes.removeClass('collapsed active-path'); + } + + remove_orphaned_connectors() { + let paths = $('#connectors > path'); + $.each(paths, (_i, path) => { + let parent = $(path).data('parent'); + let child = $(path).data('child'); + + if ($(parent).length || $(child).length) + return; + + $(path).remove(); + }) + } +} diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.json b/erpnext/hr/page/organizational_chart/organizational_chart.json new file mode 100644 index 00000000000..d802781320a --- /dev/null +++ b/erpnext/hr/page/organizational_chart/organizational_chart.json @@ -0,0 +1,26 @@ +{ + "content": null, + "creation": "2021-05-25 10:53:10.107241", + "docstatus": 0, + "doctype": "Page", + "idx": 0, + "modified": "2021-05-25 10:53:18.201931", + "modified_by": "Administrator", + "module": "HR", + "name": "organizational-chart", + "owner": "Administrator", + "page_name": "Organizational Chart", + "roles": [ + { + "role": "HR User" + }, + { + "role": "HR Manager" + } + ], + "script": null, + "standard": "Yes", + "style": null, + "system_page": 0, + "title": "Organizational Chart" +} \ No newline at end of file diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py new file mode 100644 index 00000000000..be2964530b8 --- /dev/null +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -0,0 +1,49 @@ +from __future__ import unicode_literals +import frappe + +@frappe.whitelist() +def get_children(parent=None, company=None, is_root=False, is_tree=False, fields=None): + + filters = [['status', '!=', 'Left']] + if company and company != 'All Companies': + filters.append(['company', '=', company]) + + if not fields: + fields = ['employee_name', 'name', 'reports_to', 'image', 'designation'] + + if is_root: + parent = '' + if parent and company and parent!=company: + filters.append(['reports_to', '=', parent]) + else: + filters.append(['reports_to', '=', '']) + + employees = frappe.get_list('Employee', fields=fields, + filters=filters, order_by='name') + + for employee in employees: + is_expandable = frappe.get_all('Employee', filters=[ + ['reports_to', '=', employee.get('name')] + ]) + employee.connections = get_connections(employee.name) + employee.expandable = 1 if is_expandable else 0 + + return employees + + +def get_connections(employee): + num_connections = 0 + + connections = frappe.get_list('Employee', filters=[ + ['reports_to', '=', employee] + ]) + num_connections += len(connections) + + while connections: + for entry in connections: + connections = frappe.get_list('Employee', filters=[ + ['reports_to', '=', entry.name] + ]) + num_connections += len(connections) + + return num_connections \ No newline at end of file diff --git a/erpnext/public/build.json b/erpnext/public/build.json index 7a3cb838a99..d3ebcdf7e7b 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -3,7 +3,8 @@ "public/less/erpnext.less", "public/less/hub.less", "public/scss/call_popup.scss", - "public/scss/point-of-sale.scss" + "public/scss/point-of-sale.scss", + "public/scss/organizational_chart.scss" ], "css/marketplace.css": [ "public/less/hub.less" diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/organizational_chart.scss new file mode 100644 index 00000000000..62f6ddcb6e0 --- /dev/null +++ b/erpnext/public/scss/organizational_chart.scss @@ -0,0 +1,209 @@ +.node-card { + background: white; + stroke: 1px solid var(--gray-200); + box-shadow: var(--shadow-base); + border-radius: 0.5rem; + padding: 0.75rem; + margin-left: 3rem; + width: 18rem; + + .btn-edit-node { + display: none; + } + + .edit-chart-node { + display: none; + } + + .node-edit-icon { + display: none; + } +} + +.node-image { + width: 3.0rem; + height: 3.0rem; +} + +.node-name { + font-size: 1rem; + line-height: 1.72; +} + +.node-title { + font-size: 0.75rem; + line-height: 1.35; +} + +.node-connections { + font-size: 0.75rem; + line-height: 1.35; +} + +.node-card.active { + background: var(--blue-50); + border: 1px solid var(--blue-500); + box-shadow: var(--shadow-md); + border-radius: 0.5rem; + padding: 0.75rem; + width: 18rem; + height: 5rem; + + .btn-edit-node { + display: flex; + background: var(--blue-100); + color: var(--blue-500); + padding: .25rem .5rem; + font-size: .75rem; + justify-content: center; + box-shadow: var(--shadow-sm); + } + + .edit-chart-node { + display: block; + } + + .node-edit-icon { + display: block; + } + + .edit-chart-node { + margin-right: 0.25rem; + } + + .node-edit-icon > .icon{ + stroke: var(--blue-500); + } + + .node-name { + align-items: center; + justify-content: space-between; + margin-bottom: 2px; + } +} + +.node-card.active-path { + background: var(--blue-100); + border: 1px solid var(--blue-300); + box-shadow: var(--shadow-sm); + border-radius: 0.5rem; + padding: 0.75rem; + width: 15rem; + height: 3.0rem; + + .btn-edit-node { + display: none !important; + } + + .edit-chart-node { + display: none; + } + + .node-edit-icon { + display: none; + } + + .node-info { + display: none; + } + + .node-title { + display: none; + } + + .node-connections { + display: none; + } + + .node-name { + font-size: 0.85rem; + line-height: 1.35; + } + + .node-image { + width: 1.5rem; + height: 1.5rem; + } + + .node-meta { + align-items: baseline; + } +} + +.node-card.collapsed { + background: white; + stroke: 1px solid var(--gray-200); + box-shadow: var(--shadow-sm); + border-radius: 0.5rem; + padding: 0.75rem; + width: 15rem; + height: 3.0rem; + + .btn-edit-node { + display: none !important; + } + + .edit-chart-node { + display: none; + } + + .node-edit-icon { + display: none; + } + + .node-info { + display: none; + } + + .node-title { + display: none; + } + + .node-connections { + display: none; + } + + .node-name { + font-size: 0.85rem; + line-height: 1.35; + } + + .node-image { + width: 1.5rem; + height: 1.5rem; + } + + .node-meta { + align-items: baseline; + } +} + +// horizontal hierarchy tree view +.hierarchy { + display: flex; + padding-top: 30px; +} + +.hierarchy li { + list-style-type: none; +} + +.child-node { + margin: 0px 0px 16px 0px; +} + +.level { + margin-right: 8px; +} + +#arrows { + position: absolute; +} + +.active-connector { + stroke: var(--blue-500); +} + +.collapsed-connector { + stroke: var(--blue-300); +} From 8a33a039f7723b7adb8aa5e2c489e5410192df03 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 21 Jun 2021 21:55:50 +0530 Subject: [PATCH 278/369] feat: org chart mobile interactions --- .../page/organizational_chart/node_card.html | 4 +- .../organizational_chart.js | 329 +++++++++++++++++- .../organizational_chart.py | 6 +- erpnext/public/scss/organizational_chart.scss | 70 ++++ 4 files changed, 404 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/node_card.html b/erpnext/hr/page/organizational_chart/node_card.html index 057c45ee864..e42e54f690c 100644 --- a/erpnext/hr/page/organizational_chart/node_card.html +++ b/erpnext/hr/page/organizational_chart/node_card.html @@ -17,9 +17,9 @@
                                  {{ title }}
                                  {% if connections == 1 %} -
                                  · {{ connections }} {{ __("Connection") }}
                                  +
                                  · {{ connections }}
                                  {% else %} -
                                  · {{ connections }} {{ __("Connections") }}
                                  +
                                  · {{ connections }}
                                  {% endif %}
                                  diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index 04bd9422bd9..5739a112de9 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -5,13 +5,20 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { single_column: true }); - let organizational_chart = new OrganizationalChart(wrapper); + // let organizational_chart = undefined; + // if (frappe.is_mobile()) { + // organizational_chart = new OrgChartMobile(wrapper); + // } else { + // organizational_chart = new OrgChart(wrapper); + // } + + let organizational_chart = new OrgChartMobile(wrapper); $(wrapper).bind('show', ()=> { organizational_chart.show(); }); }; -class OrganizationalChart { +class OrgChart { constructor(wrapper) { this.wrapper = $(wrapper); @@ -407,3 +414,321 @@ class OrganizationalChart { }) } } + + +class OrgChartMobile { + + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = wrapper.page; + + this.page.main.css({ + 'min-height': '300px', + 'max-height': '600px', + 'overflow': 'auto', + 'position': 'relative' + }); + this.page.main.addClass('frappe-card'); + + this.nodes = {}; + this.setup_node_class(); + } + + setup_node_class() { + let me = this; + this.Node = class { + constructor({ + id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line + }) { + // to setup values passed via constructor + $.extend(this, arguments[0]); + + this.expanded = 0; + + me.nodes[this.id] = this; + me.make_node_element(this); + me.setup_node_click_action(this); + } + } + } + + make_node_element(node) { + let node_card = frappe.render_template('node_card', { + id: node.id, + name: node.name, + title: node.title, + image: node.image, + parent: node.parent_id, + connections: node.connections, + is_mobile: 1 + }); + + node.parent.append(node_card); + node.$link = $(`#${node.id}`); + node.$link.addClass('mobile-node'); + } + + show() { + frappe.breadcrumbs.add('HR'); + + let me = this; + let company = this.page.add_field({ + fieldtype: 'Link', + options: 'Company', + fieldname: 'company', + placeholder: __('Select Company'), + default: frappe.defaults.get_default('company'), + only_select: true, + reqd: 1, + change: () => { + me.company = undefined; + + if (company.get_value() && me.company != company.get_value()) { + me.company = company.get_value(); + + if (me.$hierarchy) + me.$hierarchy.remove(); + + // setup hierarchy + me.$hierarchy = $( + `
                                    +
                                  • +
                                  `); + + me.page.main.append(me.$hierarchy); + me.render_root_node(); + } + } + }); + + company.refresh(); + $(`[data-fieldname="company"]`).trigger('change'); + } + + render_root_node() { + this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; + + let me = this; + + frappe.call({ + method: me.method, + args: { + company: me.company + }, + callback: function(r) { + if (r.message.length) { + let data = r.message[0]; + + let root_node = new me.Node({ + id: data.name, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: true, + connections: data.connections, + is_root: true, + }); + + me.expand_node(root_node); + } + } + }) + } + + expand_node(node) { + this.set_selected_node(node); + this.show_active_path(node); + + if (node.expandable && !node.expanded) { + return this.load_children(node); + } + } + + collapse_node() { + let node = this.selected_node; + if (node.expandable) { + node.$children.hide(); + node.expanded = false; + + // add a collapsed level to show the collapsed parent + // and a button beside it to move to that level + let node_parent = node.$link.parent(); + node_parent.prepend( + `
                                  ` + ); + + node_parent + .find('.collapsed-level') + .append(node.$link); + + frappe.run_serially([ + () => this.get_child_nodes(node.parent_id, node.id), + (child_nodes) => this.get_node_group(child_nodes, node.id), + (node_group) => { + node_parent.find('.collapsed-level') + .append(node_group); + } + ]); + } + } + + show_active_path(node) { + // mark node parent on active path + $(`#${node.parent_id}`).addClass('active-path'); + } + + load_children(node) { + frappe.run_serially([ + () => this.get_child_nodes(node.id), + (child_nodes) => this.render_child_nodes(node, child_nodes) + ]); + } + + get_child_nodes(node_id, exclude_node=null) { + let me = this; + return new Promise(resolve => { + frappe.call({ + method: this.method, + args: { + parent: node_id, + company: me.company, + exclude_node: exclude_node + }, + callback: (r) => { + resolve(r.message); + } + }); + }); + } + + render_child_nodes(node, child_nodes) { + if (!node.$children) { + node.$children = $('
                                    ') + .hide() + .appendTo(node.$link.parent()); + + node.$children.empty(); + + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_node(node, data); + $(`#${data.name}`).addClass('active-child'); + }); + } + } + + node.$children.show(); + node.expanded = true; + } + + add_node(node, data) { + var $li = $('
                                  • '); + + return new this.Node({ + id: data.name, + parent: $li.appendTo(node.$children), + parent_id: node.id, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: data.expandable, + connections: data.connections, + children: undefined + }); + } + + set_selected_node(node) { + // remove .active class from the current node + $('.active').removeClass('active'); + + // add active class to the newly selected node + this.selected_node = node; + node.$link.addClass('active'); + } + + setup_node_click_action(node) { + let me = this; + let node_element = $(`#${node.id}`); + let node_object = null; + + node_element.click(function() { + if (node_element.is(':visible') && node_element.hasClass('active-path')) { + me.remove_levels_after_node(node); + } else { + me.add_node_to_hierarchy(node, true); + me.collapse_node(); + } + + me.expand_node(node); + }); + } + + add_node_to_hierarchy(node) { + this.$hierarchy.append(` +
                                  • +
                                    +
                                    +
                                  • + `); + + node.$link.appendTo(this.$hierarchy.find('.level:last')); + } + + get_node_group(nodes, sibling) { + let limit = 2; + const display_nodes = nodes.slice(0, limit); + const extra_nodes = nodes.slice(limit); + + let html = display_nodes.map(node => + this.get_avatar(node) + ).join(''); + + if (extra_nodes.length === 1) { + let node = extra_nodes[0]; + html += this.get_avatar(node); + } else if (extra_nodes.length > 1) { + html = ` + ${html} + +
                                    + +${extra_nodes.length} +
                                    +
                                    + `; + } + + const $node_group = + $(`
                                    +
                                    + ${html} +
                                    +
                                    `); + + return $node_group; + } + + get_avatar(node) { + return ` + + ` + } + + remove_levels_after_node(node) { + let level = $(`#${node.id}`).parent().parent(); + + level = $('.hierarchy-mobile > li:eq('+ (level.index()) + ')'); + level.nextAll('li').remove(); + + let current_node = level.find(`#${node.id}`); + let node_object = this.nodes[node.id]; + + current_node.removeClass('active-child active-path'); + node_object.expanded = 0; + node_object.$children = undefined; + + level.empty().append(current_node); + } +} \ No newline at end of file diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index be2964530b8..ae91a919b2b 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals import frappe @frappe.whitelist() -def get_children(parent=None, company=None, is_root=False, is_tree=False, fields=None): +def get_children(parent=None, company=None, exclude_node=None, is_root=False, is_tree=False, fields=None): filters = [['status', '!=', 'Left']] if company and company != 'All Companies': @@ -13,6 +13,10 @@ def get_children(parent=None, company=None, is_root=False, is_tree=False, fields if is_root: parent = '' + + if exclude_node: + filters.append(['name', '!=', exclude_node]) + if parent and company and parent!=company: filters.append(['reports_to', '=', parent]) else: diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/organizational_chart.scss index 62f6ddcb6e0..02446be11a1 100644 --- a/erpnext/public/scss/organizational_chart.scss +++ b/erpnext/public/scss/organizational_chart.scss @@ -6,6 +6,7 @@ padding: 0.75rem; margin-left: 3rem; width: 18rem; + overflow: hidden; .btn-edit-node { display: none; @@ -207,3 +208,72 @@ .collapsed-connector { stroke: var(--blue-300); } + +// mobile + +.hierarchy-mobile { + display: flex; + flex-direction: column; + align-items: center; + padding-top: 30px; + padding-left: 0px; +} + +.hierarchy-mobile li { + list-style-type: none; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.mobile-node { + margin-left: 0; +} + +.mobile-node.active-path { + width: 12.25rem; +} + +.active-child { + width: 15.5rem; +} + +.mobile-node .node-connections { + max-width: 80px; +} + +.hierarchy-mobile .node-children { + margin-top: 16px; +} + +// node group + +.collapsed-level { + margin-bottom: 16px; +} + +.node-group { + background: white; + border: 1px solid var(--gray-300); + box-shadow: var(--shadow-sm); + border-radius: 0.5rem; + padding: 0.75rem; + margin-left: 12px; + width: 5rem; + height: 3rem; + overflow: hidden; +} + +.node-group .avatar-group { + margin-left: 0px; +} + +.node-group .avatar-extra-count { + background-color: var(--blue-100); + color: var(--blue-500); +} + +.node-group .avatar-frame { + width: 1.5rem; + height: 1.5rem; +} \ No newline at end of file From 5046cb09d82a487b91cdd4dd7e4b2631690dc56d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 11:12:47 +0530 Subject: [PATCH 279/369] feat(mobile): sibling node group expansion and rendering --- .../organizational_chart.js | 64 +++++++++++++++---- erpnext/public/scss/organizational_chart.scss | 9 ++- 2 files changed, 60 insertions(+), 13 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index 5739a112de9..edaf46162e2 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -565,11 +565,12 @@ class OrgChartMobile { frappe.run_serially([ () => this.get_child_nodes(node.parent_id, node.id), - (child_nodes) => this.get_node_group(child_nodes, node.id), + (child_nodes) => this.get_node_group(child_nodes, node.parent_id), (node_group) => { node_parent.find('.collapsed-level') .append(node_group); - } + }, + () => this.setup_node_group_action() ]); } } @@ -651,7 +652,6 @@ class OrgChartMobile { setup_node_click_action(node) { let me = this; let node_element = $(`#${node.id}`); - let node_object = null; node_element.click(function() { if (node_element.is(':visible') && node_element.hasClass('active-path')) { @@ -665,6 +665,15 @@ class OrgChartMobile { }); } + setup_node_group_action() { + let me = this; + + $('.node-group').on('click', function() { + let parent = $(this).attr('data-parent'); + me.expand_sibling_group_node(parent); + }); + } + add_node_to_hierarchy(node) { this.$hierarchy.append(`
                                  • @@ -676,7 +685,7 @@ class OrgChartMobile { node.$link.appendTo(this.$hierarchy.find('.level:last')); } - get_node_group(nodes, sibling) { + get_node_group(nodes, parent, collapsed=true) { let limit = 2; const display_nodes = nodes.slice(0, limit); const extra_nodes = nodes.slice(limit); @@ -700,14 +709,23 @@ class OrgChartMobile { `; } - const $node_group = - $(`
                                    -
                                    - ${html} -
                                    -
                                    `); + if (html) { + const $node_group = + $(`
                                    +
                                    + ${html} +
                                    +
                                    `); - return $node_group; + if (collapsed) + $node_group.addClass('collapsed'); + else + $node_group.addClass('mb-4'); + + return $node_group; + } + + return null; } get_avatar(node) { @@ -716,6 +734,30 @@ class OrgChartMobile { ` } + expand_sibling_group_node(parent) { + let node_object = this.nodes[parent]; + let node = node_object.$link; + node.removeClass('active-child active-path'); + node_object.expanded = 0; + node_object.$children = undefined; + + // show parent's siblings and expand parent node + frappe.run_serially([ + () => this.get_child_nodes(node_object.parent_id, node_object.id), + (child_nodes) => this.get_node_group(child_nodes, node_object.parent_id, false), + (node_group) => { + this.$hierarchy.empty().append(node_group) }, + () => this.setup_node_group_action(), + () => { + this.$hierarchy.append(` +
                                  • + `); + this.$hierarchy.append(node); + this.expand_node(node_object); + } + ]); + } + remove_levels_after_node(node) { let level = $(`#${node.id}`).parent().parent(); diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/organizational_chart.scss index 02446be11a1..b6d50a0470a 100644 --- a/erpnext/public/scss/organizational_chart.scss +++ b/erpnext/public/scss/organizational_chart.scss @@ -258,10 +258,10 @@ box-shadow: var(--shadow-sm); border-radius: 0.5rem; padding: 0.75rem; - margin-left: 12px; - width: 5rem; + width: 18rem; height: 3rem; overflow: hidden; + align-items: center; } .node-group .avatar-group { @@ -276,4 +276,9 @@ .node-group .avatar-frame { width: 1.5rem; height: 1.5rem; +} + +.node-group.collapsed { + width: 5rem; + margin-left: 12px; } \ No newline at end of file From 25c5cff3defed353b59f1096c00c0e8715ff377d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 15:06:09 +0530 Subject: [PATCH 280/369] fix: expanded node group interactions and visibility --- .../organizational_chart.js | 23 +++++++++++++++---- erpnext/public/scss/organizational_chart.scss | 9 +++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index edaf46162e2..f693cf6ba67 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -486,6 +486,13 @@ class OrgChartMobile { if (company.get_value() && me.company != company.get_value()) { me.company = company.get_value(); + if (me.$sibling_group) + me.$sibling_group.remove(); + + // setup sibling group wrapper + me.$sibling_group = $(`
                                    `); + me.page.main.append(me.$sibling_group); + if (me.$hierarchy) me.$hierarchy.remove(); @@ -541,6 +548,12 @@ class OrgChartMobile { this.set_selected_node(node); this.show_active_path(node); + if (this.$sibling_group) { + const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent'); + if (node.parent_id !== sibling_parent) + this.$sibling_group.empty(); + } + if (node.expandable && !node.expanded) { return this.load_children(node); } @@ -719,8 +732,6 @@ class OrgChartMobile { if (collapsed) $node_group.addClass('collapsed'); - else - $node_group.addClass('mb-4'); return $node_group; } @@ -746,13 +757,15 @@ class OrgChartMobile { () => this.get_child_nodes(node_object.parent_id, node_object.id), (child_nodes) => this.get_node_group(child_nodes, node_object.parent_id, false), (node_group) => { - this.$hierarchy.empty().append(node_group) }, + if (node_group) + this.$sibling_group.empty().append(node_group); + }, () => this.setup_node_group_action(), () => { - this.$hierarchy.append(` + this.$hierarchy.empty().append(`
                                  • `); - this.$hierarchy.append(node); + this.$hierarchy.find('.level').append(node); this.expand_node(node_object); } ]); diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/organizational_chart.scss index b6d50a0470a..6012c015733 100644 --- a/erpnext/public/scss/organizational_chart.scss +++ b/erpnext/public/scss/organizational_chart.scss @@ -215,7 +215,7 @@ display: flex; flex-direction: column; align-items: center; - padding-top: 30px; + padding-top: 10px; padding-left: 0px; } @@ -250,6 +250,7 @@ .collapsed-level { margin-bottom: 16px; + width: 18rem; } .node-group { @@ -281,4 +282,10 @@ .node-group.collapsed { width: 5rem; margin-left: 12px; +} + +.sibling-group { + display: flex; + flex-direction: column; + align-items: center; } \ No newline at end of file From fb9b628b8942c72f966abd5aa36a80a4a7df904d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 17:48:44 +0530 Subject: [PATCH 281/369] feat: connectors for mobile node cards --- .../organizational_chart.js | 138 ++++++++++++++++++ erpnext/public/scss/organizational_chart.scss | 1 + 2 files changed, 139 insertions(+) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index f693cf6ba67..15334bd4ca5 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -486,6 +486,9 @@ class OrgChartMobile { if (company.get_value() && me.company != company.get_value()) { me.company = company.get_value(); + // svg for connectors + me.make_svg_markers() + if (me.$sibling_group) me.$sibling_group.remove(); @@ -512,6 +515,31 @@ class OrgChartMobile { $(`[data-fieldname="company"]`).trigger('change'); } + make_svg_markers() { + $('#arrows').remove(); + + this.page.main.prepend(` + + + + + + + + + + + + + + + + + + + `); + } + render_root_node() { this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; @@ -554,6 +582,14 @@ class OrgChartMobile { this.$sibling_group.empty(); } + // since the previous/parent node collapses, all connections to that node need to be rebuilt + // rebuild outgoing connections of parent + this.refresh_connectors(node.parent_id, node.id); + + // rebuild incoming connections of parent + let grandparent = $(`#${node.parent_id}`).attr('data-parent'); + this.refresh_connectors(grandparent, node.parent_id); + if (node.expandable && !node.expanded) { return this.load_children(node); } @@ -629,6 +665,10 @@ class OrgChartMobile { $.each(child_nodes, (_i, data) => { this.add_node(node, data); $(`#${data.name}`).addClass('active-child'); + + setTimeout(() => { + this.add_connector(node.id, data.name); + }, 250); }); } } @@ -653,6 +693,83 @@ class OrgChartMobile { }); } + add_connector(parent_id, child_id) { + let parent_node = document.querySelector(`#${parent_id}`); + let child_node = document.querySelector(`#${child_id}`); + + // variable for the namespace + const svgns = 'http://www.w3.org/2000/svg'; + let path = document.createElementNS(svgns, 'path'); + + let connector = undefined; + + if ($(`#${parent_id}`).hasClass('active')) { + connector = this.get_connector_for_active_node(parent_node, child_node); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + connector = this.get_connector_for_collapsed_node(parent_node, child_node); + } + + path.setAttribute("d", connector); + this.set_path_attributes(path, parent_id, child_id); + + $('#connectors').append(path); + } + + get_connector_for_active_node(parent_node, child_node) { + // we need to connect the bottom left of the parent to the left side of the child node + let pos_parent_bottom = { + x: parent_node.offsetLeft + 20, + y: parent_node.offsetTop + parent_node.offsetHeight + }; + let pos_child_left = { + x: child_node.offsetLeft - 5, + y: child_node.offsetTop + child_node.offsetHeight / 2 + }; + + let connector = + "M" + + (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + + "L" + + (pos_parent_bottom.x) + "," + (pos_child_left.y) + " " + + "L" + + (pos_child_left.x) + "," + (pos_child_left.y); + + return connector; + } + + get_connector_for_collapsed_node(parent_node, child_node) { + // we need to connect the bottom left of the parent to the top left of the child node + let pos_parent_bottom = { + x: parent_node.offsetLeft + 20, + y: parent_node.offsetTop + parent_node.offsetHeight + }; + let pos_child_top = { + x: child_node.offsetLeft + 20, + y: child_node.offsetTop + }; + + let connector = + "M" + + (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + + "L" + + (pos_child_top.x) + "," + (pos_child_top.y); + + return connector; + } + + set_path_attributes(path, parent_id, child_id) { + path.setAttribute("data-parent", parent_id); + path.setAttribute("data-child", child_id); + + if ($(`#${parent_id}`).hasClass('active')) { + path.setAttribute("class", "active-connector"); + path.setAttribute("marker-start", "url(#arrowstart-active)"); + path.setAttribute("marker-end", "url(#arrowhead-active)"); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + path.setAttribute("class", "collapsed-connector"); + } + } + set_selected_node(node) { // remove .active class from the current node $('.active').removeClass('active'); @@ -669,6 +786,7 @@ class OrgChartMobile { node_element.click(function() { if (node_element.is(':visible') && node_element.hasClass('active-path')) { me.remove_levels_after_node(node); + me.remove_orphaned_connectors(); } else { me.add_node_to_hierarchy(node, true); me.collapse_node(); @@ -786,4 +904,24 @@ class OrgChartMobile { level.empty().append(current_node); } + + remove_orphaned_connectors() { + let paths = $('#connectors > path'); + $.each(paths, (_i, path) => { + let parent = $(path).data('parent'); + let child = $(path).data('child'); + + if ($(parent).length || $(child).length) + return; + + $(path).remove(); + }) + } + + refresh_connectors(node_parent, node_id) { + if (!node_parent) return; + + $(`path[data-parent="${node_parent}"]`).remove(); + this.add_connector(node_parent, node_id); + } } \ No newline at end of file diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/organizational_chart.scss index 6012c015733..16b8792432e 100644 --- a/erpnext/public/scss/organizational_chart.scss +++ b/erpnext/public/scss/organizational_chart.scss @@ -199,6 +199,7 @@ #arrows { position: absolute; + overflow: visible; } .active-connector { From e9c6ea077f4d6db5d980c58f2d5da1fadb87164b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 18:21:42 +0530 Subject: [PATCH 282/369] fix: don't refresh connections for same node - remove all connectors while expanding a group node --- .../organizational_chart/organizational_chart.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index 15334bd4ca5..efb367ad44c 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -573,6 +573,7 @@ class OrgChartMobile { } expand_node(node) { + const is_same_node = (this.selected_node && this.selected_node.id === node.id); this.set_selected_node(node); this.show_active_path(node); @@ -582,13 +583,15 @@ class OrgChartMobile { this.$sibling_group.empty(); } - // since the previous/parent node collapses, all connections to that node need to be rebuilt - // rebuild outgoing connections of parent - this.refresh_connectors(node.parent_id, node.id); + if (!is_same_node) { + // since the previous/parent node collapses, all connections to that node need to be rebuilt + // rebuild outgoing connections of parent + this.refresh_connectors(node.parent_id, node.id); - // rebuild incoming connections of parent - let grandparent = $(`#${node.parent_id}`).attr('data-parent'); - this.refresh_connectors(grandparent, node.parent_id); + // rebuild incoming connections of parent + let grandparent = $(`#${node.parent_id}`).attr('data-parent'); + this.refresh_connectors(grandparent, node.parent_id); + } if (node.expandable && !node.expanded) { return this.load_children(node); @@ -884,6 +887,7 @@ class OrgChartMobile {
                                  • `); this.$hierarchy.find('.level').append(node); + $(`#connectors`).empty(); this.expand_node(node_object); } ]); From 281241dc249799b78e86afb812e02e38bc1456e0 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 19:15:08 +0530 Subject: [PATCH 283/369] chore: create separate files for Desktop and Mobile view and bundle assets --- .../organizational_chart.js | 930 +----------------- erpnext/public/build.json | 9 +- .../hierarchy_chart_desktop.js | 396 ++++++++ .../hierarchy_chart/hierarchy_chart_mobile.js | 513 ++++++++++ .../js/templates}/node_card.html | 0 ...tional_chart.scss => hierarchy_chart.scss} | 0 6 files changed, 925 insertions(+), 923 deletions(-) create mode 100644 erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js create mode 100644 erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js rename erpnext/{hr/page/organizational_chart => public/js/templates}/node_card.html (100%) rename erpnext/public/scss/{organizational_chart.scss => hierarchy_chart.scss} (100%) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index efb367ad44c..0fe724c78e4 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -5,927 +5,15 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { single_column: true }); - // let organizational_chart = undefined; - // if (frappe.is_mobile()) { - // organizational_chart = new OrgChartMobile(wrapper); - // } else { - // organizational_chart = new OrgChart(wrapper); - // } - - let organizational_chart = new OrgChartMobile(wrapper); - $(wrapper).bind('show', ()=> { - organizational_chart.show(); - }); -}; - -class OrgChart { - - constructor(wrapper) { - this.wrapper = $(wrapper); - this.page = wrapper.page; - - this.page.main.css({ - 'min-height': '300px', - 'max-height': '600px', - 'overflow': 'auto', - 'position': 'relative' - }); - this.page.main.addClass('frappe-card'); - - this.nodes = {}; - this.setup_node_class(); - } - - setup_node_class() { - let me = this; - this.Node = class { - constructor({ - id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line - }) { - // to setup values passed via constructor - $.extend(this, arguments[0]); - - this.expanded = 0; - - me.nodes[this.id] = this; - me.make_node_element(this); - me.setup_node_click_action(this); - } - } - } - - make_node_element(node) { - let node_card = frappe.render_template('node_card', { - id: node.id, - name: node.name, - title: node.title, - image: node.image, - parent: node.parent_id, - connections: node.connections - }); - - node.parent.append(node_card); - node.$link = $(`#${node.id}`); - } - - show() { - frappe.breadcrumbs.add('HR'); - - let me = this; - let company = this.page.add_field({ - fieldtype: 'Link', - options: 'Company', - fieldname: 'company', - placeholder: __('Select Company'), - default: frappe.defaults.get_default('company'), - only_select: true, - reqd: 1, - change: () => { - me.company = undefined; - - if (company.get_value() && me.company != company.get_value()) { - me.company = company.get_value(); - - // svg for connectors - me.make_svg_markers() - - if (me.$hierarchy) - me.$hierarchy.remove(); - - // setup hierarchy - me.$hierarchy = $( - `
                                      -
                                    • -
                                    `); - - me.page.main.append(me.$hierarchy); - me.render_root_node(); - } - } - }); - - company.refresh(); - $(`[data-fieldname="company"]`).trigger('change'); - } - - make_svg_markers() { - $('#arrows').remove(); - - this.page.main.prepend(` - - - - - - - - - - - - - - - - - - - `); - } - - render_root_node() { - this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; - - let me = this; - - frappe.call({ - method: me.method, - args: { - company: me.company - }, - callback: function(r) { - if (r.message.length) { - let data = r.message[0]; - - let root_node = new me.Node({ - id: data.name, - parent: me.$hierarchy.find('.root-level'), - parent_id: undefined, - image: data.image, - name: data.employee_name, - title: data.designation, - expandable: true, - connections: data.connections, - is_root: true, - }); - - me.expand_node(root_node); - } - } - }) - } - - expand_node(node) { - let is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id; - this.set_selected_node(node); - this.show_active_path(node); - this.collapse_previous_level_nodes(node); - - // since the previous node collapses, all connections to that node need to be rebuilt - // if a sibling node is clicked, connections don't need to be rebuilt - if (!is_sibling) { - // rebuild outgoing connections - this.refresh_connectors(node.parent_id); - - // rebuild incoming connections - let grandparent = $(`#${node.parent_id}`).attr('data-parent'); - this.refresh_connectors(grandparent) - } - - if (node.expandable && !node.expanded) { - return this.load_children(node); - } - } - - collapse_node() { - if (this.selected_node.expandable) { - this.selected_node.$children.hide(); - $(`path[data-parent="${this.selected_node.id}"]`).hide(); - this.selected_node.expanded = false; - } - } - - show_active_path(node) { - // mark node parent on active path - $(`#${node.parent_id}`).addClass('active-path'); - } - - load_children(node) { - frappe.run_serially([ - () => this.get_child_nodes(node.id), - (child_nodes) => this.render_child_nodes(node, child_nodes) - ]); - } - - get_child_nodes(node_id) { - let me = this; - return new Promise(resolve => { - frappe.call({ - method: this.method, - args: { - parent: node_id, - company: me.company - }, - callback: (r) => { - resolve(r.message); - } - }); - }); - } - - render_child_nodes(node, child_nodes) { - const last_level = this.$hierarchy.find('.level:last').index(); - const current_level = $(`#${node.id}`).parent().parent().parent().index(); - - if (last_level === current_level) { - this.$hierarchy.append(` -
                                  • - `); - } - - if (!node.$children) { - node.$children = $('
                                      ') - .hide() - .appendTo(this.$hierarchy.find('.level:last')); - - node.$children.empty(); - - if (child_nodes) { - $.each(child_nodes, (_i, data) => { - this.add_node(node, data); - - setTimeout(() => { - this.add_connector(node.id, data.name); - }, 250); - }); - } - } - - node.$children.show(); - $(`path[data-parent="${node.id}"]`).show(); - node.expanded = true; - } - - add_node(node, data) { - var $li = $('
                                    • '); - - return new this.Node({ - id: data.name, - parent: $li.appendTo(node.$children), - parent_id: node.id, - image: data.image, - name: data.employee_name, - title: data.designation, - expandable: data.expandable, - connections: data.connections, - children: undefined - }); - } - - add_connector(parent_id, child_id) { - let parent_node = document.querySelector(`#${parent_id}`); - let child_node = document.querySelector(`#${child_id}`); - - // variable for the namespace - const svgns = 'http://www.w3.org/2000/svg'; - let path = document.createElementNS(svgns, 'path'); - - // we need to connect right side of the parent to the left side of the child node - let pos_parent_right = { - x: parent_node.offsetLeft + parent_node.offsetWidth, - y: parent_node.offsetTop + parent_node.offsetHeight / 2 - }; - let pos_child_left = { - x: child_node.offsetLeft - 5, - y: child_node.offsetTop + child_node.offsetHeight / 2 - }; - - let connector = - "M" + - (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + - "C" + - (pos_parent_right.x + 100) + "," + (pos_parent_right.y) + " " + - (pos_child_left.x - 100) + "," + (pos_child_left.y) + " " + - (pos_child_left.x) + "," + (pos_child_left.y); - - path.setAttribute("d", connector); - path.setAttribute("data-parent", parent_id); - path.setAttribute("data-child", child_id); - - if ($(`#${parent_id}`).hasClass('active')) { - path.setAttribute("class", "active-connector"); - path.setAttribute("marker-start", "url(#arrowstart-active)"); - path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if ($(`#${parent_id}`).hasClass('active-path')) { - path.setAttribute("class", "collapsed-connector"); - path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); - path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); - } - - $('#connectors').append(path); - } - - set_selected_node(node) { - // remove .active class from the current node - $('.active').removeClass('active'); - - // add active class to the newly selected node - this.selected_node = node; - node.$link.addClass('active'); - } - - collapse_previous_level_nodes(node) { - let node_parent = $(`#${node.parent_id}`); - - let previous_level_nodes = node_parent.parent().parent().children('li'); - if (node_parent.parent().hasClass('root-level')) { - previous_level_nodes = node_parent.parent().children('li'); - } - - let node_card = undefined; - - previous_level_nodes.each(function() { - node_card = $(this).find('.node-card'); - - if (!node_card.hasClass('active-path')) { - node_card.addClass('collapsed'); - } - }); - } - - refresh_connectors(node_parent) { - if (!node_parent) return; - - $(`path[data-parent="${node_parent}"]`).remove(); - - frappe.run_serially([ - () => this.get_child_nodes(node_parent), - (child_nodes) => { - if (child_nodes) { - $.each(child_nodes, (_i, data) => { - this.add_connector(node_parent, data.name); - }); - } - } - ]); - } - - setup_node_click_action(node) { - let me = this; - let node_element = $(`#${node.id}`); - - node_element.click(function() { - let is_sibling = me.selected_node.parent_id === node.parent_id; - - if (is_sibling) { - me.collapse_node(); - } else if (node_element.is(':visible') - && (node_element.hasClass('collapsed') || node_element.hasClass('active-path'))) { - me.remove_levels_after_node(node); - me.remove_orphaned_connectors(); - } - - me.expand_node(node); - }); - } - - remove_levels_after_node(node) { - let level = $(`#${node.id}`).parent().parent().parent(); - - if ($(`#${node.id}`).parent().hasClass('root-level')) { - level = $(`#${node.id}`).parent(); - } - - level = $('.hierarchy > li:eq('+ level.index() + ')'); - level.nextAll('li').remove(); - - let nodes = level.find('.node-card'); - let node_object = undefined; - - $.each(nodes, (_i, element) => { - node_object = this.nodes[element.id]; - node_object.expanded = 0; - node_object.$children = undefined; - }); - - nodes.removeClass('collapsed active-path'); - } - - remove_orphaned_connectors() { - let paths = $('#connectors > path'); - $.each(paths, (_i, path) => { - let parent = $(path).data('parent'); - let child = $(path).data('child'); - - if ($(parent).length || $(child).length) - return; - - $(path).remove(); - }) - } -} - - -class OrgChartMobile { - - constructor(wrapper) { - this.wrapper = $(wrapper); - this.page = wrapper.page; - - this.page.main.css({ - 'min-height': '300px', - 'max-height': '600px', - 'overflow': 'auto', - 'position': 'relative' - }); - this.page.main.addClass('frappe-card'); - - this.nodes = {}; - this.setup_node_class(); - } - - setup_node_class() { - let me = this; - this.Node = class { - constructor({ - id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line - }) { - // to setup values passed via constructor - $.extend(this, arguments[0]); - - this.expanded = 0; - - me.nodes[this.id] = this; - me.make_node_element(this); - me.setup_node_click_action(this); - } - } - } - - make_node_element(node) { - let node_card = frappe.render_template('node_card', { - id: node.id, - name: node.name, - title: node.title, - image: node.image, - parent: node.parent_id, - connections: node.connections, - is_mobile: 1 - }); - - node.parent.append(node_card); - node.$link = $(`#${node.id}`); - node.$link.addClass('mobile-node'); - } - - show() { - frappe.breadcrumbs.add('HR'); - - let me = this; - let company = this.page.add_field({ - fieldtype: 'Link', - options: 'Company', - fieldname: 'company', - placeholder: __('Select Company'), - default: frappe.defaults.get_default('company'), - only_select: true, - reqd: 1, - change: () => { - me.company = undefined; - - if (company.get_value() && me.company != company.get_value()) { - me.company = company.get_value(); - - // svg for connectors - me.make_svg_markers() - - if (me.$sibling_group) - me.$sibling_group.remove(); - - // setup sibling group wrapper - me.$sibling_group = $(`
                                      `); - me.page.main.append(me.$sibling_group); - - if (me.$hierarchy) - me.$hierarchy.remove(); - - // setup hierarchy - me.$hierarchy = $( - `
                                        -
                                      • -
                                      `); - - me.page.main.append(me.$hierarchy); - me.render_root_node(); - } - } - }); - - company.refresh(); - $(`[data-fieldname="company"]`).trigger('change'); - } - - make_svg_markers() { - $('#arrows').remove(); - - this.page.main.prepend(` - - - - - - - - - - - - - - - - - - - `); - } - - render_root_node() { - this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; - - let me = this; - - frappe.call({ - method: me.method, - args: { - company: me.company - }, - callback: function(r) { - if (r.message.length) { - let data = r.message[0]; - - let root_node = new me.Node({ - id: data.name, - parent: me.$hierarchy.find('.root-level'), - parent_id: undefined, - image: data.image, - name: data.employee_name, - title: data.designation, - expandable: true, - connections: data.connections, - is_root: true, - }); - - me.expand_node(root_node); - } - } - }) - } - - expand_node(node) { - const is_same_node = (this.selected_node && this.selected_node.id === node.id); - this.set_selected_node(node); - this.show_active_path(node); - - if (this.$sibling_group) { - const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent'); - if (node.parent_id !== sibling_parent) - this.$sibling_group.empty(); - } - - if (!is_same_node) { - // since the previous/parent node collapses, all connections to that node need to be rebuilt - // rebuild outgoing connections of parent - this.refresh_connectors(node.parent_id, node.id); - - // rebuild incoming connections of parent - let grandparent = $(`#${node.parent_id}`).attr('data-parent'); - this.refresh_connectors(grandparent, node.parent_id); - } - - if (node.expandable && !node.expanded) { - return this.load_children(node); - } - } - - collapse_node() { - let node = this.selected_node; - if (node.expandable) { - node.$children.hide(); - node.expanded = false; - - // add a collapsed level to show the collapsed parent - // and a button beside it to move to that level - let node_parent = node.$link.parent(); - node_parent.prepend( - `
                                      ` - ); - - node_parent - .find('.collapsed-level') - .append(node.$link); - - frappe.run_serially([ - () => this.get_child_nodes(node.parent_id, node.id), - (child_nodes) => this.get_node_group(child_nodes, node.parent_id), - (node_group) => { - node_parent.find('.collapsed-level') - .append(node_group); - }, - () => this.setup_node_group_action() - ]); - } - } - - show_active_path(node) { - // mark node parent on active path - $(`#${node.parent_id}`).addClass('active-path'); - } - - load_children(node) { - frappe.run_serially([ - () => this.get_child_nodes(node.id), - (child_nodes) => this.render_child_nodes(node, child_nodes) - ]); - } - - get_child_nodes(node_id, exclude_node=null) { - let me = this; - return new Promise(resolve => { - frappe.call({ - method: this.method, - args: { - parent: node_id, - company: me.company, - exclude_node: exclude_node - }, - callback: (r) => { - resolve(r.message); - } - }); - }); - } - - render_child_nodes(node, child_nodes) { - if (!node.$children) { - node.$children = $('
                                        ') - .hide() - .appendTo(node.$link.parent()); - - node.$children.empty(); - - if (child_nodes) { - $.each(child_nodes, (_i, data) => { - this.add_node(node, data); - $(`#${data.name}`).addClass('active-child'); - - setTimeout(() => { - this.add_connector(node.id, data.name); - }, 250); - }); - } - } - - node.$children.show(); - node.expanded = true; - } - - add_node(node, data) { - var $li = $('
                                      • '); - - return new this.Node({ - id: data.name, - parent: $li.appendTo(node.$children), - parent_id: node.id, - image: data.image, - name: data.employee_name, - title: data.designation, - expandable: data.expandable, - connections: data.connections, - children: undefined - }); - } - - add_connector(parent_id, child_id) { - let parent_node = document.querySelector(`#${parent_id}`); - let child_node = document.querySelector(`#${child_id}`); - - // variable for the namespace - const svgns = 'http://www.w3.org/2000/svg'; - let path = document.createElementNS(svgns, 'path'); - - let connector = undefined; - - if ($(`#${parent_id}`).hasClass('active')) { - connector = this.get_connector_for_active_node(parent_node, child_node); - } else if ($(`#${parent_id}`).hasClass('active-path')) { - connector = this.get_connector_for_collapsed_node(parent_node, child_node); - } - - path.setAttribute("d", connector); - this.set_path_attributes(path, parent_id, child_id); - - $('#connectors').append(path); - } - - get_connector_for_active_node(parent_node, child_node) { - // we need to connect the bottom left of the parent to the left side of the child node - let pos_parent_bottom = { - x: parent_node.offsetLeft + 20, - y: parent_node.offsetTop + parent_node.offsetHeight - }; - let pos_child_left = { - x: child_node.offsetLeft - 5, - y: child_node.offsetTop + child_node.offsetHeight / 2 - }; - - let connector = - "M" + - (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + - "L" + - (pos_parent_bottom.x) + "," + (pos_child_left.y) + " " + - "L" + - (pos_child_left.x) + "," + (pos_child_left.y); - - return connector; - } - - get_connector_for_collapsed_node(parent_node, child_node) { - // we need to connect the bottom left of the parent to the top left of the child node - let pos_parent_bottom = { - x: parent_node.offsetLeft + 20, - y: parent_node.offsetTop + parent_node.offsetHeight - }; - let pos_child_top = { - x: child_node.offsetLeft + 20, - y: child_node.offsetTop - }; - - let connector = - "M" + - (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + - "L" + - (pos_child_top.x) + "," + (pos_child_top.y); - - return connector; - } - - set_path_attributes(path, parent_id, child_id) { - path.setAttribute("data-parent", parent_id); - path.setAttribute("data-child", child_id); - - if ($(`#${parent_id}`).hasClass('active')) { - path.setAttribute("class", "active-connector"); - path.setAttribute("marker-start", "url(#arrowstart-active)"); - path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if ($(`#${parent_id}`).hasClass('active-path')) { - path.setAttribute("class", "collapsed-connector"); - } - } - - set_selected_node(node) { - // remove .active class from the current node - $('.active').removeClass('active'); - - // add active class to the newly selected node - this.selected_node = node; - node.$link.addClass('active'); - } - - setup_node_click_action(node) { - let me = this; - let node_element = $(`#${node.id}`); - - node_element.click(function() { - if (node_element.is(':visible') && node_element.hasClass('active-path')) { - me.remove_levels_after_node(node); - me.remove_orphaned_connectors(); + $(wrapper).bind('show', () => { + frappe.require('/assets/js/hierarchy-chart.min.js', () => { + let organizational_chart = undefined; + if (frappe.is_mobile()) { + organizational_chart = new erpnext.HierarchyChartMobile(wrapper); } else { - me.add_node_to_hierarchy(node, true); - me.collapse_node(); + organizational_chart = new erpnext.HierarchyChart(wrapper); } - - me.expand_node(node); + organizational_chart.show(); }); - } - - setup_node_group_action() { - let me = this; - - $('.node-group').on('click', function() { - let parent = $(this).attr('data-parent'); - me.expand_sibling_group_node(parent); - }); - } - - add_node_to_hierarchy(node) { - this.$hierarchy.append(` -
                                      • -
                                        -
                                        -
                                      • - `); - - node.$link.appendTo(this.$hierarchy.find('.level:last')); - } - - get_node_group(nodes, parent, collapsed=true) { - let limit = 2; - const display_nodes = nodes.slice(0, limit); - const extra_nodes = nodes.slice(limit); - - let html = display_nodes.map(node => - this.get_avatar(node) - ).join(''); - - if (extra_nodes.length === 1) { - let node = extra_nodes[0]; - html += this.get_avatar(node); - } else if (extra_nodes.length > 1) { - html = ` - ${html} - -
                                        - +${extra_nodes.length} -
                                        -
                                        - `; - } - - if (html) { - const $node_group = - $(`
                                        -
                                        - ${html} -
                                        -
                                        `); - - if (collapsed) - $node_group.addClass('collapsed'); - - return $node_group; - } - - return null; - } - - get_avatar(node) { - return ` - - ` - } - - expand_sibling_group_node(parent) { - let node_object = this.nodes[parent]; - let node = node_object.$link; - node.removeClass('active-child active-path'); - node_object.expanded = 0; - node_object.$children = undefined; - - // show parent's siblings and expand parent node - frappe.run_serially([ - () => this.get_child_nodes(node_object.parent_id, node_object.id), - (child_nodes) => this.get_node_group(child_nodes, node_object.parent_id, false), - (node_group) => { - if (node_group) - this.$sibling_group.empty().append(node_group); - }, - () => this.setup_node_group_action(), - () => { - this.$hierarchy.empty().append(` -
                                      • - `); - this.$hierarchy.find('.level').append(node); - $(`#connectors`).empty(); - this.expand_node(node_object); - } - ]); - } - - remove_levels_after_node(node) { - let level = $(`#${node.id}`).parent().parent(); - - level = $('.hierarchy-mobile > li:eq('+ (level.index()) + ')'); - level.nextAll('li').remove(); - - let current_node = level.find(`#${node.id}`); - let node_object = this.nodes[node.id]; - - current_node.removeClass('active-child active-path'); - node_object.expanded = 0; - node_object.$children = undefined; - - level.empty().append(current_node); - } - - remove_orphaned_connectors() { - let paths = $('#connectors > path'); - $.each(paths, (_i, path) => { - let parent = $(path).data('parent'); - let child = $(path).data('child'); - - if ($(parent).length || $(child).length) - return; - - $(path).remove(); - }) - } - - refresh_connectors(node_parent, node_id) { - if (!node_parent) return; - - $(`path[data-parent="${node_parent}"]`).remove(); - this.add_connector(node_parent, node_id); - } -} \ No newline at end of file + }); +}; \ No newline at end of file diff --git a/erpnext/public/build.json b/erpnext/public/build.json index d3ebcdf7e7b..3c60e3ee500 100644 --- a/erpnext/public/build.json +++ b/erpnext/public/build.json @@ -4,7 +4,7 @@ "public/less/hub.less", "public/scss/call_popup.scss", "public/scss/point-of-sale.scss", - "public/scss/organizational_chart.scss" + "public/scss/hierarchy_chart.scss" ], "css/marketplace.css": [ "public/less/hub.less" @@ -44,7 +44,8 @@ "public/js/call_popup/call_popup.js", "public/js/utils/dimension_tree_filter.js", "public/js/telephony.js", - "public/js/templates/call_link.html" + "public/js/templates/call_link.html", + "public/js/templates/node_card.html" ], "js/item-dashboard.min.js": [ "stock/dashboard/item_dashboard.html", @@ -67,5 +68,9 @@ "public/js/bank_reconciliation_tool/data_table_manager.js", "public/js/bank_reconciliation_tool/number_card.js", "public/js/bank_reconciliation_tool/dialog_manager.js" + ], + "js/hierarchy-chart.min.js": [ + "public/js/hierarchy_chart/hierarchy_chart_desktop.js", + "public/js/hierarchy_chart/hierarchy_chart_mobile.js" ] } diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js new file mode 100644 index 00000000000..fd84d4ea5ca --- /dev/null +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -0,0 +1,396 @@ +erpnext.HierarchyChart = class { + + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = wrapper.page; + + this.page.main.css({ + 'min-height': '300px', + 'max-height': '600px', + 'overflow': 'auto', + 'position': 'relative' + }); + this.page.main.addClass('frappe-card'); + + this.nodes = {}; + this.setup_node_class(); + } + + setup_node_class() { + let me = this; + this.Node = class { + constructor({ + id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line + }) { + // to setup values passed via constructor + $.extend(this, arguments[0]); + + this.expanded = 0; + + me.nodes[this.id] = this; + me.make_node_element(this); + me.setup_node_click_action(this); + } + } + } + + make_node_element(node) { + let node_card = frappe.render_template('node_card', { + id: node.id, + name: node.name, + title: node.title, + image: node.image, + parent: node.parent_id, + connections: node.connections + }); + + node.parent.append(node_card); + node.$link = $(`#${node.id}`); + } + + show() { + frappe.breadcrumbs.add('HR'); + + let me = this; + let company = this.page.add_field({ + fieldtype: 'Link', + options: 'Company', + fieldname: 'company', + placeholder: __('Select Company'), + default: frappe.defaults.get_default('company'), + only_select: true, + reqd: 1, + change: () => { + me.company = undefined; + + if (company.get_value() && me.company != company.get_value()) { + me.company = company.get_value(); + + // svg for connectors + me.make_svg_markers() + + if (me.$hierarchy) + me.$hierarchy.remove(); + + // setup hierarchy + me.$hierarchy = $( + `
                                          +
                                        • +
                                        `); + + me.page.main.append(me.$hierarchy); + me.render_root_node(); + } + } + }); + + company.refresh(); + $(`[data-fieldname="company"]`).trigger('change'); + } + + make_svg_markers() { + $('#arrows').remove(); + + this.page.main.prepend(` + + + + + + + + + + + + + + + + + + + `); + } + + render_root_node() { + this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; + + let me = this; + + frappe.call({ + method: me.method, + args: { + company: me.company + }, + callback: function(r) { + if (r.message.length) { + let data = r.message[0]; + + let root_node = new me.Node({ + id: data.name, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: true, + connections: data.connections, + is_root: true, + }); + + me.expand_node(root_node); + } + } + }) + } + + expand_node(node) { + let is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id; + this.set_selected_node(node); + this.show_active_path(node); + this.collapse_previous_level_nodes(node); + + // since the previous node collapses, all connections to that node need to be rebuilt + // if a sibling node is clicked, connections don't need to be rebuilt + if (!is_sibling) { + // rebuild outgoing connections + this.refresh_connectors(node.parent_id); + + // rebuild incoming connections + let grandparent = $(`#${node.parent_id}`).attr('data-parent'); + this.refresh_connectors(grandparent) + } + + if (node.expandable && !node.expanded) { + return this.load_children(node); + } + } + + collapse_node() { + if (this.selected_node.expandable) { + this.selected_node.$children.hide(); + $(`path[data-parent="${this.selected_node.id}"]`).hide(); + this.selected_node.expanded = false; + } + } + + show_active_path(node) { + // mark node parent on active path + $(`#${node.parent_id}`).addClass('active-path'); + } + + load_children(node) { + frappe.run_serially([ + () => this.get_child_nodes(node.id), + (child_nodes) => this.render_child_nodes(node, child_nodes) + ]); + } + + get_child_nodes(node_id) { + let me = this; + return new Promise(resolve => { + frappe.call({ + method: this.method, + args: { + parent: node_id, + company: me.company + }, + callback: (r) => { + resolve(r.message); + } + }); + }); + } + + render_child_nodes(node, child_nodes) { + const last_level = this.$hierarchy.find('.level:last').index(); + const current_level = $(`#${node.id}`).parent().parent().parent().index(); + + if (last_level === current_level) { + this.$hierarchy.append(` +
                                      • + `); + } + + if (!node.$children) { + node.$children = $('
                                          ') + .hide() + .appendTo(this.$hierarchy.find('.level:last')); + + node.$children.empty(); + + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_node(node, data); + + setTimeout(() => { + this.add_connector(node.id, data.name); + }, 250); + }); + } + } + + node.$children.show(); + $(`path[data-parent="${node.id}"]`).show(); + node.expanded = true; + } + + add_node(node, data) { + var $li = $('
                                        • '); + + return new this.Node({ + id: data.name, + parent: $li.appendTo(node.$children), + parent_id: node.id, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: data.expandable, + connections: data.connections, + children: undefined + }); + } + + add_connector(parent_id, child_id) { + let parent_node = document.querySelector(`#${parent_id}`); + let child_node = document.querySelector(`#${child_id}`); + + // variable for the namespace + const svgns = 'http://www.w3.org/2000/svg'; + let path = document.createElementNS(svgns, 'path'); + + // we need to connect right side of the parent to the left side of the child node + let pos_parent_right = { + x: parent_node.offsetLeft + parent_node.offsetWidth, + y: parent_node.offsetTop + parent_node.offsetHeight / 2 + }; + let pos_child_left = { + x: child_node.offsetLeft - 5, + y: child_node.offsetTop + child_node.offsetHeight / 2 + }; + + let connector = + "M" + + (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + + "C" + + (pos_parent_right.x + 100) + "," + (pos_parent_right.y) + " " + + (pos_child_left.x - 100) + "," + (pos_child_left.y) + " " + + (pos_child_left.x) + "," + (pos_child_left.y); + + path.setAttribute("d", connector); + path.setAttribute("data-parent", parent_id); + path.setAttribute("data-child", child_id); + + if ($(`#${parent_id}`).hasClass('active')) { + path.setAttribute("class", "active-connector"); + path.setAttribute("marker-start", "url(#arrowstart-active)"); + path.setAttribute("marker-end", "url(#arrowhead-active)"); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + path.setAttribute("class", "collapsed-connector"); + path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); + path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); + } + + $('#connectors').append(path); + } + + set_selected_node(node) { + // remove .active class from the current node + $('.active').removeClass('active'); + + // add active class to the newly selected node + this.selected_node = node; + node.$link.addClass('active'); + } + + collapse_previous_level_nodes(node) { + let node_parent = $(`#${node.parent_id}`); + + let previous_level_nodes = node_parent.parent().parent().children('li'); + if (node_parent.parent().hasClass('root-level')) { + previous_level_nodes = node_parent.parent().children('li'); + } + + let node_card = undefined; + + previous_level_nodes.each(function() { + node_card = $(this).find('.node-card'); + + if (!node_card.hasClass('active-path')) { + node_card.addClass('collapsed'); + } + }); + } + + refresh_connectors(node_parent) { + if (!node_parent) return; + + $(`path[data-parent="${node_parent}"]`).remove(); + + frappe.run_serially([ + () => this.get_child_nodes(node_parent), + (child_nodes) => { + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_connector(node_parent, data.name); + }); + } + } + ]); + } + + setup_node_click_action(node) { + let me = this; + let node_element = $(`#${node.id}`); + + node_element.click(function() { + let is_sibling = me.selected_node.parent_id === node.parent_id; + + if (is_sibling) { + me.collapse_node(); + } else if (node_element.is(':visible') + && (node_element.hasClass('collapsed') || node_element.hasClass('active-path'))) { + me.remove_levels_after_node(node); + me.remove_orphaned_connectors(); + } + + me.expand_node(node); + }); + } + + remove_levels_after_node(node) { + let level = $(`#${node.id}`).parent().parent().parent(); + + if ($(`#${node.id}`).parent().hasClass('root-level')) { + level = $(`#${node.id}`).parent(); + } + + level = $('.hierarchy > li:eq('+ level.index() + ')'); + level.nextAll('li').remove(); + + let nodes = level.find('.node-card'); + let node_object = undefined; + + $.each(nodes, (_i, element) => { + node_object = this.nodes[element.id]; + node_object.expanded = 0; + node_object.$children = undefined; + }); + + nodes.removeClass('collapsed active-path'); + } + + remove_orphaned_connectors() { + let paths = $('#connectors > path'); + $.each(paths, (_i, path) => { + let parent = $(path).data('parent'); + let child = $(path).data('child'); + + if ($(parent).length || $(child).length) + return; + + $(path).remove(); + }) + } +} \ No newline at end of file diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js new file mode 100644 index 00000000000..c705681438d --- /dev/null +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -0,0 +1,513 @@ +erpnext.HierarchyChartMobile = class { + + constructor(wrapper) { + this.wrapper = $(wrapper); + this.page = wrapper.page; + + this.page.main.css({ + 'min-height': '300px', + 'max-height': '600px', + 'overflow': 'auto', + 'position': 'relative' + }); + this.page.main.addClass('frappe-card'); + + this.nodes = {}; + this.setup_node_class(); + } + + setup_node_class() { + let me = this; + this.Node = class { + constructor({ + id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line + }) { + // to setup values passed via constructor + $.extend(this, arguments[0]); + + this.expanded = 0; + + me.nodes[this.id] = this; + me.make_node_element(this); + me.setup_node_click_action(this); + } + } + } + + make_node_element(node) { + let node_card = frappe.render_template('node_card', { + id: node.id, + name: node.name, + title: node.title, + image: node.image, + parent: node.parent_id, + connections: node.connections, + is_mobile: 1 + }); + + node.parent.append(node_card); + node.$link = $(`#${node.id}`); + node.$link.addClass('mobile-node'); + } + + show() { + frappe.breadcrumbs.add('HR'); + + let me = this; + let company = this.page.add_field({ + fieldtype: 'Link', + options: 'Company', + fieldname: 'company', + placeholder: __('Select Company'), + default: frappe.defaults.get_default('company'), + only_select: true, + reqd: 1, + change: () => { + me.company = undefined; + + if (company.get_value() && me.company != company.get_value()) { + me.company = company.get_value(); + + // svg for connectors + me.make_svg_markers() + + if (me.$sibling_group) + me.$sibling_group.remove(); + + // setup sibling group wrapper + me.$sibling_group = $(`
                                          `); + me.page.main.append(me.$sibling_group); + + if (me.$hierarchy) + me.$hierarchy.remove(); + + // setup hierarchy + me.$hierarchy = $( + `
                                            +
                                          • +
                                          `); + + me.page.main.append(me.$hierarchy); + me.render_root_node(); + } + } + }); + + company.refresh(); + $(`[data-fieldname="company"]`).trigger('change'); + } + + make_svg_markers() { + $('#arrows').remove(); + + this.page.main.prepend(` + + + + + + + + + + + + + + + + + + + `); + } + + render_root_node() { + this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; + + let me = this; + + frappe.call({ + method: me.method, + args: { + company: me.company + }, + callback: function(r) { + if (r.message.length) { + let data = r.message[0]; + + let root_node = new me.Node({ + id: data.name, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: true, + connections: data.connections, + is_root: true, + }); + + me.expand_node(root_node); + } + } + }) + } + + expand_node(node) { + const is_same_node = (this.selected_node && this.selected_node.id === node.id); + this.set_selected_node(node); + this.show_active_path(node); + + if (this.$sibling_group) { + const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent'); + if (node.parent_id !== sibling_parent) + this.$sibling_group.empty(); + } + + if (!is_same_node) { + // since the previous/parent node collapses, all connections to that node need to be rebuilt + // rebuild outgoing connections of parent + this.refresh_connectors(node.parent_id, node.id); + + // rebuild incoming connections of parent + let grandparent = $(`#${node.parent_id}`).attr('data-parent'); + this.refresh_connectors(grandparent, node.parent_id); + } + + if (node.expandable && !node.expanded) { + return this.load_children(node); + } + } + + collapse_node() { + let node = this.selected_node; + if (node.expandable) { + node.$children.hide(); + node.expanded = false; + + // add a collapsed level to show the collapsed parent + // and a button beside it to move to that level + let node_parent = node.$link.parent(); + node_parent.prepend( + `
                                          ` + ); + + node_parent + .find('.collapsed-level') + .append(node.$link); + + frappe.run_serially([ + () => this.get_child_nodes(node.parent_id, node.id), + (child_nodes) => this.get_node_group(child_nodes, node.parent_id), + (node_group) => { + node_parent.find('.collapsed-level') + .append(node_group); + }, + () => this.setup_node_group_action() + ]); + } + } + + show_active_path(node) { + // mark node parent on active path + $(`#${node.parent_id}`).addClass('active-path'); + } + + load_children(node) { + frappe.run_serially([ + () => this.get_child_nodes(node.id), + (child_nodes) => this.render_child_nodes(node, child_nodes) + ]); + } + + get_child_nodes(node_id, exclude_node=null) { + let me = this; + return new Promise(resolve => { + frappe.call({ + method: this.method, + args: { + parent: node_id, + company: me.company, + exclude_node: exclude_node + }, + callback: (r) => { + resolve(r.message); + } + }); + }); + } + + render_child_nodes(node, child_nodes) { + if (!node.$children) { + node.$children = $('
                                            ') + .hide() + .appendTo(node.$link.parent()); + + node.$children.empty(); + + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_node(node, data); + $(`#${data.name}`).addClass('active-child'); + + setTimeout(() => { + this.add_connector(node.id, data.name); + }, 250); + }); + } + } + + node.$children.show(); + node.expanded = true; + } + + add_node(node, data) { + var $li = $('
                                          • '); + + return new this.Node({ + id: data.name, + parent: $li.appendTo(node.$children), + parent_id: node.id, + image: data.image, + name: data.employee_name, + title: data.designation, + expandable: data.expandable, + connections: data.connections, + children: undefined + }); + } + + add_connector(parent_id, child_id) { + let parent_node = document.querySelector(`#${parent_id}`); + let child_node = document.querySelector(`#${child_id}`); + + // variable for the namespace + const svgns = 'http://www.w3.org/2000/svg'; + let path = document.createElementNS(svgns, 'path'); + + let connector = undefined; + + if ($(`#${parent_id}`).hasClass('active')) { + connector = this.get_connector_for_active_node(parent_node, child_node); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + connector = this.get_connector_for_collapsed_node(parent_node, child_node); + } + + path.setAttribute("d", connector); + this.set_path_attributes(path, parent_id, child_id); + + $('#connectors').append(path); + } + + get_connector_for_active_node(parent_node, child_node) { + // we need to connect the bottom left of the parent to the left side of the child node + let pos_parent_bottom = { + x: parent_node.offsetLeft + 20, + y: parent_node.offsetTop + parent_node.offsetHeight + }; + let pos_child_left = { + x: child_node.offsetLeft - 5, + y: child_node.offsetTop + child_node.offsetHeight / 2 + }; + + let connector = + "M" + + (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + + "L" + + (pos_parent_bottom.x) + "," + (pos_child_left.y) + " " + + "L" + + (pos_child_left.x) + "," + (pos_child_left.y); + + return connector; + } + + get_connector_for_collapsed_node(parent_node, child_node) { + // we need to connect the bottom left of the parent to the top left of the child node + let pos_parent_bottom = { + x: parent_node.offsetLeft + 20, + y: parent_node.offsetTop + parent_node.offsetHeight + }; + let pos_child_top = { + x: child_node.offsetLeft + 20, + y: child_node.offsetTop + }; + + let connector = + "M" + + (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + + "L" + + (pos_child_top.x) + "," + (pos_child_top.y); + + return connector; + } + + set_path_attributes(path, parent_id, child_id) { + path.setAttribute("data-parent", parent_id); + path.setAttribute("data-child", child_id); + + if ($(`#${parent_id}`).hasClass('active')) { + path.setAttribute("class", "active-connector"); + path.setAttribute("marker-start", "url(#arrowstart-active)"); + path.setAttribute("marker-end", "url(#arrowhead-active)"); + } else if ($(`#${parent_id}`).hasClass('active-path')) { + path.setAttribute("class", "collapsed-connector"); + } + } + + set_selected_node(node) { + // remove .active class from the current node + $('.active').removeClass('active'); + + // add active class to the newly selected node + this.selected_node = node; + node.$link.addClass('active'); + } + + setup_node_click_action(node) { + let me = this; + let node_element = $(`#${node.id}`); + + node_element.click(function() { + if (node_element.is(':visible') && node_element.hasClass('active-path')) { + me.remove_levels_after_node(node); + me.remove_orphaned_connectors(); + } else { + me.add_node_to_hierarchy(node, true); + me.collapse_node(); + } + + me.expand_node(node); + }); + } + + setup_node_group_action() { + let me = this; + + $('.node-group').on('click', function() { + let parent = $(this).attr('data-parent'); + me.expand_sibling_group_node(parent); + }); + } + + add_node_to_hierarchy(node) { + this.$hierarchy.append(` +
                                          • +
                                            +
                                            +
                                          • + `); + + node.$link.appendTo(this.$hierarchy.find('.level:last')); + } + + get_node_group(nodes, parent, collapsed=true) { + let limit = 2; + const display_nodes = nodes.slice(0, limit); + const extra_nodes = nodes.slice(limit); + + let html = display_nodes.map(node => + this.get_avatar(node) + ).join(''); + + if (extra_nodes.length === 1) { + let node = extra_nodes[0]; + html += this.get_avatar(node); + } else if (extra_nodes.length > 1) { + html = ` + ${html} + +
                                            + +${extra_nodes.length} +
                                            +
                                            + `; + } + + if (html) { + const $node_group = + $(`
                                            +
                                            + ${html} +
                                            +
                                            `); + + if (collapsed) + $node_group.addClass('collapsed'); + + return $node_group; + } + + return null; + } + + get_avatar(node) { + return ` + + ` + } + + expand_sibling_group_node(parent) { + let node_object = this.nodes[parent]; + let node = node_object.$link; + node.removeClass('active-child active-path'); + node_object.expanded = 0; + node_object.$children = undefined; + + // show parent's siblings and expand parent node + frappe.run_serially([ + () => this.get_child_nodes(node_object.parent_id, node_object.id), + (child_nodes) => this.get_node_group(child_nodes, node_object.parent_id, false), + (node_group) => { + if (node_group) + this.$sibling_group.empty().append(node_group); + }, + () => this.setup_node_group_action(), + () => { + this.$hierarchy.empty().append(` +
                                          • + `); + this.$hierarchy.find('.level').append(node); + $(`#connectors`).empty(); + this.expand_node(node_object); + } + ]); + } + + remove_levels_after_node(node) { + let level = $(`#${node.id}`).parent().parent(); + + level = $('.hierarchy-mobile > li:eq('+ (level.index()) + ')'); + level.nextAll('li').remove(); + + let current_node = level.find(`#${node.id}`); + let node_object = this.nodes[node.id]; + + current_node.removeClass('active-child active-path'); + node_object.expanded = 0; + node_object.$children = undefined; + + level.empty().append(current_node); + } + + remove_orphaned_connectors() { + let paths = $('#connectors > path'); + $.each(paths, (_i, path) => { + let parent = $(path).data('parent'); + let child = $(path).data('child'); + + if ($(parent).length || $(child).length) + return; + + $(path).remove(); + }) + } + + refresh_connectors(node_parent, node_id) { + if (!node_parent) return; + + $(`path[data-parent="${node_parent}"]`).remove(); + this.add_connector(node_parent, node_id); + } +} \ No newline at end of file diff --git a/erpnext/hr/page/organizational_chart/node_card.html b/erpnext/public/js/templates/node_card.html similarity index 100% rename from erpnext/hr/page/organizational_chart/node_card.html rename to erpnext/public/js/templates/node_card.html diff --git a/erpnext/public/scss/organizational_chart.scss b/erpnext/public/scss/hierarchy_chart.scss similarity index 100% rename from erpnext/public/scss/organizational_chart.scss rename to erpnext/public/scss/hierarchy_chart.scss From 249621af1b25ee356dd7337b93461ccacbbad906 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 21:26:47 +0530 Subject: [PATCH 284/369] refactor: add options to chart - method to return the node data - wrapper for showing the hierarchy --- .../organizational_chart.js | 6 ++-- .../organizational_chart.py | 6 ++-- .../hierarchy_chart_desktop.js | 28 +++++++++------- .../hierarchy_chart/hierarchy_chart_mobile.js | 32 +++++++++++-------- 4 files changed, 41 insertions(+), 31 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index 0fe724c78e4..ca9855286ca 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -8,10 +8,12 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { $(wrapper).bind('show', () => { frappe.require('/assets/js/hierarchy-chart.min.js', () => { let organizational_chart = undefined; + let method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; + if (frappe.is_mobile()) { - organizational_chart = new erpnext.HierarchyChartMobile(wrapper); + organizational_chart = new erpnext.HierarchyChartMobile(wrapper, method); } else { - organizational_chart = new erpnext.HierarchyChart(wrapper); + organizational_chart = new erpnext.HierarchyChart(wrapper, method); } organizational_chart.show(); }); diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index ae91a919b2b..f3aa13897d5 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -9,7 +9,7 @@ def get_children(parent=None, company=None, exclude_node=None, is_root=False, is filters.append(['company', '=', company]) if not fields: - fields = ['employee_name', 'name', 'reports_to', 'image', 'designation'] + fields = ['employee_name as name', 'name as id', 'reports_to', 'image', 'designation as title'] if is_root: parent = '' @@ -27,9 +27,9 @@ def get_children(parent=None, company=None, exclude_node=None, is_root=False, is for employee in employees: is_expandable = frappe.get_all('Employee', filters=[ - ['reports_to', '=', employee.get('name')] + ['reports_to', '=', employee.get('id')] ]) - employee.connections = get_connections(employee.name) + employee.connections = get_connections(employee.id) employee.expandable = 1 if is_expandable else 0 return employees diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index fd84d4ea5ca..052f140c139 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -1,8 +1,14 @@ erpnext.HierarchyChart = class { - - constructor(wrapper) { + /* Options: + - wrapper: wrapper for the hierarchy view + - method: + - to get the data for each node + - this method should return id, name, title, image, and connections for each node + */ + constructor(wrapper, method) { this.wrapper = $(wrapper); this.page = wrapper.page; + this.method = method; this.page.main.css({ 'min-height': '300px', @@ -114,8 +120,6 @@ erpnext.HierarchyChart = class { } render_root_node() { - this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; - let me = this; frappe.call({ @@ -128,12 +132,12 @@ erpnext.HierarchyChart = class { let data = r.message[0]; let root_node = new me.Node({ - id: data.name, + id: data.id, parent: me.$hierarchy.find('.root-level'), parent_id: undefined, image: data.image, - name: data.employee_name, - title: data.designation, + name: data.name, + title: data.title, expandable: true, connections: data.connections, is_root: true, @@ -225,7 +229,7 @@ erpnext.HierarchyChart = class { this.add_node(node, data); setTimeout(() => { - this.add_connector(node.id, data.name); + this.add_connector(node.id, data.id); }, 250); }); } @@ -240,12 +244,12 @@ erpnext.HierarchyChart = class { var $li = $('
                                          • '); return new this.Node({ - id: data.name, + id: data.id, parent: $li.appendTo(node.$children), parent_id: node.id, image: data.image, - name: data.employee_name, - title: data.designation, + name: data.name, + title: data.title, expandable: data.expandable, connections: data.connections, children: undefined @@ -333,7 +337,7 @@ erpnext.HierarchyChart = class { (child_nodes) => { if (child_nodes) { $.each(child_nodes, (_i, data) => { - this.add_connector(node_parent, data.name); + this.add_connector(node_parent, data.id); }); } } diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index c705681438d..1b8bc2e8e0f 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -1,8 +1,14 @@ erpnext.HierarchyChartMobile = class { - - constructor(wrapper) { + /* Options: + - wrapper: wrapper for the hierarchy view + - method: + - to get the data for each node + - this method should return id, name, title, image, and connections for each node + */ + constructor(wrapper, method) { this.wrapper = $(wrapper); this.page = wrapper.page; + this.method = method; this.page.main.css({ 'min-height': '300px', @@ -123,8 +129,6 @@ erpnext.HierarchyChartMobile = class { } render_root_node() { - this.method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; - let me = this; frappe.call({ @@ -137,12 +141,12 @@ erpnext.HierarchyChartMobile = class { let data = r.message[0]; let root_node = new me.Node({ - id: data.name, + id: data.id, parent: me.$hierarchy.find('.root-level'), parent_id: undefined, image: data.image, - name: data.employee_name, - title: data.designation, + name: data.name, + title: data.title, expandable: true, connections: data.connections, is_root: true, @@ -249,10 +253,10 @@ erpnext.HierarchyChartMobile = class { if (child_nodes) { $.each(child_nodes, (_i, data) => { this.add_node(node, data); - $(`#${data.name}`).addClass('active-child'); + $(`#${data.id}`).addClass('active-child'); setTimeout(() => { - this.add_connector(node.id, data.name); + this.add_connector(node.id, data.id); }, 250); }); } @@ -266,12 +270,12 @@ erpnext.HierarchyChartMobile = class { var $li = $('
                                          • '); return new this.Node({ - id: data.name, + id: data.id, parent: $li.appendTo(node.$children), parent_id: node.id, image: data.image, - name: data.employee_name, - title: data.designation, + name: data.name, + title: data.title, expandable: data.expandable, connections: data.connections, children: undefined @@ -418,7 +422,7 @@ erpnext.HierarchyChartMobile = class { ${html}
                                            + title="${extra_nodes.map(node => node.name).join(', ')}"> +${extra_nodes.length}
                                            @@ -443,7 +447,7 @@ erpnext.HierarchyChartMobile = class { } get_avatar(node) { - return ` + return ` ` } From aacb649050431472812b2355dff6bab9be458adb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 21:51:21 +0530 Subject: [PATCH 285/369] feat: setup node edit action --- .../organizational_chart/organizational_chart.js | 4 ++-- .../js/hierarchy_chart/hierarchy_chart_desktop.js | 14 +++++++++++++- .../js/hierarchy_chart/hierarchy_chart_mobile.js | 14 +++++++++++++- erpnext/public/js/templates/node_card.html | 4 ++-- 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index ca9855286ca..a1388867687 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -11,9 +11,9 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { let method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; if (frappe.is_mobile()) { - organizational_chart = new erpnext.HierarchyChartMobile(wrapper, method); + organizational_chart = new erpnext.HierarchyChartMobile('Employee', wrapper, method); } else { - organizational_chart = new erpnext.HierarchyChart(wrapper, method); + organizational_chart = new erpnext.HierarchyChart('Employee', wrapper, method); } organizational_chart.show(); }); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 052f140c139..0823ec77a80 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -1,14 +1,16 @@ erpnext.HierarchyChart = class { /* Options: + - doctype - wrapper: wrapper for the hierarchy view - method: - to get the data for each node - this method should return id, name, title, image, and connections for each node */ - constructor(wrapper, method) { + constructor(doctype, wrapper, method) { this.wrapper = $(wrapper); this.page = wrapper.page; this.method = method; + this.doctype = doctype; this.page.main.css({ 'min-height': '300px', @@ -36,6 +38,7 @@ erpnext.HierarchyChart = class { me.nodes[this.id] = this; me.make_node_element(this); me.setup_node_click_action(this); + me.setup_edit_node_action(this); } } } @@ -363,6 +366,15 @@ erpnext.HierarchyChart = class { }); } + setup_edit_node_action(node) { + let node_element = $(`#${node.id}`); + let me = this; + + node_element.find('.btn-edit-node').click(function() { + frappe.set_route('Form', me.doctype, node.id); + }); + } + remove_levels_after_node(node) { let level = $(`#${node.id}`).parent().parent().parent(); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 1b8bc2e8e0f..4b09714d0ad 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -1,14 +1,16 @@ erpnext.HierarchyChartMobile = class { /* Options: + - doctype - wrapper: wrapper for the hierarchy view - method: - to get the data for each node - this method should return id, name, title, image, and connections for each node */ - constructor(wrapper, method) { + constructor(doctype, wrapper, method) { this.wrapper = $(wrapper); this.page = wrapper.page; this.method = method; + this.doctype = doctype this.page.main.css({ 'min-height': '300px', @@ -36,6 +38,7 @@ erpnext.HierarchyChartMobile = class { me.nodes[this.id] = this; me.make_node_element(this); me.setup_node_click_action(this); + me.setup_edit_node_action(this); } } } @@ -385,6 +388,15 @@ erpnext.HierarchyChartMobile = class { }); } + setup_edit_node_action(node) { + let node_element = $(`#${node.id}`); + let me = this; + + node_element.find('.btn-edit-node').click(function() { + frappe.set_route('Form', me.doctype, node.id); + }); + } + setup_node_group_action() { let me = this; diff --git a/erpnext/public/js/templates/node_card.html b/erpnext/public/js/templates/node_card.html index e42e54f690c..30aedab4bb7 100644 --- a/erpnext/public/js/templates/node_card.html +++ b/erpnext/public/js/templates/node_card.html @@ -17,9 +17,9 @@
                                            {{ title }}
                                            {% if connections == 1 %} -
                                            · {{ connections }}
                                            +
                                            · {{ connections }} Connection
                                            {% else %} -
                                            · {{ connections }}
                                            +
                                            · {{ connections }} Connections
                                            {% endif %}
                                            From e179cd9841fa26727edbc534a7a0cd2a7378893b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 21:57:27 +0530 Subject: [PATCH 286/369] fix: revert changes in employee descendants query --- erpnext/hr/doctype/employee/employee.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 96046fba817..5ca47560b10 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -477,14 +477,13 @@ def get_employee_emails(employee_list): return employee_emails @frappe.whitelist() -def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False, fields=None): +def get_children(doctype, parent=None, company=None, is_root=False, is_tree=False): filters = [['status', '=', 'Active']] if company and company != 'All Companies': filters.append(['company', '=', company]) - if not fields: - fields = ['name as value', 'employee_name as title'] + fields = ['name as value', 'employee_name as title'] if is_root: parent = '' From 4e7cda6e65ece422cfbd5ddd631f55e86582eb0f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 30 Jun 2021 01:46:10 +0530 Subject: [PATCH 287/369] refactor: use arcs instead of bezier curves for cleaner connectors --- .../hierarchy_chart_desktop.js | 54 +++++++++++++++---- erpnext/public/scss/hierarchy_chart.scss | 2 +- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 0823ec77a80..ba811be5869 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -277,15 +277,53 @@ erpnext.HierarchyChart = class { y: child_node.offsetTop + child_node.offsetHeight / 2 }; - let connector = - "M" + - (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + - "C" + - (pos_parent_right.x + 100) + "," + (pos_parent_right.y) + " " + - (pos_child_left.x - 100) + "," + (pos_child_left.y) + " " + - (pos_child_left.x) + "," + (pos_child_left.y); + let connector = this.get_connector(pos_parent_right, pos_child_left); path.setAttribute("d", connector); + this.set_path_attributes(path, parent_id, child_id); + + $('#connectors').append(path); + } + + get_connector(pos_parent_right, pos_child_left) { + if (pos_parent_right.y === pos_child_left.y) { + // don't add arcs if it's a straight line + return "M" + + (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + + "L"+ + (pos_child_left.x) + "," + (pos_child_left.y); + } else { + let arc_1 = ""; + let arc_2 = ""; + let offset = 0; + + if (pos_parent_right.y > pos_child_left.y) { + // if child is above parent on Y axis 1st arc is anticlocwise + // second arc is clockwise + arc_1 = "a10,10 1 0 0 10,-10 "; + arc_2 = "a10,10 0 0 1 10,-10 "; + offset = 10; + } else { + // if child is below parent on Y axis 1st arc is clockwise + // second arc is anticlockwise + arc_1 = "a10,10 0 0 1 10,10 "; + arc_2 = "a10,10 1 0 0 10,10 "; + offset = -10; + } + + return "M" + (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + + "L" + + (pos_parent_right.x + 40) + "," + (pos_parent_right.y) + " " + + arc_1 + + "L" + + (pos_parent_right.x + 50) + "," + (pos_child_left.y + offset) + " " + + arc_2 + + "L"+ + (pos_child_left.x) + "," + (pos_child_left.y); + } + } + + set_path_attributes(path, parent_id, child_id) { path.setAttribute("data-parent", parent_id); path.setAttribute("data-child", child_id); @@ -298,8 +336,6 @@ erpnext.HierarchyChart = class { path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); } - - $('#connectors').append(path); } set_selected_node(node) { diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 16b8792432e..16137fdb5f0 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -48,7 +48,6 @@ border-radius: 0.5rem; padding: 0.75rem; width: 18rem; - height: 5rem; .btn-edit-node { display: flex; @@ -195,6 +194,7 @@ .level { margin-right: 8px; + align-items: flex-start; } #arrows { From 6d06d8c207afabd5f1c60ef0f9b9d357ec109897 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 30 Jun 2021 01:57:43 +0530 Subject: [PATCH 288/369] feat: add arc to connectors in mobile view --- erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 4b09714d0ad..b09b428b306 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -322,7 +322,8 @@ erpnext.HierarchyChartMobile = class { "M" + (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + "L" + - (pos_parent_bottom.x) + "," + (pos_child_left.y) + " " + + (pos_parent_bottom.x) + "," + (pos_child_left.y - 10) + " " + + "a10,10 1 0 0 10,10 " + "L" + (pos_child_left.x) + "," + (pos_child_left.y); From d363f9db3d19cd01188cc4516f461d14dd0eb72c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 30 Jun 2021 02:29:16 +0530 Subject: [PATCH 289/369] fix: edit node button overflowing --- erpnext/public/js/templates/node_card.html | 2 +- erpnext/public/scss/hierarchy_chart.scss | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/templates/node_card.html b/erpnext/public/js/templates/node_card.html index 30aedab4bb7..c3d8e010b53 100644 --- a/erpnext/public/js/templates/node_card.html +++ b/erpnext/public/js/templates/node_card.html @@ -8,7 +8,7 @@
                                            {{ name }} -
                                            + diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 16137fdb5f0..eefc14d6797 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -57,6 +57,7 @@ font-size: .75rem; justify-content: center; box-shadow: var(--shadow-sm); + margin-left: auto; } .edit-chart-node { @@ -79,6 +80,7 @@ align-items: center; justify-content: space-between; margin-bottom: 2px; + width: 12.2rem; } } From 781d1bf28d7cc30c9b7fbf11d7a580af1b55286f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 2 Jul 2021 18:15:18 +0530 Subject: [PATCH 290/369] fix: sider --- .../hierarchy_chart_desktop.js | 25 +++++++++---------- .../hierarchy_chart/hierarchy_chart_mobile.js | 18 ++++++------- erpnext/public/scss/hierarchy_chart.scss | 5 +--- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index ba811be5869..9e82fb20024 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -40,7 +40,7 @@ erpnext.HierarchyChart = class { me.setup_node_click_action(this); me.setup_edit_node_action(this); } - } + }; } make_node_element(node) { @@ -76,7 +76,7 @@ erpnext.HierarchyChart = class { me.company = company.get_value(); // svg for connectors - me.make_svg_markers() + me.make_svg_markers(); if (me.$hierarchy) me.$hierarchy.remove(); @@ -149,7 +149,7 @@ erpnext.HierarchyChart = class { me.expand_node(root_node); } } - }) + }); } expand_node(node) { @@ -166,7 +166,7 @@ erpnext.HierarchyChart = class { // rebuild incoming connections let grandparent = $(`#${node.parent_id}`).attr('data-parent'); - this.refresh_connectors(grandparent) + this.refresh_connectors(grandparent); } if (node.expandable && !node.expanded) { @@ -176,8 +176,8 @@ erpnext.HierarchyChart = class { collapse_node() { if (this.selected_node.expandable) { - this.selected_node.$children.hide(); - $(`path[data-parent="${this.selected_node.id}"]`).hide(); + this.selected_node.$children.hide('fast'); + $(`path[data-parent="${this.selected_node.id}"]`).hide('fast'); this.selected_node.expanded = false; } } @@ -222,15 +222,14 @@ erpnext.HierarchyChart = class { if (!node.$children) { node.$children = $('
                                              ') - .hide() - .appendTo(this.$hierarchy.find('.level:last')); + .hide() + .appendTo(this.$hierarchy.find('.level:last')); node.$children.empty(); if (child_nodes) { $.each(child_nodes, (_i, data) => { this.add_node(node, data); - setTimeout(() => { this.add_connector(node.id, data.id); }, 250); @@ -238,8 +237,8 @@ erpnext.HierarchyChart = class { } } - node.$children.show(); - $(`path[data-parent="${node.id}"]`).show(); + node.$children.show('fast'); + $(`path[data-parent="${node.id}"]`).show('fast'); node.expanded = true; } @@ -443,6 +442,6 @@ erpnext.HierarchyChart = class { return; $(path).remove(); - }) + }); } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index b09b428b306..2ff00baa7c6 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -10,7 +10,7 @@ erpnext.HierarchyChartMobile = class { this.wrapper = $(wrapper); this.page = wrapper.page; this.method = method; - this.doctype = doctype + this.doctype = doctype; this.page.main.css({ 'min-height': '300px', @@ -40,7 +40,7 @@ erpnext.HierarchyChartMobile = class { me.setup_node_click_action(this); me.setup_edit_node_action(this); } - } + }; } make_node_element(node) { @@ -78,7 +78,7 @@ erpnext.HierarchyChartMobile = class { me.company = company.get_value(); // svg for connectors - me.make_svg_markers() + me.make_svg_markers(); if (me.$sibling_group) me.$sibling_group.remove(); @@ -158,7 +158,7 @@ erpnext.HierarchyChartMobile = class { me.expand_node(root_node); } } - }) + }); } expand_node(node) { @@ -248,8 +248,8 @@ erpnext.HierarchyChartMobile = class { render_child_nodes(node, child_nodes) { if (!node.$children) { node.$children = $('
                                                ') - .hide() - .appendTo(node.$link.parent()); + .hide() + .appendTo(node.$link.parent()); node.$children.empty(); @@ -462,7 +462,7 @@ erpnext.HierarchyChartMobile = class { get_avatar(node) { return ` - ` + `; } expand_sibling_group_node(parent) { @@ -518,7 +518,7 @@ erpnext.HierarchyChartMobile = class { return; $(path).remove(); - }) + }); } refresh_connectors(node_parent, node_id) { @@ -527,4 +527,4 @@ erpnext.HierarchyChartMobile = class { $(`path[data-parent="${node_parent}"]`).remove(); this.add_connector(node_parent, node_id); } -} \ No newline at end of file +}; \ No newline at end of file diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index eefc14d6797..a54bf6f332e 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -62,16 +62,13 @@ .edit-chart-node { display: block; + margin-right: 0.25rem; } .node-edit-icon { display: block; } - .edit-chart-node { - margin-right: 0.25rem; - } - .node-edit-icon > .icon{ stroke: var(--blue-500); } From 6a4cce2431c5408788fd19a1297b64b0791cdeae Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 6 Jul 2021 18:16:49 +0530 Subject: [PATCH 291/369] fix: removing orphaned connectors --- .../js/hierarchy_chart/hierarchy_chart_desktop.js | 14 ++++++-------- .../js/hierarchy_chart/hierarchy_chart_mobile.js | 6 ++---- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 9e82fb20024..1896ac7c39e 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -176,8 +176,8 @@ erpnext.HierarchyChart = class { collapse_node() { if (this.selected_node.expandable) { - this.selected_node.$children.hide('fast'); - $(`path[data-parent="${this.selected_node.id}"]`).hide('fast'); + this.selected_node.$children.hide(); + $(`path[data-parent="${this.selected_node.id}"]`).hide(); this.selected_node.expanded = false; } } @@ -237,8 +237,8 @@ erpnext.HierarchyChart = class { } } - node.$children.show('fast'); - $(`path[data-parent="${node.id}"]`).show('fast'); + node.$children.show(); + $(`path[data-parent="${node.id}"]`).show(); node.expanded = true; } @@ -262,9 +262,7 @@ erpnext.HierarchyChart = class { let parent_node = document.querySelector(`#${parent_id}`); let child_node = document.querySelector(`#${child_id}`); - // variable for the namespace - const svgns = 'http://www.w3.org/2000/svg'; - let path = document.createElementNS(svgns, 'path'); + let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); // we need to connect right side of the parent to the left side of the child node let pos_parent_right = { @@ -438,7 +436,7 @@ erpnext.HierarchyChart = class { let parent = $(path).data('parent'); let child = $(path).data('child'); - if ($(parent).length || $(child).length) + if ($(`#${parent}`).length && $(`#${child}`).length) return; $(path).remove(); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 2ff00baa7c6..102cbb03b0a 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -289,9 +289,7 @@ erpnext.HierarchyChartMobile = class { let parent_node = document.querySelector(`#${parent_id}`); let child_node = document.querySelector(`#${child_id}`); - // variable for the namespace - const svgns = 'http://www.w3.org/2000/svg'; - let path = document.createElementNS(svgns, 'path'); + let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); let connector = undefined; @@ -514,7 +512,7 @@ erpnext.HierarchyChartMobile = class { let parent = $(path).data('parent'); let child = $(path).data('child'); - if ($(parent).length || $(child).length) + if ($(`#${parent}`).length && $(`#${child}`).length) return; $(path).remove(); From 97d2bab43404af5863aee72e832917327d44331a Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 7 Jul 2021 09:43:28 +0530 Subject: [PATCH 292/369] fix: unnecessary variables --- .../js/hierarchy_chart/hierarchy_chart_desktop.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 1896ac7c39e..8d0685f80d5 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -153,7 +153,7 @@ erpnext.HierarchyChart = class { } expand_node(node) { - let is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id; + const is_sibling = this.selected_node && this.selected_node.parent_id === node.parent_id; this.set_selected_node(node); this.show_active_path(node); this.collapse_previous_level_nodes(node); @@ -243,11 +243,9 @@ erpnext.HierarchyChart = class { } add_node(node, data) { - var $li = $('
                                              • '); - return new this.Node({ id: data.id, - parent: $li.appendTo(node.$children), + parent: $('
                                              • ').appendTo(node.$children), parent_id: node.id, image: data.image, name: data.name, @@ -259,8 +257,8 @@ erpnext.HierarchyChart = class { } add_connector(parent_id, child_id) { - let parent_node = document.querySelector(`#${parent_id}`); - let child_node = document.querySelector(`#${child_id}`); + const parent_node = document.querySelector(`#${parent_id}`); + const child_node = document.querySelector(`#${child_id}`); let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); @@ -276,7 +274,7 @@ erpnext.HierarchyChart = class { let connector = this.get_connector(pos_parent_right, pos_child_left); - path.setAttribute("d", connector); + path.setAttribute('d', connector); this.set_path_attributes(path, parent_id, child_id); $('#connectors').append(path); @@ -385,7 +383,7 @@ erpnext.HierarchyChart = class { let node_element = $(`#${node.id}`); node_element.click(function() { - let is_sibling = me.selected_node.parent_id === node.parent_id; + const is_sibling = me.selected_node.parent_id === node.parent_id; if (is_sibling) { me.collapse_node(); From 31a0f36ed9203ed2a20143be527ffe4280f50938 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 7 Jul 2021 12:05:50 +0530 Subject: [PATCH 293/369] feat: handle multiple root / orphan nodes --- .../organizational_chart.py | 3 +- .../hierarchy_chart_desktop.js | 48 +++++++++---------- .../hierarchy_chart/hierarchy_chart_mobile.js | 35 +++++++------- erpnext/public/scss/hierarchy_chart.scss | 4 ++ 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index f3aa13897d5..77b8df7520b 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -17,7 +17,7 @@ def get_children(parent=None, company=None, exclude_node=None, is_root=False, is if exclude_node: filters.append(['name', '!=', exclude_node]) - if parent and company and parent!=company: + if parent and company and parent != company: filters.append(['reports_to', '=', parent]) else: filters.append(['reports_to', '=', '']) @@ -32,6 +32,7 @@ def get_children(parent=None, company=None, exclude_node=None, is_root=False, is employee.connections = get_connections(employee.id) employee.expandable = 1 if is_expandable else 0 + employees.sort(key=lambda x: x['connections'], reverse=True) return employees diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 8d0685f80d5..e89a98ac4f8 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -84,11 +84,13 @@ erpnext.HierarchyChart = class { // setup hierarchy me.$hierarchy = $( `
                                                  -
                                                • +
                                                • +
                                                    +
                                                  `); me.page.main.append(me.$hierarchy); - me.render_root_node(); + me.render_root_nodes(); } } }); @@ -122,7 +124,7 @@ erpnext.HierarchyChart = class { `); } - render_root_node() { + render_root_nodes() { let me = this; frappe.call({ @@ -132,21 +134,28 @@ erpnext.HierarchyChart = class { }, callback: function(r) { if (r.message.length) { - let data = r.message[0]; + let nodes = r.message; + let node = undefined; + let first_root = undefined; - let root_node = new me.Node({ - id: data.id, - parent: me.$hierarchy.find('.root-level'), - parent_id: undefined, - image: data.image, - name: data.name, - title: data.title, - expandable: true, - connections: data.connections, - is_root: true, + $.each(nodes, (i, data) => { + node = new me.Node({ + id: data.id, + parent: $('
                                                • ').appendTo(me.$hierarchy.find('.node-children')), + parent_id: undefined, + image: data.image, + name: data.name, + title: data.title, + expandable: true, + connections: data.connections, + is_root: true + }); + + if (i == 0) + first_root = node; }); - me.expand_node(root_node); + me.expand_node(first_root); } } }); @@ -344,12 +353,7 @@ erpnext.HierarchyChart = class { collapse_previous_level_nodes(node) { let node_parent = $(`#${node.parent_id}`); - let previous_level_nodes = node_parent.parent().parent().children('li'); - if (node_parent.parent().hasClass('root-level')) { - previous_level_nodes = node_parent.parent().children('li'); - } - let node_card = undefined; previous_level_nodes.each(function() { @@ -409,10 +413,6 @@ erpnext.HierarchyChart = class { remove_levels_after_node(node) { let level = $(`#${node.id}`).parent().parent().parent(); - if ($(`#${node.id}`).parent().hasClass('root-level')) { - level = $(`#${node.id}`).parent(); - } - level = $('.hierarchy > li:eq('+ level.index() + ')'); level.nextAll('li').remove(); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 102cbb03b0a..5eee27b5fc3 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -97,7 +97,7 @@ erpnext.HierarchyChartMobile = class { `); me.page.main.append(me.$hierarchy); - me.render_root_node(); + me.render_root_nodes(); } } }); @@ -131,7 +131,7 @@ erpnext.HierarchyChartMobile = class { `); } - render_root_node() { + render_root_nodes() { let me = this; frappe.call({ @@ -141,21 +141,21 @@ erpnext.HierarchyChartMobile = class { }, callback: function(r) { if (r.message.length) { - let data = r.message[0]; + let nodes = r.message; - let root_node = new me.Node({ - id: data.id, - parent: me.$hierarchy.find('.root-level'), - parent_id: undefined, - image: data.image, - name: data.name, - title: data.title, - expandable: true, - connections: data.connections, - is_root: true, + $.each(nodes, (_i, data) => { + return new me.Node({ + id: data.id, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.name, + title: data.title, + expandable: true, + connections: data.connections, + is_root: true + }); }); - - me.expand_node(root_node); } } }); @@ -375,7 +375,10 @@ erpnext.HierarchyChartMobile = class { let node_element = $(`#${node.id}`); node_element.click(function() { - if (node_element.is(':visible') && node_element.hasClass('active-path')) { + if (node.is_root) { + me.$hierarchy.empty(); + me.add_node_to_hierarchy(node, true); + } else if (node_element.is(':visible') && node_element.hasClass('active-path')) { me.remove_levels_after_node(node); me.remove_orphaned_connectors(); } else { diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index a54bf6f332e..dd523c34439 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -246,6 +246,10 @@ margin-top: 16px; } +.root-level .node-card { + margin: 0 0 16px; +} + // node group .collapsed-level { From a48b23e6a5aee5831f777e1cacfc55f4ad3afbd4 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 8 Jul 2021 09:53:31 +0530 Subject: [PATCH 294/369] perf: Optimise Rendering - optimise get_children function - use promises instead of callbacks - optimise selectors - use const wherever possible - use pure js instead of jquery for connectors for faster rendering --- .../organizational_chart.py | 22 ++--- .../hierarchy_chart_desktop.js | 82 +++++++++---------- .../hierarchy_chart/hierarchy_chart_mobile.js | 65 +++++++-------- 3 files changed, 78 insertions(+), 91 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 77b8df7520b..46578f3aaf7 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,33 +2,25 @@ from __future__ import unicode_literals import frappe @frappe.whitelist() -def get_children(parent=None, company=None, exclude_node=None, is_root=False, is_tree=False, fields=None): +def get_children(parent=None, company=None): filters = [['status', '!=', 'Left']] if company and company != 'All Companies': filters.append(['company', '=', company]) - if not fields: - fields = ['employee_name as name', 'name as id', 'reports_to', 'image', 'designation as title'] - - if is_root: - parent = '' - - if exclude_node: - filters.append(['name', '!=', exclude_node]) - if parent and company and parent != company: filters.append(['reports_to', '=', parent]) else: filters.append(['reports_to', '=', '']) - employees = frappe.get_list('Employee', fields=fields, - filters=filters, order_by='name') + employees = frappe.get_list('Employee', + fields=['employee_name as name', 'name as id', 'reports_to', 'image', 'designation as title'], + filters=filters, + order_by='name' + ) for employee in employees: - is_expandable = frappe.get_all('Employee', filters=[ - ['reports_to', '=', employee.get('id')] - ]) + is_expandable = frappe.db.count('Employee', filters={'reports_to': employee.get('id')}) employee.connections = get_connections(employee.id) employee.expandable = 1 if is_expandable else 0 diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index e89a98ac4f8..bf366792a9c 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -7,7 +7,6 @@ erpnext.HierarchyChart = class { - this method should return id, name, title, image, and connections for each node */ constructor(doctype, wrapper, method) { - this.wrapper = $(wrapper); this.page = wrapper.page; this.method = method; this.doctype = doctype; @@ -61,6 +60,8 @@ erpnext.HierarchyChart = class { frappe.breadcrumbs.add('HR'); let me = this; + if ($(`[data-fieldname="company"]`).length) return; + let company = this.page.add_field({ fieldtype: 'Link', options: 'Company', @@ -131,32 +132,30 @@ erpnext.HierarchyChart = class { method: me.method, args: { company: me.company - }, - callback: function(r) { - if (r.message.length) { - let nodes = r.message; - let node = undefined; - let first_root = undefined; + } + }).then(r => { + if (r.message.length) { + let node = undefined; + let first_root = undefined; - $.each(nodes, (i, data) => { - node = new me.Node({ - id: data.id, - parent: $('
                                                • ').appendTo(me.$hierarchy.find('.node-children')), - parent_id: undefined, - image: data.image, - name: data.name, - title: data.title, - expandable: true, - connections: data.connections, - is_root: true - }); - - if (i == 0) - first_root = node; + $.each(r.message, (i, data) => { + node = new me.Node({ + id: data.id, + parent: $('
                                                • ').appendTo(me.$hierarchy.find('.node-children')), + parent_id: undefined, + image: data.image, + name: data.name, + title: data.title, + expandable: true, + connections: data.connections, + is_root: true }); - me.expand_node(first_root); - } + if (i == 0) + first_root = node; + }); + + me.expand_node(first_root); } }); } @@ -204,18 +203,14 @@ erpnext.HierarchyChart = class { } get_child_nodes(node_id) { - let me = this; return new Promise(resolve => { frappe.call({ method: this.method, args: { parent: node_id, - company: me.company - }, - callback: (r) => { - resolve(r.message); + company: this.company } - }); + }).then(r => resolve(r.message)); }); } @@ -266,27 +261,28 @@ erpnext.HierarchyChart = class { } add_connector(parent_id, child_id) { + // using pure javascript for better performance const parent_node = document.querySelector(`#${parent_id}`); const child_node = document.querySelector(`#${child_id}`); let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); // we need to connect right side of the parent to the left side of the child node - let pos_parent_right = { + const pos_parent_right = { x: parent_node.offsetLeft + parent_node.offsetWidth, y: parent_node.offsetTop + parent_node.offsetHeight / 2 }; - let pos_child_left = { + const pos_child_left = { x: child_node.offsetLeft - 5, y: child_node.offsetTop + child_node.offsetHeight / 2 }; - let connector = this.get_connector(pos_parent_right, pos_child_left); + const connector = this.get_connector(pos_parent_right, pos_child_left); path.setAttribute('d', connector); this.set_path_attributes(path, parent_id, child_id); - $('#connectors').append(path); + document.getElementById('connectors').appendChild(path); } get_connector(pos_parent_right, pos_child_left) { @@ -330,12 +326,13 @@ erpnext.HierarchyChart = class { set_path_attributes(path, parent_id, child_id) { path.setAttribute("data-parent", parent_id); path.setAttribute("data-child", child_id); + const parent = $(`#${parent_id}`); - if ($(`#${parent_id}`).hasClass('active')) { + if (parent.hasClass('active')) { path.setAttribute("class", "active-connector"); path.setAttribute("marker-start", "url(#arrowstart-active)"); path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if ($(`#${parent_id}`).hasClass('active-path')) { + } else if (parent.hasClass('active-path')) { path.setAttribute("class", "collapsed-connector"); path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); @@ -343,8 +340,9 @@ erpnext.HierarchyChart = class { } set_selected_node(node) { - // remove .active class from the current node - $('.active').removeClass('active'); + // remove active class from the current node + if (this.selected_node) + this.selected_node.$link.removeClass('active'); // add active class to the newly selected node this.selected_node = node; @@ -411,9 +409,9 @@ erpnext.HierarchyChart = class { } remove_levels_after_node(node) { - let level = $(`#${node.id}`).parent().parent().parent(); + let level = $(`#${node.id}`).parent().parent().parent().index(); - level = $('.hierarchy > li:eq('+ level.index() + ')'); + level = $('.hierarchy > li:eq('+ level + ')'); level.nextAll('li').remove(); let nodes = level.find('.node-card'); @@ -431,8 +429,8 @@ erpnext.HierarchyChart = class { remove_orphaned_connectors() { let paths = $('#connectors > path'); $.each(paths, (_i, path) => { - let parent = $(path).data('parent'); - let child = $(path).data('child'); + const parent = $(path).data('parent'); + const child = $(path).data('child'); if ($(`#${parent}`).length && $(`#${child}`).length) return; diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 5eee27b5fc3..17062e25857 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -7,7 +7,6 @@ erpnext.HierarchyChartMobile = class { - this method should return id, name, title, image, and connections for each node */ constructor(doctype, wrapper, method) { - this.wrapper = $(wrapper); this.page = wrapper.page; this.method = method; this.doctype = doctype; @@ -63,6 +62,8 @@ erpnext.HierarchyChartMobile = class { frappe.breadcrumbs.add('HR'); let me = this; + if ($(`[data-fieldname="company"]`).length) return; + let company = this.page.add_field({ fieldtype: 'Link', options: 'Company', @@ -139,24 +140,21 @@ erpnext.HierarchyChartMobile = class { args: { company: me.company }, - callback: function(r) { - if (r.message.length) { - let nodes = r.message; - - $.each(nodes, (_i, data) => { - return new me.Node({ - id: data.id, - parent: me.$hierarchy.find('.root-level'), - parent_id: undefined, - image: data.image, - name: data.name, - title: data.title, - expandable: true, - connections: data.connections, - is_root: true - }); + }).then(r => { + if (r.message.length) { + $.each(r.message, (_i, data) => { + return new me.Node({ + id: data.id, + parent: me.$hierarchy.find('.root-level'), + parent_id: undefined, + image: data.image, + name: data.name, + title: data.title, + expandable: true, + connections: data.connections, + is_root: true }); - } + }); } }); } @@ -237,11 +235,8 @@ erpnext.HierarchyChartMobile = class { parent: node_id, company: me.company, exclude_node: exclude_node - }, - callback: (r) => { - resolve(r.message); } - }); + }).then(r => resolve(r.message)); }); } @@ -286,10 +281,10 @@ erpnext.HierarchyChartMobile = class { } add_connector(parent_id, child_id) { - let parent_node = document.querySelector(`#${parent_id}`); - let child_node = document.querySelector(`#${child_id}`); + const parent_node = document.querySelector(`#${parent_id}`); + const child_node = document.querySelector(`#${child_id}`); - let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); let connector = undefined; @@ -299,10 +294,10 @@ erpnext.HierarchyChartMobile = class { connector = this.get_connector_for_collapsed_node(parent_node, child_node); } - path.setAttribute("d", connector); + path.setAttribute('d', connector); this.set_path_attributes(path, parent_id, child_id); - $('#connectors').append(path); + document.getElementById('connectors').appendChild(path); } get_connector_for_active_node(parent_node, child_node) { @@ -351,19 +346,21 @@ erpnext.HierarchyChartMobile = class { set_path_attributes(path, parent_id, child_id) { path.setAttribute("data-parent", parent_id); path.setAttribute("data-child", child_id); + const parent = $(`#${parent_id}`); - if ($(`#${parent_id}`).hasClass('active')) { + if (parent.hasClass('active')) { path.setAttribute("class", "active-connector"); path.setAttribute("marker-start", "url(#arrowstart-active)"); path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if ($(`#${parent_id}`).hasClass('active-path')) { + } else if (parent.hasClass('active-path')) { path.setAttribute("class", "collapsed-connector"); } } set_selected_node(node) { // remove .active class from the current node - $('.active').removeClass('active'); + if (this.selected_node) + this.selected_node.$link.removeClass('active'); // add active class to the newly selected node this.selected_node = node; @@ -494,9 +491,9 @@ erpnext.HierarchyChartMobile = class { } remove_levels_after_node(node) { - let level = $(`#${node.id}`).parent().parent(); + let level = $(`#${node.id}`).parent().parent().index(); - level = $('.hierarchy-mobile > li:eq('+ (level.index()) + ')'); + level = $('.hierarchy-mobile > li:eq('+ level + ')'); level.nextAll('li').remove(); let current_node = level.find(`#${node.id}`); @@ -512,8 +509,8 @@ erpnext.HierarchyChartMobile = class { remove_orphaned_connectors() { let paths = $('#connectors > path'); $.each(paths, (_i, path) => { - let parent = $(path).data('parent'); - let child = $(path).data('child'); + const parent = $(path).data('parent'); + const child = $(path).data('child'); if ($(`#${parent}`).length && $(`#${child}`).length) return; From b6715189fbb76dca90a60da972c05df267a0e030 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 8 Jul 2021 11:23:50 +0530 Subject: [PATCH 295/369] fix: do not sort by number of connections --- .../hr/page/organizational_chart/organizational_chart.py | 1 - .../public/js/hierarchy_chart/hierarchy_chart_desktop.js | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 46578f3aaf7..ce84b3c7444 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -24,7 +24,6 @@ def get_children(parent=None, company=None): employee.connections = get_connections(employee.id) employee.expandable = 1 if is_expandable else 0 - employees.sort(key=lambda x: x['connections'], reverse=True) return employees diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index bf366792a9c..374787c6ef2 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -135,8 +135,8 @@ erpnext.HierarchyChart = class { } }).then(r => { if (r.message.length) { + let expand_node = undefined; let node = undefined; - let first_root = undefined; $.each(r.message, (i, data) => { node = new me.Node({ @@ -151,11 +151,11 @@ erpnext.HierarchyChart = class { is_root: true }); - if (i == 0) - first_root = node; + if (!expand_node && data.connections) + expand_node = node; }); - me.expand_node(first_root); + me.expand_node(expand_node); } }); } From 1a3c335febcbd8c130fdc60806fc772c910d782c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 8 Jul 2021 16:55:42 +0530 Subject: [PATCH 296/369] feat: use icon for connections on mobile view --- .../js/hierarchy_chart/hierarchy_chart_mobile.js | 2 +- erpnext/public/js/templates/node_card.html | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 17062e25857..19852993789 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -50,7 +50,7 @@ erpnext.HierarchyChartMobile = class { image: node.image, parent: node.parent_id, connections: node.connections, - is_mobile: 1 + is_mobile: true }); node.parent.append(node_card); diff --git a/erpnext/public/js/templates/node_card.html b/erpnext/public/js/templates/node_card.html index c3d8e010b53..fb94df85ed8 100644 --- a/erpnext/public/js/templates/node_card.html +++ b/erpnext/public/js/templates/node_card.html @@ -16,10 +16,16 @@
                                                  {{ title }}
                                                  - {% if connections == 1 %} -
                                                  · {{ connections }} Connection
                                                  + {% if is_mobile %} +
                                                  + · {{ connections }} +
                                                  {% else %} -
                                                  · {{ connections }} Connections
                                                  + {% if connections == 1 %} +
                                                  · {{ connections }} Connection
                                                  + {% else %} +
                                                  · {{ connections }} Connections
                                                  + {% endif %} {% endif %}
                                                  From c79316a9c09a279374a4a3689265aaafaccc9665 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 8 Jul 2021 17:05:40 +0530 Subject: [PATCH 297/369] fix: exclude active node while fetching sibling group --- .../hr/page/organizational_chart/organizational_chart.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index ce84b3c7444..1e03e3d06ad 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,8 +2,7 @@ from __future__ import unicode_literals import frappe @frappe.whitelist() -def get_children(parent=None, company=None): - +def get_children(parent=None, company=None, exclude_node=None): filters = [['status', '!=', 'Left']] if company and company != 'All Companies': filters.append(['company', '=', company]) @@ -13,6 +12,9 @@ def get_children(parent=None, company=None): else: filters.append(['reports_to', '=', '']) + if exclude_node: + filters.append(['name', '!=', exclude_node]) + employees = frappe.get_list('Employee', fields=['employee_name as name', 'name as id', 'reports_to', 'image', 'designation as title'], filters=filters, From 9270de59f4c686431acd3e6a06c1974e990bb182 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 8 Jul 2021 18:44:53 +0530 Subject: [PATCH 298/369] fix: sibling group expansion not working for root nodes --- .../hierarchy_chart/hierarchy_chart_mobile.js | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 19852993789..d48b4c8f362 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -88,16 +88,7 @@ erpnext.HierarchyChartMobile = class { me.$sibling_group = $(`
                                                  `); me.page.main.append(me.$sibling_group); - if (me.$hierarchy) - me.$hierarchy.remove(); - - // setup hierarchy - me.$hierarchy = $( - `
                                                    -
                                                  • -
                                                  `); - - me.page.main.append(me.$hierarchy); + me.setup_hierarchy() me.render_root_nodes(); } } @@ -132,6 +123,19 @@ erpnext.HierarchyChartMobile = class { `); } + setup_hierarchy() { + $(`#connectors`).empty(); + if (this.$hierarchy) + this.$hierarchy.remove(); + + this.$hierarchy = $( + `
                                                    +
                                                  • +
                                                  `); + + this.page.main.append(this.$hierarchy); + } + render_root_nodes() { let me = this; @@ -142,10 +146,13 @@ erpnext.HierarchyChartMobile = class { }, }).then(r => { if (r.message.length) { + let root_level = me.$hierarchy.find('.root-level'); + root_level.empty(); + $.each(r.message, (_i, data) => { return new me.Node({ id: data.id, - parent: me.$hierarchy.find('.root-level'), + parent: root_level, parent_id: undefined, image: data.image, name: data.name, @@ -401,7 +408,12 @@ erpnext.HierarchyChartMobile = class { $('.node-group').on('click', function() { let parent = $(this).attr('data-parent'); - me.expand_sibling_group_node(parent); + if (parent === 'undefined') { + me.setup_hierarchy(); + me.render_root_nodes(); + } else { + me.expand_sibling_group_node(parent); + } }); } From 9e7302a21ea49d3abcf180f5fcbf768a94eefccc Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 9 Jul 2021 01:03:02 +0530 Subject: [PATCH 299/369] fix(mobile): collapsed nodes not expanding --- .../hierarchy_chart/hierarchy_chart_mobile.js | 58 ++++++++++--------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index d48b4c8f362..58530eaaf91 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -88,7 +88,7 @@ erpnext.HierarchyChartMobile = class { me.$sibling_group = $(`
                                                  `); me.page.main.append(me.$sibling_group); - me.setup_hierarchy() + me.setup_hierarchy(); me.render_root_nodes(); } } @@ -194,9 +194,9 @@ erpnext.HierarchyChartMobile = class { collapse_node() { let node = this.selected_node; - if (node.expandable) { + if (node.expandable && node.$children) { node.$children.hide(); - node.expanded = false; + node.expanded = 0; // add a collapsed level to show the collapsed parent // and a button beside it to move to that level @@ -212,10 +212,7 @@ erpnext.HierarchyChartMobile = class { frappe.run_serially([ () => this.get_child_nodes(node.parent_id, node.id), (child_nodes) => this.get_node_group(child_nodes, node.parent_id), - (node_group) => { - node_parent.find('.collapsed-level') - .append(node_group); - }, + (node_group) => node_parent.find('.collapsed-level').append(node_group), () => this.setup_node_group_action() ]); } @@ -268,7 +265,7 @@ erpnext.HierarchyChartMobile = class { } node.$children.show(); - node.expanded = true; + node.expanded = 1; } add_node(node, data) { @@ -380,13 +377,16 @@ erpnext.HierarchyChartMobile = class { node_element.click(function() { if (node.is_root) { + var el = $(this).detach(); me.$hierarchy.empty(); - me.add_node_to_hierarchy(node, true); + $(`#connectors`).empty(); + me.add_node_to_hierarchy(el, node); } else if (node_element.is(':visible') && node_element.hasClass('active-path')) { me.remove_levels_after_node(node); me.remove_orphaned_connectors(); } else { - me.add_node_to_hierarchy(node, true); + var el = $(this).detach(); + me.add_node_to_hierarchy(el, node); me.collapse_node(); } @@ -417,15 +417,15 @@ erpnext.HierarchyChartMobile = class { }); } - add_node_to_hierarchy(node) { - this.$hierarchy.append(` -
                                                • -
                                                  -
                                                  -
                                                • - `); + add_node_to_hierarchy(node_element, node) { + this.$hierarchy.append(`
                                                • `); + node_element.removeClass('active-child active-path'); + this.$hierarchy.find('.level:last').append(node_element); - node.$link.appendTo(this.$hierarchy.find('.level:last')); + let node_object = this.nodes[node.id]; + node_object.expanded = 0; + node_object.$children = undefined; + this.nodes[node.id] = node_object; } get_node_group(nodes, parent, collapsed=true) { @@ -478,9 +478,11 @@ erpnext.HierarchyChartMobile = class { expand_sibling_group_node(parent) { let node_object = this.nodes[parent]; let node = node_object.$link; + node.removeClass('active-child active-path'); node_object.expanded = 0; node_object.$children = undefined; + this.nodes[node.id] = node_object; // show parent's siblings and expand parent node frappe.run_serially([ @@ -491,17 +493,21 @@ erpnext.HierarchyChartMobile = class { this.$sibling_group.empty().append(node_group); }, () => this.setup_node_group_action(), - () => { - this.$hierarchy.empty().append(` -
                                                • - `); - this.$hierarchy.find('.level').append(node); - $(`#connectors`).empty(); - this.expand_node(node_object); - } + () => this.reattach_and_expand_node(node, node_object) ]); } + reattach_and_expand_node(node, node_object) { + var el = node.detach(); + + this.$hierarchy.empty().append(` +
                                                • + `); + this.$hierarchy.find('.level').append(el); + $(`#connectors`).empty(); + this.expand_node(node_object); + } + remove_levels_after_node(node) { let level = $(`#${node.id}`).parent().parent().index(); From e1cf7712d916b73a4f61154135e55c3a83fec1be Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 9 Jul 2021 01:25:26 +0530 Subject: [PATCH 300/369] fix: sider --- erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 58530eaaf91..5a6f1688767 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -376,8 +376,9 @@ erpnext.HierarchyChartMobile = class { let node_element = $(`#${node.id}`); node_element.click(function() { + let el = $(this).detach(); + if (node.is_root) { - var el = $(this).detach(); me.$hierarchy.empty(); $(`#connectors`).empty(); me.add_node_to_hierarchy(el, node); @@ -385,7 +386,6 @@ erpnext.HierarchyChartMobile = class { me.remove_levels_after_node(node); me.remove_orphaned_connectors(); } else { - var el = $(this).detach(); me.add_node_to_hierarchy(el, node); me.collapse_node(); } From 3cc85e166f6d2b0212db88a16bb94425ed8c385c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 14 Jul 2021 23:50:54 +0530 Subject: [PATCH 301/369] test: UI tests for org chart desktop --- .../test_organizational_chart_desktop.js | 102 ++++++++++++++++++ .../hierarchy_chart_desktop.js | 3 +- erpnext/tests/ui_test_helpers.py | 53 +++++++++ 3 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 cypress/integration/test_organizational_chart_desktop.js create mode 100644 erpnext/tests/ui_test_helpers.py diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js new file mode 100644 index 00000000000..d50d5513301 --- /dev/null +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -0,0 +1,102 @@ +context('Organizational Chart', () => { + before(() => { + cy.login(); + cy.visit('/app/website'); + + cy.visit(`app/organizational-chart`); + cy.fill_field('company', 'Test Org Chart'); + cy.get('body').click(); + cy.wait(500); + }); + + beforeEach(() => { + cy.window().its('frappe').then(frappe => { + return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); + }).as('employee_records'); + }); + + it('renders root nodes and loads children for the first expandable node', () => { + // check rendered root nodes and the node name, title, connections + cy.get('.hierarchy').find('.root-level ul.node-children').children() + .should('have.length', 2) + .first() + .as('first-child'); + + cy.get('@first-child').get('.node-name').contains('Test Employee 1'); + cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO'); + cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2 Connections'); + + // check children of first node + cy.get('@employee_records').then(employee_records => { + // children of 1st root visible + cy.get(`[data-parent="${employee_records.message[0]}"]`).as('child-node') + cy.get('@child-node') + .should('have.length', 1) + .should('be.visible'); + cy.get('@child-node').get('.node-name').contains('Test Employee 3'); + + // connectors between first root node and immediate child + cy.get(`path[data-parent="${employee_records.message[0]}"]`) + .should('be.visible') + .invoke('attr', 'data-child') + .should('equal', employee_records.message[2]); + }); + }); + + it('hides active nodes children and connectors on expanding sibling node', () => { + cy.get('@employee_records').then(employee_records => { + // click sibling + cy.get(`#${employee_records.message[1]}`) + .click() + .should('have.class', 'active'); + + // child nodes and connectors hidden + cy.get(`[data-parent="${employee_records.message[0]}"]`).should('not.be.visible'); + cy.get(`path[data-parent="${employee_records.message[0]}"]`).should('not.be.visible'); + }); + }); + + it('collapses previous level nodes and refreshes connectors on expanding child node', () => { + cy.get('@employee_records').then(employee_records => { + // click child node + cy.get(`#${employee_records.message[3]}`) + .click() + .should('have.class', 'active'); + + // previous level nodes: parent should be on active-path; other nodes should be collapsed + cy.get(`#${employee_records.message[0]}`).should('have.class', 'collapsed'); + cy.get(`#${employee_records.message[1]}`).should('have.class', 'active-path'); + + // previous level connectors refreshed + cy.get(`path[data-parent="${employee_records.message[1]}"]`) + .should('have.class', 'collapsed-connector'); + + // child node's children and connectors rendered + cy.get(`[data-parent="${employee_records.message[3]}"]`).should('be.visible'); + cy.get(`path[data-parent="${employee_records.message[3]}"]`).should('be.visible'); + }); + }); + + it('expands previous level nodes', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[0]}`) + .click() + .should('have.class', 'active'); + + cy.get(`[data-parent="${employee_records.message[0]}"]`) + .should('be.visible'); + + cy.get('ul.hierarchy').children().should('have.length', 2); + cy.get(`#connectors`).children().should('have.length', 1); + }); + }); + + it('edit node navigates to employee master', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node') + .click(); + + cy.url().should('include', `/employee/${employee_records.message[0]}`); + }); + }); +}); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 374787c6ef2..fe4d17c2102 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -49,7 +49,8 @@ erpnext.HierarchyChart = class { title: node.title, image: node.image, parent: node.parent_id, - connections: node.connections + connections: node.connections, + is_mobile: false }); node.parent.append(node_card); diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py new file mode 100644 index 00000000000..8e67b1cd346 --- /dev/null +++ b/erpnext/tests/ui_test_helpers.py @@ -0,0 +1,53 @@ +import frappe +from frappe import _ +from frappe.utils import getdate + +@frappe.whitelist() +def create_employee_records(): + company = create_company() + create_missing_designation() + + emp1 = create_employee('Test Employee 1', 'CEO') + emp2 = create_employee('Test Employee 2', 'CTO') + emp3 = create_employee('Test Employee 3', 'Head of Marketing and Sales', emp1) + emp4 = create_employee('Test Employee 4', 'Project Manager', emp2) + emp5 = create_employee('Test Employee 5', 'Analyst', emp3) + emp6 = create_employee('Test Employee 6', 'Software Developer', emp4) + + employees = [emp1, emp2, emp3, emp4, emp5, emp6] + return employees + +def create_company(): + company = frappe.db.exists('Company', 'Test Org Chart') + if not company: + company = frappe.get_doc({ + 'doctype': 'Company', + 'company_name': 'Test Org Chart', + 'country': 'India', + 'default_currency': 'INR' + }).insert().name + + return company + +def create_employee(first_name, designation, reports_to=None): + employee = frappe.db.exists('Employee', {'first_name': first_name, 'designation': designation}) + if not employee: + employee = frappe.get_doc({ + 'doctype': 'Employee', + 'first_name': first_name, + 'company': 'Test Org Chart', + 'gender': 'Female', + 'date_of_birth': getdate('08-12-1998'), + 'date_of_joining': getdate('01-01-2021'), + 'designation': designation, + 'reports_to': reports_to + }).insert().name + + return employee + +def create_missing_designation(): + if not frappe.db.exists('Designation', 'CTO'): + frappe.get_doc({ + 'doctype': 'Designation', + 'designation_name': 'CTO' + }).insert() \ No newline at end of file From a1d379d5d4e7d40d2f327d2c76a28c076ec5f137 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 15 Jul 2021 19:19:09 +0530 Subject: [PATCH 302/369] test: UI tests for org chart mobile fix(mobile): detach node before emptying hierarchy fix(mobile): sibling group not rendering for first level --- .../test_organizational_chart_mobile.js | 182 ++++++++++++++++++ .../hierarchy_chart/hierarchy_chart_mobile.js | 13 +- erpnext/tests/ui_test_helpers.py | 7 +- 3 files changed, 195 insertions(+), 7 deletions(-) create mode 100644 cypress/integration/test_organizational_chart_mobile.js diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js new file mode 100644 index 00000000000..656051289f1 --- /dev/null +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -0,0 +1,182 @@ +context('Organizational Chart Mobile', () => { + before(() => { + cy.login(); + cy.viewport(375, 667); + cy.visit('/app/website'); + + cy.visit(`app/organizational-chart`); + cy.wait(500); + cy.fill_field('company', 'Test Org Chart'); + cy.get('body').click(); + cy.wait(500); + }); + + beforeEach(() => { + cy.viewport(375, 667); + cy.wait(500); + + cy.window().its('frappe').then(frappe => { + return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); + }).as('employee_records'); + }); + + it('renders root nodes', () => { + // check rendered root nodes and the node name, title, connections + cy.get('.hierarchy-mobile').find('.root-level').children() + .should('have.length', 2) + .first() + .as('first-child'); + + cy.get('@first-child').get('.node-name').contains('Test Employee 1'); + cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO'); + cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2'); + }); + + it('expands root node', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[1]}`) + .click() + .should('have.class', 'active'); + + // other root node removed + cy.get(`#${employee_records.message[0]}`).should('not.exist'); + + // children of active root node + cy.get('.hierarchy-mobile').find('.level').first().find('ul.node-children').children() + .should('have.length', 2) + + cy.get(`[data-parent="${employee_records.message[1]}"]`).first().as('child-node'); + cy.get('@child-node').should('be.visible'); + + cy.get('@child-node') + .get('.node-name') + .contains('Test Employee 4'); + + // connectors between root node and immediate children + cy.get(`path[data-parent="${employee_records.message[1]}"]`).as('connectors'); + cy.get('@connectors') + .should('have.length', 2) + .should('be.visible') + + cy.get('@connectors') + .first() + .invoke('attr', 'data-child') + .should('eq', employee_records.message[3]); + }); + }); + + it('expands child node', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[3]}`) + .click() + .should('have.class', 'active') + .as('expanded_node'); + + // 2 levels on screen; 1 on active path; 1 collapsed + cy.get('.hierarchy-mobile').children().should('have.length', 2); + cy.get(`#${employee_records.message[1]}`).should('have.class', 'active-path'); + + // children of expanded node visible + cy.get('@expanded_node') + .next() + .should('have.class', 'node-children') + .as('node-children'); + + cy.get('@node-children').children().should('have.length', 1); + cy.get('@node-children') + .first() + .get('.node-card') + .should('have.class', 'active-child') + .contains('Test Employee 7'); + + // orphan connectors removed + cy.get(`#connectors`).children().should('have.length', 2); + }); + }); + + it('renders sibling group', () => { + cy.get('@employee_records').then(employee_records => { + // sibling group visible for parent + cy.get(`#${employee_records.message[1]}`) + .next() + .as('sibling_group'); + + cy.get('@sibling_group') + .should('have.attr', 'data-parent', 'undefined') + .should('have.class', 'node-group') + .and('have.class', 'collapsed') + + cy.get('@sibling_group').get('.avatar-group').children().as('siblings'); + cy.get('@siblings').should('have.length', 1); + cy.get('@siblings') + .first() + .should('have.attr', 'title', 'Test Employee 1'); + + }); + }); + + it('expands previous level nodes', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[6]}`) + .click() + .should('have.class', 'active'); + + // clicking on previous level node should remove all the nodes ahead + // and expand that node + cy.get(`#${employee_records.message[3]}`).click(); + cy.get(`#${employee_records.message[3]}`) + .should('have.class', 'active') + .should('not.have.class', 'active-path'); + + cy.get(`#${employee_records.message[6]}`).should('have.class', 'active-child'); + cy.get('.hierarchy-mobile').children().should('have.length', 2); + cy.get(`#connectors`).children().should('have.length', 2); + }); + }); + + it('expands sibling group', () => { + cy.get('@employee_records').then(employee_records => { + // sibling group visible for parent + cy.get(`#${employee_records.message[6]}`).click() + + cy.get(`#${employee_records.message[3]}`) + .next() + .click(); + + // siblings of parent should be visible + cy.get('.hierarchy-mobile').prev().as('sibling_group'); + cy.get('@sibling_group') + .should('exist') + .should('have.class', 'sibling-group') + .should('not.have.class', 'collapsed'); + + cy.get(`#${employee_records.message[1]}`) + .should('be.visible') + .should('have.class', 'active'); + + cy.get(`[data-parent="${employee_records.message[1]}"]`) + .should('be.visible') + .should('have.length', 2) + .should('have.class', 'active-child'); + }); + }); + + it('goes to the respective level after clicking on non-collapsed sibling group', () => { + // click on non-collapsed sibling group + cy.get('.hierarchy-mobile') + .prev() + .click(); + + // should take you to that level + cy.get('.hierarchy-mobile').find('li.level .node-card').should('have.length', 2); + }); + + it('edit node navigates to employee master', () => { + cy.get('@employee_records').then(employee_records => { + cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node') + .click(); + + cy.url().should('include', `/employee/${employee_records.message[0]}`); + }); + }); +}); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 5a6f1688767..bd7946a1e13 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -128,6 +128,9 @@ erpnext.HierarchyChartMobile = class { if (this.$hierarchy) this.$hierarchy.remove(); + if (this.$sibling_group) + this.$sibling_group.empty(); + this.$hierarchy = $( `
                                                  • @@ -173,7 +176,7 @@ erpnext.HierarchyChartMobile = class { if (this.$sibling_group) { const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent'); - if (node.parent_id !== sibling_parent) + if (node.parent_id !== undefined && node.parent_id != sibling_parent) this.$sibling_group.empty(); } @@ -376,9 +379,10 @@ erpnext.HierarchyChartMobile = class { let node_element = $(`#${node.id}`); node_element.click(function() { - let el = $(this).detach(); + let el = undefined; if (node.is_root) { + el = $(this).detach(); me.$hierarchy.empty(); $(`#connectors`).empty(); me.add_node_to_hierarchy(el, node); @@ -386,6 +390,7 @@ erpnext.HierarchyChartMobile = class { me.remove_levels_after_node(node); me.remove_orphaned_connectors(); } else { + el = $(this).detach(); me.add_node_to_hierarchy(el, node); me.collapse_node(); } @@ -514,10 +519,10 @@ erpnext.HierarchyChartMobile = class { level = $('.hierarchy-mobile > li:eq('+ level + ')'); level.nextAll('li').remove(); - let current_node = level.find(`#${node.id}`); let node_object = this.nodes[node.id]; - + let current_node = level.find(`#${node.id}`).detach(); current_node.removeClass('active-child active-path'); + node_object.expanded = 0; node_object.$children = undefined; diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py index 8e67b1cd346..99748dca02e 100644 --- a/erpnext/tests/ui_test_helpers.py +++ b/erpnext/tests/ui_test_helpers.py @@ -11,10 +11,11 @@ def create_employee_records(): emp2 = create_employee('Test Employee 2', 'CTO') emp3 = create_employee('Test Employee 3', 'Head of Marketing and Sales', emp1) emp4 = create_employee('Test Employee 4', 'Project Manager', emp2) - emp5 = create_employee('Test Employee 5', 'Analyst', emp3) - emp6 = create_employee('Test Employee 6', 'Software Developer', emp4) + emp5 = create_employee('Test Employee 5', 'Engineer', emp2) + emp6 = create_employee('Test Employee 6', 'Analyst', emp3) + emp7 = create_employee('Test Employee 7', 'Software Developer', emp4) - employees = [emp1, emp2, emp3, emp4, emp5, emp6] + employees = [emp1, emp2, emp3, emp4, emp5, emp6, emp7] return employees def create_company(): From 525d4d47761d8b635142d7f5847ca29cd24eca15 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 15 Jul 2021 19:32:15 +0530 Subject: [PATCH 303/369] fix: sider --- cypress/integration/test_organizational_chart_desktop.js | 4 ++-- cypress/integration/test_organizational_chart_mobile.js | 8 ++++---- erpnext/tests/ui_test_helpers.py | 3 +-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index d50d5513301..807ef5731a5 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -29,7 +29,7 @@ context('Organizational Chart', () => { // check children of first node cy.get('@employee_records').then(employee_records => { // children of 1st root visible - cy.get(`[data-parent="${employee_records.message[0]}"]`).as('child-node') + cy.get(`[data-parent="${employee_records.message[0]}"]`).as('child-node'); cy.get('@child-node') .should('have.length', 1) .should('be.visible'); @@ -39,7 +39,7 @@ context('Organizational Chart', () => { cy.get(`path[data-parent="${employee_records.message[0]}"]`) .should('be.visible') .invoke('attr', 'data-child') - .should('equal', employee_records.message[2]); + .should('equal', employee_records.message[2]); }); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 656051289f1..f48972bdc68 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -43,7 +43,7 @@ context('Organizational Chart Mobile', () => { // children of active root node cy.get('.hierarchy-mobile').find('.level').first().find('ul.node-children').children() - .should('have.length', 2) + .should('have.length', 2); cy.get(`[data-parent="${employee_records.message[1]}"]`).first().as('child-node'); cy.get('@child-node').should('be.visible'); @@ -56,7 +56,7 @@ context('Organizational Chart Mobile', () => { cy.get(`path[data-parent="${employee_records.message[1]}"]`).as('connectors'); cy.get('@connectors') .should('have.length', 2) - .should('be.visible') + .should('be.visible'); cy.get('@connectors') .first() @@ -104,7 +104,7 @@ context('Organizational Chart Mobile', () => { cy.get('@sibling_group') .should('have.attr', 'data-parent', 'undefined') .should('have.class', 'node-group') - .and('have.class', 'collapsed') + .and('have.class', 'collapsed'); cy.get('@sibling_group').get('.avatar-group').children().as('siblings'); cy.get('@siblings').should('have.length', 1); @@ -137,7 +137,7 @@ context('Organizational Chart Mobile', () => { it('expands sibling group', () => { cy.get('@employee_records').then(employee_records => { // sibling group visible for parent - cy.get(`#${employee_records.message[6]}`).click() + cy.get(`#${employee_records.message[6]}`).click(); cy.get(`#${employee_records.message[3]}`) .next() diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py index 99748dca02e..f66d69ba232 100644 --- a/erpnext/tests/ui_test_helpers.py +++ b/erpnext/tests/ui_test_helpers.py @@ -1,10 +1,9 @@ import frappe -from frappe import _ from frappe.utils import getdate @frappe.whitelist() def create_employee_records(): - company = create_company() + create_company() create_missing_designation() emp1 = create_employee('Test Employee 1', 'CEO') From 9dfddca22e31b35e1d4650cc75ac088f62000d1a Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 16 Jul 2021 10:32:38 +0530 Subject: [PATCH 304/369] ci(cypress): use env variable for key documentation ref: https://docs.cypress.io/guides/guides/command-line\#cypress-run --- .github/workflows/ui-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 9e29b6f1d2a..661525bedc1 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -102,7 +102,7 @@ jobs: - name: UI Tests run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless env: - CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + CYPRESS_RECORD_KEY: 60a8e3bf-08f5-45b1-9269-2b207d7d30cd - name: Show bench console if tests failed if: ${{ failure() }} From e126b370b01de0488da33d6fef72b58f8f3e466f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 16:26:17 +0530 Subject: [PATCH 305/369] fix(tests): clear filter before typing --- cypress/integration/test_organizational_chart_desktop.js | 4 ++++ cypress/integration/test_organizational_chart_mobile.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 807ef5731a5..ae8e75f219c 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -2,6 +2,10 @@ context('Organizational Chart', () => { before(() => { cy.login(); cy.visit('/app/website'); + cy.awesomebar('Organizational Chart'); + + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input').clear().type('Test Org Chart'); cy.visit(`app/organizational-chart`); cy.fill_field('company', 'Test Org Chart'); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index f48972bdc68..093e808da01 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -3,6 +3,10 @@ context('Organizational Chart Mobile', () => { cy.login(); cy.viewport(375, 667); cy.visit('/app/website'); + cy.awesomebar('Organizational Chart'); + + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input').clear().type('Test Org Chart'); cy.visit(`app/organizational-chart`); cy.wait(500); From e5406ece83229949b620823cb60d51aa416d0cb1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 17:03:17 +0530 Subject: [PATCH 306/369] fix(tests): apply filters correctly --- .../test_organizational_chart_desktop.js | 9 ++++--- .../test_organizational_chart_mobile.js | 24 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index ae8e75f219c..cb12eb5c0c4 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -5,16 +5,15 @@ context('Organizational Chart', () => { cy.awesomebar('Organizational Chart'); cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input').clear().type('Test Org Chart'); + cy.get('@input').clear().wait(200).type('Test Org Chart'); + cy.get('@input').type('{enter}', { delay: 100 }); + cy.get('@input').blur(); - cy.visit(`app/organizational-chart`); - cy.fill_field('company', 'Test Org Chart'); - cy.get('body').click(); cy.wait(500); }); beforeEach(() => { - cy.window().its('frappe').then(frappe => { + return cy.window().its('frappe').then(frappe => { return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); }).as('employee_records'); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 093e808da01..a1d3c0083ca 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -6,12 +6,10 @@ context('Organizational Chart Mobile', () => { cy.awesomebar('Organizational Chart'); cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input').clear().type('Test Org Chart'); + cy.get('@input').clear().wait(200).type('Test Org Chart'); + cy.get('@input').type('{enter}', { delay: 100 }); + cy.get('@input').blur(); - cy.visit(`app/organizational-chart`); - cy.wait(500); - cy.fill_field('company', 'Test Org Chart'); - cy.get('body').click(); cy.wait(500); }); @@ -19,7 +17,7 @@ context('Organizational Chart Mobile', () => { cy.viewport(375, 667); cy.wait(500); - cy.window().its('frappe').then(frappe => { + return cy.window().its('frappe').then(frappe => { return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); }).as('employee_records'); }); @@ -166,13 +164,15 @@ context('Organizational Chart Mobile', () => { }); it('goes to the respective level after clicking on non-collapsed sibling group', () => { - // click on non-collapsed sibling group - cy.get('.hierarchy-mobile') - .prev() - .click(); + cy.get('@employee_records').then(() => { + // click on non-collapsed sibling group + cy.get('.hierarchy-mobile') + .prev() + .click(); - // should take you to that level - cy.get('.hierarchy-mobile').find('li.level .node-card').should('have.length', 2); + // should take you to that level + cy.get('.hierarchy-mobile').find('li.level .node-card').should('have.length', 2); + }); }); it('edit node navigates to employee master', () => { From 2c7b500d1687f4b91f7f994e03139ca0407af558 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 17:34:15 +0530 Subject: [PATCH 307/369] fix: tests --- cypress/integration/test_organizational_chart_desktop.js | 6 +++--- cypress/integration/test_organizational_chart_mobile.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index cb12eb5c0c4..95592e2f6a8 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -4,10 +4,10 @@ context('Organizational Chart', () => { cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input').clear().wait(200).type('Test Org Chart'); + cy.get('.frappe-control[data-fieldname=company] input').first().focus().as('input'); + cy.get('@input').clear().wait(200).type('Test Org Chart', { force: true }); cy.get('@input').type('{enter}', { delay: 100 }); - cy.get('@input').blur(); + cy.get('@input').blur({ force: true }); cy.wait(500); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index a1d3c0083ca..632d15ba6c8 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -5,10 +5,10 @@ context('Organizational Chart Mobile', () => { cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input').clear().wait(200).type('Test Org Chart'); + cy.get('.frappe-control[data-fieldname=company] input').first().focus().as('input'); + cy.get('@input').clear().wait(200).type('Test Org Chart', { force: true }); cy.get('@input').type('{enter}', { delay: 100 }); - cy.get('@input').blur(); + cy.get('@input').blur({ force: true }); cy.wait(500); }); From 982a097d7ac3d324ddc43c567227e318ac071ee0 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 22:19:28 +0530 Subject: [PATCH 308/369] fix: tests --- .../test_organizational_chart_desktop.js | 31 +++++++--------- .../test_organizational_chart_mobile.js | 37 ++++++++----------- erpnext/tests/ui_test_helpers.py | 6 +++ 3 files changed, 34 insertions(+), 40 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 95592e2f6a8..0493732812f 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -1,21 +1,17 @@ context('Organizational Chart', () => { before(() => { cy.login(); - cy.visit('/app/website'); + cy.visit('/app'); + + cy.call('erpnext.tests.ui_test_helpers.create_employee_records'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').first().focus().as('input'); - cy.get('@input').clear().wait(200).type('Test Org Chart', { force: true }); - cy.get('@input').type('{enter}', { delay: 100 }); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart', { force: true }); + cy.wait(200); cy.get('@input').blur({ force: true }); - - cy.wait(500); - }); - - beforeEach(() => { - return cy.window().its('frappe').then(frappe => { - return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); - }).as('employee_records'); }); it('renders root nodes and loads children for the first expandable node', () => { @@ -29,8 +25,7 @@ context('Organizational Chart', () => { cy.get('@first-child').get('.node-info').find('.node-title').contains('CEO'); cy.get('@first-child').get('.node-info').find('.node-connections').contains('· 2 Connections'); - // check children of first node - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // children of 1st root visible cy.get(`[data-parent="${employee_records.message[0]}"]`).as('child-node'); cy.get('@child-node') @@ -47,7 +42,7 @@ context('Organizational Chart', () => { }); it('hides active nodes children and connectors on expanding sibling node', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // click sibling cy.get(`#${employee_records.message[1]}`) .click() @@ -60,7 +55,7 @@ context('Organizational Chart', () => { }); it('collapses previous level nodes and refreshes connectors on expanding child node', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // click child node cy.get(`#${employee_records.message[3]}`) .click() @@ -81,7 +76,7 @@ context('Organizational Chart', () => { }); it('expands previous level nodes', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[0]}`) .click() .should('have.class', 'active'); @@ -95,7 +90,7 @@ context('Organizational Chart', () => { }); it('edit node navigates to employee master', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node') .click(); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 632d15ba6c8..1dcfbcfeb17 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -2,24 +2,17 @@ context('Organizational Chart Mobile', () => { before(() => { cy.login(); cy.viewport(375, 667); - cy.visit('/app/website'); + cy.visit('/app'); + + cy.call('erpnext.tests.ui_test_helpers.create_employee_records'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').first().focus().as('input'); - cy.get('@input').clear().wait(200).type('Test Org Chart', { force: true }); - cy.get('@input').type('{enter}', { delay: 100 }); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart', { force: true }); + cy.wait(200); cy.get('@input').blur({ force: true }); - - cy.wait(500); - }); - - beforeEach(() => { - cy.viewport(375, 667); - cy.wait(500); - - return cy.window().its('frappe').then(frappe => { - return frappe.call('erpnext.tests.ui_test_helpers.create_employee_records'); - }).as('employee_records'); }); it('renders root nodes', () => { @@ -35,7 +28,7 @@ context('Organizational Chart Mobile', () => { }); it('expands root node', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[1]}`) .click() .should('have.class', 'active'); @@ -68,7 +61,7 @@ context('Organizational Chart Mobile', () => { }); it('expands child node', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[3]}`) .click() .should('have.class', 'active') @@ -97,7 +90,7 @@ context('Organizational Chart Mobile', () => { }); it('renders sibling group', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // sibling group visible for parent cy.get(`#${employee_records.message[1]}`) .next() @@ -118,7 +111,7 @@ context('Organizational Chart Mobile', () => { }); it('expands previous level nodes', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[6]}`) .click() .should('have.class', 'active'); @@ -137,7 +130,7 @@ context('Organizational Chart Mobile', () => { }); it('expands sibling group', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // sibling group visible for parent cy.get(`#${employee_records.message[6]}`).click(); @@ -164,7 +157,7 @@ context('Organizational Chart Mobile', () => { }); it('goes to the respective level after clicking on non-collapsed sibling group', () => { - cy.get('@employee_records').then(() => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(() => { // click on non-collapsed sibling group cy.get('.hierarchy-mobile') .prev() @@ -176,7 +169,7 @@ context('Organizational Chart Mobile', () => { }); it('edit node navigates to employee master', () => { - cy.get('@employee_records').then(employee_records => { + cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { cy.get(`#${employee_records.message[0]}`).find('.btn-edit-node') .click(); diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py index f66d69ba232..fc3aa298242 100644 --- a/erpnext/tests/ui_test_helpers.py +++ b/erpnext/tests/ui_test_helpers.py @@ -17,6 +17,12 @@ def create_employee_records(): employees = [emp1, emp2, emp3, emp4, emp5, emp6, emp7] return employees +@frappe.whitelist() +def get_employee_records(): + return frappe.db.get_list('Employee', filters={ + 'company': 'Test Org Chart' + }, pluck='name', order_by='name') + def create_company(): company = frappe.db.exists('Company', 'Test Org Chart') if not company: From 76d192f099e3fd1c88c4c13f299919ab2571b1b8 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 23:34:02 +0530 Subject: [PATCH 309/369] fix: tests --- .../test_organizational_chart_desktop.js | 19 +++++++++---------- .../test_organizational_chart_mobile.js | 19 +++++++++---------- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 0493732812f..52863f18e05 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -1,17 +1,16 @@ context('Organizational Chart', () => { before(() => { cy.login(); - cy.visit('/app'); - - cy.call('erpnext.tests.ui_test_helpers.create_employee_records'); + cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart', { force: true }); - cy.wait(200); - cy.get('@input').blur({ force: true }); + cy.call('erpnext.tests.ui_test_helpers.create_employee_records').then(() => { + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); + }); }); it('renders root nodes and loads children for the first expandable node', () => { @@ -27,7 +26,7 @@ context('Organizational Chart', () => { cy.call('erpnext.tests.ui_test_helpers.get_employee_records').then(employee_records => { // children of 1st root visible - cy.get(`[data-parent="${employee_records.message[0]}"]`).as('child-node'); + cy.get(`div[data-parent="${employee_records.message[0]}"]`).as('child-node'); cy.get('@child-node') .should('have.length', 1) .should('be.visible'); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 1dcfbcfeb17..2272a31046e 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -2,17 +2,16 @@ context('Organizational Chart Mobile', () => { before(() => { cy.login(); cy.viewport(375, 667); - cy.visit('/app'); - - cy.call('erpnext.tests.ui_test_helpers.create_employee_records'); + cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart', { force: true }); - cy.wait(200); - cy.get('@input').blur({ force: true }); + cy.call('erpnext.tests.ui_test_helpers.create_employee_records').then(() => { + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); + }); }); it('renders root nodes', () => { @@ -40,7 +39,7 @@ context('Organizational Chart Mobile', () => { cy.get('.hierarchy-mobile').find('.level').first().find('ul.node-children').children() .should('have.length', 2); - cy.get(`[data-parent="${employee_records.message[1]}"]`).first().as('child-node'); + cy.get(`div[data-parent="${employee_records.message[1]}"]`).first().as('child-node'); cy.get('@child-node').should('be.visible'); cy.get('@child-node') From d32da5507f5fe7ffc55a7baf13ba0ada57efa3eb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 20 Jul 2021 10:23:52 +0530 Subject: [PATCH 310/369] fix(test): increase timeout for record creation --- .../test_organizational_chart_desktop.js | 27 ++++++++++++++----- .../test_organizational_chart_mobile.js | 27 ++++++++++++++----- 2 files changed, 42 insertions(+), 12 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 52863f18e05..0da4e560a72 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -4,12 +4,27 @@ context('Organizational Chart', () => { cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.call('erpnext.tests.ui_test_helpers.create_employee_records').then(() => { - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart{enter}', { force: true }) - .blur({ force: true }); + cy.window().its('frappe.csrf_token').then(csrf_token => { + return cy.request({ + url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + }, + timeout: 60000 + }) + .then(res => { + expect(res.status).eq(200); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); + + cy.get('body').click(); + }); }); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 2272a31046e..0374678a1ad 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -5,12 +5,27 @@ context('Organizational Chart Mobile', () => { cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); - cy.call('erpnext.tests.ui_test_helpers.create_employee_records').then(() => { - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart{enter}', { force: true }) - .blur({ force: true }); + cy.window().its('frappe.csrf_token').then(csrf_token => { + return cy.request({ + url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + }, + timeout: 60000 + }) + .then(res => { + expect(res.status).eq(200); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); + + cy.get('body').click(); + }); }); }); From 9c63fcb01bd16d453a0af4b2baf13b7e51db109e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 20 Jul 2021 10:55:05 +0530 Subject: [PATCH 311/369] fix: sider --- .../test_organizational_chart_desktop.js | 3 +- .../test_organizational_chart_mobile.js | 36 +++++++++---------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 0da4e560a72..57b7f7dced2 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -14,8 +14,7 @@ context('Organizational Chart', () => { 'X-Frappe-CSRF-Token': csrf_token }, timeout: 60000 - }) - .then(res => { + }).then(res => { expect(res.status).eq(200); cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); cy.get('@input') diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 0374678a1ad..214229f6f6c 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -7,25 +7,25 @@ context('Organizational Chart Mobile', () => { cy.window().its('frappe.csrf_token').then(csrf_token => { return cy.request({ - url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - }, - timeout: 60000 - }) - .then(res => { - expect(res.status).eq(200); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart{enter}', { force: true }) - .blur({ force: true }); + url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + }, + timeout: 60000 + }) + .then(res => { + expect(res.status).eq(200); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); - cy.get('body').click(); - }); + cy.get('body').click(); + }); }); }); From 951b3a43132902f882774dbde9e9026bd6fcd241 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 20 Jul 2021 12:19:44 +0530 Subject: [PATCH 312/369] fix: sider --- .../test_organizational_chart_desktop.js | 2 -- .../test_organizational_chart_mobile.js | 35 +++++++++---------- 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 57b7f7dced2..fb46bbb4331 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -21,8 +21,6 @@ context('Organizational Chart', () => { .clear({ force: true }) .type('Test Org Chart{enter}', { force: true }) .blur({ force: true }); - - cy.get('body').click(); }); }); }); diff --git a/cypress/integration/test_organizational_chart_mobile.js b/cypress/integration/test_organizational_chart_mobile.js index 214229f6f6c..df90dbfa22f 100644 --- a/cypress/integration/test_organizational_chart_mobile.js +++ b/cypress/integration/test_organizational_chart_mobile.js @@ -7,25 +7,22 @@ context('Organizational Chart Mobile', () => { cy.window().its('frappe.csrf_token').then(csrf_token => { return cy.request({ - url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - 'X-Frappe-CSRF-Token': csrf_token - }, - timeout: 60000 - }) - .then(res => { - expect(res.status).eq(200); - cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); - cy.get('@input') - .clear({ force: true }) - .type('Test Org Chart{enter}', { force: true }) - .blur({ force: true }); - - cy.get('body').click(); - }); + url: `/api/method/erpnext.tests.ui_test_helpers.create_employee_records`, + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'X-Frappe-CSRF-Token': csrf_token + }, + timeout: 60000 + }).then(res => { + expect(res.status).eq(200); + cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') + .clear({ force: true }) + .type('Test Org Chart{enter}', { force: true }) + .blur({ force: true }); + }); }); }); From 6f799d17ce77b25575b65c71fdb2fd5bd843bf9e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 21 Jul 2021 23:19:47 +0530 Subject: [PATCH 313/369] feat: Expand All nodes option in Desktop view --- .../hierarchy_chart_desktop.js | 152 +++++++++++++++--- erpnext/public/scss/hierarchy_chart.scss | 1 + erpnext/utilities/hierarchy_chart.py | 29 ++++ 3 files changed, 159 insertions(+), 23 deletions(-) create mode 100644 erpnext/utilities/hierarchy_chart.py diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index fe4d17c2102..694c26567aa 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -36,7 +36,11 @@ erpnext.HierarchyChart = class { me.nodes[this.id] = this; me.make_node_element(this); - me.setup_node_click_action(this); + + if (!me.all_nodes_expanded) { + me.setup_node_click_action(this); + } + me.setup_edit_node_action(this); } }; @@ -60,8 +64,9 @@ erpnext.HierarchyChart = class { show() { frappe.breadcrumbs.add('HR'); - let me = this; + this.setup_actions(); if ($(`[data-fieldname="company"]`).length) return; + let me = this; let company = this.page.add_field({ fieldtype: 'Link', @@ -79,20 +84,9 @@ erpnext.HierarchyChart = class { // svg for connectors me.make_svg_markers(); - - if (me.$hierarchy) - me.$hierarchy.remove(); - - // setup hierarchy - me.$hierarchy = $( - `
                                                      -
                                                    • -
                                                        -
                                                      • -
                                                      `); - - me.page.main.append(me.$hierarchy); + me.setup_hierarchy() me.render_root_nodes(); + me.all_nodes_expanded = false; } } }); @@ -101,6 +95,42 @@ erpnext.HierarchyChart = class { $(`[data-fieldname="company"]`).trigger('change'); } + setup_actions() { + let me = this; + this.page.add_inner_button(__('Expand All'), function() { + me.load_children(me.root_node, true); + me.all_nodes_expanded = true; + + me.page.remove_inner_button(__('Expand All')); + me.page.add_inner_button(__('Collapse All'), function() { + me.setup_hierarchy(); + me.render_root_nodes(); + me.all_nodes_expanded = false; + + me.page.remove_inner_button(__('Collapse All')); + me.setup_actions(); + }); + }); + } + + setup_hierarchy() { + if (this.$hierarchy) + this.$hierarchy.remove(); + + $(`#connectors`).empty(); + + // setup hierarchy + this.$hierarchy = $( + `
                                                        +
                                                      • +
                                                          +
                                                        • +
                                                        `); + + this.page.main.append(this.$hierarchy); + this.nodes = {}; + } + make_svg_markers() { $('#arrows').remove(); @@ -126,7 +156,7 @@ erpnext.HierarchyChart = class { `); } - render_root_nodes() { + render_root_nodes(expanded_view=false) { let me = this; frappe.call({ @@ -156,7 +186,10 @@ erpnext.HierarchyChart = class { expand_node = node; }); - me.expand_node(expand_node); + if (!expanded_view) { + me.root_node = expand_node; + me.expand_node(expand_node); + } } }); } @@ -196,11 +229,20 @@ erpnext.HierarchyChart = class { $(`#${node.parent_id}`).addClass('active-path'); } - load_children(node) { - frappe.run_serially([ - () => this.get_child_nodes(node.id), - (child_nodes) => this.render_child_nodes(node, child_nodes) - ]); + load_children(node, deep=false) { + if (!deep) { + frappe.run_serially([ + () => this.get_child_nodes(node.id), + (child_nodes) => this.render_child_nodes(node, child_nodes) + ]); + } else { + frappe.run_serially([ + () => this.setup_hierarchy(), + () => this.render_root_nodes(true), + () => this.get_all_nodes(node.id, node.name), + (data_list) => this.render_children_of_all_nodes(data_list) + ]); + } } get_child_nodes(node_id) { @@ -247,6 +289,70 @@ erpnext.HierarchyChart = class { node.expanded = true; } + get_all_nodes(node_id, node_name) { + return new Promise(resolve => { + frappe.call({ + method: 'erpnext.utilities.hierarchy_chart.get_all_nodes', + args: { + method: this.method, + company: this.company, + parent: node_id, + parent_name: node_name + }, + callback: (r) => { + resolve(r.message); + } + }); + }); + } + + render_children_of_all_nodes(data_list) { + let entry = undefined; + let node = undefined; + + while(data_list.length) { + // to avoid overlapping connectors + entry = data_list.shift(); + node = this.nodes[entry.parent]; + if (node) { + this.render_child_nodes_for_expanded_view(node, entry.data); + } else { + data_list.push(entry); + } + } + } + + render_child_nodes_for_expanded_view(node, child_nodes) { + node.$children = $('
                                                          ') + + const last_level = this.$hierarchy.find('.level:last').index(); + const node_level = $(`#${node.id}`).parent().parent().parent().index(); + + if (last_level === node_level) { + this.$hierarchy.append(` +
                                                        • + `); + node.$children.appendTo(this.$hierarchy.find('.level:last')); + } else { + node.$children.appendTo(this.$hierarchy.find('.level:eq(' + (node_level + 1) + ')')); + } + + node.$children.hide().empty(); + + if (child_nodes) { + $.each(child_nodes, (_i, data) => { + this.add_node(node, data); + setTimeout(() => { + this.add_connector(node.id, data.id); + }, 250); + }); + } + + node.$children.show(); + $(`path[data-parent="${node.id}"]`).show(); + node.expanded = true; + } + add_node(node, data) { return new this.Node({ id: data.id, @@ -333,7 +439,7 @@ erpnext.HierarchyChart = class { path.setAttribute("class", "active-connector"); path.setAttribute("marker-start", "url(#arrowstart-active)"); path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if (parent.hasClass('active-path')) { + } else { path.setAttribute("class", "collapsed-connector"); path.setAttribute("marker-start", "url(#arrowstart-collapsed)"); path.setAttribute("marker-end", "url(#arrowhead-collapsed)"); diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index dd523c34439..1c2f9421faa 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -194,6 +194,7 @@ .level { margin-right: 8px; align-items: flex-start; + flex-direction: column; } #arrows { diff --git a/erpnext/utilities/hierarchy_chart.py b/erpnext/utilities/hierarchy_chart.py new file mode 100644 index 00000000000..9b0279351f2 --- /dev/null +++ b/erpnext/utilities/hierarchy_chart.py @@ -0,0 +1,29 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ + +@frappe.whitelist() +def get_all_nodes(parent, parent_name, method, company): + '''Recursively gets all data from nodes''' + method = frappe.get_attr(method) + + if not method in frappe.whitelisted: + frappe.throw(_('Not Permitted'), frappe.PermissionError) + + data = method(parent, company) + result = [dict(parent=parent, parent_name=parent_name, data=data)] + + nodes_to_expand = [{'id': d.get('id'), 'name': d.get('name')} for d in data if d.get('expandable')] + + while nodes_to_expand: + parent = nodes_to_expand.pop(0) + data = method(parent.get('id'), company) + result.append(dict(parent=parent.get('id'), parent_name=parent.get('name'), data=data)) + for d in data: + if d.get('expandable'): + nodes_to_expand.append({'id': d.get('id'), 'name': d.get('name')}) + + return result \ No newline at end of file From 58c31c72c37d79e8f424e371c0f868b38ed6125d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 20:23:20 +0530 Subject: [PATCH 314/369] feat: add html2canvas for easily exporting html to images using canvas --- .eslintrc | 1 + package.json | 3 ++- yarn.lock | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index cb45ce5f692..321af084ae9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -154,6 +154,7 @@ "before": true, "beforeEach": true, "onScan": true, + "html2canvas": true, "extend_cscript": true, "localforage": true, } diff --git a/package.json b/package.json index c9ee7a622c4..5bc1e56a210 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "snyk": "^1.518.0" }, "dependencies": { - "onscan.js": "^1.5.2" + "onscan.js": "^1.5.2", + "html2canvas": "^1.1.4" }, "scripts": { "snyk-protect": "snyk protect", diff --git a/yarn.lock b/yarn.lock index 242695c4b85..82e98215d1e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -688,6 +688,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= +base64-arraybuffer@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.2.0.tgz#4b944fac0191aa5907afe2d8c999ccc57ce80f45" + integrity sha512-7emyCsu1/xiBXgQZrscw/8KPRT44I4Yq9Pe6EGs3aPRTsWuggML1/1DTuZUuIaJPIm1FTDUVXl4x/yW8s0kQDQ== + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -997,6 +1002,13 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== +css-line-break@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-line-break/-/css-line-break-1.1.1.tgz#d5e9bdd297840099eb0503c7310fd34927a026ef" + integrity sha512-1feNVaM4Fyzdj4mKPIQNL2n70MmuYzAXZ1aytlROFX1JsOo070OsugwGjj7nl6jnDJWHDM8zRZswkmeYVWZJQA== + dependencies: + base64-arraybuffer "^0.2.0" + debug@^3.1.0, debug@^3.2.6: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" @@ -1472,6 +1484,13 @@ hosted-git-info@^3.0.4, hosted-git-info@^3.0.7: dependencies: lru-cache "^6.0.0" +html2canvas@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/html2canvas/-/html2canvas-1.1.4.tgz#53ae91cd26e9e9e623c56533cccb2e3f57c8124c" + integrity sha512-uHgQDwrXsRmFdnlOVFvHin9R7mdjjZvoBoXxicPR+NnucngkaLa5zIDW9fzMkiip0jSffyTyWedE8iVogYOeWg== + dependencies: + css-line-break "1.1.1" + http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" From 78f50a980947715d647985413b9a2811c8e6cd73 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 20:28:01 +0530 Subject: [PATCH 315/369] feat: Export chart option in desktop view --- .../hierarchy_chart_desktop.js | 97 +++++++++++++------ erpnext/public/scss/hierarchy_chart.scss | 10 +- erpnext/utilities/hierarchy_chart.py | 4 +- 3 files changed, 82 insertions(+), 29 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 694c26567aa..57d34d82252 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -1,3 +1,4 @@ +import html2canvas from 'html2canvas'; erpnext.HierarchyChart = class { /* Options: - doctype @@ -11,16 +12,20 @@ erpnext.HierarchyChart = class { this.method = method; this.doctype = doctype; + this.setup_page_style(); + this.page.main.addClass('frappe-card'); + + this.nodes = {}; + this.setup_node_class(); + } + + setup_page_style() { this.page.main.css({ 'min-height': '300px', 'max-height': '600px', 'overflow': 'auto', 'position': 'relative' }); - this.page.main.addClass('frappe-card'); - - this.nodes = {}; - this.setup_node_class(); } setup_node_class() { @@ -84,7 +89,7 @@ erpnext.HierarchyChart = class { // svg for connectors me.make_svg_markers(); - me.setup_hierarchy() + me.setup_hierarchy(); me.render_root_nodes(); me.all_nodes_expanded = false; } @@ -97,6 +102,10 @@ erpnext.HierarchyChart = class { setup_actions() { let me = this; + this.page.add_inner_button(__('Export'), function() { + me.export_chart(); + }); + this.page.add_inner_button(__('Expand All'), function() { me.load_children(me.root_node, true); me.all_nodes_expanded = true; @@ -113,6 +122,36 @@ erpnext.HierarchyChart = class { }); } + export_chart() { + this.page.main.css({ + 'min-height': '', + 'max-height': '', + 'overflow': 'visible', + 'position': 'fixed', + 'left': '0', + 'top': '0' + }); + + $('.node-card').addClass('exported'); + + html2canvas(document.querySelector('#hierarchy-chart-wrapper'), { + scrollY: -window.scrollY, + scrollX: 0 + }).then(function(canvas) { + // Export the canvas to its data URI representation + let dataURL = canvas.toDataURL('image/png'); + + // download the image + let a = document.createElement('a'); + a.href = dataURL; + a.download = 'hierarchy_chart'; + a.click(); + }); + + this.setup_page_style(); + $('.node-card').removeClass('exported'); + } + setup_hierarchy() { if (this.$hierarchy) this.$hierarchy.remove(); @@ -127,33 +166,37 @@ erpnext.HierarchyChart = class {
                                                        `); - this.page.main.append(this.$hierarchy); + this.page.main + .find('#hierarchy-chart-wrapper') + .append(this.$hierarchy); this.nodes = {}; } make_svg_markers() { $('#arrows').remove(); - this.page.main.prepend(` - - - - - - - - + this.page.main.append(` +
                                                        + + + + + + + + - - - - - - - - - - `); + + + + + + + + + + +
                                                        `); } render_root_nodes(expanded_view=false) { @@ -310,7 +353,7 @@ erpnext.HierarchyChart = class { let entry = undefined; let node = undefined; - while(data_list.length) { + while (data_list.length) { // to avoid overlapping connectors entry = data_list.shift(); node = this.nodes[entry.parent]; @@ -323,7 +366,7 @@ erpnext.HierarchyChart = class { } render_child_nodes_for_expanded_view(node, child_nodes) { - node.$children = $('
                                                          ') + node.$children = $('
                                                            '); const last_level = this.$hierarchy.find('.level:last').index(); const node_level = $(`#${node.id}`).parent().parent().parent().index(); diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 1c2f9421faa..44288fe1552 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -21,6 +21,10 @@ } } +.node-card.exported { + box-shadow: none +} + .node-image { width: 3.0rem; height: 3.0rem; @@ -178,9 +182,12 @@ } // horizontal hierarchy tree view +#hierarchy-chart-wrapper { + padding-top: 30px; +} + .hierarchy { display: flex; - padding-top: 30px; } .hierarchy li { @@ -200,6 +207,7 @@ #arrows { position: absolute; overflow: visible; + margin-top: -80px; } .active-connector { diff --git a/erpnext/utilities/hierarchy_chart.py b/erpnext/utilities/hierarchy_chart.py index 9b0279351f2..22d3f28faa7 100644 --- a/erpnext/utilities/hierarchy_chart.py +++ b/erpnext/utilities/hierarchy_chart.py @@ -3,14 +3,16 @@ from __future__ import unicode_literals import frappe +import os from frappe import _ +from frappe.utils.pdf import get_pdf @frappe.whitelist() def get_all_nodes(parent, parent_name, method, company): '''Recursively gets all data from nodes''' method = frappe.get_attr(method) - if not method in frappe.whitelisted: + if method not in frappe.whitelisted: frappe.throw(_('Not Permitted'), frappe.PermissionError) data = method(parent, company) From 5f0edca3f37c7d8fcc5b7cfe6a116f9d9379af8b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 20:39:51 +0530 Subject: [PATCH 316/369] fix(style): longer titles overflowing --- erpnext/public/scss/hierarchy_chart.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 44288fe1552..7f1077dbbd2 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -40,6 +40,10 @@ line-height: 1.35; } +.node-info { + width: 12.7rem; +} + .node-connections { font-size: 0.75rem; line-height: 1.35; From c1bb4eec9cf2e66f99f6ec6aa0e8f8b3b63f2a66 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 21:34:51 +0530 Subject: [PATCH 317/369] fix: remove unnecessary imports --- erpnext/utilities/hierarchy_chart.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/utilities/hierarchy_chart.py b/erpnext/utilities/hierarchy_chart.py index 22d3f28faa7..fb58a5d5867 100644 --- a/erpnext/utilities/hierarchy_chart.py +++ b/erpnext/utilities/hierarchy_chart.py @@ -3,9 +3,7 @@ from __future__ import unicode_literals import frappe -import os from frappe import _ -from frappe.utils.pdf import get_pdf @frappe.whitelist() def get_all_nodes(parent, parent_name, method, company): From f9afade7dc152b3419607f9fcd186af73417cf56 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 23:11:18 +0530 Subject: [PATCH 318/369] fix: test --- erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 57d34d82252..89fb8d57925 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -173,7 +173,7 @@ erpnext.HierarchyChart = class { } make_svg_markers() { - $('#arrows').remove(); + $('#hierarchy-chart-wrapper').remove(); this.page.main.append(`
                                                            From 4ee6e32d74da3c7277a16a5dc48d3af2728e5ecf Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Tue, 10 Aug 2021 13:14:11 +0530 Subject: [PATCH 319/369] test(fix): fixed test case --- .../doctype/coupon_code/test_coupon_code.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py index 622bd33e20a..5af12cde06d 100644 --- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py +++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py @@ -57,7 +57,7 @@ def test_create_test_data(): }) item_price.insert() # create test item pricing rule - if not frappe.db.exists("Pricing Rule","_Test Pricing Rule for _Test Item"): + if not frappe.db.exists("Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}): item_pricing_rule = frappe.get_doc({ "doctype": "Pricing Rule", "title": "_Test Pricing Rule for _Test Item", @@ -86,14 +86,15 @@ def test_create_test_data(): sales_partner.insert() # create test item coupon code if not frappe.db.exists("Coupon Code", "SAVE30"): + pricing_rule = frappe.db.get_value("Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}, ['name']) coupon_code = frappe.get_doc({ - "doctype": "Coupon Code", - "coupon_name":"SAVE30", - "coupon_code":"SAVE30", - "pricing_rule": "_Test Pricing Rule for _Test Item", - "valid_from": "2014-01-01", - "maximum_use":1, - "used":0 + "doctype": "Coupon Code", + "coupon_name":"SAVE30", + "coupon_code":"SAVE30", + "pricing_rule": pricing_rule, + "valid_from": "2014-01-01", + "maximum_use":1, + "used":0 }) coupon_code.insert() @@ -102,7 +103,7 @@ class TestCouponCode(unittest.TestCase): test_create_test_data() def tearDown(self): - frappe.set_user("Administrator") + frappe.set_user("Administrator") def test_sales_order_with_coupon_code(self): frappe.db.set_value("Coupon Code", "SAVE30", "used", 0) From 49b7c7575e9bc604fb9132929a63fb9b4bb1e484 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Tue, 10 Aug 2021 18:35:46 +0530 Subject: [PATCH 320/369] refactor: code cleanup --- .../promotional_scheme/promotional_scheme.py | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index f4ee1887c49..0fade84cfd1 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -25,12 +25,14 @@ product_discount_fields = ['free_item', 'free_qty', 'free_item_uom', class PromotionalScheme(Document): def validate(self): + if not self.selling and not self.buying: + frappe.throw(_("Atleast one of the Selling or Buying must be selected")) if not (self.price_discount_slabs or self.product_discount_slabs): frappe.throw(_("Price or product discount slabs are required")) def on_update(self): - data = frappe.get_all( + pricing_rules = frappe.get_all( 'Pricing Rule', fields = ["promotional_scheme_id", "name", "creation"], filters = { @@ -39,15 +41,15 @@ class PromotionalScheme(Document): }, order_by = 'creation asc', ) or {} - self.update_pricing_rules(data) + self.update_pricing_rules(pricing_rules) - def update_pricing_rules(self, data): + def update_pricing_rules(self, pricing_rules): rules = {} count = 0 names = [] - for d in data: - names.append(d.name) - rules[d.get('promotional_scheme_id')] = names + for rule in pricing_rules: + names.append(rule.name) + rules[rule.get('promotional_scheme_id')] = names docs = get_pricing_rules(self, rules) @@ -64,9 +66,9 @@ class PromotionalScheme(Document): frappe.msgprint(_("New {0} pricing rules are created").format(count)) def on_trash(self): - for d in frappe.get_all('Pricing Rule', + for rule in frappe.get_all('Pricing Rule', {'promotional_scheme': self.name}): - frappe.delete_doc('Pricing Rule', d.name) + frappe.delete_doc('Pricing Rule', rule.name) def get_pricing_rules(doc, rules = {}): new_doc = [] @@ -107,17 +109,20 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules = {}): new_doc.append(pr) else: - for i in range(len(args.get(applicable_for))) : - + applicable_for_values = args.get(applicable_for) or [] + for applicable_for_value in applicable_for_values: pr = frappe.new_doc("Pricing Rule") pr.title = doc.name temp_args = args.copy() - temp_args[applicable_for] = args[applicable_for][i] + temp_args[applicable_for] = applicable_for_value pr = set_args(temp_args, pr, doc, child_doc, discount_fields, d) new_doc.append(pr) return new_doc + + + def set_args(args, pr, doc, child_doc, discount_fields, child_doc_fields): pr.update(args) for field in (other_fields + discount_fields): From af9863146dea5c41b4eee4a0024d6cac1cacc2b6 Mon Sep 17 00:00:00 2001 From: Marica Date: Tue, 10 Aug 2021 18:49:07 +0530 Subject: [PATCH 321/369] fix: Grammar in Error message --- .../accounts/doctype/promotional_scheme/promotional_scheme.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py index 0fade84cfd1..3d7a891f333 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -26,7 +26,7 @@ product_discount_fields = ['free_item', 'free_qty', 'free_item_uom', class PromotionalScheme(Document): def validate(self): if not self.selling and not self.buying: - frappe.throw(_("Atleast one of the Selling or Buying must be selected")) + frappe.throw(_("Either 'Selling' or 'Buying' must be selected"), title=_("Mandatory")) if not (self.price_discount_slabs or self.product_discount_slabs): frappe.throw(_("Price or product discount slabs are required")) From 66784a16cb20088c870261cf2871d0c59f11f862 Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Tue, 10 Aug 2021 19:38:16 +0530 Subject: [PATCH 322/369] fix: Sales Return cancellation if linked with Payment Entry (#26883) --- .../purchase_invoice/purchase_invoice.py | 4 +- .../doctype/sales_invoice/sales_invoice.py | 42 ++++++++++++++++++- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index d7d9a3886a5..85df2258681 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -22,7 +22,7 @@ from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accoun from frappe.model.mapper import get_mapped_doc from six import iteritems from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\ - unlink_inter_company_doc + unlink_inter_company_doc, check_if_return_invoice_linked_with_payment_entry from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details from erpnext.accounts.deferred_revenue import validate_service_stop_date from erpnext.stock.doctype.purchase_receipt.purchase_receipt import get_item_account_wise_additional_cost @@ -988,6 +988,8 @@ class PurchaseInvoice(BuyingController): }, item=self)) def on_cancel(self): + check_if_return_invoice_linked_with_payment_entry(self) + super(PurchaseInvoice, self).on_cancel() self.check_on_hold_or_closed_status() diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index eba8ba830fc..ba1e729a46b 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -290,6 +290,8 @@ class SalesInvoice(SellingController): self.update_time_sheet(None) def on_cancel(self): + check_if_return_invoice_linked_with_payment_entry(self) + super(SalesInvoice, self).on_cancel() self.check_sales_order_on_hold_or_close("sales_order") @@ -971,7 +973,7 @@ class SalesInvoice(SellingController): def set_asset_status(self, asset): if self.is_return: asset.set_status() - else: + else: asset.set_status("Sold" if self.docstatus==1 else None) def make_loyalty_point_redemption_gle(self, gl_entries): @@ -1939,3 +1941,41 @@ def create_dunning(source_name, target_doc=None): } }, target_doc, set_missing_values) return doclist + +def check_if_return_invoice_linked_with_payment_entry(self): + # If a Return invoice is linked with payment entry along with other invoices, + # the cancellation of the Return causes allocated amount to be greater than paid + + if not frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'): + return + + payment_entries = [] + if self.is_return and self.return_against: + invoice = self.return_against + else: + invoice = self.name + + payment_entries = frappe.db.sql_list(""" + SELECT + t1.name + FROM + `tabPayment Entry` t1, `tabPayment Entry Reference` t2 + WHERE + t1.name = t2.parent + and t1.docstatus = 1 + and t2.reference_name = %s + and t2.allocated_amount < 0 + """, invoice) + + links_to_pe = [] + if payment_entries: + for payment in payment_entries: + payment_entry = frappe.get_doc("Payment Entry", payment) + if len(payment_entry.references) > 1: + links_to_pe.append(payment_entry.name) + if links_to_pe: + payment_entries_link = [get_link_to_form('Payment Entry', name, label=name) for name in links_to_pe] + message = _("Please cancel and amend the Payment Entry") + message += " " + ", ".join(payment_entries_link) + " " + message += _("to unallocate the amount of this Return Invoice before cancelling it.") + frappe.throw(message) From 1a39d1a3115605d7ccaf6285e7664197f1ff01a8 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Aug 2021 19:38:39 +0530 Subject: [PATCH 323/369] fix: cost center & account validation in Sales/Purchase Taxes and Charges (#26881) --- .../sales_taxes_and_charges_template.py | 4 +++- .../test_records.json | 8 +++++++ erpnext/controllers/accounts_controller.py | 21 +++++++++++++++++++ erpnext/public/js/controllers/accounts.js | 8 +++++++ erpnext/setup/doctype/company/company.py | 6 +++--- .../setup_wizard/operations/taxes_setup.py | 3 ++- 6 files changed, 45 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py index 52d19d54a8b..8f9eb6577b8 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.py @@ -6,7 +6,7 @@ import frappe from frappe import _ from frappe.utils import flt from frappe.model.document import Document -from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax +from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax, validate_cost_center, validate_account_head class SalesTaxesandChargesTemplate(Document): def validate(self): @@ -39,6 +39,8 @@ def valdiate_taxes_and_charges_template(doc): for tax in doc.get("taxes"): validate_taxes_and_charges(tax) + validate_account_head(tax, doc) + validate_cost_center(tax, doc) validate_inclusive_tax(tax, doc) def validate_disabled(doc): diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json index 2b737b98048..74db08d5b86 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_records.json @@ -8,6 +8,7 @@ "charge_type": "On Net Total", "description": "VAT", "doctype": "Sales Taxes and Charges", + "cost_center": "Main - _TC", "parentfield": "taxes", "rate": 6 }, @@ -16,6 +17,7 @@ "charge_type": "On Net Total", "description": "Service Tax", "doctype": "Sales Taxes and Charges", + "cost_center": "Main - _TC", "parentfield": "taxes", "rate": 6.36 } @@ -114,6 +116,7 @@ "charge_type": "On Net Total", "description": "VAT", "doctype": "Sales Taxes and Charges", + "cost_center": "Main - _TC", "parentfield": "taxes", "rate": 12 }, @@ -122,6 +125,7 @@ "charge_type": "On Net Total", "description": "Service Tax", "doctype": "Sales Taxes and Charges", + "cost_center": "Main - _TC", "parentfield": "taxes", "rate": 4 } @@ -137,6 +141,7 @@ "charge_type": "On Net Total", "description": "VAT", "doctype": "Sales Taxes and Charges", + "cost_center": "Main - _TC", "parentfield": "taxes", "rate": 12 }, @@ -145,6 +150,7 @@ "charge_type": "On Net Total", "description": "Service Tax", "doctype": "Sales Taxes and Charges", + "cost_center": "Main - _TC", "parentfield": "taxes", "rate": 4 } @@ -160,6 +166,7 @@ "charge_type": "On Net Total", "description": "VAT", "doctype": "Sales Taxes and Charges", + "cost_center": "Main - _TC", "parentfield": "taxes", "rate": 12 }, @@ -168,6 +175,7 @@ "charge_type": "On Net Total", "description": "Service Tax", "doctype": "Sales Taxes and Charges", + "cost_center": "Main - _TC", "parentfield": "taxes", "rate": 4 } diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d65efa24b4e..73d2411edef 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1437,6 +1437,27 @@ def validate_taxes_and_charges(tax): tax.rate = None +def validate_account_head(tax, doc): + company = frappe.get_cached_value('Account', + tax.account_head, 'company') + + if company != doc.company: + frappe.throw(_('Row {0}: Account {1} does not belong to Company {2}') + .format(tax.idx, frappe.bold(tax.account_head), frappe.bold(doc.company)), title=_('Invalid Account')) + + +def validate_cost_center(tax, doc): + if not tax.cost_center: + return + + company = frappe.get_cached_value('Cost Center', + tax.cost_center, 'company') + + if company != doc.company: + frappe.throw(_('Row {0}: Cost Center {1} does not belong to Company {2}') + .format(tax.idx, frappe.bold(tax.cost_center), frappe.bold(doc.company)), title=_('Invalid Cost Center')) + + def validate_inclusive_tax(tax, doc): def _on_previous_row_error(row_range): throw(_("To include tax in row {0} in Item rate, taxes in rows {1} must also be included").format(tax.idx, row_range)) diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 7b997a11530..84c717676c7 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -31,6 +31,14 @@ frappe.ui.form.on(cur_frm.doctype, { } } }); + frm.set_query("cost_center", "taxes", function(doc) { + return { + filters: { + "company": doc.company, + "is_group": 0 + } + }; + }); } }, validate: function(frm) { diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 8755125c810..95cbf5150cd 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -108,6 +108,9 @@ class Company(NestedSet): frappe.flags.country_change = True self.create_default_accounts() self.create_default_warehouses() + + if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}): + self.create_default_cost_center() if frappe.flags.country_change: install_country_fixtures(self.name, self.country) @@ -117,9 +120,6 @@ class Company(NestedSet): from erpnext.setup.setup_wizard.operations.install_fixtures import install_post_company_fixtures install_post_company_fixtures(frappe._dict({'company_name': self.name})) - if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}): - self.create_default_cost_center() - if not frappe.local.flags.ignore_chart_of_accounts: self.set_default_accounts() if self.default_cash_account: diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py index c7cc0005186..c69f3d0b3fa 100644 --- a/erpnext/setup/setup_wizard/operations/taxes_setup.py +++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py @@ -124,7 +124,8 @@ def make_taxes_and_charges_template(company_name, doctype, template): account_data = tax_row.get('account_head') tax_row_defaults = { 'category': 'Total', - 'charge_type': 'On Net Total' + 'charge_type': 'On Net Total', + 'cost_center': frappe.db.get_value('Company', company_name, 'cost_center') } if doctype == 'Purchase Taxes and Charges Template': From 52cd0072928f75dea68399c16b477cf2ad1d21eb Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 10 Aug 2021 20:13:28 +0530 Subject: [PATCH 324/369] fix: make bundled assets for hierarchy chart --- erpnext/hr/page/organizational_chart/organizational_chart.js | 2 +- erpnext/public/js/hierarchy-chart.bundle.js | 3 +++ erpnext/public/scss/erpnext.bundle.scss | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 erpnext/public/js/hierarchy-chart.bundle.js diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index a1388867687..08f2c94ad44 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -6,7 +6,7 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { }); $(wrapper).bind('show', () => { - frappe.require('/assets/js/hierarchy-chart.min.js', () => { + frappe.require('hierarchy-chart.bundle.js', () => { let organizational_chart = undefined; let method = 'erpnext.hr.page.organizational_chart.organizational_chart.get_children'; diff --git a/erpnext/public/js/hierarchy-chart.bundle.js b/erpnext/public/js/hierarchy-chart.bundle.js new file mode 100644 index 00000000000..26ab6d92b9d --- /dev/null +++ b/erpnext/public/js/hierarchy-chart.bundle.js @@ -0,0 +1,3 @@ +import "./hierarchy_chart/hierarchy_chart_desktop.js"; +import "./hierarchy_chart/hierarchy_chart_mobile.js"; +import "./templates/node_card.html"; \ No newline at end of file diff --git a/erpnext/public/scss/erpnext.bundle.scss b/erpnext/public/scss/erpnext.bundle.scss index d3313c7cee2..b68ddf52b29 100644 --- a/erpnext/public/scss/erpnext.bundle.scss +++ b/erpnext/public/scss/erpnext.bundle.scss @@ -1,3 +1,4 @@ @import "./erpnext"; @import "./call_popup"; @import "./point-of-sale"; +@import "./hierarchy_chart"; From a6aa6cd7d63eb426b986d995985985c2aae4f553 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Tue, 10 Aug 2021 20:32:15 +0530 Subject: [PATCH 325/369] fix: timesheet amount issue (#25993) * fix: timesheet amount issue * fix: timesheet detail rate conversion * fix: condition to check timesheet currency * fix: removing console statement --- .../doctype/sales_invoice/sales_invoice.js | 55 ++++++++++--------- .../sales_invoice_timesheet.json | 14 ++++- .../projects/doctype/timesheet/timesheet.json | 3 +- .../projects/doctype/timesheet/timesheet.py | 16 +++++- 4 files changed, 58 insertions(+), 30 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 56f11650ffa..568e7721a39 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -447,6 +447,15 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte this.frm.refresh_field("outstanding_amount"); this.frm.refresh_field("paid_amount"); this.frm.refresh_field("base_paid_amount"); + }, + + currency() { + this._super(); + $.each(cur_frm.doc.timesheets, function(i, d) { + let row = frappe.get_doc(d.doctype, d.name) + set_timesheet_detail_rate(row.doctype, row.name, cur_frm.doc.currency, row.timesheet_detail) + }); + calculate_total_billing_amount(cur_frm) } }); @@ -846,7 +855,8 @@ frappe.ui.form.on('Sales Invoice', { 'time_sheet': row.parent, 'billing_hours': row.billing_hours, 'billing_amount': flt(row.billing_amount) * flt(exchange_rate), - 'timesheet_detail': row.name + 'timesheet_detail': row.name, + 'project_name': row.project_name }); frm.refresh_field('timesheets'); calculate_total_billing_amount(frm); @@ -965,43 +975,34 @@ frappe.ui.form.on('Sales Invoice', { } }) -frappe.ui.form.on('Sales Invoice Timesheet', { - time_sheet: function(frm, cdt, cdn){ - var d = locals[cdt][cdn]; - if(d.time_sheet) { - frappe.call({ - method: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet_data", - args: { - 'name': d.time_sheet, - 'project': frm.doc.project || null - }, - callback: function(r, rt) { - if(r.message){ - let data = r.message; - frappe.model.set_value(cdt, cdn, "billing_hours", data.billing_hours); - frappe.model.set_value(cdt, cdn, "billing_amount", data.billing_amount); - frappe.model.set_value(cdt, cdn, "timesheet_detail", data.timesheet_detail); - calculate_total_billing_amount(frm) - } - } - }) - } - } -}) - var calculate_total_billing_amount = function(frm) { var doc = frm.doc; doc.total_billing_amount = 0.0 - if(doc.timesheets) { + if (doc.timesheets) { $.each(doc.timesheets, function(index, data){ - doc.total_billing_amount += data.billing_amount + doc.total_billing_amount += flt(data.billing_amount) }) } refresh_field('total_billing_amount') } +var set_timesheet_detail_rate = function(cdt, cdn, currency, timelog) { + frappe.call({ + method: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet_detail_rate", + args: { + timelog: timelog, + currency: currency + }, + callback: function(r) { + if (!r.exc && r.message) { + frappe.model.set_value(cdt, cdn, 'billing_amount', r.message); + } + } + }); +} + var select_loyalty_program = function(frm, loyalty_programs) { var dialog = new frappe.ui.Dialog({ title: __("Select Loyalty Program"), diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json index f069e8dd0b8..c90297328ee 100644 --- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json +++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json @@ -9,7 +9,9 @@ "description", "billing_hours", "billing_amount", + "column_break_5", "time_sheet", + "project_name", "timesheet_detail" ], "fields": [ @@ -61,11 +63,21 @@ "in_list_view": 1, "label": "Description", "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "project_name", + "fieldtype": "Data", + "label": "Project Name", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2021-05-20 22:33:57.234846", + "modified": "2021-06-08 14:43:02.748981", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Timesheet", diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json index 75f7478ed18..be6771e56f9 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.json +++ b/erpnext/projects/doctype/timesheet/timesheet.json @@ -310,6 +310,7 @@ "read_only": 1 }, { + "default": "1", "fieldname": "exchange_rate", "fieldtype": "Float", "label": "Exchange Rate" @@ -319,7 +320,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-18 16:10:08.249619", + "modified": "2021-06-09 12:08:53.930200", "modified_by": "Administrator", "module": "Projects", "name": "Timesheet", diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index ae38d4ca192..a0042eb7d1a 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -227,7 +227,8 @@ def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to return frappe.db.sql("""SELECT tsd.name as name, tsd.parent as parent, tsd.billing_hours as billing_hours, tsd.billing_amount as billing_amount, tsd.activity_type as activity_type, - tsd.description as description, ts.currency as currency + tsd.description as description, ts.currency as currency, + tsd.project_name as project_name FROM `tabTimesheet Detail` tsd INNER JOIN `tabTimesheet` ts ON ts.name = tsd.parent WHERE tsd.parenttype = 'Timesheet' @@ -235,6 +236,19 @@ def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to and tsd.is_billable = 1 and tsd.sales_invoice is null""".format(condition), {'project': project, 'parent': parent, 'from_time': from_time, 'to_time': to_time}, as_dict=1) +@frappe.whitelist() +def get_timesheet_detail_rate(timelog, currency): + timelog_detail = frappe.db.sql("""SELECT tsd.billing_amount as billing_amount, + ts.currency as currency FROM `tabTimesheet Detail` tsd + INNER JOIN `tabTimesheet` ts ON ts.name=tsd.parent + WHERE tsd.name = '{0}'""".format(timelog), as_dict = 1)[0] + + if timelog_detail.currency: + exchange_rate = get_exchange_rate(timelog_detail.currency, currency) + + return timelog_detail.billing_amount * exchange_rate + return timelog_detail.billing_amount + @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_timesheet(doctype, txt, searchfield, start, page_len, filters): From b7b111c3edd3dd42470b61061e866dfa1850d46c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Aug 2021 22:52:37 +0530 Subject: [PATCH 326/369] fix: unseting of payment if no pos profile found (#26884) (#26890) (cherry picked from commit b614834efedbef572e0567828f0d9d82e81331ee) Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com> --- erpnext/controllers/taxes_and_totals.py | 6 +----- erpnext/public/js/controllers/taxes_and_totals.js | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 099c7d43463..05edb2530c2 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -679,17 +679,13 @@ class calculate_taxes_and_totals(object): default_mode_of_payment = frappe.db.get_value('POS Payment Method', {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1) - self.doc.payments = [] - if default_mode_of_payment: + self.doc.payments = [] self.doc.append('payments', { 'mode_of_payment': default_mode_of_payment.mode_of_payment, 'amount': total_amount_to_pay, 'default': 1 }) - else: - self.doc.is_pos = 0 - self.doc.pos_profile = '' self.calculate_paid_amount() diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 53d5278bbf4..891ec6edc18 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -747,8 +747,6 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ this.frm.doc.payments.find(pay => { if (pay.default) { pay.amount = total_amount_to_pay; - } else { - pay.amount = 0.0 } }); this.frm.refresh_fields(); From 1167a9bf9481ddc806343c7a7b7c9d4ef93b1a7d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Aug 2021 23:18:23 +0530 Subject: [PATCH 327/369] fix: unsetting of payment if no pos profile found (#26891) * fix: unseting of payment if no pos profile found (#26884) (cherry picked from commit b614834efedbef572e0567828f0d9d82e81331ee) # Conflicts: # erpnext/public/js/controllers/taxes_and_totals.js * fix: conflicts * fix: conflicts * fix: conflicts * fix: conflicts Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com> Co-authored-by: Afshan --- erpnext/controllers/taxes_and_totals.py | 6 +----- erpnext/public/js/controllers/taxes_and_totals.js | 2 -- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 099c7d43463..05edb2530c2 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -679,17 +679,13 @@ class calculate_taxes_and_totals(object): default_mode_of_payment = frappe.db.get_value('POS Payment Method', {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1) - self.doc.payments = [] - if default_mode_of_payment: + self.doc.payments = [] self.doc.append('payments', { 'mode_of_payment': default_mode_of_payment.mode_of_payment, 'amount': total_amount_to_pay, 'default': 1 }) - else: - self.doc.is_pos = 0 - self.doc.pos_profile = '' self.calculate_paid_amount() diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 9d8fcb64f7f..90cb5559394 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -749,8 +749,6 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ this.frm.doc.payments.find(pay => { if (pay.default) { pay.amount = total_amount_to_pay; - } else { - pay.amount = 0.0; } }); this.frm.refresh_fields(); From 15cb248d9e43ed4fd71157b8d6e3f6a2ee182311 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 10 Aug 2021 23:44:08 +0530 Subject: [PATCH 328/369] fix(style): apply svg container margin only in desktop view --- erpnext/public/scss/hierarchy_chart.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 7f1077dbbd2..8a1ec4992b0 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -188,6 +188,10 @@ // horizontal hierarchy tree view #hierarchy-chart-wrapper { padding-top: 30px; + + #arrows { + margin-top: -80px; + } } .hierarchy { @@ -211,7 +215,6 @@ #arrows { position: absolute; overflow: visible; - margin-top: -80px; } .active-connector { From 9855bbb95e2dbcc4140753309abbee283db91a3b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 10 Aug 2021 23:49:56 +0530 Subject: [PATCH 329/369] fix(style): apply svg container margin only in desktop view (#26894) --- erpnext/public/scss/hierarchy_chart.scss | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 7f1077dbbd2..8a1ec4992b0 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -188,6 +188,10 @@ // horizontal hierarchy tree view #hierarchy-chart-wrapper { padding-top: 30px; + + #arrows { + margin-top: -80px; + } } .hierarchy { @@ -211,7 +215,6 @@ #arrows { position: absolute; overflow: visible; - margin-top: -80px; } .active-connector { From 99658ceb4e743c4758a306f70f5497a8818738e0 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 11 Aug 2021 14:19:07 +0530 Subject: [PATCH 330/369] fix: Nest .level class style under .hierarchy class - To avoid style overrides in list view --- erpnext/public/scss/hierarchy_chart.scss | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 8a1ec4992b0..a66d6474e0d 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -206,10 +206,12 @@ margin: 0px 0px 16px 0px; } -.level { - margin-right: 8px; - align-items: flex-start; - flex-direction: column; +.hierarchy, .hierarchy-mobile { + .level { + margin-right: 8px; + align-items: flex-start; + flex-direction: column; + } } #arrows { From 08e4026456a698e4a41e1919795a03366e0ae2cb Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 12 Aug 2021 10:31:01 +0530 Subject: [PATCH 331/369] fix: Stock Analytics Report must consider warehouse during calculation (#26908) * fix: Stock Analytics Report must consider warehouse during calculation * fix: Brand filter in Stock Analytics (cherry picked from commit 703b081172981833bcf9a1fc5c86517817c3ff32) --- .../report/stock_analytics/stock_analytics.py | 44 +++++++++++++++---- .../report/stock_balance/stock_balance.py | 3 ++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index 0cc8ca48aac..d44685060c7 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -114,14 +114,41 @@ def get_period(posting_date, filters): def get_periodic_data(entry, filters): + """Structured as: + Item 1 + - Balance (updated and carried forward): + - Warehouse A : bal_qty/value + - Warehouse B : bal_qty/value + - Jun 2021 (sum of warehouse quantities used in report) + - Warehouse A : bal_qty/value + - Warehouse B : bal_qty/value + - Jul 2021 (sum of warehouse quantities used in report) + - Warehouse A : bal_qty/value + - Warehouse B : bal_qty/value + Item 2 + - Balance (updated and carried forward): + - Warehouse A : bal_qty/value + - Warehouse B : bal_qty/value + - Jun 2021 (sum of warehouse quantities used in report) + - Warehouse A : bal_qty/value + - Warehouse B : bal_qty/value + - Jul 2021 (sum of warehouse quantities used in report) + - Warehouse A : bal_qty/value + - Warehouse B : bal_qty/value + """ periodic_data = {} for d in entry: period = get_period(d.posting_date, filters) bal_qty = 0 + # if period against item does not exist yet, instantiate it + # insert existing balance dict against period, and add/subtract to it + if periodic_data.get(d.item_code) and not periodic_data.get(d.item_code).get(period): + periodic_data[d.item_code][period] = periodic_data[d.item_code]['balance'] + if d.voucher_type == "Stock Reconciliation": - if periodic_data.get(d.item_code): - bal_qty = periodic_data[d.item_code]["balance"] + if periodic_data.get(d.item_code) and periodic_data.get(d.item_code).get('balance').get(d.warehouse): + bal_qty = periodic_data[d.item_code]['balance'][d.warehouse] qty_diff = d.qty_after_transaction - bal_qty else: @@ -132,12 +159,12 @@ def get_periodic_data(entry, filters): else: value = d.stock_value_difference - periodic_data.setdefault(d.item_code, {}).setdefault(period, 0.0) - periodic_data.setdefault(d.item_code, {}).setdefault("balance", 0.0) - - periodic_data[d.item_code]["balance"] += value - periodic_data[d.item_code][period] = periodic_data[d.item_code]["balance"] + # period-warehouse wise balance + periodic_data.setdefault(d.item_code, {}).setdefault('balance', {}).setdefault(d.warehouse, 0.0) + periodic_data.setdefault(d.item_code, {}).setdefault(period, {}).setdefault(d.warehouse, 0.0) + periodic_data[d.item_code]['balance'][d.warehouse] += value + periodic_data[d.item_code][period][d.warehouse] = periodic_data[d.item_code]['balance'][d.warehouse] return periodic_data @@ -160,7 +187,8 @@ def get_data(filters): total = 0 for dummy, end_date in ranges: period = get_period(end_date, filters) - amount = flt(periodic_data.get(item_data.name, {}).get(period)) + period_data = periodic_data.get(item_data.name, {}).get(period) + amount = sum(period_data.values()) if period_data else 0 row[scrub(period)] = amount total += amount row["total"] = total diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 9e56ad41306..fc3d719a780 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -235,12 +235,15 @@ def filter_items_with_no_transactions(iwb_map, float_precision): return iwb_map def get_items(filters): + "Get items based on item code, item group or brand." conditions = [] if filters.get("item_code"): conditions.append("item.name=%(item_code)s") else: if filters.get("item_group"): conditions.append(get_item_group_condition(filters.get("item_group"))) + if filters.get("brand"): # used in stock analytics report + conditions.append("item.brand=%(brand)s") items = [] if conditions: From f4b2f4aaf7823cdae0ed344035fc8835ba2d9909 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 12 Aug 2021 17:11:38 +0530 Subject: [PATCH 332/369] fix: ZeroDivisionError on creating e-invoice for credit note (#26918) --- erpnext/regional/india/e_invoice/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index e65442dbff4..2373512cca4 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -190,8 +190,10 @@ def get_item_list(invoice): item.description = sanitize_for_json(d.item_name) item.qty = abs(item.qty) - - item.unit_rate = abs(item.taxable_value / item.qty) + if flt(item.qty) != 0.0: + item.unit_rate = abs(item.taxable_value / item.qty) + else: + item.unit_rate = abs(item.taxable_value) item.gross_amount = abs(item.taxable_value) item.taxable_value = abs(item.taxable_value) item.discount_amount = 0 From 2e6899fbe439a6d194eedb3ef46adbdd9d2e4cfb Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 13 Aug 2021 15:37:45 +0530 Subject: [PATCH 333/369] fix: Copy previous balance dict object instead of assigning (#26942) - Due to plain assignment, dict mutation gave wrong monthly values (cherry picked from commit fe2a34f17197a2877356bcdf5d0bb6c46312ed33) --- erpnext/stock/report/stock_analytics/stock_analytics.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index d44685060c7..fde934b1339 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -144,7 +144,8 @@ def get_periodic_data(entry, filters): # if period against item does not exist yet, instantiate it # insert existing balance dict against period, and add/subtract to it if periodic_data.get(d.item_code) and not periodic_data.get(d.item_code).get(period): - periodic_data[d.item_code][period] = periodic_data[d.item_code]['balance'] + previous_balance = periodic_data[d.item_code]['balance'].copy() + periodic_data[d.item_code][period] = previous_balance if d.voucher_type == "Stock Reconciliation": if periodic_data.get(d.item_code) and periodic_data.get(d.item_code).get('balance').get(d.warehouse): From 15149165205876059346f9f3c6787a0e42fe9bd4 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Thu, 12 Aug 2021 21:12:52 +0530 Subject: [PATCH 334/369] fix: Nest `.level` class style under `.hierarchy` class (#26905) fix: Nest `.level` class style under `.hierarchy` class --- erpnext/public/scss/hierarchy_chart.scss | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 8a1ec4992b0..a66d6474e0d 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -206,10 +206,12 @@ margin: 0px 0px 16px 0px; } -.level { - margin-right: 8px; - align-items: flex-start; - flex-direction: column; +.hierarchy, .hierarchy-mobile { + .level { + margin-right: 8px; + align-items: flex-start; + flex-direction: column; + } } #arrows { From 4c612b7cb291b8318af595fa7efdbd7ee7e4f9f8 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 15 Aug 2021 21:32:39 +0530 Subject: [PATCH 335/369] fix: add z-index to filter to avoid svg wrapper overlapping --- erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 89fb8d57925..fd4365ad8ff 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -98,6 +98,7 @@ erpnext.HierarchyChart = class { company.refresh(); $(`[data-fieldname="company"]`).trigger('change'); + $(`[data-fieldname="company"] .link-field`).css('z-index', 2); } setup_actions() { From 0b4959966c04ee708ef8fb6d573bdeeeb3694741 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 16 Aug 2021 10:19:48 +0530 Subject: [PATCH 336/369] fix: expand all nodes not working when there are only 2 levels - added dom freeze while expanding all nodes and exporting --- .../organizational_chart.py | 17 +++++++++-------- .../hierarchy_chart/hierarchy_chart_desktop.js | 16 ++++++++++++---- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 1e03e3d06ad..29831982172 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -32,16 +32,17 @@ def get_children(parent=None, company=None, exclude_node=None): def get_connections(employee): num_connections = 0 - connections = frappe.get_list('Employee', filters=[ + nodes_to_expand = frappe.get_list('Employee', filters=[ ['reports_to', '=', employee] ]) - num_connections += len(connections) + num_connections += len(nodes_to_expand) - while connections: - for entry in connections: - connections = frappe.get_list('Employee', filters=[ - ['reports_to', '=', entry.name] - ]) - num_connections += len(connections) + while nodes_to_expand: + parent = nodes_to_expand.pop(0) + descendants = frappe.get_list('Employee', filters=[ + ['reports_to', '=', parent.name] + ]) + num_connections += len(descendants) + nodes_to_expand.extend(descendants) return num_connections \ No newline at end of file diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index fd4365ad8ff..da050abc6e4 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -103,6 +103,7 @@ erpnext.HierarchyChart = class { setup_actions() { let me = this; + this.page.clear_inner_toolbar(); this.page.add_inner_button(__('Export'), function() { me.export_chart(); }); @@ -124,6 +125,7 @@ erpnext.HierarchyChart = class { } export_chart() { + frappe.dom.freeze(__('Exporting...')); this.page.main.css({ 'min-height': '', 'max-height': '', @@ -147,6 +149,8 @@ erpnext.HierarchyChart = class { a.href = dataURL; a.download = 'hierarchy_chart'; a.click(); + }).finally(() => { + frappe.dom.unfreeze(); }); this.setup_page_style(); @@ -170,7 +174,9 @@ erpnext.HierarchyChart = class { this.page.main .find('#hierarchy-chart-wrapper') .append(this.$hierarchy); + this.nodes = {}; + this.all_nodes_expanded = false; } make_svg_markers() { @@ -203,7 +209,7 @@ erpnext.HierarchyChart = class { render_root_nodes(expanded_view=false) { let me = this; - frappe.call({ + return frappe.call({ method: me.method, args: { company: me.company @@ -230,8 +236,8 @@ erpnext.HierarchyChart = class { expand_node = node; }); + me.root_node = expand_node; if (!expanded_view) { - me.root_node = expand_node; me.expand_node(expand_node); } } @@ -281,10 +287,12 @@ erpnext.HierarchyChart = class { ]); } else { frappe.run_serially([ + () => frappe.dom.freeze(), () => this.setup_hierarchy(), () => this.render_root_nodes(true), () => this.get_all_nodes(node.id, node.name), - (data_list) => this.render_children_of_all_nodes(data_list) + (data_list) => this.render_children_of_all_nodes(data_list), + () => frappe.dom.unfreeze() ]); } } @@ -360,7 +368,7 @@ erpnext.HierarchyChart = class { node = this.nodes[entry.parent]; if (node) { this.render_child_nodes_for_expanded_view(node, entry.data); - } else { + } else if (data_list.length) { data_list.push(entry); } } From 67e3971c3bb80a37a03da320172e7df1f17dd18b Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 16 Aug 2021 10:38:39 +0530 Subject: [PATCH 337/369] fix: Org Chart fixes (#26952) * fix: add z-index to filter to avoid svg wrapper overlapping * fix: expand all nodes not working when there are only 2 levels - added dom freeze while expanding all nodes and exporting --- .../organizational_chart.py | 17 +++++++++-------- .../hierarchy_chart/hierarchy_chart_desktop.js | 17 +++++++++++++---- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 1e03e3d06ad..29831982172 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -32,16 +32,17 @@ def get_children(parent=None, company=None, exclude_node=None): def get_connections(employee): num_connections = 0 - connections = frappe.get_list('Employee', filters=[ + nodes_to_expand = frappe.get_list('Employee', filters=[ ['reports_to', '=', employee] ]) - num_connections += len(connections) + num_connections += len(nodes_to_expand) - while connections: - for entry in connections: - connections = frappe.get_list('Employee', filters=[ - ['reports_to', '=', entry.name] - ]) - num_connections += len(connections) + while nodes_to_expand: + parent = nodes_to_expand.pop(0) + descendants = frappe.get_list('Employee', filters=[ + ['reports_to', '=', parent.name] + ]) + num_connections += len(descendants) + nodes_to_expand.extend(descendants) return num_connections \ No newline at end of file diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 89fb8d57925..da050abc6e4 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -98,10 +98,12 @@ erpnext.HierarchyChart = class { company.refresh(); $(`[data-fieldname="company"]`).trigger('change'); + $(`[data-fieldname="company"] .link-field`).css('z-index', 2); } setup_actions() { let me = this; + this.page.clear_inner_toolbar(); this.page.add_inner_button(__('Export'), function() { me.export_chart(); }); @@ -123,6 +125,7 @@ erpnext.HierarchyChart = class { } export_chart() { + frappe.dom.freeze(__('Exporting...')); this.page.main.css({ 'min-height': '', 'max-height': '', @@ -146,6 +149,8 @@ erpnext.HierarchyChart = class { a.href = dataURL; a.download = 'hierarchy_chart'; a.click(); + }).finally(() => { + frappe.dom.unfreeze(); }); this.setup_page_style(); @@ -169,7 +174,9 @@ erpnext.HierarchyChart = class { this.page.main .find('#hierarchy-chart-wrapper') .append(this.$hierarchy); + this.nodes = {}; + this.all_nodes_expanded = false; } make_svg_markers() { @@ -202,7 +209,7 @@ erpnext.HierarchyChart = class { render_root_nodes(expanded_view=false) { let me = this; - frappe.call({ + return frappe.call({ method: me.method, args: { company: me.company @@ -229,8 +236,8 @@ erpnext.HierarchyChart = class { expand_node = node; }); + me.root_node = expand_node; if (!expanded_view) { - me.root_node = expand_node; me.expand_node(expand_node); } } @@ -280,10 +287,12 @@ erpnext.HierarchyChart = class { ]); } else { frappe.run_serially([ + () => frappe.dom.freeze(), () => this.setup_hierarchy(), () => this.render_root_nodes(true), () => this.get_all_nodes(node.id, node.name), - (data_list) => this.render_children_of_all_nodes(data_list) + (data_list) => this.render_children_of_all_nodes(data_list), + () => frappe.dom.unfreeze() ]); } } @@ -359,7 +368,7 @@ erpnext.HierarchyChart = class { node = this.nodes[entry.parent]; if (node) { this.render_child_nodes_for_expanded_view(node, entry.data); - } else { + } else if (data_list.length) { data_list.push(entry); } } From b58b1127f3776735484a41d4884387397e78533d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Aug 2021 13:18:39 +0530 Subject: [PATCH 338/369] fix: Add mandatory depends on condition for export type field --- erpnext/patches.txt | 2 +- erpnext/patches/v13_0/update_export_type_for_gst.py | 12 ++++++++++-- erpnext/regional/india/setup.py | 6 ++++-- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index b891719b02d..fec727d289c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -293,4 +293,4 @@ erpnext.patches.v13_0.update_job_card_details erpnext.patches.v13_0.update_level_in_bom #1234sswef erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry erpnext.patches.v13_0.update_subscription_status_in_memberships -erpnext.patches.v13_0.update_export_type_for_gst +erpnext.patches.v13_0.update_export_type_for_gst #2021-08-16 diff --git a/erpnext/patches/v13_0/update_export_type_for_gst.py b/erpnext/patches/v13_0/update_export_type_for_gst.py index 478a2a6c806..3e20212af6d 100644 --- a/erpnext/patches/v13_0/update_export_type_for_gst.py +++ b/erpnext/patches/v13_0/update_export_type_for_gst.py @@ -8,11 +8,19 @@ def execute(): # Update custom fields fieldname = frappe.db.get_value('Custom Field', {'dt': 'Customer', 'fieldname': 'export_type'}) if fieldname: - frappe.db.set_value('Custom Field', fieldname, 'default', '') + frappe.db.set_value('Custom Field', fieldname, + { + 'default': '', + 'mandatory_depends_on': 'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)' + }) fieldname = frappe.db.get_value('Custom Field', {'dt': 'Supplier', 'fieldname': 'export_type'}) if fieldname: - frappe.db.set_value('Custom Field', fieldname, 'default', '') + frappe.db.set_value('Custom Field', fieldname, + { + 'default': '', + 'mandatory_depends_on': 'eval:in_list(["SEZ", "Overseas"], doc.gst_category)' + }) # Update Customer/Supplier Masters frappe.db.sql(""" diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index e9372f9b8fc..37c714d2162 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -642,7 +642,8 @@ def make_custom_fields(update=True): 'fieldtype': 'Select', 'insert_after': 'gst_category', 'depends_on':'eval:in_list(["SEZ", "Overseas"], doc.gst_category)', - 'options': '\nWith Payment of Tax\nWithout Payment of Tax' + 'options': '\nWith Payment of Tax\nWithout Payment of Tax', + 'mandatory_depends_on': 'eval:in_list(["SEZ", "Overseas"], doc.gst_category)' } ], 'Customer': [ @@ -660,7 +661,8 @@ def make_custom_fields(update=True): 'fieldtype': 'Select', 'insert_after': 'gst_category', 'depends_on':'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)', - 'options': '\nWith Payment of Tax\nWithout Payment of Tax' + 'options': '\nWith Payment of Tax\nWithout Payment of Tax', + 'mandatory_depends_on': 'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)' } ], 'Member': [ From 8410e4854ff90f68c2e5d41072aa0665d7482edb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Aug 2021 17:14:40 +0530 Subject: [PATCH 339/369] fix: Budget variance missing values --- .../report/budget_variance_report/budget_variance_report.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index f1b231b6901..9f0eee8aa5c 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -38,8 +38,8 @@ def execute(filters=None): GROUP BY parent''',{'dimension':[dimension]}) if DCC_allocation: filters['budget_against_filter'] = [DCC_allocation[0][0]] - cam_map = get_dimension_account_month_map(filters) - dimension_items = cam_map.get(DCC_allocation[0][0]) + ddc_cam_map = get_dimension_account_month_map(filters) + dimension_items = ddc_cam_map.get(DCC_allocation[0][0]) if dimension_items: data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation[0][1]) @@ -48,7 +48,6 @@ def execute(filters=None): return columns, data, None, chart def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation): - for account, monthwise_data in iteritems(dimension_items): row = [dimension, account] totals = [0, 0, 0] From bf75ea70fba1925cbfade53ae2e9d36d8b32ae88 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 16 Aug 2021 17:22:51 +0530 Subject: [PATCH 340/369] feat: Training Event Status Update and Validations (#26698) * fix: training event employee status not updated on feedback submission * feat: update attendees status on training event status update * test: Training Event and Feedback * chore: remove unused import --- .../training_event/test_training_event.py | 57 +++++++++++------ .../doctype/training_event/training_event.py | 17 ++++- .../test_training_feedback.py | 63 +++++++++++++++++-- .../training_feedback/training_feedback.py | 38 ++++++++--- 4 files changed, 142 insertions(+), 33 deletions(-) diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py index 313f90eba85..9b32136bfb6 100644 --- a/erpnext/hr/doctype/training_event/test_training_event.py +++ b/erpnext/hr/doctype/training_event/test_training_event.py @@ -11,21 +11,34 @@ from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_ class TestTrainingEvent(unittest.TestCase): def setUp(self): create_training_program("Basic Training") - self.employee = make_employee("robert_loan@trainig.com") - self.employee2 = make_employee("suzie.tan@trainig.com") + employee = make_employee("robert_loan@trainig.com") + employee2 = make_employee("suzie.tan@trainig.com") + self.attendees = [ + {"employee": employee}, + {"employee": employee2} + ] + + def test_training_event_status_update(self): + training_event = create_training_event(self.attendees) + training_event.submit() + + training_event.event_status = "Completed" + training_event.save() + training_event.reload() + + for entry in training_event.employees: + self.assertEqual(entry.status, "Completed") + + training_event.event_status = "Scheduled" + training_event.save() + training_event.reload() + + for entry in training_event.employees: + self.assertEqual(entry.status, "Open") + + def tearDown(self): + frappe.db.rollback() - def test_create_training_event(self): - if not frappe.db.get_value("Training Event", "Basic Training Event"): - frappe.get_doc({ - "doctype": "Training Event", - "event_name": "Basic Training Event", - "training_program": "Basic Training", - "location": "Union Square", - "start_time": add_days(today(), 5), - "end_time": add_days(today(), 6), - "introduction": "Welcome to the Basic Training Event", - "employees": get_attendees(self.employee, self.employee2) - }).insert() def create_training_program(training_program): if not frappe.db.get_value("Training Program", training_program): @@ -35,8 +48,14 @@ def create_training_program(training_program): "description": training_program }).insert() -def get_attendees(employee, employee2): - return [ - {"employee": employee}, - {"employee": employee2} - ] \ No newline at end of file +def create_training_event(attendees): + return frappe.get_doc({ + "doctype": "Training Event", + "event_name": "Basic Training Event", + "training_program": "Basic Training", + "location": "Union Square", + "start_time": add_days(today(), 5), + "end_time": add_days(today(), 6), + "introduction": "Welcome to the Basic Training Event", + "employees": attendees + }).insert() \ No newline at end of file diff --git a/erpnext/hr/doctype/training_event/training_event.py b/erpnext/hr/doctype/training_event/training_event.py index 5064f033081..e2c30cb3145 100644 --- a/erpnext/hr/doctype/training_event/training_event.py +++ b/erpnext/hr/doctype/training_event/training_event.py @@ -14,10 +14,25 @@ class TrainingEvent(Document): self.set_employee_emails() self.validate_period() + def on_update_after_submit(self): + self.set_status_for_attendees() + def set_employee_emails(self): self.employee_emails = ', '.join(get_employee_emails([d.employee for d in self.employees])) def validate_period(self): if time_diff_in_seconds(self.end_time, self.start_time) <= 0: - frappe.throw(_('End time cannot be before start time')) \ No newline at end of file + frappe.throw(_('End time cannot be before start time')) + + def set_status_for_attendees(self): + if self.event_status == 'Completed': + for employee in self.employees: + if employee.attendance == 'Present' and employee.status != 'Feedback Submitted': + employee.status = 'Completed' + + elif self.event_status == 'Scheduled': + for employee in self.employees: + employee.status = 'Open' + + self.db_update_all() diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.py b/erpnext/hr/doctype/training_feedback/test_training_feedback.py index 34559982a24..c30a3ad34cd 100644 --- a/erpnext/hr/doctype/training_feedback/test_training_feedback.py +++ b/erpnext/hr/doctype/training_feedback/test_training_feedback.py @@ -5,8 +5,63 @@ from __future__ import unicode_literals import frappe import unittest - -# test_records = frappe.get_test_records('Training Feedback') - +from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_employee +from erpnext.hr.doctype.training_event.test_training_event import create_training_program, create_training_event class TestTrainingFeedback(unittest.TestCase): - pass + def setUp(self): + create_training_program("Basic Training") + self.employee = make_employee("robert_loan@trainig.com") + self.employee2 = make_employee("suzie.tan@trainig.com") + self.attendees = [{"employee": self.employee}] + + def test_employee_validations_for_feedback(self): + training_event = create_training_event(self.attendees) + training_event.submit() + + training_event.event_status = "Completed" + training_event.save() + training_event.reload() + + # should not allow creating feedback since employee2 was not part of the event + feedback = create_training_feedback(training_event.name, self.employee2) + self.assertRaises(frappe.ValidationError, feedback.save) + + # cannot record feedback for absent employee + employee = frappe.db.get_value("Training Event Employee", { + "parent": training_event.name, + "employee": self.employee + }, "name") + + frappe.db.set_value("Training Event Employee", employee, "attendance", "Absent") + feedback = create_training_feedback(training_event.name, self.employee) + self.assertRaises(frappe.ValidationError, feedback.save) + + def test_training_feedback_status(self): + training_event = create_training_event(self.attendees) + training_event.submit() + + training_event.event_status = "Completed" + training_event.save() + training_event.reload() + + feedback = create_training_feedback(training_event.name, self.employee) + feedback.submit() + + status = frappe.db.get_value("Training Event Employee", { + "parent": training_event.name, + "employee": self.employee + }, "status") + + self.assertEqual(status, "Feedback Submitted") + + def tearDown(self): + frappe.db.rollback() + + +def create_training_feedback(event, employee): + return frappe.get_doc({ + "doctype": "Training Feedback", + "training_event": event, + "employee": employee, + "feedback": "Test" + }) \ No newline at end of file diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.py b/erpnext/hr/doctype/training_feedback/training_feedback.py index 1a334507917..0d32de793c4 100644 --- a/erpnext/hr/doctype/training_feedback/training_feedback.py +++ b/erpnext/hr/doctype/training_feedback/training_feedback.py @@ -11,15 +11,35 @@ class TrainingFeedback(Document): def validate(self): training_event = frappe.get_doc("Training Event", self.training_event) if training_event.docstatus != 1: - frappe.throw(_('{0} must be submitted').format(_('Training Event'))) + frappe.throw(_("{0} must be submitted").format(_("Training Event"))) + + emp_event_details = frappe.db.get_value("Training Event Employee", { + "parent": self.training_event, + "employee": self.employee + }, ["name", "attendance"], as_dict=True) + + if not emp_event_details: + frappe.throw(_("Employee {0} not found in Training Event Participants.").format( + frappe.bold(self.employee_name))) + + if emp_event_details.attendance == "Absent": + frappe.throw(_("Feedback cannot be recorded for an absent Employee.")) def on_submit(self): - training_event = frappe.get_doc("Training Event", self.training_event) - event_status = None - for e in training_event.employees: - if e.employee == self.employee: - event_status = 'Feedback Submitted' - break + employee = frappe.db.get_value("Training Event Employee", { + "parent": self.training_event, + "employee": self.employee + }) + + if employee: + frappe.db.set_value("Training Event Employee", employee, "status", "Feedback Submitted") + + def on_cancel(self): + employee = frappe.db.get_value("Training Event Employee", { + "parent": self.training_event, + "employee": self.employee + }) + + if employee: + frappe.db.set_value("Training Event Employee", employee, "status", "Completed") - if event_status: - frappe.db.set_value("Training Event", self.training_event, "event_status", event_status) From 2a43fe1a22f993ed4eca779ce268064e56fe5246 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 16 Aug 2021 18:03:21 +0530 Subject: [PATCH 341/369] ci: ignore backports while checking docs (#26962) [skip ci] --- .github/helper/documentation.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/helper/documentation.py b/.github/helper/documentation.py index 9cc4663c394..b4a4ba1bbdd 100644 --- a/.github/helper/documentation.py +++ b/.github/helper/documentation.py @@ -32,11 +32,15 @@ if __name__ == "__main__": if response.ok: payload = response.json() - title = payload.get("title", "").lower() + title = payload.get("title", "").lower().strip() head_sha = payload.get("head", {}).get("sha") body = payload.get("body", "").lower() - if title.startswith("feat") and head_sha and "no-docs" not in body: + if (title.startswith("feat") + and head_sha + and "no-docs" not in body + and "backport" not in body + ): if docs_link_exists(body): print("Documentation Link Found. You're Awesome! 🎉") From 58c1739eacf18bd7866c6bf852375624aa985f1a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Aug 2021 17:14:40 +0530 Subject: [PATCH 342/369] fix: Budget variance missing values --- .../report/budget_variance_report/budget_variance_report.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index f1b231b6901..9f0eee8aa5c 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -38,8 +38,8 @@ def execute(filters=None): GROUP BY parent''',{'dimension':[dimension]}) if DCC_allocation: filters['budget_against_filter'] = [DCC_allocation[0][0]] - cam_map = get_dimension_account_month_map(filters) - dimension_items = cam_map.get(DCC_allocation[0][0]) + ddc_cam_map = get_dimension_account_month_map(filters) + dimension_items = ddc_cam_map.get(DCC_allocation[0][0]) if dimension_items: data = get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation[0][1]) @@ -48,7 +48,6 @@ def execute(filters=None): return columns, data, None, chart def get_final_data(dimension, dimension_items, filters, period_month_ranges, data, DCC_allocation): - for account, monthwise_data in iteritems(dimension_items): row = [dimension, account] totals = [0, 0, 0] From 33032ce07d7857dc90b8c66db1744481601664d0 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 16 Aug 2021 19:27:29 +0530 Subject: [PATCH 343/369] fix: test --- cypress/integration/test_organizational_chart_desktop.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index fb46bbb4331..5dac15f218b 100644 --- a/cypress/integration/test_organizational_chart_desktop.js +++ b/cypress/integration/test_organizational_chart_desktop.js @@ -3,6 +3,8 @@ context('Organizational Chart', () => { cy.login(); cy.visit('/app/website'); cy.awesomebar('Organizational Chart'); + cy.wait(500); + cy.url().should('include', '/organizational-chart'); cy.window().its('frappe.csrf_token').then(csrf_token => { return cy.request({ @@ -17,6 +19,7 @@ context('Organizational Chart', () => { }).then(res => { expect(res.status).eq(200); cy.get('.frappe-control[data-fieldname=company] input').focus().as('input'); + cy.get('@input') .clear({ force: true }) .type('Test Org Chart{enter}', { force: true }) From 97226418474394e982750d82b522edd7ed124351 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 16 Aug 2021 20:36:04 +0530 Subject: [PATCH 344/369] chore: Release Notes v13.9.0 --- erpnext/change_log/v13/v13_9_0.md | 46 +++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 erpnext/change_log/v13/v13_9_0.md diff --git a/erpnext/change_log/v13/v13_9_0.md b/erpnext/change_log/v13/v13_9_0.md new file mode 100644 index 00000000000..e52766673ce --- /dev/null +++ b/erpnext/change_log/v13/v13_9_0.md @@ -0,0 +1,46 @@ +# Version 13.9.0 Release Notes + +### Features & Enhancements +- Organizational Chart ([#26261](https://github.com/frappe/erpnext/pull/26261)) +- Enable discount accounting ([#26579](https://github.com/frappe/erpnext/pull/26579)) +- Added multi-select fields in promotional scheme to create multiple pricing rules ([#25622](https://github.com/frappe/erpnext/pull/25622)) +- Over transfer allowance for material transfers ([#26814](https://github.com/frappe/erpnext/pull/26814)) +- Enhancements in Tax Withholding Category ([#26661](https://github.com/frappe/erpnext/pull/26661)) + +### Fixes +- Sales Return cancellation if linked with Payment Entry ([#26883](https://github.com/frappe/erpnext/pull/26883)) +- Production plan not fetching sales order of a variant ([#25845](https://github.com/frappe/erpnext/pull/25845)) +- Stock Analytics Report must consider warehouse during calculation ([#26908](https://github.com/frappe/erpnext/pull/26908)) +- Incorrect date difference calculation ([#26805](https://github.com/frappe/erpnext/pull/26805)) +- Tax calculation for Recurring additional salary ([#24206](https://github.com/frappe/erpnext/pull/24206)) +- Cannot cancel payment entry if linked with invoices ([#26703](https://github.com/frappe/erpnext/pull/26703)) +- Included company in link document type filters for contact ([#26576](https://github.com/frappe/erpnext/pull/26576)) +- Fetch Payment Terms from linked Sales/Purchase Order ([#26723](https://github.com/frappe/erpnext/pull/26723)) +- Let all System Managers be able to delete Company transactions ([#26819](https://github.com/frappe/erpnext/pull/26819)) +- Bank remittance report issue ([#26398](https://github.com/frappe/erpnext/pull/26398)) +- Faulty Gl Entry for Asset LCVs ([#26803](https://github.com/frappe/erpnext/pull/26803)) +- Clean Serial No input on Server Side ([#26878](https://github.com/frappe/erpnext/pull/26878)) +- Supplier invoice importer fix v13 ([#26633](https://github.com/frappe/erpnext/pull/26633)) +- POS payment modes displayed wrong total ([#26808](https://github.com/frappe/erpnext/pull/26808)) +- Fetching of item tax from hsn code ([#26736](https://github.com/frappe/erpnext/pull/26736)) +- Cannot cancel invoice if IRN cancelled on portal ([#26879](https://github.com/frappe/erpnext/pull/26879)) +- Validate python expressions ([#26856](https://github.com/frappe/erpnext/pull/26856)) +- POS Item Cart non-stop scroll issue ([#26693](https://github.com/frappe/erpnext/pull/26693)) +- Add mandatory depends on condition for export type field ([#26958](https://github.com/frappe/erpnext/pull/26958)) +- Cannot generate IRNs for standalone credit notes ([#26824](https://github.com/frappe/erpnext/pull/26824)) +- Added progress bar in Repost Item Valuation to check the status of reposting ([#26630](https://github.com/frappe/erpnext/pull/26630)) +- TDS calculation for first threshold breach for TDS category 194Q ([#26710](https://github.com/frappe/erpnext/pull/26710)) +- Student category mapping from the program enrollment tool ([#26739](https://github.com/frappe/erpnext/pull/26739)) +- Cost center & account validation in Sales/Purchase Taxes and Charges ([#26881](https://github.com/frappe/erpnext/pull/26881)) +- Reset weight_per_unit on replacing Item ([#26791](https://github.com/frappe/erpnext/pull/26791)) +- Do not fetch fully return issued purchase receipts ([#26825](https://github.com/frappe/erpnext/pull/26825)) +- Incorrect amount in work order required items table. ([#26585](https://github.com/frappe/erpnext/pull/26585)) +- Additional discount calculations in Invoices ([#26553](https://github.com/frappe/erpnext/pull/26553)) +- Refactored Asset Repair ([#26415](https://github.com/frappe/erpnext/pull/25798)) +- Exchange rate revaluation posting date and precision fixes ([#26650](https://github.com/frappe/erpnext/pull/26650)) +- POS Invoice consolidated Sales Invoice field set to no copy ([#26768](https://github.com/frappe/erpnext/pull/26768)) +- Consider grand total for threshold check ([#26683](https://github.com/frappe/erpnext/pull/26683)) +- Budget variance missing values ([#26966](https://github.com/frappe/erpnext/pull/26966)) +- GL Entries for exchange gain loss ([#26728](https://github.com/frappe/erpnext/pull/26728)) +- Add missing cess amount in GSTR-3B report ([#26544](https://github.com/frappe/erpnext/pull/26544)) +- GST Reports timeout issue ([#26575](https://github.com/frappe/erpnext/pull/26575)) \ No newline at end of file From 03fdce5a1975602a295de2dcf76f8c6facc9fc6b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 17 Aug 2021 10:25:49 +0550 Subject: [PATCH 345/369] bumped to version 13.9.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c90e01cfbd6..17d650568a4 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '13.8.0' +__version__ = '13.9.0' def get_default_company(user=None): '''Get default company for user''' From 8c851b7019db72fd5a5f835b985cd08bf10f723d Mon Sep 17 00:00:00 2001 From: Chillar Anand Date: Tue, 17 Aug 2021 10:39:18 +0530 Subject: [PATCH 346/369] Merge pull request #26906 from ChillarAnand/label fix: Changed label to "Inpatient Visit Charge" in appointment type --- .../appointment_type_service_item.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json index 5ff68cd682c..ccae129ea0b 100644 --- a/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json +++ b/erpnext/healthcare/doctype/appointment_type_service_item/appointment_type_service_item.json @@ -48,13 +48,13 @@ "fieldname": "inpatient_visit_charge", "fieldtype": "Currency", "in_list_view": 1, - "label": "Inpatient Visit Charge Item" + "label": "Inpatient Visit Charge" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-01-22 09:35:26.503443", + "modified": "2021-08-17 06:05:02.240812", "modified_by": "Administrator", "module": "Healthcare", "name": "Appointment Type Service Item", From b1bcb3bf13ac0c10f155e7f37670a42939a4c217 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 17 Aug 2021 11:28:46 +0530 Subject: [PATCH 347/369] fix: Incorrect unallocated amount calculation in payment entry --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 46904f7c571..831b2708583 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -529,7 +529,7 @@ class PaymentEntry(AccountsController): if self.payment_type == "Receive" \ and self.base_total_allocated_amount < self.base_received_amount + total_deductions \ and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate): - self.unallocated_amount = (self.received_amount + total_deductions - + self.unallocated_amount = (self.base_received_amount + total_deductions - self.base_total_allocated_amount) / self.source_exchange_rate self.unallocated_amount -= included_taxes elif self.payment_type == "Pay" \ From fbb328b84a741e1d407a48c06d4abaa49e14f3aa Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 17 Aug 2021 13:07:03 +0530 Subject: [PATCH 348/369] chore: added missing fieldname --- .../doctype/service_level_agreement/service_level_agreement.json | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index b1273b704ff..b67c7fceac9 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -183,6 +183,7 @@ }, { "label": "Pause SLA On", + "fieldname": "pause_sla_on", "options": "Pause SLA On Status" }, { From d932cba38afc176e0e6384f9081c523d66c0320e Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Tue, 17 Aug 2021 13:17:09 +0530 Subject: [PATCH 349/369] Merge pull request #26976 from resilient-tech/fix-incorrect-modified fix: Incorrect `modified` time in documents that inherit from `StatusUpdater` --- erpnext/controllers/status_updater.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 943f7aaeb12..b1f89b08d79 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import flt, comma_or, nowdate, getdate +from frappe.utils import flt, comma_or, nowdate, getdate, now from frappe import _ from frappe.model.document import Document @@ -336,10 +336,14 @@ class StatusUpdater(Document): target.notify_update() def _update_modified(self, args, update_modified): - args['update_modified'] = '' - if update_modified: - args['update_modified'] = ', modified = now(), modified_by = {0}'\ - .format(frappe.db.escape(frappe.session.user)) + if not update_modified: + args['update_modified'] = '' + return + + args['update_modified'] = ', modified = {0}, modified_by = {1}'.format( + frappe.db.escape(now()), + frappe.db.escape(frappe.session.user) + ) def update_billing_status_for_zero_amount_refdoc(self, ref_dt): ref_fieldname = frappe.scrub(ref_dt) From 3c525e11360869048845e0f4f32cf4743a40871c Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 17 Aug 2021 14:22:47 +0530 Subject: [PATCH 350/369] fix(sla): 'doctype' is not defined (#26982) --- .../doctype/service_level_agreement/service_level_agreement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index eb9fa12b664..083bde9a42c 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -220,7 +220,7 @@ def get_active_service_level_agreement_for(doc): return filters = [ - ["Service Level Agreement", "document_type", "=", doctype], + ["Service Level Agreement", "document_type", "=", doc.get('doctype')], ["Service Level Agreement", "enabled", "=", 1] ] From 8bbec42fa0c9271006f04cefacae5dbe4d109a44 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 17 Aug 2021 15:14:13 +0530 Subject: [PATCH 351/369] fix: undo changes to patch (#26983) * fix: undo changes to patch * ci: ignore empty body / head --- .github/helper/documentation.py | 6 +++--- erpnext/patches/v13_0/shopify_deprecation_warning.py | 4 ---- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/helper/documentation.py b/.github/helper/documentation.py index b4a4ba1bbdd..91983d3eae5 100644 --- a/.github/helper/documentation.py +++ b/.github/helper/documentation.py @@ -32,9 +32,9 @@ if __name__ == "__main__": if response.ok: payload = response.json() - title = payload.get("title", "").lower().strip() - head_sha = payload.get("head", {}).get("sha") - body = payload.get("body", "").lower() + title = (payload.get("title") or "").lower().strip() + head_sha = (payload.get("head") or {}).get("sha") + body = (payload.get("body") or "").lower() if (title.startswith("feat") and head_sha diff --git a/erpnext/patches/v13_0/shopify_deprecation_warning.py b/erpnext/patches/v13_0/shopify_deprecation_warning.py index 8b0f1935cfb..6f199c87b6c 100644 --- a/erpnext/patches/v13_0/shopify_deprecation_warning.py +++ b/erpnext/patches/v13_0/shopify_deprecation_warning.py @@ -4,10 +4,6 @@ import frappe def execute(): - frappe.reload_doc("erpnext_integrations", "doctype", "shopify_settings") - if not frappe.db.get_single_value("Shopify Settings", "enable_shopify"): - return - click.secho( "Shopify Integration is moved to a separate app and will be removed from ERPNext in version-14.\n" "Please install the app to continue using the integration: https://github.com/frappe/ecommerce_integrations", From a14d21883414cceb29e764b3af7748fb85d70fe6 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 17 Aug 2021 16:14:37 +0530 Subject: [PATCH 352/369] fix: typo (#26967) (#26984) (cherry picked from commit ace8cf965d2aab3faa18aa704e0e796f707cc666) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 5424ef3c669..92f558fb337 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1463,7 +1463,7 @@ class SalesInvoice(SellingController): discounting_status = None if self.is_discounted: - discountng_status = get_discounting_status(self.name) + discounting_status = get_discounting_status(self.name) if not status: if self.docstatus == 2: @@ -1471,11 +1471,11 @@ class SalesInvoice(SellingController): elif self.docstatus == 1: if self.is_internal_transfer(): self.status = 'Internal Transfer' - elif outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed': + elif outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discounting_status=='Disbursed': self.status = "Overdue and Discounted" elif outstanding_amount > 0 and due_date < nowdate: self.status = "Overdue" - elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed': + elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discounting_status=='Disbursed': self.status = "Unpaid and Discounted" elif outstanding_amount > 0 and due_date >= nowdate: self.status = "Unpaid" From fd739749bae7f3f1e544270451ccee24b5dd38f8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 17 Aug 2021 16:38:25 +0530 Subject: [PATCH 353/369] test: Add test case for payment entry --- .../payment_entry/test_payment_entry.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index d1302f5ae78..801dadc7f17 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -295,6 +295,34 @@ class TestPaymentEntry(unittest.TestCase): outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount")) self.assertEqual(outstanding_amount, 80) + def test_payment_entry_against_si_usd_to_usd_with_deduction_in_base_currency (self): + si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC", + currency="USD", conversion_rate=50, do_not_save=1) + + si.plc_conversion_rate = 50 + si.save() + si.submit() + + pe = get_payment_entry("Sales Invoice", si.name, party_amount=20, + bank_account="_Test Bank USD - _TC", bank_amount=900) + + pe.source_exchange_rate = 45.263 + pe.target_exchange_rate = 45.263 + pe.reference_no = "1" + pe.reference_date = "2016-01-01" + + + pe.append("deductions", { + "account": "_Test Exchange Gain/Loss - _TC", + "cost_center": "_Test Cost Center - _TC", + "amount": 94.80 + }) + + pe.save() + + self.assertEqual(flt(pe.difference_amount, 2), 0.0) + self.assertEqual(flt(pe.unallocated_amount, 2), 0.0) + def test_payment_entry_retrieves_last_exchange_rate(self): from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records From a2966db1e55a1a5c44fe22a6818b6c3f504e3fc7 Mon Sep 17 00:00:00 2001 From: Mohammed Redah Date: Tue, 17 Aug 2021 16:15:55 +0300 Subject: [PATCH 354/369] fix: change print_format_type from Server to Jinja (#26374) --- .../bank_and_cash_payment_voucher.json | 2 +- .../cheque_printing_format/cheque_printing_format.json | 2 +- erpnext/accounts/print_format/credit_note/credit_note.json | 2 +- .../print_format/gst_purchase_invoice/gst_purchase_invoice.json | 2 +- .../journal_auditing_voucher/journal_auditing_voucher.json | 2 +- .../payment_receipt_voucher/payment_receipt_voucher.json | 2 +- .../purchase_auditing_voucher/purchase_auditing_voucher.json | 2 +- .../sales_auditing_voucher/sales_auditing_voucher.json | 2 +- .../print_format/drop_shipping_format/drop_shipping_format.json | 2 +- .../print_format/encounter_print/encounter_print.json | 2 +- .../print_format/sample_id_print/sample_id_print.json | 2 +- erpnext/hr/print_format/job_offer/job_offer.json | 2 +- .../salary_slip_based_on_timesheet.json | 2 +- .../print_format/salary_slip_standard/salary_slip_standard.json | 2 +- .../print_format/detailed_tax_invoice/detailed_tax_invoice.json | 2 +- .../regional/print_format/gst_tax_invoice/gst_tax_invoice.json | 2 +- .../simplified_tax_invoice/simplified_tax_invoice.json | 2 +- erpnext/regional/print_format/tax_invoice/tax_invoice.json | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json index e3afaec2ad0..9e126bade7b 100644 --- a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json +++ b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.json @@ -16,7 +16,7 @@ "name": "Bank and Cash Payment Voucher", "owner": "Administrator", "print_format_builder": 0, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/accounts/print_format/cheque_printing_format/cheque_printing_format.json b/erpnext/accounts/print_format/cheque_printing_format/cheque_printing_format.json index 8c9c26691e5..08b8ef88900 100755 --- a/erpnext/accounts/print_format/cheque_printing_format/cheque_printing_format.json +++ b/erpnext/accounts/print_format/cheque_printing_format/cheque_printing_format.json @@ -10,6 +10,6 @@ "modified_by": "Administrator", "name": "Cheque Printing Format", "owner": "Administrator", - "print_format_type": "Server", + "print_format_type": "Jinja", "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/accounts/print_format/credit_note/credit_note.json b/erpnext/accounts/print_format/credit_note/credit_note.json index c5a11cf3958..d12b872dd79 100644 --- a/erpnext/accounts/print_format/credit_note/credit_note.json +++ b/erpnext/accounts/print_format/credit_note/credit_note.json @@ -17,7 +17,7 @@ "owner": "Administrator", "parentfield": "__print_formats", "print_format_builder": 0, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json b/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json index 6d7c3d31ae4..1467f27e66a 100644 --- a/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json +++ b/erpnext/accounts/print_format/gst_purchase_invoice/gst_purchase_invoice.json @@ -16,7 +16,7 @@ "name": "GST Purchase Invoice", "owner": "Administrator", "print_format_builder": 1, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json index 927e818e017..e45295d9fea 100644 --- a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json +++ b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.json @@ -16,7 +16,7 @@ "name": "Journal Auditing Voucher", "owner": "Administrator", "print_format_builder": 0, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.json b/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.json index f1de448bb98..c52eeb2dfb9 100755 --- a/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.json +++ b/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.json @@ -12,6 +12,6 @@ "name": "Payment Receipt Voucher", "owner": "Administrator", "print_format_builder": 0, - "print_format_type": "Server", + "print_format_type": "Jinja", "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json index 73779d49aab..33981178220 100644 --- a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json +++ b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.json @@ -16,7 +16,7 @@ "name": "Purchase Auditing Voucher", "owner": "Administrator", "print_format_builder": 0, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json index 0544e0bc9e8..289e1848488 100644 --- a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json +++ b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.json @@ -16,7 +16,7 @@ "name": "Sales Auditing Voucher", "owner": "Administrator", "print_format_builder": 0, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/buying/print_format/drop_shipping_format/drop_shipping_format.json b/erpnext/buying/print_format/drop_shipping_format/drop_shipping_format.json index 0c7cad6e942..2e2a04826a3 100644 --- a/erpnext/buying/print_format/drop_shipping_format/drop_shipping_format.json +++ b/erpnext/buying/print_format/drop_shipping_format/drop_shipping_format.json @@ -13,6 +13,6 @@ "name": "Drop Shipping Format", "owner": "Administrator", "print_format_builder": 0, - "print_format_type": "Server", + "print_format_type": "Jinja", "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/healthcare/print_format/encounter_print/encounter_print.json b/erpnext/healthcare/print_format/encounter_print/encounter_print.json index ec1e0f202a2..3c90adb0a1c 100644 --- a/erpnext/healthcare/print_format/encounter_print/encounter_print.json +++ b/erpnext/healthcare/print_format/encounter_print/encounter_print.json @@ -16,7 +16,7 @@ "name": "Encounter Print", "owner": "Administrator", "print_format_builder": 0, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/healthcare/print_format/sample_id_print/sample_id_print.json b/erpnext/healthcare/print_format/sample_id_print/sample_id_print.json index e99ce708f46..4819e6d57ac 100644 --- a/erpnext/healthcare/print_format/sample_id_print/sample_id_print.json +++ b/erpnext/healthcare/print_format/sample_id_print/sample_id_print.json @@ -16,7 +16,7 @@ "name": "Sample ID Print", "owner": "Administrator", "print_format_builder": 0, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } diff --git a/erpnext/hr/print_format/job_offer/job_offer.json b/erpnext/hr/print_format/job_offer/job_offer.json index 3fc2bcf7d27..0a922306b46 100644 --- a/erpnext/hr/print_format/job_offer/job_offer.json +++ b/erpnext/hr/print_format/job_offer/job_offer.json @@ -15,7 +15,7 @@ "name": "Job Offer", "owner": "Administrator", "print_format_builder": 0, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json index ceaf4a6ac71..d5fee6b5b83 100644 --- a/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json +++ b/erpnext/payroll/print_format/salary_slip_based_on_timesheet/salary_slip_based_on_timesheet.json @@ -13,6 +13,6 @@ "name": "Salary Slip based on Timesheet", "owner": "Administrator", "print_format_builder": 1, - "print_format_type": "Server", + "print_format_type": "Jinja", "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json b/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json index b01239fe02d..98a4435a50e 100644 --- a/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json +++ b/erpnext/payroll/print_format/salary_slip_standard/salary_slip_standard.json @@ -16,7 +16,7 @@ "name": "Salary Slip Standard", "owner": "Administrator", "print_format_builder": 1, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/regional/print_format/detailed_tax_invoice/detailed_tax_invoice.json b/erpnext/regional/print_format/detailed_tax_invoice/detailed_tax_invoice.json index ab56c6bc3cf..f67e2450a29 100644 --- a/erpnext/regional/print_format/detailed_tax_invoice/detailed_tax_invoice.json +++ b/erpnext/regional/print_format/detailed_tax_invoice/detailed_tax_invoice.json @@ -16,7 +16,7 @@ "name": "Detailed Tax Invoice", "owner": "Administrator", "print_format_builder": 1, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/regional/print_format/gst_tax_invoice/gst_tax_invoice.json b/erpnext/regional/print_format/gst_tax_invoice/gst_tax_invoice.json index 7d8e675e9fe..b23206b0bdd 100644 --- a/erpnext/regional/print_format/gst_tax_invoice/gst_tax_invoice.json +++ b/erpnext/regional/print_format/gst_tax_invoice/gst_tax_invoice.json @@ -16,7 +16,7 @@ "name": "GST Tax Invoice", "owner": "Administrator", "print_format_builder": 1, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/regional/print_format/simplified_tax_invoice/simplified_tax_invoice.json b/erpnext/regional/print_format/simplified_tax_invoice/simplified_tax_invoice.json index b324f6ee683..aed2e89ed79 100644 --- a/erpnext/regional/print_format/simplified_tax_invoice/simplified_tax_invoice.json +++ b/erpnext/regional/print_format/simplified_tax_invoice/simplified_tax_invoice.json @@ -16,7 +16,7 @@ "name": "Simplified Tax Invoice", "owner": "Administrator", "print_format_builder": 1, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/regional/print_format/tax_invoice/tax_invoice.json b/erpnext/regional/print_format/tax_invoice/tax_invoice.json index 74db06727bf..7479891595e 100644 --- a/erpnext/regional/print_format/tax_invoice/tax_invoice.json +++ b/erpnext/regional/print_format/tax_invoice/tax_invoice.json @@ -16,7 +16,7 @@ "name": "Tax Invoice", "owner": "Administrator", "print_format_builder": 1, - "print_format_type": "Server", + "print_format_type": "Jinja", "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file From 5fec44446eed481021f5ec9c187b6558e790501e Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 17 Aug 2021 20:06:19 +0530 Subject: [PATCH 355/369] fix: set account for change amount even if pos profile not found (#26986) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 92f558fb337..5fa622856bc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -478,6 +478,9 @@ class SalesInvoice(SellingController): if cint(self.is_pos) != 1: return + if not self.account_for_change_amount: + self.account_for_change_amount = frappe.get_cached_value('Company', self.company, 'default_cash_account') + from erpnext.stock.get_item_details import get_pos_profile_item_details, get_pos_profile if not self.pos_profile: pos_profile = get_pos_profile(self.company) or {} @@ -492,9 +495,6 @@ class SalesInvoice(SellingController): if not self.get('payments') and not for_validate: update_multi_mode_option(self, pos) - if not self.account_for_change_amount: - self.account_for_change_amount = frappe.get_cached_value('Company', self.company, 'default_cash_account') - if pos: if not for_validate: self.tax_category = pos.get("tax_category") From e0862de863f46e9641620eecbc35a935ee90fe43 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 18 Aug 2021 10:27:58 +0530 Subject: [PATCH 356/369] fix: sales invoice not loading issue --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 7a273c75049..8d65101b3ba 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -447,10 +447,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e this.frm.refresh_field("outstanding_amount"); this.frm.refresh_field("paid_amount"); this.frm.refresh_field("base_paid_amount"); - }, + } currency() { - this._super(); + super.currency(); $.each(cur_frm.doc.timesheets, function(i, d) { let row = frappe.get_doc(d.doctype, d.name) set_timesheet_detail_rate(row.doctype, row.name, cur_frm.doc.currency, row.timesheet_detail) From 15df0ad6a0ede96008b3e40db0f823dff0a35570 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 18 Aug 2021 11:23:44 +0530 Subject: [PATCH 357/369] fix: undefined variable due to inconsistent porting of commits (#26994) --- erpnext/payroll/doctype/salary_slip/salary_slip.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index bd982cf488c..5f5fdd5c830 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -680,7 +680,6 @@ class SalarySlip(TransactionBase): component_row.set(attr, component_data.get(attr)) if additional_salary: - component_row.is_recurring_additional_salary = is_recurring if additional_salary.overwrite: component_row.additional_amount = flt(flt(amount) - flt(component_row.get("default_amount", 0)), component_row.precision("additional_amount")) From fbc163cea66b52494595435200c43ea581fe9bd2 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 18 Aug 2021 11:53:26 +0530 Subject: [PATCH 358/369] feat: dynamic conditions for applying SLA (#26662) --- .../service_level_agreement.json | 7 +---- .../service_level_agreement.py | 25 +++++++++--------- .../test_service_level_agreement.py | 26 +++++++++++++++++-- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json index b67c7fceac9..b649b8768bb 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -181,11 +181,6 @@ "fieldtype": "Check", "label": "Apply SLA for Resolution Time" }, - { - "label": "Pause SLA On", - "fieldname": "pause_sla_on", - "options": "Pause SLA On Status" - }, { "fieldname": "filters_section", "fieldtype": "Section Break", @@ -208,7 +203,7 @@ } ], "links": [], - "modified": "2021-07-09 12:28:46.283334", + "modified": "2021-07-27 11:16:45.596579", "modified_by": "Administrator", "module": "Support", "name": "Service Level Agreement", diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 083bde9a42c..d9cffc231f4 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -11,6 +11,7 @@ from frappe.core.utils import get_parent_doc from frappe.utils import time_diff_in_seconds, getdate, get_weekdays, add_to_date, get_time, get_datetime, \ get_time_zone, to_timedelta, get_datetime_str, get_link_to_form, cint, nowdate from datetime import datetime +from frappe.utils.safe_exec import get_safe_globals from erpnext.support.doctype.issue.issue import get_holidays from frappe.utils.safe_exec import get_safe_globals @@ -100,7 +101,7 @@ class ServiceLevelAgreement(Document): frappe.bold(self.document_type))) def validate_condition(self): - temp_doc = frappe.new_doc('Issue') + temp_doc = frappe.new_doc(self.document_type) if self.condition: try: frappe.safe_eval(self.condition, None, get_context(temp_doc)) @@ -227,25 +228,26 @@ def get_active_service_level_agreement_for(doc): if doc.get('priority'): filters.append(["Service Level Priority", "priority", "=", doc.get('priority')]) - customer = doc.get('customer') - or_filters = [ - ["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer)]] - ] - - service_level_agreement = doc.get('service_level_agreement') - if service_level_agreement: + or_filters = [] + if doc.get('service_level_agreement'): or_filters = [ ["Service Level Agreement", "name", "=", doc.get('service_level_agreement')], ] + customer = doc.get('customer') + if customer: + or_filters.append( + ["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer)]] + ) + default_sla_filter = filters + [["Service Level Agreement", "default_service_level_agreement", "=", 1]] default_sla = frappe.get_all("Service Level Agreement", filters=default_sla_filter, - fields=["name", "default_priority", "condition"]) + fields=["name", "default_priority", "apply_sla_for_resolution", "condition"]) filters += [["Service Level Agreement", "default_service_level_agreement", "=", 0]] agreements = frappe.get_all("Service Level Agreement", filters=filters, or_filters=or_filters, - fields=["name", "default_priority", "condition"]) - + fields=["name", "default_priority", "apply_sla_for_resolution", "condition"]) + # check if the current document on which SLA is to be applied fulfills all the conditions filtered_agreements = [] for agreement in agreements: @@ -261,7 +263,6 @@ def get_active_service_level_agreement_for(doc): def get_context(doc): return {"doc": doc.as_dict(), "nowdate": nowdate, "frappe": frappe._dict(utils=get_safe_globals().get("frappe").get("utils"))} - def get_customer_group(customer): return frappe.db.get_value("Customer", customer, "customer_group") if customer else None diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 7bc97d60227..1a5ff27d2a4 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -253,6 +253,26 @@ class TestServiceLevelAgreement(unittest.TestCase): lead.reload() self.assertEqual(lead.response_by_variance, 1800.0) + def test_service_level_agreement_filters(self): + doctype = "Lead" + lead_sla = create_service_level_agreement( + default_service_level_agreement=0, + doctype=doctype, + holiday_list="__Test Holiday List", + entity_type=None, entity=None, + condition='doc.source == "Test Source"', + response_time=14400, + sla_fulfilled_on=[{"status": "Replied"}], + apply_sla_for_resolution=0 + ) + creation = datetime.datetime(2019, 3, 4, 12, 0) + lead = make_lead(creation=creation, index=4) + self.assertFalse(lead.service_level_agreement) + + lead.source = "Test Source" + lead.save() + self.assertEqual(lead.service_level_agreement, lead_sla.name) + def tearDown(self): for d in frappe.get_all("Service Level Agreement"): frappe.delete_doc("Service Level Agreement", d.name, force=1) @@ -268,7 +288,7 @@ def get_service_level_agreement(default_service_level_agreement=None, entity_typ return service_level_agreement def create_service_level_agreement(default_service_level_agreement, holiday_list, response_time, entity_type, - entity, resolution_time=0, doctype="Issue", sla_fulfilled_on=[], pause_sla_on=[], apply_sla_for_resolution=1): + entity, resolution_time=0, doctype="Issue", condition="", sla_fulfilled_on=[], pause_sla_on=[], apply_sla_for_resolution=1): make_holiday_list() make_priorities() @@ -287,6 +307,7 @@ def create_service_level_agreement(default_service_level_agreement, holiday_list "document_type": doctype, "service_level": "__Test {} SLA".format(entity_type if entity_type else "Default"), "default_service_level_agreement": default_service_level_agreement, + "condition": condition, "default_priority": "Medium", "holiday_list": holiday_list, "entity_type": entity_type, @@ -488,5 +509,6 @@ def make_lead(creation=None, index=0): "lead_name": "_Test Lead {0}".format(index), "status": "Open", "creation": creation, - "service_level_agreement_creation": creation + "service_level_agreement_creation": creation, + "priority": "Medium" }).insert(ignore_permissions=True) \ No newline at end of file From cab49254fcb2ead50486851e93e8fa57ec058c3d Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 18 Aug 2021 14:53:32 +0530 Subject: [PATCH 359/369] fix: failing tests in issue doctype (#26998) --- erpnext/support/doctype/issue/test_issue.py | 4 ++++ .../service_level_agreement/service_level_agreement.py | 7 +++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 84f8c398bee..739324f5620 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -13,6 +13,10 @@ from datetime import timedelta class TestSetUp(unittest.TestCase): def setUp(self): frappe.db.sql("delete from `tabService Level Agreement`") + frappe.db.sql("delete from `tabService Level Priority`") + frappe.db.sql("delete from `tabSLA Fulfilled On Status`") + frappe.db.sql("delete from `tabPause SLA On Status`") + frappe.db.sql("delete from `tabService Day`") frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) create_service_level_agreements_for_issues() diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index d9cffc231f4..472e96c059e 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -235,10 +235,9 @@ def get_active_service_level_agreement_for(doc): ] customer = doc.get('customer') - if customer: - or_filters.append( - ["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer)]] - ) + or_filters.append( + ["Service Level Agreement", "entity", "in", [customer, get_customer_group(customer), get_customer_territory(customer)]] + ) default_sla_filter = filters + [["Service Level Agreement", "default_service_level_agreement", "=", 1]] default_sla = frappe.get_all("Service Level Agreement", filters=default_sla_filter, From 112fc888f1a2ec52d8c736669111fd62e6808109 Mon Sep 17 00:00:00 2001 From: Marica Date: Wed, 18 Aug 2021 15:29:48 +0530 Subject: [PATCH 360/369] fix: Return Qty in PR/DN for legacy data (#27001) --- .../patches/v13_0/update_returned_qty_in_pr_dn.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py b/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py index 7f42cd92e3c..409f4da8599 100644 --- a/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py +++ b/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py @@ -1,8 +1,7 @@ -# Copyright (c) 2019, Frappe and Contributors +# Copyright (c) 2021, Frappe and Contributors # License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals import frappe +from erpnext.controllers.status_updater import OverAllowanceError def execute(): frappe.reload_doc('stock', 'doctype', 'purchase_receipt') @@ -14,9 +13,15 @@ def execute(): for return_doc in frappe.get_all(doctype, filters={'is_return' : 1, 'docstatus' : 1}): # Update original receipt/delivery document from return return_doc = frappe.get_cached_doc(doctype, return_doc.name) - return_doc.update_prevdoc_status() + try: + return_doc.update_prevdoc_status() + except OverAllowanceError: + frappe.db.rollback() + continue + return_against = frappe.get_doc(doctype, return_doc.return_against) return_against.update_billing_status() + frappe.db.commit() # Set received qty in stock uom in PR, as returned qty is checked against it frappe.db.sql(""" update `tabPurchase Receipt Item` From 333e44eb471346bb56a5133e4677b889af545d10 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Wed, 18 Aug 2021 16:17:54 +0530 Subject: [PATCH 361/369] fix: Dimension filter query fix to avoid including disabled dimensions (#26988) * reverting ot v12.7.1 * fix: Dimension filter query fix to not display disabled dimensions Co-authored-by: Subin Tom Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com> --- erpnext/controllers/queries.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 21c052a3912..4b4c8befa53 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -526,6 +526,9 @@ def get_filtered_dimensions(doctype, txt, searchfield, start, page_len, filters) if meta.is_tree: query_filters.append(['is_group', '=', 0]) + if meta.has_field('disabled'): + query_filters.append(['disabled', '!=', 1]) + if meta.has_field('company'): query_filters.append(['company', '=', filters.get('company')]) From dc7280eef08cec4ce67cfe54b23d65d8bd96de2d Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 18 Aug 2021 17:44:40 +0530 Subject: [PATCH 362/369] fix: filtering of items in Sales and Purchase Orders (#26936) * fix: filtering of items in Sales and Purchase Orders * fix: slider * fix: slider --- erpnext/public/js/utils.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index db7c034596a..f1b9235fe3a 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -465,7 +465,23 @@ erpnext.utils.update_child_items = function(opts) { in_list_view: 1, read_only: 0, disabled: 0, - label: __('Item Code') + label: __('Item Code'), + get_query: function() { + let filters; + if (frm.doc.doctype == 'Sales Order') { + filters = {"is_sales_item": 1}; + } else if (frm.doc.doctype == 'Purchase Order') { + if (frm.doc.is_subcontracted == "Yes") { + filters = {"is_sub_contracted_item": 1}; + } else { + filters = {"is_purchase_item": 1}; + } + } + return { + query: "erpnext.controllers.queries.item_query", + filters: filters + }; + } }, { fieldtype:'Link', fieldname:'uom', From ef792971f397869e2ae931c995fb8e239845e7e5 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Wed, 18 Aug 2021 18:20:04 +0530 Subject: [PATCH 363/369] fix: Add ignore user perms to set_target_warehouse field in sales invoice (#26987) * reverting ot v12.7.1 * fix: Ignore user permissions for set_target_warehouse in SI Co-authored-by: Subin Tom Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com> --- .../doctype/sales_invoice/sales_invoice.json | 725 +++++------------- 1 file changed, 180 insertions(+), 545 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index b2be8608db2..7dfe77db55f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -199,9 +199,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "options": "fa fa-user", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-user" }, { "allow_on_submit": 1, @@ -213,9 +211,7 @@ "hide_seconds": 1, "label": "Title", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "bold": 1, @@ -230,9 +226,7 @@ "options": "ACC-SINV-.YYYY.-\nACC-SINV-RET-.YYYY.-", "print_hide": 1, "reqd": 1, - "set_only_once": 1, - "show_days": 1, - "show_seconds": 1 + "set_only_once": 1 }, { "bold": 1, @@ -246,9 +240,7 @@ "oldfieldtype": "Link", "options": "Customer", "print_hide": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "bold": 1, @@ -262,9 +254,7 @@ "label": "Customer Name", "oldfieldname": "customer_name", "oldfieldtype": "Data", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "tax_id", @@ -273,9 +263,7 @@ "hide_seconds": 1, "label": "Tax Id", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "project", @@ -287,9 +275,7 @@ "oldfieldname": "project_name", "oldfieldtype": "Link", "options": "Project", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -300,9 +286,7 @@ "label": "Include Payment (POS)", "oldfieldname": "is_pos", "oldfieldtype": "Check", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "is_pos", @@ -312,9 +296,7 @@ "hide_seconds": 1, "label": "POS Profile", "options": "POS Profile", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -324,18 +306,14 @@ "hide_seconds": 1, "label": "Is Return (Credit Note)", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break1", "fieldtype": "Column Break", "hide_days": 1, "hide_seconds": 1, - "oldfieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Column Break" }, { "fieldname": "company", @@ -349,9 +327,7 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "cost_center", @@ -359,9 +335,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Cost Center", - "options": "Cost Center", - "show_days": 1, - "show_seconds": 1 + "options": "Cost Center" }, { "bold": 1, @@ -375,9 +349,7 @@ "oldfieldname": "posting_date", "oldfieldtype": "Date", "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "fieldname": "posting_time", @@ -388,9 +360,7 @@ "no_copy": 1, "oldfieldname": "posting_time", "oldfieldtype": "Time", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -400,9 +370,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Edit Posting Date and Time", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "due_date", @@ -412,9 +380,7 @@ "label": "Payment Due Date", "no_copy": 1, "oldfieldname": "due_date", - "oldfieldtype": "Date", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Date" }, { "fieldname": "amended_from", @@ -428,9 +394,7 @@ "oldfieldtype": "Link", "options": "Sales Invoice", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:doc.return_against || doc.is_debit_note", @@ -443,9 +407,7 @@ "options": "Sales Invoice", "print_hide": 1, "read_only_depends_on": "eval:doc.is_return", - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "default": "0", @@ -454,9 +416,7 @@ "fieldtype": "Check", "hide_days": 1, "hide_seconds": 1, - "label": "Update Billed Amount in Sales Order", - "show_days": 1, - "show_seconds": 1 + "label": "Update Billed Amount in Sales Order" }, { "collapsible": 1, @@ -465,9 +425,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Customer PO Details", - "show_days": 1, - "show_seconds": 1 + "label": "Customer PO Details" }, { "allow_on_submit": 1, @@ -477,17 +435,13 @@ "hide_seconds": 1, "label": "Customer's Purchase Order", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_23", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "allow_on_submit": 1, @@ -495,9 +449,7 @@ "fieldtype": "Date", "hide_days": 1, "hide_seconds": 1, - "label": "Customer's Purchase Order Date", - "show_days": 1, - "show_seconds": 1 + "label": "Customer's Purchase Order Date" }, { "collapsible": 1, @@ -505,9 +457,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Address and Contact", - "show_days": 1, - "show_seconds": 1 + "label": "Address and Contact" }, { "fieldname": "customer_address", @@ -516,9 +466,7 @@ "hide_seconds": 1, "label": "Customer Address", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "address_display", @@ -526,9 +474,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Address", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_person", @@ -538,9 +484,7 @@ "in_global_search": 1, "label": "Contact Person", "options": "Contact", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "contact_display", @@ -548,9 +492,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Contact", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_mobile", @@ -559,9 +501,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Mobile No", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "contact_email", @@ -572,9 +512,7 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "territory", @@ -583,17 +521,13 @@ "hide_seconds": 1, "label": "Territory", "options": "Territory", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "col_break4", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fieldname": "shipping_address_name", @@ -602,9 +536,7 @@ "hide_seconds": 1, "label": "Shipping Address Name", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "shipping_address", @@ -613,9 +545,7 @@ "hide_seconds": 1, "label": "Shipping Address", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "company_address", @@ -624,9 +554,7 @@ "hide_seconds": 1, "label": "Company Address Name", "options": "Address", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "company_address_display", @@ -636,9 +564,7 @@ "hide_seconds": 1, "label": "Company Address", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, @@ -647,9 +573,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Currency and Price List", - "show_days": 1, - "show_seconds": 1 + "label": "Currency and Price List" }, { "fieldname": "currency", @@ -661,9 +585,7 @@ "oldfieldtype": "Select", "options": "Currency", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "description": "Rate at which Customer Currency is converted to customer's base currency", @@ -676,17 +598,13 @@ "oldfieldtype": "Currency", "precision": "9", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "column_break2", "fieldtype": "Column Break", "hide_days": 1, "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -699,9 +617,7 @@ "oldfieldtype": "Select", "options": "Price List", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "price_list_currency", @@ -712,9 +628,7 @@ "options": "Currency", "print_hide": 1, "read_only": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "description": "Rate at which Price list currency is converted to customer's base currency", @@ -725,9 +639,7 @@ "label": "Price List Exchange Rate", "precision": "9", "print_hide": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "default": "0", @@ -738,18 +650,14 @@ "label": "Ignore Pricing Rule", "no_copy": 1, "permlevel": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "sec_warehouse", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Warehouse", - "show_days": 1, - "show_seconds": 1 + "label": "Warehouse" }, { "depends_on": "update_stock", @@ -759,9 +667,7 @@ "hide_seconds": 1, "label": "Source Warehouse", "options": "Warehouse", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "items_section", @@ -770,9 +676,7 @@ "hide_seconds": 1, "label": "Items", "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-shopping-cart" }, { "default": "0", @@ -783,18 +687,14 @@ "label": "Update Stock", "oldfieldname": "update_stock", "oldfieldtype": "Check", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "scan_barcode", "fieldtype": "Data", "hide_days": 1, "hide_seconds": 1, - "label": "Scan Barcode", - "show_days": 1, - "show_seconds": 1 + "label": "Scan Barcode" }, { "allow_bulk_edit": 1, @@ -806,18 +706,14 @@ "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Sales Invoice Item", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "pricing_rule_details", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Pricing Rules", - "show_days": 1, - "show_seconds": 1 + "label": "Pricing Rules" }, { "fieldname": "pricing_rules", @@ -826,9 +722,7 @@ "hide_seconds": 1, "label": "Pricing Rule Detail", "options": "Pricing Rule Detail", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "packing_list", @@ -837,9 +731,7 @@ "hide_seconds": 1, "label": "Packing List", "options": "fa fa-suitcase", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "packed_items", @@ -848,9 +740,7 @@ "hide_seconds": 1, "label": "Packed Items", "options": "Packed Item", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "product_bundle_help", @@ -858,9 +748,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Product Bundle Help", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -870,9 +758,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Time Sheet List", - "show_days": 1, - "show_seconds": 1 + "label": "Time Sheet List" }, { "fieldname": "timesheets", @@ -881,9 +767,7 @@ "hide_seconds": 1, "label": "Time Sheets", "options": "Sales Invoice Timesheet", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -894,17 +778,13 @@ "label": "Total Billing Amount", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "section_break_30", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fieldname": "total_qty", @@ -912,9 +792,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Total Quantity", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_total", @@ -924,9 +802,7 @@ "label": "Total (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "base_net_total", @@ -939,17 +815,13 @@ "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "column_break_32", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fieldname": "total", @@ -958,9 +830,7 @@ "hide_seconds": 1, "label": "Total", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "net_total", @@ -970,9 +840,7 @@ "label": "Net Total", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_net_weight", @@ -981,9 +849,7 @@ "hide_seconds": 1, "label": "Total Net Weight", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "taxes_section", @@ -991,9 +857,7 @@ "hide_days": 1, "hide_seconds": 1, "oldfieldtype": "Section Break", - "options": "fa fa-money", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-money" }, { "fieldname": "taxes_and_charges", @@ -1004,17 +868,13 @@ "oldfieldname": "charge", "oldfieldtype": "Link", "options": "Sales Taxes and Charges Template", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_38", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fieldname": "shipping_rule", @@ -1024,9 +884,7 @@ "label": "Shipping Rule", "oldfieldtype": "Button", "options": "Shipping Rule", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "tax_category", @@ -1035,17 +893,13 @@ "hide_seconds": 1, "label": "Tax Category", "options": "Tax Category", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "section_break_40", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fieldname": "taxes", @@ -1055,9 +909,7 @@ "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", - "options": "Sales Taxes and Charges", - "show_days": 1, - "show_seconds": 1 + "options": "Sales Taxes and Charges" }, { "collapsible": 1, @@ -1065,9 +917,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Tax Breakup", - "show_days": 1, - "show_seconds": 1 + "label": "Tax Breakup" }, { "fieldname": "other_charges_calculation", @@ -1078,17 +928,13 @@ "no_copy": 1, "oldfieldtype": "HTML", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "section_break_43", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fieldname": "base_total_taxes_and_charges", @@ -1100,17 +946,13 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_47", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fieldname": "total_taxes_and_charges", @@ -1120,9 +962,7 @@ "label": "Total Taxes and Charges", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, @@ -1130,9 +970,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Loyalty Points Redemption", - "show_days": 1, - "show_seconds": 1 + "label": "Loyalty Points Redemption" }, { "depends_on": "redeem_loyalty_points", @@ -1142,9 +980,7 @@ "hide_seconds": 1, "label": "Loyalty Points", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "redeem_loyalty_points", @@ -1156,9 +992,7 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "default": "0", @@ -1168,17 +1002,13 @@ "hide_seconds": 1, "label": "Redeem Loyalty Points", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_77", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fetch_from": "customer.loyalty_program", @@ -1190,9 +1020,7 @@ "no_copy": 1, "options": "Loyalty Program", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "redeem_loyalty_points", @@ -1202,9 +1030,7 @@ "hide_seconds": 1, "label": "Redemption Account", "no_copy": 1, - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "redeem_loyalty_points", @@ -1214,9 +1040,7 @@ "hide_seconds": 1, "label": "Redemption Cost Center", "no_copy": 1, - "options": "Cost Center", - "show_days": 1, - "show_seconds": 1 + "options": "Cost Center" }, { "collapsible": 1, @@ -1225,9 +1049,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Additional Discount", - "show_days": 1, - "show_seconds": 1 + "label": "Additional Discount" }, { "default": "Grand Total", @@ -1237,9 +1059,7 @@ "hide_seconds": 1, "label": "Apply Additional Discount On", "options": "\nGrand Total\nNet Total", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "base_discount_amount", @@ -1249,17 +1069,13 @@ "label": "Additional Discount Amount (Company Currency)", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_51", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fieldname": "additional_discount_percentage", @@ -1267,9 +1083,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Additional Discount Percentage", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "discount_amount", @@ -1278,9 +1092,7 @@ "hide_seconds": 1, "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "totals", @@ -1289,9 +1101,7 @@ "hide_seconds": 1, "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "base_grand_total", @@ -1304,9 +1114,7 @@ "options": "Company:company:default_currency", "print_hide": 1, "read_only": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -1318,9 +1126,7 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -1333,9 +1139,7 @@ "oldfieldtype": "Currency", "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "description": "In Words will be visible once you save the Sales Invoice.", @@ -1348,9 +1152,7 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break5", @@ -1359,8 +1161,6 @@ "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1, - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -1375,9 +1175,7 @@ "oldfieldtype": "Currency", "options": "currency", "read_only": 1, - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "depends_on": "eval:!doc.disable_rounded_total", @@ -1389,9 +1187,7 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "bold": 1, @@ -1404,9 +1200,7 @@ "oldfieldname": "rounded_total_export", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "in_words", @@ -1418,9 +1212,7 @@ "oldfieldname": "in_words_export", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "total_advance", @@ -1432,9 +1224,7 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "outstanding_amount", @@ -1447,9 +1237,7 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "collapsible": 1, @@ -1461,9 +1249,7 @@ "label": "Advance Payments", "oldfieldtype": "Section Break", "options": "fa fa-money", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -1471,9 +1257,7 @@ "fieldtype": "Check", "hide_days": 1, "hide_seconds": 1, - "label": "Allocate Advances Automatically (FIFO)", - "show_days": 1, - "show_seconds": 1 + "label": "Allocate Advances Automatically (FIFO)" }, { "depends_on": "eval:!doc.allocate_advances_automatically", @@ -1482,9 +1266,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Get Advances Received", - "options": "set_advances", - "show_days": 1, - "show_seconds": 1 + "options": "set_advances" }, { "fieldname": "advances", @@ -1495,9 +1277,7 @@ "oldfieldname": "advance_adjustment_details", "oldfieldtype": "Table", "options": "Sales Invoice Advance", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -1506,9 +1286,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Payment Terms", - "show_days": 1, - "show_seconds": 1 + "label": "Payment Terms" }, { "depends_on": "eval:(!doc.is_pos && !doc.is_return)", @@ -1519,9 +1297,7 @@ "label": "Payment Terms Template", "no_copy": 1, "options": "Payment Terms Template", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "eval:(!doc.is_pos && !doc.is_return)", @@ -1532,9 +1308,7 @@ "label": "Payment Schedule", "no_copy": 1, "options": "Payment Schedule", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)", @@ -1543,9 +1317,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Payments", - "options": "fa fa-money", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-money" }, { "depends_on": "is_pos", @@ -1558,9 +1330,7 @@ "oldfieldname": "cash_bank_account", "oldfieldtype": "Link", "options": "Account", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "eval:doc.is_pos===1", @@ -1570,17 +1340,13 @@ "hide_seconds": 1, "label": "Sales Invoice Payment", "options": "Sales Invoice Payment", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "section_break_84", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fieldname": "base_paid_amount", @@ -1591,17 +1357,13 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_86", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points", @@ -1615,17 +1377,13 @@ "oldfieldtype": "Currency", "options": "currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "section_break_88", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "depends_on": "is_pos", @@ -1637,17 +1395,13 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_90", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "depends_on": "is_pos", @@ -1658,9 +1412,7 @@ "label": "Change Amount", "no_copy": 1, "options": "currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "depends_on": "is_pos", @@ -1670,9 +1422,7 @@ "hide_seconds": 1, "label": "Account for Change Amount", "options": "Account", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -1683,8 +1433,6 @@ "hide_days": 1, "hide_seconds": 1, "label": "Write Off", - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -1695,9 +1443,7 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "base_write_off_amount", @@ -1708,9 +1454,7 @@ "no_copy": 1, "options": "Company:company:default_currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "default": "0", @@ -1720,17 +1464,13 @@ "hide_days": 1, "hide_seconds": 1, "label": "Write Off Outstanding Amount", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_74", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "fieldname": "write_off_account", @@ -1739,9 +1479,7 @@ "hide_seconds": 1, "label": "Write Off Account", "options": "Account", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "write_off_cost_center", @@ -1750,9 +1488,7 @@ "hide_seconds": 1, "label": "Write Off Cost Center", "options": "Cost Center", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -1762,9 +1498,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Terms and Conditions", - "oldfieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Section Break" }, { "fieldname": "tc_name", @@ -1775,9 +1509,7 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "terms", @@ -1786,9 +1518,7 @@ "hide_seconds": 1, "label": "Terms and Conditions Details", "oldfieldname": "terms", - "oldfieldtype": "Text Editor", - "show_days": 1, - "show_seconds": 1 + "oldfieldtype": "Text Editor" }, { "collapsible": 1, @@ -1796,9 +1526,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Printing Settings", - "show_days": 1, - "show_seconds": 1 + "label": "Printing Settings" }, { "allow_on_submit": 1, @@ -1810,9 +1538,7 @@ "oldfieldname": "letter_head", "oldfieldtype": "Select", "options": "Letter Head", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -1822,9 +1548,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Group same items", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "language", @@ -1833,17 +1557,13 @@ "hide_seconds": 1, "label": "Print Language", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_84", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "allow_on_submit": 1, @@ -1857,9 +1577,7 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1, - "show_days": 1, - "show_seconds": 1 + "report_hide": 1 }, { "collapsible": 1, @@ -1868,9 +1586,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "More Information", - "show_days": 1, - "show_seconds": 1 + "label": "More Information" }, { "fieldname": "inter_company_invoice_reference", @@ -1879,9 +1595,7 @@ "hide_seconds": 1, "label": "Inter Company Invoice Reference", "options": "Purchase Invoice", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "customer_group", @@ -1891,9 +1605,7 @@ "hide_seconds": 1, "label": "Customer Group", "options": "Customer Group", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "campaign", @@ -1904,9 +1616,7 @@ "oldfieldname": "campaign", "oldfieldtype": "Link", "options": "Campaign", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "default": "0", @@ -1916,17 +1626,13 @@ "hide_seconds": 1, "label": "Is Discounted", "no_copy": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "col_break23", "fieldtype": "Column Break", "hide_days": 1, "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -1940,9 +1646,7 @@ "no_copy": 1, "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nCancelled\nInternal Transfer", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "source", @@ -1953,9 +1657,7 @@ "oldfieldname": "source", "oldfieldtype": "Select", "options": "Lead Source", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -1966,9 +1668,7 @@ "label": "Accounting Details", "oldfieldtype": "Section Break", "options": "fa fa-file-text", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "debit_to", @@ -1981,9 +1681,7 @@ "options": "Account", "print_hide": 1, "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "fieldname": "party_account_currency", @@ -1995,9 +1693,7 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "default": "No", @@ -2009,9 +1705,7 @@ "oldfieldname": "is_opening", "oldfieldtype": "Select", "options": "No\nYes", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "c_form_applicable", @@ -2021,9 +1715,7 @@ "label": "C-Form Applicable", "no_copy": 1, "options": "No\nYes", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "c_form_no", @@ -2034,9 +1726,7 @@ "no_copy": 1, "options": "C-Form", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break8", @@ -2044,9 +1734,7 @@ "hide_days": 1, "hide_seconds": 1, "oldfieldtype": "Column Break", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "remarks", @@ -2057,9 +1745,7 @@ "no_copy": 1, "oldfieldname": "remarks", "oldfieldtype": "Text", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -2071,9 +1757,7 @@ "label": "Commission", "oldfieldtype": "Section Break", "options": "fa fa-group", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "sales_partner", @@ -2084,9 +1768,7 @@ "oldfieldname": "sales_partner", "oldfieldtype": "Link", "options": "Sales Partner", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break10", @@ -2095,8 +1777,6 @@ "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1, - "show_days": 1, - "show_seconds": 1, "width": "50%" }, { @@ -2107,9 +1787,7 @@ "label": "Commission Rate (%)", "oldfieldname": "commission_rate", "oldfieldtype": "Currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "total_commission", @@ -2120,9 +1798,7 @@ "oldfieldname": "total_commission", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -2132,9 +1808,7 @@ "hide_days": 1, "hide_seconds": 1, "label": "Sales Team", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -2146,9 +1820,7 @@ "oldfieldname": "sales_team", "oldfieldtype": "Table", "options": "Sales Team", - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "collapsible": 1, @@ -2156,9 +1828,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Subscription Section", - "show_days": 1, - "show_seconds": 1 + "label": "Subscription Section" }, { "allow_on_submit": 1, @@ -2168,9 +1838,7 @@ "hide_seconds": 1, "label": "From Date", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -2180,17 +1848,13 @@ "hide_seconds": 1, "label": "To Date", "no_copy": 1, - "print_hide": 1, - "show_days": 1, - "show_seconds": 1 + "print_hide": 1 }, { "fieldname": "column_break_140", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "allow_on_submit": 1, @@ -2202,9 +1866,7 @@ "no_copy": 1, "options": "Auto Repeat", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "allow_on_submit": 1, @@ -2213,9 +1875,7 @@ "fieldtype": "Button", "hide_days": 1, "hide_seconds": 1, - "label": "Update Auto Repeat Reference", - "show_days": 1, - "show_seconds": 1 + "label": "Update Auto Repeat Reference" }, { "fieldname": "against_income_account", @@ -2228,9 +1888,7 @@ "oldfieldname": "against_income_account", "oldfieldtype": "Small Text", "print_hide": 1, - "report_hide": 1, - "show_days": 1, - "show_seconds": 1 + "report_hide": 1 }, { "collapsible": 1, @@ -2238,17 +1896,13 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Accounting Dimensions", - "show_days": 1, - "show_seconds": 1 + "label": "Accounting Dimensions" }, { "fieldname": "dimension_col_break", "fieldtype": "Column Break", "hide_days": 1, - "hide_seconds": 1, - "show_days": 1, - "show_seconds": 1 + "hide_seconds": 1 }, { "default": "0", @@ -2256,9 +1910,7 @@ "fieldname": "is_consolidated", "fieldtype": "Check", "label": "Is Consolidated", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "default": "0", @@ -2268,18 +1920,14 @@ "hide_days": 1, "hide_seconds": 1, "label": "Is Internal Customer", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fetch_from": "company.tax_id", "fieldname": "company_tax_id", "fieldtype": "Data", "label": "Company Tax ID", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "depends_on": "eval:doc.is_internal_customer", @@ -2287,9 +1935,7 @@ "fieldname": "unrealized_profit_loss_account", "fieldtype": "Link", "label": "Unrealized Profit / Loss Account", - "options": "Account", - "show_days": 1, - "show_seconds": 1 + "options": "Account" }, { "depends_on": "eval:doc.is_internal_customer", @@ -2299,41 +1945,32 @@ "fieldtype": "Link", "label": "Represents Company", "options": "Company", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "column_break_55", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "depends_on": "eval: doc.is_internal_customer && doc.update_stock", "fieldname": "set_target_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Set Target Warehouse", - "options": "Warehouse", - "show_days": 1, - "show_seconds": 1 + "options": "Warehouse" }, { "default": "0", "fieldname": "is_debit_note", "fieldtype": "Check", - "label": "Is Debit Note", - "show_days": 1, - "show_seconds": 1 + "label": "Is Debit Note" }, { "default": "0", "depends_on": "grand_total", "fieldname": "disable_rounded_total", "fieldtype": "Check", - "label": "Disable Rounded Total", - "show_days": 1, - "show_seconds": 1 + "label": "Disable Rounded Total" }, { "fieldname": "additional_discount_account", @@ -2362,9 +1999,7 @@ "fieldtype": "Check", "hidden": 1, "label": "Ignore Default Payment Terms Template", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 } ], "icon": "fa fa-file-text", @@ -2377,7 +2012,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-08-07 23:02:20.445127", + "modified": "2021-08-17 19:00:32.230701", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From 44434ff70fdb68ae3dce8c9e837350e51533dcf0 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 18 Aug 2021 18:33:06 +0530 Subject: [PATCH 364/369] fix: date_unchanged calculation in "Update Items" (#26992) (#27011) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Branch corrected https://github.com/frappe/erpnext/pull/26058 ERPNext generates "Cannot set quantity less than delivered quantity" error even the delivered qty is zero when user clicks "Update Items". "date_unchanged" variable gets false value because of new_date is string. "getdate(new_date)" corrects the date comparison. ![ERPNext_PR](https://user-images.githubusercontent.com/710051/121928377-c0263180-cd48-11eb-8cd9-eda7dace09d6.gif) (cherry picked from commit d8a7abcd02f674bf6c4270a817ae9762a0b57140) Co-authored-by: Türker Tunalı --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 4c79a5cb5f1..26eb5bb337c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1863,7 +1863,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil qty_unchanged = prev_qty == new_qty uom_unchanged = prev_uom == new_uom conversion_factor_unchanged = prev_con_fac == new_con_fac - date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc + date_unchanged = prev_date == getdate(new_date) if prev_date and new_date else False # in case of delivery note etc if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged: continue From ecd6584c50718adb6bae1da3de0d66a2b7d3985b Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 19 Aug 2021 12:18:27 +0530 Subject: [PATCH 365/369] fix: assigning values to rows in sales register reports (#26546) * fix: assigning values to rows in sales register reports * fix: check for is_internal_customer for unrealized_profit_loss_account --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 1 + .../item_wise_sales_register/item_wise_sales_register.py | 3 ++- erpnext/accounts/report/sales_register/sales_register.py | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 7dfe77db55f..b65b101051d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1934,6 +1934,7 @@ "description": "Unrealized Profit / Loss account for intra-company transfers", "fieldname": "unrealized_profit_loss_account", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Unrealized Profit / Loss Account", "options": "Account" }, diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 2e794da8425..08065a204ef 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -76,7 +76,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum 'company': d.company, 'sales_order': d.sales_order, 'delivery_note': d.delivery_note, - 'income_account': d.unrealized_profit_loss_account or d.income_account, + 'income_account': d.unrealized_profit_loss_account if d.is_internal_customer == 1 else d.income_account, 'cost_center': d.cost_center, 'stock_qty': d.stock_qty, 'stock_uom': d.stock_uom @@ -380,6 +380,7 @@ def get_items(filters, additional_query_columns): `tabSales Invoice Item`.name, `tabSales Invoice Item`.parent, `tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to, `tabSales Invoice`.unrealized_profit_loss_account, + `tabSales Invoice`.is_internal_customer, `tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks, `tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total, `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description, diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index 909959323f7..f38bd78c0d2 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -84,7 +84,7 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No # Add amount in unrealized account for account in unrealized_profit_loss_accounts: row.update({ - frappe.scrub(account): flt(internal_invoice_map.get((inv.name, account))) + frappe.scrub(account+"_unrealized"): flt(internal_invoice_map.get((inv.name, account))) }) # net total @@ -258,6 +258,7 @@ def get_columns(invoice_list, additional_table_columns): unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account from `tabSales Invoice` where docstatus = 1 and name in (%s) + and is_internal_customer = 1 and ifnull(unrealized_profit_loss_account, '') != '' order by unrealized_profit_loss_account""" % ', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list)) @@ -284,7 +285,7 @@ def get_columns(invoice_list, additional_table_columns): for account in unrealized_profit_loss_accounts: unrealized_profit_loss_account_columns.append({ "label": account, - "fieldname": frappe.scrub(account), + "fieldname": frappe.scrub(account+"_unrealized"), "fieldtype": "Currency", "options": "currency", "width": 120 From 4551d7d6029b6f587f6c99d4f8df5519241c6a86 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 19 Aug 2021 13:41:10 +0530 Subject: [PATCH 366/369] chore: mass trailing whitespace and EOF fixes --- erpnext/.stylelintrc | 2 +- .../account_balance_timeline.js | 2 +- erpnext/accounts/deferred_revenue.py | 2 - .../accounting_dimension.js | 2 +- .../test_accounting_dimension.py | 2 - .../accounting_dimension_filter.js | 2 +- .../accounting_period/accounting_period.py | 2 +- .../accounts_settings/accounts_settings.js | 2 +- .../accounts_settings/accounts_settings.py | 4 +- .../regional/united_states.js | 2 +- erpnext/accounts/doctype/bank/bank.js | 2 +- erpnext/accounts/doctype/bank/bank.py | 2 +- .../bank_account/bank_account_dashboard.py | 2 +- .../doctype/bank_clearance/bank_clearance.js | 2 +- .../bank_clearance_detail.py | 2 +- .../doctype/bank_guarantee/bank_guarantee.py | 2 +- .../bank_transaction/bank_transaction.py | 1 - .../bank_transaction/bank_transaction_list.js | 2 +- .../bank_transaction_upload.py | 2 +- .../c_form_invoice_detail.py | 2 +- .../cash_flow_mapping/cash_flow_mapping.py | 2 - .../cashier_closing/cashier_closing.py | 2 +- .../cheque_print_template.js | 10 +-- .../doctype/cost_center/cost_center.py | 2 +- .../cost_center/cost_center_dashboard.py | 2 +- .../doctype/cost_center/cost_center_tree.js | 2 +- .../doctype/cost_center/test_cost_center.py | 3 - .../doctype/coupon_code/coupon_code.py | 2 +- .../doctype/coupon_code/test_coupon_code.py | 3 - .../discounted_invoice/discounted_invoice.py | 2 +- .../doctype/dunning/dunning_dashboard.py | 2 +- .../accounts/doctype/dunning/test_dunning.py | 2 +- .../exchange_rate_revaluation.js | 4 +- .../exchange_rate_revaluation.py | 4 +- .../doctype/finance_book/test_finance_book.py | 2 +- .../invoice_discounting_dashboard.py | 2 +- .../invoice_discounting_list.js | 2 +- .../doctype/journal_entry/regional/india.js | 2 +- .../journal_entry/test_journal_entry.py | 2 +- .../journal_entry_template.js | 2 +- .../mode_of_payment/mode_of_payment.js | 2 +- .../mode_of_payment/mode_of_payment.py | 1 - .../monthly_distribution.py | 2 +- .../monthly_distribution_dashboard.py | 2 +- .../opening_invoice_creation_tool.js | 2 +- .../opening_invoice_creation_tool.py | 1 - .../doctype/payment_entry/payment_entry.py | 6 +- .../payment_entry/payment_entry_list.js | 2 +- .../test_payment_against_purchase_invoice.js | 2 +- .../payment_entry/tests/test_payment_entry.js | 2 +- .../tests/test_payment_entry_write_off.js | 2 +- .../payment_gateway_account.py | 10 +-- .../doctype/payment_order/payment_order.js | 2 +- .../payment_order/payment_order_dashboard.py | 2 +- .../payment_order/test_payment_order.py | 2 +- .../payment_reconciliation.py | 2 +- .../payment_request/payment_request.py | 2 +- .../payment_request/test_payment_request.py | 2 +- .../doctype/payment_term/payment_term.js | 2 +- .../payment_terms_template.js | 2 +- .../payment_terms_template_dashboard.py | 2 +- .../period_closing_voucher.py | 4 +- .../test_period_closing_voucher.py | 2 +- .../pos_closing_entry/pos_closing_entry.js | 6 +- .../pos_invoice_merge_log.js | 6 +- .../pos_invoice_merge_log.py | 2 +- .../test_pos_invoice_merge_log.py | 1 - .../pos_opening_entry/pos_opening_entry.js | 2 +- .../pos_opening_entry/pos_opening_entry.py | 2 +- .../test_pos_opening_entry.py | 6 +- .../doctype/pos_settings/pos_settings.py | 2 +- .../doctype/pricing_rule/test_pricing_rule.py | 2 +- .../pricing_rule/tests/test_pricing_rule.js | 1 - .../process_deferred_accounting.py | 2 +- .../test_process_deferred_accounting.py | 2 +- .../process_statement_of_accounts.py | 2 +- .../promotional_scheme/promotional_scheme.js | 2 +- .../promotional_scheme_dashboard.py | 2 +- .../test_promotional_scheme.py | 12 +-- .../purchase_invoice/purchase_invoice.js | 2 +- .../purchase_invoice_dashboard.py | 2 +- .../purchase_invoice/purchase_invoice_list.js | 2 +- .../purchase_invoice/test_purchase_invoice.js | 1 - .../purchase_invoice_advance.py | 2 +- .../purchase_taxes_and_charges.py | 2 +- ...se_taxes_and_charges_template_dashboard.py | 2 +- ...est_purchase_taxes_and_charges_template.js | 1 - .../sales_invoice/regional/india_list.js | 8 +- .../doctype/sales_invoice/regional/italy.js | 2 +- .../sales_invoice/sales_invoice_dashboard.py | 2 +- .../sales_invoice/tests/test_sales_invoice.js | 1 - .../tests/test_sales_invoice_with_margin.js | 1 - .../tests/test_sales_invoice_with_payment.js | 1 - ...test_sales_invoice_with_payment_request.js | 1 - .../test_sales_invoice_with_serialize_item.js | 1 - .../sales_invoice_advance.py | 2 +- .../sales_taxes_and_charges.py | 2 +- ...es_taxes_and_charges_template_dashboard.py | 2 +- .../test_sales_taxes_and_charges_template.js | 1 - .../doctype/share_transfer/share_transfer.js | 2 +- .../doctype/share_transfer/share_transfer.py | 2 +- .../shipping_rule/test_shipping_rule.js | 1 - .../tests/test_shipping_rule_for_buying.js | 1 - .../shipping_rule_condition.py | 2 +- .../doctype/subscription/subscription_list.js | 2 +- .../doctype/subscription/test_subscription.py | 2 - .../subscription_plan/subscription_plan.js | 2 +- .../subscription_plan/subscription_plan.py | 2 +- erpnext/accounts/doctype/tax_rule/tax_rule.py | 2 +- .../tax_withholding_category.py | 4 +- .../test_tax_withholding_category.py | 2 +- .../bank_and_cash_payment_voucher.html | 2 +- .../gst_e_invoice/gst_e_invoice.html | 2 +- .../journal_auditing_voucher.html | 2 +- .../payment_receipt_voucher.html | 1 - .../purchase_auditing_voucher.html | 2 +- .../sales_auditing_voucher.html | 2 +- .../account_balance/test_account_balance.py | 5 -- .../accounts_payable/accounts_payable.js | 1 - .../accounts_payable_summary.js | 1 - .../accounts_payable_summary.py | 1 - .../accounts_receivable.js | 1 - .../test_accounts_receivable.py | 1 - .../accounts_receivable_summary.py | 2 +- .../report/balance_sheet/balance_sheet.py | 2 +- .../bank_clearance_summary.js | 2 +- .../bank_clearance_summary.py | 12 +-- .../bank_reconciliation_statement.js | 2 +- .../billed_items_to_be_received.py | 2 +- .../budget_variance_report.js | 1 - .../budget_variance_report.py | 5 +- .../accounts/report/cash_flow/cash_flow.html | 2 +- .../accounts/report/cash_flow/cash_flow.js | 2 +- .../consolidated_financial_statement.js | 6 +- .../delivered_items_to_be_billed.py | 2 +- .../report/general_ledger/general_ledger.js | 1 - .../report/general_ledger/general_ledger.py | 4 +- .../gross_and_net_profit_report.html | 2 +- .../gross_and_net_profit_report.py | 2 +- .../item_wise_sales_register.py | 4 - erpnext/accounts/report/non_billed_report.py | 2 +- .../payment_period_based_on_invoice_date.py | 2 +- .../report/pos_register/pos_register.py | 30 ++++---- .../profitability_analysis.html | 2 +- .../purchase_invoice_trends.js | 2 +- .../purchase_invoice_trends.py | 2 +- .../purchase_register/purchase_register.js | 2 +- .../received_items_to_be_billed.py | 2 +- .../sales_invoice_trends.js | 2 +- .../sales_payment_summary.js | 2 +- .../test_sales_payment_summary.py | 2 +- .../report/sales_register/sales_register.js | 1 - .../supplier_ledger_summary.py | 2 +- .../report/trial_balance/trial_balance.js | 3 - .../report/trial_balance/trial_balance.py | 2 +- .../trial_balance_for_party.py | 2 +- .../unpaid_expense_claim.py | 4 +- erpnext/agriculture/doctype/crop/crop.js | 2 +- .../doctype/crop/crop_dashboard.py | 2 +- erpnext/agriculture/doctype/crop/test_crop.js | 2 +- erpnext/agriculture/doctype/crop/test_crop.py | 2 +- .../doctype/crop_cycle/test_crop_cycle.js | 2 +- .../agriculture/doctype/disease/disease.py | 2 +- .../doctype/disease/test_disease.js | 1 - .../doctype/disease/test_disease.py | 2 +- .../doctype/fertilizer/fertilizer.py | 2 +- .../doctype/fertilizer/test_fertilizer.py | 2 +- .../doctype/plant_analysis/plant_analysis.py | 2 +- .../doctype/soil_analysis/soil_analysis.py | 2 +- .../doctype/soil_texture/test_soil_texture.py | 2 +- .../doctype/water_analysis/water_analysis.py | 2 +- erpnext/agriculture/setup.py | 2 +- erpnext/assets/dashboard_fixtures.py | 2 +- erpnext/assets/doctype/asset/asset.js | 4 +- .../assets/doctype/asset/asset_dashboard.py | 2 +- erpnext/assets/doctype/asset/asset_list.js | 2 +- erpnext/assets/doctype/asset/test_asset.py | 2 +- .../doctype/asset_category/asset_category.py | 12 +-- .../asset_category/test_asset_category.py | 8 +- .../asset_maintenance/asset_maintenance.js | 2 +- .../asset_maintenance/asset_maintenance.py | 2 +- .../test_asset_maintenance.py | 6 +- .../asset_maintenance_log.js | 2 +- .../doctype/asset_movement/asset_movement.js | 2 +- .../doctype/asset_movement/asset_movement.py | 16 ++-- .../doctype/asset_repair/asset_repair.js | 4 +- .../doctype/asset_repair/asset_repair.py | 10 +-- .../doctype/asset_repair/asset_repair_list.js | 1 - .../doctype/asset_repair/test_asset_repair.py | 8 +- .../test_asset_value_adjustment.py | 2 +- .../assets/doctype/location/location_tree.js | 2 +- .../fixed_asset_register.js | 2 +- .../fixed_asset_register.py | 4 +- .../buying_settings/buying_settings.js | 2 +- .../doctype/purchase_order/purchase_order.py | 2 +- .../doctype/purchase_order/regional/india.js | 2 +- .../purchase_order/test_purchase_order.py | 2 +- .../tests/test_purchase_order.js | 2 +- .../tests/test_purchase_order_get_items.js | 2 +- ...hase_order_with_discount_on_grand_total.js | 2 +- ..._purchase_order_with_item_wise_discount.js | 2 +- .../test_purchase_order_with_multi_uom.js | 2 +- .../test_purchase_order_with_shipping_rule.js | 2 +- ...t_purchase_order_with_taxes_and_charges.js | 2 +- .../purchase_order_item.py | 2 +- .../purchase_order_item_supplied.py | 2 +- .../purchase_receipt_item_supplied.py | 2 +- .../request_for_quotation.py | 2 +- .../request_for_quotation_dashboard.py | 2 +- .../tests/test_request_for_quotation.js | 2 +- .../test_request_for_quotation_for_status.js | 2 +- .../buying/doctype/supplier/regional/india.js | 2 +- .../buying/doctype/supplier/test_supplier.js | 2 +- .../supplier_item_group.py | 2 +- .../supplier_quotation/supplier_quotation.py | 2 +- .../tests/test_supplier_quotation.js | 2 +- ...pplier_quotation_for_item_wise_discount.js | 2 +- ...upplier_quotation_for_taxes_and_charges.js | 2 +- .../supplier_scorecard/supplier_scorecard.js | 2 - .../supplier_scorecard_dashboard.py | 2 +- .../test_supplier_scorecard.py | 1 - .../test_supplier_scorecard_criteria.py | 2 +- .../supplier_scorecard_period.py | 1 - .../supplier_scorecard_standing.py | 2 +- .../supplier_scorecard_variable.py | 2 +- .../test_supplier_scorecard_variable.py | 2 +- .../procurement_tracker.py | 2 +- .../test_procurement_tracker.py | 2 +- .../purchase_order_analysis.py | 1 - .../purchase_order_trends.js | 2 +- .../purchase_order_trends.py | 2 +- .../subcontract_order_summary.py | 2 +- .../test_subcontracted_item_to_be_received.py | 2 +- ...tracted_raw_materials_to_be_transferred.py | 2 +- .../supplier_quotation_comparison.html | 2 +- .../supplier_quotation_comparison.js | 2 +- .../supplier_quotation_comparison.py | 2 +- erpnext/buying/utils.py | 1 - erpnext/commands/__init__.py | 2 +- erpnext/controllers/accounts_controller.py | 22 +++--- erpnext/controllers/item_variant.py | 1 - erpnext/controllers/subcontracting.py | 2 +- .../crm/doctype/appointment/appointment.py | 1 - .../appointment_booking_settings.js | 2 +- erpnext/crm/doctype/contract/contract.js | 4 +- erpnext/crm/doctype/contract/contract_list.js | 2 +- .../contract_template/contract_template.py | 6 +- erpnext/crm/doctype/lead/lead.py | 2 +- erpnext/crm/doctype/lead/lead_dashboard.py | 2 +- erpnext/crm/doctype/lead/test_lead.py | 2 +- .../crm/doctype/opportunity/opportunity.js | 2 +- .../crm/doctype/opportunity/opportunity.py | 2 +- .../opportunity/opportunity_dashboard.py | 2 +- .../doctype/opportunity/test_opportunity.py | 2 +- .../social_media_post/social_media_post.js | 6 +- .../campaign_efficiency.js | 1 - .../campaign_efficiency.py | 2 +- .../lead_conversion_time.js | 2 - .../crm/report/lead_details/lead_details.js | 2 +- .../crm/report/lead_details/lead_details.py | 7 +- .../lost_opportunity/lost_opportunity.js | 2 +- .../lost_opportunity/lost_opportunity.py | 26 +++---- .../prospects_engaged_but_not_converted.py | 2 +- erpnext/demo/domains.py | 2 +- erpnext/demo/user/education.py | 12 +-- erpnext/domains/agriculture.py | 2 +- erpnext/domains/education.py | 2 +- erpnext/domains/manufacturing.py | 2 +- erpnext/domains/non_profit.py | 2 +- erpnext/domains/services.py | 2 +- .../doctype/academic_term/academic_term.py | 4 +- .../academic_term/academic_term_dashboard.py | 2 +- .../academic_term/test_academic_term.js | 2 +- .../doctype/academic_year/academic_year.js | 2 +- .../academic_year/academic_year_dashboard.py | 2 +- .../academic_year/test_academic_year.js | 2 +- erpnext/education/doctype/article/article.js | 2 +- erpnext/education/doctype/article/article.py | 2 +- .../assessment_criteria.py | 2 +- .../test_assessment_criteria.js | 2 +- .../test_assessment_criteria_group.js | 2 +- .../assessment_group_dashboard.py | 2 +- .../assessment_group/assessment_group_tree.js | 4 +- .../assessment_group/test_assessment_group.js | 2 +- .../assessment_plan/assessment_plan.js | 2 +- .../assessment_plan_dashboard.py | 2 +- .../assessment_result/assessment_result.js | 2 +- .../assessment_result/assessment_result.py | 4 - .../assessment_result_dashboard.py | 2 +- .../test_assessment_result.js | 2 +- .../test_assessment_result.py | 1 - .../assessment_result_tool.py | 2 +- .../test_assessment_result_tool.js | 2 +- erpnext/education/doctype/course/course.js | 2 +- erpnext/education/doctype/course/course.py | 2 +- .../doctype/course/course_dashboard.py | 2 +- .../education/doctype/course/test_course.js | 2 +- .../course_activity/course_activity.py | 2 +- .../course_enrollment_dashboard.py | 2 +- .../test_course_enrollment.py | 3 - .../course_schedule/course_schedule.js | 2 +- .../course_schedule/course_schedule.py | 13 ++-- .../course_schedule_dashboard.py | 2 +- .../course_schedule/test_course_schedule.py | 20 ++--- .../course_scheduling_tool.js | 2 +- .../education_settings/education_settings.py | 2 +- .../doctype/fee_schedule/fee_schedule.js | 2 +- .../fee_schedule/fee_schedule_dashboard.py | 2 +- .../doctype/fee_structure/fee_structure.js | 2 +- .../doctype/fee_structure/fee_structure.py | 6 +- .../fee_structure/fee_structure_dashboard.py | 2 +- erpnext/education/doctype/fees/fees.py | 2 +- erpnext/education/doctype/fees/fees_list.js | 2 +- .../doctype/grading_scale/grading_scale.py | 2 +- .../grading_scale/test_grading_scale.js | 2 +- .../doctype/guardian/test_guardian.js | 2 +- .../doctype/instructor/instructor.js | 2 +- .../instructor/instructor_dashboard.py | 2 +- erpnext/education/doctype/program/program.js | 4 +- erpnext/education/doctype/program/program.py | 2 +- .../doctype/program/program_dashboard.py | 2 +- .../education/doctype/program/test_program.js | 2 +- .../education/doctype/program/test_program.py | 2 +- .../program_enrollment/program_enrollment.js | 2 +- .../program_enrollment/program_enrollment.py | 1 - .../program_enrollment_dashboard.py | 2 +- .../test_program_enrollment.py | 2 +- .../education/doctype/question/question.py | 2 +- erpnext/education/doctype/quiz/quiz.js | 2 +- erpnext/education/doctype/quiz/quiz.py | 2 +- erpnext/education/doctype/room/room.js | 2 +- .../education/doctype/room/room_dashboard.py | 2 +- erpnext/education/doctype/student/student.js | 2 +- .../education/doctype/student/student_list.js | 2 +- .../education/doctype/student/test_student.py | 2 +- .../templates/student_admission_row.html | 2 +- .../test_student_admission.js | 2 +- .../student_applicant/student_applicant.js | 2 +- .../student_applicant/student_applicant.py | 2 +- .../student_applicant_list.js | 2 +- .../tests/test_student_applicant.js | 2 +- .../test_student_applicant_dummy_data.js | 2 +- .../tests/test_student_applicant_options.js | 2 +- .../student_attendance/student_attendance.js | 2 +- .../student_attendance_dashboard.py | 2 +- .../student_attendance_list.js | 2 +- .../test_student_attendance.js | 2 +- .../student_attendance_tool.py | 2 +- .../test_student_attendance_tool.js | 2 +- .../doctype/student_group/student_group.js | 2 +- .../doctype/student_group/student_group.py | 1 - .../student_group/student_group_dashboard.py | 2 +- .../student_group/test_student_group.js | 2 +- .../student_group_creation_tool.js | 2 +- .../student_group_creation_tool.py | 2 +- .../test_student_group_creation_tool.js | 2 +- .../student_group_student.py | 2 +- .../student_leave_application_dashboard.py | 2 +- .../test_student_leave_application.js | 2 +- .../test_student_leave_application.py | 2 +- .../doctype/student_log/test_student_log.js | 2 +- .../student_report_generation_tool.html | 74 +++++++++---------- erpnext/education/doctype/topic/topic.js | 2 +- erpnext/education/doctype/topic/topic.py | 2 +- .../program_wise_fee_collection.py | 1 - .../student_batch_wise_attendance.js | 2 +- .../student_batch_wise_attendance.py | 2 +- .../student_monthly_attendance_sheet.js | 2 +- .../student_applicant/student_applicant.js | 2 +- .../amazon_mws_settings.js | 1 - .../doctype/amazon_mws_settings/xml_utils.py | 2 +- .../exotel_settings/exotel_settings.py | 2 +- .../mpesa_settings/account_balance.html | 2 +- .../doctype/mpesa_settings/mpesa_connector.py | 2 +- .../mpesa_settings/mpesa_custom_fields.py | 2 +- .../doctype/mpesa_settings/mpesa_settings.py | 2 +- .../mpesa_settings/test_mpesa_settings.py | 2 +- .../doctype/plaid_settings/plaid_connector.py | 2 +- .../doctype/plaid_settings/plaid_settings.js | 2 +- .../doctype/plaid_settings/plaid_settings.py | 2 +- .../tally_migration/tally_migration.js | 2 +- .../woocommerce_settings.py | 4 +- .../stripe_integration.py | 2 +- erpnext/erpnext_integrations/utils.py | 2 +- .../department_wise_patient_appointments.js | 2 +- .../department_wise_patient_appointments.py | 2 +- .../appointment_type/appointment_type.js | 2 +- .../test_clinical_procedure.py | 2 +- .../clinical_procedure_template.js | 1 - .../clinical_procedure_template.py | 1 - .../doctype/exercise_type/exercise_type.py | 1 - .../doctype/fee_validity/fee_validity.py | 2 +- .../doctype/fee_validity/test_fee_validity.py | 2 +- .../healthcare_practitioner.js | 1 - .../test_healthcare_service_unit_type.py | 2 +- .../test_inpatient_medication_entry.py | 2 +- .../test_inpatient_medication_order.py | 1 - .../test_patient_appointment.py | 2 +- .../patient_assessment/patient_assessment.py | 3 - .../patient_encounter/patient_encounter.py | 2 +- .../patient_history_settings.py | 2 +- .../test_patient_medical_record.py | 2 +- .../therapy_plan_template.py | 2 +- .../therapy_session/therapy_session.js | 2 +- .../doctype/therapy_type/test_therapy_type.py | 2 +- .../doctype/vital_signs/vital_signs.py | 1 - .../page/patient_history/patient_history.html | 2 +- .../patient_progress/patient_progress.html | 2 +- .../page/patient_progress/patient_progress.js | 2 +- .../page/patient_progress/patient_progress.py | 1 - .../patient_progress_sidebar.html | 2 +- .../inpatient_medication_orders.py | 2 +- .../patient_appointment_analytics.py | 2 +- erpnext/healthcare/setup.py | 2 +- .../patient_registration.js | 2 +- .../hotels/doctype/hotel_room/hotel_room.py | 2 +- .../hotel_room_reservation_calendar.js | 2 +- .../hotel_room_occupancy.py | 2 +- erpnext/hr/doctype/appraisal/appraisal.js | 2 +- .../hr/doctype/appraisal/test_appraisal.js | 1 - .../doctype/appraisal_goal/appraisal_goal.py | 2 +- .../appraisal_template_dashboard.py | 2 +- .../test_appraisal_template.js | 1 - .../appraisal_template_goal.py | 2 +- .../doctype/attendance/attendance_calendar.js | 2 +- .../hr/doctype/attendance/test_attendance.js | 2 +- .../attendance_request_dashboard.py | 2 +- erpnext/hr/doctype/branch/branch.py | 2 +- erpnext/hr/doctype/branch/test_branch.js | 2 +- erpnext/hr/doctype/branch/test_branch.py | 2 +- .../test_daily_work_summary.js | 2 +- .../hr/doctype/department/department_tree.js | 2 +- .../hr/doctype/department/test_department.js | 2 +- .../hr/doctype/department/test_department.py | 2 +- erpnext/hr/doctype/designation/designation.py | 2 +- .../doctype/designation/test_designation.js | 2 +- .../doctype/designation/test_designation.py | 2 +- erpnext/hr/doctype/employee/employee.py | 2 +- erpnext/hr/doctype/employee/employee_tree.js | 2 +- erpnext/hr/doctype/employee/test_employee.js | 2 +- .../employee_advance/test_employee_advance.py | 2 +- .../employee_attendance_tool.css | 2 +- .../employee_attendance_tool.js | 2 - .../test_employee_attendance_tool.js | 2 +- .../employee_checkin/employee_checkin.py | 1 - .../employee_checkin/test_employee_checkin.py | 4 +- .../employee_education/employee_education.py | 2 +- .../employee_external_work_history.py | 2 +- .../employee_grade_dashboard.py | 2 +- .../employee_grievance/employee_grievance.py | 1 - .../employee_grievance_list.js | 2 +- .../test_employee_grievance.py | 1 - .../employee_group/test_employee_group.py | 2 +- .../employee_internal_work_history.py | 2 +- .../employee_onboarding.py | 1 - .../test_employee_onboarding.py | 2 +- .../employee_onboarding_template_dashboard.py | 2 +- .../employee_referral/employee_referral.py | 1 - .../employee_referral_dashboard.py | 2 +- .../employee_referral_list.js | 2 +- .../test_employee_referral.py | 2 +- .../test_employee_separation.py | 2 +- .../employee_separation_template_dashboard.py | 2 +- .../employment_type/employment_type.py | 2 +- .../employment_type/test_employment_type.js | 2 +- .../employment_type/test_employment_type.py | 2 +- .../hr/doctype/expense_claim/expense_claim.js | 2 +- .../expense_claim/expense_claim_dashboard.py | 2 +- .../expense_claim/test_expense_claim.js | 1 - .../expense_claim/test_expense_claim.py | 2 +- .../expense_claim_detail.py | 2 +- .../expense_claim_type/expense_claim_type.py | 2 +- .../test_expense_claim_type.js | 1 - erpnext/hr/doctype/holiday/holiday.py | 2 +- .../holiday_list/holiday_list_dashboard.py | 2 +- .../doctype/holiday_list/test_holiday_list.js | 2 +- erpnext/hr/doctype/hr_settings/hr_settings.js | 2 +- erpnext/hr/doctype/hr_settings/hr_settings.py | 1 - .../hr/doctype/job_applicant/job_applicant.js | 2 +- .../hr/doctype/job_applicant/job_applicant.py | 1 - .../job_applicant/job_applicant_dashboard.py | 2 +- .../job_applicant/test_job_applicant.js | 1 - .../hr/doctype/job_offer/test_job_offer.js | 2 +- .../hr/doctype/job_offer/test_job_offer.py | 2 +- .../job_opening/job_opening_dashboard.py | 2 +- .../templates/job_opening_row.html | 6 +- .../doctype/job_opening/test_job_opening.js | 1 - .../leave_allocation/leave_allocation.js | 2 +- .../leave_allocation_dashboard.py | 2 +- .../leave_allocation/test_leave_allocation.js | 2 +- .../leave_application_calendar.js | 2 +- .../leave_application_dashboard.py | 2 +- .../leave_application_email_template.html | 2 +- .../test_leave_application.js | 2 +- .../leave_block_list_dashboard.py | 2 +- .../leave_block_list/test_leave_block_list.js | 2 +- .../leave_block_list_allow.py | 2 +- .../leave_block_list_date.py | 2 +- .../leave_control_panel.js | 2 +- .../test_leave_control_panel.js | 2 +- .../leave_encashment/leave_encashment.py | 2 +- .../leave_ledger_entry/leave_ledger_entry.py | 2 +- .../leave_period/leave_period_dashboard.py | 2 +- .../doctype/leave_period/test_leave_period.py | 2 +- .../leave_policy/leave_policy_dashboard.py | 2 +- .../doctype/leave_policy/test_leave_policy.py | 2 +- .../leave_policy_assignment_dashboard.py | 2 +- .../leave_policy_assignment_list.js | 2 +- .../test_leave_policy_assignment.py | 2 - .../leave_type/leave_type_dashboard.py | 2 +- .../hr/doctype/leave_type/test_leave_type.js | 2 +- .../hr/doctype/leave_type/test_leave_type.py | 2 +- .../shift_assignment_calendar.js | 2 +- .../shift_assignment/test_shift_assignment.py | 2 +- .../hr/doctype/shift_request/shift_request.py | 2 +- .../shift_request/shift_request_dashboard.py | 2 +- .../shift_request/test_shift_request.py | 2 +- .../staffing_plan/staffing_plan_dashboard.py | 2 +- .../staffing_plan/test_staffing_plan.py | 2 +- .../training_event/test_training_event.py | 2 +- .../tests/test_training_event.js | 2 +- .../doctype/training_event/training_event.js | 1 - .../training_event_dashboard.py | 2 +- .../test_training_feedback.js | 1 - .../test_training_feedback.py | 2 +- .../training_feedback/training_feedback.js | 2 +- .../training_feedback/training_feedback.py | 1 - .../training_program/training_program.js | 2 +- .../training_program_dashboard.py | 2 +- .../training_result/training_result.js | 2 +- .../test_training_result.js | 1 - .../hr/doctype/vehicle/vehicle_dashboard.py | 2 +- .../doctype/vehicle_log/test_vehicle_log.py | 2 +- erpnext/hr/doctype/vehicle_log/vehicle_log.js | 1 - .../training_feedback/training_feedback.html | 2 +- .../training_scheduled.html | 2 +- .../organizational_chart.js | 2 +- .../organizational_chart.py | 2 +- erpnext/hr/page/team_updates/team_updates.py | 2 +- .../standard_appointment_letter.html | 2 +- .../daily_work_summary_replies.py | 2 +- .../employee_advance_summary.js | 1 - .../employee_analytics/employee_analytics.py | 1 - .../employee_birthday/employee_birthday.js | 4 +- .../recruitment_analytics.js | 2 +- .../vehicle_expenses/vehicle_expenses.js | 1 - erpnext/hr/utils.py | 2 +- .../job_application/job_application.js | 2 +- .../top_10_pledged_loan_securities.js | 2 +- .../top_10_pledged_loan_securities.py | 2 +- .../doctype/loan/loan_dashboard.py | 2 +- .../loan_management/doctype/loan/test_loan.py | 2 +- .../loan_application_dashboard.py | 2 +- .../loan_disbursement/loan_disbursement.py | 2 - .../loan_interest_accrual.py | 1 - .../doctype/loan_repayment/loan_repayment.py | 3 - .../loan_security/loan_security_dashboard.py | 2 +- .../loan_security_pledge.js | 2 +- .../loan_security_price.py | 9 --- .../loan_security_shortfall.py | 1 - .../loan_security_type_dashboard.py | 2 +- .../loan_security_unpledge.py | 5 -- .../doctype/loan_type/loan_type.py | 1 - .../doctype/loan_type/loan_type_dashboard.py | 2 +- .../doctype/loan_write_off/loan_write_off.py | 2 - .../process_loan_interest_accrual.py | 1 - ...process_loan_interest_accrual_dashboard.py | 2 +- ...ocess_loan_security_shortfall_dashboard.py | 2 +- erpnext/loan_management/loan_common.js | 2 +- .../applicant_wise_loan_security_exposure.py | 2 +- .../loan_interest_report.py | 2 +- .../loan_repayment_and_closure.py | 2 +- .../loan_security_exposure.py | 3 - .../maintenance_schedule.js | 9 +-- .../maintenance_schedule.py | 14 ++-- .../test_maintenance_schedule.py | 4 +- .../maintenance_visit/maintenance_visit.py | 4 +- .../doctype/blanket_order/blanket_order.js | 2 - .../doctype/blanket_order/blanket_order.py | 2 +- .../blanket_order/test_blanket_order.py | 2 +- .../doctype/bom/bom_item_preview.html | 2 +- erpnext/manufacturing/doctype/bom/bom_tree.js | 2 +- erpnext/manufacturing/doctype/bom/test_bom.js | 2 +- .../bom_explosion_item/bom_explosion_item.py | 2 +- .../doctype/bom_item/bom_item.py | 2 +- .../doctype/bom_operation/bom_operation.py | 2 +- .../bom_update_tool/bom_update_tool.js | 2 +- .../doctype/job_card/job_card.js | 2 +- .../doctype/job_card/job_card_list.js | 2 +- .../doctype/job_card/test_job_card.py | 2 +- .../manufacturing_settings.js | 2 +- .../manufacturing_settings.py | 2 +- .../doctype/operation/operation.js | 2 +- .../doctype/operation/test_operation.py | 2 +- .../production_plan_dashboard.py | 2 +- .../production_plan_item.py | 2 +- .../production_plan_sales_order.py | 2 +- .../doctype/routing/routing_dashboard.py | 2 +- .../work_order/work_order_dashboard.py | 2 +- .../work_order/work_order_preview.html | 2 +- .../work_order_item/work_order_item.py | 2 +- .../doctype/workstation/workstation.js | 2 +- .../bom_operations_time.py | 2 - .../bom_stock_report/bom_stock_report.html | 2 +- .../cost_of_poor_quality_report.py | 2 +- .../downtime_analysis/downtime_analysis.py | 2 +- .../exponential_smoothing_forecasting.py | 2 +- .../job_card_summary/job_card_summary.py | 2 +- .../production_analytics.py | 4 - .../quality_inspection_summary.py | 2 +- .../work_order_stock_report.py | 8 +- .../work_order_summary/work_order_summary.py | 2 +- .../doctype/chapter_member/chapter_member.py | 2 - .../non_profit/doctype/donation/donation.py | 1 - .../doctype/donation/donation_dashboard.py | 2 +- .../doctype/donation/test_donation.py | 2 +- erpnext/non_profit/doctype/donor/donor.py | 1 - .../grant_application/grant_application.py | 2 +- erpnext/non_profit/doctype/member/member.js | 2 +- .../doctype/membership/test_membership.py | 2 +- .../membership_type/membership_type.py | 2 +- .../non_profit_settings.py | 2 +- .../grant_application/grant_application.js | 2 +- .../grant_application/grant_application.py | 2 - ...ry_settings_to_daily_work_summary_group.py | 2 +- .../v10_0/rename_offer_letter_to_job_offer.py | 2 +- .../rename_price_to_rate_in_pricing_rule.py | 2 +- .../add_default_email_template_for_leave.py | 1 - .../add_expense_claim_default_account.py | 2 +- .../add_healthcare_service_unit_tree_root.py | 1 - .../v11_0/add_index_on_nestedset_doctypes.py | 2 +- erpnext/patches/v11_0/add_market_segments.py | 2 +- erpnext/patches/v11_0/add_sales_stages.py | 2 +- ...eck_buying_selling_in_currency_exchange.py | 2 +- .../create_salary_structure_assignments.py | 2 +- .../v11_0/drop_column_max_days_allowed.py | 2 +- .../v11_0/ewaybill_fields_gst_india.py | 2 +- erpnext/patches/v11_0/hr_ux_cleanups.py | 1 - ..._asset_finance_book_against_old_entries.py | 2 +- .../v11_0/make_location_from_warehouse.py | 1 - ...efaults_to_child_table_for_multicompany.py | 2 +- .../move_leave_approvers_from_employee.py | 2 +- .../patches/v11_0/refactor_autoname_naming.py | 2 +- .../patches/v11_0/refactor_naming_series.py | 2 +- .../v11_0/rename_asset_adjustment_doctype.py | 2 +- erpnext/patches/v11_0/rename_bom_wo_fields.py | 2 +- .../patches/v11_0/rename_health_insurance.py | 2 +- .../rename_overproduction_percent_field.py | 2 +- .../renamed_from_to_fields_in_project.py | 2 +- .../patches/v11_0/set_missing_gst_hsn_code.py | 2 +- .../v11_0/set_salary_component_properties.py | 2 +- .../set_user_permissions_for_department.py | 4 +- ...ip_user_permission_check_for_department.py | 2 +- .../update_account_type_in_party_type.py | 2 +- .../update_allow_transfer_for_manufacture.py | 2 +- ...e_backflush_subcontract_rm_based_on_bom.py | 2 +- .../v11_0/update_brand_in_item_price.py | 2 +- .../v11_0/update_department_lft_rgt.py | 2 +- erpnext/patches/v11_1/delete_bom_browser.py | 2 +- .../patches/v11_1/make_job_card_time_logs.py | 2 +- .../move_customer_lead_to_dynamic_column.py | 2 +- .../patches/v11_1/rename_depends_on_lwp.py | 2 +- .../v11_1/renamed_delayed_item_report.py | 2 +- ...s_for_material_request_type_manufacture.py | 2 +- erpnext/patches/v11_1/set_variant_based_on.py | 2 +- .../v11_1/update_bank_transaction_status.py | 2 +- ...pdate_default_supplier_in_item_defaults.py | 2 +- .../v11_1/woocommerce_set_creation_user.py | 2 +- .../add_company_link_to_einvoice_settings.py | 2 +- ...default_buying_selling_terms_in_company.py | 2 +- ...ocument_type_field_for_italy_einvoicing.py | 2 +- .../v12_0/add_einvoice_status_field.py | 8 +- ...add_einvoice_summary_report_permissions.py | 2 +- .../v12_0/add_eway_bill_in_delivery_note.py | 2 +- .../v12_0/add_ewaybill_validity_field.py | 2 +- .../add_export_type_field_in_party_master.py | 2 - .../add_gst_category_in_delivery_note.py | 2 +- .../v12_0/add_item_name_in_work_orders.py | 2 +- .../add_permission_in_lower_deduction.py | 2 +- ...counting_dimensions_in_missing_doctypes.py | 2 +- .../create_default_energy_point_rules.py | 2 +- .../create_irs_1099_field_united_states.py | 2 +- .../create_itc_reversal_custom_fields.py | 2 +- .../v12_0/create_taxable_value_field.py | 2 +- .../v12_0/delete_priority_property_setter.py | 2 +- .../v12_0/fix_quotation_expired_status.py | 12 +-- ...arget_distribution_from_parent_to_child.py | 2 +- .../v12_0/recalculate_requested_qty_in_bin.py | 2 +- .../remove_bank_remittance_custom_fields.py | 2 +- .../remove_denied_leaves_from_leave_ledger.py | 2 +- .../remove_duplicate_leave_ledger_entries.py | 2 +- .../v12_0/rename_account_type_doctype.py | 2 +- ..._account_field_in_journal_entry_account.py | 2 +- .../v12_0/rename_lost_reason_detail.py | 2 +- .../v12_0/rename_pos_closing_doctype.py | 6 +- .../patches/v12_0/rename_tolerance_fields.py | 2 +- ...counting_with_accounts_in_home_settings.py | 2 +- ...ock_ledger_entries_for_target_warehouse.py | 3 - ...eferred_accounting_in_accounts_settings.py | 2 +- ..._center_in_child_table_of_expense_claim.py | 2 +- .../set_cwip_and_delete_asset_settings.py | 4 +- .../v12_0/set_default_homepage_type.py | 2 +- .../v12_0/set_default_payroll_based_on.py | 2 +- ...se_account_in_landed_cost_voucher_taxes.py | 2 +- erpnext/patches/v12_0/set_gst_category.py | 2 - ...ian_import_supplier_invoice_permissions.py | 2 +- erpnext/patches/v12_0/set_multi_uom_in_rfq.py | 2 +- .../patches/v12_0/set_payment_entry_status.py | 2 +- .../patches/v12_0/set_priority_for_support.py | 2 +- ...qty_field_in_sales_order_for_work_order.py | 2 +- .../set_production_capacity_in_workstation.py | 2 +- erpnext/patches/v12_0/set_quotation_status.py | 2 +- .../v12_0/set_updated_purpose_in_pick_list.py | 2 +- .../patches/v12_0/setup_einvoice_fields.py | 4 +- .../patches/v12_0/stock_entry_enhancements.py | 2 +- .../patches/v12_0/unhide_cost_center_field.py | 2 +- ...te_appointment_reminder_scheduler_entry.py | 2 +- erpnext/patches/v12_0/update_bom_in_so_mr.py | 2 +- ...e_end_date_and_status_in_email_campaign.py | 2 +- .../v12_0/update_ewaybill_field_position.py | 2 +- erpnext/patches/v12_0/update_gst_category.py | 2 +- .../update_healthcare_refactored_changes.py | 2 +- .../v12_0/update_is_cancelled_field.py | 2 +- .../v12_0/update_item_tax_template_company.py | 2 +- ...r_fields_in_acc_dimension_custom_fields.py | 2 +- .../update_price_list_currency_in_bom.py | 2 +- .../update_state_code_for_daman_and_diu.py | 2 +- .../v12_0/update_uom_conversion_factor.py | 2 +- erpnext/patches/v13_0/add_doctype_to_sla.py | 2 +- .../add_naming_series_to_old_projects.py | 1 - .../v13_0/change_default_pos_print_format.py | 2 +- .../v13_0/check_is_income_tax_component.py | 2 +- .../convert_qi_parameter_to_link_field.py | 2 +- ...are_custom_fields_in_stock_entry_detail.py | 2 +- ..._based_on_employee_current_leave_policy.py | 3 - .../v13_0/create_uae_pos_invoice_fields.py | 2 +- .../v13_0/delete_old_purchase_reports.py | 2 +- .../patches/v13_0/delete_old_sales_reports.py | 2 +- .../patches/v13_0/delete_orphaned_tables.py | 8 +- .../delete_report_requested_items_to_order.py | 2 +- .../v13_0/drop_razorpay_payload_column.py | 2 +- .../fix_non_unique_represents_company.py | 2 +- .../germany_fill_debtor_creditor_number.py | 2 +- .../item_reposting_for_incorrect_sl_and_gl.py | 2 +- .../loyalty_points_entry_for_pos_invoice.py | 4 +- .../v13_0/make_non_standard_user_type.py | 2 +- .../v13_0/move_branch_code_to_bank_account.py | 2 +- ...itional_salary_encashment_and_incentive.py | 1 - .../rename_issue_status_hold_to_on_hold.py | 2 +- ...bership_settings_to_non_profit_settings.py | 2 +- ...eplace_pos_page_with_point_of_sale_page.py | 2 +- .../v13_0/replace_pos_payment_mode_table.py | 2 +- .../set_company_in_leave_ledger_entry.py | 2 +- ...ment_channel_in_payment_gateway_account.py | 2 +- .../v13_0/set_pos_closing_as_failed.py | 2 +- .../v13_0/set_training_event_attendance.py | 2 +- erpnext/patches/v13_0/set_youtube_video_id.py | 2 +- ..._custom_roles_for_some_regional_reports.py | 2 +- ..._history_settings_for_standard_doctypes.py | 2 +- .../patches/v13_0/stock_entry_enhancements.py | 8 +- .../update_actual_start_and_end_date_in_wo.py | 2 +- ...update_amt_in_work_order_required_items.py | 1 - .../patches/v13_0/update_deferred_settings.py | 2 +- .../v13_0/update_export_type_for_gst.py | 6 +- .../patches/v13_0/update_job_card_details.py | 2 +- .../v13_0/update_project_template_tasks.py | 2 +- ...date_reason_for_resignation_in_employee.py | 1 - .../v13_0/update_returned_qty_in_pr_dn.py | 2 +- erpnext/patches/v13_0/update_subscription.py | 2 +- ...date_subscription_status_in_memberships.py | 2 +- .../patches/v13_0/update_tds_check_field.py | 2 +- .../patches/v13_0/update_timesheet_changes.py | 2 +- .../updates_for_multi_currency_payroll.py | 4 +- ...oles_from_gst_report_non_indian_account.py | 2 +- erpnext/patches/v8_1/setup_gst_india.py | 2 +- .../test_additional_salary.py | 2 +- .../employee_benefit_application.py | 2 +- .../employee_tax_exemption_sub_category.py | 2 +- erpnext/payroll/doctype/gratuity/gratuity.js | 2 +- erpnext/payroll/doctype/gratuity/gratuity.py | 1 - .../doctype/gratuity/gratuity_dashboard.py | 2 +- .../doctype/gratuity_rule/gratuity_rule.js | 2 +- .../gratuity_rule/gratuity_rule_dashboard.py | 2 +- .../payroll_entry/payroll_entry_dashboard.py | 2 +- .../payroll_period_dashboard.py | 2 +- .../doctype/salary_slip/test_salary_slip.js | 2 +- .../condition_and_formula_help.html | 2 +- .../salary_structure/salary_structure.py | 1 - .../salary_structure_dashboard.py | 2 +- .../salary_structure_assignment.py | 4 +- erpnext/payroll/notification/as | 2 +- .../report/bank_remittance/bank_remittance.js | 1 - .../income_tax_deductions.js | 2 +- .../salary_payments_based_on_payment_mode.js | 2 +- erpnext/portal/doctype/homepage/homepage.py | 1 - .../test_product_configurator.py | 2 +- .../doctype/activity_cost/activity_cost.js | 2 +- .../doctype/activity_cost/activity_cost.py | 2 +- .../activity_cost/test_activity_cost.py | 2 +- .../doctype/activity_type/activity_type.py | 2 +- .../activity_type/test_activity_type.py | 2 +- .../doctype/project/project_dashboard.html | 2 +- .../project_template/project_template.py | 2 +- .../project_template/test_project_template.py | 2 +- .../doctype/project_type/project_type.js | 2 +- .../doctype/project_type/project_type.py | 2 +- .../doctype/project_update/project_update.py | 2 +- .../project_update/test_project_update.py | 2 +- erpnext/projects/doctype/task/task_tree.js | 2 +- .../projects/doctype/timesheet/timesheet.css | 2 +- .../projects/doctype/timesheet/timesheet.js | 2 +- .../projects/doctype/timesheet/timesheet.py | 6 +- .../doctype/timesheet/timesheet_calendar.js | 4 +- .../doctype/timesheet/timesheet_dashboard.py | 2 +- .../doctype/timesheet/timesheet_list.js | 6 +- erpnext/projects/report/billing_summary.py | 2 +- .../daily_timesheet_summary.py | 6 +- .../test_delayed_tasks_summary.py | 6 +- .../employee_billing_summary.py | 2 +- .../test_employee_util.py | 2 +- .../project_billing_summary.py | 2 +- .../project_profitability.py | 2 +- erpnext/projects/web_form/tasks/tasks.js | 2 +- erpnext/projects/web_form/tasks/tasks.py | 2 +- erpnext/public/images/erpnext-favicon.svg | 2 +- erpnext/public/images/erpnext-logo.svg | 2 +- erpnext/public/images/pos.svg | 2 +- .../js/education/assessment_result_tool.html | 2 +- .../public/js/education/student_button.html | 8 +- erpnext/public/js/erpnext.bundle.js | 1 - erpnext/public/js/financial_statements.js | 2 - erpnext/public/js/hierarchy-chart.bundle.js | 2 +- .../hierarchy_chart_desktop.js | 2 +- .../hierarchy_chart/hierarchy_chart_mobile.js | 2 +- .../public/js/hub/components/ReviewArea.vue | 2 +- .../js/hub/components/ReviewTimelineItem.vue | 1 - erpnext/public/js/hub/pages/FeaturedItems.vue | 2 +- erpnext/public/js/hub/pages/Publish.vue | 2 +- erpnext/public/js/hub/pages/Seller.vue | 4 +- erpnext/public/js/hub/vue-plugins.js | 2 +- erpnext/public/js/leaflet/leaflet.draw.js | 2 +- erpnext/public/js/leaflet/leaflet.js | 2 +- erpnext/public/js/projects/timer.js | 2 +- erpnext/public/js/setup_wizard.js | 2 +- erpnext/public/js/stock_analytics.js | 1 - .../public/js/templates/item_quick_entry.html | 2 +- .../public/js/templates/item_selector.html | 2 +- erpnext/public/js/templates/node_card.html | 2 +- .../public/js/utils/dimension_tree_filter.js | 2 +- erpnext/public/scss/hierarchy_chart.scss | 2 +- erpnext/public/scss/shopping_cart.scss | 1 - erpnext/public/scss/website.scss | 2 +- .../doctype/quality_action/quality_action.js | 2 +- .../doctype/quality_action/quality_action.py | 2 +- .../quality_action/test_quality_action.py | 2 +- .../quality_feedback/quality_feedback.py | 1 - .../test_quality_feedback_template.py | 2 +- .../doctype/quality_goal/quality_goal.py | 2 +- .../doctype/quality_goal/test_quality_goal.py | 2 +- .../quality_meeting/quality_meeting.py | 2 +- .../quality_meeting/quality_meeting_list.js | 2 +- .../quality_meeting/test_quality_meeting.py | 2 +- .../quality_procedure/quality_procedure.js | 2 +- .../quality_procedure/quality_procedure.py | 2 +- .../quality_procedure_tree.js | 2 +- .../test_quality_procedure.py | 2 +- .../doctype/quality_review/quality_review.js | 2 +- .../doctype/quality_review/quality_review.py | 2 +- .../quality_review/quality_review_list.js | 2 +- .../quality_review/test_quality_review.py | 2 +- erpnext/regional/address_template/setup.py | 2 +- .../address_template/templates/germany.html | 2 +- .../e_invoice_settings/e_invoice_settings.py | 1 - .../doctype/gst_hsn_code/gst_hsn_code.js | 2 +- .../doctype/gst_hsn_code/gst_hsn_code.py | 2 +- .../gstr_3b_report/gstr_3b_report.html | 2 +- .../import_supplier_invoice.js | 2 +- .../lower_deduction_certificate.py | 4 +- .../test_tax_exemption_80g_certificate.py | 2 +- .../germany/utils/datev/datev_constants.py | 2 +- erpnext/regional/india/e_invoice/einvoice.js | 2 +- erpnext/regional/india/e_invoice/utils.py | 2 +- erpnext/regional/india/taxes.js | 1 - erpnext/regional/india/utils.py | 6 +- erpnext/regional/italy/__init__.py | 2 +- erpnext/regional/report/datev/datev.py | 4 +- .../e_invoice_summary/e_invoice_summary.py | 40 +++++----- .../electronic_invoice_register.js | 2 +- .../regional/report/eway_bill/eway_bill.py | 2 +- .../gst_purchase_register.js | 2 +- .../gst_purchase_register.py | 1 - .../hsn_wise_summary_of_outward_supplies.py | 2 - .../india_gst_common/india_gst_common.js | 2 +- erpnext/regional/report/irs_1099/irs_1099.py | 2 +- .../professional_tax_deductions.js | 2 +- .../professional_tax_deductions.py | 2 +- .../provident_fund_deductions.js | 2 +- .../provident_fund_deductions.py | 2 +- .../report/uae_vat_201/uae_vat_201.html | 2 +- .../vat_audit_report/vat_audit_report.py | 2 +- erpnext/regional/south_africa/setup.py | 6 +- erpnext/regional/turkey/setup.py | 2 +- .../restaurant/restaurant_dashboard.py | 2 +- .../doctype/restaurant/test_restaurant.js | 2 +- .../restaurant_menu/restaurant_menu.py | 2 - erpnext/selling/doctype/customer/customer.js | 1 - .../doctype/customer/regional/india.js | 2 +- .../doctype/industry_type/industry_type.js | 8 +- .../doctype/industry_type/industry_type.py | 2 +- .../industry_type/test_industry_type.py | 2 +- .../installation_note_item.py | 2 +- .../product_bundle/test_product_bundle.js | 1 - .../doctype/quotation/quotation_dashboard.py | 2 +- ..._quotation_with_discount_on_grand_total.js | 1 - .../test_quotation_with_item_wise_discount.js | 1 - .../tests/test_quotation_with_margin.js | 1 - .../tests/test_quotation_with_multi_uom.js | 1 - .../test_quotation_with_taxes_and_charges.js | 1 - .../sales_order/sales_order_dashboard.py | 2 +- .../doctype/sales_order/test_sales_order.py | 4 +- .../tests/test_sales_order_with_margin.js | 1 - ...sales_order_with_multiple_delivery_date.js | 2 +- .../sales_order_item/sales_order_item.py | 2 +- .../selling/doctype/sales_team/sales_team.py | 2 +- .../selling_settings/selling_settings.js | 2 +- .../selling/doctype/sms_center/sms_center.py | 1 - .../page/point_of_sale/point_of_sale.py | 2 +- .../page/point_of_sale/pos_controller.js | 5 +- .../page/point_of_sale/pos_item_cart.js | 2 +- .../page/point_of_sale/pos_item_details.js | 6 +- .../page/point_of_sale/pos_item_selector.js | 2 +- .../page/point_of_sale/pos_number_pad.js | 2 +- .../page/point_of_sale/pos_past_order_list.js | 2 +- .../point_of_sale/pos_past_order_summary.js | 2 +- .../page/sales_funnel/sales_funnel.css | 2 +- .../selling/page/sales_funnel/sales_funnel.py | 4 +- .../address_and_contacts.py | 2 +- .../available_stock_for_packing_items.py | 2 +- .../customer_acquisition_and_loyalty.js | 2 +- .../item_wise_sales_history.js | 2 +- .../item_wise_sales_history.py | 2 +- .../quotation_trends/quotation_trends.js | 1 - .../report/sales_analytics/sales_analytics.js | 2 - .../sales_order_analysis.py | 2 +- .../sales_order_trends/sales_order_trends.js | 2 +- .../sales_partner_commission_summary.py | 2 +- .../item_group_wise_sales_target_variance.py | 2 +- ...ner_target_variance_based_on_item_group.js | 4 +- ...ner_target_variance_based_on_item_group.py | 1 - .../sales_partner_transaction_summary.py | 2 +- .../sales_person_commission_summary.py | 6 +- ...son_target_variance_based_on_item_group.js | 4 +- ...son_target_variance_based_on_item_group.py | 2 +- .../sales_person_wise_transaction_summary.js | 2 +- ...ory_target_variance_based_on_item_group.js | 4 +- erpnext/selling/sales_common.js | 4 +- erpnext/setup/default_energy_point_rules.py | 1 - erpnext/setup/default_success_action.py | 1 - erpnext/setup/doctype/brand/brand.js | 8 +- erpnext/setup/doctype/brand/brand.py | 2 +- erpnext/setup/doctype/brand/test_brand.py | 2 +- erpnext/setup/doctype/company/company.js | 1 - erpnext/setup/doctype/company/company.py | 2 +- .../doctype/company/company_dashboard.py | 2 +- erpnext/setup/doctype/company/company_tree.js | 2 +- erpnext/setup/doctype/company/test_company.py | 1 - .../doctype/company/tests/test_company.js | 2 +- .../company/tests/test_company_production.js | 2 +- .../doctype/customer_group/customer_group.py | 2 +- .../customer_group/customer_group_tree.js | 2 +- .../customer_group/test_customer_group.py | 2 +- .../doctype/email_digest/email_digest.js | 2 +- erpnext/setup/doctype/email_digest/quotes.py | 1 - .../email_digest/templates/default.html | 8 +- .../doctype/item_group/item_group_tree.js | 2 +- .../doctype/print_heading/print_heading.js | 8 +- .../doctype/print_heading/print_heading.py | 2 +- .../print_heading/test_print_heading.py | 2 +- .../quotation_lost_reason.js | 8 +- .../quotation_lost_reason.py | 2 +- .../test_quotation_lost_reason.py | 2 +- .../sales_person/sales_person_dashboard.py | 2 +- .../doctype/sales_person/sales_person_tree.js | 2 +- .../supplier_group/supplier_group_tree.js | 2 +- .../doctype/target_detail/target_detail.py | 2 +- .../terms_and_conditions.js | 8 +- .../terms_and_conditions.py | 4 +- erpnext/setup/doctype/territory/territory.js | 2 +- erpnext/setup/doctype/territory/territory.py | 2 +- .../setup/doctype/territory/territory_tree.js | 2 +- .../test_transaction_deletion_record.py | 6 +- .../transaction_deletion_record.js | 8 +- .../transaction_deletion_record_list.js | 2 +- erpnext/setup/doctype/uom/uom.js | 8 +- erpnext/setup/doctype/uom/uom.py | 2 +- .../website_item_group/website_item_group.py | 2 +- .../setup_wizard/operations/sample_data.py | 2 +- .../setup_wizard/operations/taxes_setup.py | 4 +- .../shopping_cart_settings.py | 2 +- .../test_shopping_cart_settings.py | 4 +- erpnext/shopping_cart/product_info.py | 2 +- erpnext/shopping_cart/search.py | 2 +- erpnext/shopping_cart/utils.py | 2 +- .../web_template/hero_slider/hero_slider.html | 2 +- .../item_card_group/item_card_group.html | 2 +- erpnext/startup/filters.py | 2 +- erpnext/startup/leaderboard.py | 2 +- erpnext/stock/dashboard/item_dashboard.html | 2 +- .../dashboard/warehouse_capacity_dashboard.py | 2 +- .../warehouse_wise_stock_value.js | 2 +- .../warehouse_wise_stock_value.py | 2 +- erpnext/stock/doctype/batch/test_batch.js | 1 - .../delivery_note/delivery_note_dashboard.py | 2 +- .../doctype/delivery_note/regional/india.js | 1 - .../delivery_note/test_delivery_note.js | 1 - .../delivery_note/test_delivery_note.py | 2 +- .../test_delivery_note_with_margin.js | 1 - .../delivery_note_item/delivery_note_item.py | 2 +- .../doctype/delivery_trip/delivery_trip.py | 2 +- .../dispatch_notification_template.html | 2 +- erpnext/stock/doctype/item/item.py | 2 +- erpnext/stock/doctype/item/regional/india.js | 2 +- .../stock/doctype/item/templates/item.html | 2 +- .../doctype/item/templates/item_row.html | 2 +- erpnext/stock/doctype/item/tests/test_item.js | 2 +- .../item_attribute/test_item_attribute.py | 1 - .../item_customer_detail.py | 2 +- .../item_manufacturer/item_manufacturer.py | 2 +- .../item_quality_inspection_parameter.py | 2 +- .../doctype/item_reorder/item_reorder.py | 2 +- .../doctype/item_supplier/item_supplier.py | 2 +- erpnext/stock/doctype/item_tax/item_tax.py | 2 +- .../item_website_specification.py | 2 +- .../landed_cost_item/landed_cost_item.py | 2 +- .../landed_cost_purchase_receipt.py | 2 +- .../landed_cost_taxes_and_charges.py | 2 +- .../material_request_dashboard.py | 2 +- .../tests/test_material_request.js | 1 - .../tests/test_material_request_from_bom.js | 1 - .../test_material_request_type_manufacture.js | 1 - ...st_material_request_type_material_issue.js | 1 - ...material_request_type_material_transfer.js | 1 - .../material_request_item.py | 2 +- .../packing_slip_item/packing_slip_item.py | 2 +- erpnext/stock/doctype/pick_list/pick_list.js | 2 +- .../doctype/pick_list/pick_list_dashboard.py | 2 +- .../stock/doctype/price_list/price_list.css | 2 +- .../stock/doctype/price_list/price_list.js | 2 +- .../stock/doctype/price_list/price_list.py | 2 +- .../doctype/price_list/test_price_list.py | 2 +- .../doctype/price_list/test_price_list_uom.js | 2 +- .../purchase_receipt/regional/india.js | 2 +- .../doctype/putaway_rule/putaway_rule.py | 2 +- .../doctype/putaway_rule/test_putaway_rule.py | 2 +- .../quality_inspection/quality_inspection.js | 2 +- .../quality_inspection_reading.py | 2 +- .../quality_inspection_template.py | 2 +- .../stock/doctype/serial_no/test_serial_no.py | 2 +- erpnext/stock/doctype/shipment/shipment.js | 8 +- .../stock/doctype/shipment/shipment_list.js | 2 +- .../stock/doctype/shipment/test_shipment.py | 6 +- .../test_stock_entry_for_material_issue.js | 1 - ..._for_material_issue_with_serialize_item.js | 1 - .../test_stock_entry_for_material_receipt.js | 1 - ...for_material_receipt_for_serialize_item.js | 1 - .../test_stock_entry_for_material_transfer.js | 1 - ...y_for_material_transfer_for_manufacture.js | 1 - .../tests/test_stock_entry_for_repack.js | 1 - .../tests/test_stock_entry_for_subcontract.js | 1 - .../stock_entry_detail/stock_entry_detail.py | 2 +- .../test_stock_reconciliation.js | 1 - .../test_stock_reconciliation.py | 1 - .../uom_conversion_detail.py | 2 +- .../stock/doctype/warehouse/test_warehouse.js | 2 +- .../stock/doctype/warehouse/test_warehouse.py | 2 +- erpnext/stock/doctype/warehouse/warehouse.js | 8 +- .../stock/doctype/warehouse/warehouse_tree.js | 2 +- .../stock/landed_taxes_and_charges_common.js | 1 - .../warehouse_capacity_summary.html | 2 +- .../warehouse_capacity_summary_header.html | 2 +- .../batch_item_expiry_status.py | 4 +- .../cogs_by_item_group/cogs_by_item_group.py | 4 +- .../delayed_item_report.py | 2 +- .../delayed_order_report.py | 2 +- .../delivery_note_trends.js | 1 - .../delivery_note_trends.py | 2 +- ...incorrect_balance_qty_after_transaction.py | 2 +- .../incorrect_serial_no_valuation.py | 2 +- .../incorrect_stock_value_report.py | 2 +- .../item_price_stock/item_price_stock.js | 2 +- .../item_shortage_report.py | 2 - .../itemwise_recommended_reorder_level.js | 2 +- .../purchase_receipt_trends.js | 1 - .../purchase_receipt_trends.py | 2 +- .../serial_no_ledger/serial_no_ledger.py | 1 - .../stock/report/stock_ageing/stock_ageing.js | 2 +- .../report/stock_analytics/stock_analytics.py | 4 - .../stock_and_account_value_comparison.py | 2 +- .../stock_projected_qty.py | 2 +- .../stock_qty_vs_serial_no_count.py | 8 +- .../supplier_wise_sales_analytics.js | 2 +- .../total_stock_summary.js | 2 +- erpnext/support/doctype/issue/issue.js | 2 +- erpnext/support/doctype/issue/test_issue.py | 10 +-- .../doctype/issue_priority/issue_priority.py | 2 +- .../issue_priority/test_issue_priority.py | 2 +- .../service_level_agreement.py | 2 +- .../service_level_agreement_dashboard.py | 2 +- .../test_service_level_agreement.py | 2 +- .../first_response_time_for_issues.py | 2 +- .../report/issue_analytics/issue_analytics.py | 2 +- .../issue_analytics/test_issue_analytics.py | 4 +- .../report/issue_summary/issue_summary.py | 1 - erpnext/support/web_form/issues/issues.js | 2 +- .../telephony/doctype/call_log/call_log.py | 1 - .../incoming_call_settings.js | 1 - .../templates/emails/birthday_reminder.html | 2 +- .../emails/daily_project_summary.html | 2 +- .../templates/emails/daily_work_summary.html | 2 +- .../emails/request_for_quotation.html | 2 +- erpnext/templates/emails/training_event.html | 2 +- .../templates/generators/item/item_inquiry.js | 2 +- .../generators/item/item_specifications.html | 2 +- erpnext/templates/generators/item_group.html | 2 +- erpnext/templates/generators/job_opening.html | 6 +- .../generators/student_admission.html | 2 +- .../includes/cart/address_picker_card.html | 2 +- .../includes/cart/cart_address_picker.html | 1 - .../includes/cart/cart_items_dropdown.html | 2 +- erpnext/templates/includes/course/macros.html | 2 +- .../includes/itemised_tax_breakup.html | 2 +- erpnext/templates/includes/macros.html | 2 +- .../includes/navbar/navbar_items.html | 2 +- .../includes/order/order_macros.html | 2 +- erpnext/templates/includes/projects.css | 2 +- .../includes/projects/project_search_box.html | 2 +- .../templates/includes/salary_slip_log.html | 2 +- .../templates/includes/topic/topic_row.html | 4 +- erpnext/templates/pages/cart_terms.html | 2 +- erpnext/templates/pages/courses.html | 2 +- erpnext/templates/pages/courses.py | 1 - erpnext/templates/pages/home.css | 2 +- erpnext/templates/pages/home.html | 2 +- .../integrations/gocardless_checkout.html | 2 +- .../pages/integrations/gocardless_checkout.py | 2 +- .../integrations/gocardless_confirmation.html | 2 +- .../integrations/gocardless_confirmation.py | 2 +- .../pages/material_request_info.html | 2 +- .../templates/pages/material_request_info.py | 6 +- .../pages/non_profit/join-chapter.html | 2 +- .../pages/non_profit/leave-chapter.html | 2 +- erpnext/templates/pages/order.py | 4 +- erpnext/templates/pages/product_search.py | 1 - erpnext/templates/pages/projects.js | 2 +- erpnext/templates/pages/task_info.html | 2 +- erpnext/templates/pages/task_info.py | 6 +- erpnext/templates/pages/timelog_info.html | 2 +- erpnext/templates/pages/timelog_info.py | 4 +- .../includes/item_table_qty.html | 1 - erpnext/tests/test_regional.py | 2 +- erpnext/tests/test_subcontracting.py | 2 +- erpnext/tests/ui/setup_wizard.js | 2 +- erpnext/tests/ui_test_helpers.py | 2 +- erpnext/utilities/activation.py | 26 +++---- erpnext/utilities/bot.py | 2 +- .../doctype/rename_tool/rename_tool.py | 1 - erpnext/utilities/doctype/video/video_list.js | 2 +- .../doctype/video_settings/video_settings.py | 2 +- erpnext/utilities/hierarchy_chart.py | 2 +- .../youtube_interactions.py | 2 +- .../utilities/web_form/addresses/addresses.js | 2 +- erpnext/www/all-products/index.html | 2 +- erpnext/www/all-products/item_row.html | 1 - erpnext/www/all-products/not_found.html | 2 +- erpnext/www/book_appointment/index.css | 2 +- erpnext/www/book_appointment/index.html | 2 +- .../www/book_appointment/verify/index.html | 4 +- erpnext/www/book_appointment/verify/index.py | 2 +- erpnext/www/lms/content.py | 2 +- erpnext/www/lms/course.html | 2 +- erpnext/www/lms/index.py | 2 +- erpnext/www/lms/macros/card.html | 2 +- erpnext/www/lms/macros/hero.html | 2 +- erpnext/www/lms/profile.py | 2 +- erpnext/www/lms/program.html | 2 +- erpnext/www/lms/program.py | 2 +- erpnext/www/lms/topic.html | 2 +- erpnext/www/lms/topic.py | 2 +- erpnext/www/support/index.html | 2 +- erpnext/www/support/index.py | 24 +++--- 1190 files changed, 1352 insertions(+), 1604 deletions(-) diff --git a/erpnext/.stylelintrc b/erpnext/.stylelintrc index 1e05d1fb41d..30075f13d04 100644 --- a/erpnext/.stylelintrc +++ b/erpnext/.stylelintrc @@ -6,4 +6,4 @@ "scss/at-rule-no-unknown": true, "no-descending-specificity": null } -} \ No newline at end of file +} diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js index e12eae9c1c1..d8a83e53dc0 100644 --- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js +++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js @@ -19,4 +19,4 @@ frappe.dashboards.chart_sources["Account Balance Timeline"] = { reqd: 1 }, ] -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index 335e8a15ab0..0c81d83ed8e 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -450,5 +450,3 @@ def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr): return debit_account else: return credit_account - - diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index 65c5ff1ceaf..2fa1d53c60c 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -60,4 +60,4 @@ frappe.ui.form.on('Accounting Dimension Detail', { let row = locals[cdt][cdn]; row.reference_document = frm.doc.document_type; } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py index e657a9ae34b..4f3ee7643ab 100644 --- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py @@ -113,5 +113,3 @@ def disable_dimension(): dimension2 = frappe.get_doc("Accounting Dimension", "Location") dimension2.disabled = 1 dimension2.save() - - diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js index 74b7b516763..9dd882a3119 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js @@ -79,4 +79,4 @@ frappe.ui.form.on('Allowed Dimension', { row.accounting_dimension = frm.doc.accounting_dimension; frm.refresh_field("dimensions"); } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py index 63b5dbbd3e6..739d8f6bc63 100644 --- a/erpnext/accounts/doctype/accounting_period/accounting_period.py +++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py @@ -56,4 +56,4 @@ class AccountingPeriod(Document): self.append('closed_documents', { "document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed - }) \ No newline at end of file + }) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js index 541901c9abf..e44af3a9167 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js @@ -48,4 +48,4 @@ frappe.tour['Accounts Settings'] = [ title: "Unlink Advance Payment on Cancellation of Order", description: __("Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.") } -]; \ No newline at end of file +]; diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 55449132928..62c97f24d5f 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -37,7 +37,7 @@ class AccountsSettings(Document): def toggle_discount_accounting_fields(self): enable_discount_accounting = cint(self.enable_discount_accounting) - + for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]: make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) if enable_discount_accounting: @@ -52,4 +52,4 @@ class AccountsSettings(Document): else: make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False) - make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) \ No newline at end of file + make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False) diff --git a/erpnext/accounts/doctype/accounts_settings/regional/united_states.js b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js index d47d6e58039..3e38386481c 100644 --- a/erpnext/accounts/doctype/accounts_settings/regional/united_states.js +++ b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js @@ -5,4 +5,4 @@ frappe.ui.form.on('Accounts Settings', { frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods"); frm.set_df_property("credit_controller", "label", "Credit Manager"); } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js index 19041a3f73d..059e1d31588 100644 --- a/erpnext/accounts/doctype/bank/bank.js +++ b/erpnext/accounts/doctype/bank/bank.js @@ -120,4 +120,4 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink { plaid_success(token, response) { frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' }); } -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/doctype/bank/bank.py b/erpnext/accounts/doctype/bank/bank.py index 41aae14362f..99fa21c8f9a 100644 --- a/erpnext/accounts/doctype/bank/bank.py +++ b/erpnext/accounts/doctype/bank/bank.py @@ -13,4 +13,4 @@ class Bank(Document): load_address_and_contact(self) def on_trash(self): - delete_contact_and_address('Bank', self.name) \ No newline at end of file + delete_contact_and_address('Bank', self.name) diff --git a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py b/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py index a959cea98f2..c7ea1522993 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py +++ b/erpnext/accounts/doctype/bank_account/bank_account_dashboard.py @@ -26,4 +26,4 @@ def get_data(): 'items': ['Journal Entry'] } ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js index ba3f2face63..63cc46518ff 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js @@ -8,7 +8,7 @@ frappe.ui.form.on("Bank Clearance", { onload: function(frm) { - let default_bank_account = frappe.defaults.get_user_default("Company")? + let default_bank_account = frappe.defaults.get_user_default("Company")? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: ""; frm.set_value("account", default_bank_account); diff --git a/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py b/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py index ecc536733f2..59299f81e50 100644 --- a/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py +++ b/erpnext/accounts/doctype/bank_clearance_detail/bank_clearance_detail.py @@ -6,4 +6,4 @@ import frappe from frappe.model.document import Document class BankClearanceDetail(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py index 88e1055beb4..a0aac6ab170 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py @@ -25,6 +25,6 @@ class BankGuarantee(Document): def get_vouchar_detials(column_list, doctype, docname): column_list = json.loads(column_list) for col in column_list: - sanitize_searchfield(col) + sanitize_searchfield(col) return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s''' .format(columns=", ".join(column_list), doctype=doctype), docname, as_dict=1)[0] diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 5246baa02b3..31cfb2da1da 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -105,4 +105,3 @@ def unclear_reference_payment(doctype, docname): frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None) return doc.payment_entry - diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js index 2ecc2b0cda3..bff41d5539b 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js @@ -10,4 +10,4 @@ frappe.listview_settings['Bank Transaction'] = { return [__("Reconciled"), "green", "unallocated_amount,=,0"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py index 33ae45439e7..dc3b8674700 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py @@ -77,4 +77,4 @@ def get_bank_mapping(bank_account): mapping = {row.file_field:row.bank_transaction_field for row in bank.bank_transaction_mapping} - return mapping \ No newline at end of file + return mapping diff --git a/erpnext/accounts/doctype/c_form_invoice_detail/c_form_invoice_detail.py b/erpnext/accounts/doctype/c_form_invoice_detail/c_form_invoice_detail.py index ee5098bea12..20e423a610e 100644 --- a/erpnext/accounts/doctype/c_form_invoice_detail/c_form_invoice_detail.py +++ b/erpnext/accounts/doctype/c_form_invoice_detail/c_form_invoice_detail.py @@ -6,4 +6,4 @@ import frappe from frappe.model.document import Document class CFormInvoiceDetail(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py index 28d84b4442f..b1ad2972beb 100644 --- a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py +++ b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.py @@ -18,5 +18,3 @@ class CashFlowMapping(Document): frappe._('You can only select a maximum of one option from the list of check boxes.'), title='Error' ) - - diff --git a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py index 7ad1d3ab831..081c6fa4718 100644 --- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py +++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py @@ -33,4 +33,4 @@ class CashierClosing(Document): def validate_time(self): if self.from_time >= self.time: - frappe.throw(_("From Time Should Be Less Than To Time")) \ No newline at end of file + frappe.throw(_("From Time Should Be Less Than To Time")) diff --git a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js index 6a430eb02bf..d10c61858f1 100644 --- a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js +++ b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js @@ -10,10 +10,10 @@ frappe.ui.form.on('Cheque Print Template', { function() { erpnext.cheque_print.view_cheque_print(frm); }).addClass("btn-primary"); - + $(frm.fields_dict.cheque_print_preview.wrapper).empty() - - + + var template = '
                                                            \
                                                            Signatory Name \
                                                            \
                                                            '; - + $(frappe.render(template, frm.doc)).appendTo(frm.fields_dict.cheque_print_preview.wrapper) - + if (frm.doc.scanned_cheque) { $(frm.fields_dict.cheque_print_preview.wrapper).find("#cheque_preview").css('background-image', 'url(' + frm.doc.scanned_cheque + ')'); } diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py index 8a5473f3a16..981fec308cc 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.py +++ b/erpnext/accounts/doctype/cost_center/cost_center.py @@ -129,4 +129,4 @@ def get_name_with_number(new_account, account_number): def check_if_distributed_cost_center_enabled(cost_center_list): value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1) - return next((True for x in value_list if x[0]), False) \ No newline at end of file + return next((True for x in value_list if x[0]), False) diff --git a/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py b/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py index 788ac8be83f..24cf3ea0689 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py +++ b/erpnext/accounts/doctype/cost_center/cost_center_dashboard.py @@ -12,4 +12,4 @@ def get_data(): 'items': ['Budget Variance Report', 'General Ledger'] } ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/cost_center/cost_center_tree.js b/erpnext/accounts/doctype/cost_center/cost_center_tree.js index fde41233c4d..1d482c58f1a 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center_tree.js +++ b/erpnext/accounts/doctype/cost_center/cost_center_tree.js @@ -51,4 +51,4 @@ frappe.treeview_settings["Cost Center"] = { } -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py index b5fc7e3b497..7779ccefc20 100644 --- a/erpnext/accounts/doctype/cost_center/test_cost_center.py +++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py @@ -62,6 +62,3 @@ def create_cost_center(**args): cc.is_group = args.is_group or 0 cc.parent_cost_center = args.parent_cost_center or "_Test Company - _TC" cc.insert() - - - diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.py b/erpnext/accounts/doctype/coupon_code/coupon_code.py index 55c119315e0..92a816d25e9 100644 --- a/erpnext/accounts/doctype/coupon_code/coupon_code.py +++ b/erpnext/accounts/doctype/coupon_code/coupon_code.py @@ -17,7 +17,7 @@ class CouponCode(Document): self.coupon_code =''.join(i for i in self.coupon_name if not i.isdigit())[0:8].upper() elif self.coupon_type == "Gift Card": self.coupon_code = frappe.generate_hash()[:10].upper() - + def validate(self): if self.coupon_type == "Gift Card": self.maximum_use = 1 diff --git a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py index 5af12cde06d..06987a8a4a5 100644 --- a/erpnext/accounts/doctype/coupon_code/test_coupon_code.py +++ b/erpnext/accounts/doctype/coupon_code/test_coupon_code.py @@ -124,6 +124,3 @@ class TestCouponCode(unittest.TestCase): so.submit() self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1) - - - diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py index 109737f7276..93dfcc14bda 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py @@ -7,4 +7,4 @@ from __future__ import unicode_literals from frappe.model.document import Document class DiscountedInvoice(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/accounts/doctype/dunning/dunning_dashboard.py b/erpnext/accounts/doctype/dunning/dunning_dashboard.py index 19a73ddfa48..33c6ab080c9 100644 --- a/erpnext/accounts/doctype/dunning/dunning_dashboard.py +++ b/erpnext/accounts/doctype/dunning/dunning_dashboard.py @@ -14,4 +14,4 @@ def get_data(): 'items': ['Payment Entry', 'Journal Entry'] } ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/dunning/test_dunning.py b/erpnext/accounts/doctype/dunning/test_dunning.py index 7fc2e4b3060..67692ecc472 100644 --- a/erpnext/accounts/doctype/dunning/test_dunning.py +++ b/erpnext/accounts/doctype/dunning/test_dunning.py @@ -143,4 +143,4 @@ def create_dunning_type_with_zero_interest_rate(): 'closing_text': 'We kindly request that you pay the outstanding amount immediately, and late fees.' } ) - dunning_type.save() + dunning_type.save() diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js index b7b6020caa9..926a442f808 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js @@ -31,7 +31,7 @@ frappe.ui.form.on('Exchange Rate Revaluation', { }, __('Create')); } } - }); + }); } }, @@ -128,4 +128,4 @@ var get_account_details = function(frm, cdt, cdn) { frm.events.get_total_gain_loss(frm); } }); -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index f2b0a8c08a6..dbbcedcadfa 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -44,7 +44,7 @@ class ExchangeRateRevaluation(Document): if total_amt != total_debit: return True - + return False @frappe.whitelist() @@ -205,4 +205,4 @@ def get_account_details(account, company, posting_date, party_type=None, party=N "new_balance_in_base_currency": new_balance_in_base_currency } - return account_details \ No newline at end of file + return account_details diff --git a/erpnext/accounts/doctype/finance_book/test_finance_book.py b/erpnext/accounts/doctype/finance_book/test_finance_book.py index 502765812ae..cd8e204f4c8 100644 --- a/erpnext/accounts/doctype/finance_book/test_finance_book.py +++ b/erpnext/accounts/doctype/finance_book/test_finance_book.py @@ -19,7 +19,7 @@ class TestFinanceBook(unittest.TestCase): finance_book = frappe.get_doc("Finance Book", "_Test Finance Book") return finance_book - + def test_finance_book(self): finance_book = self.create_finance_book() diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py index 6523cd3cdb8..6d35ca24397 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_dashboard.py @@ -17,4 +17,4 @@ def get_data(): 'items': ['Payment Entry', 'Journal Entry'] } ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js index a72023d8e12..4895efcd4cc 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js @@ -18,4 +18,4 @@ frappe.listview_settings['Invoice Discounting'] = { return [__("Canceled"), "red", "status,=,Canceled"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/doctype/journal_entry/regional/india.js b/erpnext/accounts/doctype/journal_entry/regional/india.js index 75a69ac0cf3..c5f5520479f 100644 --- a/erpnext/accounts/doctype/journal_entry/regional/india.js +++ b/erpnext/accounts/doctype/journal_entry/regional/india.js @@ -14,4 +14,4 @@ frappe.ui.form.on("Journal Entry", { }; }); } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py index 5f003e022a0..5835d462ae9 100644 --- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py @@ -100,7 +100,7 @@ class TestJournalEntry(unittest.TestCase): "debit_in_account_currency": 0 if diff > 0 else abs(diff), "credit_in_account_currency": diff if diff > 0 else 0 }) - + jv.append("accounts", { "account": "Stock Adjustment - TCP1", "cost_center": "Main - TCP1", diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js index cbb9fc4b0f9..1c19c1d2255 100644 --- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js +++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js @@ -88,4 +88,4 @@ frappe.ui.form.on("Journal Entry Template", { frappe.model.clear_table(frm.doc, "accounts"); frm.refresh_field("accounts"); } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js index 7a06d3572a6..103fa96d02d 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js @@ -14,4 +14,4 @@ frappe.ui.form.on('Mode of Payment', { }; }); }, -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py index 32473694c80..cea921e999e 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.py @@ -39,4 +39,3 @@ class ModeofPayment(Document): message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \ Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode." frappe.throw(_(message), title="Not Allowed") - diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py index bff64227325..ad8623fb4ee 100644 --- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py +++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.py @@ -55,4 +55,4 @@ def get_percentage(doc, start_date, period): if d.month in months: percentage += d.percentage_allocation - return percentage \ No newline at end of file + return percentage diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py index a6794998159..912bd9e331a 100644 --- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py +++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution_dashboard.py @@ -20,4 +20,4 @@ def get_data(): 'items': ['Budget'] } ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js index a8c07d6bb9b..7eb5c4234d1 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js @@ -162,4 +162,4 @@ frappe.ui.form.on('Opening Invoice Creation Tool Item', { invoices_add: (frm) => { frm.trigger('update_invoice_table'); } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index d76d9099628..9914b45dfc1 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -241,4 +241,3 @@ def get_temporary_opening_account(company=None): frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts")) return accounts[0].name - diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 831b2708583..d2dffde5cdc 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -185,7 +185,7 @@ class PaymentEntry(AccountsController): for field, value in iteritems(ref_details): if d.exchange_gain_loss: # for cases where gain/loss is booked into invoice - # exchange_gain_loss is calculated from invoice & populated + # exchange_gain_loss is calculated from invoice & populated # and row.exchange_rate is already set to payment entry's exchange rate # refer -> `update_reference_in_payment_entry()` in utils.py continue @@ -417,7 +417,7 @@ class PaymentEntry(AccountsController): net_total_for_tds = 0 if reference.reference_doctype == 'Purchase Order': net_total_for_tds += flt(frappe.db.get_value('Purchase Order', reference.reference_name, 'net_total')) - + if net_total_for_tds: net_total = net_total_for_tds @@ -841,7 +841,7 @@ class PaymentEntry(AccountsController): if account_details: row.update(account_details) - + if not row.get('amount'): # if no difference amount return diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry_list.js b/erpnext/accounts/doctype/payment_entry/payment_entry_list.js index e6d83b9f683..2d76fe69ef9 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry_list.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry_list.js @@ -11,4 +11,4 @@ frappe.listview_settings['Payment Entry'] = { }; } } -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js index 14aa0736d40..e8db2c3159d 100644 --- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js +++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_against_purchase_invoice.js @@ -57,4 +57,4 @@ QUnit.test("test payment entry", function(assert) { () => frappe.timeout(3), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js index 0c76343fa90..34af79fcd10 100644 --- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry.js @@ -25,4 +25,4 @@ QUnit.test("test payment entry", function(assert) { () => frappe.timeout(0.3), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js index 9849d767271..8c7f6f47dd3 100644 --- a/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js +++ b/erpnext/accounts/doctype/payment_entry/tests/test_payment_entry_write_off.js @@ -64,4 +64,4 @@ QUnit.test("test payment entry", function(assert) { }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py index fd213a47a1e..3529c16a1c2 100644 --- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py +++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.py @@ -9,19 +9,19 @@ from frappe.model.document import Document class PaymentGatewayAccount(Document): def autoname(self): self.name = self.payment_gateway + " - " + self.currency - + def validate(self): self.currency = frappe.db.get_value("Account", self.payment_account, "account_currency") - + self.update_default_payment_gateway() self.set_as_default_if_not_set() - + def update_default_payment_gateway(self): if self.is_default: frappe.db.sql("""update `tabPayment Gateway Account` set is_default = 0 where is_default = 1 """) - + def set_as_default_if_not_set(self): - if not frappe.db.get_value("Payment Gateway Account", + if not frappe.db.get_value("Payment Gateway Account", {"is_default": 1, "name": ("!=", self.name)}, "name"): self.is_default = 1 diff --git a/erpnext/accounts/doctype/payment_order/payment_order.js b/erpnext/accounts/doctype/payment_order/payment_order.js index d12e474c5b1..aa373bc2fcc 100644 --- a/erpnext/accounts/doctype/payment_order/payment_order.js +++ b/erpnext/accounts/doctype/payment_order/payment_order.js @@ -136,4 +136,4 @@ frappe.ui.form.on('Payment Order', { dialog.show(); }, -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py b/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py index 6b93f926cdf..a4f335833ee 100644 --- a/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py +++ b/erpnext/accounts/doctype/payment_order/payment_order_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Payment Entry', 'Journal Entry'] } ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.py b/erpnext/accounts/doctype/payment_order/test_payment_order.py index 5fdde07faa4..9ba57aef300 100644 --- a/erpnext/accounts/doctype/payment_order/test_payment_order.py +++ b/erpnext/accounts/doctype/payment_order/test_payment_order.py @@ -46,4 +46,4 @@ def create_payment_order_against_payment_entry(ref_doc, order_type): doc = make_payment_order(ref_doc.name, payment_order) doc.save() doc.submit() - return doc \ No newline at end of file + return doc diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index d788d91855e..acfe1fef2ee 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -307,4 +307,4 @@ def reconcile_dr_cr_note(dr_cr_notes, company): ] }) jv.flags.ignore_mandatory = True - jv.submit() \ No newline at end of file + jv.submit() diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 438951db627..f83cb375fcf 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -541,4 +541,4 @@ def make_payment_order(source_name, target_doc=None): } }, target_doc, set_missing_values) - return doclist \ No newline at end of file + return doclist diff --git a/erpnext/accounts/doctype/payment_request/test_payment_request.py b/erpnext/accounts/doctype/payment_request/test_payment_request.py index 5eba62c0b31..ad6ff6f5553 100644 --- a/erpnext/accounts/doctype/payment_request/test_payment_request.py +++ b/erpnext/accounts/doctype/payment_request/test_payment_request.py @@ -138,4 +138,4 @@ class TestPaymentRequest(unittest.TestCase): # Try to make Payment Request more than SO amount, should give validation pr2.grand_total = 900 - self.assertRaises(frappe.ValidationError, pr2.save) \ No newline at end of file + self.assertRaises(frappe.ValidationError, pr2.save) diff --git a/erpnext/accounts/doctype/payment_term/payment_term.js b/erpnext/accounts/doctype/payment_term/payment_term.js index acd0144c2ea..feecf93484c 100644 --- a/erpnext/accounts/doctype/payment_term/payment_term.js +++ b/erpnext/accounts/doctype/payment_term/payment_term.js @@ -19,4 +19,4 @@ frappe.ui.form.on('Payment Term', { frm.set_df_property("discount", "description", description); } } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js index 84c8d09b164..ea18adefa35 100644 --- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js +++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js @@ -3,6 +3,6 @@ frappe.ui.form.on('Payment Terms Template', { setup: function(frm) { - + } }); diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py index c705097ac66..5c8cb4fbdc1 100644 --- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py +++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template_dashboard.py @@ -30,4 +30,4 @@ def get_data(): 'items': ['Customer Group', 'Supplier Group'] } ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index 9cfb47876c8..a6e3bd98e7c 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -51,7 +51,7 @@ class PeriodClosingVoucher(AccountsController): def make_gl_entries(self): gl_entries = [] - net_pl_balance = 0 + net_pl_balance = 0 pl_accounts = self.get_pl_balances() @@ -79,7 +79,7 @@ class PeriodClosingVoucher(AccountsController): from erpnext.accounts.general_ledger import make_gl_entries make_gl_entries(gl_entries) - + def get_pnl_gl_entry(self, net_pl_balance): cost_center = frappe.db.get_value("Company", self.company, "cost_center") gl_entry = self.get_gl_dict({ diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index 2f29372b01c..f17a5c51a08 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -139,7 +139,7 @@ def create_company(): 'company_name': "Test PCV Company", 'country': 'United States', 'default_currency': 'USD' - }) + }) company.insert(ignore_if_duplicate = True) return company.name diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index 6418d730903..264d4a68b00 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -20,9 +20,9 @@ frappe.ui.form.on('POS Closing Entry', { frm.set_query("pos_opening_entry", function(doc) { return { filters: { 'status': 'Open', 'docstatus': 1 } }; }); - + if (frm.doc.docstatus === 0 && !frm.doc.amended_from) frm.set_value("period_end_date", frappe.datetime.now_datetime()); - + frappe.realtime.on('closing_process_complete', async function(data) { await frm.reload_doc(); if (frm.doc.status == 'Failed' && frm.doc.error_message && data.user == frappe.session.user) { @@ -43,7 +43,7 @@ frappe.ui.form.on('POS Closing Entry', { const issue = 'issue'; frm.dashboard.set_headline( __('POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.', [issue])); - + $('#jump_to_error').on('click', (e) => { e.preventDefault(); frappe.utils.scroll_to( diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js index cd08efc55fb..2f8081b95ce 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js @@ -5,10 +5,10 @@ frappe.ui.form.on('POS Invoice Merge Log', { setup: function(frm) { frm.set_query("pos_invoice", "pos_invoices", doc => { return{ - filters: { + filters: { 'docstatus': 1, - 'customer': doc.customer, - 'consolidated_invoice': '' + 'customer': doc.customer, + 'consolidated_invoice': '' } } }); diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 08e072e2049..e50d437ba6a 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -354,4 +354,4 @@ def safe_load_json(message): except Exception: json_message = message - return json_message \ No newline at end of file + return json_message diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py index 040a815fab3..1b9659409c0 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py @@ -147,4 +147,3 @@ class TestPOSInvoiceMergeLog(unittest.TestCase): frappe.set_user("Administrator") frappe.db.sql("delete from `tabPOS Profile`") frappe.db.sql("delete from `tabPOS Invoice`") - diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js index 372e75649b3..d23f348f04e 100644 --- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js +++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js @@ -53,4 +53,4 @@ frappe.ui.form.on('POS Opening Entry', { }); } } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py index 0023a84a46e..3318fefab14 100644 --- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py +++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py @@ -38,4 +38,4 @@ class POSOpeningEntry(StatusUpdater): frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account")) def on_submit(self): - self.set_status(update=True) \ No newline at end of file + self.set_status(update=True) diff --git a/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py index 2e36391714b..c115be5ae94 100644 --- a/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py +++ b/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py @@ -21,8 +21,8 @@ def create_opening_entry(pos_profile, user): balance_details.append(frappe._dict({ 'mode_of_payment': d.mode_of_payment })) - + entry.set("balance_details", balance_details) entry.submit() - - return entry.as_dict() + + return entry.as_dict() diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.py b/erpnext/accounts/doctype/pos_settings/pos_settings.py index 913f49829c1..d925dd9d86e 100644 --- a/erpnext/accounts/doctype/pos_settings/pos_settings.py +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.py @@ -8,4 +8,4 @@ from frappe.model.document import Document class POSSettings(Document): def validate(self): - pass \ No newline at end of file + pass diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 3173db13af3..680370b6af0 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -615,4 +615,4 @@ def delete_existing_pricing_rules(): for doctype in ["Pricing Rule", "Pricing Rule Item Code", "Pricing Rule Item Group", "Pricing Rule Brand"]: - frappe.db.sql("delete from `tab{0}`".format(doctype)) \ No newline at end of file + frappe.db.sql("delete from `tab{0}`".format(doctype)) diff --git a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js index 8155e7d799a..8279b59cb41 100644 --- a/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js +++ b/erpnext/accounts/doctype/pricing_rule/tests/test_pricing_rule.js @@ -26,4 +26,3 @@ QUnit.test("test pricing rule", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py index 0eac73236e9..5e7583a9745 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py +++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py @@ -31,4 +31,4 @@ class ProcessDeferredAccounting(Document): 'against_voucher': self.name }) - make_reverse_gl_entries(gl_entries=gl_entries) \ No newline at end of file + make_reverse_gl_entries(gl_entries=gl_entries) diff --git a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py index e08a0e5cc2b..03c269ac766 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py +++ b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py @@ -45,4 +45,4 @@ class TestProcessDeferredAccounting(unittest.TestCase): ["Sales - _TC", 0.0, 33.85, "2019-01-31"] ] - check_gl_entries(self, si.name, expected_gle, "2019-01-10") \ No newline at end of file + check_gl_entries(self, si.name, expected_gle, "2019-01-10") diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py index 500952e38ad..a12ea4033df 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -284,4 +284,4 @@ def send_auto_email(): selected = frappe.get_list('Process Statement Of Accounts', filters={'to_date': format_date(today()), 'enable_auto_email': 1}) for entry in selected: send_emails(entry.name, from_scheduler=True) - return True \ No newline at end of file + return True diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js index 890a1871bdd..e840c79cd75 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js @@ -48,4 +48,4 @@ frappe.ui.form.on('Promotional Scheme', { frm.doc.apply_on === key ? 1 : 0); } } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py index 28c4c61b9fa..54fedb77387 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Pricing Rule'] } ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py index 7354ef036c5..286f7cf6edd 100644 --- a/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/test_promotional_scheme.py @@ -11,25 +11,25 @@ class TestPromotionalScheme(unittest.TestCase): ps = make_promotional_scheme() price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name", "creation"], filters = {'promotional_scheme': ps.name}) - self.assertTrue(len(price_rules),1) + self.assertTrue(len(price_rules),1) price_doc_details = frappe.db.get_value('Pricing Rule', price_rules[0].name, ['customer', 'min_qty', 'discount_percentage'], as_dict = 1) self.assertTrue(price_doc_details.customer, '_Test Customer') self.assertTrue(price_doc_details.min_qty, 4) self.assertTrue(price_doc_details.discount_percentage, 20) ps.price_discount_slabs[0].min_qty = 6 - ps.append('customer', { + ps.append('customer', { 'customer': "_Test Customer 2"}) ps.save() price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"], filters = {'promotional_scheme': ps.name}) - self.assertTrue(len(price_rules), 2) + self.assertTrue(len(price_rules), 2) price_doc_details = frappe.db.get_value('Pricing Rule', price_rules[1].name, ['customer', 'min_qty', 'discount_percentage'], as_dict = 1) self.assertTrue(price_doc_details.customer, '_Test Customer 2') self.assertTrue(price_doc_details.min_qty, 6) self.assertTrue(price_doc_details.discount_percentage, 20) - + price_doc_details = frappe.db.get_value('Pricing Rule', price_rules[0].name, ['customer', 'min_qty', 'discount_percentage'], as_dict = 1) self.assertTrue(price_doc_details.customer, '_Test Customer') self.assertTrue(price_doc_details.min_qty, 6) @@ -38,7 +38,7 @@ class TestPromotionalScheme(unittest.TestCase): price_rules = frappe.get_all('Pricing Rule', fields = ["promotional_scheme_id", "name"], filters = {'promotional_scheme': ps.name}) self.assertEqual(price_rules, []) - + def make_promotional_scheme(): ps = frappe.new_doc('Promotional Scheme') ps.name = '_Test Scheme' @@ -57,4 +57,4 @@ def make_promotional_scheme(): }) ps.save() - return ps \ No newline at end of file + return ps diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 3b91118402c..6c74d2b438f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -591,4 +591,4 @@ frappe.ui.form.on("Purchase Invoice", { company: function(frm) { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, -}) \ No newline at end of file +}) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py index 173939df008..b6467a3d5ca 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_dashboard.py @@ -34,4 +34,4 @@ def get_data(): 'items': ['Auto Repeat'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index 914a2457d45..771b49ac629 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -26,4 +26,4 @@ frappe.listview_settings['Purchase Invoice'] = { return [__("Paid"), "green", "outstanding_amount,=,0"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js index b470051b51d..94b3b9ed338 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.js @@ -72,4 +72,3 @@ QUnit.test("test purchase invoice", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py index bfaa849200e..d157837a7a5 100644 --- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py +++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class PurchaseInvoiceAdvance(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.py b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.py index a7489da316f..5854ddee940 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.py +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class PurchaseTaxesandCharges(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template_dashboard.py b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template_dashboard.py index 11c220bf2db..db9793d77a6 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template_dashboard.py +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template_dashboard.py @@ -19,4 +19,4 @@ def get_data(): 'items': ['Supplier Quotation', 'Tax Rule'] } ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js index c73f03b57bb..10b05d0594f 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/test_purchase_taxes_and_charges_template.js @@ -26,4 +26,3 @@ QUnit.test("test sales taxes and charges template", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js index ada665a0ca9..f01325d80bd 100644 --- a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js +++ b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js @@ -67,7 +67,7 @@ frappe.listview_settings['Sales Invoice'].onload = function (list_view) { "default": "1-Duplicate", "options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"] }, - { + { "label": "Remark", "fieldname": "remark", "fieldtype": "Data", @@ -82,7 +82,7 @@ frappe.listview_settings['Sales Invoice'].onload = function (list_view) { const data = d.get_values(); frappe.call({ method: 'erpnext.regional.india.e_invoice.utils.cancel_irns', - args: { + args: { doctype: list_view.doctype, docnames, reason: data.reason.split('-')[0], @@ -122,7 +122,7 @@ frappe.listview_settings['Sales Invoice'].onload = function (list_view) { frappe.realtime.on("bulk_einvoice_generation_complete", (data) => { const { failures, user, invoices } = data; - + if (invoices.length != failures.length) { frappe.msgprint({ message: __('{0} e-invoices generated successfully', [invoices.length]), @@ -171,4 +171,4 @@ frappe.listview_settings['Sales Invoice'].onload = function (list_view) { }); } }); -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/doctype/sales_invoice/regional/italy.js b/erpnext/accounts/doctype/sales_invoice/regional/italy.js index 1c47d3ab9fd..21eb8ce6619 100644 --- a/erpnext/accounts/doctype/sales_invoice/regional/italy.js +++ b/erpnext/accounts/doctype/sales_invoice/regional/italy.js @@ -1,3 +1,3 @@ {% include "erpnext/regional/italy/sales_invoice.js" %} -erpnext.setup_e_invoice_button('Sales Invoice') \ No newline at end of file +erpnext.setup_e_invoice_button('Sales Invoice') diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py index f1069282edc..3238ead4316 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_dashboard.py @@ -33,4 +33,4 @@ def get_data(): 'items': ['Auto Repeat'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js index e12ac038500..61d78e1fe4b 100644 --- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice.js @@ -40,4 +40,3 @@ QUnit.test("test sales Invoice", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js index f1cb22a4976..cf2d0fbedba 100644 --- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js +++ b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_margin.js @@ -33,4 +33,3 @@ QUnit.test("test sales invoice with margin", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js index 651bf0aa4ce..45d9a14bffb 100644 --- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js +++ b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment.js @@ -54,4 +54,3 @@ QUnit.test("test sales Invoice with payment", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js index b959cf961b8..0464e4509f6 100644 --- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js +++ b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_payment_request.js @@ -49,4 +49,3 @@ QUnit.test("test sales Invoice with payment request", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js index 2697758d7a3..af484d7899c 100644 --- a/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js +++ b/erpnext/accounts/doctype/sales_invoice/tests/test_sales_invoice_with_serialize_item.js @@ -42,4 +42,3 @@ QUnit.test("test sales Invoice with serialize item", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py index 1ec517929ef..28aeef4d5e1 100644 --- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py +++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class SalesInvoiceAdvance(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.py b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.py index 8d1df5c19a3..b1de9d85fdb 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.py +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class SalesTaxesandCharges(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py index d825c6fd325..522e282a170 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py @@ -21,4 +21,4 @@ def get_data(): 'items': ['POS Profile', 'Subscription', 'Restaurant', 'Tax Rule'] } ] - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js index d02e70b5419..8cd42f63a41 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/test_sales_taxes_and_charges_template.js @@ -26,4 +26,3 @@ QUnit.test("test sales taxes and charges template", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.js b/erpnext/accounts/doctype/share_transfer/share_transfer.js index 1cad4dfae3d..6317c9c8c0d 100644 --- a/erpnext/accounts/doctype/share_transfer/share_transfer.js +++ b/erpnext/accounts/doctype/share_transfer/share_transfer.js @@ -115,4 +115,4 @@ erpnext.share_transfer.make_jv = function (frm) { frappe.set_route("Form", doc.doctype, doc.name); } }); -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.py b/erpnext/accounts/doctype/share_transfer/share_transfer.py index 4024b8155a4..3d4543fb051 100644 --- a/erpnext/accounts/doctype/share_transfer/share_transfer.py +++ b/erpnext/accounts/doctype/share_transfer/share_transfer.py @@ -299,4 +299,4 @@ def make_jv_entry( company, account, amount, payment_account,\ "party": credit_applicant, }) journal_entry.set("accounts", account_amt_list) - return journal_entry.as_dict() \ No newline at end of file + return journal_entry.as_dict() diff --git a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js index 0201f762b37..63ea1bf35f4 100644 --- a/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js +++ b/erpnext/accounts/doctype/shipping_rule/test_shipping_rule.js @@ -34,4 +34,3 @@ QUnit.test("test Shipping Rule", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js b/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js index ab1b77cd5f5..f3668b8b406 100644 --- a/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js +++ b/erpnext/accounts/doctype/shipping_rule/tests/test_shipping_rule_for_buying.js @@ -34,4 +34,3 @@ QUnit.test("test Shipping Rule", function(assert) { () => done() ]); }); - diff --git a/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py b/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py index dab59db70c3..db6ef117c22 100644 --- a/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py +++ b/erpnext/accounts/doctype/shipping_rule_condition/shipping_rule_condition.py @@ -9,4 +9,4 @@ import frappe from frappe.model.document import Document class ShippingRuleCondition(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/accounts/doctype/subscription/subscription_list.js b/erpnext/accounts/doctype/subscription/subscription_list.js index c7325fb9f74..6490ff3776e 100644 --- a/erpnext/accounts/doctype/subscription/subscription_list.js +++ b/erpnext/accounts/doctype/subscription/subscription_list.js @@ -14,4 +14,4 @@ frappe.listview_settings['Subscription'] = { return [__("Cancelled"), "gray"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index 7c58e9865fd..4f2cf487a4f 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -630,5 +630,3 @@ class TestSubscription(unittest.TestCase): subscription.process() self.assertEqual(len(subscription.invoices), 1) - - diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.js b/erpnext/accounts/doctype/subscription_plan/subscription_plan.js index aaa32cfe7ef..7d6f2aed100 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.js +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.js @@ -6,4 +6,4 @@ frappe.ui.form.on('Subscription Plan', { frm.toggle_reqd("cost", frm.doc.price_determination === 'Fixed rate'); frm.toggle_reqd("price_list", frm.doc.price_determination === 'Based on price list'); } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py index 1ca442a4531..a341c2af6ac 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py @@ -54,4 +54,4 @@ def get_plan_rate(plan, quantity=1, customer=None, start_date=None, end_date=Non cost -= (plan.cost * prorate_factor) - return cost \ No newline at end of file + return cost diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.py b/erpnext/accounts/doctype/tax_rule/tax_rule.py index e4ebc6d12f9..58142318177 100644 --- a/erpnext/accounts/doctype/tax_rule/tax_rule.py +++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py @@ -188,4 +188,4 @@ def get_customer_group_condition(customer_group): customer_groups = ["%s"%(frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)] if customer_groups: condition = ",".join(['%s'] * len(customer_groups))%(tuple(customer_groups)) - return condition \ No newline at end of file + return condition diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 481ef285e72..1536a237dec 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -240,7 +240,7 @@ def get_deducted_tax(taxable_vouchers, fiscal_year, tax_details): def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers): tds_amount = 0 invoice_filters = { - 'name': ('in', vouchers), + 'name': ('in', vouchers), 'docstatus': 1 } @@ -282,7 +282,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details) else: tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0 - + if cint(tax_details.round_off_tax_amount): tds_amount = round(tds_amount) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 2ba22ca4353..1c687e5cb15 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -97,7 +97,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): pi.save() pi.submit() invoices.append(pi) - + # Second Invoice will apply TDS checked pi1 = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000) pi1.submit() diff --git a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html index e588ed6609e..4ac657d1ae6 100644 --- a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html +++ b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html @@ -73,4 +73,4 @@
                                                            -
                                                            \ No newline at end of file +
                                                            diff --git a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html index 71c26e8c55a..7643eca7635 100644 --- a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html +++ b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html @@ -159,4 +159,4 @@
                                                            -
                                                            \ No newline at end of file + diff --git a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html index 0ca940f8bd5..c1c611ee3a3 100644 --- a/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html +++ b/erpnext/accounts/print_format/journal_auditing_voucher/journal_auditing_voucher.html @@ -68,4 +68,4 @@
                                                            -
                                                            \ No newline at end of file + diff --git a/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html b/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html index 283d505e3be..ae07582704c 100644 --- a/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html +++ b/erpnext/accounts/print_format/payment_receipt_voucher/payment_receipt_voucher.html @@ -27,4 +27,3 @@ {{ _("Authorized Signatory") }}

                                                            - diff --git a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html index 043ac254ed3..8696bffbfcb 100644 --- a/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html +++ b/erpnext/accounts/print_format/purchase_auditing_voucher/purchase_auditing_voucher.html @@ -103,4 +103,4 @@ - \ No newline at end of file + diff --git a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html index a53b593a72a..efb2d00f0bf 100644 --- a/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html +++ b/erpnext/accounts/print_format/sales_auditing_voucher/sales_auditing_voucher.html @@ -93,4 +93,4 @@ - \ No newline at end of file + diff --git a/erpnext/accounts/report/account_balance/test_account_balance.py b/erpnext/accounts/report/account_balance/test_account_balance.py index 14ddf4a30fc..f5c9449e85d 100644 --- a/erpnext/accounts/report/account_balance/test_account_balance.py +++ b/erpnext/accounts/report/account_balance/test_account_balance.py @@ -62,8 +62,3 @@ def make_sales_invoice(): income_account = 'Sales - _TC2', expense_account = 'Cost of Goods Sold - _TC2', cost_center = 'Main - _TC2') - - - - - diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 6abd6e5cf77..b6c6689be0b 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -136,4 +136,3 @@ frappe.query_reports["Accounts Payable"] = { } erpnext.utils.add_dimensions('Accounts Payable', 9); - diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js index 9c6b0639c0a..ea200720dff 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -105,4 +105,3 @@ frappe.query_reports["Accounts Payable Summary"] = { } erpnext.utils.add_dimensions('Accounts Payable Summary', 9); - diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py index 729eda9492f..c08582b564c 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.py @@ -12,4 +12,3 @@ def execute(filters=None): "naming_by": ["Buying Settings", "supp_master_name"], } return AccountsReceivableSummary(filters).run(args) - diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 29c4f7d3941..1a32e2a8e06 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -200,4 +200,3 @@ frappe.query_reports["Accounts Receivable"] = { } erpnext.utils.add_dimensions('Accounts Receivable', 9); - diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 2ff5b531c51..cca67608238 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -93,4 +93,3 @@ def make_credit_note(docname): cost_center = 'Main - _TC2', is_return = 1, return_against = docname) - diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index 657b3e8f204..e94b30921f3 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -134,4 +134,4 @@ class AccountsReceivableSummary(ReceivablePayableReport): "{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]), "{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]), "{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]): - self.add_column(label=label, fieldname='range' + str(i+1)) \ No newline at end of file + self.add_column(label=label, fieldname='range' + str(i+1)) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 26bb44f4f7b..7838385dc56 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -209,4 +209,4 @@ def get_chart_data(filters, columns, asset, liability, equity): else: chart["type"] = "line" - return chart \ No newline at end of file + return chart diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js index dbee0229737..f0b6c6b20ac 100644 --- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js +++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js @@ -22,7 +22,7 @@ frappe.query_reports["Bank Clearance Summary"] = { "fieldtype": "Link", "options": "Account", "reqd": 1, - "default": frappe.defaults.get_user_default("Company")? + "default": frappe.defaults.get_user_default("Company")? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "", "get_query": function() { return { diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py index 79b0a6f30ec..95f724cc580 100644 --- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py +++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py @@ -74,19 +74,19 @@ def get_entries(filters): journal_entries = frappe.db.sql("""SELECT "Journal Entry", jv.name, jv.posting_date, jv.cheque_no, jv.clearance_date, jvd.against_account, jvd.debit - jvd.credit - FROM + FROM `tabJournal Entry Account` jvd, `tabJournal Entry` jv - WHERE + WHERE jvd.parent = jv.name and jv.docstatus=1 and jvd.account = %(account)s {0} order by posting_date DESC, jv.name DESC""".format(conditions), filters, as_list=1) payment_entries = frappe.db.sql("""SELECT - "Payment Entry", name, posting_date, reference_no, clearance_date, party, + "Payment Entry", name, posting_date, reference_no, clearance_date, party, if(paid_from=%(account)s, paid_amount * -1, received_amount) - FROM + FROM `tabPayment Entry` - WHERE + WHERE docstatus=1 and (paid_from = %(account)s or paid_to = %(account)s) {0} order by posting_date DESC, name DESC""".format(conditions), filters, as_list=1) - return sorted(journal_entries + payment_entries, key=lambda k: k[2] or getdate(nowdate())) \ No newline at end of file + return sorted(journal_entries + payment_entries, key=lambda k: k[2] or getdate(nowdate())) diff --git a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js index 8f028496cd5..9bb6a14c677 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js @@ -16,7 +16,7 @@ frappe.query_reports["Bank Reconciliation Statement"] = { "label": __("Bank Account"), "fieldtype": "Link", "options": "Account", - "default": frappe.defaults.get_user_default("Company")? + "default": frappe.defaults.get_user_default("Company")? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "", "reqd": 1, "get_query": function() { diff --git a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py index 2ce5d50edf4..2dcea22f7e6 100644 --- a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py +++ b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py @@ -104,4 +104,4 @@ def get_columns(): 'fieldtype': 'Currency', 'width': 100 } - ] \ No newline at end of file + ] diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js index f547ca619bd..718b6e2fcb6 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -92,4 +92,3 @@ frappe.query_reports["Budget Variance Report"] = { erpnext.dimension_filters.forEach((dimension) => { frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]); }); - diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 9f0eee8aa5c..443126e4655 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -78,7 +78,7 @@ def get_final_data(dimension, dimension_items, filters, period_month_ranges, dat if filters["period"] != "Yearly" : row += totals data.append(row) - + return data @@ -388,7 +388,7 @@ def get_chart_data(filters, columns, data): budget_values[i] += values[index] actual_values[i] += values[index+1] index += 3 - + return { 'data': { 'labels': labels, @@ -399,4 +399,3 @@ def get_chart_data(filters, columns, data): }, 'type' : 'bar' } - diff --git a/erpnext/accounts/report/cash_flow/cash_flow.html b/erpnext/accounts/report/cash_flow/cash_flow.html index 40ba20c4ac6..d4ae54d4f38 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.html +++ b/erpnext/accounts/report/cash_flow/cash_flow.html @@ -1 +1 @@ -{% include "accounts/report/financial_statements.html" %} \ No newline at end of file +{% include "accounts/report/financial_statements.html" %} diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index a984bf46b50..a2c34c6ee26 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -21,4 +21,4 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "default": 1 } ); -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index 1363b53746a..6a8301a6f91 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -94,10 +94,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "default": 1 } ], - "formatter": function(value, row, column, data, default_formatter) { + "formatter": function(value, row, column, data, default_formatter) { if (data && column.fieldname=="account") { value = data.account_name || value; - + column.link_onclick = "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")"; column.is_tree = true; @@ -126,4 +126,4 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { }); } } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py index 515fd995e66..9953d8fcaf5 100644 --- a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py +++ b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.py @@ -105,4 +105,4 @@ def get_column(): def get_args(): return {'doctype': 'Delivery Note', 'party': 'customer', - 'date': 'posting_date', 'order': 'name', 'order_by': 'desc'} \ No newline at end of file + 'date': 'posting_date', 'order': 'name', 'order_by': 'desc'} diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 4a551b80124..095f5eda66a 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -176,4 +176,3 @@ frappe.query_reports["General Ledger"] = { } erpnext.utils.add_dimensions('General Ledger', 15) - diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 1759fa3a48f..5d8d49d6a65 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -48,7 +48,7 @@ def validate_filters(filters, account_details): if not filters.get("from_date") and not filters.get("to_date"): frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date")))) - + if filters.get('account'): filters.account = frappe.parse_json(filters.get('account')) for account in filters.account: @@ -92,7 +92,7 @@ def set_account_currency(filters): account_currency = None if filters.get("account"): - if len(filters.get("account")) == 1: + if len(filters.get("account")) == 1: account_currency = get_account_currency(filters.account[0]) else: currency = get_account_currency(filters.account[0]) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html index 40ba20c4ac6..d4ae54d4f38 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.html @@ -1 +1 @@ -{% include "accounts/report/financial_statements.html" %} \ No newline at end of file +{% include "accounts/report/financial_statements.html" %} diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index 714e48d279b..8e33af7ee8e 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -165,4 +165,4 @@ def get_net_profit(non_gross_income, gross_income, gross_expense, non_gross_expe has_value=True if has_value: - return profit_loss \ No newline at end of file + return profit_loss diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 08065a204ef..c9c22c246ed 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -626,7 +626,3 @@ def add_sub_total_row(item, total_row_map, group_by_value, tax_columns): for tax in tax_columns: total_row.setdefault(frappe.scrub(tax + ' Amount'), 0.0) total_row[frappe.scrub(tax + ' Amount')] += flt(item[frappe.scrub(tax + ' Amount')]) - - - - diff --git a/erpnext/accounts/report/non_billed_report.py b/erpnext/accounts/report/non_billed_report.py index 2e18ce11ddc..51735056896 100644 --- a/erpnext/accounts/report/non_billed_report.py +++ b/erpnext/accounts/report/non_billed_report.py @@ -44,4 +44,4 @@ def get_ordered_to_be_billed_data(args): def get_project_field(doctype, party): if party == "supplier": doctype = doctype + ' Item' - return "`tab%s`.project"%(doctype) \ No newline at end of file + return "`tab%s`.project"%(doctype) diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py index 7195c7e0b8b..556f5ad4f79 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py @@ -40,7 +40,7 @@ def execute(filters=None): row = [ d.voucher_type, d.voucher_no, d.party_type, d.party, d.posting_date, d.against_voucher, - invoice.posting_date, invoice.due_date, d.debit, d.credit, d.remarks, + invoice.posting_date, invoice.due_date, d.debit, d.credit, d.remarks, d.age, d.range1, d.range2, d.range3, d.range4 ] diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py index 6a42bb4fb65..b7e112c0c9a 100644 --- a/erpnext/accounts/report/pos_register/pos_register.py +++ b/erpnext/accounts/report/pos_register/pos_register.py @@ -10,7 +10,7 @@ from erpnext.accounts.report.sales_register.sales_register import get_mode_of_pa def execute(filters=None): if not filters: return [], [] - + validate_filters(filters) columns = get_columns(filters) @@ -29,7 +29,7 @@ def execute(filters=None): invoice_map, grouped_data = {}, [] for d in pos_entries: invoice_map.setdefault(d[group_by_field], []).append(d) - + for key in invoice_map: invoices = invoice_map[key] grouped_data += invoices @@ -56,7 +56,7 @@ def get_pos_entries(filters, group_by_field): return frappe.db.sql( """ - SELECT + SELECT p.posting_date, p.name as pos_invoice, p.pos_profile, p.owner, p.base_grand_total as grand_total, p.base_paid_amount as paid_amount, p.customer, p.is_return {select_mop_field} @@ -96,22 +96,22 @@ def add_subtotal_row(data, group_invoices, group_by_field, group_by_value): def validate_filters(filters): if not filters.get("company"): frappe.throw(_("{0} is mandatory").format(_("Company"))) - + if not filters.get("from_date") and not filters.get("to_date"): frappe.throw(_("{0} and {1} are mandatory").format(frappe.bold(_("From Date")), frappe.bold(_("To Date")))) - + if filters.from_date > filters.to_date: frappe.throw(_("From Date must be before To Date")) if (filters.get("pos_profile") and filters.get("group_by") == _('POS Profile')): frappe.throw(_("Can not filter based on POS Profile, if grouped by POS Profile")) - + if (filters.get("customer") and filters.get("group_by") == _('Customer')): frappe.throw(_("Can not filter based on Customer, if grouped by Customer")) - + if (filters.get("owner") and filters.get("group_by") == _('Cashier')): frappe.throw(_("Can not filter based on Cashier, if grouped by Cashier")) - + if (filters.get("mode_of_payment") and filters.get("group_by") == _('Payment Method')): frappe.throw(_("Can not filter based on Payment Method, if grouped by Payment Method")) @@ -120,23 +120,23 @@ def get_conditions(filters): if filters.get("pos_profile"): conditions += " AND pos_profile = %(pos_profile)s" - + if filters.get("owner"): conditions += " AND owner = %(owner)s" - + if filters.get("customer"): conditions += " AND customer = %(customer)s" - + if filters.get("is_return"): conditions += " AND is_return = %(is_return)s" - + if filters.get("mode_of_payment"): conditions += """ AND EXISTS( SELECT name FROM `tabSales Invoice Payment` sip WHERE parent=p.name AND ifnull(sip.mode_of_payment, '') = %(mode_of_payment)s )""" - + return conditions def get_group_by_field(group_by): @@ -150,7 +150,7 @@ def get_group_by_field(group_by): group_by_field = "customer" elif group_by == "Payment Method": group_by_field = "mode_of_payment" - + return group_by_field def get_columns(filters): @@ -217,4 +217,4 @@ def get_columns(filters): }, ] - return columns \ No newline at end of file + return columns diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.html b/erpnext/accounts/report/profitability_analysis/profitability_analysis.html index 40ba20c4ac6..d4ae54d4f38 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.html +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.html @@ -1 +1 @@ -{% include "accounts/report/financial_statements.html" %} \ No newline at end of file +{% include "accounts/report/financial_statements.html" %} diff --git a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js index a95cfacaeef..feab96f2652 100644 --- a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js +++ b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js @@ -5,4 +5,4 @@ frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() { frappe.query_reports["Purchase Invoice Trends"] = { filters: erpnext.get_purchase_trends_filters() } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py index ad3783f0de1..ba236b9969d 100644 --- a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py +++ b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.py @@ -11,4 +11,4 @@ def execute(filters=None): conditions = get_columns(filters, "Purchase Invoice") data = get_data(filters, conditions) - return conditions["columns"], data \ No newline at end of file + return conditions["columns"], data diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index f34ea571639..aaf76c42997 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -56,4 +56,4 @@ frappe.query_reports["Purchase Register"] = { ] } -erpnext.utils.add_dimensions('Purchase Register', 7); \ No newline at end of file +erpnext.utils.add_dimensions('Purchase Register', 7); diff --git a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py index e9e9c9c4e69..a5eced5f80b 100644 --- a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py +++ b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.py @@ -105,4 +105,4 @@ def get_column(): def get_args(): return {'doctype': 'Purchase Receipt', 'party': 'supplier', - 'date': 'posting_date', 'order': 'name', 'order_by': 'desc'} \ No newline at end of file + 'date': 'posting_date', 'order': 'name', 'order_by': 'desc'} diff --git a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js index 2d320f52cfa..e3d43a7de1e 100644 --- a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js +++ b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js @@ -5,4 +5,4 @@ frappe.require("assets/erpnext/js/sales_trends_filters.js", function() { frappe.query_reports["Sales Invoice Trends"] = { filters: erpnext.get_sales_trends_filters() } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js index 068926b063d..44e20e83c50 100644 --- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js +++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js @@ -42,4 +42,4 @@ frappe.query_reports["Sales Payment Summary"] = { "fieldtype": "Check" }, ] -}; \ No newline at end of file +}; diff --git a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py index a51c4276301..e4a3d3527fd 100644 --- a/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py +++ b/erpnext/accounts/report/sales_payment_summary/test_sales_payment_summary.py @@ -162,4 +162,4 @@ def create_records(): "price_list": "Standard Selling", "item_code": item.item_code, "price_list_rate": 10000 - }).insert() \ No newline at end of file + }).insert() diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 85bbceab827..2c9b01bbaa3 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -69,4 +69,3 @@ frappe.query_reports["Sales Register"] = { } erpnext.utils.add_dimensions('Sales Register', 7); - diff --git a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.py b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.py index d2c23ee4e78..fbd25b13bb5 100644 --- a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.py +++ b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.py @@ -10,4 +10,4 @@ def execute(filters=None): "party_type": "Supplier", "naming_by": ["Buying Settings", "supp_master_name"], } - return PartyLedgerSummaryReport(filters).run(args) \ No newline at end of file + return PartyLedgerSummaryReport(filters).run(args) diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 8645d55d0fe..078b06519f1 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -110,6 +110,3 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.utils.add_dimensions('Trial Balance', 6); }); - - - diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 33360e2b01e..1fc0faab3a7 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -321,4 +321,4 @@ def prepare_opening_closing(row): row[reverse_col] = abs(row[valid_col]) row[valid_col] = 0.0 else: - row[reverse_col] = 0.0 \ No newline at end of file + row[reverse_col] = 0.0 diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py index 78c7e439d38..f034e7450ee 100644 --- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py +++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py @@ -242,4 +242,4 @@ def is_party_name_visible(filters): else: show_party_name = True - return show_party_name \ No newline at end of file + return show_party_name diff --git a/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py b/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py index eee620b7cc8..1250d676a06 100644 --- a/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py +++ b/erpnext/accounts/report/unpaid_expense_claim/unpaid_expense_claim.py @@ -20,11 +20,11 @@ def get_unclaimed_expese_claims(filters): if filters.get("employee"): cond = "ec.employee = %(employee)s" - return frappe.db.sql(""" + return frappe.db.sql(""" select ec.employee, ec.employee_name, ec.name, ec.total_sanctioned_amount, ec.total_amount_reimbursed, sum(gle.credit_in_account_currency - gle.debit_in_account_currency) as outstanding_amt - from + from `tabExpense Claim` ec, `tabGL Entry` gle where gle.against_voucher_type = "Expense Claim" and gle.against_voucher = ec.name diff --git a/erpnext/agriculture/doctype/crop/crop.js b/erpnext/agriculture/doctype/crop/crop.js index afd84fd9f66..550824636b6 100644 --- a/erpnext/agriculture/doctype/crop/crop.js +++ b/erpnext/agriculture/doctype/crop/crop.js @@ -52,4 +52,4 @@ erpnext.crop.update_item_qty_amount = function(frm, cdt, cdn) { } }); }); -}; \ No newline at end of file +}; diff --git a/erpnext/agriculture/doctype/crop/crop_dashboard.py b/erpnext/agriculture/doctype/crop/crop_dashboard.py index 9a8f26fe90c..8f37735c812 100644 --- a/erpnext/agriculture/doctype/crop/crop_dashboard.py +++ b/erpnext/agriculture/doctype/crop/crop_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Crop Cycle'] } ] - } \ No newline at end of file + } diff --git a/erpnext/agriculture/doctype/crop/test_crop.js b/erpnext/agriculture/doctype/crop/test_crop.js index 138acbf85a3..40555634a2c 100644 --- a/erpnext/agriculture/doctype/crop/test_crop.js +++ b/erpnext/agriculture/doctype/crop/test_crop.js @@ -105,7 +105,7 @@ QUnit.test("test: Crop", function (assert) { ] ]} ]), - // agriculture task list + // agriculture task list () => { assert.equal(cur_frm.doc.name, 'Basil from seed'); assert.equal(cur_frm.doc.period, 15); diff --git a/erpnext/agriculture/doctype/crop/test_crop.py b/erpnext/agriculture/doctype/crop/test_crop.py index c2e49174047..b3079837c35 100644 --- a/erpnext/agriculture/doctype/crop/test_crop.py +++ b/erpnext/agriculture/doctype/crop/test_crop.py @@ -11,4 +11,4 @@ test_dependencies = ["Fertilizer"] class TestCrop(unittest.TestCase): def test_crop_period(self): basil = frappe.get_doc('Crop', 'Basil from seed') - self.assertEqual(basil.period, 15) \ No newline at end of file + self.assertEqual(basil.period, 15) diff --git a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js index 464a3680baa..87184daedc9 100644 --- a/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js +++ b/erpnext/agriculture/doctype/crop_cycle/test_crop_cycle.js @@ -19,7 +19,7 @@ QUnit.test("test: Crop Cycle", function (assert) { {disease: 'Aphids'} ] ]}, - {linked_land_unit: [ + {linked_land_unit: [ [ {land_unit: 'Basil Farm'} ] diff --git a/erpnext/agriculture/doctype/disease/disease.py b/erpnext/agriculture/doctype/disease/disease.py index c7707a54652..affa57046e5 100644 --- a/erpnext/agriculture/doctype/disease/disease.py +++ b/erpnext/agriculture/doctype/disease/disease.py @@ -17,4 +17,4 @@ class Disease(Document): frappe.throw(_("Start day is greater than end day in task '{0}'").format(task.task_name)) # to calculate the period of the Crop Cycle if task.end_day > max_period: max_period = task.end_day - self.treatment_period = max_period \ No newline at end of file + self.treatment_period = max_period diff --git a/erpnext/agriculture/doctype/disease/test_disease.js b/erpnext/agriculture/doctype/disease/test_disease.js index 57d62c16c25..33f60c4e152 100644 --- a/erpnext/agriculture/doctype/disease/test_disease.js +++ b/erpnext/agriculture/doctype/disease/test_disease.js @@ -36,4 +36,3 @@ QUnit.test("test: Disease", function (assert) { ]); }); - diff --git a/erpnext/agriculture/doctype/disease/test_disease.py b/erpnext/agriculture/doctype/disease/test_disease.py index 54788a2c817..80861770b0d 100644 --- a/erpnext/agriculture/doctype/disease/test_disease.py +++ b/erpnext/agriculture/doctype/disease/test_disease.py @@ -9,4 +9,4 @@ import unittest class TestDisease(unittest.TestCase): def test_treatment_period(self): disease = frappe.get_doc('Disease', 'Aphids') - self.assertEqual(disease.treatment_period, 3) \ No newline at end of file + self.assertEqual(disease.treatment_period, 3) diff --git a/erpnext/agriculture/doctype/fertilizer/fertilizer.py b/erpnext/agriculture/doctype/fertilizer/fertilizer.py index 9cb492aff1e..c475f002981 100644 --- a/erpnext/agriculture/doctype/fertilizer/fertilizer.py +++ b/erpnext/agriculture/doctype/fertilizer/fertilizer.py @@ -11,4 +11,4 @@ class Fertilizer(Document): def load_contents(self): docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Fertilizer'}) for doc in docs: - self.append('fertilizer_contents', {'title': str(doc.name)}) \ No newline at end of file + self.append('fertilizer_contents', {'title': str(doc.name)}) diff --git a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py b/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py index 3a25b3f0a7a..4c71d33fe80 100644 --- a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py +++ b/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py @@ -8,4 +8,4 @@ import unittest class TestFertilizer(unittest.TestCase): def test_fertilizer_creation(self): - self.assertEqual(frappe.db.exists('Fertilizer', 'Urea'), 'Urea') \ No newline at end of file + self.assertEqual(frappe.db.exists('Fertilizer', 'Urea'), 'Urea') diff --git a/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py b/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py index 2806cc6523e..b65f93de0a0 100644 --- a/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py +++ b/erpnext/agriculture/doctype/plant_analysis/plant_analysis.py @@ -12,4 +12,4 @@ class PlantAnalysis(Document): def load_contents(self): docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Plant Analysis'}) for doc in docs: - self.append('plant_analysis_criteria', {'title': str(doc.name)}) \ No newline at end of file + self.append('plant_analysis_criteria', {'title': str(doc.name)}) diff --git a/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py b/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py index 37835f8c7b1..234d0d4b011 100644 --- a/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py +++ b/erpnext/agriculture/doctype/soil_analysis/soil_analysis.py @@ -11,4 +11,4 @@ class SoilAnalysis(Document): def load_contents(self): docs = frappe.get_all("Agriculture Analysis Criteria", filters={'linked_doctype':'Soil Analysis'}) for doc in docs: - self.append('soil_analysis_criteria', {'title': str(doc.name)}) \ No newline at end of file + self.append('soil_analysis_criteria', {'title': str(doc.name)}) diff --git a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py b/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py index 937c06ccadf..16d105c9c58 100644 --- a/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py +++ b/erpnext/agriculture/doctype/soil_texture/test_soil_texture.py @@ -11,4 +11,4 @@ class TestSoilTexture(unittest.TestCase): soil_tex = frappe.get_all('Soil Texture', fields=['name'], filters={'collection_datetime': '2017-11-08'}) doc = frappe.get_doc('Soil Texture', soil_tex[0].name) self.assertEqual(doc.silt_composition, 50) - self.assertEqual(doc.soil_type, 'Silt Loam') \ No newline at end of file + self.assertEqual(doc.soil_type, 'Silt Loam') diff --git a/erpnext/agriculture/doctype/water_analysis/water_analysis.py b/erpnext/agriculture/doctype/water_analysis/water_analysis.py index d9f007cea13..cb2691d4555 100644 --- a/erpnext/agriculture/doctype/water_analysis/water_analysis.py +++ b/erpnext/agriculture/doctype/water_analysis/water_analysis.py @@ -24,4 +24,4 @@ class WaterAnalysis(Document): if self.collection_datetime > self.laboratory_testing_datetime: frappe.throw(_('Lab testing datetime cannot be before collection datetime')) if self.laboratory_testing_datetime > self.result_datetime: - frappe.throw(_('Lab result datetime cannot be before testing datetime')) \ No newline at end of file + frappe.throw(_('Lab result datetime cannot be before testing datetime')) diff --git a/erpnext/agriculture/setup.py b/erpnext/agriculture/setup.py index ab91343d5d1..75f07be5de2 100644 --- a/erpnext/agriculture/setup.py +++ b/erpnext/agriculture/setup.py @@ -426,5 +426,5 @@ def create_agriculture_data(): title='Degree Days', standard=1, linked_doctype='Weather') - ] + ] insert_record(records) diff --git a/erpnext/assets/dashboard_fixtures.py b/erpnext/assets/dashboard_fixtures.py index 7f3c1de406a..2c701796072 100644 --- a/erpnext/assets/dashboard_fixtures.py +++ b/erpnext/assets/dashboard_fixtures.py @@ -176,4 +176,4 @@ def get_number_cards(fiscal_year, year_start_date, year_end_date): "filters_json": "[]", "doctype": "Number Card" } - ] \ No newline at end of file + ] diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 922cc4a7b26..da5778ea3d5 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -103,11 +103,11 @@ frappe.ui.form.on('Asset', { frm.trigger("create_asset_maintenance"); }, __("Manage")); } - + frm.add_custom_button(__("Repair Asset"), function() { frm.trigger("create_asset_repair"); }, __("Manage")); - + if (frm.doc.status != 'Fully Depreciated') { frm.add_custom_button(__("Adjust Asset Value"), function() { frm.trigger("create_asset_adjustment"); diff --git a/erpnext/assets/doctype/asset/asset_dashboard.py b/erpnext/assets/doctype/asset/asset_dashboard.py index a5cf23803d2..62bb4be53aa 100644 --- a/erpnext/assets/doctype/asset/asset_dashboard.py +++ b/erpnext/assets/doctype/asset/asset_dashboard.py @@ -11,4 +11,4 @@ def get_data(): 'items': ['Asset Movement'] } ] - } \ No newline at end of file + } diff --git a/erpnext/assets/doctype/asset/asset_list.js b/erpnext/assets/doctype/asset/asset_list.js index 02f39e0e7f4..4302cb2c518 100644 --- a/erpnext/assets/doctype/asset/asset_list.js +++ b/erpnext/assets/doctype/asset/asset_list.js @@ -50,4 +50,4 @@ frappe.listview_settings['Asset'] = { }); }); }, -} \ No newline at end of file +} diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index e23a7154524..605ce2e2503 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -763,4 +763,4 @@ def set_depreciation_settings_in_company(): company.save() # Enable booking asset depreciation entry automatically - frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) \ No newline at end of file + frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index 46620d56e98..39032d637b5 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -20,7 +20,7 @@ class AssetCategory(Document): for field in ("Total Number of Depreciations", "Frequency of Depreciation"): if cint(d.get(frappe.scrub(field)))<1: frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError) - + def validate_account_currency(self): account_types = [ 'fixed_asset_account', 'accumulated_depreciation_account', 'depreciation_expense_account', 'capital_work_in_progress_account' @@ -33,13 +33,13 @@ class AssetCategory(Document): account_currency = frappe.get_value("Account", d.get(type_of_account), "account_currency") if account_currency != company_currency: invalid_accounts.append(frappe._dict({ 'type': type_of_account, 'idx': d.idx, 'account': d.get(type_of_account) })) - + for d in invalid_accounts: frappe.throw(_("Row #{}: Currency of {} - {} doesn't matches company currency.") .format(d.idx, frappe.bold(frappe.unscrub(d.type)), frappe.bold(d.account)), title=_("Invalid Account")) - + def validate_account_types(self): account_type_map = { 'fixed_asset_account': { 'account_type': 'Fixed Asset' }, @@ -59,12 +59,12 @@ class AssetCategory(Document): frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account.") .format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type)), title=_("Invalid Account")) - + def valide_cwip_account(self): if self.enable_cwip_accounting: missing_cwip_accounts_for_company = [] for d in self.accounts: - if (not d.capital_work_in_progress_account and + if (not d.capital_work_in_progress_account and not frappe.db.get_value("Company", d.company_name, "capital_work_in_progress_account")): missing_cwip_accounts_for_company.append(get_link_to_form("Company", d.company_name)) @@ -93,4 +93,4 @@ def get_asset_category_account(fieldname, item=None, asset=None, account=None, a account = frappe.db.get_value("Asset Category Account", filters={"parent": asset_category, "company_name": company}, fieldname=fieldname) - return account \ No newline at end of file + return account diff --git a/erpnext/assets/doctype/asset_category/test_asset_category.py b/erpnext/assets/doctype/asset_category/test_asset_category.py index 39b79d6c507..9f7ada65d82 100644 --- a/erpnext/assets/doctype/asset_category/test_asset_category.py +++ b/erpnext/assets/doctype/asset_category/test_asset_category.py @@ -10,9 +10,9 @@ class TestAssetCategory(unittest.TestCase): def test_mandatory_fields(self): asset_category = frappe.new_doc("Asset Category") asset_category.asset_category_name = "Computers" - + self.assertRaises(frappe.MandatoryError, asset_category.insert) - + asset_category.total_number_of_depreciations = 3 asset_category.frequency_of_depreciation = 3 asset_category.append("accounts", { @@ -21,7 +21,7 @@ class TestAssetCategory(unittest.TestCase): "accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC", "depreciation_expense_account": "_Test Depreciations - _TC" }) - + try: asset_category.insert() except frappe.DuplicateEntryError: @@ -44,4 +44,4 @@ class TestAssetCategory(unittest.TestCase): "depreciation_expense_account": "_Test Depreciations - _TC" }) - self.assertRaises(frappe.ValidationError, asset_category.insert) \ No newline at end of file + self.assertRaises(frappe.ValidationError, asset_category.insert) diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js index 70b8654509f..52996e93475 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js @@ -97,4 +97,4 @@ var get_next_due_date = function (frm, cdt, cdn) { } }); } -}; \ No newline at end of file +}; diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py index a506deec93e..e14f1d88dcb 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -116,4 +116,4 @@ def get_maintenance_log(asset_name): select maintenance_status, count(asset_name) as count, asset_name from `tabAsset Maintenance Log` where asset_name=%s group by maintenance_status""", - (asset_name), as_dict=1) \ No newline at end of file + (asset_name), as_dict=1) diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py index 392fbdd2af7..7610152039d 100644 --- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py @@ -73,7 +73,7 @@ def create_asset_data(): 'doctype': 'Location', 'location_name': 'Test Location' }).insert() - + if not frappe.db.exists("Item", "Photocopier"): meta = frappe.get_meta('Asset') naming_series = meta.get_field("naming_series").options @@ -157,6 +157,6 @@ def set_depreciation_settings_in_company(): company.disposal_account = "_Test Gain/Loss on Asset Disposal - _TC" company.depreciation_cost_center = "_Test Cost Center - _TC" company.save() - + # Enable booking asset depreciation entry automatically - frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) \ No newline at end of file + frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js index c5db90ad370..bcdc3acf0ac 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js @@ -12,4 +12,4 @@ frappe.ui.form.on('Asset Maintenance Log', { }; }); } -}); \ No newline at end of file +}); diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js index 06d8879091c..2df7db97446 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.js +++ b/erpnext/assets/doctype/asset_movement/asset_movement.js @@ -99,4 +99,4 @@ frappe.ui.form.on('Asset Movement Item', { }); } } -}); \ No newline at end of file +}); diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index b2de250b168..1771e27ddfe 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -40,14 +40,14 @@ class AssetMovement(Document): if current_location != d.source_location: frappe.throw(_("Asset {0} does not belongs to the location {1}"). format(d.asset, d.source_location)) - + if self.purpose == 'Issue': if d.target_location: frappe.throw(_("Issuing cannot be done to a location. \ Please enter employee who has issued Asset {0}").format(d.asset), title="Incorrect Movement Purpose") if not d.to_employee: frappe.throw(_("Employee is required while issuing Asset {0}").format(d.asset)) - + if self.purpose == 'Transfer': if d.to_employee: frappe.throw(_("Transferring cannot be done to an Employee. \ @@ -57,7 +57,7 @@ class AssetMovement(Document): frappe.throw(_("Target Location is required while transferring Asset {0}").format(d.asset)) if d.source_location == d.target_location: frappe.throw(_("Source and Target Location cannot be same")) - + if self.purpose == 'Receipt': # only when asset is bought and first entry is made if not d.source_location and not (d.target_location or d.to_employee): @@ -80,14 +80,14 @@ class AssetMovement(Document): if current_custodian != d.from_employee: frappe.throw(_("Asset {0} does not belongs to the custodian {1}"). format(d.asset, d.from_employee)) - + if d.to_employee and frappe.db.get_value("Employee", d.to_employee, "company") != self.company: frappe.throw(_("Employee {0} does not belongs to the company {1}"). format(d.to_employee, self.company)) def on_submit(self): self.set_latest_location_in_asset() - + def on_cancel(self): self.set_latest_location_in_asset() @@ -105,12 +105,12 @@ class AssetMovement(Document): # In case of cancellation it corresponds to previous latest document's location, employee latest_movement_entry = frappe.db.sql( """ - SELECT asm_item.target_location, asm_item.to_employee + SELECT asm_item.target_location, asm_item.to_employee FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm - WHERE + WHERE asm_item.parent=asm.name and asm_item.asset=%(asset)s and - asm.company=%(company)s and + asm.company=%(company)s and asm.docstatus=1 and {0} ORDER BY asm.transaction_date desc limit 1 diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index 1cebfff66e5..18a56d33e6d 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -59,7 +59,7 @@ frappe.ui.form.on('Asset Repair', { if (frm.doc.repair_status == "Completed") { frm.set_value('completion_date', frappe.datetime.now_datetime()); - } + } } }); @@ -68,4 +68,4 @@ frappe.ui.form.on('Asset Repair Consumed Item', { var row = locals[cdt][cdn]; frappe.model.set_value(cdt, cdn, 'total_value', row.consumed_quantity * row.valuation_rate); }, -}); \ No newline at end of file +}); diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index d32fdf7054f..746f582fdcd 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -18,7 +18,7 @@ class AssetRepair(AccountsController): if self.get('stock_items'): self.set_total_value() self.calculate_total_repair_cost() - + def update_status(self): if self.repair_status == 'Pending': frappe.db.set_value('Asset', self.asset, 'status', 'Out of Order') @@ -98,7 +98,7 @@ class AssetRepair(AccountsController): if self.capitalize_repair_cost: row.value_after_depreciation -= self.repair_cost - + def get_total_value_of_stock_consumed(self): total_value_of_stock_consumed = 0 if self.get('stock_consumption'): @@ -141,7 +141,7 @@ class AssetRepair(AccountsController): gl_entries = [] repair_and_maintenance_account = frappe.db.get_value('Company', self.company, 'repair_and_maintenance_account') fixed_asset_account = get_asset_account("fixed_asset_account", asset=self.asset, company=self.company) - expense_account = frappe.get_doc('Purchase Invoice', self.purchase_invoice).items[0].expense_account + expense_account = frappe.get_doc('Purchase Invoice', self.purchase_invoice).items[0].expense_account gl_entries.append( self.get_gl_dict({ @@ -149,7 +149,7 @@ class AssetRepair(AccountsController): "credit": self.repair_cost, "credit_in_account_currency": self.repair_cost, "against": repair_and_maintenance_account, - "voucher_type": self.doctype, + "voucher_type": self.doctype, "voucher_no": self.name, "cost_center": self.cost_center, "posting_date": getdate(), @@ -167,7 +167,7 @@ class AssetRepair(AccountsController): "credit": item.amount, "credit_in_account_currency": item.amount, "against": repair_and_maintenance_account, - "voucher_type": self.doctype, + "voucher_type": self.doctype, "voucher_no": self.name, "cost_center": self.cost_center, "posting_date": getdate(), diff --git a/erpnext/assets/doctype/asset_repair/asset_repair_list.js b/erpnext/assets/doctype/asset_repair/asset_repair_list.js index f36fd2f8dcb..86376f40046 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair_list.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair_list.js @@ -10,4 +10,3 @@ frappe.listview_settings['Asset Repair'] = { } } }; - diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py index 30bbb37851e..5e727d007a9 100644 --- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py @@ -41,7 +41,7 @@ class TestAssetRepair(unittest.TestCase): self.assertEqual(total_repair_cost, asset_repair.repair_cost) for item in asset_repair.stock_items: total_repair_cost += item.total_value - + self.assertEqual(total_repair_cost, asset_repair.total_repair_cost) def test_repair_status_after_submit(self): @@ -99,7 +99,7 @@ class TestAssetRepair(unittest.TestCase): initial_num_of_depreciations = num_of_depreciations(asset) create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1) asset.reload() - + self.assertEqual((initial_num_of_depreciations + 1), num_of_depreciations(asset)) self.assertEqual(asset.schedules[-1].accumulated_depreciation_amount, asset.finance_books[0].value_after_depreciation) @@ -139,7 +139,7 @@ def create_asset_repair(**args): }) asset_repair.insert(ignore_if_duplicate=True) - + if args.submit: asset_repair.repair_status = "Completed" asset_repair.cost_center = "_Test Cost Center - _TC" @@ -165,4 +165,4 @@ def create_asset_repair(**args): asset_repair.purchase_invoice = make_purchase_invoice().name asset_repair.submit() - return asset_repair \ No newline at end of file + return asset_repair diff --git a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py index 03dc47b0bba..a9dc9795ee3 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/test_asset_value_adjustment.py @@ -91,4 +91,4 @@ def make_asset_value_adjustment(**args): "cost_center": args.cost_center or "Main - _TC" }).insert() - return doc \ No newline at end of file + return doc diff --git a/erpnext/assets/doctype/location/location_tree.js b/erpnext/assets/doctype/location/location_tree.js index b405afd1ddd..3e105f6ca49 100644 --- a/erpnext/assets/doctype/location/location_tree.js +++ b/erpnext/assets/doctype/location/location_tree.js @@ -30,4 +30,4 @@ frappe.treeview_settings["Location"] = { onload: function (treeview) { treeview.make_tree(); } -}; \ No newline at end of file +}; diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index 1a6ef54a830..75f42a9f783 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -76,7 +76,7 @@ frappe.query_reports["Fixed Asset Register"] = { fieldtype: "Link", options: "Asset Category" }, - { + { fieldname:"finance_book", label: __("Finance Book"), fieldtype: "Link", diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index d1457b9b85a..7d07397944b 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -99,7 +99,7 @@ def prepare_chart_data(data, filters): labels_values_map = {} date_field = frappe.scrub(filters.date_based_on) - period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, + period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year, filters.from_date, filters.to_date, filters.filter_based_on, "Monthly", company=filters.company) for d in period_list: @@ -293,4 +293,4 @@ def get_columns(filters): "options": "Location", "width": 100 }, - ] \ No newline at end of file + ] diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index e496e9628d1..944bb61cfeb 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -28,4 +28,4 @@ frappe.tour['Buying Settings'] = [ title: "Purchase Receipt Required for Purchase Invoice Creation", description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.") } -]; \ No newline at end of file +]; diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index a0b1e073cc6..ca3bd90960c 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -635,4 +635,4 @@ def add_items_in_ste(ste_doc, row, qty, po_details, batch_no=None): 'item_code': row.item_details['rm_item_code'], 'subcontracted_item': row.item_details['main_item_code'], 'serial_no': '\n'.join(row.serial_no) if row.serial_no else '' - }) \ No newline at end of file + }) diff --git a/erpnext/buying/doctype/purchase_order/regional/india.js b/erpnext/buying/doctype/purchase_order/regional/india.js index 42d3995907f..ef83f203e73 100644 --- a/erpnext/buying/doctype/purchase_order/regional/india.js +++ b/erpnext/buying/doctype/purchase_order/regional/india.js @@ -1,3 +1,3 @@ {% include "erpnext/regional/india/taxes.js" %} -erpnext.setup_auto_gst_taxation('Purchase Order'); \ No newline at end of file +erpnext.setup_auto_gst_taxation('Purchase Order'); diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index d668c76b6b9..fa174ba8fa8 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -485,7 +485,7 @@ class TestPurchaseOrder(unittest.TestCase): def test_make_purchase_invoice_with_terms(self): from erpnext.selling.doctype.sales_order.test_sales_order import automatically_fetch_payment_terms, compare_payment_schedules - + automatically_fetch_payment_terms() po = create_purchase_order(do_not_save=True) diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js index 5d196874c98..012b0619cc9 100644 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order.js @@ -77,4 +77,4 @@ QUnit.test("test: purchase order", function(assert) { () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js index 8c0c1443144..bc3d767f95d 100644 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js +++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_get_items.js @@ -58,4 +58,4 @@ QUnit.test("test: purchase order with get items", function(assert) { () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js index 4e73ab8ef4f..83eb295010a 100644 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js +++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_discount_on_grand_total.js @@ -44,4 +44,4 @@ QUnit.test("test: purchase order with discount on grand total", function(assert) () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js index 1e54e50dda9..a729dd9839f 100644 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js +++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_item_wise_discount.js @@ -41,4 +41,4 @@ QUnit.test("test: purchase order with item wise discount", function(assert) { () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js index bf2dfeb37b7..b605e76ddf4 100644 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js +++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_multi_uom.js @@ -36,4 +36,4 @@ QUnit.test("test: purchase order with multi UOM", function(assert) { () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js index 96775eb0075..c258756b2a1 100644 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js +++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_shipping_rule.js @@ -40,4 +40,4 @@ QUnit.test("test: purchase order with shipping rule", function(assert) { () => frappe.timeout(0.3), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js index 39716ed560c..ccc383fd74e 100644 --- a/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js +++ b/erpnext/buying/doctype/purchase_order/tests/test_purchase_order_with_taxes_and_charges.js @@ -41,4 +41,4 @@ QUnit.test("test: purchase order with taxes and charges", function(assert) { () => frappe.timeout(0.3), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py index 8bdcd47e028..b6e28b6c674 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.py @@ -10,4 +10,4 @@ class PurchaseOrderItem(Document): pass def on_doctype_update(): - frappe.db.add_index("Purchase Order Item", ["item_code", "warehouse"]) \ No newline at end of file + frappe.db.add_index("Purchase Order Item", ["item_code", "warehouse"]) diff --git a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.py b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.py index 6caffbda1f3..c85ca2fbafc 100644 --- a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.py +++ b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class PurchaseOrderItemSupplied(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.py b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.py index 1a76f0ee7db..00c93ed1ea3 100644 --- a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.py +++ b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class PurchaseReceiptItemSupplied(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index a4ce84e1cf9..8ed6c9e2a6d 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -425,4 +425,4 @@ def get_rfq_containing_supplier(doctype, txt, searchfield, start, page_len, filt .format(filters.get("supplier"), filters.get("company"), conditions), {"page_len": page_len, "start": start}, as_dict=1) - return rfq_data \ No newline at end of file + return rfq_data diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation_dashboard.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation_dashboard.py index 6efbc782252..751336dc4c6 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation_dashboard.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Supplier Quotation'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js index 1fcfe75bb03..75f85f86d1d 100644 --- a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation.js @@ -73,4 +73,4 @@ QUnit.test("test: request_for_quotation", function(assert) { () => frappe.click_button('Close'), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js index 2e1652de733..f06c3f34c44 100644 --- a/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js +++ b/erpnext/buying/doctype/request_for_quotation/tests/test_request_for_quotation_for_status.js @@ -125,4 +125,4 @@ QUnit.test("Test: Request for Quotation", function (assert) { }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/supplier/regional/india.js b/erpnext/buying/doctype/supplier/regional/india.js index bd710e0df71..5f49a47e75e 100644 --- a/erpnext/buying/doctype/supplier/regional/india.js +++ b/erpnext/buying/doctype/supplier/regional/india.js @@ -1,3 +1,3 @@ {% include "erpnext/regional/india/party.js" %} -erpnext.setup_gst_reminder_button('Supplier'); \ No newline at end of file +erpnext.setup_gst_reminder_button('Supplier'); diff --git a/erpnext/buying/doctype/supplier/test_supplier.js b/erpnext/buying/doctype/supplier/test_supplier.js index bf7c192c91c..eaa4d0989d2 100644 --- a/erpnext/buying/doctype/supplier/test_supplier.js +++ b/erpnext/buying/doctype/supplier/test_supplier.js @@ -74,4 +74,4 @@ QUnit.test("test: supplier", function(assert) { }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py index 3a2e5d6dcef..4473ddea28e 100644 --- a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py +++ b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py @@ -15,4 +15,4 @@ class SupplierItemGroup(Document): 'item_group': self.item_group }) if exists: - frappe.throw(_("Item Group has already been linked to this supplier.")) \ No newline at end of file + frappe.throw(_("Item Group has already been linked to this supplier.")) diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py index 6a4c02c075c..25e4e2a4dcf 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py @@ -166,4 +166,4 @@ def set_expired_status(): `tabSupplier Quotation` SET `status` = 'Expired' WHERE `status` not in ('Cancelled', 'Stopped') AND `valid_till` < %s - """, (nowdate())) \ No newline at end of file + """, (nowdate())) diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js index 2d2b29cb916..20fb43026ab 100644 --- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js +++ b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation.js @@ -71,4 +71,4 @@ QUnit.test("test: supplier quotation", function(assert) { () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js index b151824ba68..0a51565b08e 100644 --- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js +++ b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_item_wise_discount.js @@ -31,4 +31,4 @@ QUnit.test("test: supplier quotation with item wise discount", function(assert){ () => frappe.timeout(0.3), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js index e37731eb579..7ea3e6079cd 100644 --- a/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js +++ b/erpnext/buying/doctype/supplier_quotation/tests/test_supplier_quotation_for_taxes_and_charges.js @@ -34,4 +34,4 @@ QUnit.test("test: supplier quotation with taxes and charges", function(assert) { () => frappe.timeout(0.3), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js index 5f5f54b79f5..b4cd852c32f 100644 --- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js @@ -93,5 +93,3 @@ var loadAllStandings = function(frm) { } }); }; - - diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py index 3d2305e2853..8e5cce5696b 100644 --- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_dashboard.py @@ -13,4 +13,4 @@ def get_data(): 'items': ['Supplier Scorecard Period'] } ] - } \ No newline at end of file + } diff --git a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py index 25282405492..a5f05ea5258 100644 --- a/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py +++ b/erpnext/buying/doctype/supplier_scorecard/test_supplier_scorecard.py @@ -128,4 +128,3 @@ valid_scorecard = [ "weighting_function":"{total_score} * max( 0, min ( 1 , (12 - {period_number}) / 12) )" } ] - diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py b/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py index 4eef4b4e03e..3babfc8cab3 100644 --- a/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py +++ b/erpnext/buying/doctype/supplier_scorecard_criteria/test_supplier_scorecard_criteria.py @@ -72,4 +72,4 @@ test_bad_criteria = [ "criteria_name":"Fake Criteria 3", "max_score":100.0 }, -] \ No newline at end of file +] diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py index 9938710e6e6..cc345e96bb8 100644 --- a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py +++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py @@ -109,4 +109,3 @@ def make_supplier_scorecard(source_name, target_doc=None): }, target_doc, post_process, ignore_permissions=True) return doc - diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py index 1ba5d06c536..678855a457b 100644 --- a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py +++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.py @@ -26,4 +26,4 @@ def get_standings_list(): `tabSupplier Scorecard Standing` scs""", {}, as_dict=1) - return standings \ No newline at end of file + return standings diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py index 37fdc5724f5..89a6459bbab 100644 --- a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py +++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.py @@ -493,4 +493,4 @@ def get_rfq_response_days(scorecard): total_sq_days = 0 - return total_sq_days \ No newline at end of file + return total_sq_days diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py b/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py index fe6dde50489..14b87105e66 100644 --- a/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py +++ b/erpnext/buying/doctype/supplier_scorecard_variable/test_supplier_scorecard_variable.py @@ -54,4 +54,4 @@ test_bad_variables = [ "variable_label":"Fake Variable 1", "path":"get_fake_variable1" }, -] \ No newline at end of file +] diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index beeca091c8a..99bcbe633cc 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -296,4 +296,4 @@ def get_po_entries(conditions): {conditions} GROUP BY parent.name, child.item_code - """.format(conditions=conditions), as_dict=1) #nosec \ No newline at end of file + """.format(conditions=conditions), as_dict=1) #nosec diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py index 44ab767c0a9..c36083f2aff 100644 --- a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py @@ -68,4 +68,4 @@ class TestProcurementTracker(unittest.TestCase): "actual_delivery_date": date_obj } - return expected_data \ No newline at end of file + return expected_data diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 89be62231b9..bda172769a9 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -268,4 +268,3 @@ def get_columns(filters): ]) return columns - diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js index 83d25d80ba2..90919dcc6a3 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js @@ -5,4 +5,4 @@ frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() { frappe.query_reports["Purchase Order Trends"] = { filters: erpnext.get_purchase_trends_filters() } -}); \ No newline at end of file +}); diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py index 1ed6cad6b46..095a44319d6 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py @@ -55,4 +55,4 @@ def get_chart_data(data, conditions, filters): "lineOptions": { "regionFill": 1 } - } \ No newline at end of file + } diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py index 0c0d4f0531d..9a45972837b 100644 --- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py +++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.py @@ -149,4 +149,4 @@ def get_columns(): "fieldtype": "Float", "width": 110 } - ] \ No newline at end of file + ] diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py index d8de701bf6e..cb304a1fdab 100644 --- a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py +++ b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py @@ -33,4 +33,4 @@ def make_purchase_receipt_against_po(po, quantity=5): pr.items[0].qty = quantity pr.supplier_warehouse = '_Test Warehouse 1 - _TC' pr.insert() - pr.submit() \ No newline at end of file + pr.submit() diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py index 68426abbb04..96cacb6f1b5 100644 --- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py +++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.py @@ -94,4 +94,4 @@ def get_po_items_to_supply(filters): ["Purchase Order", "transaction_date", ">=", filters.from_date], ["Purchase Order", "docstatus", "=", 1] ] - ) \ No newline at end of file + ) diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html index 098214d741c..015b31c2064 100644 --- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.html @@ -129,4 +129,4 @@ -

                                                            Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}

                                                            \ No newline at end of file +

                                                            Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}

                                                            diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js index 80e521a8bfa..7a8d08dd22d 100644 --- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js @@ -174,4 +174,4 @@ frappe.query_reports["Supplier Quotation Comparison"] = { }); dialog.show(); } -} \ No newline at end of file +} diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py index 2b371915f32..a5a3105a847 100644 --- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py @@ -263,4 +263,4 @@ def get_message():    Expires today / Already Expired - """ \ No newline at end of file +
                                                            """ diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py index a73cb0d62ec..17928634e78 100644 --- a/erpnext/buying/utils.py +++ b/erpnext/buying/utils.py @@ -102,4 +102,3 @@ def get_linked_material_requests(items): mr_list.append(material_request) return mr_list - diff --git a/erpnext/commands/__init__.py b/erpnext/commands/__init__.py index a991cf9881e..2276c738fbe 100644 --- a/erpnext/commands/__init__.py +++ b/erpnext/commands/__init__.py @@ -46,4 +46,4 @@ def make_demo(context, site, domain='Manufacturing', days=100, commands = [ make_demo -] \ No newline at end of file +] diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 26eb5bb337c..4c243d0cc46 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -842,7 +842,7 @@ class AccountsController(TransactionBase): dr_or_cr = "credit" rev_dr_cr = "debit" supplier_or_customer = self.supplier - + else: dr_or_cr = "debit" rev_dr_cr = "credit" @@ -853,11 +853,11 @@ class AccountsController(TransactionBase): discount_amount = item.discount_amount * item.qty if self.doctype == "Purchase Invoice": income_or_expense_account = (item.expense_account - if (not item.enable_deferred_expense or self.is_return) + if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) else: income_or_expense_account = (item.income_account - if (not item.enable_deferred_revenue or self.is_return) + if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account) account_currency = get_account_currency(item.discount_account) @@ -866,7 +866,7 @@ class AccountsController(TransactionBase): "account": item.discount_account, "against": supplier_or_customer, dr_or_cr: flt(discount_amount, item.precision('discount_amount')), - dr_or_cr + "_in_account_currency": flt(discount_amount * self.get('conversion_rate'), + dr_or_cr + "_in_account_currency": flt(discount_amount * self.get('conversion_rate'), item.precision('discount_amount')), "cost_center": item.cost_center, "project": item.project @@ -879,7 +879,7 @@ class AccountsController(TransactionBase): "account": income_or_expense_account, "against": supplier_or_customer, rev_dr_cr: flt(discount_amount, item.precision('discount_amount')), - rev_dr_cr + "_in_account_currency": flt(discount_amount * self.get('conversion_rate'), + rev_dr_cr + "_in_account_currency": flt(discount_amount * self.get('conversion_rate'), item.precision('discount_amount')), "cost_center": item.cost_center, "project": item.project or self.project @@ -894,8 +894,8 @@ class AccountsController(TransactionBase): dr_or_cr: self.discount_amount, "cost_center": self.cost_center }, item=self) - ) - + ) + def allocate_advance_taxes(self, gl_entries): tax_map = self.get_tax_map() for pe in self.get("advances"): @@ -1223,7 +1223,7 @@ class AccountsController(TransactionBase): po_or_so = self.get('items')[0].get('purchase_order') po_or_so_doctype = "Purchase Order" po_or_so_doctype_name = "purchase_order" - + return po_or_so, po_or_so_doctype, po_or_so_doctype_name def linked_order_has_payment_terms(self, po_or_so, fieldname, doctype): @@ -1232,14 +1232,14 @@ class AccountsController(TransactionBase): return True elif self.linked_order_has_payment_schedule(po_or_so): return True - + return False def all_items_have_same_po_or_so(self, po_or_so, fieldname): for item in self.get('items'): if item.get(fieldname) != po_or_so: return False - + return True def linked_order_has_payment_terms_template(self, po_or_so, doctype): @@ -1978,4 +1978,4 @@ def validate_regional(doc): @erpnext.allow_regional def validate_einvoice_fields(doc): - pass \ No newline at end of file + pass diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 051481ff603..8c361a2e561 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -344,4 +344,3 @@ def create_variant_doc_for_quick_entry(template, args): variant.name = variant.item_code validate_item_variant_attributes(variant, args) return variant.as_dict() - diff --git a/erpnext/controllers/subcontracting.py b/erpnext/controllers/subcontracting.py index 36ae1102164..969829f9651 100644 --- a/erpnext/controllers/subcontracting.py +++ b/erpnext/controllers/subcontracting.py @@ -390,4 +390,4 @@ class Subcontracting(): incorrect_sn = "\n".join(incorrect_sn) link = get_link_to_form('Purchase Order', row.purchase_order) msg = f'The Serial Nos {incorrect_sn} has not supplied against the Purchase Order {link}' - frappe.throw(_(msg), title=_("Incorrect Serial Number Consumed")) \ No newline at end of file + frappe.throw(_(msg), title=_("Incorrect Serial Number Consumed")) diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py index df73f09c493..f7c6b6c7993 100644 --- a/erpnext/crm/doctype/appointment/appointment.py +++ b/erpnext/crm/doctype/appointment/appointment.py @@ -235,4 +235,3 @@ def _get_employee_from_user(user): # frappe.db.exists returns a tuple of a tuple return frappe.get_doc('Employee', employee_docname[0][0]) return None - diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js index dc3ae8bf41a..0c64eb8e822 100644 --- a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js +++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js @@ -7,4 +7,4 @@ function check_times(frm) { frappe.throw(__('In row {0} of Appointment Booking Slots: "To Time" must be later than "From Time".', [i + 1])); } }); -} \ No newline at end of file +} diff --git a/erpnext/crm/doctype/contract/contract.js b/erpnext/crm/doctype/contract/contract.js index 99688551630..7848de7a727 100644 --- a/erpnext/crm/doctype/contract/contract.js +++ b/erpnext/crm/doctype/contract/contract.js @@ -15,7 +15,7 @@ frappe.ui.form.on("Contract", { let contract_template = r.message.contract_template; frm.set_value("contract_terms", r.message.contract_terms); frm.set_value("requires_fulfilment", contract_template.requires_fulfilment); - + if (frm.doc.requires_fulfilment) { // Populate the fulfilment terms table from a contract template, if any r.message.contract_template.fulfilment_terms.forEach(element => { @@ -23,7 +23,7 @@ frappe.ui.form.on("Contract", { d.requirement = element.requirement; }); frm.refresh_field("fulfilment_terms"); - } + } } } }); diff --git a/erpnext/crm/doctype/contract/contract_list.js b/erpnext/crm/doctype/contract/contract_list.js index 26a2907c7cc..7d5609651a1 100644 --- a/erpnext/crm/doctype/contract/contract_list.js +++ b/erpnext/crm/doctype/contract/contract_list.js @@ -9,4 +9,4 @@ frappe.listview_settings['Contract'] = { return [__(doc.status), "gray", "status,=," + doc.status]; } }, -}; \ No newline at end of file +}; diff --git a/erpnext/crm/doctype/contract_template/contract_template.py b/erpnext/crm/doctype/contract_template/contract_template.py index 69fd86f7fb5..9281220eef4 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.py +++ b/erpnext/crm/doctype/contract_template/contract_template.py @@ -24,8 +24,8 @@ def get_contract_template(template_name, doc): if contract_template.contract_terms: contract_terms = frappe.render_template(contract_template.contract_terms, doc) - + return { - 'contract_template': contract_template, + 'contract_template': contract_template, 'contract_terms': contract_terms - } \ No newline at end of file + } diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 7f028cb3160..c0ce6badbf5 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -34,7 +34,7 @@ class Lead(SellingController): "ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if (not cint(self.is_new())) else None, "contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if (not cint(self.is_new())) else None, }) - + def set_full_name(self): self.lead_name = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) diff --git a/erpnext/crm/doctype/lead/lead_dashboard.py b/erpnext/crm/doctype/lead/lead_dashboard.py index 69d8ca70926..3950d063f22 100644 --- a/erpnext/crm/doctype/lead/lead_dashboard.py +++ b/erpnext/crm/doctype/lead/lead_dashboard.py @@ -16,4 +16,4 @@ def get_data(): 'items': ['Opportunity', 'Quotation'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/crm/doctype/lead/test_lead.py b/erpnext/crm/doctype/lead/test_lead.py index d4886d35067..d7bc46165fd 100644 --- a/erpnext/crm/doctype/lead/test_lead.py +++ b/erpnext/crm/doctype/lead/test_lead.py @@ -82,4 +82,4 @@ def make_lead(**args): "email_id": args.email_id or "new_lead_{}@example.com".format(random_string(5)), }).insert() - return lead_doc \ No newline at end of file + return lead_doc diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index e9a7a95fc7d..632012b31d3 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -57,7 +57,7 @@ frappe.ui.form.on("Opportunity", { if (frm.doc.status == "Lost"){ frm.trigger('set_as_lost_dialog'); } - + }, customer_address: function(frm, cdt, cdn) { diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 23ad98a2828..8ce482a3f9f 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -372,4 +372,4 @@ def get_events(start, end, filters=None): "start": start, "end": end }, as_dict=True, update={"allDay": 0}) - return data \ No newline at end of file + return data diff --git a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py index 68f0104fd6c..b8c53f077ae 100644 --- a/erpnext/crm/doctype/opportunity/opportunity_dashboard.py +++ b/erpnext/crm/doctype/opportunity/opportunity_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Quotation', 'Supplier Quotation'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 04cd8a26cad..52aa0b036ae 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -87,4 +87,4 @@ def make_opportunity(**args): }) opp_doc.insert() - return opp_doc \ No newline at end of file + return opp_doc diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.js b/erpnext/crm/doctype/social_media_post/social_media_post.js index 0ce8b44e19b..6fb0f975f46 100644 --- a/erpnext/crm/doctype/social_media_post/social_media_post.js +++ b/erpnext/crm/doctype/social_media_post/social_media_post.js @@ -19,7 +19,7 @@ frappe.ui.form.on('Social Media Post', { refresh: function(frm){ if (frm.doc.docstatus === 1){ if (frm.doc.post_status != "Posted"){ - add_post_btn(frm); + add_post_btn(frm); } else if (frm.doc.post_status == "Posted"){ frm.set_df_property('sheduled_time', 'read_only', 1); @@ -63,5 +63,5 @@ var post = function(frm){ frappe.dom.unfreeze(); } }) - -} \ No newline at end of file + +} diff --git a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js index 0bc77a3f2a8..f29c2c64e14 100644 --- a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js +++ b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js @@ -16,4 +16,3 @@ frappe.query_reports["Campaign Efficiency"] = { } ] }; - diff --git a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py index ec498837f5e..238884b5190 100644 --- a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py +++ b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.py @@ -132,4 +132,4 @@ def get_order_amount(leads): where prevdoc_docname in ( select name from `tabQuotation` where status = 'Ordered' and quotation_to = 'Lead' and party_name in (%s) - )""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0] \ No newline at end of file + )""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0] diff --git a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js index 0325de9b8d9..eeb8984513e 100644 --- a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js +++ b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js @@ -20,5 +20,3 @@ frappe.query_reports["Lead Conversion Time"] = { }, ] }; - - diff --git a/erpnext/crm/report/lead_details/lead_details.js b/erpnext/crm/report/lead_details/lead_details.js index f92070daf3f..2f6d24224fb 100644 --- a/erpnext/crm/report/lead_details/lead_details.js +++ b/erpnext/crm/report/lead_details/lead_details.js @@ -49,4 +49,4 @@ frappe.query_reports["Lead Details"] = { "options": "Territory", } ] -}; \ No newline at end of file +}; diff --git a/erpnext/crm/report/lead_details/lead_details.py b/erpnext/crm/report/lead_details/lead_details.py index eeaaec2bce2..072a47611b7 100644 --- a/erpnext/crm/report/lead_details/lead_details.py +++ b/erpnext/crm/report/lead_details/lead_details.py @@ -107,7 +107,7 @@ def get_columns(): "options": "Country", "width": 100 }, - + ] return columns @@ -142,7 +142,7 @@ def get_data(filters): company = %(company)s AND `tabLead`.creation BETWEEN %(from_date)s AND %(to_date)s {conditions} - ORDER BY + ORDER BY `tabLead`.creation asc """.format(conditions=get_conditions(filters)), filters, as_dict=1) def get_conditions(filters) : @@ -153,6 +153,5 @@ def get_conditions(filters) : if filters.get("status"): conditions.append(" and `tabLead`.status=%(status)s") - - return " ".join(conditions) if conditions else "" + return " ".join(conditions) if conditions else "" diff --git a/erpnext/crm/report/lost_opportunity/lost_opportunity.js b/erpnext/crm/report/lost_opportunity/lost_opportunity.js index d79f8c8480f..97c56f8c434 100644 --- a/erpnext/crm/report/lost_opportunity/lost_opportunity.js +++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.js @@ -64,4 +64,4 @@ frappe.query_reports["Lost Opportunity"] = { "options": "User" }, ] -}; \ No newline at end of file +}; diff --git a/erpnext/crm/report/lost_opportunity/lost_opportunity.py b/erpnext/crm/report/lost_opportunity/lost_opportunity.py index 1aa4afe1865..858dcc4da81 100644 --- a/erpnext/crm/report/lost_opportunity/lost_opportunity.py +++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.py @@ -87,17 +87,17 @@ def get_data(filters): `tabOpportunity`.sales_stage, `tabOpportunity`.territory FROM - `tabOpportunity` + `tabOpportunity` {join} WHERE `tabOpportunity`.status = 'Lost' and `tabOpportunity`.company = %(company)s - AND `tabOpportunity`.modified BETWEEN %(from_date)s AND %(to_date)s - {conditions} - GROUP BY - `tabOpportunity`.name - ORDER BY + AND `tabOpportunity`.modified BETWEEN %(from_date)s AND %(to_date)s + {conditions} + GROUP BY + `tabOpportunity`.name + ORDER BY `tabOpportunity`.creation asc """.format(conditions=get_conditions(filters), join=get_join(filters)), filters, as_dict=1) - + def get_conditions(filters): conditions = [] @@ -117,15 +117,15 @@ def get_conditions(filters): return " ".join(conditions) if conditions else "" def get_join(filters): - join = """LEFT JOIN `tabOpportunity Lost Reason Detail` - ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and + join = """LEFT JOIN `tabOpportunity Lost Reason Detail` + ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and `tabOpportunity Lost Reason Detail`.parent = `tabOpportunity`.name""" if filters.get("lost_reason"): - join = """JOIN `tabOpportunity Lost Reason Detail` - ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and + join = """JOIN `tabOpportunity Lost Reason Detail` + ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and `tabOpportunity Lost Reason Detail`.parent = `tabOpportunity`.name and `tabOpportunity Lost Reason Detail`.lost_reason = '{0}' """.format(filters.get("lost_reason")) - - return join \ No newline at end of file + + return join diff --git a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py index 3a9d57d6075..425b7a8fdd7 100644 --- a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py +++ b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.py @@ -106,4 +106,4 @@ def get_lead_filters(filters): return lead_filters def get_creation_date_based_on_lead_age(filters): - return add_days(now(), (filters.get('lead_age') * -1)) \ No newline at end of file + return add_days(now(), (filters.get('lead_age') * -1)) diff --git a/erpnext/demo/domains.py b/erpnext/demo/domains.py index d5c2bfd2f02..b1db7b57b17 100644 --- a/erpnext/demo/domains.py +++ b/erpnext/demo/domains.py @@ -25,4 +25,4 @@ data = { 'Non Profit': { 'company_name': 'Erpnext Foundation' } -} \ No newline at end of file +} diff --git a/erpnext/demo/user/education.py b/erpnext/demo/user/education.py index fc31176e1e5..883a6d88cf2 100644 --- a/erpnext/demo/user/education.py +++ b/erpnext/demo/user/education.py @@ -19,7 +19,7 @@ def work(): approve_random_student_applicant() enroll_random_student(frappe.flags.current_date) # if frappe.flags.current_date.weekday()== 0: - # make_course_schedule(frappe.flags.current_date, frappe.utils.add_days(frappe.flags.current_date, 5)) + # make_course_schedule(frappe.flags.current_date, frappe.utils.add_days(frappe.flags.current_date, 5)) mark_student_attendance(frappe.flags.current_date) # make_assessment_plan() make_fees() @@ -48,7 +48,7 @@ def enroll_random_student(current_date): frappe.db.commit() assign_student_group(enrollment.student, enrollment.student_name, enrollment.program, enrolled_courses, enrollment.student_batch_name) - + def assign_student_group(student, student_name, program, courses, batch): course_list = [d["course"] for d in courses] for d in frappe.get_list("Student Group", fields=("name"), filters={"program": program, "course":("in", course_list), "disabled": 0}): @@ -69,11 +69,11 @@ def mark_student_attendance(current_date): students = get_student_group_students(d.name) for stud in students: make_attendance_records(stud.student, stud.student_name, status[weighted_choice([9,4])], None, d.name, current_date) - + def make_fees(): for d in range(1,10): random_fee = get_random("Fees", {"paid_amount": 0}) - collect_fees(random_fee, frappe.db.get_value("Fees", random_fee, "outstanding_amount")) + collect_fees(random_fee, frappe.db.get_value("Fees", random_fee, "outstanding_amount")) def make_assessment_plan(date): for d in range(1,4): @@ -84,7 +84,7 @@ def make_assessment_plan(date): doc.assessment_group = get_random("Assessment Group", {"is_group": 0, "parent": "2017-18 (Semester 2)"}) doc.grading_scale = get_random("Grading Scale") doc.maximum_assessment_score = 100 - + def make_course_schedule(start_date, end_date): for d in frappe.db.get_list("Student Group"): cs = frappe.new_doc("Scheduling Tool") @@ -114,4 +114,4 @@ def weighted_choice(weights): rnd = random.random() * running_total for i, total in enumerate(totals): if rnd < total: - return i \ No newline at end of file + return i diff --git a/erpnext/domains/agriculture.py b/erpnext/domains/agriculture.py index 8c7427ab2d1..9212d2ea719 100644 --- a/erpnext/domains/agriculture.py +++ b/erpnext/domains/agriculture.py @@ -25,4 +25,4 @@ data = { ], 'default_portal_role': 'System Manager', 'on_setup': 'erpnext.agriculture.setup.setup_agriculture' -} \ No newline at end of file +} diff --git a/erpnext/domains/education.py b/erpnext/domains/education.py index bbaa6e55d99..870624ab3b2 100644 --- a/erpnext/domains/education.py +++ b/erpnext/domains/education.py @@ -26,4 +26,4 @@ data = { ], 'on_setup': 'erpnext.education.setup.setup_education' -} \ No newline at end of file +} diff --git a/erpnext/domains/manufacturing.py b/erpnext/domains/manufacturing.py index 259ee9238e5..b9ad49e772b 100644 --- a/erpnext/domains/manufacturing.py +++ b/erpnext/domains/manufacturing.py @@ -21,4 +21,4 @@ data = { ['Stock Settings', None, 'show_barcode_field', 1] ], 'default_portal_role': 'Customer' -} \ No newline at end of file +} diff --git a/erpnext/domains/non_profit.py b/erpnext/domains/non_profit.py index b6772c53153..7c4f6b1f9de 100644 --- a/erpnext/domains/non_profit.py +++ b/erpnext/domains/non_profit.py @@ -21,4 +21,4 @@ data = { 'Non Profit' ], 'default_portal_role': 'Non Profit Manager' -} \ No newline at end of file +} diff --git a/erpnext/domains/services.py b/erpnext/domains/services.py index 7a4ffc4993f..89213720767 100644 --- a/erpnext/domains/services.py +++ b/erpnext/domains/services.py @@ -18,4 +18,4 @@ data = { ['Stock Settings', None, 'show_barcode_field', 0] ], 'default_portal_role': 'Customer' -} \ No newline at end of file +} diff --git a/erpnext/education/doctype/academic_term/academic_term.py b/erpnext/education/doctype/academic_term/academic_term.py index 3aa0be157bc..fa7f2899dcb 100644 --- a/erpnext/education/doctype/academic_term/academic_term.py +++ b/erpnext/education/doctype/academic_term/academic_term.py @@ -22,9 +22,9 @@ class AcademicTerm(Document): and getdate(self.term_start_date) > getdate(self.term_end_date): frappe.throw(_("The Term End Date cannot be earlier than the Term Start Date. Please correct the dates and try again.")) - # Check that the start of the term is not before the start of the academic year + # Check that the start of the term is not before the start of the academic year # and end of term is not after the end of the academic year""" - + year = frappe.get_doc("Academic Year",self.academic_year) if self.term_start_date and getdate(year.year_start_date) and (getdate(self.term_start_date) < getdate(year.year_start_date)): frappe.throw(_("The Term Start Date cannot be earlier than the Year Start Date of the Academic Year to which the term is linked (Academic Year {}). Please correct the dates and try again.").format(self.academic_year)) diff --git a/erpnext/education/doctype/academic_term/academic_term_dashboard.py b/erpnext/education/doctype/academic_term/academic_term_dashboard.py index 871e0f32845..eb2f90742ce 100644 --- a/erpnext/education/doctype/academic_term/academic_term_dashboard.py +++ b/erpnext/education/doctype/academic_term/academic_term_dashboard.py @@ -22,4 +22,4 @@ def get_data(): 'items': ['Assessment Plan', 'Assessment Result'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/academic_term/test_academic_term.js b/erpnext/education/doctype/academic_term/test_academic_term.js index 6d91e977c63..383b65a7032 100644 --- a/erpnext/education/doctype/academic_term/test_academic_term.js +++ b/erpnext/education/doctype/academic_term/test_academic_term.js @@ -21,4 +21,4 @@ QUnit.test('Test: Academic Term', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/academic_year/academic_year.js b/erpnext/education/doctype/academic_year/academic_year.js index 0e8619849cd..20e25281ffc 100644 --- a/erpnext/education/doctype/academic_year/academic_year.js +++ b/erpnext/education/doctype/academic_year/academic_year.js @@ -1,2 +1,2 @@ frappe.ui.form.on("Academic Year", { -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/academic_year/academic_year_dashboard.py b/erpnext/education/doctype/academic_year/academic_year_dashboard.py index f27f7d14cf6..d3734df8036 100644 --- a/erpnext/education/doctype/academic_year/academic_year_dashboard.py +++ b/erpnext/education/doctype/academic_year/academic_year_dashboard.py @@ -22,4 +22,4 @@ def get_data(): 'items': ['Assessment Plan', 'Assessment Result'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/academic_year/test_academic_year.js b/erpnext/education/doctype/academic_year/test_academic_year.js index ec2f49c5a1b..51e9cf307d8 100644 --- a/erpnext/education/doctype/academic_year/test_academic_year.js +++ b/erpnext/education/doctype/academic_year/test_academic_year.js @@ -20,4 +20,4 @@ QUnit.test('Test: Academic Year', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/article/article.js b/erpnext/education/doctype/article/article.js index edfec26273b..85b387f6217 100644 --- a/erpnext/education/doctype/article/article.js +++ b/erpnext/education/doctype/article/article.js @@ -53,4 +53,4 @@ let get_topics_without_article = function(article) { method: 'erpnext.education.doctype.article.article.get_topics_without_article', args: {'article': article} }); -}; \ No newline at end of file +}; diff --git a/erpnext/education/doctype/article/article.py b/erpnext/education/doctype/article/article.py index 8ba367da76e..b5cc5cbc7a6 100644 --- a/erpnext/education/doctype/article/article.py +++ b/erpnext/education/doctype/article/article.py @@ -18,4 +18,4 @@ def get_topics_without_article(article): topic_contents = [tc.content for tc in topic.topic_content] if not topic_contents or article not in topic_contents: data.append(topic.name) - return data \ No newline at end of file + return data diff --git a/erpnext/education/doctype/assessment_criteria/assessment_criteria.py b/erpnext/education/doctype/assessment_criteria/assessment_criteria.py index 1ea37023b24..bfbf26cf6c1 100644 --- a/erpnext/education/doctype/assessment_criteria/assessment_criteria.py +++ b/erpnext/education/doctype/assessment_criteria/assessment_criteria.py @@ -12,4 +12,4 @@ STD_CRITERIA = ["total", "total score", "total grade", "maximum score", "score", class AssessmentCriteria(Document): def validate(self): if self.assessment_criteria.lower() in STD_CRITERIA: - frappe.throw(_("Can't create standard criteria. Please rename the criteria")) \ No newline at end of file + frappe.throw(_("Can't create standard criteria. Please rename the criteria")) diff --git a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js index db4a4cf5a8d..724c4dac499 100644 --- a/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js +++ b/erpnext/education/doctype/assessment_criteria/test_assessment_criteria.js @@ -13,4 +13,4 @@ QUnit.test('Test: Assessment Criteria', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js b/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js index bcfcaf82e63..ab27e637239 100644 --- a/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js +++ b/erpnext/education/doctype/assessment_criteria_group/test_assessment_criteria_group.js @@ -12,4 +12,4 @@ QUnit.test('Test: Assessment Criteria Group', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py b/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py index 2649d4b90c9..1a23606a61d 100644 --- a/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py +++ b/erpnext/education/doctype/assessment_group/assessment_group_dashboard.py @@ -12,4 +12,4 @@ def get_data(): 'items': ['Assessment Plan', 'Assessment Result'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/assessment_group/assessment_group_tree.js b/erpnext/education/doctype/assessment_group/assessment_group_tree.js index e4676831a3b..e0dfaa31fd7 100644 --- a/erpnext/education/doctype/assessment_group/assessment_group_tree.js +++ b/erpnext/education/doctype/assessment_group/assessment_group_tree.js @@ -1,3 +1,3 @@ frappe.treeview_settings["Assessment Group"] = { - -} \ No newline at end of file + +} diff --git a/erpnext/education/doctype/assessment_group/test_assessment_group.js b/erpnext/education/doctype/assessment_group/test_assessment_group.js index a127fd4adf5..00e6309837d 100644 --- a/erpnext/education/doctype/assessment_group/test_assessment_group.js +++ b/erpnext/education/doctype/assessment_group/test_assessment_group.js @@ -62,4 +62,4 @@ frappe.map_group = { () => frappe.click_button('Create New'), ]); } -}; \ No newline at end of file +}; diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan.js b/erpnext/education/doctype/assessment_plan/assessment_plan.js index 726c0fcecd4..cf545c41afb 100644 --- a/erpnext/education/doctype/assessment_plan/assessment_plan.js +++ b/erpnext/education/doctype/assessment_plan/assessment_plan.js @@ -75,4 +75,4 @@ frappe.ui.form.on('Assessment Plan', { maximum_assessment_score: function(frm) { frm.trigger('course'); } -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py index 5e6c29dcdf3..8ac3faf6dde 100644 --- a/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py +++ b/erpnext/education/doctype/assessment_plan/assessment_plan_dashboard.py @@ -18,4 +18,4 @@ def get_data(): 'items': ['Assessment Plan Status'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/assessment_result/assessment_result.js b/erpnext/education/doctype/assessment_result/assessment_result.js index c35f607a99d..b6d28817b5d 100644 --- a/erpnext/education/doctype/assessment_result/assessment_result.js +++ b/erpnext/education/doctype/assessment_result/assessment_result.js @@ -122,4 +122,4 @@ frappe.ui.form.on('Assessment Result Detail', { }); } } -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/assessment_result/assessment_result.py b/erpnext/education/doctype/assessment_result/assessment_result.py index 6b873ecf97a..7dfe0cf6c27 100644 --- a/erpnext/education/doctype/assessment_result/assessment_result.py +++ b/erpnext/education/doctype/assessment_result/assessment_result.py @@ -42,7 +42,3 @@ class AssessmentResult(Document): "student":self.student, "assessment_plan":self.assessment_plan, "docstatus":("!=", 2)}) if assessment_result: frappe.throw(_("Assessment Result record {0} already exists.").format(getlink("Assessment Result",assessment_result[0].name))) - - - - diff --git a/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py b/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py index 438379d08e4..2526076d308 100644 --- a/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py +++ b/erpnext/education/doctype/assessment_result/assessment_result_dashboard.py @@ -11,4 +11,4 @@ def get_data(): 'items': ['Final Assessment Grades', 'Course wise Assessment Report'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/assessment_result/test_assessment_result.js b/erpnext/education/doctype/assessment_result/test_assessment_result.js index b7adfacb1a2..d4eb4b8ba66 100644 --- a/erpnext/education/doctype/assessment_result/test_assessment_result.js +++ b/erpnext/education/doctype/assessment_result/test_assessment_result.js @@ -70,4 +70,4 @@ QUnit.test('Test: Assessment Result', function(assert){ () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/assessment_result/test_assessment_result.py b/erpnext/education/doctype/assessment_result/test_assessment_result.py index e5535d6085a..adce57769dd 100644 --- a/erpnext/education/doctype/assessment_result/test_assessment_result.py +++ b/erpnext/education/doctype/assessment_result/test_assessment_result.py @@ -16,4 +16,3 @@ class TestAssessmentResult(unittest.TestCase): grade = get_grade("_Test Grading Scale", 70) self.assertEqual("B", grade) - \ No newline at end of file diff --git a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py index 649f420d41f..a0d286ccbe9 100644 --- a/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py +++ b/erpnext/education/doctype/assessment_result_tool/assessment_result_tool.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class AssessmentResultTool(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js b/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js index 0bbe33194a3..7ef5c688fb9 100644 --- a/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js +++ b/erpnext/education/doctype/assessment_result_tool/test_assessment_result_tool.js @@ -26,4 +26,4 @@ QUnit.test('Test: Assessment Result Tool', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/course/course.js b/erpnext/education/doctype/course/course.js index 81e4a8c08d3..bd8d62c8d2a 100644 --- a/erpnext/education/doctype/course/course.js +++ b/erpnext/education/doctype/course/course.js @@ -76,4 +76,4 @@ let get_programs_without_course = function(course) { method: 'erpnext.education.doctype.course.course.get_programs_without_course', args: {'course': course} }); -} \ No newline at end of file +} diff --git a/erpnext/education/doctype/course/course.py b/erpnext/education/doctype/course/course.py index 06efa54e770..92f92ed9f3e 100644 --- a/erpnext/education/doctype/course/course.py +++ b/erpnext/education/doctype/course/course.py @@ -53,4 +53,4 @@ def get_programs_without_course(course): courses = [c.course for c in program.courses] if not courses or course not in courses: data.append(program.name) - return data \ No newline at end of file + return data diff --git a/erpnext/education/doctype/course/course_dashboard.py b/erpnext/education/doctype/course/course_dashboard.py index 8a570bdc579..8de91b1c092 100644 --- a/erpnext/education/doctype/course/course_dashboard.py +++ b/erpnext/education/doctype/course/course_dashboard.py @@ -20,4 +20,4 @@ def get_data(): 'items': ['Assessment Plan', 'Assessment Result'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/course/test_course.js b/erpnext/education/doctype/course/test_course.js index 88fddc2bb6d..2b6860cb7f4 100644 --- a/erpnext/education/doctype/course/test_course.js +++ b/erpnext/education/doctype/course/test_course.js @@ -33,4 +33,4 @@ QUnit.test('test course', function(assert) { }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/course_activity/course_activity.py b/erpnext/education/doctype/course_activity/course_activity.py index e7fc08a1d7f..3aa1ea0c5b3 100644 --- a/erpnext/education/doctype/course_activity/course_activity.py +++ b/erpnext/education/doctype/course_activity/course_activity.py @@ -16,4 +16,4 @@ class CourseActivity(Document): if frappe.db.exists("Course Enrollment", self.enrollment): return True else: - frappe.throw(_("Course Enrollment {0} does not exists").format(self.enrollment)) \ No newline at end of file + frappe.throw(_("Course Enrollment {0} does not exists").format(self.enrollment)) diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py b/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py index b9dd457b61c..37972fe354c 100644 --- a/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py +++ b/erpnext/education/doctype/course_enrollment/course_enrollment_dashboard.py @@ -12,4 +12,4 @@ def get_data(): 'items': ['Course Activity', 'Quiz Activity'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py index e22c7ce0bab..874bf121f47 100644 --- a/erpnext/education/doctype/course_enrollment/test_course_enrollment.py +++ b/erpnext/education/doctype/course_enrollment/test_course_enrollment.py @@ -39,6 +39,3 @@ class TestCourseEnrollment(unittest.TestCase): doc = frappe.get_doc("Program Enrollment", entry.name) doc.cancel() doc.delete() - - - diff --git a/erpnext/education/doctype/course_schedule/course_schedule.js b/erpnext/education/doctype/course_schedule/course_schedule.js index 4275f6ef2a6..366bbd8b0d4 100644 --- a/erpnext/education/doctype/course_schedule/course_schedule.js +++ b/erpnext/education/doctype/course_schedule/course_schedule.js @@ -13,4 +13,4 @@ frappe.ui.form.on("Course Schedule", { }).addClass("btn-primary"); } } -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/course_schedule/course_schedule.py b/erpnext/education/doctype/course_schedule/course_schedule.py index 5083ff6589d..748728d104e 100644 --- a/erpnext/education/doctype/course_schedule/course_schedule.py +++ b/erpnext/education/doctype/course_schedule/course_schedule.py @@ -14,11 +14,11 @@ class CourseSchedule(Document): self.validate_course() self.validate_date() self.validate_overlap() - + def set_title(self): """Set document Title""" self.title = self.course + " by " + (self.instructor_name if self.instructor_name else self.instructor) - + def validate_course(self): group_based_on, course = frappe.db.get_value("Student Group", self.student_group, ["group_based_on", "course"]) if group_based_on == "Course": @@ -28,23 +28,22 @@ class CourseSchedule(Document): """Validates if from_time is greater than to_time""" if self.from_time > self.to_time: frappe.throw(_("From Time cannot be greater than To Time.")) - + def validate_overlap(self): """Validates overlap for Student Group, Instructor, Room""" - + from erpnext.education.utils import validate_overlap_for #Validate overlapping course schedules. if self.student_group: validate_overlap_for(self, "Course Schedule", "student_group") - + validate_overlap_for(self, "Course Schedule", "instructor") validate_overlap_for(self, "Course Schedule", "room") #validate overlapping assessment schedules. if self.student_group: validate_overlap_for(self, "Assessment Plan", "student_group") - + validate_overlap_for(self, "Assessment Plan", "room") validate_overlap_for(self, "Assessment Plan", "supervisor", self.instructor) - diff --git a/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py b/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py index 0866cd65357..22ce7e1ec24 100644 --- a/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py +++ b/erpnext/education/doctype/course_schedule/course_schedule_dashboard.py @@ -12,4 +12,4 @@ def get_data(): 'items': ['Student Attendance'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/course_schedule/test_course_schedule.py b/erpnext/education/doctype/course_schedule/test_course_schedule.py index a901f1e467a..5bb4de85846 100644 --- a/erpnext/education/doctype/course_schedule/test_course_schedule.py +++ b/erpnext/education/doctype/course_schedule/test_course_schedule.py @@ -17,28 +17,28 @@ class TestCourseSchedule(unittest.TestCase): def test_student_group_conflict(self): cs1 = make_course_schedule_test_record(simulate= True) - cs2 = make_course_schedule_test_record(schedule_date=cs1.schedule_date, from_time= cs1.from_time, + cs2 = make_course_schedule_test_record(schedule_date=cs1.schedule_date, from_time= cs1.from_time, to_time= cs1.to_time, instructor="_Test Instructor 2", room=frappe.get_all("Room")[1].name, do_not_save= 1) self.assertRaises(OverlapError, cs2.save) def test_instructor_conflict(self): cs1 = make_course_schedule_test_record(simulate= True) - - cs2 = make_course_schedule_test_record(from_time= cs1.from_time, to_time= cs1.to_time, + + cs2 = make_course_schedule_test_record(from_time= cs1.from_time, to_time= cs1.to_time, student_group="Course-TC101-2014-2015 (_Test Academic Term)", room=frappe.get_all("Room")[1].name, do_not_save= 1) self.assertRaises(OverlapError, cs2.save) def test_room_conflict(self): cs1 = make_course_schedule_test_record(simulate= True) - - cs2 = make_course_schedule_test_record(from_time= cs1.from_time, to_time= cs1.to_time, + + cs2 = make_course_schedule_test_record(from_time= cs1.from_time, to_time= cs1.to_time, student_group="Course-TC101-2014-2015 (_Test Academic Term)", instructor="_Test Instructor 2", do_not_save= 1) self.assertRaises(OverlapError, cs2.save) - + def test_no_conflict(self): cs1 = make_course_schedule_test_record(simulate= True) - - make_course_schedule_test_record(from_time= cs1.from_time, to_time= cs1.to_time, + + make_course_schedule_test_record(from_time= cs1.from_time, to_time= cs1.to_time, student_group="Course-TC102-2014-2015 (_Test Academic Term)", instructor="_Test Instructor 2", room=frappe.get_all("Room")[1].name) def make_course_schedule_test_record(**args): @@ -49,12 +49,12 @@ def make_course_schedule_test_record(**args): course_schedule.course = args.course or "TC101" course_schedule.instructor = args.instructor or "_Test Instructor" course_schedule.room = args.room or frappe.get_all("Room")[0].name - + course_schedule.schedule_date = args.schedule_date or today() course_schedule.from_time = args.from_time or to_timedelta("01:00:00") course_schedule.to_time = args.to_time or course_schedule.from_time + datetime.timedelta(hours= 1) - + if not args.do_not_save: if args.simulate: while True: diff --git a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js index d57f46ab98e..7b0e4ab47c8 100644 --- a/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js +++ b/erpnext/education/doctype/course_scheduling_tool/course_scheduling_tool.js @@ -41,4 +41,4 @@ frappe.ui.form.on('Course Scheduling Tool', { }); }); } -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/education_settings/education_settings.py b/erpnext/education/doctype/education_settings/education_settings.py index 658380ea429..6c7e95c80da 100644 --- a/erpnext/education/doctype/education_settings/education_settings.py +++ b/erpnext/education/doctype/education_settings/education_settings.py @@ -36,4 +36,4 @@ class EducationSettings(Document): make_property_setter('Instructor', "naming_series", "hidden", 1, "Check", validate_fields_for_doctype=False) def update_website_context(context): - context["lms_enabled"] = frappe.get_doc("Education Settings").enable_lms \ No newline at end of file + context["lms_enabled"] = frappe.get_doc("Education Settings").enable_lms diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule.js b/erpnext/education/doctype/fee_schedule/fee_schedule.js index 0089957df40..97691a5b62a 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule.js +++ b/erpnext/education/doctype/fee_schedule/fee_schedule.js @@ -130,4 +130,4 @@ frappe.ui.form.on('Fee Schedule Student Group', { }); } } -}) \ No newline at end of file +}) diff --git a/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py b/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py index acfe4002193..4d7da21ea17 100644 --- a/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py +++ b/erpnext/education/doctype/fee_schedule/fee_schedule_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Fees'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/fee_structure/fee_structure.js b/erpnext/education/doctype/fee_structure/fee_structure.js index 310c4105f47..d9ab99f8180 100644 --- a/erpnext/education/doctype/fee_structure/fee_structure.js +++ b/erpnext/education/doctype/fee_structure/fee_structure.js @@ -69,4 +69,4 @@ frappe.ui.form.on('Fee Component', { } frm.set_value('total_amount', total_amount); } -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/fee_structure/fee_structure.py b/erpnext/education/doctype/fee_structure/fee_structure.py index 781382b51be..9755717ee94 100644 --- a/erpnext/education/doctype/fee_structure/fee_structure.py +++ b/erpnext/education/doctype/fee_structure/fee_structure.py @@ -11,13 +11,13 @@ from frappe.model.mapper import get_mapped_doc class FeeStructure(Document): def validate(self): self.calculate_total() - + def calculate_total(self): """Calculates total amount.""" self.total_amount = 0 for d in self.components: self.total_amount += d.amount - + @frappe.whitelist() def make_fee_schedule(source_name, target_doc=None): @@ -31,4 +31,4 @@ def make_fee_schedule(source_name, target_doc=None): "Fee Component": { "doctype": "Fee Component" } - }, target_doc) \ No newline at end of file + }, target_doc) diff --git a/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py b/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py index 73e314f3512..fdf7df7aa26 100644 --- a/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py +++ b/erpnext/education/doctype/fee_structure/fee_structure_dashboard.py @@ -12,4 +12,4 @@ def get_data(): 'items': ['Fees', 'Fee Schedule'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/fees/fees.py b/erpnext/education/doctype/fees/fees.py index 25d67d2d5f6..7e867049047 100644 --- a/erpnext/education/doctype/fees/fees.py +++ b/erpnext/education/doctype/fees/fees.py @@ -132,4 +132,4 @@ def get_list_context(context=None): "title": _("Fees"), "get_list": get_fee_list, "row_template": "templates/includes/fee/fee_row.html" - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/fees/fees_list.js b/erpnext/education/doctype/fees/fees_list.js index 52e1c4beb5a..ee8e1e382e9 100644 --- a/erpnext/education/doctype/fees/fees_list.js +++ b/erpnext/education/doctype/fees/fees_list.js @@ -9,4 +9,4 @@ frappe.listview_settings['Fees'] = { return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/education/doctype/grading_scale/grading_scale.py b/erpnext/education/doctype/grading_scale/grading_scale.py index 6309d02c155..0e732971619 100644 --- a/erpnext/education/doctype/grading_scale/grading_scale.py +++ b/erpnext/education/doctype/grading_scale/grading_scale.py @@ -17,4 +17,4 @@ class GradingScale(Document): else: thresholds.append(cint(d.threshold)) if 0 not in thresholds: - frappe.throw(_("Please define grade for Threshold 0%")) \ No newline at end of file + frappe.throw(_("Please define grade for Threshold 0%")) diff --git a/erpnext/education/doctype/grading_scale/test_grading_scale.js b/erpnext/education/doctype/grading_scale/test_grading_scale.js index e363545ff8d..fb56918fdb8 100644 --- a/erpnext/education/doctype/grading_scale/test_grading_scale.js +++ b/erpnext/education/doctype/grading_scale/test_grading_scale.js @@ -99,4 +99,4 @@ QUnit.test('Test: Grading Scale', function(assert){ () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/guardian/test_guardian.js b/erpnext/education/doctype/guardian/test_guardian.js index 9bbfacd5802..1ea6dc290bd 100644 --- a/erpnext/education/doctype/guardian/test_guardian.js +++ b/erpnext/education/doctype/guardian/test_guardian.js @@ -31,4 +31,4 @@ QUnit.test('Test: Guardian', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/instructor/instructor.js b/erpnext/education/doctype/instructor/instructor.js index 24e80fa9378..034b0aaf5dd 100644 --- a/erpnext/education/doctype/instructor/instructor.js +++ b/erpnext/education/doctype/instructor/instructor.js @@ -61,4 +61,4 @@ frappe.ui.form.on("Instructor", { }; }); } -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/instructor/instructor_dashboard.py b/erpnext/education/doctype/instructor/instructor_dashboard.py index a404fc56c54..c19c85947d6 100644 --- a/erpnext/education/doctype/instructor/instructor_dashboard.py +++ b/erpnext/education/doctype/instructor/instructor_dashboard.py @@ -21,4 +21,4 @@ def get_data(): 'items': ['Student Group'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/program/program.js b/erpnext/education/doctype/program/program.js index 98263b55a1f..2d89351504b 100644 --- a/erpnext/education/doctype/program/program.js +++ b/erpnext/education/doctype/program/program.js @@ -4,5 +4,5 @@ cur_frm.add_fetch('fee_structure', 'total_amount', 'amount'); frappe.ui.form.on("Program", "refresh", function(frm) { - -}); \ No newline at end of file + +}); diff --git a/erpnext/education/doctype/program/program.py b/erpnext/education/doctype/program/program.py index d24df5d6142..9d886b7b9e6 100644 --- a/erpnext/education/doctype/program/program.py +++ b/erpnext/education/doctype/program/program.py @@ -11,4 +11,4 @@ class Program(Document): def get_course_list(self): program_course_list = self.courses course_list = [frappe.get_doc("Course", program_course.course) for program_course in program_course_list] - return course_list \ No newline at end of file + return course_list diff --git a/erpnext/education/doctype/program/program_dashboard.py b/erpnext/education/doctype/program/program_dashboard.py index c5d249451f2..6c503e1bf1f 100644 --- a/erpnext/education/doctype/program/program_dashboard.py +++ b/erpnext/education/doctype/program/program_dashboard.py @@ -21,4 +21,4 @@ def get_data(): 'items': ['Assessment Plan', 'Assessment Result'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/program/test_program.js b/erpnext/education/doctype/program/test_program.js index dc347cf1b06..b9ca41ae3f9 100644 --- a/erpnext/education/doctype/program/test_program.js +++ b/erpnext/education/doctype/program/test_program.js @@ -31,4 +31,4 @@ QUnit.test('Test: Program', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/program/test_program.py b/erpnext/education/doctype/program/test_program.py index d7530365117..204f2961e7f 100644 --- a/erpnext/education/doctype/program/test_program.py +++ b/erpnext/education/doctype/program/test_program.py @@ -88,4 +88,4 @@ def setup_program(): course_list = [course['course_name'] for course in test_data['course']] program = make_program_and_linked_courses(test_data['program_name'], course_list) - return program \ No newline at end of file + return program diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.js b/erpnext/education/doctype/program_enrollment/program_enrollment.js index f9c65fbbfb3..e92d063602d 100644 --- a/erpnext/education/doctype/program_enrollment/program_enrollment.js +++ b/erpnext/education/doctype/program_enrollment/program_enrollment.js @@ -101,4 +101,4 @@ frappe.ui.form.on('Program Enrollment Course', { return { filters: [['Course', 'name', 'not in', course_list]] }; }; } -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.py b/erpnext/education/doctype/program_enrollment/program_enrollment.py index b282babd0fc..dd4aa576ac0 100644 --- a/erpnext/education/doctype/program_enrollment/program_enrollment.py +++ b/erpnext/education/doctype/program_enrollment/program_enrollment.py @@ -174,4 +174,3 @@ def get_students(doctype, txt, searchfield, start, page_len, filters): tuple(students + ["%%%s%%" % txt, start, page_len] ) ) - diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py b/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py index 18d307cdf07..c47f8666898 100644 --- a/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py +++ b/erpnext/education/doctype/program_enrollment/program_enrollment_dashboard.py @@ -16,4 +16,4 @@ def get_data(): 'items': ['Student and Guardian Contact Details'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py index fec6422e75f..497ee288aac 100644 --- a/erpnext/education/doctype/program_enrollment/test_program_enrollment.py +++ b/erpnext/education/doctype/program_enrollment/test_program_enrollment.py @@ -32,4 +32,4 @@ class TestProgramEnrollment(unittest.TestCase): for entry in frappe.db.get_all("Program Enrollment"): doc = frappe.get_doc("Program Enrollment", entry.name) doc.cancel() - doc.delete() \ No newline at end of file + doc.delete() diff --git a/erpnext/education/doctype/question/question.py b/erpnext/education/doctype/question/question.py index a7deeab6f65..fb3b50478c8 100644 --- a/erpnext/education/doctype/question/question.py +++ b/erpnext/education/doctype/question/question.py @@ -43,4 +43,4 @@ class Question(Document): elif len(answers) == 1: return answers[0] else: - return answers \ No newline at end of file + return answers diff --git a/erpnext/education/doctype/quiz/quiz.js b/erpnext/education/doctype/quiz/quiz.js index 01bcf732360..320869be31e 100644 --- a/erpnext/education/doctype/quiz/quiz.js +++ b/erpnext/education/doctype/quiz/quiz.js @@ -68,4 +68,4 @@ let get_topics_without_quiz = function(quiz) { method: 'erpnext.education.doctype.quiz.quiz.get_topics_without_quiz', args: {'quiz': quiz} }); -}; \ No newline at end of file +}; diff --git a/erpnext/education/doctype/quiz/quiz.py b/erpnext/education/doctype/quiz/quiz.py index a774b88579a..a128e1f3427 100644 --- a/erpnext/education/doctype/quiz/quiz.py +++ b/erpnext/education/doctype/quiz/quiz.py @@ -68,4 +68,4 @@ def get_topics_without_quiz(quiz): topic_contents = [tc.content for tc in topic.topic_content] if not topic_contents or quiz not in topic_contents: data.append(topic.name) - return data \ No newline at end of file + return data diff --git a/erpnext/education/doctype/room/room.js b/erpnext/education/doctype/room/room.js index 20cee6b2a61..1263b60ced2 100644 --- a/erpnext/education/doctype/room/room.js +++ b/erpnext/education/doctype/room/room.js @@ -1,2 +1,2 @@ frappe.ui.form.on("Room", { -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/room/room_dashboard.py b/erpnext/education/doctype/room/room_dashboard.py index 99aac3393e6..7bcb97f709f 100644 --- a/erpnext/education/doctype/room/room_dashboard.py +++ b/erpnext/education/doctype/room/room_dashboard.py @@ -16,4 +16,4 @@ def get_data(): 'items': ['Assessment Plan'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/student/student.js b/erpnext/education/doctype/student/student.js index fd23ae41ef1..aead556dc9f 100644 --- a/erpnext/education/doctype/student/student.js +++ b/erpnext/education/doctype/student/student.js @@ -60,4 +60,4 @@ frappe.ui.form.on('Student Sibling', { return { filters: [['Student', 'name', 'not in', sibling_list]] }; }; } -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student/student_list.js b/erpnext/education/doctype/student/student_list.js index 763f120f417..c1bce24d153 100644 --- a/erpnext/education/doctype/student/student_list.js +++ b/erpnext/education/doctype/student/student_list.js @@ -1,3 +1,3 @@ frappe.listview_settings['Student'] = { add_fields: [ "image"] -} \ No newline at end of file +} diff --git a/erpnext/education/doctype/student/test_student.py b/erpnext/education/doctype/student/test_student.py index 2e5263788f7..fcb2b5fb930 100644 --- a/erpnext/education/doctype/student/test_student.py +++ b/erpnext/education/doctype/student/test_student.py @@ -68,4 +68,4 @@ def get_student(email): student_id = frappe.get_all("Student", {"student_email_id": email}, ["name"])[0].name return frappe.get_doc("Student", student_id) except IndexError: - return None \ No newline at end of file + return None diff --git a/erpnext/education/doctype/student_admission/templates/student_admission_row.html b/erpnext/education/doctype/student_admission/templates/student_admission_row.html index 99868d5f020..529d65184a8 100644 --- a/erpnext/education/doctype/student_admission/templates/student_admission_row.html +++ b/erpnext/education/doctype/student_admission/templates/student_admission_row.html @@ -41,4 +41,4 @@ - \ No newline at end of file + diff --git a/erpnext/education/doctype/student_admission/test_student_admission.js b/erpnext/education/doctype/student_admission/test_student_admission.js index 3a0bb0b2f23..e01791a78ac 100644 --- a/erpnext/education/doctype/student_admission/test_student_admission.js +++ b/erpnext/education/doctype/student_admission/test_student_admission.js @@ -37,4 +37,4 @@ QUnit.test('Test: Student Admission', function(assert) { }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_applicant/student_applicant.js b/erpnext/education/doctype/student_applicant/student_applicant.js index b4cfdf16e0d..7b41a721748 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.js +++ b/erpnext/education/doctype/student_applicant/student_applicant.js @@ -59,4 +59,4 @@ frappe.ui.form.on('Student Sibling', { frm.add_fetch("student", "gender", "gender"); frm.add_fetch("student", "date_of_birth", "date_of_birth"); } -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_applicant/student_applicant.py b/erpnext/education/doctype/student_applicant/student_applicant.py index 211348201e3..193b6d32977 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.py +++ b/erpnext/education/doctype/student_applicant/student_applicant.py @@ -49,7 +49,7 @@ class StudentApplicant(Document): frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant")) def validation_from_student_admission(self): - + student_admission = get_student_admission_data(self.student_admission, self.program) if student_admission and student_admission.min_age and \ diff --git a/erpnext/education/doctype/student_applicant/student_applicant_list.js b/erpnext/education/doctype/student_applicant/student_applicant_list.js index 817a728f696..c39d46ec63a 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant_list.js +++ b/erpnext/education/doctype/student_applicant/student_applicant_list.js @@ -18,4 +18,4 @@ frappe.listview_settings['Student Applicant'] = { return [__("Admitted"), "blue", "application_status,=,Admitted"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js index a69ad8a5646..fa679779858 100644 --- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js +++ b/erpnext/education/doctype/student_applicant/tests/test_student_applicant.js @@ -92,4 +92,4 @@ QUnit.test('Test: Student Applicant', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js index 26244ab1845..03101e41e00 100644 --- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js +++ b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_dummy_data.js @@ -84,4 +84,4 @@ QUnit.test('Make Students', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js index 114358f32a1..daa36e75ce4 100644 --- a/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js +++ b/erpnext/education/doctype/student_applicant/tests/test_student_applicant_options.js @@ -107,4 +107,4 @@ QUnit.test('test student applicant', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_attendance/student_attendance.js b/erpnext/education/doctype/student_attendance/student_attendance.js index f025a1a5397..2bbecb911f6 100644 --- a/erpnext/education/doctype/student_attendance/student_attendance.js +++ b/erpnext/education/doctype/student_attendance/student_attendance.js @@ -2,4 +2,4 @@ // For license information, please see license.txt cur_frm.add_fetch("course_schedule", "schedule_date", "date"); -cur_frm.add_fetch("course_schedule", "student_group", "student_group") \ No newline at end of file +cur_frm.add_fetch("course_schedule", "student_group", "student_group") diff --git a/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py b/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py index 9c41b8f3dc6..e405b8aed9d 100644 --- a/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py +++ b/erpnext/education/doctype/student_attendance/student_attendance_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Student Monthly Attendance Sheet', 'Student Batch-Wise Attendance'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/student_attendance/student_attendance_list.js b/erpnext/education/doctype/student_attendance/student_attendance_list.js index 0d3e7ade152..e89b76c8d55 100644 --- a/erpnext/education/doctype/student_attendance/student_attendance_list.js +++ b/erpnext/education/doctype/student_attendance/student_attendance_list.js @@ -8,4 +8,4 @@ frappe.listview_settings['Student Attendance'] = { return [__("Present"), "green", "status,=,Present"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/education/doctype/student_attendance/test_student_attendance.js b/erpnext/education/doctype/student_attendance/test_student_attendance.js index c7da6f6b246..3d30b090ba0 100644 --- a/erpnext/education/doctype/student_attendance/test_student_attendance.js +++ b/erpnext/education/doctype/student_attendance/test_student_attendance.js @@ -28,4 +28,4 @@ QUnit.test('Test: Student Attendance', function(assert){ () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py index 028db918812..972973fbadb 100644 --- a/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py +++ b/erpnext/education/doctype/student_attendance_tool/student_attendance_tool.py @@ -38,4 +38,4 @@ def get_student_attendance_records(based_on, date=None, student_group=None, cour if student.student == attendance.student: student.status = attendance.status - return student_list \ No newline at end of file + return student_list diff --git a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js b/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js index cea0761ae8b..b66d8397ba2 100644 --- a/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js +++ b/erpnext/education/doctype/student_attendance_tool/test_student_attendance_tool.js @@ -82,4 +82,4 @@ QUnit.test('Test: Student Attendace Tool', function(assert){ () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_group/student_group.js b/erpnext/education/doctype/student_group/student_group.js index 51e3b74a5cf..39ee9cebd10 100644 --- a/erpnext/education/doctype/student_group/student_group.js +++ b/erpnext/education/doctype/student_group/student_group.js @@ -142,4 +142,4 @@ frappe.ui.form.on('Student Group Instructor', { return { filters: [['Instructor', 'name', 'not in', instructor_list]] }; }; } -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_group/student_group.py b/erpnext/education/doctype/student_group/student_group.py index 0260b808646..3d4572abf70 100644 --- a/erpnext/education/doctype/student_group/student_group.py +++ b/erpnext/education/doctype/student_group/student_group.py @@ -128,4 +128,3 @@ def fetch_students(doctype, txt, searchfield, start, page_len, filters): order by idx desc, name limit %s, %s""".format(searchfield), tuple(["%%%s%%" % txt, "%%%s%%" % txt, start, page_len])) - diff --git a/erpnext/education/doctype/student_group/student_group_dashboard.py b/erpnext/education/doctype/student_group/student_group_dashboard.py index ad7a6de7b3c..d37445f7b98 100644 --- a/erpnext/education/doctype/student_group/student_group_dashboard.py +++ b/erpnext/education/doctype/student_group/student_group_dashboard.py @@ -16,4 +16,4 @@ def get_data(): 'items': ['Course Schedule'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/student_group/test_student_group.js b/erpnext/education/doctype/student_group/test_student_group.js index 6673343be7e..4c7e47bc38f 100644 --- a/erpnext/education/doctype/student_group/test_student_group.js +++ b/erpnext/education/doctype/student_group/test_student_group.js @@ -53,4 +53,4 @@ QUnit.test('Test: Student Group', function(assert){ () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.js b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.js index d0d7afd701c..c189e2763c8 100644 --- a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.js +++ b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.js @@ -37,4 +37,4 @@ frappe.ui.form.on("Student Group Creation Tool", "onload", function(frm){ } }; }); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py index dc8667ec065..28ff7d618c1 100644 --- a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py +++ b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py @@ -76,4 +76,4 @@ class StudentGroupCreationTool(Document): student_group.append('students', student) student_group.save() - frappe.msgprint(_("{0} Student Groups created.").format(l)) \ No newline at end of file + frappe.msgprint(_("{0} Student Groups created.").format(l)) diff --git a/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js b/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js index 34c10930b57..fa612ba2727 100644 --- a/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js +++ b/erpnext/education/doctype/student_group_creation_tool/test_student_group_creation_tool.js @@ -81,4 +81,4 @@ QUnit.test('Test: Student Group Creation Tool', function(assert){ () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_group_student/student_group_student.py b/erpnext/education/doctype/student_group_student/student_group_student.py index 820e30118dc..1fe4ea1dc35 100644 --- a/erpnext/education/doctype/student_group_student/student_group_student.py +++ b/erpnext/education/doctype/student_group_student/student_group_student.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class StudentGroupStudent(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py b/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py index fdcc1474797..0ff6d1a76ea 100644 --- a/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py +++ b/erpnext/education/doctype/student_leave_application/student_leave_application_dashboard.py @@ -8,4 +8,4 @@ def get_data(): 'items': ['Student Attendance'] } ] - } \ No newline at end of file + } diff --git a/erpnext/education/doctype/student_leave_application/test_student_leave_application.js b/erpnext/education/doctype/student_leave_application/test_student_leave_application.js index 5af9f5d50f7..6bbf17babfa 100644 --- a/erpnext/education/doctype/student_leave_application/test_student_leave_application.js +++ b/erpnext/education/doctype/student_leave_application/test_student_leave_application.js @@ -66,4 +66,4 @@ QUnit.test('Test: Student Leave Application', function(assert){ () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_leave_application/test_student_leave_application.py b/erpnext/education/doctype/student_leave_application/test_student_leave_application.py index fcdd42825f3..9cae2577481 100644 --- a/erpnext/education/doctype/student_leave_application/test_student_leave_application.py +++ b/erpnext/education/doctype/student_leave_application/test_student_leave_application.py @@ -112,4 +112,4 @@ def create_holiday_list(): company = get_default_company() or frappe.get_all('Company')[0].name frappe.db.set_value('Company', company, 'default_holiday_list', holiday_list) - return holiday_list \ No newline at end of file + return holiday_list diff --git a/erpnext/education/doctype/student_log/test_student_log.js b/erpnext/education/doctype/student_log/test_student_log.js index 5775369e52e..4c90c5f6ef2 100644 --- a/erpnext/education/doctype/student_log/test_student_log.js +++ b/erpnext/education/doctype/student_log/test_student_log.js @@ -32,4 +32,4 @@ QUnit.test('Test: Student Log', function(assert){ }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.html b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.html index 72772b7b32c..a9e84e6e277 100644 --- a/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.html +++ b/erpnext/education/doctype/student_report_generation_tool/student_report_generation_tool.html @@ -12,67 +12,67 @@ padding: 0.75in; margin: auto; } - + .print-format.landscape { max-width: 11.69in; padding: 0.2in; } - + .page-break { padding: 30px 0px; border-bottom: 1px dashed #888; } - + .page-break:first-child { padding-top: 0px; } - + .page-break:last-child { border-bottom: 0px; } - + /* mozilla hack for images in table */ body:last-child .print-format td img { width: 100% !important; } - + @media(max-width: 767px) { .print-format { padding: 0.2in; } } } - + @media print { .print-format p { margin-left: 1px; margin-right: 1px; } } - + .data-field { margin-top: 5px; margin-bottom: 5px; } - + .data-field .value { word-wrap: break-word; } - + .important .value { font-size: 120%; font-weight: bold; } - + .important label { line-height: 1.8; margin: 0px; } - + .table { margin: 20px 0px; } - + .square-image { width: 100%; height: 0; @@ -83,88 +83,88 @@ background-position: center center; border-radius: 4px; } - + .print-item-image { object-fit: contain; } - + .pdf-variables, .pdf-variable, .visible-pdf { display: none !important; } - + .print-format { font-size: 9pt; font-family: "Helvetica Neue", Helvetica, Arial, "Open Sans", sans-serif; -webkit-print-color-adjust:exact; } - + .page-break { page-break-after: always; } - + .print-heading { border-bottom: 1px solid #aaa; margin-bottom: 10px; } - + .print-heading h2 { margin: 0px; } .print-heading h4 { margin-top: 5px; } - + table.no-border, table.no-border td { border: 0px; } - + .print-format label { /* wkhtmltopdf breaks label into multiple lines when it is inline-block */ display: block; } - + .print-format img { max-width: 100%; } - + .print-format table td > .primary:first-child { font-weight: bold; } - + .print-format td, .print-format th { vertical-align: top !important; padding: 6px !important; } - + .print-format p { margin: 3px 0px 3px; } - + table td div { - + /* needed to avoid partial cutting of text between page break in wkhtmltopdf */ page-break-inside: avoid !important; - + } - + /* hack for webkit specific browser */ @media (-webkit-min-device-pixel-ratio:0) { thead, tfoot { display: table-row-group; } } - + [document-status] { margin-bottom: 5mm; } - + .signature-img { background: #fff; border-radius: 3px; margin-top: 5px; max-height: 150px; } - + .print-heading { text-align: right; text-transform: uppercase; @@ -173,16 +173,16 @@ margin-bottom: 20px; border-bottom: 1px solid #d1d8dd; } - + .print-heading h2 { font-size: 24px; } - + .print-format th { background-color: #eee !important; border-bottom: 0px !important; } - + /* modern format: for-test */ .pbi_avoid { @@ -344,7 +344,7 @@
                                                            -
                                                            +

                                                            {{ _("Student Attendance")}}


                                                            Present {{ doc.attendance.get("Present") if doc.attendance.get("Present") != None else '0' }} days @@ -352,7 +352,7 @@
                                                            -
                                                            +

                                                            {{ _("Parents Teacher Meeting Attendance")}}


                                                            Present {{ doc.parents_attendance if doc.parents_attendance != None else '0' }} diff --git a/erpnext/education/doctype/topic/topic.js b/erpnext/education/doctype/topic/topic.js index 2002b0c8e3b..0c903c5a56a 100644 --- a/erpnext/education/doctype/topic/topic.js +++ b/erpnext/education/doctype/topic/topic.js @@ -52,4 +52,4 @@ let get_courses_without_topic = function(topic) { method: 'erpnext.education.doctype.topic.topic.get_courses_without_topic', args: {'topic': topic} }); -}; \ No newline at end of file +}; diff --git a/erpnext/education/doctype/topic/topic.py b/erpnext/education/doctype/topic/topic.py index a5253e93294..fb680d725b0 100644 --- a/erpnext/education/doctype/topic/topic.py +++ b/erpnext/education/doctype/topic/topic.py @@ -56,4 +56,4 @@ def add_content_to_topics(content_type, content, topics): topic.save() frappe.db.commit() frappe.msgprint(_('{0} {1} has been added to all the selected topics successfully.').format(content_type, frappe.bold(content)), - title=_('Topics updated'), indicator='green') \ No newline at end of file + title=_('Topics updated'), indicator='green') diff --git a/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.py b/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.py index c145359129e..c0ec0357cc6 100644 --- a/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.py +++ b/erpnext/education/report/program_wise_fee_collection/program_wise_fee_collection.py @@ -121,4 +121,3 @@ def get_chart_data(data): }, 'type': 'bar' } - diff --git a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.js b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.js index ad04356201e..9f1fcbc8162 100644 --- a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.js +++ b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.js @@ -9,4 +9,4 @@ frappe.query_reports["Student Batch-Wise Attendance"] = { "default": frappe.datetime.get_today(), "reqd": 1 }] -} \ No newline at end of file +} diff --git a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py index 7793dcf3953..e2576a0c710 100644 --- a/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py +++ b/erpnext/education/report/student_batch_wise_attendance/student_batch_wise_attendance.py @@ -67,4 +67,4 @@ def get_student_attendance(student_group, date): student_group= %s and date= %s and docstatus = 1 and (course_schedule is Null or course_schedule='') group by status""", (student_group, date), as_dict=1) - return student_attendance \ No newline at end of file + return student_attendance diff --git a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.js b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.js index 104d3ec06f9..62c94557d7e 100644 --- a/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.js +++ b/erpnext/education/report/student_monthly_attendance_sheet/student_monthly_attendance_sheet.js @@ -39,4 +39,4 @@ frappe.query_reports["Student Monthly Attendance Sheet"] = { } }); } -} \ No newline at end of file +} diff --git a/erpnext/education/web_form/student_applicant/student_applicant.js b/erpnext/education/web_form/student_applicant/student_applicant.js index 699703c5792..ffc5e984253 100644 --- a/erpnext/education/web_form/student_applicant/student_applicant.js +++ b/erpnext/education/web_form/student_applicant/student_applicant.js @@ -1,3 +1,3 @@ frappe.ready(function() { // bind events here -}) \ No newline at end of file +}) diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js index a9925adee7a..f5ea8047c6a 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js @@ -1,3 +1,2 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt - diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py index a25a29f9e5f..99ede8f31de 100644 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py +++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py @@ -103,4 +103,4 @@ class xml2dict(object): """parse a string""" t = ET.fromstring(s) root_tag, root_tree = self._namespace_split(t.tag, self._parse_node(t)) - return object_dict({root_tag: root_tree}) \ No newline at end of file + return object_dict({root_tag: root_tree}) diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py index 6a846efad70..bff928c1c96 100644 --- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py +++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py @@ -17,4 +17,4 @@ class ExotelSettings(Document): response = requests.get('https://api.exotel.com/v1/Accounts/{sid}' .format(sid = self.account_sid), auth=(self.api_key, self.api_token)) if response.status_code != 200: - frappe.throw(_("Invalid credentials")) \ No newline at end of file + frappe.throw(_("Invalid credentials")) diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html b/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html index 2c4d4bbdecf..b74a7187f0c 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/account_balance.html @@ -25,4 +25,4 @@ {% else %}

                                                            Account Balance Information Not Available.

                                                            -{% endif %} \ No newline at end of file +{% endif %} diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py index 554c6b0eb0f..d1adeeee072 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_connector.py @@ -115,4 +115,4 @@ class MpesaConnector(): saf_url = "{0}{1}".format(self.base_url, "/mpesa/stkpush/v1/processrequest") r = requests.post(saf_url, headers=headers, json=payload) - return r.json() \ No newline at end of file + return r.json() diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py index 0499e88b5e7..139e2fb192b 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_custom_fields.py @@ -50,4 +50,4 @@ def create_pos_settings(record_dict): for record in record_dict: if frappe.db.exists("POS Field", {"fieldname": record.get("fieldname")}): continue - frappe.get_doc(record).insert() \ No newline at end of file + frappe.get_doc(record).insert() diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py index fdfaa1b0540..de933578613 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/mpesa_settings.py @@ -276,4 +276,4 @@ def fetch_param_value(response, key, key_field): """Fetch the specified key from list of dictionary. Key is identified via the key field.""" for param in response: if param[key_field] == key: - return param["Value"] \ No newline at end of file + return param["Value"] diff --git a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py index b0e662d3f32..d4cb6b982bb 100644 --- a/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py +++ b/erpnext/erpnext_integrations/doctype/mpesa_settings/test_mpesa_settings.py @@ -355,4 +355,4 @@ def get_account_balance_callback_payload(): } } } - } \ No newline at end of file + } diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py index 42d4b9b2b43..73f5927df40 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py @@ -50,7 +50,7 @@ class PlaidConnector(): "secret": self.settings.plaid_secret, "products": self.products, }) - + return args def get_link_token(self, update_mode=False): diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js index 37bf2824505..3740d049839 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js @@ -135,4 +135,4 @@ erpnext.integrations.plaidLink = class plaidLink { }); }, __("Select a company"), __("Continue")); } -}; \ No newline at end of file +}; diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index 3ef069b5e20..eddcb3401f6 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -110,7 +110,7 @@ def add_bank_accounts(response, bank, company): frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"])) except Exception: frappe.log_error(frappe.get_traceback(), title=_("Plaid Link Error")) - frappe.throw(_("There was an error creating Bank Account while linking with Plaid."), + frappe.throw(_("There was an error creating Bank Account while linking with Plaid."), title=_("Plaid Link Failed")) else: diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js index 5482b9cc695..af06b3451e0 100644 --- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js +++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.js @@ -346,4 +346,4 @@ erpnext.tally_migration.get_html_rows = (logs, field) => { }).join(""); return rows -} \ No newline at end of file +} diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py index bd072f40a19..45f261007f8 100644 --- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py +++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py @@ -27,7 +27,7 @@ class WoocommerceSettings(Document): for doctype in ["Customer", "Address"]: df = dict(fieldname='woocommerce_email', label='Woocommerce Email', fieldtype='Data', read_only=1, print_hide=1) create_custom_field(doctype, df) - + if not frappe.get_value("Item Group", {"name": _("WooCommerce Products")}): item_group = frappe.new_doc("Item Group") item_group.item_group_name = _("WooCommerce Products") @@ -74,4 +74,4 @@ def generate_secret(): def get_series(): return { "sales_order_series" : frappe.get_meta("Sales Order").get_options("naming_series") or "SO-WOO-", - } \ No newline at end of file + } diff --git a/erpnext/erpnext_integrations/stripe_integration.py b/erpnext/erpnext_integrations/stripe_integration.py index a35ca28e0a3..108b4c0dd81 100644 --- a/erpnext/erpnext_integrations/stripe_integration.py +++ b/erpnext/erpnext_integrations/stripe_integration.py @@ -50,4 +50,4 @@ def create_subscription_on_stripe(stripe_settings): stripe_settings.integration_request.db_set('status', 'Failed', update_modified=False) frappe.log_error(frappe.get_traceback()) - return stripe_settings.finalize_request() \ No newline at end of file + return stripe_settings.finalize_request() diff --git a/erpnext/erpnext_integrations/utils.py b/erpnext/erpnext_integrations/utils.py index a5e162f8b5d..caafc0821e1 100644 --- a/erpnext/erpnext_integrations/utils.py +++ b/erpnext/erpnext_integrations/utils.py @@ -52,7 +52,7 @@ def create_mode_of_payment(gateway, payment_type="General"): "payment_gateway": gateway }, ['payment_account']) - mode_of_payment = frappe.db.exists("Mode of Payment", gateway) + mode_of_payment = frappe.db.exists("Mode of Payment", gateway) if not mode_of_payment and payment_gateway_account: mode_of_payment = frappe.get_doc({ "doctype": "Mode of Payment", diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js index dd6dc666d23..e494489d21a 100644 --- a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.js @@ -11,4 +11,4 @@ frappe.dashboards.chart_sources["Department wise Patient Appointments"] = { default: frappe.defaults.get_user_default("Company") } ] -}; \ No newline at end of file +}; diff --git a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py index 062da6e4654..eca7143e689 100644 --- a/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py +++ b/erpnext/healthcare/dashboard_chart_source/department_wise_patient_appointments/department_wise_patient_appointments.py @@ -69,4 +69,4 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d } ], 'type': 'bar' - } \ No newline at end of file + } diff --git a/erpnext/healthcare/doctype/appointment_type/appointment_type.js b/erpnext/healthcare/doctype/appointment_type/appointment_type.js index 861675acea3..99b7cb295a9 100644 --- a/erpnext/healthcare/doctype/appointment_type/appointment_type.js +++ b/erpnext/healthcare/doctype/appointment_type/appointment_type.js @@ -80,4 +80,4 @@ frappe.ui.form.on('Appointment Type Service Item', { }); } } -}); \ No newline at end of file +}); diff --git a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py index 03e96a4b3be..81a3982c4bf 100644 --- a/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py +++ b/erpnext/healthcare/doctype/clinical_procedure/test_clinical_procedure.py @@ -63,4 +63,4 @@ def create_procedure(procedure_template, patient, practitioner): procedure.company = "_Test Company" procedure.warehouse = "_Test Warehouse - _TC" procedure.submit() - return procedure \ No newline at end of file + return procedure diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js index 1ef110dc6f4..ae6b39bb181 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.js @@ -188,4 +188,3 @@ frappe.tour['Clinical Procedure Template'] = [ description: __('You can also set the Medical Department for the template. After saving the document, an Item will automatically be created for billing this Clinical Procedure. You can then use this template while creating Clinical Procedures for Patients. Templates save you from filling up redundant data every single time. You can also create templates for other operations like Lab Tests, Therapy Sessions, etc.') } ]; - diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py index f32b7cf9d8d..58194f10a8c 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py @@ -118,4 +118,3 @@ def change_item_code_from_template(item_code, doc): rename_doc('Item', doc.item_code, item_code, ignore_permissions=True) frappe.db.set_value('Clinical Procedure Template', doc.name, 'item_code', item_code) return - diff --git a/erpnext/healthcare/doctype/exercise_type/exercise_type.py b/erpnext/healthcare/doctype/exercise_type/exercise_type.py index fb635c85788..ae44a2b77b5 100644 --- a/erpnext/healthcare/doctype/exercise_type/exercise_type.py +++ b/erpnext/healthcare/doctype/exercise_type/exercise_type.py @@ -12,4 +12,3 @@ class ExerciseType(Document): self.name = ' - '.join(filter(None, [self.exercise_name, self.difficulty_level])) else: self.name = self.exercise_name - diff --git a/erpnext/healthcare/doctype/fee_validity/fee_validity.py b/erpnext/healthcare/doctype/fee_validity/fee_validity.py index 058bc971929..5b9c17934fa 100644 --- a/erpnext/healthcare/doctype/fee_validity/fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/fee_validity.py @@ -60,4 +60,4 @@ def check_is_new_patient(appointment): }) if len(appointment_exists) and appointment_exists[0]: return False - return True \ No newline at end of file + return True diff --git a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py index 7e7fd824119..6ae3e12d50e 100644 --- a/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py +++ b/erpnext/healthcare/doctype/fee_validity/test_fee_validity.py @@ -47,4 +47,4 @@ class TestFeeValidity(unittest.TestCase): # appointment should be invoiced as it is not within fee validity and the max_visits are exceeded appointment = create_appointment(patient, practitioner, add_days(nowdate(), 10), invoice=1) invoiced = frappe.db.get_value("Patient Appointment", appointment.name, "invoiced") - self.assertEqual(invoiced, 1) \ No newline at end of file + self.assertEqual(invoiced, 1) diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js index fc0b24122ae..44c399856c8 100644 --- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js +++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.js @@ -142,4 +142,3 @@ frappe.tour['Healthcare Practitioner'] = [ description: __('If this Healthcare Practitioner also works for the In-Patient Department, set the inpatient visit charge for this Practitioner.') } ]; - diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py index 01cf4b0a494..3ee3377b004 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/test_healthcare_service_unit_type.py @@ -30,4 +30,4 @@ def get_unit_type(): unit_type.no_of_hours = 1 unit_type.rate = 4000 unit_type.save() - return unit_type \ No newline at end of file + return unit_type diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py b/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py index 7cb5a4814e8..ff9e21252a1 100644 --- a/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py +++ b/erpnext/healthcare/doctype/inpatient_medication_entry/test_inpatient_medication_entry.py @@ -153,4 +153,4 @@ def make_stock_entry(warehouse=None): # in stock uom se_child.conversion_factor = 1.0 se_child.expense_account = expense_account - stock_entry.submit() \ No newline at end of file + stock_entry.submit() diff --git a/erpnext/healthcare/doctype/inpatient_medication_order/test_inpatient_medication_order.py b/erpnext/healthcare/doctype/inpatient_medication_order/test_inpatient_medication_order.py index 21776d2380a..798976283b3 100644 --- a/erpnext/healthcare/doctype/inpatient_medication_order/test_inpatient_medication_order.py +++ b/erpnext/healthcare/doctype/inpatient_medication_order/test_inpatient_medication_order.py @@ -140,4 +140,3 @@ def create_ipme(filters, update_stock=0): ipme = ipme.get_medication_orders() return ipme - diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py index 5f2dc480a1b..9dd4a2c73c0 100644 --- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py @@ -295,4 +295,4 @@ def create_appointment_type(args=None): 'color': args.get('color') or '#7575ff', 'price_list': args.get('price_list') or frappe.db.get_value("Price List", {"selling": 1}), 'items': args.get('items') or items - }).insert() \ No newline at end of file + }).insert() diff --git a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.py b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.py index 3033a3e6ac9..7bad20dffdc 100644 --- a/erpnext/healthcare/doctype/patient_assessment/patient_assessment.py +++ b/erpnext/healthcare/doctype/patient_assessment/patient_assessment.py @@ -31,6 +31,3 @@ def create_patient_assessment(source_name, target_doc=None): }, target_doc) return doc - - - diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py index cc2141790f7..2b3029efdeb 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py @@ -99,4 +99,4 @@ def create_therapy_plan(encounter): def delete_ip_medication_order(encounter): record = frappe.db.exists('Inpatient Medication Order', {'patient_encounter': encounter.name}) if record: - frappe.delete_doc('Inpatient Medication Order', record, force=1) \ No newline at end of file + frappe.delete_doc('Inpatient Medication Order', record, force=1) diff --git a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py index 887d58a2e04..63b00859d71 100644 --- a/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py +++ b/erpnext/healthcare/doctype/patient_history_settings/patient_history_settings.py @@ -187,4 +187,4 @@ def get_module(doc): if not module: module = frappe.db.get_value('DocType', doc.doctype, 'module') - return module \ No newline at end of file + return module diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py index c1d9872a019..f8ccc8a002a 100644 --- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py +++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py @@ -88,4 +88,4 @@ def create_lab_test(template, patient): lab_test.template = template lab_test.save() lab_test.submit() - return lab_test \ No newline at end of file + return lab_test diff --git a/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.py b/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.py index 748c12c6896..635d4beb8df 100644 --- a/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.py +++ b/erpnext/healthcare/doctype/therapy_plan_template/therapy_plan_template.py @@ -70,4 +70,4 @@ class TherapyPlanTemplate(Document): item_price.item_name = self.item_name item_price.price_list_rate = self.total_amount item_price.ignore_mandatory = True - item_price.save(ignore_permissions=True) \ No newline at end of file + item_price.save(ignore_permissions=True) diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.js b/erpnext/healthcare/doctype/therapy_session/therapy_session.js index fd200036935..fbfa774c91c 100644 --- a/erpnext/healthcare/doctype/therapy_session/therapy_session.js +++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.js @@ -168,4 +168,4 @@ frappe.ui.form.on('Therapy Session', { }); } } -}); \ No newline at end of file +}); diff --git a/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py b/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py index 21f63699753..a5dad293e31 100644 --- a/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py +++ b/erpnext/healthcare/doctype/therapy_type/test_therapy_type.py @@ -47,4 +47,4 @@ def create_exercise_type(): 'description': 'Squat and Rise' }) exercise_type.save() - return exercise_type \ No newline at end of file + return exercise_type diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py index 35c823d739c..4bb3940ae0f 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py @@ -15,4 +15,3 @@ class VitalSigns(Document): def set_title(self): self.title = _('{0} on {1}').format(self.patient_name or self.patient, frappe.utils.format_date(self.signs_date))[:100] - diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html index be486c62d1e..f1706557f45 100644 --- a/erpnext/healthcare/page/patient_history/patient_history.html +++ b/erpnext/healthcare/page/patient_history/patient_history.html @@ -23,4 +23,4 @@
                                                            -
                                                            \ No newline at end of file +
                                                            diff --git a/erpnext/healthcare/page/patient_progress/patient_progress.html b/erpnext/healthcare/page/patient_progress/patient_progress.html index c20537ea81d..30064bd1654 100644 --- a/erpnext/healthcare/page/patient_progress/patient_progress.html +++ b/erpnext/healthcare/page/patient_progress/patient_progress.html @@ -65,4 +65,4 @@
                                                            - \ No newline at end of file + diff --git a/erpnext/healthcare/page/patient_progress/patient_progress.js b/erpnext/healthcare/page/patient_progress/patient_progress.js index 2410b0ce845..4b7599df296 100644 --- a/erpnext/healthcare/page/patient_progress/patient_progress.js +++ b/erpnext/healthcare/page/patient_progress/patient_progress.js @@ -528,4 +528,4 @@ class PatientProgress { } $(parent).find('.chart-container').hide(); } -} \ No newline at end of file +} diff --git a/erpnext/healthcare/page/patient_progress/patient_progress.py b/erpnext/healthcare/page/patient_progress/patient_progress.py index a04fb2b592a..46bfb3db5d4 100644 --- a/erpnext/healthcare/page/patient_progress/patient_progress.py +++ b/erpnext/healthcare/page/patient_progress/patient_progress.py @@ -194,4 +194,3 @@ def get_date_range(time_span): return time_span except json.decoder.JSONDecodeError: return get_timespan_date_range(time_span.lower()) - diff --git a/erpnext/healthcare/page/patient_progress/patient_progress_sidebar.html b/erpnext/healthcare/page/patient_progress/patient_progress_sidebar.html index cd62dd39035..4ee65738ba3 100644 --- a/erpnext/healthcare/page/patient_progress/patient_progress_sidebar.html +++ b/erpnext/healthcare/page/patient_progress/patient_progress_sidebar.html @@ -26,4 +26,4 @@

                                                            {%=__("Therapy Plan") %}

                                                            {%=__("Patient History") %}

                                                            - \ No newline at end of file + diff --git a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py index b9077301bad..28b60bdcc92 100644 --- a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py +++ b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py @@ -195,4 +195,4 @@ def get_chart_data(data): chart["fieldtype"] = "Data" - return chart \ No newline at end of file + return chart diff --git a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.py b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.py index 9c35dbb3ea5..9a4840acfea 100644 --- a/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.py +++ b/erpnext/healthcare/report/patient_appointment_analytics/patient_appointment_analytics.py @@ -191,4 +191,4 @@ class Analytics(object): 'datasets': [] }, "type": "line" - } \ No newline at end of file + } diff --git a/erpnext/healthcare/setup.py b/erpnext/healthcare/setup.py index bf4df7e4c88..891272ddf81 100644 --- a/erpnext/healthcare/setup.py +++ b/erpnext/healthcare/setup.py @@ -292,4 +292,4 @@ def get_patient_history_config(): {"label": "Medication Orders", "fieldname": "medication_orders", "fieldtype": "Table"}, {"label": "Total Orders", "fieldname": "total_orders", "fieldtype": "Float"} ]) - } \ No newline at end of file + } diff --git a/erpnext/healthcare/web_form/patient_registration/patient_registration.js b/erpnext/healthcare/web_form/patient_registration/patient_registration.js index 7da3f1fb41c..f09e5409192 100644 --- a/erpnext/healthcare/web_form/patient_registration/patient_registration.js +++ b/erpnext/healthcare/web_form/patient_registration/patient_registration.js @@ -1,3 +1,3 @@ frappe.ready(function() { // bind events here -}); \ No newline at end of file +}); diff --git a/erpnext/hotels/doctype/hotel_room/hotel_room.py b/erpnext/hotels/doctype/hotel_room/hotel_room.py index 8471aee4a03..6a2fc02574f 100644 --- a/erpnext/hotels/doctype/hotel_room/hotel_room.py +++ b/erpnext/hotels/doctype/hotel_room/hotel_room.py @@ -10,4 +10,4 @@ class HotelRoom(Document): def validate(self): if not self.capacity: self.capacity, self.extra_bed_capacity = frappe.db.get_value('Hotel Room Type', - self.hotel_room_type, ['capacity', 'extra_bed_capacity']) \ No newline at end of file + self.hotel_room_type, ['capacity', 'extra_bed_capacity']) diff --git a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js index 7f7322cf4b6..7bde292a2bc 100644 --- a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js +++ b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation_calendar.js @@ -6,4 +6,4 @@ frappe.views.calendar["Hotel Room Reservation"] = { "title": "guest_name", "status": "status" } -} \ No newline at end of file +} diff --git a/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py index f77d43b3143..259edb9c06d 100644 --- a/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py +++ b/erpnext/hotels/report/hotel_room_occupancy/hotel_room_occupancy.py @@ -30,4 +30,4 @@ def get_data(filters): out.append([room_type.name, total_booked]) - return out \ No newline at end of file + return out diff --git a/erpnext/hr/doctype/appraisal/appraisal.js b/erpnext/hr/doctype/appraisal/appraisal.js index 1a30ceac6d3..50612b923ef 100644 --- a/erpnext/hr/doctype/appraisal/appraisal.js +++ b/erpnext/hr/doctype/appraisal/appraisal.js @@ -15,7 +15,7 @@ frappe.ui.form.on('Appraisal', { frm.set_value('status', 'Draft'); } }, - + kra_template: function(frm) { frm.doc.goals = []; erpnext.utils.map_current_doc({ diff --git a/erpnext/hr/doctype/appraisal/test_appraisal.js b/erpnext/hr/doctype/appraisal/test_appraisal.js index 9ca17e2e226..fb1354c3f6b 100644 --- a/erpnext/hr/doctype/appraisal/test_appraisal.js +++ b/erpnext/hr/doctype/appraisal/test_appraisal.js @@ -55,4 +55,3 @@ QUnit.test("Test: Expense Claim [HR]", function (assert) { () => done() ]); }); - diff --git a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py index a6868ee2b1c..11d9f3944d5 100644 --- a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py +++ b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class AppraisalGoal(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py b/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py index 309427e30c2..392b370e6c3 100644 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py +++ b/erpnext/hr/doctype/appraisal_template/appraisal_template_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Appraisal'] }, ], - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js index 0403cad0683..3eb64e08501 100644 --- a/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js +++ b/erpnext/hr/doctype/appraisal_template/test_appraisal_template.js @@ -27,4 +27,3 @@ QUnit.test("Test: Appraisal Template [HR]", function (assert) { () => done() ]); }); - diff --git a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py index ca58e0c3202..b3c5704fa53 100644 --- a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py +++ b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class AppraisalTemplateGoal(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/attendance/attendance_calendar.js b/erpnext/hr/doctype/attendance/attendance_calendar.js index 45664896965..d9f6d2eb3eb 100644 --- a/erpnext/hr/doctype/attendance/attendance_calendar.js +++ b/erpnext/hr/doctype/attendance/attendance_calendar.js @@ -9,4 +9,4 @@ frappe.views.calendar["Attendance"] = { } }, get_events_method: "erpnext.hr.doctype.attendance.attendance.get_events" -}; \ No newline at end of file +}; diff --git a/erpnext/hr/doctype/attendance/test_attendance.js b/erpnext/hr/doctype/attendance/test_attendance.js index 8f30e8cc161..b3e7fef02a0 100644 --- a/erpnext/hr/doctype/attendance/test_attendance.js +++ b/erpnext/hr/doctype/attendance/test_attendance.js @@ -36,4 +36,4 @@ QUnit.test("Test: Attendance [HR]", function (assert) { "attendance for Present day is marked"), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py b/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py index cfdd6d3aefb..2d3eb000119 100644 --- a/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py +++ b/erpnext/hr/doctype/attendance_request/attendance_request_dashboard.py @@ -8,4 +8,4 @@ def get_data(): 'items': ['Attendance'] } ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/branch/branch.py b/erpnext/hr/doctype/branch/branch.py index fab2ffc1a37..a847c8e2174 100644 --- a/erpnext/hr/doctype/branch/branch.py +++ b/erpnext/hr/doctype/branch/branch.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class Branch(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/branch/test_branch.js b/erpnext/hr/doctype/branch/test_branch.js index c315385f116..82a6ae103ee 100644 --- a/erpnext/hr/doctype/branch/test_branch.js +++ b/erpnext/hr/doctype/branch/test_branch.js @@ -20,4 +20,4 @@ QUnit.test("Test: Branch [HR]", function (assert) { 'name of branch correctly saved'), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/branch/test_branch.py b/erpnext/hr/doctype/branch/test_branch.py index 5ba02b36b8a..807698ba0a2 100644 --- a/erpnext/hr/doctype/branch/test_branch.py +++ b/erpnext/hr/doctype/branch/test_branch.py @@ -4,4 +4,4 @@ from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Branch') \ No newline at end of file +test_records = frappe.get_test_records('Branch') diff --git a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js index d2ceb8bd527..15335171473 100644 --- a/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js +++ b/erpnext/hr/doctype/daily_work_summary/test_daily_work_summary.js @@ -20,4 +20,4 @@ QUnit.test("test: Daily Work Summary", function (assert) { () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/department/department_tree.js b/erpnext/hr/doctype/department/department_tree.js index 52d864bc0e6..5c7726de6a6 100644 --- a/erpnext/hr/doctype/department/department_tree.js +++ b/erpnext/hr/doctype/department/department_tree.js @@ -25,4 +25,4 @@ frappe.treeview_settings["Department"] = { onload: function(treeview) { treeview.make_tree(); } -}; \ No newline at end of file +}; diff --git a/erpnext/hr/doctype/department/test_department.js b/erpnext/hr/doctype/department/test_department.js index 3a571f76535..e73779c97c6 100644 --- a/erpnext/hr/doctype/department/test_department.js +++ b/erpnext/hr/doctype/department/test_department.js @@ -20,4 +20,4 @@ QUnit.test("Test: Department [HR]", function (assert) { 'name of department correctly saved'), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/department/test_department.py b/erpnext/hr/doctype/department/test_department.py index 2eeca26e303..e4f6645ee42 100644 --- a/erpnext/hr/doctype/department/test_department.py +++ b/erpnext/hr/doctype/department/test_department.py @@ -21,4 +21,4 @@ def create_department(department_name, parent_department=None): return doc -test_records = frappe.get_test_records('Department') \ No newline at end of file +test_records = frappe.get_test_records('Department') diff --git a/erpnext/hr/doctype/designation/designation.py b/erpnext/hr/doctype/designation/designation.py index efd864ad593..a3f84aab5f0 100644 --- a/erpnext/hr/doctype/designation/designation.py +++ b/erpnext/hr/doctype/designation/designation.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class Designation(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/designation/test_designation.js b/erpnext/hr/doctype/designation/test_designation.js index 45c34171911..00adf8293f7 100644 --- a/erpnext/hr/doctype/designation/test_designation.js +++ b/erpnext/hr/doctype/designation/test_designation.js @@ -20,4 +20,4 @@ QUnit.test("Test: Designation [HR]", function (assert) { 'name of designation correctly saved'), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/designation/test_designation.py b/erpnext/hr/doctype/designation/test_designation.py index 3b300941a65..2778862a1c2 100644 --- a/erpnext/hr/doctype/designation/test_designation.py +++ b/erpnext/hr/doctype/designation/test_designation.py @@ -17,4 +17,4 @@ def create_designation(**args): "description": args.description or "_Test description" }) designation.save() - return designation \ No newline at end of file + return designation diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 5ca47560b10..f4280152c5c 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -520,4 +520,4 @@ def has_upload_permission(doc, ptype='read', user=None): user = frappe.session.user if get_doc_permissions(doc, user=user, ptype=ptype).get(ptype): return True - return doc.user_id == user \ No newline at end of file + return doc.user_id == user diff --git a/erpnext/hr/doctype/employee/employee_tree.js b/erpnext/hr/doctype/employee/employee_tree.js index 9ab091a1eb6..7d6a70013d4 100644 --- a/erpnext/hr/doctype/employee/employee_tree.js +++ b/erpnext/hr/doctype/employee/employee_tree.js @@ -33,4 +33,4 @@ frappe.treeview_settings['Employee'] = { condition: 'frappe.boot.user.can_create.indexOf("Employee") !== -1' } ], -}; \ No newline at end of file +}; diff --git a/erpnext/hr/doctype/employee/test_employee.js b/erpnext/hr/doctype/employee/test_employee.js index 200dcd79666..3a414584804 100644 --- a/erpnext/hr/doctype/employee/test_employee.js +++ b/erpnext/hr/doctype/employee/test_employee.js @@ -37,4 +37,4 @@ QUnit.test("Test: Employee [HR]", function (assert) { () => frappe.timeout(10), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/employee_advance/test_employee_advance.py b/erpnext/hr/doctype/employee_advance/test_employee_advance.py index c88b2b8e49e..100968bb7aa 100644 --- a/erpnext/hr/doctype/employee_advance/test_employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/test_employee_advance.py @@ -48,4 +48,4 @@ def make_employee_advance(employee_name): doc.insert() doc.submit() - return doc \ No newline at end of file + return doc diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css index d25fb2247ed..c8d6644b2f8 100644 --- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css +++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.css @@ -18,4 +18,4 @@ .checkbox{ margin-top: -3px; -} \ No newline at end of file +} diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js index ab965d54e3b..5ae8c6bd03b 100644 --- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js +++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js @@ -267,5 +267,3 @@ erpnext.EmployeeSelector = class EmployeeSelector { mark_employee_toolbar.appendTo($(this.wrapper)); } }; - - diff --git a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js index 2827d4ba289..48d4344df22 100644 --- a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js +++ b/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js @@ -58,4 +58,4 @@ QUnit.test("Test: Employee attendance tool [HR]", function (assert) { }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py index 60ea0f9895d..6c0cd4f963b 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py @@ -176,4 +176,3 @@ def time_diff_in_hours(start, end): def find_index_in_dict(dict_list, key, value): return next((index for (index, d) in enumerate(dict_list) if d[key] == value), None) - diff --git a/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py b/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py index 9f12ef24e62..7ba511f08d5 100644 --- a/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/test_employee_checkin.py @@ -42,11 +42,11 @@ class TestEmployeeCheckin(unittest.TestCase): self.assertEqual(logs_count, 4) attendance_count = frappe.db.count('Attendance', {'status':'Present', 'working_hours':8.2, 'employee':employee, 'attendance_date':now_date}) - self.assertEqual(attendance_count, 1) + self.assertEqual(attendance_count, 1) def test_calculate_working_hours(self): check_in_out_type = ['Alternating entries as IN and OUT during the same shift', - 'Strictly based on Log Type in Employee Checkin'] + 'Strictly based on Log Type in Employee Checkin'] working_hours_calc_type = ['First Check-in and Last Check-out', 'Every Valid Check-in and Check-out'] logs_type_1 = [ diff --git a/erpnext/hr/doctype/employee_education/employee_education.py b/erpnext/hr/doctype/employee_education/employee_education.py index a1d449291c9..f0a76172b2c 100644 --- a/erpnext/hr/doctype/employee_education/employee_education.py +++ b/erpnext/hr/doctype/employee_education/employee_education.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class EmployeeEducation(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/employee_external_work_history/employee_external_work_history.py b/erpnext/hr/doctype/employee_external_work_history/employee_external_work_history.py index c7166309f37..517ef57be85 100644 --- a/erpnext/hr/doctype/employee_external_work_history/employee_external_work_history.py +++ b/erpnext/hr/doctype/employee_external_work_history/employee_external_work_history.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class EmployeeExternalWorkHistory(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py b/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py index f2656e9a2b2..df679104185 100644 --- a/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py +++ b/erpnext/hr/doctype/employee_grade/employee_grade_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Employee Onboarding Template', 'Employee Separation Template'] } ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance.py b/erpnext/hr/doctype/employee_grievance/employee_grievance.py index 503b5ea4449..17055829efb 100644 --- a/erpnext/hr/doctype/employee_grievance/employee_grievance.py +++ b/erpnext/hr/doctype/employee_grievance/employee_grievance.py @@ -12,4 +12,3 @@ class EmployeeGrievance(Document): bold("Invalid"), bold("Resolved")) ) - diff --git a/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js b/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js index fc08e216099..11672ca4e0e 100644 --- a/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js +++ b/erpnext/hr/doctype/employee_grievance/employee_grievance_list.js @@ -9,4 +9,4 @@ frappe.listview_settings["Employee Grievance"] = { }; return [__(doc.status), colors[doc.status], "status,=," + doc.status]; } -}; \ No newline at end of file +}; diff --git a/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py b/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py index a615b20a5a2..ed897ee1032 100644 --- a/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py +++ b/erpnext/hr/doctype/employee_grievance/test_employee_grievance.py @@ -48,4 +48,3 @@ def create_grievance_type(): grievance_type.save() return grievance_type.name - diff --git a/erpnext/hr/doctype/employee_group/test_employee_group.py b/erpnext/hr/doctype/employee_group/test_employee_group.py index 3a6bf8594b3..26a61c407b2 100644 --- a/erpnext/hr/doctype/employee_group/test_employee_group.py +++ b/erpnext/hr/doctype/employee_group/test_employee_group.py @@ -29,4 +29,4 @@ def make_employee_group(): def get_employee_group(): employee_group = frappe.db.exists("Employee Group", "_Test Employee Group") - return employee_group \ No newline at end of file + return employee_group diff --git a/erpnext/hr/doctype/employee_internal_work_history/employee_internal_work_history.py b/erpnext/hr/doctype/employee_internal_work_history/employee_internal_work_history.py index d0f3d8d016a..2f385a8113e 100644 --- a/erpnext/hr/doctype/employee_internal_work_history/employee_internal_work_history.py +++ b/erpnext/hr/doctype/employee_internal_work_history/employee_internal_work_history.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class EmployeeInternalWorkHistory(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py index 55fe317b9ee..ca9b2987a6f 100644 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py +++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py @@ -57,4 +57,3 @@ def make_employee(source_name, target_doc=None): }} }, target_doc, set_missing_values) return doc - diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py index 5f7756bcada..0445270b9ff 100644 --- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py +++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py @@ -101,4 +101,4 @@ def create_employee_onboarding(): onboarding.insert() onboarding.submit() - return onboarding \ No newline at end of file + return onboarding diff --git a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py index 837da530162..ab0eb2f5dce 100644 --- a/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py +++ b/erpnext/hr/doctype/employee_onboarding_template/employee_onboarding_template_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Employee Onboarding'] }, ], - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.py b/erpnext/hr/doctype/employee_referral/employee_referral.py index 0493306166f..547a95e3bdf 100644 --- a/erpnext/hr/doctype/employee_referral/employee_referral.py +++ b/erpnext/hr/doctype/employee_referral/employee_referral.py @@ -70,4 +70,3 @@ def create_additional_salary(doc): additional_salary.ref_docname = doc.name return additional_salary - diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py index afa2a1ff1fc..caca2961a1a 100644 --- a/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py +++ b/erpnext/hr/doctype/employee_referral/employee_referral_dashboard.py @@ -12,4 +12,4 @@ def get_data(): }, ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/employee_referral/employee_referral_list.js b/erpnext/hr/doctype/employee_referral/employee_referral_list.js index 7533ab635f5..38dfc4d4c86 100644 --- a/erpnext/hr/doctype/employee_referral/employee_referral_list.js +++ b/erpnext/hr/doctype/employee_referral/employee_referral_list.js @@ -11,4 +11,4 @@ frappe.listview_settings['Employee Referral'] = { return [__(doc.status), "red", "status,=," + doc.status]; } }, -}; \ No newline at end of file +}; diff --git a/erpnext/hr/doctype/employee_referral/test_employee_referral.py b/erpnext/hr/doctype/employee_referral/test_employee_referral.py index a674f390265..599f3262240 100644 --- a/erpnext/hr/doctype/employee_referral/test_employee_referral.py +++ b/erpnext/hr/doctype/employee_referral/test_employee_referral.py @@ -57,4 +57,4 @@ def create_employee_referral(): emp_ref.save() emp_ref.submit() - return emp_ref \ No newline at end of file + return emp_ref diff --git a/erpnext/hr/doctype/employee_separation/test_employee_separation.py b/erpnext/hr/doctype/employee_separation/test_employee_separation.py index f787d9c6568..d63501a9314 100644 --- a/erpnext/hr/doctype/employee_separation/test_employee_separation.py +++ b/erpnext/hr/doctype/employee_separation/test_employee_separation.py @@ -45,4 +45,4 @@ def create_employee_separation(): separation.boarding_status = 'Pending' separation.insert() separation.submit() - return separation \ No newline at end of file + return separation diff --git a/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py b/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py index 39345f07663..75f985cec39 100644 --- a/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py +++ b/erpnext/hr/doctype/employee_separation_template/employee_separation_template_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Employee Separation'] }, ], - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/employment_type/employment_type.py b/erpnext/hr/doctype/employment_type/employment_type.py index fb306b65d28..00aa6bb9bc4 100644 --- a/erpnext/hr/doctype/employment_type/employment_type.py +++ b/erpnext/hr/doctype/employment_type/employment_type.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class EmploymentType(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/employment_type/test_employment_type.js b/erpnext/hr/doctype/employment_type/test_employment_type.js index 9835aabd481..fd7c6a1ce33 100644 --- a/erpnext/hr/doctype/employment_type/test_employment_type.js +++ b/erpnext/hr/doctype/employment_type/test_employment_type.js @@ -19,4 +19,4 @@ QUnit.test("Test: Employment type [HR]", function (assert) { 'name of employment type correctly saved'), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/employment_type/test_employment_type.py b/erpnext/hr/doctype/employment_type/test_employment_type.py index e138136605c..0297ffa01a3 100644 --- a/erpnext/hr/doctype/employment_type/test_employment_type.py +++ b/erpnext/hr/doctype/employment_type/test_employment_type.py @@ -4,4 +4,4 @@ from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Employment Type') \ No newline at end of file +test_records = frappe.get_test_records('Employment Type') diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 629341ff2a5..3c4c672816c 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -442,4 +442,4 @@ frappe.ui.form.on("Expense Taxes and Charges", { tax_amount: function(frm, cdt, cdn) { frm.trigger("calculate_total_tax", cdt, cdn); } -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py b/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py index 7de8f4fc13a..fe973507019 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim_dashboard.py @@ -17,4 +17,4 @@ def get_data(): 'items': ['Employee Advance'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.js b/erpnext/hr/doctype/expense_claim/test_expense_claim.js index d0c43d3be47..2529faec983 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.js @@ -42,4 +42,3 @@ QUnit.test("Test: Expense Claim [HR]", function (assert) { () => done() ]); }); - diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 96ea686706c..c2bd1e9f9f1 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -72,7 +72,7 @@ class TestExpenseClaim(unittest.TestCase): def test_expense_claim_gl_entry(self): payable_account = get_payable_account(company_name) taxes = generate_taxes() - expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes) expense_claim.submit() diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py index 8bfa1ade072..5d48990c5ce 100644 --- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py +++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class ExpenseClaimDetail(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py index 2595506486d..a637a540213 100644 --- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py +++ b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.py @@ -25,4 +25,4 @@ class ExpenseClaimType(Document): """Error when Company of Ledger account doesn't match with Company Selected""" if frappe.db.get_value("Account", entry.default_account, "company") != entry.company: frappe.throw(_("Account {0} does not match with Company {1}" - ).format(entry.default_account, entry.company)) \ No newline at end of file + ).format(entry.default_account, entry.company)) diff --git a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js index 62234e08a04..3c9ed35313d 100644 --- a/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js +++ b/erpnext/hr/doctype/expense_claim_type/test_expense_claim_type.js @@ -27,4 +27,3 @@ QUnit.test("Test: Expense Claim Type [HR]", function (assert) { () => done() ]); }); - diff --git a/erpnext/hr/doctype/holiday/holiday.py b/erpnext/hr/doctype/holiday/holiday.py index aabab0b0d35..78a95b9b741 100644 --- a/erpnext/hr/doctype/holiday/holiday.py +++ b/erpnext/hr/doctype/holiday/holiday.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class Holiday(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py b/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py index 22e1de0c342..05641c7dc26 100644 --- a/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py +++ b/erpnext/hr/doctype/holiday_list/holiday_list_dashboard.py @@ -18,4 +18,4 @@ def get_data(): 'items': ['Service Level', 'Service Level Agreement'] } ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/holiday_list/test_holiday_list.js b/erpnext/hr/doctype/holiday_list/test_holiday_list.js index bfcafa9460c..ce766143a62 100644 --- a/erpnext/hr/doctype/holiday_list/test_holiday_list.js +++ b/erpnext/hr/doctype/holiday_list/test_holiday_list.js @@ -39,4 +39,4 @@ QUnit.test("Test: Holiday list [HR]", function (assert) { }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js index fd082fda09b..ec99472d9bc 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.js +++ b/erpnext/hr/doctype/hr_settings/hr_settings.js @@ -5,4 +5,4 @@ frappe.ui.form.on('HR Settings', { restrict_backdated_leave_application: function(frm) { frm.toggle_reqd("role_allowed_to_create_backdated_leave_application", frm.doc.restrict_backdated_leave_application); } -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.py b/erpnext/hr/doctype/hr_settings/hr_settings.py index ced98fb9a58..c99df269cc9 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.py +++ b/erpnext/hr/doctype/hr_settings/hr_settings.py @@ -15,4 +15,3 @@ class HRSettings(Document): from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series set_by_naming_series("Employee", "employee_number", self.get("emp_created_by")=="Naming Series", hide_name_field=True) - diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.js b/erpnext/hr/doctype/job_applicant/job_applicant.js index c62515597ce..7658bc93539 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.js +++ b/erpnext/hr/doctype/job_applicant/job_applicant.js @@ -38,4 +38,4 @@ frappe.ui.form.on("Job Applicant", { }); } -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/job_applicant/job_applicant.py b/erpnext/hr/doctype/job_applicant/job_applicant.py index 0594ba395ba..14aeb03a87e 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.py +++ b/erpnext/hr/doctype/job_applicant/job_applicant.py @@ -50,4 +50,3 @@ class JobApplicant(Document): if names: frappe.throw(_("Email Address must be unique, already exists for {0}").format(comma_and(names)), frappe.DuplicateEntryError) - diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py index 7f131151e18..ed97978a8ad 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py +++ b/erpnext/hr/doctype/job_applicant/job_applicant_dashboard.py @@ -12,4 +12,4 @@ def get_data(): 'items': ['Job Offer'] }, ], - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/job_applicant/test_job_applicant.js b/erpnext/hr/doctype/job_applicant/test_job_applicant.js index b5391c8bf36..741a182addc 100644 --- a/erpnext/hr/doctype/job_applicant/test_job_applicant.js +++ b/erpnext/hr/doctype/job_applicant/test_job_applicant.js @@ -26,4 +26,3 @@ QUnit.test("Test: Job Opening [HR]", function (assert) { () => done() ]); }); - diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.js b/erpnext/hr/doctype/job_offer/test_job_offer.js index c9d7d2bef79..5339b9c3d67 100644 --- a/erpnext/hr/doctype/job_offer/test_job_offer.js +++ b/erpnext/hr/doctype/job_offer/test_job_offer.js @@ -48,4 +48,4 @@ QUnit.test("Test: Job Offer [HR]", function (assert) { () => frappe.timeout(2), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/job_offer/test_job_offer.py b/erpnext/hr/doctype/job_offer/test_job_offer.py index b3e1dc8d87b..edb21321fcc 100644 --- a/erpnext/hr/doctype/job_offer/test_job_offer.py +++ b/erpnext/hr/doctype/job_offer/test_job_offer.py @@ -79,4 +79,4 @@ def create_staffing_plan(**args): }) staffing_plan.insert() staffing_plan.submit() - return staffing_plan \ No newline at end of file + return staffing_plan diff --git a/erpnext/hr/doctype/job_opening/job_opening_dashboard.py b/erpnext/hr/doctype/job_opening/job_opening_dashboard.py index c0890b4f57c..31ef33ef2ce 100644 --- a/erpnext/hr/doctype/job_opening/job_opening_dashboard.py +++ b/erpnext/hr/doctype/job_opening/job_opening_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Job Applicant'] } ], - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/job_opening/templates/job_opening_row.html b/erpnext/hr/doctype/job_opening/templates/job_opening_row.html index c015101600a..69bf49bef77 100644 --- a/erpnext/hr/doctype/job_opening/templates/job_opening_row.html +++ b/erpnext/hr/doctype/job_opening/templates/job_opening_row.html @@ -1,16 +1,16 @@

                                                            {{ doc.job_title }}

                                                            {{ doc.description }}

                                                            - {%- if doc.publish_salary_range -%} + {%- if doc.publish_salary_range -%}

                                                            {{_("Salary range per month")}}: {{ frappe.format_value(frappe.utils.flt(doc.lower_range), currency=doc.currency) }} - {{ frappe.format_value(frappe.utils.flt(doc.upper_range), currency=doc.currency) }}

                                                            {% endif %}
                                                            {%- if doc.job_application_route -%} - {{ _("Apply Now") }} {% else %} - {{ _("Apply Now") }} {% endif %} diff --git a/erpnext/hr/doctype/job_opening/test_job_opening.js b/erpnext/hr/doctype/job_opening/test_job_opening.js index b9e6c0a8b2d..cc2f027e85b 100644 --- a/erpnext/hr/doctype/job_opening/test_job_opening.js +++ b/erpnext/hr/doctype/job_opening/test_job_opening.js @@ -24,4 +24,3 @@ QUnit.test("Test: Job Opening [HR]", function (assert) { () => done() ]); }); - diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.js b/erpnext/hr/doctype/leave_allocation/leave_allocation.js index e9e129cdd24..d94764104d0 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.js @@ -100,4 +100,4 @@ frappe.ui.form.on("Leave Allocation", { frm.set_value("total_leaves_allocated", flt(frm.doc.new_leaves_allocated)); } } -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py b/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py index 7456aebb457..7a063d92eac 100644 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation_dashboard.py @@ -17,4 +17,4 @@ def get_data(): 'items': ['Employee Leave Balance'] } ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js index 0ef78f2f883..d5364fc8b2e 100644 --- a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js @@ -38,4 +38,4 @@ QUnit.test("Test: Leave allocation [HR]", function (assert) { "total leave calculation is correctly set"), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/leave_application/leave_application_calendar.js b/erpnext/hr/doctype/leave_application/leave_application_calendar.js index 0286f300646..31faadb1079 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_calendar.js +++ b/erpnext/hr/doctype/leave_application/leave_application_calendar.js @@ -17,4 +17,4 @@ frappe.views.calendar["Leave Application"] = { } }, get_events_method: "erpnext.hr.doctype.leave_application.leave_application.get_events" -} \ No newline at end of file +} diff --git a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py index c1d6a6665b6..c45717f5870 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_dashboard.py +++ b/erpnext/hr/doctype/leave_application/leave_application_dashboard.py @@ -17,4 +17,4 @@ def get_data(): 'items': ['Employee Leave Balance'] } ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/leave_application/leave_application_email_template.html b/erpnext/hr/doctype/leave_application/leave_application_email_template.html index 209302e8f30..14ca41bebca 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_email_template.html +++ b/erpnext/hr/doctype/leave_application/leave_application_email_template.html @@ -21,5 +21,5 @@ Status {{status}} - + diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.js b/erpnext/hr/doctype/leave_application/test_leave_application.js index 6d7b6a70588..0866b0b6d2a 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.js +++ b/erpnext/hr/doctype/leave_application/test_leave_application.js @@ -39,4 +39,4 @@ QUnit.test("Test: Leave application [HR]", function (assert) { // "leave for correct employee is submitted"), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py b/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py index 2aa54984ec5..45aa4915bc6 100644 --- a/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py +++ b/erpnext/hr/doctype/leave_block_list/leave_block_list_dashboard.py @@ -8,4 +8,4 @@ def get_data(): 'items': ['Department'] } ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js index 453787865c7..b39601b490d 100644 --- a/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js +++ b/erpnext/hr/doctype/leave_block_list/test_leave_block_list.js @@ -24,4 +24,4 @@ QUnit.test("Test: Leave block list [HR]", function (assert) { 'name of blocked leave list correctly saved'), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py b/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py index be06b768bf7..8e5a09e01ec 100644 --- a/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py +++ b/erpnext/hr/doctype/leave_block_list_allow/leave_block_list_allow.py @@ -9,4 +9,4 @@ import frappe from frappe.model.document import Document class LeaveBlockListAllow(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py b/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py index f4028f54eba..54978a1e83a 100644 --- a/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py +++ b/erpnext/hr/doctype/leave_block_list_date/leave_block_list_date.py @@ -9,4 +9,4 @@ import frappe from frappe.model.document import Document class LeaveBlockListDate(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js index b60e225a727..4a450807ccf 100644 --- a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js +++ b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.js @@ -21,4 +21,4 @@ frappe.ui.form.on("Leave Control Panel", { }); } } -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js index 2b5cec1c1e1..9d373277175 100644 --- a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js +++ b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js @@ -47,4 +47,4 @@ QUnit.test("Test: Leave control panel [HR]", function (assert) { }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index 912bd8ad92f..d136210a043 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -134,4 +134,4 @@ def create_leave_encashment(leave_allocation): leave_type=allocation.leave_type, encashment_date=allocation.to_date )) - leave_encashment.insert(ignore_permissions=True) \ No newline at end of file + leave_encashment.insert(ignore_permissions=True) diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index cf130361810..33a6243e609 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -185,4 +185,4 @@ def expire_carried_forward_allocation(allocation): from_date=allocation.to_date, to_date=allocation.to_date ) - create_leave_ledger_entry(allocation, args) \ No newline at end of file + create_leave_ledger_entry(allocation, args) diff --git a/erpnext/hr/doctype/leave_period/leave_period_dashboard.py b/erpnext/hr/doctype/leave_period/leave_period_dashboard.py index 1572de3cb72..7c2c9632d85 100644 --- a/erpnext/hr/doctype/leave_period/leave_period_dashboard.py +++ b/erpnext/hr/doctype/leave_period/leave_period_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Leave Allocation'] } ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.py b/erpnext/hr/doctype/leave_period/test_leave_period.py index b5857bcd8fe..cbb34371fc9 100644 --- a/erpnext/hr/doctype/leave_period/test_leave_period.py +++ b/erpnext/hr/doctype/leave_period/test_leave_period.py @@ -27,4 +27,4 @@ def create_leave_period(from_date, to_date, company=None): "to_date": to_date, "is_active": 1 }).insert() - return leave_period \ No newline at end of file + return leave_period diff --git a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py index ff7f0422e03..474f3a77ad0 100644 --- a/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py +++ b/erpnext/hr/doctype/leave_policy/leave_policy_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Leave Policy Assignment', 'Leave Allocation'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/leave_policy/test_leave_policy.py b/erpnext/hr/doctype/leave_policy/test_leave_policy.py index fc868ea15a6..af7567b5bc7 100644 --- a/erpnext/hr/doctype/leave_policy/test_leave_policy.py +++ b/erpnext/hr/doctype/leave_policy/test_leave_policy.py @@ -28,4 +28,4 @@ def create_leave_policy(**args): "leave_type": args.leave_type or "_Test Leave Type", "annual_allocation": args.annual_allocation or 10 }] - }) \ No newline at end of file + }) diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py index 4bb0535cf8c..a2f7f5866b7 100644 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Leave Allocation'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js index 8fe4b8f8efa..8b954c46a10 100644 --- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js +++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment_list.js @@ -105,4 +105,4 @@ frappe.listview_settings['Leave Policy Assignment'] = { }); } } -}; \ No newline at end of file +}; diff --git a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py index 9a14e3588d0..0089804f512 100644 --- a/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py +++ b/erpnext/hr/doctype/leave_policy_assignment/test_leave_policy_assignment.py @@ -99,5 +99,3 @@ class TestLeavePolicyAssignment(unittest.TestCase): def tearDown(self): for doctype in ["Leave Application", "Leave Allocation", "Leave Policy Assignment", "Leave Ledger Entry"]: frappe.db.sql("delete from `tab{0}`".format(doctype)) #nosec - - diff --git a/erpnext/hr/doctype/leave_type/leave_type_dashboard.py b/erpnext/hr/doctype/leave_type/leave_type_dashboard.py index 5cae9a8809c..c8944fcb9e2 100644 --- a/erpnext/hr/doctype/leave_type/leave_type_dashboard.py +++ b/erpnext/hr/doctype/leave_type/leave_type_dashboard.py @@ -11,4 +11,4 @@ def get_data(): 'items': ['Attendance', 'Leave Encashment'] } ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.js b/erpnext/hr/doctype/leave_type/test_leave_type.js index d939a248102..db910cde512 100644 --- a/erpnext/hr/doctype/leave_type/test_leave_type.js +++ b/erpnext/hr/doctype/leave_type/test_leave_type.js @@ -19,4 +19,4 @@ QUnit.test("Test: Leave type [HR]", function (assert) { 'leave type correctly saved'), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.py b/erpnext/hr/doctype/leave_type/test_leave_type.py index 7fef2975c8a..048dddd3ef9 100644 --- a/erpnext/hr/doctype/leave_type/test_leave_type.py +++ b/erpnext/hr/doctype/leave_type/test_leave_type.py @@ -28,4 +28,4 @@ def create_leave_type(**args): if leave_type.is_ppl: leave_type.fraction_of_daily_salary_per_leave = args.fraction_of_daily_salary_per_leave or 0.5 - return leave_type \ No newline at end of file + return leave_type diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js index bb692e1402e..5d2360f10fa 100644 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js +++ b/erpnext/hr/doctype/shift_assignment/shift_assignment_calendar.js @@ -10,4 +10,4 @@ frappe.views.calendar["Shift Assignment"] = { "allDay": "allDay", }, get_events_method: "erpnext.hr.doctype.shift_assignment.shift_assignment.get_events" -} \ No newline at end of file +} diff --git a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py b/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py index 4c3c1ed579e..07d92fe61d6 100644 --- a/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py +++ b/erpnext/hr/doctype/shift_assignment/test_shift_assignment.py @@ -77,4 +77,4 @@ class TestShiftAssignment(unittest.TestCase): "status": 'Active' }) - self.assertRaises(frappe.ValidationError, shift_assignment_3.save) \ No newline at end of file + self.assertRaises(frappe.ValidationError, shift_assignment_3.save) diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py index 6461f07552b..2731da125a8 100644 --- a/erpnext/hr/doctype/shift_request/shift_request.py +++ b/erpnext/hr/doctype/shift_request/shift_request.py @@ -94,4 +94,4 @@ class ShiftRequest(Document): msg = _("Employee {0} has already applied for {1} between {2} and {3} : ").format(self.employee, d['shift_type'], formatdate(d['from_date']), formatdate(d['to_date'])) \ + """ {0}""".format(d["name"]) - frappe.throw(msg, OverlapError) \ No newline at end of file + frappe.throw(msg, OverlapError) diff --git a/erpnext/hr/doctype/shift_request/shift_request_dashboard.py b/erpnext/hr/doctype/shift_request/shift_request_dashboard.py index e3bf5df9490..f70b61a20a6 100644 --- a/erpnext/hr/doctype/shift_request/shift_request_dashboard.py +++ b/erpnext/hr/doctype/shift_request/shift_request_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Shift Assignment'] }, ], - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/shift_request/test_shift_request.py b/erpnext/hr/doctype/shift_request/test_shift_request.py index 3525540cdfd..60b7676e251 100644 --- a/erpnext/hr/doctype/shift_request/test_shift_request.py +++ b/erpnext/hr/doctype/shift_request/test_shift_request.py @@ -106,4 +106,4 @@ def make_shift_request(approver, do_not_submit=0): return shift_request shift_request.submit() - return shift_request \ No newline at end of file + return shift_request diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py b/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py index 35a303f0fb2..8e89d53c8e0 100644 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py +++ b/erpnext/hr/doctype/staffing_plan/staffing_plan_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Job Opening'] } ], - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py index 628255b11f5..1c6218e9a70 100644 --- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py +++ b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py @@ -94,4 +94,4 @@ def make_company(): company.parent_company = "_Test Company 3" company.default_currency = "INR" company.country = "Pakistan" - company.insert() \ No newline at end of file + company.insert() diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py index 9b32136bfb6..6a275b330c0 100644 --- a/erpnext/hr/doctype/training_event/test_training_event.py +++ b/erpnext/hr/doctype/training_event/test_training_event.py @@ -58,4 +58,4 @@ def create_training_event(attendees): "end_time": add_days(today(), 6), "introduction": "Welcome to the Basic Training Event", "employees": attendees - }).insert() \ No newline at end of file + }).insert() diff --git a/erpnext/hr/doctype/training_event/tests/test_training_event.js b/erpnext/hr/doctype/training_event/tests/test_training_event.js index 8ff4fecd6ee..08031a1963e 100644 --- a/erpnext/hr/doctype/training_event/tests/test_training_event.js +++ b/erpnext/hr/doctype/training_event/tests/test_training_event.js @@ -56,4 +56,4 @@ QUnit.test("Test: Training Event [HR]", function (assert) { () => frappe.timeout(2), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/training_event/training_event.js b/erpnext/hr/doctype/training_event/training_event.js index d5f6e5f573e..642e6a1fd75 100644 --- a/erpnext/hr/doctype/training_event/training_event.js +++ b/erpnext/hr/doctype/training_event/training_event.js @@ -46,4 +46,3 @@ frappe.ui.form.on("Training Event Employee", { frm.events.set_employee_query(frm); } }); - diff --git a/erpnext/hr/doctype/training_event/training_event_dashboard.py b/erpnext/hr/doctype/training_event/training_event_dashboard.py index 1c1645c766f..19afd8dd6e1 100644 --- a/erpnext/hr/doctype/training_event/training_event_dashboard.py +++ b/erpnext/hr/doctype/training_event/training_event_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Training Result', 'Training Feedback'] }, ], - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.js b/erpnext/hr/doctype/training_feedback/test_training_feedback.js index 9daa51f9275..5c825aea7fb 100644 --- a/erpnext/hr/doctype/training_feedback/test_training_feedback.js +++ b/erpnext/hr/doctype/training_feedback/test_training_feedback.js @@ -49,4 +49,3 @@ QUnit.test("Test: Training Feedback [HR]", function (assert) { () => done() ]); }); - diff --git a/erpnext/hr/doctype/training_feedback/test_training_feedback.py b/erpnext/hr/doctype/training_feedback/test_training_feedback.py index c30a3ad34cd..4c0c18029d0 100644 --- a/erpnext/hr/doctype/training_feedback/test_training_feedback.py +++ b/erpnext/hr/doctype/training_feedback/test_training_feedback.py @@ -64,4 +64,4 @@ def create_training_feedback(event, employee): "training_event": event, "employee": employee, "feedback": "Test" - }) \ No newline at end of file + }) diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.js b/erpnext/hr/doctype/training_feedback/training_feedback.js index 0dea098a67e..5e875c1b434 100644 --- a/erpnext/hr/doctype/training_feedback/training_feedback.js +++ b/erpnext/hr/doctype/training_feedback/training_feedback.js @@ -7,4 +7,4 @@ frappe.ui.form.on('Training Feedback', { frm.add_fetch("training_event", "event_name", "event_name"); frm.add_fetch("training_event", "trainer_name", "trainer_name"); } -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/training_feedback/training_feedback.py b/erpnext/hr/doctype/training_feedback/training_feedback.py index 0d32de793c4..3d4b9b3ea96 100644 --- a/erpnext/hr/doctype/training_feedback/training_feedback.py +++ b/erpnext/hr/doctype/training_feedback/training_feedback.py @@ -42,4 +42,3 @@ class TrainingFeedback(Document): if employee: frappe.db.set_value("Training Event Employee", employee, "status", "Completed") - diff --git a/erpnext/hr/doctype/training_program/training_program.js b/erpnext/hr/doctype/training_program/training_program.js index 7d85cab59dc..a4ccf540636 100644 --- a/erpnext/hr/doctype/training_program/training_program.js +++ b/erpnext/hr/doctype/training_program/training_program.js @@ -2,4 +2,4 @@ // For license information, please see license.txt frappe.ui.form.on('Training Program', { -}); \ No newline at end of file +}); diff --git a/erpnext/hr/doctype/training_program/training_program_dashboard.py b/erpnext/hr/doctype/training_program/training_program_dashboard.py index 441a71bba77..0fc18a80298 100644 --- a/erpnext/hr/doctype/training_program/training_program_dashboard.py +++ b/erpnext/hr/doctype/training_program/training_program_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Training Event'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/training_result/training_result.js b/erpnext/hr/doctype/training_result/training_result.js index 62ac383ab78..5cdbcad8058 100644 --- a/erpnext/hr/doctype/training_result/training_result.js +++ b/erpnext/hr/doctype/training_result/training_result.js @@ -11,7 +11,7 @@ frappe.ui.form.on('Training Result', { }, training_event: function(frm) { - if (frm.doc.training_event && !frm.doc.docstatus && !frm.doc.employees) { + if (frm.doc.training_event && !frm.doc.docstatus && !frm.doc.employees) { frappe.call({ method: "erpnext.hr.doctype.training_result.training_result.get_employees", args: { diff --git a/erpnext/hr/doctype/training_result_employee/test_training_result.js b/erpnext/hr/doctype/training_result_employee/test_training_result.js index 2ebf8962ee2..3f397508357 100644 --- a/erpnext/hr/doctype/training_result_employee/test_training_result.js +++ b/erpnext/hr/doctype/training_result_employee/test_training_result.js @@ -50,4 +50,3 @@ QUnit.test("Test: Training Result [HR]", function (assert) { () => done() ]); }); - diff --git a/erpnext/hr/doctype/vehicle/vehicle_dashboard.py b/erpnext/hr/doctype/vehicle/vehicle_dashboard.py index 761c70182b2..628c8972cec 100644 --- a/erpnext/hr/doctype/vehicle/vehicle_dashboard.py +++ b/erpnext/hr/doctype/vehicle/vehicle_dashboard.py @@ -17,4 +17,4 @@ def get_data(): 'items': ['Delivery Trip'] } ] - } \ No newline at end of file + } diff --git a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py index ed52c4e1222..ed02120cca3 100644 --- a/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py +++ b/erpnext/hr/doctype/vehicle_log/test_vehicle_log.py @@ -115,4 +115,4 @@ def make_vehicle_log(license_plate, employee_id, with_services=False): vehicle_log.save() vehicle_log.submit() - return vehicle_log \ No newline at end of file + return vehicle_log diff --git a/erpnext/hr/doctype/vehicle_log/vehicle_log.js b/erpnext/hr/doctype/vehicle_log/vehicle_log.js index 6f3a0dc40eb..14fe9a02da2 100644 --- a/erpnext/hr/doctype/vehicle_log/vehicle_log.js +++ b/erpnext/hr/doctype/vehicle_log/vehicle_log.js @@ -24,4 +24,3 @@ frappe.ui.form.on("Vehicle Log", { }); } }); - diff --git a/erpnext/hr/notification/training_feedback/training_feedback.html b/erpnext/hr/notification/training_feedback/training_feedback.html index fd8fef9e82c..b49662a6eb9 100644 --- a/erpnext/hr/notification/training_feedback/training_feedback.html +++ b/erpnext/hr/notification/training_feedback/training_feedback.html @@ -3,4 +3,4 @@

                                                            You attended training {{ frappe.utils.get_link_to_form( "Training Event", doc.training_event) }}

                                                            -

                                                            {{ _("Please share your feedback to the training by clicking on 'Training Feedback' and then 'New'") }}

                                                            \ No newline at end of file +

                                                            {{ _("Please share your feedback to the training by clicking on 'Training Feedback' and then 'New'") }}

                                                            diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.html b/erpnext/hr/notification/training_scheduled/training_scheduled.html index 374038ac202..50f6d07a470 100644 --- a/erpnext/hr/notification/training_scheduled/training_scheduled.html +++ b/erpnext/hr/notification/training_scheduled/training_scheduled.html @@ -41,4 +41,4 @@ - \ No newline at end of file + diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.js b/erpnext/hr/page/organizational_chart/organizational_chart.js index 08f2c94ad44..81162a4c9a8 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.js +++ b/erpnext/hr/page/organizational_chart/organizational_chart.js @@ -18,4 +18,4 @@ frappe.pages['organizational-chart'].on_page_load = function(wrapper) { organizational_chart.show(); }); }); -}; \ No newline at end of file +}; diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 29831982172..4423d29e402 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -45,4 +45,4 @@ def get_connections(employee): num_connections += len(descendants) nodes_to_expand.extend(descendants) - return num_connections \ No newline at end of file + return num_connections diff --git a/erpnext/hr/page/team_updates/team_updates.py b/erpnext/hr/page/team_updates/team_updates.py index a6cf935985b..58cdc4b7e1d 100644 --- a/erpnext/hr/page/team_updates/team_updates.py +++ b/erpnext/hr/page/team_updates/team_updates.py @@ -17,4 +17,4 @@ def get_data(start=0): if d.text_content: d.content = frappe.utils.md_to_html(EmailReplyParser.parse_reply(d.text_content)) - return data \ No newline at end of file + return data diff --git a/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html b/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html index d60582e1a18..87daafcaaee 100644 --- a/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html +++ b/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html @@ -35,4 +35,4 @@
                                                            ________________
                                                            {{ doc.applicant_name }} -
                                                            \ No newline at end of file +
                                                            diff --git a/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py b/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py index aa8eea5d74e..d8691b4d025 100644 --- a/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py +++ b/erpnext/hr/report/daily_work_summary_replies/daily_work_summary_replies.py @@ -54,4 +54,4 @@ def get_data(filters): user_name = frappe.get_value('User', user, 'full_name') count = len([d for d in replies if d.sender == user]) data.append([user_name, count, total]) - return data \ No newline at end of file + return data diff --git a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js index 528ae4cea63..8de4af5d4fc 100644 --- a/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js +++ b/erpnext/hr/report/employee_advance_summary/employee_advance_summary.js @@ -38,4 +38,3 @@ frappe.query_reports["Employee Advance Summary"] = { } ] }; - diff --git a/erpnext/hr/report/employee_analytics/employee_analytics.py b/erpnext/hr/report/employee_analytics/employee_analytics.py index 8f393889269..fe77b6abc96 100644 --- a/erpnext/hr/report/employee_analytics/employee_analytics.py +++ b/erpnext/hr/report/employee_analytics/employee_analytics.py @@ -81,4 +81,3 @@ def get_chart_data(parameters,employees, filters): } chart["type"] = "donut" return chart - diff --git a/erpnext/hr/report/employee_birthday/employee_birthday.js b/erpnext/hr/report/employee_birthday/employee_birthday.js index 60b69b409a0..bbe4a8d179e 100644 --- a/erpnext/hr/report/employee_birthday/employee_birthday.js +++ b/erpnext/hr/report/employee_birthday/employee_birthday.js @@ -8,7 +8,7 @@ frappe.query_reports["Employee Birthday"] = { "label": __("Month"), "fieldtype": "Select", "options": "Jan\nFeb\nMar\nApr\nMay\nJun\nJul\nAug\nSep\nOct\nNov\nDec", - "default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", + "default": ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][frappe.datetime.str_to_obj(frappe.datetime.get_today()).getMonth()], }, { @@ -19,4 +19,4 @@ frappe.query_reports["Employee Birthday"] = { "default": frappe.defaults.get_user_default("Company") } ] -} \ No newline at end of file +} diff --git a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js index 9620f520002..51dc7ff85b8 100644 --- a/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js +++ b/erpnext/hr/report/recruitment_analytics/recruitment_analytics.js @@ -20,4 +20,4 @@ frappe.query_reports["Recruitment Analytics"] = { "reqd": 1, }, ] -}; \ No newline at end of file +}; diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js index 879acd18ef4..2d0aa0f36d3 100644 --- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js +++ b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.js @@ -49,4 +49,3 @@ frappe.query_reports["Vehicle Expenses"] = { } ] }; - diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 992b18d37a6..a1026ce055b 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -413,4 +413,4 @@ def share_doc_with_approver(doc, user): def validate_active_employee(employee): if frappe.db.get_value("Employee", employee, "status") == "Inactive": frappe.throw(_("Transactions cannot be created for an Inactive Employee {0}.").format( - get_link_to_form("Employee", employee)), InactiveEmployeeStatusError) \ No newline at end of file + get_link_to_form("Employee", employee)), InactiveEmployeeStatusError) diff --git a/erpnext/hr/web_form/job_application/job_application.js b/erpnext/hr/web_form/job_application/job_application.js index 699703c5792..ffc5e984253 100644 --- a/erpnext/hr/web_form/job_application/job_application.js +++ b/erpnext/hr/web_form/job_application/job_application.js @@ -1,3 +1,3 @@ frappe.ready(function() { // bind events here -}) \ No newline at end of file +}) diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js index cf75cc8e41a..58179416b1a 100644 --- a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js +++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js @@ -11,4 +11,4 @@ frappe.dashboards.chart_sources["Top 10 Pledged Loan Securities"] = { default: frappe.defaults.get_user_default("Company") } ] -}; \ No newline at end of file +}; diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py index 6bb04401bed..6ce2a54b190 100644 --- a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py +++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.py @@ -73,4 +73,4 @@ def get_data(chart_name = None, chart = None, no_cache = None, filters = None, f 'chartType': 'bar', 'values': values }] - } \ No newline at end of file + } diff --git a/erpnext/loan_management/doctype/loan/loan_dashboard.py b/erpnext/loan_management/doctype/loan/loan_dashboard.py index 7a8190f7450..711a7829baf 100644 --- a/erpnext/loan_management/doctype/loan/loan_dashboard.py +++ b/erpnext/loan_management/doctype/loan/loan_dashboard.py @@ -16,4 +16,4 @@ def get_data(): 'items': ['Loan Repayment', 'Loan Interest Accrual', 'Loan Write Off', 'Loan Security Unpledge'] } ] - } \ No newline at end of file + } diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py index 314f58dd15e..122d7236051 100644 --- a/erpnext/loan_management/doctype/loan/test_loan.py +++ b/erpnext/loan_management/doctype/loan/test_loan.py @@ -988,4 +988,4 @@ def create_demand_loan(applicant, loan_type, loan_application, posting_date=None loan.save() - return loan \ No newline at end of file + return loan diff --git a/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py b/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py index bf3f58b83ef..3975adf4431 100644 --- a/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py +++ b/erpnext/loan_management/doctype/loan_application/loan_application_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Loan', 'Loan Security Pledge'] }, ], - } \ No newline at end of file + } diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index f341e81065f..f113c10ef71 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -203,5 +203,3 @@ def get_disbursal_amount(loan, on_current_security_price=0): disbursal_amount = loan_details.loan_amount - loan_details.disbursed_amount return disbursal_amount - - diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 7978350adf8..d75213ce78d 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -247,4 +247,3 @@ def get_per_day_interest(principal_amount, rate_of_interest, posting_date=None): posting_date = getdate() return flt((principal_amount * rate_of_interest) / (days_in_year(get_datetime(posting_date).year) * 100)) - diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index b8b1a40b5fd..57aec2e5c9c 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -455,6 +455,3 @@ def calculate_amounts(against_loan, posting_date, payment_type=''): amounts['payable_amount'] = amounts['payable_principal_amount'] + amounts['interest_amount'] return amounts - - - diff --git a/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py b/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py index 878b3fd051e..3eec5660ac1 100644 --- a/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py +++ b/erpnext/loan_management/doctype/loan_security/loan_security_dashboard.py @@ -12,4 +12,4 @@ def get_data(): 'items': ['Loan Security Pledge', 'Loan Security Unpledge'] } ] - } \ No newline at end of file + } diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js index 11c932ff1c1..48ca392edf7 100644 --- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js +++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js @@ -40,4 +40,4 @@ frappe.ui.form.on("Pledge", { qty: function(frm, cdt, cdn) { frm.events.calculate_amounts(frm, cdt, cdn); }, -}); \ No newline at end of file +}); diff --git a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.py b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.py index 32d81afed5d..9fc1fda53f4 100644 --- a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.py +++ b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.py @@ -40,12 +40,3 @@ def get_loan_security_price(loan_security, valid_time=None): frappe.throw(_("No valid Loan Security Price found for {0}").format(frappe.bold(loan_security))) else: return loan_security_price - - - - - - - - - diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py index 8233b7b297a..cd7694b7b17 100644 --- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py +++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py @@ -122,4 +122,3 @@ def update_pending_shortfall(shortfall): "shortfall_amount": 0, "shortfall_percentage": 0 }) - diff --git a/erpnext/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py b/erpnext/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py index ac33589b549..17de8c1da4d 100644 --- a/erpnext/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py +++ b/erpnext/loan_management/doctype/loan_security_type/loan_security_type_dashboard.py @@ -12,4 +12,4 @@ def get_data(): 'items': ['Loan Security Pledge', 'Loan Security Unpledge'] } ] - } \ No newline at end of file + } diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py index b24dc2f7c28..4f936dd7c11 100644 --- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py +++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py @@ -147,8 +147,3 @@ def get_pledged_security_qty(loan): current_pledges[security] -= unpledges.get(security, 0.0) return current_pledges - - - - - diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.py b/erpnext/loan_management/doctype/loan_type/loan_type.py index 208cb19c88e..50ef930dbbe 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type.py +++ b/erpnext/loan_management/doctype/loan_type/loan_type.py @@ -21,4 +21,3 @@ class LoanType(Document): if self.get('loan_account') == self.get('payment_account'): frappe.throw(_('Loan Account and Payment Account cannot be same')) - diff --git a/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py b/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py index 58c668948c2..95d97fdf9b0 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py +++ b/erpnext/loan_management/doctype/loan_type/loan_type_dashboard.py @@ -12,4 +12,4 @@ def get_data(): 'items': ['Loan Application'] } ] - } \ No newline at end of file + } diff --git a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py index 54a3f2cbb1f..676df701cc3 100644 --- a/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py +++ b/erpnext/loan_management/doctype/loan_write_off/loan_write_off.py @@ -84,5 +84,3 @@ class LoanWriteOff(AccountsController): ) make_gl_entries(gl_entries, cancel=cancel, merge_entries=False) - - diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py index 11333dc2aaf..8c67c0affee 100644 --- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py @@ -61,4 +61,3 @@ def term_loan_accrual_pending(date): }) return pending_accrual - diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual_dashboard.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual_dashboard.py index 243a7a3ba66..e104c6646b0 100644 --- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual_dashboard.py +++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Loan Interest Accrual'] } ] - } \ No newline at end of file + } diff --git a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall_dashboard.py b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall_dashboard.py index dc9bd81a1dd..e67e4d4738f 100644 --- a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall_dashboard.py +++ b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Loan Security Shortfall'] } ] - } \ No newline at end of file + } diff --git a/erpnext/loan_management/loan_common.js b/erpnext/loan_management/loan_common.js index 50b68da30e3..43980ffef48 100644 --- a/erpnext/loan_management/loan_common.js +++ b/erpnext/loan_management/loan_common.js @@ -40,4 +40,4 @@ frappe.ui.form.on(cur_frm.doctype, { frm.set_value("applicant_name", null); } } -}); \ No newline at end of file +}); diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py index 0ccd149e5fb..f2cbbb469f0 100644 --- a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py +++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.py @@ -136,4 +136,4 @@ def get_applicant_wise_total_loan_security_qty(filters, loan_security_details): total_value_map[security.applicant] += current_pledges.get((security.applicant, security.loan_security)) \ * loan_security_details.get(security.loan_security, {}).get('latest_price', 0) - return current_pledges, total_value_map, applicant_type_map \ No newline at end of file + return current_pledges, total_value_map, applicant_type_map diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py index 2a74a1eb858..a505e72c4d9 100644 --- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py +++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py @@ -182,4 +182,4 @@ def get_loan_wise_security_value(filters, current_pledges): loan_wise_security_value[key[0]] += \ flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0)) - return loan_wise_security_value \ No newline at end of file + return loan_wise_security_value diff --git a/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py b/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py index c6f6b990cc5..65910770881 100644 --- a/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py +++ b/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.py @@ -126,4 +126,4 @@ def get_data(filters): data.append(row) - return data \ No newline at end of file + return data diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py index 887a86a46c5..34bbe5a4503 100644 --- a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py +++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.py @@ -79,6 +79,3 @@ def get_company_wise_loan_security_details(filters, loan_security_details): total_portfolio_value += flt(qty * loan_security_details.get(key[1], {}).get('latest_price', 0)) return security_wise_map, total_portfolio_value - - - diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js index 546a68f268b..d1a8c8de27c 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js @@ -69,10 +69,10 @@ erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frap if (flag) { this.frm.add_custom_button(__('Maintenance Visit'), function () { let options = ""; - + me.frm.call('get_pending_data', {data_type: "items"}).then(r => { options = r.message; - + let schedule_id = ""; let d = new frappe.ui.Dialog({ title: __("Enter Visit Details"), @@ -86,7 +86,7 @@ erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frap let field = d.get_field("scheduled_date"); me.frm.call('get_pending_data', { - item_name: this.value, + item_name: this.value, data_type: "date" }).then(r => { field.df.options = r.message; @@ -157,10 +157,9 @@ erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frap let me = this; if (item.start_date && item.periodicity) { me.frm.call('validate_end_date_visits'); - + } } }; extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm})); - diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index d6e42f3ee1c..97289032d70 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -52,15 +52,15 @@ class MaintenanceSchedule(TransactionBase): item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity]) else: item.end_date = add_days(item.start_date, days_in_period[item.periodicity]) - + diff = date_diff(item.end_date, item.start_date) + 1 no_of_visits = cint(diff / days_in_period[item.periodicity]) - + if not item.no_of_visits or item.no_of_visits == 0: item.end_date = add_days(item.start_date, days_in_period[item.periodicity]) diff = date_diff(item.end_date, item.start_date) + 1 item.no_of_visits = cint(diff / days_in_period[item.periodicity]) - + elif item.no_of_visits > no_of_visits: item.end_date = add_days(item.start_date, item.no_of_visits * days_in_period[item.periodicity]) @@ -207,7 +207,7 @@ class MaintenanceSchedule(TransactionBase): def on_update(self): frappe.db.set(self, 'status', 'Draft') - + def update_amc_date(self, serial_nos, amc_expiry_date=None): for serial_no in serial_nos: serial_no_doc = frappe.get_doc("Serial No", serial_no) @@ -300,7 +300,7 @@ class MaintenanceSchedule(TransactionBase): for schedule in self.schedules: if schedule.item_name == item_name and s_date == formatdate(schedule.scheduled_date, "dd-mm-yyyy"): return schedule.name - + @frappe.whitelist() def update_serial_nos(s_id): serial_nos = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'serial_no') @@ -318,12 +318,12 @@ def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=No target.maintenance_type = "Scheduled" target.maintenance_schedule = source.name target.maintenance_schedule_detail = s_id - + def update_sales(source, target, parent): sales_person = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'sales_person') target.service_person = sales_person target.serial_no = '' - + doclist = get_mapped_doc("Maintenance Schedule", source_name, { "Maintenance Schedule": { "doctype": "Maintenance Visit", diff --git a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py index 09981bad05f..c733dd0c92c 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/test_maintenance_schedule.py @@ -22,7 +22,7 @@ class TestMaintenanceSchedule(unittest.TestCase): ms.cancel() events_after_cancel = get_events(ms) self.assertTrue(len(events_after_cancel) == 0) - + def test_make_schedule(self): ms = make_maintenance_schedule() ms.save() @@ -72,7 +72,7 @@ class TestMaintenanceSchedule(unittest.TestCase): #checks if visit status is back updated in schedule self.assertTrue(ms.schedules[1].completion_status, "Partially Completed") - + def get_events(ms): return frappe.get_all("Event Participants", filters={ "reference_doctype": ms.doctype, diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py index 7fffc942a03..d63c7003870 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py @@ -28,11 +28,11 @@ class MaintenanceVisit(TransactionBase): def validate(self): self.validate_serial_no() self.validate_maintenance_date() - + def update_completion_status(self): if self.maintenance_schedule_detail: frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'completion_status', self.completion_status) - + def update_actual_date(self): if self.maintenance_schedule_detail: frappe.db.set_value('Maintenance Schedule Detail', self.maintenance_schedule_detail, 'actual_date', self.mntc_date) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js index f19a1b08681..d3bb33e86e0 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js @@ -85,5 +85,3 @@ frappe.ui.form.on('Blanket Order', { frm.trigger('set_tc_name_filter'); } }); - - diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py index d7556add805..1aedb1e590f 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -76,4 +76,4 @@ def make_order(source_name): "postprocess": update_item } }) - return target_doc \ No newline at end of file + return target_doc diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py index 3171defdaea..9a0a72fb475 100644 --- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py @@ -88,4 +88,4 @@ def make_blanket_order(**args): bo.insert() bo.submit() - return bo \ No newline at end of file + return bo diff --git a/erpnext/manufacturing/doctype/bom/bom_item_preview.html b/erpnext/manufacturing/doctype/bom/bom_item_preview.html index 6088e46265b..e614a7ebaa1 100644 --- a/erpnext/manufacturing/doctype/bom/bom_item_preview.html +++ b/erpnext/manufacturing/doctype/bom/bom_item_preview.html @@ -38,4 +38,4 @@ {{ __("Open Item {0}", [data.item_code.bold()]) }} {% endif %}

                                                            -
                                                            \ No newline at end of file + diff --git a/erpnext/manufacturing/doctype/bom/bom_tree.js b/erpnext/manufacturing/doctype/bom/bom_tree.js index 60fb377f476..6e2599e41bc 100644 --- a/erpnext/manufacturing/doctype/bom/bom_tree.js +++ b/erpnext/manufacturing/doctype/bom/bom_tree.js @@ -70,4 +70,4 @@ frappe.treeview_settings["BOM"] = { } }, view_template: 'bom_item_preview' -} \ No newline at end of file +} diff --git a/erpnext/manufacturing/doctype/bom/test_bom.js b/erpnext/manufacturing/doctype/bom/test_bom.js index 5044a284444..98a9198b79b 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.js +++ b/erpnext/manufacturing/doctype/bom/test_bom.js @@ -60,4 +60,4 @@ QUnit.test("test: item", function (assert) { () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.py b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.py index cc5a3f8cb1a..39ccbddbea2 100644 --- a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.py +++ b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class BOMExplosionItem(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.py b/erpnext/manufacturing/doctype/bom_item/bom_item.py index e7cdea290b8..220c73e1493 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.py +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class BOMItem(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/manufacturing/doctype/bom_operation/bom_operation.py b/erpnext/manufacturing/doctype/bom_operation/bom_operation.py index ee3f877da3c..e3501eb9cf6 100644 --- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.py +++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class BOMOperation(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js index e4b8a202882..bf5fe2e18de 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js @@ -46,4 +46,4 @@ frappe.ui.form.on('BOM Update Tool', { } }); } -}); \ No newline at end of file +}); diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index 81860c9fbcf..91eb4a0fa90 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -367,4 +367,4 @@ frappe.ui.form.on('Job Card Time Log', { to_time: function(frm) { frm.set_value('started_time', ''); } -}) \ No newline at end of file +}) diff --git a/erpnext/manufacturing/doctype/job_card/job_card_list.js b/erpnext/manufacturing/doctype/job_card/job_card_list.js index ed851ebc83b..8017209e7de 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_list.js +++ b/erpnext/manufacturing/doctype/job_card/job_card_list.js @@ -12,4 +12,4 @@ frappe.listview_settings['Job Card'] = { return [__("Open"), "red", "status,=,Open"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py index b6a6c33d37f..8fa0b27fcb8 100644 --- a/erpnext/manufacturing/doctype/job_card/test_job_card.py +++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py @@ -72,4 +72,4 @@ class TestJobCard(unittest.TestCase): doc.cancel() for d in job_cards: - frappe.delete_doc("Job Card", d.name) \ No newline at end of file + frappe.delete_doc("Job Card", d.name) diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js index 668e981d188..a0122a47385 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js @@ -30,4 +30,4 @@ frappe.tour["Manufacturing Settings"] = [ title: __("Update BOM Cost Automatically"), description: __("If ticked, the BOM cost will be automatically updated based on Valuation Rate / Price List Rate / last purchase rate of raw materials.") } -]; \ No newline at end of file +]; diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py index e88164f9178..149fe3e22b8 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py @@ -20,4 +20,4 @@ def is_material_consumption_enabled(): frappe.local.material_consumption = cint(frappe.db.get_single_value('Manufacturing Settings', 'material_consumption')) - return frappe.local.material_consumption \ No newline at end of file + return frappe.local.material_consumption diff --git a/erpnext/manufacturing/doctype/operation/operation.js b/erpnext/manufacturing/doctype/operation/operation.js index 102b6780e5f..2936e33b118 100644 --- a/erpnext/manufacturing/doctype/operation/operation.js +++ b/erpnext/manufacturing/doctype/operation/operation.js @@ -11,4 +11,4 @@ frappe.ui.form.on('Operation', { }; }); } -}); \ No newline at end of file +}); diff --git a/erpnext/manufacturing/doctype/operation/test_operation.py b/erpnext/manufacturing/doctype/operation/test_operation.py index 00672317018..8e7e7237263 100644 --- a/erpnext/manufacturing/doctype/operation/test_operation.py +++ b/erpnext/manufacturing/doctype/operation/test_operation.py @@ -28,4 +28,4 @@ def make_operation(*args, **kwargs): return doc except frappe.DuplicateEntryError: - return frappe.get_doc("Operation", args.operation) \ No newline at end of file + return frappe.get_doc("Operation", args.operation) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py index ca597f63278..52a56af7bce 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py @@ -14,4 +14,4 @@ def get_data(): 'items': ['Purchase Order'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.py b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.py index 8b570422ddc..37cf5a49dc9 100644 --- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.py +++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class ProductionPlanItem(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.py b/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.py index ef7f79e8d2c..99c7273a640 100644 --- a/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.py +++ b/erpnext/manufacturing/doctype/production_plan_sales_order/production_plan_sales_order.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class ProductionPlanSalesOrder(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/manufacturing/doctype/routing/routing_dashboard.py b/erpnext/manufacturing/doctype/routing/routing_dashboard.py index ab309cc9d50..50a3fe62da5 100644 --- a/erpnext/manufacturing/doctype/routing/routing_dashboard.py +++ b/erpnext/manufacturing/doctype/routing/routing_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['BOM'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py index 9aa0715e7ff..403d46d8d42 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py +++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py @@ -17,4 +17,4 @@ def get_data(): 'items': ['Serial No', 'Batch'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/work_order/work_order_preview.html b/erpnext/manufacturing/doctype/work_order/work_order_preview.html index a4bf93edef1..95bdd291ee0 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_preview.html +++ b/erpnext/manufacturing/doctype/work_order/work_order_preview.html @@ -30,4 +30,4 @@

                                                            - \ No newline at end of file + diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.py b/erpnext/manufacturing/doctype/work_order_item/work_order_item.py index d18f028fc6e..9aa53b5e3c3 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.py +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.py @@ -10,4 +10,4 @@ class WorkOrderItem(Document): pass def on_doctype_update(): - frappe.db.add_index("Work Order Item", ["item_code", "source_warehouse"]) \ No newline at end of file + frappe.db.add_index("Work Order Item", ["item_code", "source_warehouse"]) diff --git a/erpnext/manufacturing/doctype/workstation/workstation.js b/erpnext/manufacturing/doctype/workstation/workstation.js index ba8e30cba07..d8d25fc6f83 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.js +++ b/erpnext/manufacturing/doctype/workstation/workstation.js @@ -16,4 +16,4 @@ frappe.ui.form.on("Workstation", { }) } } -}) \ No newline at end of file +}) diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py index e7d92658f7d..8778d9ba557 100644 --- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.py @@ -108,5 +108,3 @@ def get_columns(filters): "fieldtype": "Int", "width": 180 }] - - diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html index 119a4fc6292..2ae8848cc03 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.html @@ -24,4 +24,4 @@ {% } %} - \ No newline at end of file + diff --git a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py index 9f81e7d26a1..b4db98c3d7e 100644 --- a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py +++ b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.py @@ -124,4 +124,4 @@ def get_columns(filters): "fieldname": "total_time_in_mins", "width": "100" } - ] \ No newline at end of file + ] diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py index 093309a005b..74c794b5dd0 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.py @@ -110,4 +110,4 @@ def get_columns(filters): "fieldtype": "Text", "width": 100 } - ] \ No newline at end of file + ] diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py index fc27d355984..9a6c764c609 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.py @@ -239,4 +239,4 @@ class ForecastingReport(ExponentialSmoothingForecast): "currency": self.company_currency, "datatype": self.fieldtype } - ] \ No newline at end of file + ] diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py index b1bff3500c6..a8939051523 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -201,4 +201,4 @@ def get_columns(filters): } ]) - return columns \ No newline at end of file + return columns diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.py b/erpnext/manufacturing/report/production_analytics/production_analytics.py index 79af8a1e39b..42c9d97cb5e 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.py +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.py @@ -139,7 +139,3 @@ def get_chart_data(periodic_data, columns): chart["type"] = "line" return chart - - - - diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py index 6192632bda6..a12ac7f9d91 100644 --- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.py @@ -129,4 +129,4 @@ def get_columns(filters): } ]) - return columns \ No newline at end of file + return columns diff --git a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py index 97553e699d8..599a738f6f6 100644 --- a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py +++ b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.py @@ -10,10 +10,10 @@ def execute(filters=None): data = get_item_list(wo_list, filters) columns = get_columns() return columns, data - + def get_item_list(wo_list, filters): out = [] - + #Add a row for each item/qty for wo_details in wo_list: desc = frappe.db.get_value("BOM", wo_details.bom_no, "description") @@ -70,13 +70,13 @@ def get_item_list(wo_list, filters): out.append(row) return out - + def get_work_orders(): out = frappe.get_all("Work Order", filters={"docstatus": 1, "status": ( "!=","Completed")}, fields=["name","status", "bom_no", "qty", "produced_qty"], order_by='name') return out - + def get_columns(): columns = [{ "fieldname": "work_order", diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py index 612dad0bf51..d0766f9abe5 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py @@ -265,4 +265,4 @@ def get_columns(filters): }, ]) - return columns \ No newline at end of file + return columns diff --git a/erpnext/non_profit/doctype/chapter_member/chapter_member.py b/erpnext/non_profit/doctype/chapter_member/chapter_member.py index c4b899913b9..a1b25f2d4e2 100644 --- a/erpnext/non_profit/doctype/chapter_member/chapter_member.py +++ b/erpnext/non_profit/doctype/chapter_member/chapter_member.py @@ -7,5 +7,3 @@ from frappe.model.document import Document class ChapterMember(Document): pass - - diff --git a/erpnext/non_profit/doctype/donation/donation.py b/erpnext/non_profit/doctype/donation/donation.py index 4fd1a30ab9e..9aa7e13433c 100644 --- a/erpnext/non_profit/doctype/donation/donation.py +++ b/erpnext/non_profit/doctype/donation/donation.py @@ -217,4 +217,3 @@ def notify_failure(log): sendmail_to_system_managers(_('[Important] [ERPNext] Razorpay donation webhook failed, please check.'), content) except Exception: pass - diff --git a/erpnext/non_profit/doctype/donation/donation_dashboard.py b/erpnext/non_profit/doctype/donation/donation_dashboard.py index 7e25c8d2173..3da89423d37 100644 --- a/erpnext/non_profit/doctype/donation/donation_dashboard.py +++ b/erpnext/non_profit/doctype/donation/donation_dashboard.py @@ -13,4 +13,4 @@ def get_data(): 'items': ['Payment Entry'] } ] - } \ No newline at end of file + } diff --git a/erpnext/non_profit/doctype/donation/test_donation.py b/erpnext/non_profit/doctype/donation/test_donation.py index bbe9bf5228d..b206f54523e 100644 --- a/erpnext/non_profit/doctype/donation/test_donation.py +++ b/erpnext/non_profit/doctype/donation/test_donation.py @@ -73,4 +73,4 @@ def create_mode_of_payment(): 'company': '_Test Company', 'default_account': 'Cash - _TC' }] - }).insert() \ No newline at end of file + }).insert() diff --git a/erpnext/non_profit/doctype/donor/donor.py b/erpnext/non_profit/doctype/donor/donor.py index fb70e59575b..ab6a197ed51 100644 --- a/erpnext/non_profit/doctype/donor/donor.py +++ b/erpnext/non_profit/doctype/donor/donor.py @@ -15,4 +15,3 @@ class Donor(Document): from frappe.utils import validate_email_address if self.email: validate_email_address(self.email.strip(), True) - diff --git a/erpnext/non_profit/doctype/grant_application/grant_application.py b/erpnext/non_profit/doctype/grant_application/grant_application.py index f0123b2e494..b810fd027af 100644 --- a/erpnext/non_profit/doctype/grant_application/grant_application.py +++ b/erpnext/non_profit/doctype/grant_application/grant_application.py @@ -55,4 +55,4 @@ def send_grant_review_emails(grant_application): grant.save() frappe.db.commit() - frappe.msgprint(_("Review Invitation Sent")) \ No newline at end of file + frappe.msgprint(_("Review Invitation Sent")) diff --git a/erpnext/non_profit/doctype/member/member.js b/erpnext/non_profit/doctype/member/member.js index 6b8f1b1deb6..e58ec0f5eea 100644 --- a/erpnext/non_profit/doctype/member/member.js +++ b/erpnext/non_profit/doctype/member/member.js @@ -61,4 +61,4 @@ frappe.ui.form.on('Member', { } }); } -}); \ No newline at end of file +}); diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py index 0f5a9bed826..5ad2088fc31 100644 --- a/erpnext/non_profit/doctype/membership/test_membership.py +++ b/erpnext/non_profit/doctype/membership/test_membership.py @@ -159,4 +159,4 @@ def get_subscription_payload(): } } } - } \ No newline at end of file + } diff --git a/erpnext/non_profit/doctype/membership_type/membership_type.py b/erpnext/non_profit/doctype/membership_type/membership_type.py index 022829bd3a6..c712b99c3b8 100644 --- a/erpnext/non_profit/doctype/membership_type/membership_type.py +++ b/erpnext/non_profit/doctype/membership_type/membership_type.py @@ -15,4 +15,4 @@ class MembershipType(Document): frappe.throw(_("The Linked Item should be a service item")) def get_membership_type(razorpay_id): - return frappe.db.exists("Membership Type", {"razorpay_plan_id": razorpay_id}) \ No newline at end of file + return frappe.db.exists("Membership Type", {"razorpay_plan_id": razorpay_id}) diff --git a/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py b/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py index a84cc2cdb53..50c93516adc 100644 --- a/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py +++ b/erpnext/non_profit/doctype/non_profit_settings/non_profit_settings.py @@ -35,4 +35,4 @@ class NonProfitSettings(Document): def get_plans_for_membership(*args, **kwargs): controller = get_payment_gateway_controller("Razorpay") plans = controller.get_plans() - return [plan.get("item") for plan in plans.get("items")] \ No newline at end of file + return [plan.get("item") for plan in plans.get("items")] diff --git a/erpnext/non_profit/web_form/grant_application/grant_application.js b/erpnext/non_profit/web_form/grant_application/grant_application.js index 7da3f1fb41c..f09e5409192 100644 --- a/erpnext/non_profit/web_form/grant_application/grant_application.js +++ b/erpnext/non_profit/web_form/grant_application/grant_application.js @@ -1,3 +1,3 @@ frappe.ready(function() { // bind events here -}); \ No newline at end of file +}); diff --git a/erpnext/non_profit/web_form/grant_application/grant_application.py b/erpnext/non_profit/web_form/grant_application/grant_application.py index 7666ef6b616..186722a8bf0 100644 --- a/erpnext/non_profit/web_form/grant_application/grant_application.py +++ b/erpnext/non_profit/web_form/grant_application/grant_application.py @@ -4,5 +4,3 @@ def get_context(context): context.no_cache = True context.parents = [dict(label='View All ', route='grant-application', title='View All')] - - diff --git a/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py b/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py index 102b6da8757..daa258e8825 100644 --- a/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py +++ b/erpnext/patches/v10_0/migrate_daily_work_summary_settings_to_daily_work_summary_group.py @@ -48,4 +48,4 @@ def get_previous_setting(): return obj def get_setting_companies(): - return frappe.db.sql("select * from `tabDaily Work Summary Settings Company`", as_dict=True) \ No newline at end of file + return frappe.db.sql("select * from `tabDaily Work Summary Settings Company`", as_dict=True) diff --git a/erpnext/patches/v10_0/rename_offer_letter_to_job_offer.py b/erpnext/patches/v10_0/rename_offer_letter_to_job_offer.py index 2e3095153a5..f832936b10a 100644 --- a/erpnext/patches/v10_0/rename_offer_letter_to_job_offer.py +++ b/erpnext/patches/v10_0/rename_offer_letter_to_job_offer.py @@ -7,4 +7,4 @@ def execute(): frappe.rename_doc("DocType", "Offer Letter Term", "Job Offer Term", force=True) frappe.reload_doc("hr", "doctype", "job_offer") frappe.reload_doc("hr", "doctype", "job_offer_term") - frappe.delete_doc("Print Format", "Offer Letter") \ No newline at end of file + frappe.delete_doc("Print Format", "Offer Letter") diff --git a/erpnext/patches/v10_0/rename_price_to_rate_in_pricing_rule.py b/erpnext/patches/v10_0/rename_price_to_rate_in_pricing_rule.py index 48fa22204d1..a9dd3103100 100644 --- a/erpnext/patches/v10_0/rename_price_to_rate_in_pricing_rule.py +++ b/erpnext/patches/v10_0/rename_price_to_rate_in_pricing_rule.py @@ -11,4 +11,4 @@ def execute(): except Exception as e: if e.args[0]!=1054: - raise \ No newline at end of file + raise diff --git a/erpnext/patches/v11_0/add_default_email_template_for_leave.py b/erpnext/patches/v11_0/add_default_email_template_for_leave.py index f722be26b41..0f1e4966231 100644 --- a/erpnext/patches/v11_0/add_default_email_template_for_leave.py +++ b/erpnext/patches/v11_0/add_default_email_template_for_leave.py @@ -27,4 +27,3 @@ def execute(): 'subject': _("Leave Status Notification"), 'owner': frappe.session.user, }).insert(ignore_permissions=True) - diff --git a/erpnext/patches/v11_0/add_expense_claim_default_account.py b/erpnext/patches/v11_0/add_expense_claim_default_account.py index eecf75568a4..a613bd88497 100644 --- a/erpnext/patches/v11_0/add_expense_claim_default_account.py +++ b/erpnext/patches/v11_0/add_expense_claim_default_account.py @@ -8,4 +8,4 @@ def execute(): for company in companies: if company.default_payable_account is not None: - frappe.db.set_value("Company", company.name, "default_expense_claim_payable_account", company.default_payable_account) \ No newline at end of file + frappe.db.set_value("Company", company.name, "default_expense_claim_payable_account", company.default_payable_account) diff --git a/erpnext/patches/v11_0/add_healthcare_service_unit_tree_root.py b/erpnext/patches/v11_0/add_healthcare_service_unit_tree_root.py index d956052f1a6..a45f39d4340 100644 --- a/erpnext/patches/v11_0/add_healthcare_service_unit_tree_root.py +++ b/erpnext/patches/v11_0/add_healthcare_service_unit_tree_root.py @@ -18,4 +18,3 @@ def execute(): 'is_group': 1, 'company': company }).insert(ignore_permissions=True) - diff --git a/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py b/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py index 5a30c780f8c..0243dfb38ed 100644 --- a/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py +++ b/erpnext/patches/v11_0/add_index_on_nestedset_doctypes.py @@ -8,4 +8,4 @@ def execute(): frappe.reload_doc("assets", "doctype", "Location") for dt in ("Account", "Cost Center", "File", "Employee", "Location", "Task", "Customer Group", "Sales Person", "Territory"): frappe.reload_doctype(dt) - frappe.get_doc("DocType", dt).run_module_method("on_doctype_update") \ No newline at end of file + frappe.get_doc("DocType", dt).run_module_method("on_doctype_update") diff --git a/erpnext/patches/v11_0/add_market_segments.py b/erpnext/patches/v11_0/add_market_segments.py index ed47d4293f7..a8841ef3a44 100644 --- a/erpnext/patches/v11_0/add_market_segments.py +++ b/erpnext/patches/v11_0/add_market_segments.py @@ -9,4 +9,4 @@ def execute(): frappe.local.lang = frappe.db.get_default("lang") or 'en' - add_market_segments() \ No newline at end of file + add_market_segments() diff --git a/erpnext/patches/v11_0/add_sales_stages.py b/erpnext/patches/v11_0/add_sales_stages.py index ac2ae1511ae..d06c6889ff7 100644 --- a/erpnext/patches/v11_0/add_sales_stages.py +++ b/erpnext/patches/v11_0/add_sales_stages.py @@ -8,4 +8,4 @@ def execute(): frappe.local.lang = frappe.db.get_default("lang") or 'en' - add_sale_stages() \ No newline at end of file + add_sale_stages() diff --git a/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py b/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py index 462f830c183..0a1a36007e5 100644 --- a/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py +++ b/erpnext/patches/v11_0/check_buying_selling_in_currency_exchange.py @@ -3,4 +3,4 @@ import frappe def execute(): frappe.reload_doc('setup', 'doctype', 'currency_exchange') - frappe.db.sql("""update `tabCurrency Exchange` set for_buying = 1, for_selling = 1""") \ No newline at end of file + frappe.db.sql("""update `tabCurrency Exchange` set for_buying = 1, for_selling = 1""") diff --git a/erpnext/patches/v11_0/create_salary_structure_assignments.py b/erpnext/patches/v11_0/create_salary_structure_assignments.py index a908c16715a..d3ea7a3c1c0 100644 --- a/erpnext/patches/v11_0/create_salary_structure_assignments.py +++ b/erpnext/patches/v11_0/create_salary_structure_assignments.py @@ -69,4 +69,4 @@ def execute(): except DuplicateAssignment: pass - frappe.db.sql("update `tabSalary Structure` set docstatus=1") \ No newline at end of file + frappe.db.sql("update `tabSalary Structure` set docstatus=1") diff --git a/erpnext/patches/v11_0/drop_column_max_days_allowed.py b/erpnext/patches/v11_0/drop_column_max_days_allowed.py index 591c521efbe..029f75a2258 100644 --- a/erpnext/patches/v11_0/drop_column_max_days_allowed.py +++ b/erpnext/patches/v11_0/drop_column_max_days_allowed.py @@ -4,4 +4,4 @@ import frappe def execute(): if frappe.db.exists("DocType", "Leave Type"): if 'max_days_allowed' in frappe.db.get_table_columns("Leave Type"): - frappe.db.sql("alter table `tabLeave Type` drop column max_days_allowed") \ No newline at end of file + frappe.db.sql("alter table `tabLeave Type` drop column max_days_allowed") diff --git a/erpnext/patches/v11_0/ewaybill_fields_gst_india.py b/erpnext/patches/v11_0/ewaybill_fields_gst_india.py index 9925b70a963..4247c788e33 100644 --- a/erpnext/patches/v11_0/ewaybill_fields_gst_india.py +++ b/erpnext/patches/v11_0/ewaybill_fields_gst_india.py @@ -7,4 +7,4 @@ def execute(): if not company: return - make_custom_fields() \ No newline at end of file + make_custom_fields() diff --git a/erpnext/patches/v11_0/hr_ux_cleanups.py b/erpnext/patches/v11_0/hr_ux_cleanups.py index 80476c8a74c..8d187965011 100644 --- a/erpnext/patches/v11_0/hr_ux_cleanups.py +++ b/erpnext/patches/v11_0/hr_ux_cleanups.py @@ -10,4 +10,3 @@ def execute(): for holiday_list in frappe.get_all('Holiday List'): holiday_list = frappe.get_doc('Holiday List', holiday_list.name) holiday_list.db_set('total_holidays', len(holiday_list.holidays), update_modified = False) - diff --git a/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py b/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py index ee709ac2d49..dfcf5ab2886 100644 --- a/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py +++ b/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py @@ -42,4 +42,4 @@ def execute(): 'frequency_of_depreciation': asset_category_doc.frequency_of_depreciation }) - row.db_update() \ No newline at end of file + row.db_update() diff --git a/erpnext/patches/v11_0/make_location_from_warehouse.py b/erpnext/patches/v11_0/make_location_from_warehouse.py index a307e8c3659..8c92b5180d9 100644 --- a/erpnext/patches/v11_0/make_location_from_warehouse.py +++ b/erpnext/patches/v11_0/make_location_from_warehouse.py @@ -28,4 +28,3 @@ def execute(): def get_parent_warehouse_name(warehouse): return frappe.db.get_value('Warehouse', warehouse, 'warehouse_name') - \ No newline at end of file diff --git a/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py b/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py index c7c76355400..6da70b4ce38 100644 --- a/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py +++ b/erpnext/patches/v11_0/move_item_defaults_to_child_table_for_multicompany.py @@ -93,4 +93,4 @@ def execute(): `expense_account`, `income_account`, `buying_cost_center`, `selling_cost_center` ) VALUES {} - '''.format(', '.join(['%s'] * len(to_insert_data))), tuple(to_insert_data)) \ No newline at end of file + '''.format(', '.join(['%s'] * len(to_insert_data))), tuple(to_insert_data)) diff --git a/erpnext/patches/v11_0/move_leave_approvers_from_employee.py b/erpnext/patches/v11_0/move_leave_approvers_from_employee.py index edab34cc58a..ef703d0ea71 100644 --- a/erpnext/patches/v11_0/move_leave_approvers_from_employee.py +++ b/erpnext/patches/v11_0/move_leave_approvers_from_employee.py @@ -31,4 +31,4 @@ def execute(): if not len(department.leave_approvers): department.append("leave_approvers",{ "approver": record.leave_approver - }).db_insert() \ No newline at end of file + }).db_insert() diff --git a/erpnext/patches/v11_0/refactor_autoname_naming.py b/erpnext/patches/v11_0/refactor_autoname_naming.py index b997ba2db22..dd5cb639b1b 100644 --- a/erpnext/patches/v11_0/refactor_autoname_naming.py +++ b/erpnext/patches/v11_0/refactor_autoname_naming.py @@ -117,4 +117,4 @@ def get_series(): def get_series_to_preserve(doctype): series_to_preserve = frappe.db.get_value('DocType', doctype, 'autoname') - return series_to_preserve \ No newline at end of file + return series_to_preserve diff --git a/erpnext/patches/v11_0/refactor_naming_series.py b/erpnext/patches/v11_0/refactor_naming_series.py index b85ab66f148..9f231edea73 100644 --- a/erpnext/patches/v11_0/refactor_naming_series.py +++ b/erpnext/patches/v11_0/refactor_naming_series.py @@ -132,4 +132,4 @@ def get_series_to_preserve(doctype): def get_default_series(doctype): field = frappe.get_meta(doctype).get_field("naming_series") default_series = field.get('default', '') if field else '' - return default_series \ No newline at end of file + return default_series diff --git a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py index fad0cf7a45e..923b23048df 100644 --- a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py +++ b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py @@ -8,4 +8,4 @@ import frappe def execute(): if frappe.db.table_exists("Asset Adjustment") and not frappe.db.table_exists("Asset Value Adjustment"): frappe.rename_doc('DocType', 'Asset Adjustment', 'Asset Value Adjustment', force=True) - frappe.reload_doc('assets', 'doctype', 'asset_value_adjustment') \ No newline at end of file + frappe.reload_doc('assets', 'doctype', 'asset_value_adjustment') diff --git a/erpnext/patches/v11_0/rename_bom_wo_fields.py b/erpnext/patches/v11_0/rename_bom_wo_fields.py index 882ec84e644..0e6036b0740 100644 --- a/erpnext/patches/v11_0/rename_bom_wo_fields.py +++ b/erpnext/patches/v11_0/rename_bom_wo_fields.py @@ -30,4 +30,4 @@ def execute(): else: frappe.db.sql(""" UPDATE `tab%s` SET transfer_material_against = 'Work Order' - WHERE docstatus < 2""" % (doctype)) \ No newline at end of file + WHERE docstatus < 2""" % (doctype)) diff --git a/erpnext/patches/v11_0/rename_health_insurance.py b/erpnext/patches/v11_0/rename_health_insurance.py index e605071a297..06fc6151675 100644 --- a/erpnext/patches/v11_0/rename_health_insurance.py +++ b/erpnext/patches/v11_0/rename_health_insurance.py @@ -6,4 +6,4 @@ import frappe def execute(): frappe.rename_doc('DocType', 'Health Insurance', 'Employee Health Insurance', force=True) - frappe.reload_doc('hr', 'doctype', 'employee_health_insurance') \ No newline at end of file + frappe.reload_doc('hr', 'doctype', 'employee_health_insurance') diff --git a/erpnext/patches/v11_0/rename_overproduction_percent_field.py b/erpnext/patches/v11_0/rename_overproduction_percent_field.py index 077829f4818..fbf925d955c 100644 --- a/erpnext/patches/v11_0/rename_overproduction_percent_field.py +++ b/erpnext/patches/v11_0/rename_overproduction_percent_field.py @@ -7,4 +7,4 @@ import frappe def execute(): frappe.reload_doc('manufacturing', 'doctype', 'manufacturing_settings') - rename_field('Manufacturing Settings', 'over_production_allowance_percentage', 'overproduction_percentage_for_sales_order') \ No newline at end of file + rename_field('Manufacturing Settings', 'over_production_allowance_percentage', 'overproduction_percentage_for_sales_order') diff --git a/erpnext/patches/v11_0/renamed_from_to_fields_in_project.py b/erpnext/patches/v11_0/renamed_from_to_fields_in_project.py index 4f684400025..d5ca4cc5749 100644 --- a/erpnext/patches/v11_0/renamed_from_to_fields_in_project.py +++ b/erpnext/patches/v11_0/renamed_from_to_fields_in_project.py @@ -10,4 +10,4 @@ def execute(): if frappe.db.has_column('Project', 'from'): rename_field('Project', 'from', 'from_time') - rename_field('Project', 'to', 'to_time') \ No newline at end of file + rename_field('Project', 'to', 'to_time') diff --git a/erpnext/patches/v11_0/set_missing_gst_hsn_code.py b/erpnext/patches/v11_0/set_missing_gst_hsn_code.py index 4353ef80e24..8f8a545c410 100644 --- a/erpnext/patches/v11_0/set_missing_gst_hsn_code.py +++ b/erpnext/patches/v11_0/set_missing_gst_hsn_code.py @@ -41,4 +41,4 @@ def execute(): for t in list(parent): trans_doc = frappe.get_doc(dt, t) hsnwise_tax = get_itemised_tax_breakup_html(trans_doc) - frappe.db.set_value(dt, t, "other_charges_calculation", hsnwise_tax, update_modified=False) \ No newline at end of file + frappe.db.set_value(dt, t, "other_charges_calculation", hsnwise_tax, update_modified=False) diff --git a/erpnext/patches/v11_0/set_salary_component_properties.py b/erpnext/patches/v11_0/set_salary_component_properties.py index 2498888273d..d8ce31f3076 100644 --- a/erpnext/patches/v11_0/set_salary_component_properties.py +++ b/erpnext/patches/v11_0/set_salary_component_properties.py @@ -13,4 +13,4 @@ def execute(): frappe.db.sql("""update `tabSalary Detail` set is_tax_applicable=1 where parentfield='earnings' and statistical_component=0""") frappe.db.sql("""update `tabSalary Detail` set variable_based_on_taxable_salary=1 - where parentfield='deductions' and salary_component in ('TDS', 'Tax Deducted at Source')""") \ No newline at end of file + where parentfield='deductions' and salary_component in ('TDS', 'Tax Deducted at Source')""") diff --git a/erpnext/patches/v11_0/set_user_permissions_for_department.py b/erpnext/patches/v11_0/set_user_permissions_for_department.py index 7bd8577f9c5..2f90f14db3e 100644 --- a/erpnext/patches/v11_0/set_user_permissions_for_department.py +++ b/erpnext/patches/v11_0/set_user_permissions_for_department.py @@ -6,7 +6,7 @@ def execute(): where allow='Department'""", as_dict=1) for d in user_permissions: user_permission = frappe.get_doc("User Permission", d.name) - for new_dept in frappe.db.sql("""select name from tabDepartment + for new_dept in frappe.db.sql("""select name from tabDepartment where ifnull(company, '') != '' and department_name=%s""", d.for_value): try: new_user_permission = frappe.copy_doc(user_permission) @@ -16,4 +16,4 @@ def execute(): pass frappe.reload_doc("hr", "doctype", "department") - frappe.db.sql("update tabDepartment set disabled=1 where ifnull(company, '') = ''") \ No newline at end of file + frappe.db.sql("update tabDepartment set disabled=1 where ifnull(company, '') = ''") diff --git a/erpnext/patches/v11_0/skip_user_permission_check_for_department.py b/erpnext/patches/v11_0/skip_user_permission_check_for_department.py index 0f7fad7e497..4e72917547b 100644 --- a/erpnext/patches/v11_0/skip_user_permission_check_for_department.py +++ b/erpnext/patches/v11_0/skip_user_permission_check_for_department.py @@ -58,4 +58,4 @@ def execute(): if user_permissions_to_delete: frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `name` IN ({})'.format( # nosec ','.join(['%s'] * len(user_permissions_to_delete)) - ), tuple(user_permissions_to_delete)) \ No newline at end of file + ), tuple(user_permissions_to_delete)) diff --git a/erpnext/patches/v11_0/update_account_type_in_party_type.py b/erpnext/patches/v11_0/update_account_type_in_party_type.py index efa04fd2cec..dabaeffc94a 100644 --- a/erpnext/patches/v11_0/update_account_type_in_party_type.py +++ b/erpnext/patches/v11_0/update_account_type_in_party_type.py @@ -10,4 +10,4 @@ def execute(): 'Employee': 'Payable', 'Member': 'Receivable', 'Shareholder': 'Payable', 'Student': 'Receivable'} for party_type, account_type in party_types.items(): - frappe.db.set_value('Party Type', party_type, 'account_type', account_type) \ No newline at end of file + frappe.db.set_value('Party Type', party_type, 'account_type', account_type) diff --git a/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py b/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py index 1b58c97ea4d..799e91a3e22 100644 --- a/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py +++ b/erpnext/patches/v11_0/update_allow_transfer_for_manufacture.py @@ -17,4 +17,4 @@ def execute(): child.include_item_in_manufacturing = 1 where child.item_code = item.name and ifnull(item.is_stock_item, 0) = 1 - """.format(doctype)) \ No newline at end of file + """.format(doctype)) diff --git a/erpnext/patches/v11_0/update_backflush_subcontract_rm_based_on_bom.py b/erpnext/patches/v11_0/update_backflush_subcontract_rm_based_on_bom.py index f2eeadac600..37a616c7021 100644 --- a/erpnext/patches/v11_0/update_backflush_subcontract_rm_based_on_bom.py +++ b/erpnext/patches/v11_0/update_backflush_subcontract_rm_based_on_bom.py @@ -16,4 +16,4 @@ def execute(): where se.purpose = 'Send to Subcontractor' and sed.parent = se.name and pois.rm_item_code = sed.item_code and se.docstatus = 1 - and pois.parenttype = 'Purchase Order'""") \ No newline at end of file + and pois.parenttype = 'Purchase Order'""") diff --git a/erpnext/patches/v11_0/update_brand_in_item_price.py b/erpnext/patches/v11_0/update_brand_in_item_price.py index a8d3fab4812..977d84fefe8 100644 --- a/erpnext/patches/v11_0/update_brand_in_item_price.py +++ b/erpnext/patches/v11_0/update_brand_in_item_price.py @@ -12,4 +12,4 @@ def execute(): `tabItem Price`.brand = `tabItem`.brand where `tabItem Price`.item_code = `tabItem`.name - and `tabItem`.brand is not null and `tabItem`.brand != ''""") \ No newline at end of file + and `tabItem`.brand is not null and `tabItem`.brand != ''""") diff --git a/erpnext/patches/v11_0/update_department_lft_rgt.py b/erpnext/patches/v11_0/update_department_lft_rgt.py index b2f407b18ef..2b382037109 100644 --- a/erpnext/patches/v11_0/update_department_lft_rgt.py +++ b/erpnext/patches/v11_0/update_department_lft_rgt.py @@ -17,4 +17,4 @@ def execute(): frappe.db.sql("""update `tabDepartment` set parent_department = '{0}' where is_group = 0""".format(_('All Departments'))) - rebuild_tree("Department", "parent_department") \ No newline at end of file + rebuild_tree("Department", "parent_department") diff --git a/erpnext/patches/v11_1/delete_bom_browser.py b/erpnext/patches/v11_1/delete_bom_browser.py index 457f5116670..2892674d374 100644 --- a/erpnext/patches/v11_1/delete_bom_browser.py +++ b/erpnext/patches/v11_1/delete_bom_browser.py @@ -5,4 +5,4 @@ from __future__ import unicode_literals import frappe def execute(): - frappe.delete_doc_if_exists('Page', 'bom-browser') \ No newline at end of file + frappe.delete_doc_if_exists('Page', 'bom-browser') diff --git a/erpnext/patches/v11_1/make_job_card_time_logs.py b/erpnext/patches/v11_1/make_job_card_time_logs.py index 6e708df48d8..b706e5c1ffb 100644 --- a/erpnext/patches/v11_1/make_job_card_time_logs.py +++ b/erpnext/patches/v11_1/make_job_card_time_logs.py @@ -26,4 +26,4 @@ def execute(): frappe.reload_doc('manufacturing', 'doctype', 'job_card') frappe.db.sql(""" update `tabJob Card` set total_completed_qty = for_quantity, - total_time_in_mins = time_in_mins where docstatus < 2 """) \ No newline at end of file + total_time_in_mins = time_in_mins where docstatus < 2 """) 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 5b1251c31cf..fc3ec74083a 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 @@ -11,4 +11,4 @@ def execute(): frappe.reload_doctype("Opportunity") 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 + frappe.db.sql(""" UPDATE `tabOpportunity` set party_name = customer WHERE opportunity_from = 'Customer' """) diff --git a/erpnext/patches/v11_1/rename_depends_on_lwp.py b/erpnext/patches/v11_1/rename_depends_on_lwp.py index a0f2536f7d8..4c4b14fd4e7 100644 --- a/erpnext/patches/v11_1/rename_depends_on_lwp.py +++ b/erpnext/patches/v11_1/rename_depends_on_lwp.py @@ -10,4 +10,4 @@ def execute(): for doctype in ("Salary Component", "Salary Detail"): if "depends_on_lwp" in frappe.db.get_table_columns(doctype): frappe.reload_doc("Payroll", "doctype", scrub(doctype)) - rename_field(doctype, "depends_on_lwp", "depends_on_payment_days") \ No newline at end of file + rename_field(doctype, "depends_on_lwp", "depends_on_payment_days") diff --git a/erpnext/patches/v11_1/renamed_delayed_item_report.py b/erpnext/patches/v11_1/renamed_delayed_item_report.py index 222b9a0b170..8e8725c8af6 100644 --- a/erpnext/patches/v11_1/renamed_delayed_item_report.py +++ b/erpnext/patches/v11_1/renamed_delayed_item_report.py @@ -7,4 +7,4 @@ import frappe def execute(): for report in ["Delayed Order Item Summary", "Delayed Order Summary"]: if frappe.db.exists("Report", report): - frappe.delete_doc("Report", report) \ No newline at end of file + frappe.delete_doc("Report", report) diff --git a/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py b/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py index d41cff523d5..ec01fbb642e 100644 --- a/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py +++ b/erpnext/patches/v11_1/set_status_for_material_request_type_manufacture.py @@ -6,4 +6,4 @@ def execute(): update `tabMaterial Request` set status='Manufactured' where docstatus=1 and material_request_type='Manufacture' and per_ordered=100 and status != 'Stopped' - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v11_1/set_variant_based_on.py b/erpnext/patches/v11_1/set_variant_based_on.py index 019eefd68f4..49a9a177246 100644 --- a/erpnext/patches/v11_1/set_variant_based_on.py +++ b/erpnext/patches/v11_1/set_variant_based_on.py @@ -8,4 +8,4 @@ def execute(): frappe.db.sql("""update tabItem set variant_based_on = 'Item Attribute' where ifnull(variant_based_on, '') = '' and (has_variants=1 or ifnull(variant_of, '') != '') - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v11_1/update_bank_transaction_status.py b/erpnext/patches/v11_1/update_bank_transaction_status.py index 544bc5e6911..354e636c9b0 100644 --- a/erpnext/patches/v11_1/update_bank_transaction_status.py +++ b/erpnext/patches/v11_1/update_bank_transaction_status.py @@ -23,4 +23,4 @@ def execute(): WHERE status = 'Settled' and (deposit = allocated_amount or withdrawal = allocated_amount) and ifnull(allocated_amount, 0) > 0 - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py index 347dec1f74d..8c360ad9353 100644 --- a/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py +++ b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py @@ -22,4 +22,4 @@ def execute(): SET `tabItem Default`.default_supplier = `tabItem`.default_supplier WHERE `tabItem Default`.parent = `tabItem`.name and `tabItem Default`.default_supplier is null - and `tabItem`.default_supplier is not null and `tabItem`.default_supplier != '' """) \ No newline at end of file + and `tabItem`.default_supplier is not null and `tabItem`.default_supplier != '' """) diff --git a/erpnext/patches/v11_1/woocommerce_set_creation_user.py b/erpnext/patches/v11_1/woocommerce_set_creation_user.py index 5ccdec6d262..074b904002c 100644 --- a/erpnext/patches/v11_1/woocommerce_set_creation_user.py +++ b/erpnext/patches/v11_1/woocommerce_set_creation_user.py @@ -8,4 +8,4 @@ def execute(): if cint(doc.enable_sync): doc.creation_user = doc.modified_by - doc.save(ignore_permissions=True) \ No newline at end of file + doc.save(ignore_permissions=True) diff --git a/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py b/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py index b6bd5fa311c..c2ed6c288fe 100644 --- a/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py +++ b/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py @@ -13,4 +13,4 @@ def execute(): where a.gstin = %s and dl.parent = a.name and dl.link_doctype = 'Company' """, (creds.get('gstin'))) if company_name and len(company_name) > 0: - frappe.db.set_value('E Invoice User', creds.get('name'), 'company', company_name[0][0]) \ No newline at end of file + frappe.db.set_value('E Invoice User', creds.get('name'), 'company', company_name[0][0]) diff --git a/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py b/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py index 484f81a7aca..855d21dd992 100644 --- a/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py +++ b/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py @@ -14,6 +14,6 @@ def execute(): for company in frappe.get_all("Company", ["name", "default_selling_terms", "default_buying_terms"]): if company.default_selling_terms and not company.default_buying_terms: frappe.db.set_value("Company", company.name, "default_buying_terms", company.default_selling_terms) - + frappe.reload_doc("setup", "doctype", "terms_and_conditions") frappe.db.sql("update `tabTerms and Conditions` set selling=1, buying=1, hr=1") diff --git a/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py b/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py index 4d649dd0f0c..6fe578dbd95 100644 --- a/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py +++ b/erpnext/patches/v12_0/add_document_type_field_for_italy_einvoicing.py @@ -15,4 +15,4 @@ def execute(): ] } - create_custom_fields(custom_fields, update=True) \ No newline at end of file + create_custom_fields(custom_fields, update=True) diff --git a/erpnext/patches/v12_0/add_einvoice_status_field.py b/erpnext/patches/v12_0/add_einvoice_status_field.py index 387e88588d9..2dfd30714c8 100644 --- a/erpnext/patches/v12_0/add_einvoice_status_field.py +++ b/erpnext/patches/v12_0/add_einvoice_status_field.py @@ -13,13 +13,13 @@ def execute(): 'Sales Invoice': [ dict(fieldname='einvoice_section', label='E-Invoice Fields', fieldtype='Section Break', insert_after='gst_vehicle_type', print_hide=1, hidden=1), - + dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='einvoice_section', no_copy=1, print_hide=1), - + dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1), - dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date', + dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date', no_copy=1, print_hide=1), dict(fieldname='signed_einvoice', label='Signed E-Invoice', fieldtype='Code', options='JSON', hidden=1, insert_after='irn_cancel_date', @@ -66,4 +66,4 @@ def execute(): if signed_einvoice: signed_einvoice = json.loads(signed_einvoice) frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_no', signed_einvoice.get('AckNo'), update_modified=False) - frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_date', signed_einvoice.get('AckDt'), update_modified=False) \ No newline at end of file + frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_date', signed_einvoice.get('AckDt'), update_modified=False) diff --git a/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py b/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py index bf8f566d32a..c1c11e26006 100644 --- a/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py +++ b/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py @@ -15,4 +15,4 @@ def execute(): dict(role='Accounts User'), dict(role='Accounts Manager') ] - )).insert() \ No newline at end of file + )).insert() diff --git a/erpnext/patches/v12_0/add_eway_bill_in_delivery_note.py b/erpnext/patches/v12_0/add_eway_bill_in_delivery_note.py index bb4b0380f88..cf1ed3676bf 100644 --- a/erpnext/patches/v12_0/add_eway_bill_in_delivery_note.py +++ b/erpnext/patches/v12_0/add_eway_bill_in_delivery_note.py @@ -16,4 +16,4 @@ def execute(): 'insert_after': 'customer_name_in_arabic', 'translatable': 0, 'owner': 'Administrator' - }) \ No newline at end of file + }) diff --git a/erpnext/patches/v12_0/add_ewaybill_validity_field.py b/erpnext/patches/v12_0/add_ewaybill_validity_field.py index 87d98f1a563..f29b71437e8 100644 --- a/erpnext/patches/v12_0/add_ewaybill_validity_field.py +++ b/erpnext/patches/v12_0/add_ewaybill_validity_field.py @@ -13,4 +13,4 @@ def execute(): depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill') ] } - create_custom_fields(custom_fields, update=True) \ No newline at end of file + create_custom_fields(custom_fields, update=True) diff --git a/erpnext/patches/v12_0/add_export_type_field_in_party_master.py b/erpnext/patches/v12_0/add_export_type_field_in_party_master.py index 5bb6e3fb339..a0b1f87d61b 100644 --- a/erpnext/patches/v12_0/add_export_type_field_in_party_master.py +++ b/erpnext/patches/v12_0/add_export_type_field_in_party_master.py @@ -38,5 +38,3 @@ def execute(): WHERE fieldname = 'is_inter_state' AND dt IN ('Sales Taxes and Charges Template', 'Purchase Taxes and Charges Template') """) - - diff --git a/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py b/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py index 1208222504c..c90819238c8 100644 --- a/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py +++ b/erpnext/patches/v12_0/add_gst_category_in_delivery_note.py @@ -16,4 +16,4 @@ def execute(): ] } - create_custom_fields(custom_fields, update=True) \ No newline at end of file + create_custom_fields(custom_fields, update=True) diff --git a/erpnext/patches/v12_0/add_item_name_in_work_orders.py b/erpnext/patches/v12_0/add_item_name_in_work_orders.py index 485dd314a16..d765b93d218 100644 --- a/erpnext/patches/v12_0/add_item_name_in_work_orders.py +++ b/erpnext/patches/v12_0/add_item_name_in_work_orders.py @@ -11,4 +11,4 @@ def execute(): SET wo.item_name = item.item_name """) - frappe.db.commit() \ No newline at end of file + frappe.db.commit() diff --git a/erpnext/patches/v12_0/add_permission_in_lower_deduction.py b/erpnext/patches/v12_0/add_permission_in_lower_deduction.py index af9bf74f30e..2e42368b152 100644 --- a/erpnext/patches/v12_0/add_permission_in_lower_deduction.py +++ b/erpnext/patches/v12_0/add_permission_in_lower_deduction.py @@ -10,4 +10,4 @@ def execute(): add_permission('Lower Deduction Certificate', 'Accounts Manager', 0) update_permission_property('Lower Deduction Certificate', 'Accounts Manager', 0, 'write', 1) - update_permission_property('Lower Deduction Certificate', 'Accounts Manager', 0, 'create', 1) \ No newline at end of file + update_permission_property('Lower Deduction Certificate', 'Accounts Manager', 0, 'create', 1) diff --git a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py index 657decfed23..f171542df16 100644 --- a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py +++ b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py @@ -39,4 +39,4 @@ def execute(): create_custom_field(doctype, df) frappe.clear_cache(doctype=doctype) - count += 1 \ No newline at end of file + count += 1 diff --git a/erpnext/patches/v12_0/create_default_energy_point_rules.py b/erpnext/patches/v12_0/create_default_energy_point_rules.py index 88233b4cf7f..93d2576bb6d 100644 --- a/erpnext/patches/v12_0/create_default_energy_point_rules.py +++ b/erpnext/patches/v12_0/create_default_energy_point_rules.py @@ -3,4 +3,4 @@ from erpnext.setup.install import create_default_energy_point_rules def execute(): frappe.reload_doc('social', 'doctype', 'energy_point_rule') - create_default_energy_point_rules() \ No newline at end of file + create_default_energy_point_rules() diff --git a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py index 7feaffdf408..23a8f24d780 100644 --- a/erpnext/patches/v12_0/create_irs_1099_field_united_states.py +++ b/erpnext/patches/v12_0/create_irs_1099_field_united_states.py @@ -13,4 +13,4 @@ def execute(): if not company: return - make_custom_fields() \ No newline at end of file + make_custom_fields() diff --git a/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py b/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py index 0078a53cd69..a6230f42771 100644 --- a/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py +++ b/erpnext/patches/v12_0/create_itc_reversal_custom_fields.py @@ -112,4 +112,4 @@ def execute(): 'itc_central_tax': values.get('itc_central_tax'), 'itc_state_tax': values['itc_state_tax'], 'itc_cess_amount': values['itc_cess_amount'], - }) \ No newline at end of file + }) diff --git a/erpnext/patches/v12_0/create_taxable_value_field.py b/erpnext/patches/v12_0/create_taxable_value_field.py index a0c9fcf4cbe..b9ee81df50e 100644 --- a/erpnext/patches/v12_0/create_taxable_value_field.py +++ b/erpnext/patches/v12_0/create_taxable_value_field.py @@ -15,4 +15,4 @@ def execute(): ] } - create_custom_fields(custom_fields, update=True) \ No newline at end of file + create_custom_fields(custom_fields, update=True) diff --git a/erpnext/patches/v12_0/delete_priority_property_setter.py b/erpnext/patches/v12_0/delete_priority_property_setter.py index 59272675430..163855729df 100644 --- a/erpnext/patches/v12_0/delete_priority_property_setter.py +++ b/erpnext/patches/v12_0/delete_priority_property_setter.py @@ -6,4 +6,4 @@ def execute(): WHERE `tabProperty Setter`.doc_type='Issue' AND `tabProperty Setter`.field_name='priority' AND `tabProperty Setter`.property='options' - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v12_0/fix_quotation_expired_status.py b/erpnext/patches/v12_0/fix_quotation_expired_status.py index c8708d80134..ac7e82d2d0d 100644 --- a/erpnext/patches/v12_0/fix_quotation_expired_status.py +++ b/erpnext/patches/v12_0/fix_quotation_expired_status.py @@ -6,23 +6,23 @@ def execute(): # filter out submitted expired quotations which has sales order created cond = "qo.docstatus = 1 and qo.status = 'Expired'" invalid_so_against_quo = """ - SELECT + SELECT so.name FROM `tabSales Order` so, `tabSales Order Item` so_item - WHERE + WHERE so_item.docstatus = 1 and so.docstatus = 1 and so_item.parent = so.name and so_item.prevdoc_docname = qo.name and qo.valid_till < so.transaction_date""" # check if SO was created after quotation expired - + frappe.db.sql( """UPDATE `tabQuotation` qo SET qo.status = 'Expired' WHERE {cond} and exists({invalid_so_against_quo})""" .format(cond=cond, invalid_so_against_quo=invalid_so_against_quo) ) - + valid_so_against_quo = """ - SELECT + SELECT so.name FROM `tabSales Order` so, `tabSales Order Item` so_item - WHERE + WHERE so_item.docstatus = 1 and so.docstatus = 1 and so_item.parent = so.name and so_item.prevdoc_docname = qo.name diff --git a/erpnext/patches/v12_0/move_target_distribution_from_parent_to_child.py b/erpnext/patches/v12_0/move_target_distribution_from_parent_to_child.py index 548c1a47175..97badf355d9 100644 --- a/erpnext/patches/v12_0/move_target_distribution_from_parent_to_child.py +++ b/erpnext/patches/v12_0/move_target_distribution_from_parent_to_child.py @@ -19,4 +19,4 @@ def execute(): frappe.delete_doc("Report", "Sales Partner-wise Transaction Summary") frappe.delete_doc("Report", "Sales Person Target Variance Item Group-Wise") - frappe.delete_doc("Report", "Territory Target Variance Item Group-Wise") \ No newline at end of file + frappe.delete_doc("Report", "Territory Target Variance Item Group-Wise") diff --git a/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py index 8267df95e11..46794bebe70 100644 --- a/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py +++ b/erpnext/patches/v12_0/recalculate_requested_qty_in_bin.py @@ -10,4 +10,4 @@ def execute(): for entry in bin_details: update_bin_qty(entry.get("item_code"), entry.get("warehouse"), { "indented_qty": get_indented_qty(entry.get("item_code"), entry.get("warehouse")) - }) \ No newline at end of file + }) diff --git a/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py index d1446b3227d..be884f94d15 100644 --- a/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py +++ b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py @@ -11,4 +11,4 @@ def execute(): if frappe.db.exists("Custom Field", "Company-bank_remittance_section"): deprecated_fields = ['bank_remittance_section', 'client_code', 'remittance_column_break', 'product_code'] for i in range(len(deprecated_fields)): - frappe.delete_doc("Custom Field", 'Company-'+deprecated_fields[i]) \ No newline at end of file + frappe.delete_doc("Custom Field", 'Company-'+deprecated_fields[i]) diff --git a/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py b/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py index 7859606e5cb..4fcffb702a4 100644 --- a/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py +++ b/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py @@ -25,4 +25,4 @@ def delete_denied_leaves_from_leave_ledger_entry(leave_application_list): WHERE transaction_type = 'Leave Application' AND transaction_name in (%s) ''' % (', '.join(['%s'] * len(leave_application_list))), #nosec - tuple(leave_application_list)) \ No newline at end of file + tuple(leave_application_list)) diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py index 24286dcebf9..6b1b601db19 100644 --- a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py +++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py @@ -43,4 +43,4 @@ def delete_duplicate_ledger_entries(duplicate_records_list): AND is_carry_forward = %s AND from_date = %s AND to_date = %s - ''', tuple(d)) \ No newline at end of file + ''', tuple(d)) diff --git a/erpnext/patches/v12_0/rename_account_type_doctype.py b/erpnext/patches/v12_0/rename_account_type_doctype.py index ffb4e937b18..9a08ad45213 100644 --- a/erpnext/patches/v12_0/rename_account_type_doctype.py +++ b/erpnext/patches/v12_0/rename_account_type_doctype.py @@ -4,4 +4,4 @@ import frappe def execute(): frappe.rename_doc('DocType', 'Account Type', 'Bank Account Type', force=True) frappe.rename_doc('DocType', 'Account Subtype', 'Bank Account Subtype', force=True) - frappe.reload_doc('accounts', 'doctype', 'bank_account') \ No newline at end of file + frappe.reload_doc('accounts', 'doctype', 'bank_account') diff --git a/erpnext/patches/v12_0/rename_bank_account_field_in_journal_entry_account.py b/erpnext/patches/v12_0/rename_bank_account_field_in_journal_entry_account.py index 4230cb88f4d..7489ea30a09 100644 --- a/erpnext/patches/v12_0/rename_bank_account_field_in_journal_entry_account.py +++ b/erpnext/patches/v12_0/rename_bank_account_field_in_journal_entry_account.py @@ -14,4 +14,4 @@ def execute(): def update_journal_entry_account_fieldname(): ''' maps data from old field to the new field ''' if frappe.db.has_column('Journal Entry Account', 'bank_account_no'): - rename_field("Journal Entry Account", "bank_account_no", "bank_account") \ No newline at end of file + rename_field("Journal Entry Account", "bank_account_no", "bank_account") diff --git a/erpnext/patches/v12_0/rename_lost_reason_detail.py b/erpnext/patches/v12_0/rename_lost_reason_detail.py index d0dc356bd0e..c71b91c9256 100644 --- a/erpnext/patches/v12_0/rename_lost_reason_detail.py +++ b/erpnext/patches/v12_0/rename_lost_reason_detail.py @@ -15,4 +15,4 @@ def execute(): SELECT o.`name`, o.`creation`, o.`modified`, o.`modified_by`, o.`owner`, o.`docstatus`, o.`parent`, o.`parentfield`, o.`parenttype`, o.`idx`, o.`_comments`, o.`_assign`, o.`_user_tags`, o.`_liked_by`, o.`lost_reason` FROM `tabOpportunity Lost Reason` o LEFT JOIN `tabQuotation Lost Reason` q ON q.name = o.name WHERE q.name IS NULL""") - frappe.delete_doc("DocType", "Lost Reason Detail") \ No newline at end of file + frappe.delete_doc("DocType", "Lost Reason Detail") diff --git a/erpnext/patches/v12_0/rename_pos_closing_doctype.py b/erpnext/patches/v12_0/rename_pos_closing_doctype.py index 0577f81234c..9d8626b8527 100644 --- a/erpnext/patches/v12_0/rename_pos_closing_doctype.py +++ b/erpnext/patches/v12_0/rename_pos_closing_doctype.py @@ -7,10 +7,10 @@ def execute(): if frappe.db.table_exists("POS Closing Voucher"): if not frappe.db.exists("DocType", "POS Closing Entry"): frappe.rename_doc('DocType', 'POS Closing Voucher', 'POS Closing Entry', force=True) - + if not frappe.db.exists('DocType', 'POS Closing Entry Taxes'): frappe.rename_doc('DocType', 'POS Closing Voucher Taxes', 'POS Closing Entry Taxes', force=True) - + if not frappe.db.exists('DocType', 'POS Closing Voucher Details'): frappe.rename_doc('DocType', 'POS Closing Voucher Details', 'POS Closing Entry Detail', force=True) @@ -22,4 +22,4 @@ def execute(): frappe.delete_doc("DocType", "POS Closing Voucher") frappe.delete_doc("DocType", "POS Closing Voucher Taxes") frappe.delete_doc("DocType", "POS Closing Voucher Details") - frappe.delete_doc("DocType", "POS Closing Voucher Invoices") \ No newline at end of file + frappe.delete_doc("DocType", "POS Closing Voucher Invoices") diff --git a/erpnext/patches/v12_0/rename_tolerance_fields.py b/erpnext/patches/v12_0/rename_tolerance_fields.py index aa2fff4ca72..20b096331ed 100644 --- a/erpnext/patches/v12_0/rename_tolerance_fields.py +++ b/erpnext/patches/v12_0/rename_tolerance_fields.py @@ -12,4 +12,4 @@ def execute(): qty_allowance = frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance") frappe.db.set_value("Accounts Settings", None, "over_delivery_receipt_allowance", qty_allowance) - frappe.db.sql("update tabItem set over_billing_allowance=over_delivery_receipt_allowance") \ No newline at end of file + frappe.db.sql("update tabItem set over_billing_allowance=over_delivery_receipt_allowance") diff --git a/erpnext/patches/v12_0/replace_accounting_with_accounts_in_home_settings.py b/erpnext/patches/v12_0/replace_accounting_with_accounts_in_home_settings.py index 09fc4c1b04e..f88a22f6c9d 100644 --- a/erpnext/patches/v12_0/replace_accounting_with_accounts_in_home_settings.py +++ b/erpnext/patches/v12_0/replace_accounting_with_accounts_in_home_settings.py @@ -2,4 +2,4 @@ import frappe def execute(): frappe.db.sql("""UPDATE `tabUser` SET `home_settings` = REPLACE(`home_settings`, 'Accounting', 'Accounts')""") - frappe.cache().delete_key('home_settings') \ No newline at end of file + frappe.cache().delete_key('home_settings') diff --git a/erpnext/patches/v12_0/repost_stock_ledger_entries_for_target_warehouse.py b/erpnext/patches/v12_0/repost_stock_ledger_entries_for_target_warehouse.py index 13e935b2d39..c52f380d8c2 100644 --- a/erpnext/patches/v12_0/repost_stock_ledger_entries_for_target_warehouse.py +++ b/erpnext/patches/v12_0/repost_stock_ledger_entries_for_target_warehouse.py @@ -66,6 +66,3 @@ def execute(): frappe.db.sql(""" UPDATE `tabPacked Item` set target_warehouse = null WHERE creation > '2020-04-16' and docstatus < 2 and parenttype = 'Sales Order' """) - - - diff --git a/erpnext/patches/v12_0/set_automatically_process_deferred_accounting_in_accounts_settings.py b/erpnext/patches/v12_0/set_automatically_process_deferred_accounting_in_accounts_settings.py index 5ee75be4990..b5d7e3dcb9e 100644 --- a/erpnext/patches/v12_0/set_automatically_process_deferred_accounting_in_accounts_settings.py +++ b/erpnext/patches/v12_0/set_automatically_process_deferred_accounting_in_accounts_settings.py @@ -4,4 +4,4 @@ import frappe def execute(): frappe.reload_doc("accounts", "doctype", "accounts_settings") - frappe.db.set_value("Accounts Settings", None, "automatically_process_deferred_accounting_entry", 1) \ No newline at end of file + frappe.db.set_value("Accounts Settings", None, "automatically_process_deferred_accounting_entry", 1) diff --git a/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py b/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py index 8ba0d79a831..4415cfeaba9 100644 --- a/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py +++ b/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py @@ -5,4 +5,4 @@ def execute(): UPDATE `tabExpense Claim Detail` child, `tabExpense Claim` par SET child.cost_center = par.cost_center WHERE child.parent = par.name - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py b/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py index 4d4fc7c4629..13110dfe03f 100644 --- a/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py +++ b/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py @@ -10,8 +10,8 @@ def execute(): if frappe.db.exists("DocType", "Asset Settings"): frappe.reload_doctype("Asset Category") cwip_value = frappe.db.get_single_value("Asset Settings", "disable_cwip_accounting") - + frappe.db.sql("""UPDATE `tabAsset Category` SET enable_cwip_accounting = %s""", cint(cwip_value)) frappe.db.sql("""DELETE FROM `tabSingles` where doctype = 'Asset Settings'""") - frappe.delete_doc_if_exists("DocType", "Asset Settings") \ No newline at end of file + frappe.delete_doc_if_exists("DocType", "Asset Settings") diff --git a/erpnext/patches/v12_0/set_default_homepage_type.py b/erpnext/patches/v12_0/set_default_homepage_type.py index 241e4b9b5e1..a290e31cf24 100644 --- a/erpnext/patches/v12_0/set_default_homepage_type.py +++ b/erpnext/patches/v12_0/set_default_homepage_type.py @@ -1,4 +1,4 @@ import frappe def execute(): - frappe.db.set_value('Homepage', 'Homepage', 'hero_section_based_on', 'Default') \ No newline at end of file + frappe.db.set_value('Homepage', 'Homepage', 'hero_section_based_on', 'Default') diff --git a/erpnext/patches/v12_0/set_default_payroll_based_on.py b/erpnext/patches/v12_0/set_default_payroll_based_on.py index 04b54a6cf61..038bd6d21ae 100644 --- a/erpnext/patches/v12_0/set_default_payroll_based_on.py +++ b/erpnext/patches/v12_0/set_default_payroll_based_on.py @@ -3,4 +3,4 @@ import frappe def execute(): frappe.reload_doc("hr", "doctype", "hr_settings") - frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave") \ No newline at end of file + frappe.db.set_value("HR Settings", None, "payroll_based_on", "Leave") diff --git a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py b/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py index a996a69b3d9..a27c7b24a8c 100644 --- a/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py +++ b/erpnext/patches/v12_0/set_expense_account_in_landed_cost_voucher_taxes.py @@ -30,4 +30,4 @@ def execute(): s.docstatus = 1 AND s.company = %s AND t.parent = s.name - """, (account, company)) \ No newline at end of file + """, (account, company)) diff --git a/erpnext/patches/v12_0/set_gst_category.py b/erpnext/patches/v12_0/set_gst_category.py index 55bbdee7edf..cc093953bf4 100644 --- a/erpnext/patches/v12_0/set_gst_category.py +++ b/erpnext/patches/v12_0/set_gst_category.py @@ -48,5 +48,3 @@ def execute(): frappe.db.sql(""" UPDATE `tab{doctype}` t1, `tabAddress` t2, `tabDynamic Link` t3 SET t1.gst_category = "Overseas" where t3.link_name = t1.name and t3.parent = t2.name and t2.country != 'India' """.format(doctype=doctype)) #nosec - - diff --git a/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py index a6011c4dace..8fdc73b8ff1 100644 --- a/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py +++ b/erpnext/patches/v12_0/set_italian_import_supplier_invoice_permissions.py @@ -9,4 +9,4 @@ def execute(): countries = frappe.get_all("Company", fields="country") countries = [country["country"] for country in countries] if "Italy" in countries: - add_permissions() \ No newline at end of file + add_permissions() diff --git a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py index 70ca6b222e9..a5c8f7524a7 100644 --- a/erpnext/patches/v12_0/set_multi_uom_in_rfq.py +++ b/erpnext/patches/v12_0/set_multi_uom_in_rfq.py @@ -13,4 +13,4 @@ def execute(): SET stock_uom = uom, conversion_factor = 1, - stock_qty = qty""") \ No newline at end of file + stock_qty = qty""") diff --git a/erpnext/patches/v12_0/set_payment_entry_status.py b/erpnext/patches/v12_0/set_payment_entry_status.py index fafbec6a9a7..84645a38639 100644 --- a/erpnext/patches/v12_0/set_payment_entry_status.py +++ b/erpnext/patches/v12_0/set_payment_entry_status.py @@ -6,4 +6,4 @@ def execute(): WHEN docstatus = 1 THEN 'Submitted' WHEN docstatus = 2 THEN 'Cancelled' ELSE 'Draft' - END;""") \ No newline at end of file + END;""") diff --git a/erpnext/patches/v12_0/set_priority_for_support.py b/erpnext/patches/v12_0/set_priority_for_support.py index a5490ef20d5..66696bee541 100644 --- a/erpnext/patches/v12_0/set_priority_for_support.py +++ b/erpnext/patches/v12_0/set_priority_for_support.py @@ -81,4 +81,4 @@ def set_priorities_service_level_agreement(): doc.flags.ignore_validate = True doc.save(ignore_permissions=True) except frappe.db.TableMissingError: - frappe.reload_doc("support", "doctype", "service_level_agreement") \ No newline at end of file + frappe.reload_doc("support", "doctype", "service_level_agreement") diff --git a/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py b/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py index 07026732fd4..6c11cb415f9 100644 --- a/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py +++ b/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py @@ -11,4 +11,4 @@ def execute(): filters={'sales_order': ('!=', ''), 'sales_order_item': ('!=', '')}): # update produced qty in sales order - update_produced_qty_in_so_item(d.sales_order, d.sales_order_item) \ No newline at end of file + update_produced_qty_in_so_item(d.sales_order, d.sales_order_item) diff --git a/erpnext/patches/v12_0/set_production_capacity_in_workstation.py b/erpnext/patches/v12_0/set_production_capacity_in_workstation.py index bae1e28deb9..babaebeaefc 100644 --- a/erpnext/patches/v12_0/set_production_capacity_in_workstation.py +++ b/erpnext/patches/v12_0/set_production_capacity_in_workstation.py @@ -5,4 +5,4 @@ def execute(): frappe.reload_doc("manufacturing", "doctype", "workstation") frappe.db.sql(""" UPDATE `tabWorkstation` - SET production_capacity = 1 """) \ No newline at end of file + SET production_capacity = 1 """) diff --git a/erpnext/patches/v12_0/set_quotation_status.py b/erpnext/patches/v12_0/set_quotation_status.py index 64a9080a8fe..87643a23545 100644 --- a/erpnext/patches/v12_0/set_quotation_status.py +++ b/erpnext/patches/v12_0/set_quotation_status.py @@ -4,4 +4,4 @@ import frappe def execute(): frappe.db.sql(""" UPDATE `tabQuotation` set status = 'Open' - where docstatus = 1 and status = 'Submitted' """) \ No newline at end of file + where docstatus = 1 and status = 'Submitted' """) diff --git a/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py b/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py index 63ca540a8e2..1cc37caba42 100644 --- a/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py +++ b/erpnext/patches/v12_0/set_updated_purpose_in_pick_list.py @@ -8,4 +8,4 @@ import frappe def execute(): frappe.reload_doc("stock", "doctype", "pick_list") frappe.db.sql("""UPDATE `tabPick List` set purpose = 'Delivery' - WHERE docstatus = 1 and purpose = 'Delivery against Sales Order' """) \ No newline at end of file + WHERE docstatus = 1 and purpose = 'Delivery against Sales Order' """) diff --git a/erpnext/patches/v12_0/setup_einvoice_fields.py b/erpnext/patches/v12_0/setup_einvoice_fields.py index 2474bc3b82c..82b14fc9d60 100644 --- a/erpnext/patches/v12_0/setup_einvoice_fields.py +++ b/erpnext/patches/v12_0/setup_einvoice_fields.py @@ -14,9 +14,9 @@ def execute(): 'Sales Invoice': [ dict(fieldname='irn', label='IRN', fieldtype='Data', read_only=1, insert_after='customer', no_copy=1, print_hide=1, depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'), - + dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='irn', no_copy=1, print_hide=1), - + dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1), dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1, diff --git a/erpnext/patches/v12_0/stock_entry_enhancements.py b/erpnext/patches/v12_0/stock_entry_enhancements.py index 847d92894be..17fdcd9395a 100644 --- a/erpnext/patches/v12_0/stock_entry_enhancements.py +++ b/erpnext/patches/v12_0/stock_entry_enhancements.py @@ -49,4 +49,4 @@ def add_gst_hsn_code_field(): `tabStock Entry Detail`.gst_hsn_code = `tabItem`.gst_hsn_code Where `tabItem`.name = `tabStock Entry Detail`.item_code and `tabItem`.gst_hsn_code is not null - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v12_0/unhide_cost_center_field.py b/erpnext/patches/v12_0/unhide_cost_center_field.py index 6005ab70726..3474a34af4b 100644 --- a/erpnext/patches/v12_0/unhide_cost_center_field.py +++ b/erpnext/patches/v12_0/unhide_cost_center_field.py @@ -10,4 +10,4 @@ def execute(): WHERE doc_type in ('Sales Invoice', 'Purchase Invoice', 'Payment Entry') AND field_name = 'cost_center' AND property = 'hidden' - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v12_0/update_appointment_reminder_scheduler_entry.py b/erpnext/patches/v12_0/update_appointment_reminder_scheduler_entry.py index 91931eeb3bc..f4516649610 100644 --- a/erpnext/patches/v12_0/update_appointment_reminder_scheduler_entry.py +++ b/erpnext/patches/v12_0/update_appointment_reminder_scheduler_entry.py @@ -4,4 +4,4 @@ def execute(): job = frappe.db.exists('Scheduled Job Type', 'patient_appointment.send_appointment_reminder') if job: method = 'erpnext.healthcare.doctype.patient_appointment.patient_appointment.send_appointment_reminder' - frappe.db.set_value('Scheduled Job Type', job, 'method', method) \ No newline at end of file + frappe.db.set_value('Scheduled Job Type', job, 'method', method) diff --git a/erpnext/patches/v12_0/update_bom_in_so_mr.py b/erpnext/patches/v12_0/update_bom_in_so_mr.py index 309ae4c2ab7..8a871718133 100644 --- a/erpnext/patches/v12_0/update_bom_in_so_mr.py +++ b/erpnext/patches/v12_0/update_bom_in_so_mr.py @@ -16,4 +16,4 @@ def execute(): WHERE child_doc.item_code = item.name and child_doc.docstatus < 2 and item.default_bom is not null and item.default_bom != '' {cond} - """.format(doc = doctype, cond = condition)) \ No newline at end of file + """.format(doc = doctype, cond = condition)) diff --git a/erpnext/patches/v12_0/update_end_date_and_status_in_email_campaign.py b/erpnext/patches/v12_0/update_end_date_and_status_in_email_campaign.py index db71a735def..c45f6221f93 100644 --- a/erpnext/patches/v12_0/update_end_date_and_status_in_email_campaign.py +++ b/erpnext/patches/v12_0/update_end_date_and_status_in_email_campaign.py @@ -21,4 +21,4 @@ def execute(): elif end_date >= today_date: doc.db_set("status", "In Progress") elif end_date < today_date: - doc.db_set("status", "Completed") \ No newline at end of file + doc.db_set("status", "Completed") diff --git a/erpnext/patches/v12_0/update_ewaybill_field_position.py b/erpnext/patches/v12_0/update_ewaybill_field_position.py index c0230c43954..9e5f599d2c8 100644 --- a/erpnext/patches/v12_0/update_ewaybill_field_position.py +++ b/erpnext/patches/v12_0/update_ewaybill_field_position.py @@ -25,4 +25,4 @@ def execute(): 'translatable': 0 }) - ewaybill_field.save() \ No newline at end of file + ewaybill_field.save() diff --git a/erpnext/patches/v12_0/update_gst_category.py b/erpnext/patches/v12_0/update_gst_category.py index 963edad150e..1a54216b885 100644 --- a/erpnext/patches/v12_0/update_gst_category.py +++ b/erpnext/patches/v12_0/update_gst_category.py @@ -16,4 +16,4 @@ def execute(): frappe.db.sql(""" UPDATE `tabPurchase Invoice` set gst_category = 'Unregistered' where gst_category = 'Registered Regular' and ifnull(supplier_gstin, '')='' - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py index d06c5713d23..d0b04433979 100644 --- a/erpnext/patches/v12_0/update_healthcare_refactored_changes.py +++ b/erpnext/patches/v12_0/update_healthcare_refactored_changes.py @@ -134,4 +134,4 @@ def execute(): status = (CASE WHEN visited >= max_visits THEN 'Completed' ELSE 'Pending' END) - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v12_0/update_is_cancelled_field.py b/erpnext/patches/v12_0/update_is_cancelled_field.py index 0b2e82750b2..4bbec44aa42 100644 --- a/erpnext/patches/v12_0/update_is_cancelled_field.py +++ b/erpnext/patches/v12_0/update_is_cancelled_field.py @@ -12,4 +12,4 @@ def execute(): frappe.reload_doc("stock", "doctype", "stock_ledger_entry") frappe.reload_doc("stock", "doctype", "serial_no") except: - pass \ No newline at end of file + pass diff --git a/erpnext/patches/v12_0/update_item_tax_template_company.py b/erpnext/patches/v12_0/update_item_tax_template_company.py index f7496999b33..e15894df890 100644 --- a/erpnext/patches/v12_0/update_item_tax_template_company.py +++ b/erpnext/patches/v12_0/update_item_tax_template_company.py @@ -10,4 +10,4 @@ def execute(): for tax in doc.taxes: doc.company = frappe.get_value('Account', tax.tax_type, 'company') break - doc.save() \ No newline at end of file + doc.save() diff --git a/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py b/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py index e4dcecd9bdb..6ebaf48e0e8 100644 --- a/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py +++ b/erpnext/patches/v12_0/update_owner_fields_in_acc_dimension_custom_fields.py @@ -14,4 +14,4 @@ def execute(): SET owner = 'Administrator' WHERE fieldname = %s AND dt IN (%s)""" % #nosec - ('%s', ', '.join(['%s']* len(doclist))), tuple([dimension.fieldname] + doclist)) \ No newline at end of file + ('%s', ', '.join(['%s']* len(doclist))), tuple([dimension.fieldname] + doclist)) diff --git a/erpnext/patches/v12_0/update_price_list_currency_in_bom.py b/erpnext/patches/v12_0/update_price_list_currency_in_bom.py index f5e7b947c23..09f07074299 100644 --- a/erpnext/patches/v12_0/update_price_list_currency_in_bom.py +++ b/erpnext/patches/v12_0/update_price_list_currency_in_bom.py @@ -28,4 +28,4 @@ def execute(): plc_conversion_rate = get_exchange_rate(d.currency, d.company_currency, getdate(d.creation), "for_buying") - frappe.db.set_value("BOM", d.name, "plc_conversion_rate", plc_conversion_rate) \ No newline at end of file + frappe.db.set_value("BOM", d.name, "plc_conversion_rate", plc_conversion_rate) diff --git a/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py b/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py index 7450e9cd8c0..8dbfa1866d3 100644 --- a/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py +++ b/erpnext/patches/v12_0/update_state_code_for_daman_and_diu.py @@ -19,4 +19,4 @@ def execute(): gst_state = 'Dadra and Nagar Haveli and Daman and Diu', gst_state_number = 26 WHERE gst_state = 'Daman and Diu' - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v12_0/update_uom_conversion_factor.py b/erpnext/patches/v12_0/update_uom_conversion_factor.py index b5a20aa6fd9..24914fd13bc 100644 --- a/erpnext/patches/v12_0/update_uom_conversion_factor.py +++ b/erpnext/patches/v12_0/update_uom_conversion_factor.py @@ -8,4 +8,4 @@ def execute(): frappe.reload_doc("setup", "doctype", "UOM") frappe.reload_doc("stock", "doctype", "UOM Category") - add_uom_data() \ No newline at end of file + add_uom_data() diff --git a/erpnext/patches/v13_0/add_doctype_to_sla.py b/erpnext/patches/v13_0/add_doctype_to_sla.py index e2c7fd268aa..cdc5a1eabb5 100644 --- a/erpnext/patches/v13_0/add_doctype_to_sla.py +++ b/erpnext/patches/v13_0/add_doctype_to_sla.py @@ -18,4 +18,4 @@ def execute(): agreement.apply_sla_for_resolution = 1 agreement.append('sla_fulfilled_on', {'status': 'Resolved'}) agreement.append('sla_fulfilled_on', {'status': 'Closed'}) - agreement.save() \ No newline at end of file + agreement.save() diff --git a/erpnext/patches/v13_0/add_naming_series_to_old_projects.py b/erpnext/patches/v13_0/add_naming_series_to_old_projects.py index 5ed9040f1ed..a7b66f0d2bb 100644 --- a/erpnext/patches/v13_0/add_naming_series_to_old_projects.py +++ b/erpnext/patches/v13_0/add_naming_series_to_old_projects.py @@ -10,4 +10,3 @@ def execute(): naming_series = 'PROJ-.####' WHERE naming_series is NULL""") - diff --git a/erpnext/patches/v13_0/change_default_pos_print_format.py b/erpnext/patches/v13_0/change_default_pos_print_format.py index 605a29e4778..1e4f383dda6 100644 --- a/erpnext/patches/v13_0/change_default_pos_print_format.py +++ b/erpnext/patches/v13_0/change_default_pos_print_format.py @@ -5,4 +5,4 @@ def execute(): frappe.db.sql( """UPDATE `tabPOS Profile` profile SET profile.`print_format` = 'POS Invoice' - WHERE profile.`print_format` = 'Point of Sale'""") \ No newline at end of file + WHERE profile.`print_format` = 'Point of Sale'""") diff --git a/erpnext/patches/v13_0/check_is_income_tax_component.py b/erpnext/patches/v13_0/check_is_income_tax_component.py index c92d52dcec0..ebae3ad7157 100644 --- a/erpnext/patches/v13_0/check_is_income_tax_component.py +++ b/erpnext/patches/v13_0/check_is_income_tax_component.py @@ -43,4 +43,4 @@ def execute(): if frappe.db.exists("Salary Component", "Provident Fund"): frappe.db.set_value("Salary Component", "Provident Fund", "component_type", "Provident Fund") if frappe.db.exists("Salary Component", "Professional Tax"): - frappe.db.set_value("Salary Component", "Professional Tax", "component_type", "Professional Tax") \ No newline at end of file + frappe.db.set_value("Salary Component", "Professional Tax", "component_type", "Professional Tax") diff --git a/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py index 289b6a761e3..341955aa35f 100644 --- a/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py +++ b/erpnext/patches/v13_0/convert_qi_parameter_to_link_field.py @@ -20,4 +20,4 @@ def execute(): "doctype": "Quality Inspection Parameter", "parameter": parameter, "description": parameter - }).insert(ignore_permissions=True) \ No newline at end of file + }).insert(ignore_permissions=True) diff --git a/erpnext/patches/v13_0/create_healthcare_custom_fields_in_stock_entry_detail.py b/erpnext/patches/v13_0/create_healthcare_custom_fields_in_stock_entry_detail.py index 585e5406265..08d4876c0d1 100644 --- a/erpnext/patches/v13_0/create_healthcare_custom_fields_in_stock_entry_detail.py +++ b/erpnext/patches/v13_0/create_healthcare_custom_fields_in_stock_entry_detail.py @@ -7,4 +7,4 @@ def execute(): return if data['custom_fields']: - create_custom_fields(data['custom_fields']) \ No newline at end of file + create_custom_fields(data['custom_fields']) diff --git a/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py b/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py index 90dc0e2e18b..9a354537f7d 100644 --- a/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py +++ b/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py @@ -74,6 +74,3 @@ def create_assignment(employee, leave_policy, leave_period=None, allocation_exis def get_employee_with_grade(grade): return frappe.get_list("Employee", filters = {"grade": grade}) - - - diff --git a/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py index 59b2e49b26e..6ad3402ba02 100644 --- a/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py +++ b/erpnext/patches/v13_0/create_uae_pos_invoice_fields.py @@ -15,4 +15,4 @@ def execute(): frappe.reload_doc('accounts', 'doctype', 'pos_invoice') frappe.reload_doc('accounts', 'doctype', 'pos_invoice_item') - make_custom_fields() \ No newline at end of file + make_custom_fields() diff --git a/erpnext/patches/v13_0/delete_old_purchase_reports.py b/erpnext/patches/v13_0/delete_old_purchase_reports.py index 8bdc07ee5b8..c17aad06c7f 100644 --- a/erpnext/patches/v13_0/delete_old_purchase_reports.py +++ b/erpnext/patches/v13_0/delete_old_purchase_reports.py @@ -20,4 +20,4 @@ def delete_auto_email_reports(report): """ Check for one or multiple Auto Email Reports and delete """ auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"]) for auto_email_report in auto_email_reports: - frappe.delete_doc("Auto Email Report", auto_email_report[0]) \ No newline at end of file + frappe.delete_doc("Auto Email Report", auto_email_report[0]) diff --git a/erpnext/patches/v13_0/delete_old_sales_reports.py b/erpnext/patches/v13_0/delete_old_sales_reports.py index 0f44865808a..671c012c8a0 100644 --- a/erpnext/patches/v13_0/delete_old_sales_reports.py +++ b/erpnext/patches/v13_0/delete_old_sales_reports.py @@ -18,4 +18,4 @@ def delete_auto_email_reports(report): """ Check for one or multiple Auto Email Reports and delete """ auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"]) for auto_email_report in auto_email_reports: - frappe.delete_doc("Auto Email Report", auto_email_report[0]) \ No newline at end of file + frappe.delete_doc("Auto Email Report", auto_email_report[0]) diff --git a/erpnext/patches/v13_0/delete_orphaned_tables.py b/erpnext/patches/v13_0/delete_orphaned_tables.py index 1d6eebe0398..50a4a0efcbe 100644 --- a/erpnext/patches/v13_0/delete_orphaned_tables.py +++ b/erpnext/patches/v13_0/delete_orphaned_tables.py @@ -28,9 +28,9 @@ def has_deleted_company_transactions(): def get_child_doctypes_whose_parent_doctypes_were_affected(): parent_doctypes = get_affected_doctypes() child_doctypes = frappe.get_all( - 'DocField', + 'DocField', filters={ - 'fieldtype': 'Table', + 'fieldtype': 'Table', 'parent':['in', parent_doctypes] }, pluck='options') @@ -39,7 +39,7 @@ def get_child_doctypes_whose_parent_doctypes_were_affected(): def get_affected_doctypes(): affected_doctypes = [] tdr_docs = frappe.get_all('Transaction Deletion Record', pluck="name") - + for tdr in tdr_docs: tdr_doc = frappe.get_doc("Transaction Deletion Record", tdr) @@ -66,4 +66,4 @@ def check_for_new_doc_with_same_name_as_deleted_parent(doc): parent_creation_time = frappe.db.get_value(doc['parenttype'], doc['parent'], 'creation') child_creation_time = doc['creation'] - return getdate(parent_creation_time) > getdate(child_creation_time) \ No newline at end of file + return getdate(parent_creation_time) > getdate(child_creation_time) diff --git a/erpnext/patches/v13_0/delete_report_requested_items_to_order.py b/erpnext/patches/v13_0/delete_report_requested_items_to_order.py index 94a9fa85a8e..8d6340d44ef 100644 --- a/erpnext/patches/v13_0/delete_report_requested_items_to_order.py +++ b/erpnext/patches/v13_0/delete_report_requested_items_to_order.py @@ -9,4 +9,4 @@ def execute(): frappe.db.sql(""" DELETE FROM `tabReport` WHERE name = 'Requested Items to Order' - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v13_0/drop_razorpay_payload_column.py b/erpnext/patches/v13_0/drop_razorpay_payload_column.py index 8980fd00392..76b8041cd94 100644 --- a/erpnext/patches/v13_0/drop_razorpay_payload_column.py +++ b/erpnext/patches/v13_0/drop_razorpay_payload_column.py @@ -4,4 +4,4 @@ import frappe def execute(): if frappe.db.exists("DocType", "Membership"): if 'webhook_payload' in frappe.db.get_table_columns("Membership"): - frappe.db.sql("alter table `tabMembership` drop column webhook_payload") \ No newline at end of file + frappe.db.sql("alter table `tabMembership` drop column webhook_payload") diff --git a/erpnext/patches/v13_0/fix_non_unique_represents_company.py b/erpnext/patches/v13_0/fix_non_unique_represents_company.py index 61dc824dd4c..f20c73ae102 100644 --- a/erpnext/patches/v13_0/fix_non_unique_represents_company.py +++ b/erpnext/patches/v13_0/fix_non_unique_represents_company.py @@ -5,4 +5,4 @@ def execute(): update tabCustomer set represents_company = NULL where represents_company = '' - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py index 11e1e9b3b94..dca43b4193d 100644 --- a/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py +++ b/erpnext/patches/v13_0/germany_fill_debtor_creditor_number.py @@ -12,7 +12,7 @@ def execute(): German companies used to use a dedicated payable/receivable account for every party to mimick party accounts in the external accounting software "DATEV". This is no longer necessary. The reference ID for DATEV will be - stored in a new custom field "debtor_creditor_number". + stored in a new custom field "debtor_creditor_number". """ company_list = frappe.get_all('Company', filters={'country': 'Germany'}) diff --git a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py index 021bb72cae6..c4ad1b7ff4f 100644 --- a/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py +++ b/erpnext/patches/v13_0/item_reposting_for_incorrect_sl_and_gl.py @@ -67,4 +67,4 @@ def execute(): def get_creation_time(): return frappe.db.sql(''' SELECT create_time FROM - INFORMATION_SCHEMA.TABLES where TABLE_NAME = "tabRepost Item Valuation" ''', as_list=1)[0][0] \ No newline at end of file + INFORMATION_SCHEMA.TABLES where TABLE_NAME = "tabRepost Item Valuation" ''', as_list=1)[0][0] diff --git a/erpnext/patches/v13_0/loyalty_points_entry_for_pos_invoice.py b/erpnext/patches/v13_0/loyalty_points_entry_for_pos_invoice.py index ee7734053c1..d2228c3bf31 100644 --- a/erpnext/patches/v13_0/loyalty_points_entry_for_pos_invoice.py +++ b/erpnext/patches/v13_0/loyalty_points_entry_for_pos_invoice.py @@ -9,7 +9,7 @@ def execute(): '''`sales_invoice` field from loyalty point entry is splitted into `invoice_type` & `invoice` fields''' frappe.reload_doc("Accounts", "doctype", "loyalty_point_entry") - + if not frappe.db.has_column('Loyalty Point Entry', 'sales_invoice'): return @@ -17,4 +17,4 @@ def execute(): """UPDATE `tabLoyalty Point Entry` lpe SET lpe.`invoice_type` = 'Sales Invoice', lpe.`invoice` = lpe.`sales_invoice` WHERE lpe.`sales_invoice` IS NOT NULL - AND (lpe.`invoice` IS NULL OR lpe.`invoice` = '')""") \ No newline at end of file + AND (lpe.`invoice` IS NULL OR lpe.`invoice` = '')""") diff --git a/erpnext/patches/v13_0/make_non_standard_user_type.py b/erpnext/patches/v13_0/make_non_standard_user_type.py index a9d7883d40a..73361f00262 100644 --- a/erpnext/patches/v13_0/make_non_standard_user_type.py +++ b/erpnext/patches/v13_0/make_non_standard_user_type.py @@ -21,4 +21,4 @@ def execute(): frappe.flags.ignore_select_perm = True frappe.flags.update_select_perm_after_migrate = True - add_non_standard_user_types() \ No newline at end of file + add_non_standard_user_types() diff --git a/erpnext/patches/v13_0/move_branch_code_to_bank_account.py b/erpnext/patches/v13_0/move_branch_code_to_bank_account.py index 833ae2a48fb..24d9196d29f 100644 --- a/erpnext/patches/v13_0/move_branch_code_to_bank_account.py +++ b/erpnext/patches/v13_0/move_branch_code_to_bank_account.py @@ -14,4 +14,4 @@ def execute(): frappe.db.sql("""UPDATE `tabBank` b, `tabBank Account` ba SET ba.branch_code = b.branch_code WHERE ba.bank = b.name AND - ifnull(b.branch_code, '') != '' AND ifnull(ba.branch_code, '') = ''""") \ No newline at end of file + ifnull(b.branch_code, '') != '' AND ifnull(ba.branch_code, '') = ''""") diff --git a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py index fde8f864703..15aeb76e53f 100644 --- a/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py +++ b/erpnext/patches/v13_0/patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive.py @@ -51,4 +51,3 @@ def execute(): and parent = %s and salary_component = %s """, (salary["name"], comp_type, salary["salary_slip"], salary["salary_component"])) - diff --git a/erpnext/patches/v13_0/rename_issue_status_hold_to_on_hold.py b/erpnext/patches/v13_0/rename_issue_status_hold_to_on_hold.py index 48325fc2d43..4ef04ad9b1b 100644 --- a/erpnext/patches/v13_0/rename_issue_status_hold_to_on_hold.py +++ b/erpnext/patches/v13_0/rename_issue_status_hold_to_on_hold.py @@ -17,4 +17,4 @@ def rename_status(): status = 'On Hold' WHERE status = 'Hold' - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py b/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py index 3fa09a7baaa..f60567b6b21 100644 --- a/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py +++ b/erpnext/patches/v13_0/rename_membership_settings_to_non_profit_settings.py @@ -19,4 +19,4 @@ def execute(): } for old_name, new_name in rename_fields_map.items(): - rename_field("Non Profit Settings", old_name, new_name) \ No newline at end of file + rename_field("Non Profit Settings", old_name, new_name) diff --git a/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py b/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py index 390e217cada..d8bcd7f0775 100644 --- a/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py +++ b/erpnext/patches/v13_0/replace_pos_page_with_point_of_sale_page.py @@ -3,4 +3,4 @@ import frappe def execute(): if frappe.db.exists("Page", "point-of-sale"): - frappe.rename_doc("Page", "pos", "point-of-sale", 1, 1) \ No newline at end of file + frappe.rename_doc("Page", "pos", "point-of-sale", 1, 1) diff --git a/erpnext/patches/v13_0/replace_pos_payment_mode_table.py b/erpnext/patches/v13_0/replace_pos_payment_mode_table.py index 7cb264830ab..bc1fc98e4da 100644 --- a/erpnext/patches/v13_0/replace_pos_payment_mode_table.py +++ b/erpnext/patches/v13_0/replace_pos_payment_mode_table.py @@ -23,5 +23,5 @@ def execute(): pos_payment_method.parentfield = payment_mode.parentfield pos_payment_method.parenttype = payment_mode.parenttype pos_payment_method.db_insert() - + frappe.db.sql("""delete from `tabSales Invoice Payment` where parent=%s""", pos_profile.name) diff --git a/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py b/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py index 66857c4e659..13ec41ec55e 100644 --- a/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py +++ b/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py @@ -4,4 +4,4 @@ def execute(): frappe.reload_doc('HR', 'doctype', 'Leave Allocation') frappe.reload_doc('HR', 'doctype', 'Leave Ledger Entry') frappe.db.sql("""update `tabLeave Ledger Entry` as lle set company = (select company from `tabEmployee` where employee = lle.employee)""") - frappe.db.sql("""update `tabLeave Allocation` as la set company = (select company from `tabEmployee` where employee = la.employee)""") \ No newline at end of file + frappe.db.sql("""update `tabLeave Allocation` as la set company = (select company from `tabEmployee` where employee = la.employee)""") diff --git a/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py b/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py index edca2383930..7f75946af9f 100644 --- a/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py +++ b/erpnext/patches/v13_0/set_payment_channel_in_payment_gateway_account.py @@ -14,4 +14,4 @@ def set_payment_channel_as_email(): frappe.db.sql(""" UPDATE `tabPayment Gateway Account` SET `payment_channel` = "Email" - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v13_0/set_pos_closing_as_failed.py b/erpnext/patches/v13_0/set_pos_closing_as_failed.py index 1c576db1c7e..7968e74f50f 100644 --- a/erpnext/patches/v13_0/set_pos_closing_as_failed.py +++ b/erpnext/patches/v13_0/set_pos_closing_as_failed.py @@ -4,4 +4,4 @@ import frappe def execute(): frappe.reload_doc('accounts', 'doctype', 'pos_closing_entry') - frappe.db.sql("update `tabPOS Closing Entry` set `status` = 'Failed' where `status` = 'Queued'") \ No newline at end of file + frappe.db.sql("update `tabPOS Closing Entry` set `status` = 'Failed' where `status` = 'Queued'") diff --git a/erpnext/patches/v13_0/set_training_event_attendance.py b/erpnext/patches/v13_0/set_training_event_attendance.py index 18cad8d86c0..3db183fb2ab 100644 --- a/erpnext/patches/v13_0/set_training_event_attendance.py +++ b/erpnext/patches/v13_0/set_training_event_attendance.py @@ -6,4 +6,4 @@ def execute(): frappe.reload_doc('hr', 'doctype', 'training_event_employee') frappe.db.sql("update `tabTraining Event Employee` set `attendance` = 'Present'") - frappe.db.sql("update `tabTraining Event Employee` set `is_mandatory` = 1 where `attendance` = 'Mandatory'") \ No newline at end of file + frappe.db.sql("update `tabTraining Event Employee` set `is_mandatory` = 1 where `attendance` = 'Mandatory'") diff --git a/erpnext/patches/v13_0/set_youtube_video_id.py b/erpnext/patches/v13_0/set_youtube_video_id.py index c3b49eb4fe5..f6104d1579f 100644 --- a/erpnext/patches/v13_0/set_youtube_video_id.py +++ b/erpnext/patches/v13_0/set_youtube_video_id.py @@ -7,4 +7,4 @@ def execute(): for video in frappe.get_all("Video", fields=["name", "url", "youtube_video_id"]): if video.url and not video.youtube_video_id: - frappe.db.set_value("Video", video.name, "youtube_video_id", get_id_from_url(video.url)) \ No newline at end of file + frappe.db.set_value("Video", video.name, "youtube_video_id", get_id_from_url(video.url)) diff --git a/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py b/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py index ecc7822e1d7..c8c160fae71 100644 --- a/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py +++ b/erpnext/patches/v13_0/setting_custom_roles_for_some_regional_reports.py @@ -7,4 +7,4 @@ def execute(): if not company: return - add_custom_roles_for_reports() \ No newline at end of file + add_custom_roles_for_reports() diff --git a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py index d927524a3c0..83581dd4144 100644 --- a/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py +++ b/erpnext/patches/v13_0/setup_patient_history_settings_for_standard_doctypes.py @@ -13,4 +13,4 @@ def execute(): frappe.reload_doc("healthcare", "doctype", "Patient History Standard Document Type") frappe.reload_doc("healthcare", "doctype", "Patient History Custom Document Type") - setup_patient_history_settings() \ No newline at end of file + setup_patient_history_settings() diff --git a/erpnext/patches/v13_0/stock_entry_enhancements.py b/erpnext/patches/v13_0/stock_entry_enhancements.py index 0bdcc9c0e88..7b93ce35768 100644 --- a/erpnext/patches/v13_0/stock_entry_enhancements.py +++ b/erpnext/patches/v13_0/stock_entry_enhancements.py @@ -8,18 +8,18 @@ def execute(): frappe.reload_doc("stock", "doctype", "stock_entry") if frappe.db.has_column("Stock Entry", "add_to_transit"): frappe.db.sql(""" - UPDATE `tabStock Entry` SET + UPDATE `tabStock Entry` SET stock_entry_type = 'Material Transfer', purpose = 'Material Transfer', add_to_transit = 1 WHERE stock_entry_type = 'Send to Warehouse' """) - frappe.db.sql("""UPDATE `tabStock Entry` SET + frappe.db.sql("""UPDATE `tabStock Entry` SET stock_entry_type = 'Material Transfer', purpose = 'Material Transfer' WHERE stock_entry_type = 'Receive at Warehouse' """) - + frappe.reload_doc("stock", "doctype", "warehouse_type") if not frappe.db.exists('Warehouse Type', 'Transit'): doc = frappe.new_doc('Warehouse Type') @@ -28,4 +28,4 @@ def execute(): frappe.reload_doc("stock", "doctype", "stock_entry_type") frappe.delete_doc_if_exists("Stock Entry Type", "Send to Warehouse") - frappe.delete_doc_if_exists("Stock Entry Type", "Receive at Warehouse") \ No newline at end of file + frappe.delete_doc_if_exists("Stock Entry Type", "Receive at Warehouse") diff --git a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py index adfa20e368a..50f233deef4 100644 --- a/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py +++ b/erpnext/patches/v13_0/update_actual_start_and_end_date_in_wo.py @@ -38,4 +38,4 @@ def execute(): jc.production_item = wo.production_item, jc.item_name = wo.item_name WHERE jc.work_order = wo.name and IFNULL(jc.production_item, "") = "" - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py index eae5ff60b90..dc9ed18eade 100644 --- a/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py +++ b/erpnext/patches/v13_0/update_amt_in_work_order_required_items.py @@ -7,4 +7,3 @@ def execute(): frappe.reload_doc("manufacturing", "doctype", "work_order_item") frappe.db.sql("""UPDATE `tabWork Order Item` SET amount = rate * required_qty""") - diff --git a/erpnext/patches/v13_0/update_deferred_settings.py b/erpnext/patches/v13_0/update_deferred_settings.py index a7d82077b76..bcc09527a29 100644 --- a/erpnext/patches/v13_0/update_deferred_settings.py +++ b/erpnext/patches/v13_0/update_deferred_settings.py @@ -8,4 +8,4 @@ def execute(): accounts_settings.book_deferred_entries_based_on = 'Days' accounts_settings.book_deferred_entries_via_journal_entry = 0 accounts_settings.submit_journal_entries = 0 - accounts_settings.save() \ No newline at end of file + accounts_settings.save() diff --git a/erpnext/patches/v13_0/update_export_type_for_gst.py b/erpnext/patches/v13_0/update_export_type_for_gst.py index 3e20212af6d..ef70b55d94c 100644 --- a/erpnext/patches/v13_0/update_export_type_for_gst.py +++ b/erpnext/patches/v13_0/update_export_type_for_gst.py @@ -8,7 +8,7 @@ def execute(): # Update custom fields fieldname = frappe.db.get_value('Custom Field', {'dt': 'Customer', 'fieldname': 'export_type'}) if fieldname: - frappe.db.set_value('Custom Field', fieldname, + frappe.db.set_value('Custom Field', fieldname, { 'default': '', 'mandatory_depends_on': 'eval:in_list(["SEZ", "Overseas", "Deemed Export"], doc.gst_category)' @@ -16,7 +16,7 @@ def execute(): fieldname = frappe.db.get_value('Custom Field', {'dt': 'Supplier', 'fieldname': 'export_type'}) if fieldname: - frappe.db.set_value('Custom Field', fieldname, + frappe.db.set_value('Custom Field', fieldname, { 'default': '', 'mandatory_depends_on': 'eval:in_list(["SEZ", "Overseas"], doc.gst_category)' @@ -29,4 +29,4 @@ def execute(): frappe.db.sql(""" UPDATE `tabSupplier` set export_type = '' WHERE gst_category NOT IN ('SEZ', 'Overseas') - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v13_0/update_job_card_details.py b/erpnext/patches/v13_0/update_job_card_details.py index d4e65c6f2f2..733b3a960cf 100644 --- a/erpnext/patches/v13_0/update_job_card_details.py +++ b/erpnext/patches/v13_0/update_job_card_details.py @@ -13,4 +13,4 @@ def execute(): SET jc.hour_rate = wo.hour_rate WHERE jc.operation_id = wo.name and jc.docstatus < 2 and wo.hour_rate > 0 - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v13_0/update_project_template_tasks.py b/erpnext/patches/v13_0/update_project_template_tasks.py index 8cc27d217fe..b41b74205c7 100644 --- a/erpnext/patches/v13_0/update_project_template_tasks.py +++ b/erpnext/patches/v13_0/update_project_template_tasks.py @@ -44,4 +44,4 @@ def execute(): "task": tsk.name, "subject": tsk.subject }) - template.save() \ No newline at end of file + template.save() diff --git a/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py b/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py index 792118fbee2..ccdc334f306 100644 --- a/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py +++ b/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py @@ -12,4 +12,3 @@ def execute(): SET reason_for_leaving = reason_for_resignation WHERE status = 'Left' and reason_for_leaving is null and reason_for_resignation is not null """) - diff --git a/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py b/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py index 409f4da8599..e642547ef82 100644 --- a/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py +++ b/erpnext/patches/v13_0/update_returned_qty_in_pr_dn.py @@ -29,4 +29,4 @@ def execute(): where docstatus = 1 """) for doctype in ('Purchase Receipt', 'Delivery Note'): - update_from_return_docs(doctype) \ No newline at end of file + update_from_return_docs(doctype) diff --git a/erpnext/patches/v13_0/update_subscription.py b/erpnext/patches/v13_0/update_subscription.py index 871ebf17c4e..d25e9c805b7 100644 --- a/erpnext/patches/v13_0/update_subscription.py +++ b/erpnext/patches/v13_0/update_subscription.py @@ -38,4 +38,4 @@ def execute(): UPDATE `tabSubscription Plan` SET price_determination = %s WHERE price_determination = %s - """, (value, key)) \ No newline at end of file + """, (value, key)) diff --git a/erpnext/patches/v13_0/update_subscription_status_in_memberships.py b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py index 28e650e9ced..d9c3e453d47 100644 --- a/erpnext/patches/v13_0/update_subscription_status_in_memberships.py +++ b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py @@ -6,4 +6,4 @@ def execute(): if frappe.db.has_column('Member', 'subscription_activated'): frappe.db.sql('UPDATE `tabMember` SET subscription_status = "Active" WHERE subscription_activated = 1') - frappe.db.sql_ddl('ALTER table `tabMember` DROP COLUMN subscription_activated') \ No newline at end of file + frappe.db.sql_ddl('ALTER table `tabMember` DROP COLUMN subscription_activated') diff --git a/erpnext/patches/v13_0/update_tds_check_field.py b/erpnext/patches/v13_0/update_tds_check_field.py index 3d149586a04..341b0e8e2e2 100644 --- a/erpnext/patches/v13_0/update_tds_check_field.py +++ b/erpnext/patches/v13_0/update_tds_check_field.py @@ -6,4 +6,4 @@ def execute(): frappe.db.sql(""" UPDATE `tabTax Withholding Category` set round_off_tax_amount = 0 WHERE round_off_tax_amount IS NULL - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v13_0/update_timesheet_changes.py b/erpnext/patches/v13_0/update_timesheet_changes.py index 93b7f8e59a4..a36c84ea6e2 100644 --- a/erpnext/patches/v13_0/update_timesheet_changes.py +++ b/erpnext/patches/v13_0/update_timesheet_changes.py @@ -22,4 +22,4 @@ def execute(): exchange_rate = 1.0, base_total_billable_amount = total_billable_amount, base_total_billed_amount = total_billed_amount, - base_total_costing_amount = total_costing_amount""".format(base_currency)) \ No newline at end of file + base_total_costing_amount = total_costing_amount""".format(base_currency)) diff --git a/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py b/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py index 340bf4947b6..7d344f9cd7e 100644 --- a/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py +++ b/erpnext/patches/v13_0/updates_for_multi_currency_payroll.py @@ -96,8 +96,8 @@ def execute(): # update currency in following doctypes based on company currency doctypes_for_currency = ['Employee Advance', 'Leave Encashment', 'Employee Benefit Application', - 'Employee Benefit Claim', 'Employee Incentive', 'Additional Salary', - 'Employee Tax Exemption Declaration', 'Employee Tax Exemption Proof Submission', + 'Employee Benefit Claim', 'Employee Incentive', 'Additional Salary', + 'Employee Tax Exemption Declaration', 'Employee Tax Exemption Proof Submission', 'Income Tax Slab', 'Retention Bonus', 'Salary Structure'] for dt in doctypes_for_currency: diff --git a/erpnext/patches/v8_1/removed_roles_from_gst_report_non_indian_account.py b/erpnext/patches/v8_1/removed_roles_from_gst_report_non_indian_account.py index ccb2e0ec74a..55f5f8201fb 100644 --- a/erpnext/patches/v8_1/removed_roles_from_gst_report_non_indian_account.py +++ b/erpnext/patches/v8_1/removed_roles_from_gst_report_non_indian_account.py @@ -15,4 +15,4 @@ def execute(): where parenttype = 'Report' and parent in('GST Sales Register', 'GST Purchase Register', 'GST Itemised Sales Register', - 'GST Itemised Purchase Register', 'Eway Bill')""") \ No newline at end of file + 'GST Itemised Purchase Register', 'Eway Bill')""") diff --git a/erpnext/patches/v8_1/setup_gst_india.py b/erpnext/patches/v8_1/setup_gst_india.py index e8b017d8644..c214990693c 100644 --- a/erpnext/patches/v8_1/setup_gst_india.py +++ b/erpnext/patches/v8_1/setup_gst_india.py @@ -50,4 +50,4 @@ ERPNext Team. try: sendmail_to_system_managers("[Important] ERPNext GST updates", message) except Exception as e: - pass \ No newline at end of file + pass diff --git a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py index 4d47f25fcf3..2a9c56179e7 100644 --- a/erpnext/payroll/doctype/additional_salary/test_additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/test_additional_salary.py @@ -27,7 +27,7 @@ class TestAdditionalSalary(unittest.TestCase): frappe.db.set_value("Employee", emp_id, "relieving_date", add_days(nowdate(), 1800)) salary_structure = make_salary_structure("Test Salary Structure Additional Salary", "Monthly", employee=emp_id) add_sal = get_additional_salary(emp_id) - + ss = make_employee_salary_slip("test_additional@salary.com", "Monthly", salary_structure=salary_structure.name) for earning in ss.earnings: if earning.salary_component == "Recurring Salary Component": diff --git a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py index 5ebe514ac05..c7fbb06b100 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py @@ -253,4 +253,4 @@ def get_earning_components_max_benefits(employee, date, earning_component): order by name """, salary_structure, earning_component) - return amount if amount else 0 \ No newline at end of file + return amount if amount else 0 diff --git a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py index a8dd7e4d6dd..d3f24c93780 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_sub_category/employee_tax_exemption_sub_category.py @@ -13,4 +13,4 @@ class EmployeeTaxExemptionSubCategory(Document): 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 + .format(category_max_amount, self.exemption_category)) diff --git a/erpnext/payroll/doctype/gratuity/gratuity.js b/erpnext/payroll/doctype/gratuity/gratuity.js index 565d2c49f94..377f3c64916 100644 --- a/erpnext/payroll/doctype/gratuity/gratuity.js +++ b/erpnext/payroll/doctype/gratuity/gratuity.js @@ -69,4 +69,4 @@ frappe.ui.form.on('Gratuity', { } } -}); \ No newline at end of file +}); diff --git a/erpnext/payroll/doctype/gratuity/gratuity.py b/erpnext/payroll/doctype/gratuity/gratuity.py index 1acd6e342fd..8cb804db6fa 100644 --- a/erpnext/payroll/doctype/gratuity/gratuity.py +++ b/erpnext/payroll/doctype/gratuity/gratuity.py @@ -246,4 +246,3 @@ def get_last_salary_slip(employee): "employee": employee, 'docstatus': 1 }, order_by = "start_date desc")[0].name - diff --git a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py b/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py index 5b2489f22cd..483e346a32d 100644 --- a/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py +++ b/erpnext/payroll/doctype/gratuity/gratuity_dashboard.py @@ -17,4 +17,4 @@ def get_data(): 'items': ['Additional Salary'] } ] - } \ No newline at end of file + } diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js index ee6c5df7371..014a121c96a 100644 --- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js +++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule.js @@ -37,4 +37,4 @@ frappe.ui.form.on('Gratuity Rule Slab', { frappe.throw(__("To(Year) year can not be less than From(year) ")); } } -}); \ No newline at end of file +}); diff --git a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py index 0d70163495a..0f27315cfbf 100644 --- a/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py +++ b/erpnext/payroll/doctype/gratuity_rule/gratuity_rule_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Gratuity'] } ] - } \ No newline at end of file + } diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py index 7af507d119c..0346a7cc594 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry_dashboard.py @@ -13,4 +13,4 @@ def get_data(): 'items': ['Salary Slip', 'Journal Entry'] } ] - } \ No newline at end of file + } diff --git a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py index 4e9c7c9e7cc..e33299559cc 100644 --- a/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py +++ b/erpnext/payroll/doctype/payroll_period/payroll_period_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Employee Tax Exemption Proof Submission', 'Employee Tax Exemption Declaration'] }, ], - } \ No newline at end of file + } diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.js b/erpnext/payroll/doctype/salary_slip/test_salary_slip.js index 06a1c7d72df..a47eba1887d 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.js +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.js @@ -52,4 +52,4 @@ QUnit.test("test salary slip", function(assert) { () => frappe.click_button('Yes'), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html index d07a1ab551a..0f6cc378513 100644 --- a/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html +++ b/erpnext/payroll/doctype/salary_structure/condition_and_formula_help.html @@ -44,4 +44,4 @@
                                                            Condition: annual_taxable_earning > 20000000
                                                            Formula: annual_taxable_earning * 0.10 
                                                            - \ No newline at end of file + diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure.py b/erpnext/payroll/doctype/salary_structure/salary_structure.py index 58c445f8a96..6dfb3a57d5d 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/salary_structure.py @@ -206,4 +206,3 @@ def get_employees(salary_structure): salary_structure, salary_structure)) return list(set([d.employee for d in employees])) - diff --git a/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py index 547f2b81be7..0159e3530fb 100644 --- a/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py +++ b/erpnext/payroll/doctype/salary_structure/salary_structure_dashboard.py @@ -15,4 +15,4 @@ def get_data(): 'items': ['Employee Grade'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py index a0c3013061d..5fb3ce2a98e 100644 --- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py +++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py @@ -36,7 +36,7 @@ class SalaryStructureAssignment(Document): def validate_income_tax_slab(self): if not self.income_tax_slab: return - + income_tax_slab_currency = frappe.db.get_value('Income Tax Slab', self.income_tax_slab, 'currency') if self.currency != income_tax_slab_currency: frappe.throw(_("Currency of selected Income Tax Slab should be {0} instead of {1}").format(self.currency, income_tax_slab_currency)) @@ -69,4 +69,4 @@ def get_employee_currency(employee): employee_currency = frappe.db.get_value('Salary Structure Assignment', {'employee': employee}, 'currency') if not employee_currency: frappe.throw(_("There is no Salary Structure assigned to {0}. First assign a Salary Stucture.").format(employee)) - return employee_currency \ No newline at end of file + return employee_currency diff --git a/erpnext/payroll/notification/as b/erpnext/payroll/notification/as index 7a395572613..05c2c1bec27 100644 --- a/erpnext/payroll/notification/as +++ b/erpnext/payroll/notification/as @@ -1 +1 @@ -update from `tabNotification` set module='Payroll' where name = "Retention Bonus" \ No newline at end of file +update from `tabNotification` set module='Payroll' where name = "Retention Bonus" diff --git a/erpnext/payroll/report/bank_remittance/bank_remittance.js b/erpnext/payroll/report/bank_remittance/bank_remittance.js index 6482ed34516..8b75b4facea 100644 --- a/erpnext/payroll/report/bank_remittance/bank_remittance.js +++ b/erpnext/payroll/report/bank_remittance/bank_remittance.js @@ -25,4 +25,3 @@ frappe.query_reports["Bank Remittance"] = { ] } - diff --git a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js index 4bbb7f6a1be..6ecf2b1960c 100644 --- a/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js +++ b/erpnext/payroll/report/income_tax_deductions/income_tax_deductions.js @@ -4,4 +4,4 @@ frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { frappe.query_reports["Income Tax Deductions"] = erpnext.salary_slip_deductions_report_filters; -}); \ No newline at end of file +}); diff --git a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js index 166d982c9c6..9b829541692 100644 --- a/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js +++ b/erpnext/payroll/report/salary_payments_based_on_payment_mode/salary_payments_based_on_payment_mode.js @@ -4,4 +4,4 @@ frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { frappe.query_reports["Salary Payments Based On Payment Mode"] = erpnext.salary_slip_deductions_report_filters; -}); \ No newline at end of file +}); diff --git a/erpnext/portal/doctype/homepage/homepage.py b/erpnext/portal/doctype/homepage/homepage.py index 4e4d4774abf..54ea7c62df4 100644 --- a/erpnext/portal/doctype/homepage/homepage.py +++ b/erpnext/portal/doctype/homepage/homepage.py @@ -23,4 +23,3 @@ class Homepage(Document): doc.save() self.append('products', dict(item_code=d.name, item_name=d.item_name, description=d.description, image=d.image)) - diff --git a/erpnext/portal/product_configurator/test_product_configurator.py b/erpnext/portal/product_configurator/test_product_configurator.py index 8aa073402ac..ec7c83aa397 100644 --- a/erpnext/portal/product_configurator/test_product_configurator.py +++ b/erpnext/portal/product_configurator/test_product_configurator.py @@ -139,4 +139,4 @@ class TestProductConfigurator(unittest.TestCase): # teardown doc.delete() - item_group_doc.delete() \ No newline at end of file + item_group_doc.delete() diff --git a/erpnext/projects/doctype/activity_cost/activity_cost.js b/erpnext/projects/doctype/activity_cost/activity_cost.js index ba10153e5cd..2d22caad8e2 100644 --- a/erpnext/projects/doctype/activity_cost/activity_cost.js +++ b/erpnext/projects/doctype/activity_cost/activity_cost.js @@ -1 +1 @@ -cur_frm.add_fetch('employee', 'employee_name', 'employee_name'); \ No newline at end of file +cur_frm.add_fetch('employee', 'employee_name', 'employee_name'); diff --git a/erpnext/projects/doctype/activity_cost/activity_cost.py b/erpnext/projects/doctype/activity_cost/activity_cost.py index 862a70717ab..99226ea581c 100644 --- a/erpnext/projects/doctype/activity_cost/activity_cost.py +++ b/erpnext/projects/doctype/activity_cost/activity_cost.py @@ -13,7 +13,7 @@ class ActivityCost(Document): def validate(self): self.set_title() self.check_unique() - + def set_title(self): if self.employee: if not self.employee_name: diff --git a/erpnext/projects/doctype/activity_cost/test_activity_cost.py b/erpnext/projects/doctype/activity_cost/test_activity_cost.py index 67d76eb1eee..5f35f299b36 100644 --- a/erpnext/projects/doctype/activity_cost/test_activity_cost.py +++ b/erpnext/projects/doctype/activity_cost/test_activity_cost.py @@ -22,4 +22,4 @@ class TestActivityCost(unittest.TestCase): activity_cost1.insert() activity_cost2 = frappe.copy_doc(activity_cost1) self.assertRaises(DuplicationError, activity_cost2.insert ) - frappe.db.sql("delete from `tabActivity Cost`") \ No newline at end of file + frappe.db.sql("delete from `tabActivity Cost`") diff --git a/erpnext/projects/doctype/activity_type/activity_type.py b/erpnext/projects/doctype/activity_type/activity_type.py index 8b610c29561..50e18ef4de9 100644 --- a/erpnext/projects/doctype/activity_type/activity_type.py +++ b/erpnext/projects/doctype/activity_type/activity_type.py @@ -5,4 +5,4 @@ from __future__ import unicode_literals from frappe.model.document import Document class ActivityType(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/projects/doctype/activity_type/test_activity_type.py b/erpnext/projects/doctype/activity_type/test_activity_type.py index 3ea28dfbe2c..dcb01018de0 100644 --- a/erpnext/projects/doctype/activity_type/test_activity_type.py +++ b/erpnext/projects/doctype/activity_type/test_activity_type.py @@ -4,4 +4,4 @@ from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Activity Type') \ No newline at end of file +test_records = frappe.get_test_records('Activity Type') diff --git a/erpnext/projects/doctype/project/project_dashboard.html b/erpnext/projects/doctype/project/project_dashboard.html index f5bfbb7ca1f..1f299e30833 100644 --- a/erpnext/projects/doctype/project/project_dashboard.html +++ b/erpnext/projects/doctype/project/project_dashboard.html @@ -23,4 +23,4 @@ -{% endfor %} \ No newline at end of file +{% endfor %} diff --git a/erpnext/projects/doctype/project_template/project_template.py b/erpnext/projects/doctype/project_template/project_template.py index aace40240c4..2426fd2af89 100644 --- a/erpnext/projects/doctype/project_template/project_template.py +++ b/erpnext/projects/doctype/project_template/project_template.py @@ -22,7 +22,7 @@ class ProjectTemplate(Document): task_details_format = get_link_to_form("Task",task_details.name) dependency_task_format = get_link_to_form("Task", dependency_task.task) frappe.throw(_("Task {0} depends on Task {1}. Please add Task {1} to the Tasks list.").format(frappe.bold(task_details_format), frappe.bold(dependency_task_format))) - + def check_dependent_task_presence(self, task): for task_details in self.tasks: if task_details.task == task: diff --git a/erpnext/projects/doctype/project_template/test_project_template.py b/erpnext/projects/doctype/project_template/test_project_template.py index 95663cdcbbb..d546fd09a30 100644 --- a/erpnext/projects/doctype/project_template/test_project_template.py +++ b/erpnext/projects/doctype/project_template/test_project_template.py @@ -26,4 +26,4 @@ def make_project_template(project_template_name, project_tasks=[]): }) doc.insert() - return frappe.get_doc('Project Template', project_template_name) \ No newline at end of file + return frappe.get_doc('Project Template', project_template_name) diff --git a/erpnext/projects/doctype/project_type/project_type.js b/erpnext/projects/doctype/project_type/project_type.js index a1f941fe148..e3dda5eccc5 100644 --- a/erpnext/projects/doctype/project_type/project_type.js +++ b/erpnext/projects/doctype/project_type/project_type.js @@ -3,4 +3,4 @@ frappe.ui.form.on('Project Type', { -}); \ No newline at end of file +}); diff --git a/erpnext/projects/doctype/project_type/project_type.py b/erpnext/projects/doctype/project_type/project_type.py index f46876eda23..36137ca0186 100644 --- a/erpnext/projects/doctype/project_type/project_type.py +++ b/erpnext/projects/doctype/project_type/project_type.py @@ -10,4 +10,4 @@ from frappe import _ class ProjectType(Document): def on_trash(self): if self.name == "External": - frappe.throw(_("You cannot delete Project Type 'External'")) \ No newline at end of file + frappe.throw(_("You cannot delete Project Type 'External'")) diff --git a/erpnext/projects/doctype/project_update/project_update.py b/erpnext/projects/doctype/project_update/project_update.py index faa4bf1f9b6..2e1ec746ed6 100644 --- a/erpnext/projects/doctype/project_update/project_update.py +++ b/erpnext/projects/doctype/project_update/project_update.py @@ -39,4 +39,4 @@ def email_sending(project_name,frequency,date_start,date_end,progress,number_of_ for emails in email: frappe.sendmail(recipients=emails,subject=frappe._(project_name + ' ' + 'Summary'),message = msg) else: - pass \ No newline at end of file + pass diff --git a/erpnext/projects/doctype/project_update/test_project_update.py b/erpnext/projects/doctype/project_update/test_project_update.py index d5d09194446..2edd2f85a31 100644 --- a/erpnext/projects/doctype/project_update/test_project_update.py +++ b/erpnext/projects/doctype/project_update/test_project_update.py @@ -10,4 +10,4 @@ class TestProjectUpdate(unittest.TestCase): pass test_records = frappe.get_test_records('Project Update') -test_ignore = ["Sales Order"] \ No newline at end of file +test_ignore = ["Sales Order"] diff --git a/erpnext/projects/doctype/task/task_tree.js b/erpnext/projects/doctype/task/task_tree.js index d1d872f28a4..9ebfcdd180c 100644 --- a/erpnext/projects/doctype/task/task_tree.js +++ b/erpnext/projects/doctype/task/task_tree.js @@ -81,4 +81,4 @@ frappe.treeview_settings['Task'] = { } ], extend_toolbar: true -}; \ No newline at end of file +}; diff --git a/erpnext/projects/doctype/timesheet/timesheet.css b/erpnext/projects/doctype/timesheet/timesheet.css index 3a38415e6c6..1e055629ba8 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.css +++ b/erpnext/projects/doctype/timesheet/timesheet.css @@ -20,4 +20,4 @@ .playpause { border-right: 1px dashed #fff; border-bottom: 1px dashed #fff; -} \ No newline at end of file +} diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index 84c7b8118b8..1655b76b988 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -399,4 +399,4 @@ function set_project_in_timelog(frm) { frappe.model.set_value(item.doctype, item.name, "project", frm.doc.parent_project); }); } -} \ No newline at end of file +} diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index a0042eb7d1a..5f569d6bcd4 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -238,9 +238,9 @@ def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to @frappe.whitelist() def get_timesheet_detail_rate(timelog, currency): - timelog_detail = frappe.db.sql("""SELECT tsd.billing_amount as billing_amount, - ts.currency as currency FROM `tabTimesheet Detail` tsd - INNER JOIN `tabTimesheet` ts ON ts.name=tsd.parent + timelog_detail = frappe.db.sql("""SELECT tsd.billing_amount as billing_amount, + ts.currency as currency FROM `tabTimesheet Detail` tsd + INNER JOIN `tabTimesheet` ts ON ts.name=tsd.parent WHERE tsd.name = '{0}'""".format(timelog), as_dict = 1)[0] if timelog_detail.currency: diff --git a/erpnext/projects/doctype/timesheet/timesheet_calendar.js b/erpnext/projects/doctype/timesheet/timesheet_calendar.js index 14f016a7653..80967ede1ce 100644 --- a/erpnext/projects/doctype/timesheet/timesheet_calendar.js +++ b/erpnext/projects/doctype/timesheet/timesheet_calendar.js @@ -9,8 +9,8 @@ frappe.views.calendar["Timesheet"] = { "title": "title" }, style_map: { - "0": "info", - "1": "standard", + "0": "info", + "1": "standard", "2": "danger" }, gantt: true, diff --git a/erpnext/projects/doctype/timesheet/timesheet_dashboard.py b/erpnext/projects/doctype/timesheet/timesheet_dashboard.py index acff97a2269..088d98c4d5f 100644 --- a/erpnext/projects/doctype/timesheet/timesheet_dashboard.py +++ b/erpnext/projects/doctype/timesheet/timesheet_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Sales Invoice', 'Salary Slip'] } ] - } \ No newline at end of file + } diff --git a/erpnext/projects/doctype/timesheet/timesheet_list.js b/erpnext/projects/doctype/timesheet/timesheet_list.js index 1b200f855db..b59fdc96fe8 100644 --- a/erpnext/projects/doctype/timesheet/timesheet_list.js +++ b/erpnext/projects/doctype/timesheet/timesheet_list.js @@ -4,13 +4,13 @@ frappe.listview_settings['Timesheet'] = { if (doc.status== "Billed") { return [__("Billed"), "green", "status,=," + "Billed"] } - + if (doc.status== "Payslip") { return [__("Payslip"), "green", "status,=," + "Payslip"] } - + if (doc.status== "Completed") { return [__("Completed"), "green", "status,=," + "Completed"] } } -}; \ No newline at end of file +}; diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py index 5efde41b5b3..a22ed7b8338 100644 --- a/erpnext/projects/report/billing_summary.py +++ b/erpnext/projects/report/billing_summary.py @@ -144,4 +144,4 @@ def get_billable_and_total_duration(activity, start_time, end_time): if activity_duration != activity.billing_hours: billing_duration = activity_duration * activity.billing_hours / activity.hours - return flt(activity_duration, precision), flt(billing_duration, precision) \ No newline at end of file + return flt(activity_duration, precision), flt(billing_duration, precision) diff --git a/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py b/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py index 682fb2e09dc..3dcae5b1b53 100644 --- a/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py +++ b/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.py @@ -20,8 +20,8 @@ def execute(filters=None): return columns, data def get_column(): - return [_("Timesheet") + ":Link/Timesheet:120", _("Employee") + "::150", _("Employee Name") + "::150", - _("From Datetime") + "::140", _("To Datetime") + "::140", _("Hours") + "::70", + return [_("Timesheet") + ":Link/Timesheet:120", _("Employee") + "::150", _("Employee Name") + "::150", + _("From Datetime") + "::140", _("To Datetime") + "::140", _("Hours") + "::70", _("Activity Type") + "::120", _("Task") + ":Link/Task:150", _("Project") + ":Link/Project:120", _("Status") + "::70"] @@ -45,4 +45,4 @@ def get_conditions(filters): if match_conditions: conditions += " and %s" % match_conditions - return conditions \ No newline at end of file + return conditions diff --git a/erpnext/projects/report/delayed_tasks_summary/test_delayed_tasks_summary.py b/erpnext/projects/report/delayed_tasks_summary/test_delayed_tasks_summary.py index dbeedb4be92..78291b2d781 100644 --- a/erpnext/projects/report/delayed_tasks_summary/test_delayed_tasks_summary.py +++ b/erpnext/projects/report/delayed_tasks_summary/test_delayed_tasks_summary.py @@ -10,7 +10,7 @@ class TestDelayedTasksSummary(unittest.TestCase): def setUp(self): task1 = create_task("_Test Task 98", add_days(nowdate(), -10), nowdate()) create_task("_Test Task 99", add_days(nowdate(), -10), add_days(nowdate(), -1)) - + task1.status = "Completed" task1.completed_on = add_days(nowdate(), -1) task1.save() @@ -38,7 +38,7 @@ class TestDelayedTasksSummary(unittest.TestCase): ] report = execute(filters) data = list(filter(lambda x: x.subject == "_Test Task 99", report[1]))[0] - + for key in ["subject", "status", "priority", "delay"]: self.assertEqual(expected_data[0].get(key), data.get(key)) @@ -51,4 +51,4 @@ class TestDelayedTasksSummary(unittest.TestCase): def tearDown(self): for task in ["_Test Task 98", "_Test Task 99"]: - frappe.get_doc("Task", {"subject": task}).delete() \ No newline at end of file + frappe.get_doc("Task", {"subject": task}).delete() diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py index cd5ad7803a5..17c92c234d5 100644 --- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py +++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py @@ -11,4 +11,4 @@ def execute(filters=None): columns = get_columns() data = get_data(filters) - return columns, data \ No newline at end of file + return columns, data diff --git a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py index 0e5a59756e3..969fc556e8d 100644 --- a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py +++ b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py @@ -195,4 +195,4 @@ class TestEmployeeUtilization(unittest.TestCase): 'per_util': 27.78, 'per_util_billed_only': 27.78 } - ] \ No newline at end of file + ] diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.py b/erpnext/projects/report/project_billing_summary/project_billing_summary.py index cd5ad7803a5..17c92c234d5 100644 --- a/erpnext/projects/report/project_billing_summary/project_billing_summary.py +++ b/erpnext/projects/report/project_billing_summary/project_billing_summary.py @@ -11,4 +11,4 @@ def execute(filters=None): columns = get_columns() data = get_data(filters) - return columns, data \ No newline at end of file + return columns, data diff --git a/erpnext/projects/report/project_profitability/project_profitability.py b/erpnext/projects/report/project_profitability/project_profitability.py index 9139d84facc..0a52f7bf904 100644 --- a/erpnext/projects/report/project_profitability/project_profitability.py +++ b/erpnext/projects/report/project_profitability/project_profitability.py @@ -208,4 +208,4 @@ def get_columns(): "options": "Currency", "width": 80 } - ] \ No newline at end of file + ] diff --git a/erpnext/projects/web_form/tasks/tasks.js b/erpnext/projects/web_form/tasks/tasks.js index 699703c5792..ffc5e984253 100644 --- a/erpnext/projects/web_form/tasks/tasks.js +++ b/erpnext/projects/web_form/tasks/tasks.js @@ -1,3 +1,3 @@ frappe.ready(function() { // bind events here -}) \ No newline at end of file +}) diff --git a/erpnext/projects/web_form/tasks/tasks.py b/erpnext/projects/web_form/tasks/tasks.py index e97f36d04b4..e5a94048be1 100644 --- a/erpnext/projects/web_form/tasks/tasks.py +++ b/erpnext/projects/web_form/tasks/tasks.py @@ -6,7 +6,7 @@ def get_context(context): if frappe.form_dict.project: context.parents = [{'title': frappe.form_dict.project, 'route': '/projects?project='+ frappe.form_dict.project}] context.success_url = "/projects?project=" + frappe.form_dict.project - + elif context.doc and context.doc.get('project'): context.parents = [{'title': context.doc.project, 'route': '/projects?project='+ context.doc.project}] context.success_url = "/projects?project=" + context.doc.project diff --git a/erpnext/public/images/erpnext-favicon.svg b/erpnext/public/images/erpnext-favicon.svg index a3ac3bb2ce2..6bc6b2c2db1 100644 --- a/erpnext/public/images/erpnext-favicon.svg +++ b/erpnext/public/images/erpnext-favicon.svg @@ -2,4 +2,4 @@ - \ No newline at end of file + diff --git a/erpnext/public/images/erpnext-logo.svg b/erpnext/public/images/erpnext-logo.svg index a3ac3bb2ce2..6bc6b2c2db1 100644 --- a/erpnext/public/images/erpnext-logo.svg +++ b/erpnext/public/images/erpnext-logo.svg @@ -2,4 +2,4 @@ - \ No newline at end of file + diff --git a/erpnext/public/images/pos.svg b/erpnext/public/images/pos.svg index 3d12d9cb86e..90714e9491d 100644 --- a/erpnext/public/images/pos.svg +++ b/erpnext/public/images/pos.svg @@ -1,4 +1,4 @@ - \ No newline at end of file + diff --git a/erpnext/public/js/education/assessment_result_tool.html b/erpnext/public/js/education/assessment_result_tool.html index b591010ec86..f7d1ab39fcf 100644 --- a/erpnext/public/js/education/assessment_result_tool.html +++ b/erpnext/public/js/education/assessment_result_tool.html @@ -69,4 +69,4 @@ {% endfor %} - \ No newline at end of file + diff --git a/erpnext/public/js/education/student_button.html b/erpnext/public/js/education/student_button.html index 3cf259216a7..b64c73a43c7 100644 --- a/erpnext/public/js/education/student_button.html +++ b/erpnext/public/js/education/student_button.html @@ -1,12 +1,12 @@
                                                            -
                                                            \ No newline at end of file + diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js index 519cfcac72b..9f7f29ad72b 100644 --- a/erpnext/public/js/erpnext.bundle.js +++ b/erpnext/public/js/erpnext.bundle.js @@ -24,4 +24,3 @@ import "./telephony"; import "./templates/call_link.html"; // import { sum } from 'frappe/public/utils/util.js' - diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index b2f7afe53f3..0d79b10c041 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -176,5 +176,3 @@ function get_filters() { return filters; } - - diff --git a/erpnext/public/js/hierarchy-chart.bundle.js b/erpnext/public/js/hierarchy-chart.bundle.js index 26ab6d92b9d..02703139dd9 100644 --- a/erpnext/public/js/hierarchy-chart.bundle.js +++ b/erpnext/public/js/hierarchy-chart.bundle.js @@ -1,3 +1,3 @@ import "./hierarchy_chart/hierarchy_chart_desktop.js"; import "./hierarchy_chart/hierarchy_chart_mobile.js"; -import "./templates/node_card.html"; \ No newline at end of file +import "./templates/node_card.html"; diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index da050abc6e4..23ec2fdb849 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -597,4 +597,4 @@ erpnext.HierarchyChart = class { $(path).remove(); }); } -}; \ No newline at end of file +}; diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index bd7946a1e13..b1b78c05174 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -548,4 +548,4 @@ erpnext.HierarchyChartMobile = class { $(`path[data-parent="${node_parent}"]`).remove(); this.add_connector(node_parent, node_id); } -}; \ No newline at end of file +}; diff --git a/erpnext/public/js/hub/components/ReviewArea.vue b/erpnext/public/js/hub/components/ReviewArea.vue index 5e4e439f3d0..aa83bb0e465 100644 --- a/erpnext/public/js/hub/components/ReviewArea.vue +++ b/erpnext/public/js/hub/components/ReviewArea.vue @@ -137,4 +137,4 @@ export default { } } } - \ No newline at end of file + diff --git a/erpnext/public/js/hub/components/ReviewTimelineItem.vue b/erpnext/public/js/hub/components/ReviewTimelineItem.vue index f0fe001973c..d0e83f3b1cd 100644 --- a/erpnext/public/js/hub/components/ReviewTimelineItem.vue +++ b/erpnext/public/js/hub/components/ReviewTimelineItem.vue @@ -51,4 +51,3 @@ export default { } } - diff --git a/erpnext/public/js/hub/pages/FeaturedItems.vue b/erpnext/public/js/hub/pages/FeaturedItems.vue index 63ae7e99bbd..8380b2b2c0b 100644 --- a/erpnext/public/js/hub/pages/FeaturedItems.vue +++ b/erpnext/public/js/hub/pages/FeaturedItems.vue @@ -69,7 +69,7 @@ export default { const item_name = this.items.filter(item => item.hub_item_name === hub_item_name); - alert_message = __('{0} removed. {1}', [item_name, + alert_message = __('{0} removed. {1}', [item_name, `${__('Undo')}`]); alert = frappe.show_alert(alert_message, grace_period / 1000, { diff --git a/erpnext/public/js/hub/pages/Publish.vue b/erpnext/public/js/hub/pages/Publish.vue index 96fa0aae4e5..ecba4b1e5a8 100644 --- a/erpnext/public/js/hub/pages/Publish.vue +++ b/erpnext/public/js/hub/pages/Publish.vue @@ -78,7 +78,7 @@ export default { empty_state_message: __('No Items selected yet. Browse and click on items below to publish.'), valid_items_instruction: __('Only items with an image and description can be published. Please update them if an item in your inventory does not appear.'), last_sync_message: (hub.settings.last_sync_datetime) - ? __('Last sync was {0}.', [`${comment_when(hub.settings.last_sync_datetime)}`]) + + ? __('Last sync was {0}.', [`${comment_when(hub.settings.last_sync_datetime)}`]) + ` ${__('See your Published Items.')}` : '' }; diff --git a/erpnext/public/js/hub/pages/Seller.vue b/erpnext/public/js/hub/pages/Seller.vue index c0903c64c37..3c9b800f4a0 100644 --- a/erpnext/public/js/hub/pages/Seller.vue +++ b/erpnext/public/js/hub/pages/Seller.vue @@ -24,7 +24,7 @@
                                                            - {{ item_container_heading }} + {{ item_container_heading }} Customize your Featured Items @@ -160,7 +160,7 @@ export default { ]; setTimeout(() => this.init_seller_traffic_chart(), 1); - + }); }, diff --git a/erpnext/public/js/hub/vue-plugins.js b/erpnext/public/js/hub/vue-plugins.js index 6e6a7cb5989..4912d684991 100644 --- a/erpnext/public/js/hub/vue-plugins.js +++ b/erpnext/public/js/hub/vue-plugins.js @@ -55,4 +55,4 @@ const handleImage = (el, src) => { Vue.filter('striphtml', function (text) { return strip_html(text || ''); -}); \ No newline at end of file +}); diff --git a/erpnext/public/js/leaflet/leaflet.draw.js b/erpnext/public/js/leaflet/leaflet.draw.js index 4352f7025b7..26f1e19da5a 100755 --- a/erpnext/public/js/leaflet/leaflet.draw.js +++ b/erpnext/public/js/leaflet/leaflet.draw.js @@ -140,4 +140,4 @@ e.on("click", this._removeLayer, this) }, _disableLayerDelete: function(t) { var e = t.layer || t.target || t; e.off("click", this._removeLayer, this), this._deletedLayers.removeLayer(e) }, _removeLayer: function(t) { var e = t.layer || t.target || t; this._deletableLayers.removeLayer(e), this._deletedLayers.addLayer(e) }, _onMouseMove: function(t) { this._tooltip.updatePosition(t.latlng) }, _hasAvailableLayers: function() { return 0 !== this._deletableLayers.getLayers().length } }) -}(window, document); \ No newline at end of file +}(window, document); diff --git a/erpnext/public/js/leaflet/leaflet.js b/erpnext/public/js/leaflet/leaflet.js index 41d9bb9ed4c..91dd3d434c7 100755 --- a/erpnext/public/js/leaflet/leaflet.js +++ b/erpnext/public/js/leaflet/leaflet.js @@ -768,4 +768,4 @@ r = this._locateOptions; if (r.setView) { var a = this.getBoundsZoom(s); this.setView(n, r.maxZoom ? Math.min(a, r.maxZoom) : a) } var h = { latlng: n, bounds: s, timestamp: t.timestamp }; for (var l in t.coords) "number" == typeof t.coords[l] && (h[l] = t.coords[l]); this.fire("locationfound", h) } }) -}(window, document); \ No newline at end of file +}(window, document); diff --git a/erpnext/public/js/projects/timer.js b/erpnext/public/js/projects/timer.js index 26be997d482..0e5c0d3720e 100644 --- a/erpnext/public/js/projects/timer.js +++ b/erpnext/public/js/projects/timer.js @@ -159,4 +159,4 @@ erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) { $btn_complete.hide(); $btn_start.show(); } -}; \ No newline at end of file +}; diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index 6f5d67c7462..38e1eb5156c 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -147,7 +147,7 @@ erpnext.setup.slides_settings = [ } // Validate bank name - if(me.values.bank_account) { + if(me.values.bank_account) { frappe.call({ async: false, method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.validate_bank_account", diff --git a/erpnext/public/js/stock_analytics.js b/erpnext/public/js/stock_analytics.js index dfe2c88ea82..a343c3402af 100644 --- a/erpnext/public/js/stock_analytics.js +++ b/erpnext/public/js/stock_analytics.js @@ -204,4 +204,3 @@ erpnext.StockAnalytics = class StockAnalytics extends erpnext.StockGridReport { frappe.set_route("query-report", "Stock Ledger"); } }; - diff --git a/erpnext/public/js/templates/item_quick_entry.html b/erpnext/public/js/templates/item_quick_entry.html index 6a5f36da77c..e5e78690622 100644 --- a/erpnext/public/js/templates/item_quick_entry.html +++ b/erpnext/public/js/templates/item_quick_entry.html @@ -1,3 +1,3 @@
                                                            {{ __("Variant Attributes") }}
                                                            -
                                                            \ No newline at end of file +
                                                            diff --git a/erpnext/public/js/templates/item_selector.html b/erpnext/public/js/templates/item_selector.html index 58fb26c0e4c..86a15f49072 100644 --- a/erpnext/public/js/templates/item_selector.html +++ b/erpnext/public/js/templates/item_selector.html @@ -34,4 +34,4 @@ {% if ((i % 4 === 3) || (i===data.length - 1)) { %}{% } %} {% endfor %} - \ No newline at end of file + diff --git a/erpnext/public/js/templates/node_card.html b/erpnext/public/js/templates/node_card.html index fb94df85ed8..4cb6ee03c0c 100644 --- a/erpnext/public/js/templates/node_card.html +++ b/erpnext/public/js/templates/node_card.html @@ -30,4 +30,4 @@ - \ No newline at end of file + diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index 96e181788e3..bb23f1512b9 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -100,4 +100,4 @@ erpnext.accounts.dimensions = { }); } } -}; \ No newline at end of file +}; diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index a66d6474e0d..57d5e8414ae 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -310,4 +310,4 @@ display: flex; flex-direction: column; align-items: center; -} \ No newline at end of file +} diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss index 5962859be5a..490a7c4af73 100644 --- a/erpnext/public/scss/shopping_cart.scss +++ b/erpnext/public/scss/shopping_cart.scss @@ -483,4 +483,3 @@ body.product-page { border: 1px solid var(--dark-border-color); } } - diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss index f4325c03f5b..9ea84160342 100644 --- a/erpnext/public/scss/website.scss +++ b/erpnext/public/scss/website.scss @@ -67,4 +67,4 @@ .card-body > .card-title { line-height: 1.3; } -} \ No newline at end of file +} diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.js b/erpnext/quality_management/doctype/quality_action/quality_action.js index e216a7539c8..b44f2a20344 100644 --- a/erpnext/quality_management/doctype/quality_action/quality_action.js +++ b/erpnext/quality_management/doctype/quality_action/quality_action.js @@ -3,4 +3,4 @@ frappe.ui.form.on('Quality Action', { -}); \ No newline at end of file +}); diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.py b/erpnext/quality_management/doctype/quality_action/quality_action.py index d6fa5051ee6..02401ba689d 100644 --- a/erpnext/quality_management/doctype/quality_action/quality_action.py +++ b/erpnext/quality_management/doctype/quality_action/quality_action.py @@ -8,4 +8,4 @@ from frappe.model.document import Document class QualityAction(Document): def validate(self): - self.status = 'Open' if any([d.status=='Open' for d in self.resolutions]) else 'Completed' \ No newline at end of file + self.status = 'Open' if any([d.status=='Open' for d in self.resolutions]) else 'Completed' diff --git a/erpnext/quality_management/doctype/quality_action/test_quality_action.py b/erpnext/quality_management/doctype/quality_action/test_quality_action.py index 24b97ca3a09..98d665f3910 100644 --- a/erpnext/quality_management/doctype/quality_action/test_quality_action.py +++ b/erpnext/quality_management/doctype/quality_action/test_quality_action.py @@ -8,4 +8,4 @@ import unittest class TestQualityAction(unittest.TestCase): # quality action has no code - pass \ No newline at end of file + pass diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py index 5a8ec73cfe1..d3e96cf2d94 100644 --- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py +++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py @@ -21,4 +21,3 @@ class QualityFeedback(Document): self.document_type ='User' self.document_name = frappe.session.user self.set_parameters() - diff --git a/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py b/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py index b3eed103836..afed14b6ad0 100644 --- a/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py +++ b/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py @@ -7,4 +7,4 @@ import frappe import unittest class TestQualityFeedbackTemplate(unittest.TestCase): - pass \ No newline at end of file + pass diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.py b/erpnext/quality_management/doctype/quality_goal/quality_goal.py index f3fe986d539..3e616b75ceb 100644 --- a/erpnext/quality_management/doctype/quality_goal/quality_goal.py +++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.py @@ -9,4 +9,4 @@ from frappe.model.document import Document class QualityGoal(Document): def validate(self): - pass \ No newline at end of file + pass diff --git a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py index f61d6e581d7..0e135b50212 100644 --- a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py +++ b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py @@ -22,4 +22,4 @@ def get_quality_goal(): objectives = [ dict(objective = 'Check test cases', target='100', uom='Percent') ] - )).insert() \ No newline at end of file + )).insert() diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.py b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.py index f8de22958bd..9e453ebfc2e 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.py +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.py @@ -6,4 +6,4 @@ from __future__ import unicode_literals from frappe.model.document import Document class QualityMeeting(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js b/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js index ff85c84dc9d..5fd1b30eb45 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js @@ -8,4 +8,4 @@ frappe.listview_settings['Quality Meeting'] = { return [__("Close"), "green", ",status=,Close"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py index 754bccb06e0..6bf4c179c6b 100644 --- a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py +++ b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py @@ -8,4 +8,4 @@ import unittest class TestQualityMeeting(unittest.TestCase): # nothing to test - pass \ No newline at end of file + pass diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js index ac876229ecb..fd2b6a4eaa0 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js @@ -19,4 +19,4 @@ frappe.ui.form.on('Quality Procedure', { }; }); } -}); \ No newline at end of file +}); diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index 53f4e6c70fe..117db0012ba 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -77,4 +77,4 @@ def add_node(): if args.parent_quality_procedure == 'All Quality Procedures': args.parent_quality_procedure = None - return frappe.get_doc(args).insert() \ No newline at end of file + return frappe.get_doc(args).insert() diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js index eeb4cf617c3..2851fcc5969 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js @@ -31,4 +31,4 @@ frappe.treeview_settings["Quality Procedure"] = { onload: function(treeview) { treeview.make_tree(); }, -}; \ No newline at end of file +}; diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py index 36bdf26acf5..4fa7734bc68 100644 --- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py @@ -47,4 +47,4 @@ def create_procedure(): processes = [ dict(process_description = 'Test Step 1') ] - )).insert() \ No newline at end of file + )).insert() diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.js b/erpnext/quality_management/doctype/quality_review/quality_review.js index 67371bfc5c6..0e6b7034101 100644 --- a/erpnext/quality_management/doctype/quality_review/quality_review.js +++ b/erpnext/quality_management/doctype/quality_review/quality_review.js @@ -22,4 +22,4 @@ frappe.ui.form.on('Quality Review', { } }); }, -}); \ No newline at end of file +}); diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.py b/erpnext/quality_management/doctype/quality_review/quality_review.py index e3a8b073f0f..34cc890e219 100644 --- a/erpnext/quality_management/doctype/quality_review/quality_review.py +++ b/erpnext/quality_management/doctype/quality_review/quality_review.py @@ -61,4 +61,4 @@ def get_quarter(month): if month in ["January", "April", "July", "October"]: return True else: - return False \ No newline at end of file + return False diff --git a/erpnext/quality_management/doctype/quality_review/quality_review_list.js b/erpnext/quality_management/doctype/quality_review/quality_review_list.js index e2eb31b55a3..b0be783de56 100644 --- a/erpnext/quality_management/doctype/quality_review/quality_review_list.js +++ b/erpnext/quality_management/doctype/quality_review/quality_review_list.js @@ -9,4 +9,4 @@ frappe.listview_settings['Quality Review'] = { return [__("Action Initialised"), "red", "action,=,Action Initialised"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/quality_management/doctype/quality_review/test_quality_review.py b/erpnext/quality_management/doctype/quality_review/test_quality_review.py index a7d92da8ace..161ecd01ef1 100644 --- a/erpnext/quality_management/doctype/quality_review/test_quality_review.py +++ b/erpnext/quality_management/doctype/quality_review/test_quality_review.py @@ -19,4 +19,4 @@ class TestQualityReview(unittest.TestCase): self.assertEqual(quality_goal.objectives[0].target, quality_review.reviews[0].target) quality_review.delete() - quality_goal.delete() \ No newline at end of file + quality_goal.delete() diff --git a/erpnext/regional/address_template/setup.py b/erpnext/regional/address_template/setup.py index 9f318de3451..1b4087d77ba 100644 --- a/erpnext/regional/address_template/setup.py +++ b/erpnext/regional/address_template/setup.py @@ -10,7 +10,7 @@ def set_up_address_templates(default_country=None): def get_address_templates(): """ Return country and path for all HTML files in this directory. - + Returns a list of dicts. """ def country(file_name): diff --git a/erpnext/regional/address_template/templates/germany.html b/erpnext/regional/address_template/templates/germany.html index 7fa4c32612a..25c9c9d32e6 100644 --- a/erpnext/regional/address_template/templates/germany.html +++ b/erpnext/regional/address_template/templates/germany.html @@ -3,6 +3,6 @@ {% if country in ["Germany", "Deutschland"] %} {{ pincode }} {{ city }} {% else %} - {{ pincode }} {{ city | upper }}
                                                            + {{ pincode }} {{ city | upper }}
                                                            {{ country | upper }} {% endif %} diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py index c24ad886ea1..4f6b3eca7a6 100644 --- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py +++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py @@ -11,4 +11,3 @@ class EInvoiceSettings(Document): def validate(self): if self.enable and not self.credentials: frappe.throw(_('You must add atleast one credentials to be able to use E Invoicing.')) - diff --git a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.js b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.js index 7ff4de48639..347fdfe61b6 100644 --- a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.js +++ b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.js @@ -25,4 +25,4 @@ frappe.ui.form.on('GST HSN Code', { }); } } -}); \ No newline at end of file +}); diff --git a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py index 86cd4d1545d..4791dc26753 100644 --- a/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py +++ b/erpnext/regional/doctype/gst_hsn_code/gst_hsn_code.py @@ -30,4 +30,4 @@ def update_item_document(items, taxes): 'tax_category': tax.tax_category, 'valid_from': tax.valid_from }) - item_to_be_updated.save() \ No newline at end of file + item_to_be_updated.save() diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html index 3b6a45a3b42..f3fc60fdb63 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.html @@ -294,4 +294,4 @@ text-align: right; } - \ No newline at end of file + diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js index c2d6edfc773..5918ec8b316 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js @@ -43,4 +43,4 @@ frappe.ui.form.on('Import Supplier Invoice', { } } -}); \ No newline at end of file +}); diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py index ad60db05595..656c3296e58 100644 --- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py +++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.py @@ -13,7 +13,7 @@ class LowerDeductionCertificate(Document): def validate(self): self.validate_dates() self.validate_supplier_against_section_code() - + def validate_dates(self): if getdate(self.valid_upto) < getdate(self.valid_from): frappe.throw(_("Valid Upto date cannot be before Valid From date")) @@ -44,4 +44,4 @@ class LowerDeductionCertificate(Document): return True elif getdate(self.valid_from) <= valid_from and valid_upto <= getdate(self.valid_upto): return True - return False \ No newline at end of file + return False diff --git a/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py b/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py index c478b0f3228..41b42036687 100644 --- a/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py +++ b/erpnext/regional/doctype/tax_exemption_80g_certificate/test_tax_exemption_80g_certificate.py @@ -98,4 +98,4 @@ def create_80g_certificate(args): certificate.update(args) - return certificate \ No newline at end of file + return certificate diff --git a/erpnext/regional/germany/utils/datev/datev_constants.py b/erpnext/regional/germany/utils/datev/datev_constants.py index 63f9a777bb5..be3d7a3e542 100644 --- a/erpnext/regional/germany/utils/datev/datev_constants.py +++ b/erpnext/regional/germany/utils/datev/datev_constants.py @@ -455,7 +455,7 @@ ACCOUNT_NAME_COLUMNS = [ "Konto", # Account name "Kontenbeschriftung", - # Language of the account name + # Language of the account name # "de-DE" or "en-GB" "Sprach-ID" ] diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js index 8ad30fa9106..348f0c6feed 100644 --- a/erpnext/regional/india/e_invoice/einvoice.js +++ b/erpnext/regional/india/e_invoice/einvoice.js @@ -289,4 +289,4 @@ const show_einvoice_preview = (frm, einvoice) => { } } }); -}; \ No newline at end of file +}; diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index fa7e88d3a15..765b51f435f 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -388,7 +388,7 @@ def validate_totals(einvoice): frappe.throw(_('Total Taxable Value of the items is not equal to the Invoice Net Total. Please check item taxes / discounts for any correction.')) if abs( - flt(value_details['TotInvVal']) + flt(value_details['Discount']) - + flt(value_details['TotInvVal']) + flt(value_details['Discount']) - flt(value_details['OthChrg']) - flt(value_details['RndOffAmt']) - total_item_value) > 1: frappe.throw(_('Total Value of the items is not equal to the Invoice Grand Total. Please check item taxes / discounts for any correction.')) diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js index d3b7ea3b1a0..5f6dcdeb922 100644 --- a/erpnext/regional/india/taxes.js +++ b/erpnext/regional/india/taxes.js @@ -49,4 +49,3 @@ erpnext.setup_auto_gst_taxation = (doctype) => { } }); } - diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index a152797a5d4..949733e0ad8 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -845,7 +845,7 @@ def get_depreciation_amount(asset, depreciable_value, row): else: depreciation_amount = (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / (date_diff(asset.to_date, asset.available_for_use_date) / 365) - + else: rate_of_depreciation = row.rate_of_depreciation # if its the first depreciation @@ -862,7 +862,7 @@ def get_depreciation_amount(asset, depreciable_value, row): return depreciation_amount def set_item_tax_from_hsn_code(item): - if not item.taxes and item.gst_hsn_code: + if not item.taxes and item.gst_hsn_code: hsn_doc = frappe.get_doc("GST HSN Code", item.gst_hsn_code) for tax in hsn_doc.taxes: @@ -870,4 +870,4 @@ def set_item_tax_from_hsn_code(item): 'item_tax_template': tax.item_tax_template, 'tax_category': tax.tax_category, 'valid_from': tax.valid_from - }) \ No newline at end of file + }) diff --git a/erpnext/regional/italy/__init__.py b/erpnext/regional/italy/__init__.py index ef1d5822ba1..4932f660ca5 100644 --- a/erpnext/regional/italy/__init__.py +++ b/erpnext/regional/italy/__init__.py @@ -76,4 +76,4 @@ state_codes = {'Siracusa': 'SR', 'Bologna': 'BO', 'Grosseto': 'GR', 'Caserta': ' 'Cagliari': 'CA', 'Siena': 'SI', 'Vibo Valentia': 'VV', 'Reggio Calabria': 'RC', 'Ascoli Piceno': 'AP', 'Carbonia-Iglesias': 'CI', 'Oristano': 'OR', 'Asti': 'AT', 'Ravenna': 'RA', 'Vicenza': 'VI', 'Savona': 'SV', 'Biella': 'BI', 'Rimini': 'RN', 'Agrigento': 'AG', 'Prato': 'PO', 'Cuneo': 'CN', 'Cosenza': 'CS', 'Livorno or Leghorn': 'LI', 'Sondrio': 'SO', 'Cremona': 'CR', 'Isernia': 'IS', 'Trento': 'TN', 'Terni': 'TR', 'Bolzano/Bozen': 'BZ', - 'Parma': 'PR', 'Varese': 'VA', 'Venezia': 'VE', 'Sassari': 'SS', 'Arezzo': 'AR'} \ No newline at end of file + 'Parma': 'PR', 'Varese': 'VA', 'Venezia': 'VE', 'Sassari': 'SS', 'Arezzo': 'AR'} diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index a5ca7eee5d4..86aed2ef814 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -202,7 +202,7 @@ def get_transactions(filters, as_dict=1): FROM `tabGL Entry` gl /* Kontonummer */ - left join `tabAccount` acc + left join `tabAccount` acc on gl.account = acc.name left join `tabCustomer` cus @@ -218,7 +218,7 @@ def get_transactions(filters, as_dict=1): and par.parenttype = gl.party_type and par.company = %(company)s - WHERE gl.company = %(company)s + WHERE gl.company = %(company)s AND DATE(gl.posting_date) >= %(from_date)s AND DATE(gl.posting_date) <= %(to_date)s {} diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py index 47acf291a39..66ffceae539 100644 --- a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py +++ b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py @@ -54,53 +54,53 @@ def get_columns(): "width": 0 }, { - "fieldtype": "Link", - "fieldname": "name", + "fieldtype": "Link", + "fieldname": "name", "label": _("Sales Invoice"), "options": "Sales Invoice", "width": 140 }, - { - "fieldtype": "Data", - "fieldname": "einvoice_status", - "label": _("Status"), + { + "fieldtype": "Data", + "fieldname": "einvoice_status", + "label": _("Status"), "width": 100 }, - { + { "fieldtype": "Link", "fieldname": "customer", "options": "Customer", "label": _("Customer") }, - { + { "fieldtype": "Check", "fieldname": "is_return", "label": _("Is Return"), "width": 85 }, { - "fieldtype": "Data", - "fieldname": "ack_no", - "label": "Ack. No.", + "fieldtype": "Data", + "fieldname": "ack_no", + "label": "Ack. No.", "width": 145 }, - { - "fieldtype": "Data", - "fieldname": "ack_date", - "label": "Ack. Date", + { + "fieldtype": "Data", + "fieldname": "ack_date", + "label": "Ack. Date", "width": 165 }, { - "fieldtype": "Data", - "fieldname": "irn", + "fieldtype": "Data", + "fieldname": "irn", "label": _("IRN No."), "width": 250 }, { "fieldtype": "Currency", - "options": "Company:company:default_currency", - "fieldname": "base_grand_total", + "options": "Company:company:default_currency", + "fieldname": "base_grand_total", "label": _("Grand Total"), "width": 120 } - ] \ No newline at end of file + ] diff --git a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js index 67297f757ca..d7e3ac9a5d3 100644 --- a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js +++ b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js @@ -41,7 +41,7 @@ frappe.query_reports["Electronic Invoice Register"] = { var w = window.open( frappe.urllib.get_full_url( - "/api/method/erpnext.regional.italy.utils.export_invoices?" + "/api/method/erpnext.regional.italy.utils.export_invoices?" + "filters=" + JSON.stringify(reportview.get_filter_values()) ) ); diff --git a/erpnext/regional/report/eway_bill/eway_bill.py b/erpnext/regional/report/eway_bill/eway_bill.py index 5b9896be2a1..4f777fcf7e3 100644 --- a/erpnext/regional/report/eway_bill/eway_bill.py +++ b/erpnext/regional/report/eway_bill/eway_bill.py @@ -388,4 +388,4 @@ def get_columns(): }, ] - return columns \ No newline at end of file + return columns diff --git a/erpnext/regional/report/gst_purchase_register/gst_purchase_register.js b/erpnext/regional/report/gst_purchase_register/gst_purchase_register.js index 2b4359a7493..bbcd355d13b 100644 --- a/erpnext/regional/report/gst_purchase_register/gst_purchase_register.js +++ b/erpnext/regional/report/gst_purchase_register/gst_purchase_register.js @@ -4,4 +4,4 @@ {% include "erpnext/accounts/report/purchase_register/purchase_register.js" %} -frappe.query_reports["GST Purchase Register"] = frappe.query_reports["Purchase Register"] \ No newline at end of file +frappe.query_reports["GST Purchase Register"] = frappe.query_reports["Purchase Register"] diff --git a/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py b/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py index 7274e0accea..12e9676b4ba 100644 --- a/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py +++ b/erpnext/regional/report/gst_purchase_register/gst_purchase_register.py @@ -21,4 +21,3 @@ def execute(filters=None): 'export_type', 'ecommerce_gstin' ]) - diff --git a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py index 59389ce3269..1adddbdae57 100644 --- a/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py +++ b/erpnext/regional/report/hsn_wise_summary_of_outward_supplies/hsn_wise_summary_of_outward_supplies.py @@ -285,5 +285,3 @@ def get_hsn_wise_json_data(filters, report_data): count +=1 return data - - diff --git a/erpnext/regional/report/india_gst_common/india_gst_common.js b/erpnext/regional/report/india_gst_common/india_gst_common.js index 49606013946..bddc32096f1 100644 --- a/erpnext/regional/report/india_gst_common/india_gst_common.js +++ b/erpnext/regional/report/india_gst_common/india_gst_common.js @@ -18,4 +18,4 @@ function fetch_gstins(report) { company_gstins.df.options = [""]; company_gstins.refresh(); } -} \ No newline at end of file +} diff --git a/erpnext/regional/report/irs_1099/irs_1099.py b/erpnext/regional/report/irs_1099/irs_1099.py index 4e57ff7ea37..f67d622fdf8 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.py +++ b/erpnext/regional/report/irs_1099/irs_1099.py @@ -52,7 +52,7 @@ def execute(filters=None): AND gl.party_type = "Supplier" AND gl.company = %(company)s {conditions} - + GROUP BY gl.party diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js index 29c7dbf43c6..bb75238b8c0 100644 --- a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js +++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.js @@ -4,4 +4,4 @@ frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { frappe.query_reports["Professional Tax Deductions"] = erpnext.salary_slip_deductions_report_filters; -}); \ No newline at end of file +}); diff --git a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py index acde68a942b..54808e59e1a 100644 --- a/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py +++ b/erpnext/regional/report/professional_tax_deductions/professional_tax_deductions.py @@ -69,4 +69,4 @@ def get_data(filters): data.append(employee) - return data \ No newline at end of file + return data diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js index b4dc28d177d..a91a30796bc 100644 --- a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.js @@ -4,4 +4,4 @@ frappe.require("assets/erpnext/js/salary_slip_deductions_report_filters.js", function() { frappe.query_reports["Provident Fund Deductions"] = erpnext.salary_slip_deductions_report_filters; -}); \ No newline at end of file +}); diff --git a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py index 597072c53a1..82423f005cc 100644 --- a/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py +++ b/erpnext/regional/report/provident_fund_deductions/provident_fund_deductions.py @@ -165,4 +165,4 @@ def get_years(): if not year_list: year_list = [getdate().year] - return "\n".join(str(year) for year in year_list) \ No newline at end of file + return "\n".join(str(year) for year in year_list) diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.html b/erpnext/regional/report/uae_vat_201/uae_vat_201.html index d9b9968d90c..7328f3f218e 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.html +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.html @@ -74,4 +74,4 @@ {% } %} - \ No newline at end of file + diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.py b/erpnext/regional/report/vat_audit_report/vat_audit_report.py index 292605ef13d..17aca17afda 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -189,7 +189,7 @@ class VATAuditReport(object): row["posting_date"] = formatdate(inv_data.get("posting_date"), "dd-mm-yyyy") row["voucher_type"] = doctype row["voucher_no"] = inv - row["party_type"] = "Customer" if doctype == "Sales Invoice" else "Supplier" + row["party_type"] = "Customer" if doctype == "Sales Invoice" else "Supplier" row["party"] = inv_data.get("party") row["remarks"] = inv_data.get("remarks") row["gross_amount"]= item_details[0].get("gross_amount") diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py index 4657ff833dd..8a75987c3d7 100644 --- a/erpnext/regional/south_africa/setup.py +++ b/erpnext/regional/south_africa/setup.py @@ -24,7 +24,7 @@ def make_custom_fields(update=True): 'Sales Invoice Item': is_zero_rated, 'Purchase Invoice Item': is_zero_rated } - + create_custom_fields(custom_fields, update=update) def add_permissions(): @@ -36,7 +36,7 @@ def add_permissions(): add_permission(doctype, role, 0) update_permission_property(doctype, role, 0, 'write', 1) update_permission_property(doctype, role, 0, 'create', 1) - + if not frappe.db.get_value('Custom Role', dict(report="VAT Audit Report")): frappe.get_doc(dict( @@ -47,4 +47,4 @@ def add_permissions(): dict(role='Accounts Manager'), dict(role='Auditor') ] - )).insert() \ No newline at end of file + )).insert() diff --git a/erpnext/regional/turkey/setup.py b/erpnext/regional/turkey/setup.py index ebf3b2bee1f..2396aab91f5 100644 --- a/erpnext/regional/turkey/setup.py +++ b/erpnext/regional/turkey/setup.py @@ -1,4 +1,4 @@ from __future__ import unicode_literals def setup(company=None, patch=True): - pass \ No newline at end of file + pass diff --git a/erpnext/restaurant/doctype/restaurant/restaurant_dashboard.py b/erpnext/restaurant/doctype/restaurant/restaurant_dashboard.py index ec62ba22b4d..adce5c73352 100644 --- a/erpnext/restaurant/doctype/restaurant/restaurant_dashboard.py +++ b/erpnext/restaurant/doctype/restaurant/restaurant_dashboard.py @@ -14,4 +14,4 @@ def get_data(): 'items': ['Restaurant Reservation', 'Sales Invoice'] } ] - } \ No newline at end of file + } diff --git a/erpnext/restaurant/doctype/restaurant/test_restaurant.js b/erpnext/restaurant/doctype/restaurant/test_restaurant.js index 26de5d04aaa..8fe4e7b84d5 100644 --- a/erpnext/restaurant/doctype/restaurant/test_restaurant.js +++ b/erpnext/restaurant/doctype/restaurant/test_restaurant.js @@ -18,7 +18,7 @@ QUnit.test("test: Restaurant", function (assert) { frappe.run_serially([ // insert a new Restaurant - () => frappe.tests.setup_doctype('Customer', customer), + () => frappe.tests.setup_doctype('Customer', customer), () => { return frappe.tests.make('Restaurant', [ // values to be set diff --git a/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py b/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py index 83020b6cca8..952c46769b7 100644 --- a/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py +++ b/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py @@ -57,5 +57,3 @@ class RestaurantMenu(Document): price_list.save() return price_list - - diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 28494662673..2f06e988804 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -160,4 +160,3 @@ frappe.ui.form.on("Customer", { } }); - diff --git a/erpnext/selling/doctype/customer/regional/india.js b/erpnext/selling/doctype/customer/regional/india.js index edb83838b61..cad9a27ace6 100644 --- a/erpnext/selling/doctype/customer/regional/india.js +++ b/erpnext/selling/doctype/customer/regional/india.js @@ -1,3 +1,3 @@ {% include "erpnext/regional/india/party.js" %} -erpnext.setup_gst_reminder_button('Customer') \ No newline at end of file +erpnext.setup_gst_reminder_button('Customer') diff --git a/erpnext/selling/doctype/industry_type/industry_type.js b/erpnext/selling/doctype/industry_type/industry_type.js index 3878a791db6..3680906057f 100644 --- a/erpnext/selling/doctype/industry_type/industry_type.js +++ b/erpnext/selling/doctype/industry_type/industry_type.js @@ -1,13 +1,13 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - + //--------- ONLOAD ------------- cur_frm.cscript.onload = function(doc, cdt, cdn) { - + } cur_frm.cscript.refresh = function(doc, cdt, cdn) { - -} \ No newline at end of file + +} diff --git a/erpnext/selling/doctype/industry_type/industry_type.py b/erpnext/selling/doctype/industry_type/industry_type.py index 65b17e976a6..7a30d6524a0 100644 --- a/erpnext/selling/doctype/industry_type/industry_type.py +++ b/erpnext/selling/doctype/industry_type/industry_type.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class IndustryType(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/selling/doctype/industry_type/test_industry_type.py b/erpnext/selling/doctype/industry_type/test_industry_type.py index 1246a241c7e..ebc6366155e 100644 --- a/erpnext/selling/doctype/industry_type/test_industry_type.py +++ b/erpnext/selling/doctype/industry_type/test_industry_type.py @@ -4,4 +4,4 @@ from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Industry Type') \ No newline at end of file +test_records = frappe.get_test_records('Industry Type') diff --git a/erpnext/selling/doctype/installation_note_item/installation_note_item.py b/erpnext/selling/doctype/installation_note_item/installation_note_item.py index 681b8171e2c..7e1205231bb 100644 --- a/erpnext/selling/doctype/installation_note_item/installation_note_item.py +++ b/erpnext/selling/doctype/installation_note_item/installation_note_item.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class InstallationNoteItem(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/selling/doctype/product_bundle/test_product_bundle.js b/erpnext/selling/doctype/product_bundle/test_product_bundle.js index ba5ba0dc3ba..0dc90ec2114 100644 --- a/erpnext/selling/doctype/product_bundle/test_product_bundle.js +++ b/erpnext/selling/doctype/product_bundle/test_product_bundle.js @@ -33,4 +33,3 @@ QUnit.test("test sales order", function(assert) { () => done() ]); }); - diff --git a/erpnext/selling/doctype/quotation/quotation_dashboard.py b/erpnext/selling/doctype/quotation/quotation_dashboard.py index f1ac951ef94..d1bb788937b 100644 --- a/erpnext/selling/doctype/quotation/quotation_dashboard.py +++ b/erpnext/selling/doctype/quotation/quotation_dashboard.py @@ -17,4 +17,4 @@ def get_data(): 'items': ['Auto Repeat'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js index aeb5d1b9eb7..b59bb0510e8 100644 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js +++ b/erpnext/selling/doctype/quotation/tests/test_quotation_with_discount_on_grand_total.js @@ -41,4 +41,3 @@ QUnit.test("test quotation with additional discount in grand total", function(as () => done() ]); }); - diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js index e7349e3201c..f5172fbae2e 100644 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js +++ b/erpnext/selling/doctype/quotation/tests/test_quotation_with_item_wise_discount.js @@ -35,4 +35,3 @@ QUnit.test("test quotation with item wise discount", function(assert) { () => done() ]); }); - diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js index 5b4224dfe9a..0d340997ad9 100644 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js +++ b/erpnext/selling/doctype/quotation/tests/test_quotation_with_margin.js @@ -33,4 +33,3 @@ QUnit.test("test quotation with margin", function(assert) { () => done() ]); }); - diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js index 50b8a8396d7..84be56f4605 100644 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js +++ b/erpnext/selling/doctype/quotation/tests/test_quotation_with_multi_uom.js @@ -36,4 +36,3 @@ QUnit.test("test quotation with multi uom", function(assert) { () => done() ]); }); - diff --git a/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js b/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js index ac7ed65ec02..5e21f817573 100644 --- a/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js +++ b/erpnext/selling/doctype/quotation/tests/test_quotation_with_taxes_and_charges.js @@ -38,4 +38,3 @@ QUnit.test("test quotation with taxes and charges", function(assert) { () => done() ]); }); - diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py index 05a760de273..2a71c27009f 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py +++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py @@ -41,4 +41,4 @@ def get_data(): 'items': ['Payment Entry', 'Payment Request', 'Journal Entry'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index a0a21eef5a3..d685fbff82b 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -1222,7 +1222,7 @@ class TestSalesOrder(unittest.TestCase): def test_so_cancellation_when_si_drafted(self): """ Test to check if Sales Order gets cancelled if Sales Invoice is in Draft state - Expected result: sales order should not get cancelled + Expected result: sales order should not get cancelled """ so = make_sales_order() so.submit() @@ -1250,7 +1250,7 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.payment_terms_template, si.payment_terms_template) compare_payment_schedules(self, so, si) - automatically_fetch_payment_terms(enable=0) + automatically_fetch_payment_terms(enable=0) def automatically_fetch_payment_terms(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js index 74268685079..9eebfdaf21a 100644 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js +++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_margin.js @@ -35,4 +35,3 @@ QUnit.test("test sales order with margin", function(assert) { () => done() ]); }); - diff --git a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js index 8e0538511af..be76c49f845 100644 --- a/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js +++ b/erpnext/selling/doctype/sales_order/tests/test_sales_order_with_multiple_delivery_date.js @@ -56,4 +56,4 @@ QUnit.test("test: Sales Order", function (assert) { }, () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.py b/erpnext/selling/doctype/sales_order_item/sales_order_item.py index 27f303d43b1..62afef3e170 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.py +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.py @@ -10,4 +10,4 @@ class SalesOrderItem(Document): pass def on_doctype_update(): - frappe.db.add_index("Sales Order Item", ["item_code", "warehouse"]) \ No newline at end of file + frappe.db.add_index("Sales Order Item", ["item_code", "warehouse"]) diff --git a/erpnext/selling/doctype/sales_team/sales_team.py b/erpnext/selling/doctype/sales_team/sales_team.py index 1832108399f..28bea254d68 100644 --- a/erpnext/selling/doctype/sales_team/sales_team.py +++ b/erpnext/selling/doctype/sales_team/sales_team.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class SalesTeam(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.js b/erpnext/selling/doctype/selling_settings/selling_settings.js index 95a4243fb45..d8d30515f8f 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.js +++ b/erpnext/selling/doctype/selling_settings/selling_settings.js @@ -28,4 +28,4 @@ frappe.tour['Selling Settings'] = [ title: "Delivery Note Required for Sales Invoice Creation", description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Sales Invoice without creating a Delivery Note first. This configuration can be overridden for a particular Customer by enabling the 'Allow Sales Invoice Creation Without Delivery Note' checkbox in the Customer master.") } -]; \ No newline at end of file +]; diff --git a/erpnext/selling/doctype/sms_center/sms_center.py b/erpnext/selling/doctype/sms_center/sms_center.py index d142d16248f..87846a84d30 100644 --- a/erpnext/selling/doctype/sms_center/sms_center.py +++ b/erpnext/selling/doctype/sms_center/sms_center.py @@ -83,4 +83,3 @@ class SMSCenter(Document): receiver_list = self.get_receiver_nos() if receiver_list: send_sms(receiver_list, cstr(self.message)) - diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index 8d1f112dc28..03c46bb2ae0 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -146,7 +146,7 @@ def filter_service_items(items): if not item['is_stock_item']: if not frappe.db.exists('Product Bundle', item['item_code']): items.remove(item) - + return items def get_conditions(search_term): diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index c827368dbf5..e61a634aaee 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -525,7 +525,7 @@ erpnext.PointOfSale.Controller = class { } } else { - if (!this.frm.doc.customer) + if (!this.frm.doc.customer) return this.raise_customer_selection_alert(); const { item_code, batch_no, serial_no, rate } = item; @@ -549,7 +549,7 @@ erpnext.PointOfSale.Controller = class { await this.check_stock_availability(item_row, value, this.frm.doc.set_warehouse); await this.trigger_new_item_events(item_row); - + this.update_cart_html(item_row); if (this.item_details.$component.is(':visible')) @@ -708,4 +708,3 @@ erpnext.PointOfSale.Controller = class { .catch(e => console.log(e)); } }; - diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js index a4a4b0e0ed2..9d8338e5fed 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -973,7 +973,7 @@ erpnext.PointOfSale.ItemCart = class { load_invoice() { const frm = this.events.get_frm(); - + this.attach_refresh_field_event(frm); this.fetch_customer_details(frm.doc.customer).then(() => { diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js index 6a4d3d5214d..d899c5c19b4 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_details.js +++ b/erpnext/selling/page/point_of_sale/pos_item_details.js @@ -65,7 +65,7 @@ erpnext.PointOfSale.ItemDetails = class { // if item is null or highlighted cart item is clicked twice const hide_item_details = !Boolean(item) || !current_item_changed; - + this.events.toggle_item_selector(!hide_item_details); this.toggle_component(!hide_item_details); @@ -127,7 +127,7 @@ erpnext.PointOfSale.ItemDetails = class { this.$item_price.html(format_currency(price_list_rate, this.currency)); if (!this.hide_images && image) { this.$item_image.html( - `${frappe.get_abbr(item_name)}${qty_to_display}
                                                            - ${frappe.get_abbr(item.item_name)}"; } diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.py b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.py index e41011fba2c..87ed5a8ea21 100644 --- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.py +++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.py @@ -9,4 +9,3 @@ def execute(filters=None): data = [] return get_data_column(filters, "Sales Partner") - diff --git a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py index 53560285b3f..f07293d8ec2 100644 --- a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py +++ b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.py @@ -165,4 +165,4 @@ def get_conditions(filters, date_field): `tabItem Group` where lft >= %s and rgt <= %s)""" % (lft, rgt) - return conditions \ No newline at end of file + return conditions diff --git a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py index 0c849096116..9917d72af86 100644 --- a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py +++ b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.py @@ -101,7 +101,7 @@ def get_columns(filters): def get_entries(filters): date_field = filters["doc_type"] == "Sales Order" and "transaction_date" or "posting_date" - + conditions, values = get_conditions(filters, date_field) entries = frappe.db.sql(""" select @@ -111,7 +111,7 @@ def get_entries(filters): `tab%s` dt, `tabSales Team` st where st.parent = dt.name and st.parenttype = %s - and dt.docstatus = 1 %s order by dt.name desc,st.sales_person + and dt.docstatus = 1 %s order by dt.name desc,st.sales_person """ %(date_field, filters["doc_type"], '%s', conditions), tuple([filters["doc_type"]] + values), as_dict=1) @@ -138,5 +138,3 @@ def get_conditions(filters, date_field): values.append(filters["to_date"]) return " and ".join(conditions), values - - diff --git a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js index a8e2fad3734..2b8443627d5 100644 --- a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js +++ b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js @@ -47,9 +47,9 @@ frappe.query_reports["Sales Person Target Variance Based On Item Group"] = { ], "formatter": function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - + if (column.fieldname.includes('variance')) { - + if (data[column.fieldname] < 0) { value = "" + value + ""; } diff --git a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.py b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.py index 5166cc808e9..ea9bbab0c72 100644 --- a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.py +++ b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.py @@ -8,4 +8,4 @@ from erpnext.selling.report.sales_partner_target_variance_based_on_item_group.it def execute(filters=None): data = [] - return get_data_column(filters, "Sales Person") \ No newline at end of file + return get_data_column(filters, "Sales Person") diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js index b236151bad9..e269f02d0ce 100644 --- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js +++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js @@ -67,4 +67,4 @@ frappe.query_reports["Sales Person-wise Transaction Summary"] = { default: 0, }, ] -} \ No newline at end of file +} diff --git a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js index 263391a7f72..9f3d255e662 100644 --- a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js +++ b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js @@ -47,9 +47,9 @@ frappe.query_reports["Territory Target Variance Based On Item Group"] = { ], "formatter": function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - + if (column.fieldname.includes('variance')) { - + if (data[column.fieldname] < 0) { value = "" + value + ""; } diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index f515baf31bc..22bf3fc94fd 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -26,7 +26,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran } }; }); - } + } setup_queries() { var me = this; @@ -85,7 +85,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran refresh() { super.refresh(); - + frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'} this.frm.toggle_display("customer_name", diff --git a/erpnext/setup/default_energy_point_rules.py b/erpnext/setup/default_energy_point_rules.py index 94f5aa488dc..8dbccc497b7 100644 --- a/erpnext/setup/default_energy_point_rules.py +++ b/erpnext/setup/default_energy_point_rules.py @@ -55,4 +55,3 @@ def get_default_energy_point_rules(): 'points': rule.get('points'), 'user_field': rule.get('user_field') or 'owner' } for doctype, rule in doctype_rule_map.items()] - diff --git a/erpnext/setup/default_success_action.py b/erpnext/setup/default_success_action.py index b8b09cbc53f..827839f8b75 100644 --- a/erpnext/setup/default_success_action.py +++ b/erpnext/setup/default_success_action.py @@ -24,4 +24,3 @@ def get_default_success_action(): 'first_success_message': get_first_success_message(doctype), 'next_actions': 'new\nprint\nemail' } for doctype in doctype_list] - diff --git a/erpnext/setup/doctype/brand/brand.js b/erpnext/setup/doctype/brand/brand.js index 3878a791db6..3680906057f 100644 --- a/erpnext/setup/doctype/brand/brand.js +++ b/erpnext/setup/doctype/brand/brand.js @@ -1,13 +1,13 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - + //--------- ONLOAD ------------- cur_frm.cscript.onload = function(doc, cdt, cdn) { - + } cur_frm.cscript.refresh = function(doc, cdt, cdn) { - -} \ No newline at end of file + +} diff --git a/erpnext/setup/doctype/brand/brand.py b/erpnext/setup/doctype/brand/brand.py index 12839d18aea..a8d1cf8ff2d 100644 --- a/erpnext/setup/doctype/brand/brand.py +++ b/erpnext/setup/doctype/brand/brand.py @@ -21,4 +21,4 @@ def get_brand_defaults(item, company): row.pop("name") return row - return frappe._dict() \ No newline at end of file + return frappe._dict() diff --git a/erpnext/setup/doctype/brand/test_brand.py b/erpnext/setup/doctype/brand/test_brand.py index 265d2fe577e..25ed86ef1dd 100644 --- a/erpnext/setup/doctype/brand/test_brand.py +++ b/erpnext/setup/doctype/brand/test_brand.py @@ -4,4 +4,4 @@ from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Brand') \ No newline at end of file +test_records = frappe.get_test_records('Brand') diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index d05541b6344..8f83d3cd73a 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -313,4 +313,3 @@ var disbale_coa_fields = function(frm, bool=true) { frm.set_df_property("chart_of_accounts", "read_only", bool); frm.set_df_property("existing_company", "read_only", bool); } - diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 95cbf5150cd..54c67538aef 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -108,7 +108,7 @@ class Company(NestedSet): frappe.flags.country_change = True self.create_default_accounts() self.create_default_warehouses() - + if not frappe.db.get_value("Cost Center", {"is_group": 0, "company": self.name}): self.create_default_cost_center() diff --git a/erpnext/setup/doctype/company/company_dashboard.py b/erpnext/setup/doctype/company/company_dashboard.py index 9b483dd55e5..2d760284e5a 100644 --- a/erpnext/setup/doctype/company/company_dashboard.py +++ b/erpnext/setup/doctype/company/company_dashboard.py @@ -37,4 +37,4 @@ def get_data(): 'items': ['Project'] } ] - } \ No newline at end of file + } diff --git a/erpnext/setup/doctype/company/company_tree.js b/erpnext/setup/doctype/company/company_tree.js index 19b276c77db..160481cc952 100644 --- a/erpnext/setup/doctype/company/company_tree.js +++ b/erpnext/setup/doctype/company/company_tree.js @@ -30,4 +30,4 @@ frappe.treeview_settings["Company"] = { onload: function(treeview) { treeview.make_tree(); } -}; \ No newline at end of file +}; diff --git a/erpnext/setup/doctype/company/test_company.py b/erpnext/setup/doctype/company/test_company.py index e1c803a038b..1b7fd4fd5c8 100644 --- a/erpnext/setup/doctype/company/test_company.py +++ b/erpnext/setup/doctype/company/test_company.py @@ -130,4 +130,3 @@ def create_test_lead_in_company(company): lead.company = company lead.save() return lead.name - diff --git a/erpnext/setup/doctype/company/tests/test_company.js b/erpnext/setup/doctype/company/tests/test_company.js index 8c0b609775a..b568494c84a 100644 --- a/erpnext/setup/doctype/company/tests/test_company.js +++ b/erpnext/setup/doctype/company/tests/test_company.js @@ -22,4 +22,4 @@ QUnit.test("Test: Company [SetUp]", function (assert) { 'chart of cost centers created'), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/setup/doctype/company/tests/test_company_production.js b/erpnext/setup/doctype/company/tests/test_company_production.js index bf6e5405b42..a4c1e2e7dea 100644 --- a/erpnext/setup/doctype/company/tests/test_company_production.js +++ b/erpnext/setup/doctype/company/tests/test_company_production.js @@ -16,4 +16,4 @@ QUnit.test("Test: Company", function (assert) { () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/setup/doctype/customer_group/customer_group.py b/erpnext/setup/doctype/customer_group/customer_group.py index 68e1ccb6356..c06669b16b4 100644 --- a/erpnext/setup/doctype/customer_group/customer_group.py +++ b/erpnext/setup/doctype/customer_group/customer_group.py @@ -30,4 +30,4 @@ def get_parent_customer_groups(customer_group): order by lft asc""", (lft, rgt), as_dict=True) def on_doctype_update(): - frappe.db.add_index("Customer Group", ["lft", "rgt"]) \ No newline at end of file + frappe.db.add_index("Customer Group", ["lft", "rgt"]) diff --git a/erpnext/setup/doctype/customer_group/customer_group_tree.js b/erpnext/setup/doctype/customer_group/customer_group_tree.js index b52c79c497b..d50e9c8835c 100644 --- a/erpnext/setup/doctype/customer_group/customer_group_tree.js +++ b/erpnext/setup/doctype/customer_group/customer_group_tree.js @@ -1,3 +1,3 @@ frappe.treeview_settings["Customer Group"] = { ignore_fields:["parent_customer_group"] -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/customer_group/test_customer_group.py b/erpnext/setup/doctype/customer_group/test_customer_group.py index ec1af7a6765..ec90b376cdc 100644 --- a/erpnext/setup/doctype/customer_group/test_customer_group.py +++ b/erpnext/setup/doctype/customer_group/test_customer_group.py @@ -7,4 +7,4 @@ test_ignore = ["Price List"] import frappe -test_records = frappe.get_test_records('Customer Group') \ No newline at end of file +test_records = frappe.get_test_records('Customer Group') diff --git a/erpnext/setup/doctype/email_digest/email_digest.js b/erpnext/setup/doctype/email_digest/email_digest.js index 2e415af2828..c2c2710b025 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.js +++ b/erpnext/setup/doctype/email_digest/email_digest.js @@ -28,4 +28,4 @@ frappe.ui.form.on("Email Digest", { }); } } -}); \ No newline at end of file +}); diff --git a/erpnext/setup/doctype/email_digest/quotes.py b/erpnext/setup/doctype/email_digest/quotes.py index 95afe974b29..5451ee1daff 100644 --- a/erpnext/setup/doctype/email_digest/quotes.py +++ b/erpnext/setup/doctype/email_digest/quotes.py @@ -32,4 +32,3 @@ def get_random_quote(): ] return random.choice(quotes) - diff --git a/erpnext/setup/doctype/email_digest/templates/default.html b/erpnext/setup/doctype/email_digest/templates/default.html index 4ee4b0ff166..666301a643e 100644 --- a/erpnext/setup/doctype/email_digest/templates/default.html +++ b/erpnext/setup/doctype/email_digest/templates/default.html @@ -180,8 +180,8 @@
                                                            {% endif %} - - + + {% if purchase_orders_items_overdue_list %}

                                                            {{ _("Purchase Order Items not received on time") }}

                                                            @@ -254,6 +254,6 @@


                                                            Please take necessary action
                                                            -{% endif %} - +{% endif %} +
                                                            diff --git a/erpnext/setup/doctype/item_group/item_group_tree.js b/erpnext/setup/doctype/item_group/item_group_tree.js index 57afe02d796..b2628f4f4f8 100644 --- a/erpnext/setup/doctype/item_group/item_group_tree.js +++ b/erpnext/setup/doctype/item_group/item_group_tree.js @@ -1,3 +1,3 @@ frappe.treeview_settings["Item Group"] = { ignore_fields:["parent_item_group"] -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/print_heading/print_heading.js b/erpnext/setup/doctype/print_heading/print_heading.js index 3878a791db6..3680906057f 100644 --- a/erpnext/setup/doctype/print_heading/print_heading.js +++ b/erpnext/setup/doctype/print_heading/print_heading.js @@ -1,13 +1,13 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - + //--------- ONLOAD ------------- cur_frm.cscript.onload = function(doc, cdt, cdn) { - + } cur_frm.cscript.refresh = function(doc, cdt, cdn) { - -} \ No newline at end of file + +} diff --git a/erpnext/setup/doctype/print_heading/print_heading.py b/erpnext/setup/doctype/print_heading/print_heading.py index 00dc0f3d919..3d5cd2d6f9e 100644 --- a/erpnext/setup/doctype/print_heading/print_heading.py +++ b/erpnext/setup/doctype/print_heading/print_heading.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class PrintHeading(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/setup/doctype/print_heading/test_print_heading.py b/erpnext/setup/doctype/print_heading/test_print_heading.py index 59455d2b1dd..b2be2e375e4 100644 --- a/erpnext/setup/doctype/print_heading/test_print_heading.py +++ b/erpnext/setup/doctype/print_heading/test_print_heading.py @@ -4,4 +4,4 @@ from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Print Heading') \ No newline at end of file +test_records = frappe.get_test_records('Print Heading') diff --git a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js index 3878a791db6..3680906057f 100644 --- a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js +++ b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js @@ -1,13 +1,13 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - + //--------- ONLOAD ------------- cur_frm.cscript.onload = function(doc, cdt, cdn) { - + } cur_frm.cscript.refresh = function(doc, cdt, cdn) { - -} \ No newline at end of file + +} diff --git a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.py b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.py index 2cc6235b946..42c5a5a54fb 100644 --- a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.py +++ b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class QuotationLostReason(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/setup/doctype/quotation_lost_reason/test_quotation_lost_reason.py b/erpnext/setup/doctype/quotation_lost_reason/test_quotation_lost_reason.py index ff4c7885bc7..f6b30b649b5 100644 --- a/erpnext/setup/doctype/quotation_lost_reason/test_quotation_lost_reason.py +++ b/erpnext/setup/doctype/quotation_lost_reason/test_quotation_lost_reason.py @@ -4,4 +4,4 @@ from __future__ import unicode_literals import frappe -test_records = frappe.get_test_records('Quotation Lost Reason') \ No newline at end of file +test_records = frappe.get_test_records('Quotation Lost Reason') diff --git a/erpnext/setup/doctype/sales_person/sales_person_dashboard.py b/erpnext/setup/doctype/sales_person/sales_person_dashboard.py index 3d0b2ff7f8d..662008ec8db 100644 --- a/erpnext/setup/doctype/sales_person/sales_person_dashboard.py +++ b/erpnext/setup/doctype/sales_person/sales_person_dashboard.py @@ -12,4 +12,4 @@ def get_data(): 'items': ['Sales Order', 'Delivery Note', 'Sales Invoice'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/setup/doctype/sales_person/sales_person_tree.js b/erpnext/setup/doctype/sales_person/sales_person_tree.js index bcdfac926c4..00056fde869 100644 --- a/erpnext/setup/doctype/sales_person/sales_person_tree.js +++ b/erpnext/setup/doctype/sales_person/sales_person_tree.js @@ -9,4 +9,4 @@ frappe.treeview_settings["Sales Person"] = { {fieldtype:'Check', fieldname:'is_group', label:__('Group Node'), description: __("Further nodes can be only created under 'Group' type nodes")} ], -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/supplier_group/supplier_group_tree.js b/erpnext/setup/doctype/supplier_group/supplier_group_tree.js index 0788e2e167a..728793eb25f 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group_tree.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group_tree.js @@ -1,4 +1,4 @@ frappe.treeview_settings["Supplier Group"] = { breadcrumbs: "Buying", ignore_fields:["parent_supplier_group"] -}; \ No newline at end of file +}; diff --git a/erpnext/setup/doctype/target_detail/target_detail.py b/erpnext/setup/doctype/target_detail/target_detail.py index d2e2597cb47..633be45d20b 100644 --- a/erpnext/setup/doctype/target_detail/target_detail.py +++ b/erpnext/setup/doctype/target_detail/target_detail.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class TargetDetail(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js index 3878a791db6..3680906057f 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js @@ -1,13 +1,13 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - + //--------- ONLOAD ------------- cur_frm.cscript.onload = function(doc, cdt, cdn) { - + } cur_frm.cscript.refresh = function(doc, cdt, cdn) { - -} \ No newline at end of file + +} diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py index 372cc6d3e3e..5b00ccbdbb3 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py @@ -24,6 +24,6 @@ def get_terms_and_conditions(template_name, doc): doc = json.loads(doc) terms_and_conditions = frappe.get_doc("Terms and Conditions", template_name) - + if terms_and_conditions.terms: - return frappe.render_template(terms_and_conditions.terms, doc) \ No newline at end of file + return frappe.render_template(terms_and_conditions.terms, doc) diff --git a/erpnext/setup/doctype/territory/territory.js b/erpnext/setup/doctype/territory/territory.js index ceec47ae8c6..3caf814c90b 100644 --- a/erpnext/setup/doctype/territory/territory.js +++ b/erpnext/setup/doctype/territory/territory.js @@ -36,4 +36,4 @@ cur_frm.fields_dict['parent_territory'].get_query = function(doc,cdt,cdn) { ['Territory', 'name', '!=', doc.territory_name] ] } -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/territory/territory.py b/erpnext/setup/doctype/territory/territory.py index 05e8f666cfb..7eefe77495c 100644 --- a/erpnext/setup/doctype/territory/territory.py +++ b/erpnext/setup/doctype/territory/territory.py @@ -24,4 +24,4 @@ class Territory(NestedSet): self.validate_one_root() def on_doctype_update(): - frappe.db.add_index("Territory", ["lft", "rgt"]) \ No newline at end of file + frappe.db.add_index("Territory", ["lft", "rgt"]) diff --git a/erpnext/setup/doctype/territory/territory_tree.js b/erpnext/setup/doctype/territory/territory_tree.js index edd11dfa69b..dadeeef09e9 100644 --- a/erpnext/setup/doctype/territory/territory_tree.js +++ b/erpnext/setup/doctype/territory/territory_tree.js @@ -1,3 +1,3 @@ frappe.treeview_settings["Territory"] = { ignore_fields:["parent_territory"] -} \ No newline at end of file +} diff --git a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py index bbe68369ffd..933a8c3bed8 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py @@ -23,7 +23,7 @@ class TestTransactionDeletionRecord(unittest.TestCase): contains_company = True break self.assertTrue(contains_company) - + def test_no_of_docs_is_correct(self): for i in range(5): create_task('Dunder Mifflin Paper Co') @@ -40,13 +40,13 @@ class TestTransactionDeletionRecord(unittest.TestCase): 'company' : 'Dunder Mifflin Paper Co' }) self.assertEqual(tasks_containing_company, []) - + def create_company(company_name): company = frappe.get_doc({ 'doctype': 'Company', 'company_name': company_name, 'default_currency': 'INR' - }) + }) company.insert(ignore_if_duplicate = True) def create_transaction_deletion_request(company): diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index 20caa15ee41..6a50ef8bbd9 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -4,7 +4,7 @@ frappe.ui.form.on('Transaction Deletion Record', { onload: function(frm) { if (frm.doc.docstatus == 0) { - let doctypes_to_be_ignored_array; + let doctypes_to_be_ignored_array; frappe.call({ method: 'erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.get_doctypes_to_be_ignored', callback: function(r) { @@ -25,15 +25,15 @@ frappe.ui.form.on('Transaction Deletion Record', { frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false); frm.refresh_field('doctypes_to_be_ignored'); } - + }); function populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm) { if (!(frm.doc.doctypes_to_be_ignored)) { var i; - for (i = 0; i < doctypes_to_be_ignored_array.length; i++) { + for (i = 0; i < doctypes_to_be_ignored_array.length; i++) { frm.add_child('doctypes_to_be_ignored', { - doctype_name: doctypes_to_be_ignored_array[i] + doctype_name: doctypes_to_be_ignored_array[i] }); } } diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js index d7175ddac43..c238f18abad 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js @@ -9,4 +9,4 @@ frappe.listview_settings['Transaction Deletion Record'] = { return [__("Completed"), "green"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/setup/doctype/uom/uom.js b/erpnext/setup/doctype/uom/uom.js index 3878a791db6..3680906057f 100644 --- a/erpnext/setup/doctype/uom/uom.js +++ b/erpnext/setup/doctype/uom/uom.js @@ -1,13 +1,13 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - + //--------- ONLOAD ------------- cur_frm.cscript.onload = function(doc, cdt, cdn) { - + } cur_frm.cscript.refresh = function(doc, cdt, cdn) { - -} \ No newline at end of file + +} diff --git a/erpnext/setup/doctype/uom/uom.py b/erpnext/setup/doctype/uom/uom.py index f7f86d67509..404b84b1134 100644 --- a/erpnext/setup/doctype/uom/uom.py +++ b/erpnext/setup/doctype/uom/uom.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class UOM(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/setup/doctype/website_item_group/website_item_group.py b/erpnext/setup/doctype/website_item_group/website_item_group.py index 9ac7df2c667..e416b509b98 100644 --- a/erpnext/setup/doctype/website_item_group/website_item_group.py +++ b/erpnext/setup/doctype/website_item_group/website_item_group.py @@ -9,4 +9,4 @@ import frappe from frappe.model.document import Document class WebsiteItemGroup(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/setup/setup_wizard/operations/sample_data.py b/erpnext/setup/setup_wizard/operations/sample_data.py index c11a3885c9b..c6d9f0851b1 100644 --- a/erpnext/setup/setup_wizard/operations/sample_data.py +++ b/erpnext/setup/setup_wizard/operations/sample_data.py @@ -173,4 +173,4 @@ def test_sample(): frappe.db.sql('delete from tabProject') frappe.db.sql('delete from tabTask') make_projects('Education') - import_notification() \ No newline at end of file + import_notification() diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py index bacada9f5cc..faa25dfbaa2 100644 --- a/erpnext/setup/setup_wizard/operations/taxes_setup.py +++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py @@ -145,7 +145,7 @@ def make_taxes_and_charges_template(company_name, doctype, template): doc = frappe.get_doc(template) - # Data in country wise json is already pre validated, hence validations can be ignored + # Data in country wise json is already pre validated, hence validations can be ignored # Ingone validations to make doctypes faster doc.flags.ignore_links = True doc.flags.ignore_validate = True @@ -177,7 +177,7 @@ def make_item_tax_template(company_name, template): doc = frappe.get_doc(template) - # Data in country wise json is already pre validated, hence validations can be ignored + # Data in country wise json is already pre validated, hence validations can be ignored # Ingone validations to make doctypes faster doc.flags.ignore_links = True doc.flags.ignore_validate = True diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py index c069b90e986..2a497225fbc 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/shopping_cart_settings.py @@ -31,7 +31,7 @@ class ShoppingCartSettings(Document): [self.price_list], "currency") price_list_currency_map = dict(price_list_currency_map) - + # check if all price lists have a currency for price_list, currency in price_list_currency_map.items(): if not currency: diff --git a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py index 75899e121a5..008751e2088 100644 --- a/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py +++ b/erpnext/shopping_cart/doctype/shopping_cart_settings/test_shopping_cart_settings.py @@ -36,7 +36,7 @@ class TestShoppingCartSettings(unittest.TestCase): cart_settings.enabled = 1 if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1}, "name"): self.assertRaises(ShoppingCartSetupError, cart_settings.validate_tax_rule) - + frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 1") -test_dependencies = ["Tax Rule"] \ No newline at end of file +test_dependencies = ["Tax Rule"] diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py index 29617a87485..6c9e531a4d1 100644 --- a/erpnext/shopping_cart/product_info.py +++ b/erpnext/shopping_cart/product_info.py @@ -66,4 +66,4 @@ def set_product_info_for_website(item): item["price_sales_uom"] = product_info.get("price").get("formatted_price_sales_uom") else: item["price_stock_uom"] = "" - item["price_sales_uom"] = "" \ No newline at end of file + item["price_sales_uom"] = "" diff --git a/erpnext/shopping_cart/search.py b/erpnext/shopping_cart/search.py index 63e9fe1b31b..9f674dcebf8 100644 --- a/erpnext/shopping_cart/search.py +++ b/erpnext/shopping_cart/search.py @@ -123,4 +123,4 @@ def remove_document_from_index(path): def build_index_for_all_routes(): search = ProductSearch(INDEX_NAME) - return search.build() \ No newline at end of file + return search.build() diff --git a/erpnext/shopping_cart/utils.py b/erpnext/shopping_cart/utils.py index 3241234af5f..0e1466fd1fa 100644 --- a/erpnext/shopping_cart/utils.py +++ b/erpnext/shopping_cart/utils.py @@ -38,4 +38,4 @@ def check_customer_or_supplier(): if link.link_doctype in ('Customer', 'Supplier'): return link.link_doctype, link.link_name - return 'Customer', None \ No newline at end of file + return 'Customer', None diff --git a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html index 1b3953435e4..1e3d0d069a1 100644 --- a/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html +++ b/erpnext/shopping_cart/web_template/hero_slider/hero_slider.html @@ -82,4 +82,4 @@ \ No newline at end of file + diff --git a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html index 890ae502c82..fe061d5f5f5 100644 --- a/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html +++ b/erpnext/shopping_cart/web_template/item_card_group/item_card_group.html @@ -35,4 +35,4 @@ \ No newline at end of file + diff --git a/erpnext/startup/filters.py b/erpnext/startup/filters.py index ec07329dedf..98210165dfa 100644 --- a/erpnext/startup/filters.py +++ b/erpnext/startup/filters.py @@ -11,4 +11,4 @@ def get_filters_config(): } } - return filters_config \ No newline at end of file + return filters_config diff --git a/erpnext/startup/leaderboard.py b/erpnext/startup/leaderboard.py index 8819a55c0ab..a89435d4866 100644 --- a/erpnext/startup/leaderboard.py +++ b/erpnext/startup/leaderboard.py @@ -202,4 +202,4 @@ def get_date_condition(date_range, field): date_condition = "and {0} between {1} and {2}".format( field, frappe.db.escape(from_date), frappe.db.escape(to_date) ) - return date_condition \ No newline at end of file + return date_condition diff --git a/erpnext/stock/dashboard/item_dashboard.html b/erpnext/stock/dashboard/item_dashboard.html index 1e18969e63e..99698ba69a9 100644 --- a/erpnext/stock/dashboard/item_dashboard.html +++ b/erpnext/stock/dashboard/item_dashboard.html @@ -4,4 +4,4 @@ - \ No newline at end of file + diff --git a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py index ab573e566ac..70b030e48f2 100644 --- a/erpnext/stock/dashboard/warehouse_capacity_dashboard.py +++ b/erpnext/stock/dashboard/warehouse_capacity_dashboard.py @@ -66,4 +66,4 @@ def get_warehouse_capacity_data(filters, start): 'percent_occupied': flt((flt(balance_qty) / flt(entry.stock_capacity)) * 100, 0) }) - return capacity_data \ No newline at end of file + return capacity_data diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js index a4137547f7e..2b9d46e4ab0 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js @@ -11,4 +11,4 @@ frappe.dashboards.chart_sources["Warehouse wise Stock Value"] = { default: frappe.defaults.get_user_default("Company") } ] -}; \ No newline at end of file +}; diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py index 374a34ea7ca..2258532c6fe 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.py @@ -45,4 +45,4 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d "values": datapoints }], "type": "bar" - } \ No newline at end of file + } diff --git a/erpnext/stock/doctype/batch/test_batch.js b/erpnext/stock/doctype/batch/test_batch.js index af7f50ff91e..2d2150b8acd 100644 --- a/erpnext/stock/doctype/batch/test_batch.js +++ b/erpnext/stock/doctype/batch/test_batch.js @@ -20,4 +20,3 @@ QUnit.test("test Batch", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py index 47684d5c6ec..9db5db865f5 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py @@ -30,4 +30,4 @@ def get_data(): 'items': ['Auto Repeat'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/stock/doctype/delivery_note/regional/india.js b/erpnext/stock/doctype/delivery_note/regional/india.js index 5e1ff980009..e853858b60b 100644 --- a/erpnext/stock/doctype/delivery_note/regional/india.js +++ b/erpnext/stock/doctype/delivery_note/regional/india.js @@ -27,4 +27,3 @@ frappe.ui.form.on('Delivery Note', { } } }) - diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.js b/erpnext/stock/doctype/delivery_note/test_delivery_note.js index 3f6e8d15035..76f79894290 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.js @@ -33,4 +33,3 @@ QUnit.test("test delivery note", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 756825e826d..91e7c006eef 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -772,7 +772,7 @@ class TestDeliveryNote(unittest.TestCase): so.submit() dn = create_dn_against_so(so.name, delivered_qty=10) - + si = create_sales_invoice(qty=10, do_not_save=1) si.items[0].delivery_note= dn.name si.items[0].dn_detail = dn.items[0].name diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js b/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js index 21eb35ce378..9f1375f563c 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note_with_margin.js @@ -34,4 +34,3 @@ QUnit.test("test delivery note with margin", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py index 50305957892..8bd381a2ed3 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class DeliveryNoteItem(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.py b/erpnext/stock/doctype/delivery_trip/delivery_trip.py index 9ec28d89814..f76bb87efed 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.py +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.py @@ -406,4 +406,4 @@ def make_expense_claim(source_name, target_doc=None): } }}, target_doc) - return doc \ No newline at end of file + return doc diff --git a/erpnext/stock/doctype/delivery_trip/dispatch_notification_template.html b/erpnext/stock/doctype/delivery_trip/dispatch_notification_template.html index 9c062bc34ca..d12334e355a 100644 --- a/erpnext/stock/doctype/delivery_trip/dispatch_notification_template.html +++ b/erpnext/stock/doctype/delivery_trip/dispatch_notification_template.html @@ -47,4 +47,4 @@ {{ vehicle }} - \ No newline at end of file + diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 614c53abb57..422fe3e4b7a 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1309,4 +1309,4 @@ def on_doctype_update(): @erpnext.allow_regional def set_item_tax_from_hsn_code(item): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/item/regional/india.js b/erpnext/stock/doctype/item/regional/india.js index 77ae51fa341..cceb1ec895f 100644 --- a/erpnext/stock/doctype/item/regional/india.js +++ b/erpnext/stock/doctype/item/regional/india.js @@ -12,4 +12,4 @@ frappe.ui.form.on('Item', { }); } }, -}); \ No newline at end of file +}); diff --git a/erpnext/stock/doctype/item/templates/item.html b/erpnext/stock/doctype/item/templates/item.html index db123090aae..5c42f3b1247 100644 --- a/erpnext/stock/doctype/item/templates/item.html +++ b/erpnext/stock/doctype/item/templates/item.html @@ -4,4 +4,4 @@

                                                            {{ title }}

                                                            {% endblock %} - \ No newline at end of file + diff --git a/erpnext/stock/doctype/item/templates/item_row.html b/erpnext/stock/doctype/item/templates/item_row.html index 2b999819cbb..f81fc1d8743 100644 --- a/erpnext/stock/doctype/item/templates/item_row.html +++ b/erpnext/stock/doctype/item/templates/item_row.html @@ -1,4 +1,4 @@ - \ No newline at end of file + diff --git a/erpnext/stock/doctype/item/tests/test_item.js b/erpnext/stock/doctype/item/tests/test_item.js index 5e3524e5b6d..7f7e72d5c0f 100644 --- a/erpnext/stock/doctype/item/tests/test_item.js +++ b/erpnext/stock/doctype/item/tests/test_item.js @@ -118,4 +118,4 @@ QUnit.test("test: item", function (assert) { ), () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/stock/doctype/item_attribute/test_item_attribute.py b/erpnext/stock/doctype/item_attribute/test_item_attribute.py index 61e53d24a46..07af176a944 100644 --- a/erpnext/stock/doctype/item_attribute/test_item_attribute.py +++ b/erpnext/stock/doctype/item_attribute/test_item_attribute.py @@ -28,4 +28,3 @@ class TestItemAttribute(unittest.TestCase): item_attribute.increment = 0.5 item_attribute.save() - diff --git a/erpnext/stock/doctype/item_customer_detail/item_customer_detail.py b/erpnext/stock/doctype/item_customer_detail/item_customer_detail.py index a9183ce5866..3e4e8500467 100644 --- a/erpnext/stock/doctype/item_customer_detail/item_customer_detail.py +++ b/erpnext/stock/doctype/item_customer_detail/item_customer_detail.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class ItemCustomerDetail(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py index c27d1be7892..939abf8d324 100644 --- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py +++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py @@ -65,4 +65,4 @@ class ItemManufacturer(Document): @frappe.whitelist() def get_item_manufacturer_part_no(item_code, manufacturer): return frappe.db.get_value("Item Manufacturer", - {'item_code': item_code, 'manufacturer': manufacturer}, 'manufacturer_part_no') \ No newline at end of file + {'item_code': item_code, 'manufacturer': manufacturer}, 'manufacturer_part_no') diff --git a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.py b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.py index 92aefc8d9e9..785737b267f 100644 --- a/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.py +++ b/erpnext/stock/doctype/item_quality_inspection_parameter/item_quality_inspection_parameter.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class ItemQualityInspectionParameter(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/item_reorder/item_reorder.py b/erpnext/stock/doctype/item_reorder/item_reorder.py index 0f9c593d36a..5cdaa229565 100644 --- a/erpnext/stock/doctype/item_reorder/item_reorder.py +++ b/erpnext/stock/doctype/item_reorder/item_reorder.py @@ -9,4 +9,4 @@ import frappe from frappe.model.document import Document class ItemReorder(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/item_supplier/item_supplier.py b/erpnext/stock/doctype/item_supplier/item_supplier.py index 1a07f03ec58..5dda535f810 100644 --- a/erpnext/stock/doctype/item_supplier/item_supplier.py +++ b/erpnext/stock/doctype/item_supplier/item_supplier.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class ItemSupplier(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/item_tax/item_tax.py b/erpnext/stock/doctype/item_tax/item_tax.py index 1fe2f454681..7c9e8115758 100644 --- a/erpnext/stock/doctype/item_tax/item_tax.py +++ b/erpnext/stock/doctype/item_tax/item_tax.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class ItemTax(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/item_website_specification/item_website_specification.py b/erpnext/stock/doctype/item_website_specification/item_website_specification.py index 6d0dbad2a5e..e3041cf3eef 100644 --- a/erpnext/stock/doctype/item_website_specification/item_website_specification.py +++ b/erpnext/stock/doctype/item_website_specification/item_website_specification.py @@ -9,4 +9,4 @@ import frappe from frappe.model.document import Document class ItemWebsiteSpecification(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.py b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.py index 0521a7ad1cb..493e8b239a4 100644 --- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.py +++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class LandedCostItem(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.py b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.py index f7ccb9b6e29..38f4eafc3aa 100644 --- a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.py +++ b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class LandedCostPurchaseReceipt(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py index e4458207dbb..0dc396aefac 100644 --- a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py +++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.py @@ -6,4 +6,4 @@ import frappe from frappe.model.document import Document class LandedCostTaxesandCharges(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/material_request/material_request_dashboard.py b/erpnext/stock/doctype/material_request/material_request_dashboard.py index f3e5e5db250..e1e4faf6825 100644 --- a/erpnext/stock/doctype/material_request/material_request_dashboard.py +++ b/erpnext/stock/doctype/material_request/material_request_dashboard.py @@ -20,4 +20,4 @@ def get_data(): 'items': ['Work Order'] } ] - } \ No newline at end of file + } diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request.js b/erpnext/stock/doctype/material_request/tests/test_material_request.js index bf26cd117f8..a2cd03b6495 100644 --- a/erpnext/stock/doctype/material_request/tests/test_material_request.js +++ b/erpnext/stock/doctype/material_request/tests/test_material_request.js @@ -37,4 +37,3 @@ QUnit.test("test material request", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js b/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js index d8b39fe5aaf..6fb55ae02ac 100644 --- a/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js +++ b/erpnext/stock/doctype/material_request/tests/test_material_request_from_bom.js @@ -25,4 +25,3 @@ QUnit.test("test material request get items from BOM", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js index 91b47bac4d9..137079b9838 100644 --- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js +++ b/erpnext/stock/doctype/material_request/tests/test_material_request_type_manufacture.js @@ -27,4 +27,3 @@ QUnit.test("test material request", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js index 050e0f0d1c9..b03a8543c6f 100644 --- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js +++ b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_issue.js @@ -27,4 +27,3 @@ QUnit.test("test material request for issue", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js index d6f9b661414..7c62c2e63a1 100644 --- a/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js +++ b/erpnext/stock/doctype/material_request/tests/test_material_request_type_material_transfer.js @@ -27,4 +27,3 @@ QUnit.test("test material request for transfer", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.py b/erpnext/stock/doctype/material_request_item/material_request_item.py index 16f007f6a20..e0066e65d2c 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.py +++ b/erpnext/stock/doctype/material_request_item/material_request_item.py @@ -12,4 +12,4 @@ class MaterialRequestItem(Document): pass def on_doctype_update(): - frappe.db.add_index("Material Request Item", ["item_code", "warehouse"]) \ No newline at end of file + frappe.db.add_index("Material Request Item", ["item_code", "warehouse"]) diff --git a/erpnext/stock/doctype/packing_slip_item/packing_slip_item.py b/erpnext/stock/doctype/packing_slip_item/packing_slip_item.py index 694ab384bf6..b0a855961f9 100644 --- a/erpnext/stock/doctype/packing_slip_item/packing_slip_item.py +++ b/erpnext/stock/doctype/packing_slip_item/packing_slip_item.py @@ -9,4 +9,4 @@ import frappe from frappe.model.document import Document class PackingSlipItem(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index ee218f2f685..730fd7a829c 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -201,4 +201,4 @@ function get_item_details(item_code, uom=null) { uom }); } -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py index 6e007df5e6b..7c321c450a6 100644 --- a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py +++ b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Stock Entry', 'Delivery Note'] }, ] - } \ No newline at end of file + } diff --git a/erpnext/stock/doctype/price_list/price_list.css b/erpnext/stock/doctype/price_list/price_list.css index 61b069442f8..6832954a811 100644 --- a/erpnext/stock/doctype/price_list/price_list.css +++ b/erpnext/stock/doctype/price_list/price_list.css @@ -4,4 +4,4 @@ .table-grid thead tr { height: 50px; -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/price_list/price_list.js b/erpnext/stock/doctype/price_list/price_list.js index c362b5a765c..9291498e863 100644 --- a/erpnext/stock/doctype/price_list/price_list.js +++ b/erpnext/stock/doctype/price_list/price_list.js @@ -11,4 +11,4 @@ frappe.ui.form.on("Price List", { frappe.set_route("Report", "Item Price"); }, "fa fa-money"); } -}); \ No newline at end of file +}); diff --git a/erpnext/stock/doctype/price_list/price_list.py b/erpnext/stock/doctype/price_list/price_list.py index 33713faf696..10abde17eb2 100644 --- a/erpnext/stock/doctype/price_list/price_list.py +++ b/erpnext/stock/doctype/price_list/price_list.py @@ -62,4 +62,4 @@ def get_price_list_details(price_list): frappe.cache().hset("price_list_details", price_list, price_list_details) - return price_list_details or {} \ No newline at end of file + return price_list_details or {} diff --git a/erpnext/stock/doctype/price_list/test_price_list.py b/erpnext/stock/doctype/price_list/test_price_list.py index 5979c861294..2c287c9033c 100644 --- a/erpnext/stock/doctype/price_list/test_price_list.py +++ b/erpnext/stock/doctype/price_list/test_price_list.py @@ -6,4 +6,4 @@ import frappe # test_ignore = ["Item"] -test_records = frappe.get_test_records('Price List') \ No newline at end of file +test_records = frappe.get_test_records('Price List') diff --git a/erpnext/stock/doctype/price_list/test_price_list_uom.js b/erpnext/stock/doctype/price_list/test_price_list_uom.js index 7fbce7d59d2..3896c0e59ea 100644 --- a/erpnext/stock/doctype/price_list/test_price_list_uom.js +++ b/erpnext/stock/doctype/price_list/test_price_list_uom.js @@ -55,4 +55,4 @@ QUnit.test("test price list with uom dependancy", function(assert) { () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/stock/doctype/purchase_receipt/regional/india.js b/erpnext/stock/doctype/purchase_receipt/regional/india.js index b4f1201f36c..2d982cc1bb3 100644 --- a/erpnext/stock/doctype/purchase_receipt/regional/india.js +++ b/erpnext/stock/doctype/purchase_receipt/regional/india.js @@ -1,3 +1,3 @@ {% include "erpnext/regional/india/taxes.js" %} -erpnext.setup_auto_gst_taxation('Purchase Receipt'); \ No newline at end of file +erpnext.setup_auto_gst_taxation('Purchase Receipt'); diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py index 0f50bcd6ea8..315e723fabc 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py @@ -232,4 +232,4 @@ def get_serial_nos_to_allocate(serial_nos, to_allocate): allocated_serial_nos = serial_nos[0: cint(to_allocate)] serial_nos[:] = serial_nos[cint(to_allocate):] # pop out allocated serial nos and modify list return "\n".join(allocated_serial_nos) if allocated_serial_nos else "" - else: return "" \ No newline at end of file + else: return "" diff --git a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py index 86f7dc3e084..0590ae1abeb 100644 --- a/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py +++ b/erpnext/stock/doctype/putaway_rule/test_putaway_rule.py @@ -386,4 +386,4 @@ def create_putaway_rule(**args): if not args.do_not_save: putaway.save() - return putaway \ No newline at end of file + return putaway diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js index f7565fd505c..d08dc3e8b76 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js @@ -81,4 +81,4 @@ frappe.ui.form.on("Quality Inspection", { }); } }, -}); \ No newline at end of file +}); diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.py b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.py index 65188a22c6e..b10fa310d66 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.py +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class QualityInspectionReading(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py index 01d2031b3a4..971b3c29825 100644 --- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py +++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.py @@ -16,4 +16,4 @@ def get_template_details(template): fields=["specification", "value", "acceptance_formula", "numeric", "formula_based_criteria", "min_value", "max_value"], filters={'parenttype': 'Quality Inspection Template', 'parent': template}, - order_by="idx") \ No newline at end of file + order_by="idx") diff --git a/erpnext/stock/doctype/serial_no/test_serial_no.py b/erpnext/stock/doctype/serial_no/test_serial_no.py index b9a58cf43e4..0eccce3a58e 100644 --- a/erpnext/stock/doctype/serial_no/test_serial_no.py +++ b/erpnext/stock/doctype/serial_no/test_serial_no.py @@ -193,4 +193,4 @@ class TestSerialNo(unittest.TestCase): frappe.db.rollback() def tearDown(self): - frappe.db.rollback() \ No newline at end of file + frappe.db.rollback() diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js index ce2906ecbe9..13a17a25913 100644 --- a/erpnext/stock/doctype/shipment/shipment.js +++ b/erpnext/stock/doctype/shipment/shipment.js @@ -150,8 +150,8 @@ frappe.ui.form.on('Shipment', { frm.set_value('pickup_contact_name', ''); frm.set_value('pickup_contact', ''); } - frappe.throw(__("Email or Phone/Mobile of the Contact are mandatory to continue.") - + "
                                                            " + __("Please set Email/Phone for the contact") + frappe.throw(__("Email or Phone/Mobile of the Contact are mandatory to continue.") + + "
                                                            " + __("Please set Email/Phone for the contact") + ` ${contact_name}`); } let contact_display = r.message.contact_display; @@ -244,8 +244,8 @@ frappe.ui.form.on('Shipment', { frm.set_value('pickup_company', ''); frm.set_value('pickup_contact', ''); } - frappe.throw(__("Last Name, Email or Phone/Mobile of the user are mandatory to continue.") + "
                                                            " - + __("Please first set Last Name, Email and Phone for the user") + frappe.throw(__("Last Name, Email or Phone/Mobile of the user are mandatory to continue.") + "
                                                            " + + __("Please first set Last Name, Email and Phone for the user") + ` ${frappe.session.user}`); } let contact_display = r.full_name; diff --git a/erpnext/stock/doctype/shipment/shipment_list.js b/erpnext/stock/doctype/shipment/shipment_list.js index 52b052c81f3..ae6a3c154e8 100644 --- a/erpnext/stock/doctype/shipment/shipment_list.js +++ b/erpnext/stock/doctype/shipment/shipment_list.js @@ -5,4 +5,4 @@ frappe.listview_settings['Shipment'] = { return [__("Booked"), "green"]; } } -}; \ No newline at end of file +}; diff --git a/erpnext/stock/doctype/shipment/test_shipment.py b/erpnext/stock/doctype/shipment/test_shipment.py index 9c3e22f0231..db2f1161743 100644 --- a/erpnext/stock/doctype/shipment/test_shipment.py +++ b/erpnext/stock/doctype/shipment/test_shipment.py @@ -24,7 +24,7 @@ def create_test_delivery_note(): customer = get_shipment_customer() item = get_shipment_item(company.name) posting_date = date.today() + timedelta(days=1) - + create_material_receipt(item, company.name) delivery_note = frappe.new_doc("Delivery Note") delivery_note.company = company.name @@ -73,7 +73,7 @@ def create_test_shipment(delivery_notes = None): shipment.pickup_to = '17:00' shipment.description_of_content = 'unit test entry' for delivery_note in delivery_notes: - shipment.append('shipment_delivery_note', + shipment.append('shipment_delivery_note', { "delivery_note": delivery_note.name } @@ -222,7 +222,7 @@ def create_material_receipt(item, company): ) stock.insert() stock.submit() - + def create_shipment_item(item_name, company_name): item = frappe.new_doc("Item") diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js index 3cf4861ccb8..a87a7fb7fd8 100644 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js +++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue.js @@ -28,4 +28,3 @@ QUnit.test("test material request", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js index aac09c30cd5..cae318d8f2c 100644 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js +++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_issue_with_serialize_item.js @@ -32,4 +32,3 @@ QUnit.test("test material issue", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js index 828738eb6ca..ef0286fe1b9 100644 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js +++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt.js @@ -29,4 +29,3 @@ QUnit.test("test material request", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js index ffd06642bf0..54e1ac81211 100644 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js +++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_receipt_for_serialize_item.js @@ -32,4 +32,3 @@ QUnit.test("test material receipt", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js index cdeb4ab04a7..fac0b4b8922 100644 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js +++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer.js @@ -31,4 +31,3 @@ QUnit.test("test material request", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js index e8b2973c457..9f853072709 100644 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js +++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_material_transfer_for_manufacture.js @@ -31,4 +31,3 @@ QUnit.test("test material Transfer to manufacture", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js index 699634df6d2..20f119ad617 100644 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js +++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_repack.js @@ -39,4 +39,3 @@ QUnit.test("test repack", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js index 770f886d043..8243426032d 100644 --- a/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js +++ b/erpnext/stock/doctype/stock_entry/tests/test_stock_entry_for_subcontract.js @@ -31,4 +31,3 @@ QUnit.test("test material Transfer to manufacture", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py index f9e062f8516..a5623fded23 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class StockEntryDetail(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js index 80001d63fd4..666d2c7144f 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.js @@ -29,4 +29,3 @@ QUnit.test("test Stock Reconciliation", function(assert) { () => done() ]); }); - diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index c192582531a..94b006c8944 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -458,4 +458,3 @@ def set_valuation_method(item_code, valuation_method): }, allow_negative_stock=1) test_dependencies = ["Item", "Warehouse"] - diff --git a/erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.py b/erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.py index 67fe20bd379..fdead205670 100644 --- a/erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.py +++ b/erpnext/stock/doctype/uom_conversion_detail/uom_conversion_detail.py @@ -7,4 +7,4 @@ import frappe from frappe.model.document import Document class UOMConversionDetail(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.js b/erpnext/stock/doctype/warehouse/test_warehouse.js index 8ea280cc59e..850da1ee45f 100644 --- a/erpnext/stock/doctype/warehouse/test_warehouse.js +++ b/erpnext/stock/doctype/warehouse/test_warehouse.js @@ -16,4 +16,4 @@ QUnit.test("test: warehouse", function (assert) { () => done() ]); -}); \ No newline at end of file +}); diff --git a/erpnext/stock/doctype/warehouse/test_warehouse.py b/erpnext/stock/doctype/warehouse/test_warehouse.py index e3981c913e1..6e429a22552 100644 --- a/erpnext/stock/doctype/warehouse/test_warehouse.py +++ b/erpnext/stock/doctype/warehouse/test_warehouse.py @@ -180,4 +180,4 @@ def get_group_stock_account(company, company_abbr=None): if not company_abbr: company_abbr = frappe.get_cached_value("Company", company, 'abbr') group_stock_account = "Current Assets - " + company_abbr - return group_stock_account \ No newline at end of file + return group_stock_account diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js index 1f172504a7f..9243e1ed84f 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.js +++ b/erpnext/stock/doctype/warehouse/warehouse.js @@ -48,11 +48,11 @@ frappe.ui.form.on("Warehouse", { frm.add_custom_button(__('Non-Group to Group'), function() { convert_to_group_or_ledger(frm); }, 'fa fa-retweet', 'btn-default') } - + frm.toggle_enable(['is_group', 'company'], false); frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Warehouse'}; - + frm.fields_dict['parent_warehouse'].get_query = function(doc) { return { filters: { @@ -83,6 +83,6 @@ function convert_to_group_or_ledger(frm){ callback: function(){ frm.refresh(); } - + }) -} \ No newline at end of file +} diff --git a/erpnext/stock/doctype/warehouse/warehouse_tree.js b/erpnext/stock/doctype/warehouse/warehouse_tree.js index 407d7d1ccd5..e9e14c72466 100644 --- a/erpnext/stock/doctype/warehouse/warehouse_tree.js +++ b/erpnext/stock/doctype/warehouse/warehouse_tree.js @@ -24,4 +24,4 @@ frappe.treeview_settings['Warehouse'] = { + '').insertBefore(node.$ul); } } -} \ No newline at end of file +} diff --git a/erpnext/stock/landed_taxes_and_charges_common.js b/erpnext/stock/landed_taxes_and_charges_common.js index f3f61963a88..ff8a69fb033 100644 --- a/erpnext/stock/landed_taxes_and_charges_common.js +++ b/erpnext/stock/landed_taxes_and_charges_common.js @@ -59,4 +59,3 @@ document_list.forEach((doctype) => { } }); }); - diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html index 90112c78a83..de7e38e7d3e 100644 --- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html +++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.html @@ -37,4 +37,4 @@ {% endif %} -{% endfor %} \ No newline at end of file +{% endfor %} diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html index acaf180a903..7ac5e640302 100644 --- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html +++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html @@ -16,4 +16,4 @@ % Occupied - \ No newline at end of file + diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py index 7354eee4130..29689b1a912 100644 --- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py +++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py @@ -24,7 +24,7 @@ def execute(filters=None): data.append([item, item_map[item]["item_name"], item_map[item]["description"], wh, batch, frappe.db.get_value('Batch', batch, 'expiry_date'), qty_dict.expiry_status ]) - + return columns, data @@ -70,7 +70,7 @@ def get_item_warehouse_batch_map(filters, float_precision): "expires_on": None, "expiry_status": None})) qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no] - + expiry_date_unicode = frappe.db.get_value('Batch', d.batch_no, 'expiry_date') qty_dict.expires_on = expiry_date_unicode diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py index 9e5e63e37e2..da593a40d68 100644 --- a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py +++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py @@ -64,7 +64,7 @@ def get_data(filters: Filters) -> Data: assign_self_values(leveled_dict, svd_list) assign_agg_values(leveled_dict) - + data = [] for item in leveled_dict.items(): i = item[1] @@ -160,7 +160,7 @@ def get_row(name:str, value:float, is_bold:int, indent:int) -> Row: if is_bold: item_group = frappe.bold(item_group) return frappe._dict(item_group=item_group, cogs_debit=value, indent=indent) - + def assign_item_groups_to_svd_list(svd_list: SVDList) -> None: ig_map = get_item_groups_map(svd_list) diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.py b/erpnext/stock/report/delayed_item_report/delayed_item_report.py index 4fc4027200d..61306662c0d 100644 --- a/erpnext/stock/report/delayed_item_report/delayed_item_report.py +++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.py @@ -174,4 +174,4 @@ class DelayedItemReport(object): "fieldname": "po_no", "fieldtype": "Data", "width": 100 - }] \ No newline at end of file + }] diff --git a/erpnext/stock/report/delayed_order_report/delayed_order_report.py b/erpnext/stock/report/delayed_order_report/delayed_order_report.py index 79dc5d88219..d9151606884 100644 --- a/erpnext/stock/report/delayed_order_report/delayed_order_report.py +++ b/erpnext/stock/report/delayed_order_report/delayed_order_report.py @@ -87,4 +87,4 @@ class DelayedOrderReport(DelayedItemReport): "fieldname": "po_no", "fieldtype": "Data", "width": 110 - }] \ No newline at end of file + }] diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js index ade004cde42..8a04565c197 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js @@ -6,4 +6,3 @@ frappe.require("assets/erpnext/js/sales_trends_filters.js", function() { filters: erpnext.get_sales_trends_filters() } }); - diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py index 446d3049b71..77fd2ff2447 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py @@ -47,4 +47,4 @@ def get_chart_data(data, filters): ] }, "type" : "bar" - } \ No newline at end of file + } diff --git a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py index cf174c93682..00125e71a9a 100644 --- a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py +++ b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.py @@ -108,4 +108,4 @@ def get_columns(): 'fieldtype': 'Float', 'fieldname': 'differnce', 'width': 110 - }] \ No newline at end of file + }] diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py index e54cf4c66c7..b3b7594ffd7 100644 --- a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py +++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.py @@ -145,4 +145,4 @@ def get_columns(): 'fieldtype': 'Currency', 'fieldname': 'valuation_rate', 'width': 110 - }] \ No newline at end of file + }] diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py index a7243878eb8..c8f60a15d64 100644 --- a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py +++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py @@ -138,4 +138,4 @@ def get_columns(filters): "fieldtype": "Currency", "width": "150" } - ] \ No newline at end of file + ] diff --git a/erpnext/stock/report/item_price_stock/item_price_stock.js b/erpnext/stock/report/item_price_stock/item_price_stock.js index 0bbc61b9dbf..7af1dab6a0b 100644 --- a/erpnext/stock/report/item_price_stock/item_price_stock.js +++ b/erpnext/stock/report/item_price_stock/item_price_stock.js @@ -11,4 +11,4 @@ frappe.query_reports["Item Price Stock"] = { "options": "Item" } ] -} \ No newline at end of file +} diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.py b/erpnext/stock/report/item_shortage_report/item_shortage_report.py index 086d833bbc4..c67eed7e926 100644 --- a/erpnext/stock/report/item_shortage_report/item_shortage_report.py +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.py @@ -158,5 +158,3 @@ def get_columns(): ] return columns - - diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js index c0535bf0efa..173aad6d5a9 100644 --- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js +++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js @@ -29,4 +29,4 @@ frappe.query_reports["Itemwise Recommended Reorder Level"] = { "options": "Brand" } ] -} \ No newline at end of file +} diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js index d16485e8cc6..695efacb694 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js @@ -6,4 +6,3 @@ frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() { filters: erpnext.get_purchase_trends_filters() } }); - diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py index 8227f1548c1..0d96ea6aa8e 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py @@ -48,4 +48,4 @@ def get_chart_data(data, filters): }, "type" : "bar", "colors":["#5e64ff"] - } \ No newline at end of file + } diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py index c3339fd341e..cc3aa3522d8 100644 --- a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py +++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.py @@ -50,4 +50,3 @@ def get_columns(filters): def get_data(filters): return get_stock_ledger_entries(filters, '<=', order="asc") or [] - diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.js b/erpnext/stock/report/stock_ageing/stock_ageing.js index 8495142ba5b..b22788f7a29 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.js +++ b/erpnext/stock/report/stock_ageing/stock_ageing.js @@ -64,4 +64,4 @@ frappe.query_reports["Stock Ageing"] = { "default": 0 } ] -} \ No newline at end of file +} diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index fde934b1339..d62abed91f3 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -208,7 +208,3 @@ def get_chart_data(columns): chart["type"] = "line" return chart - - - - diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py index bfc4471b9af..7e0c0e8ab35 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py @@ -128,4 +128,4 @@ def get_columns(filters): "fieldtype": "Currency", "width": "120" } - ] \ No newline at end of file + ] diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index 808d2791709..7956f2e8648 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -32,7 +32,7 @@ def execute(filters=None): if filters.brand and filters.brand != item.brand: continue - + elif filters.item_group and filters.item_group != item.item_group: continue diff --git a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py index 78e95df9898..fa19eeba58b 100644 --- a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py +++ b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.py @@ -58,14 +58,14 @@ def get_data(warehouse): serial_item_list = frappe.get_all("Item", filters={ 'has_serial_no': True, }, fields=['item_code', 'item_name']) - + status_list = ['Active', 'Expired'] data = [] for item in serial_item_list: - total_serial_no = frappe.db.count("Serial No", + total_serial_no = frappe.db.count("Serial No", filters={"item_code": item.item_code, "status": ("in", status_list), "warehouse": warehouse}) - actual_qty = frappe.db.get_value('Bin', fieldname=['actual_qty'], + actual_qty = frappe.db.get_value('Bin', fieldname=['actual_qty'], filters={"warehouse": warehouse, "item_code": item.item_code}) # frappe.db.get_value returns null if no record exist. @@ -84,4 +84,4 @@ def get_data(warehouse): data.append(row) - return data \ No newline at end of file + return data diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js index cdc9895917c..5b006470756 100644 --- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js +++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js @@ -25,4 +25,4 @@ frappe.query_reports["Supplier-Wise Sales Analytics"] = { "default": frappe.datetime.month_end() }, ] -} \ No newline at end of file +} diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.js b/erpnext/stock/report/total_stock_summary/total_stock_summary.js index 264642856da..90648f1b249 100644 --- a/erpnext/stock/report/total_stock_summary/total_stock_summary.js +++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.js @@ -38,4 +38,4 @@ frappe.query_reports["Total Stock Summary"] = { "reqd": 1 }, ] -} \ No newline at end of file +} diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 9ac1efa268d..d4daacd4ea4 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -145,4 +145,4 @@ frappe.ui.form.on("Issue", { // frm.timeline.wrapper.data("help-article-event-attached", true); // } }, -}); \ No newline at end of file +}); diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 739324f5620..4146e488629 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -182,7 +182,7 @@ class TestFirstResponseTime(TestSetUp): # issue creation and first response are on consecutive days def test_first_response_time_case6(self): """ - Test frt when the issue was created before working hours and the first response is also sent before working hours, but on the next day. + Test frt when the issue was created before working hours and the first response is also sent before working hours, but on the next day. """ issue = create_issue_and_communication(get_datetime("06-28-2021 6:00"), get_datetime("06-29-2021 6:00")) self.assertEqual(issue.first_response_time, 28800.0) @@ -204,7 +204,7 @@ class TestFirstResponseTime(TestSetUp): def test_first_response_time_case9(self): """ Test frt when the issue was created before working hours and the first response is sent on the next day, which is not a work day. - """ + """ issue = create_issue_and_communication(get_datetime("06-25-2021 6:00"), get_datetime("06-26-2021 11:00")) self.assertEqual(issue.first_response_time, 28800.0) @@ -232,7 +232,7 @@ class TestFirstResponseTime(TestSetUp): def test_first_response_time_case13(self): """ Test frt when the issue was created during working hours and the first response is sent on the next day, which is not a work day. - """ + """ issue = create_issue_and_communication(get_datetime("06-25-2021 12:00"), get_datetime("06-26-2021 11:00")) self.assertEqual(issue.first_response_time, 21600.0) @@ -348,7 +348,7 @@ class TestFirstResponseTime(TestSetUp): """ issue = create_issue_and_communication(get_datetime("06-25-2021 20:00"), get_datetime("06-27-2021 11:00")) self.assertEqual(issue.first_response_time, 1.0) - + def create_issue_and_communication(issue_creation, first_responded_on): issue = make_issue(issue_creation, index=1) sender = create_user("test@admin.com") @@ -422,4 +422,4 @@ def create_communication(reference_name, sender, sent_or_received, creation): "creation": creation, "reference_name": reference_name }) - communication.save() \ No newline at end of file + communication.save() diff --git a/erpnext/support/doctype/issue_priority/issue_priority.py b/erpnext/support/doctype/issue_priority/issue_priority.py index 7c8925ebc30..514b6cc26ba 100644 --- a/erpnext/support/doctype/issue_priority/issue_priority.py +++ b/erpnext/support/doctype/issue_priority/issue_priority.py @@ -8,4 +8,4 @@ from frappe import _ from frappe.model.document import Document class IssuePriority(Document): - pass \ No newline at end of file + pass diff --git a/erpnext/support/doctype/issue_priority/test_issue_priority.py b/erpnext/support/doctype/issue_priority/test_issue_priority.py index a7b55f8a74c..618c93ea9d8 100644 --- a/erpnext/support/doctype/issue_priority/test_issue_priority.py +++ b/erpnext/support/doctype/issue_priority/test_issue_priority.py @@ -25,4 +25,4 @@ def insert_priority(name): frappe.get_doc({ "doctype": "Issue Priority", "name": name - }).insert(ignore_permissions=True) \ No newline at end of file + }).insert(ignore_permissions=True) diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 472e96c059e..8c1c1ef0de0 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -246,7 +246,7 @@ def get_active_service_level_agreement_for(doc): filters += [["Service Level Agreement", "default_service_level_agreement", "=", 0]] agreements = frappe.get_all("Service Level Agreement", filters=filters, or_filters=or_filters, fields=["name", "default_priority", "apply_sla_for_resolution", "condition"]) - + # check if the current document on which SLA is to be applied fulfills all the conditions filtered_agreements = [] for agreement in agreements: diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py index f2bd6813965..7e7a405d6e7 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement_dashboard.py @@ -9,4 +9,4 @@ def get_data(): 'items': ['Issue'] } ] - } \ No newline at end of file + } diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 1a5ff27d2a4..a81516ec116 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -511,4 +511,4 @@ def make_lead(creation=None, index=0): "creation": creation, "service_level_agreement_creation": creation, "priority": "Medium" - }).insert(ignore_permissions=True) \ No newline at end of file + }).insert(ignore_permissions=True) diff --git a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py index 922da2b33de..69bf2730d35 100644 --- a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py +++ b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.py @@ -32,4 +32,4 @@ def execute(filters=None): ORDER BY creation_date desc ''', (filters.from_date, filters.to_date)) - return columns, data \ No newline at end of file + return columns, data diff --git a/erpnext/support/report/issue_analytics/issue_analytics.py b/erpnext/support/report/issue_analytics/issue_analytics.py index 3fdb10ddf38..54fce0b3592 100644 --- a/erpnext/support/report/issue_analytics/issue_analytics.py +++ b/erpnext/support/report/issue_analytics/issue_analytics.py @@ -218,4 +218,4 @@ class IssueAnalytics(object): 'datasets': [] }, 'type': 'line' - } \ No newline at end of file + } diff --git a/erpnext/support/report/issue_analytics/test_issue_analytics.py b/erpnext/support/report/issue_analytics/test_issue_analytics.py index 77483198ecc..a9d961a4592 100644 --- a/erpnext/support/report/issue_analytics/test_issue_analytics.py +++ b/erpnext/support/report/issue_analytics/test_issue_analytics.py @@ -22,7 +22,7 @@ class TestIssueAnalytics(unittest.TestCase): if current_month_date.year != last_month_date.year: self.current_month += '_' + str(current_month_date.year) self.last_month += '_' + str(last_month_date.year) - + def test_issue_analytics(self): create_service_level_agreements_for_issues() create_issue_types() @@ -211,4 +211,4 @@ def create_records(): "assign_to": ["test@example.com", "test1@example.com"], "doctype": "Issue", "name": issue.name - }) \ No newline at end of file + }) diff --git a/erpnext/support/report/issue_summary/issue_summary.py b/erpnext/support/report/issue_summary/issue_summary.py index bba25b8bed6..7c4af39f104 100644 --- a/erpnext/support/report/issue_summary/issue_summary.py +++ b/erpnext/support/report/issue_summary/issue_summary.py @@ -362,4 +362,3 @@ class IssueSummary(object): 'datatype': 'Int', } ] - diff --git a/erpnext/support/web_form/issues/issues.js b/erpnext/support/web_form/issues/issues.js index 699703c5792..ffc5e984253 100644 --- a/erpnext/support/web_form/issues/issues.js +++ b/erpnext/support/web_form/issues/issues.js @@ -1,3 +1,3 @@ frappe.ready(function() { // bind events here -}) \ No newline at end of file +}) diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index c00dfa90566..6f8e4116956 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -173,4 +173,3 @@ def get_linked_call_logs(doctype, docname): }) return timeline_contents - diff --git a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js index 1bcc8461323..b80acdb3760 100644 --- a/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js +++ b/erpnext/telephony/doctype/incoming_call_settings/incoming_call_settings.js @@ -99,4 +99,3 @@ frappe.ui.form.on('Incoming Call Settings', { validate_call_schedule(frm.doc.call_handling_schedule); } }); - diff --git a/erpnext/templates/emails/birthday_reminder.html b/erpnext/templates/emails/birthday_reminder.html index 12cdf1ec600..1f57b4969c0 100644 --- a/erpnext/templates/emails/birthday_reminder.html +++ b/erpnext/templates/emails/birthday_reminder.html @@ -22,4 +22,4 @@ {{ reminder_text }}

                                                            {{ message }}

                                                            - \ No newline at end of file + diff --git a/erpnext/templates/emails/daily_project_summary.html b/erpnext/templates/emails/daily_project_summary.html index 8b60830db62..5ccc6101665 100644 --- a/erpnext/templates/emails/daily_project_summary.html +++ b/erpnext/templates/emails/daily_project_summary.html @@ -43,4 +43,4 @@
                                                            -{% endfor %} \ No newline at end of file +{% endfor %} diff --git a/erpnext/templates/emails/daily_work_summary.html b/erpnext/templates/emails/daily_work_summary.html index a22e09cb8de..1764e8f7038 100644 --- a/erpnext/templates/emails/daily_work_summary.html +++ b/erpnext/templates/emails/daily_work_summary.html @@ -52,4 +52,4 @@ -{% endif %} \ No newline at end of file +{% endif %} diff --git a/erpnext/templates/emails/request_for_quotation.html b/erpnext/templates/emails/request_for_quotation.html index 812939a5538..3283987fab0 100644 --- a/erpnext/templates/emails/request_for_quotation.html +++ b/erpnext/templates/emails/request_for_quotation.html @@ -21,4 +21,4 @@

                                                            -{% endif %} \ No newline at end of file +{% endif %} diff --git a/erpnext/templates/emails/training_event.html b/erpnext/templates/emails/training_event.html index 51c232d8e87..8a2414a3c92 100644 --- a/erpnext/templates/emails/training_event.html +++ b/erpnext/templates/emails/training_event.html @@ -11,7 +11,7 @@

                                                            {{_("Update Response")}}

                                                            {% if not self_study %}

                                                            {{_("Please update your status for this training event")}}:

                                                            -
                                                            +
                                                            {% else %}

                                                            {{_("Please confirm once you have completed your training")}}:

                                                            diff --git a/erpnext/templates/generators/item/item_inquiry.js b/erpnext/templates/generators/item/item_inquiry.js index e7db3a368df..4724b681196 100644 --- a/erpnext/templates/generators/item/item_inquiry.js +++ b/erpnext/templates/generators/item/item_inquiry.js @@ -74,4 +74,4 @@ frappe.ready(() => { d.show(); }); -}); \ No newline at end of file +}); diff --git a/erpnext/templates/generators/item/item_specifications.html b/erpnext/templates/generators/item/item_specifications.html index 469a45fd7d4..d4dfa8e591a 100644 --- a/erpnext/templates/generators/item/item_specifications.html +++ b/erpnext/templates/generators/item/item_specifications.html @@ -11,4 +11,4 @@ -{%- endif %} \ No newline at end of file +{%- endif %} diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html index 9050cc388ae..b5f18ba66d1 100644 --- a/erpnext/templates/generators/item_group.html +++ b/erpnext/templates/generators/item_group.html @@ -159,4 +159,4 @@ }); }); -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/generators/job_opening.html b/erpnext/templates/generators/job_opening.html index c562db3c25a..135fb3643d3 100644 --- a/erpnext/templates/generators/job_opening.html +++ b/erpnext/templates/generators/job_opening.html @@ -14,17 +14,17 @@
                                                            {{ description }}
                                                            {% endif %} -{%- if publish_salary_range -%} +{%- if publish_salary_range -%}
                                                            {{_("Salary range per month")}}: {{ frappe.format_value(frappe.utils.flt(lower_range), currency=currency) }} - {{ frappe.format_value(frappe.utils.flt(upper_range), currency=currency) }}
                                                            {% endif %}

                                                            {%- if job_application_route -%} - {{ _("Apply Now") }} {% else %} - {{ _("Apply Now") }} {% endif %} diff --git a/erpnext/templates/generators/student_admission.html b/erpnext/templates/generators/student_admission.html index 8b153448eea..8cc58a0a1f2 100644 --- a/erpnext/templates/generators/student_admission.html +++ b/erpnext/templates/generators/student_admission.html @@ -14,7 +14,7 @@ {%- if introduction -%}

                                                            {{ introduction }}
                                                            -{% endif %} +{% endif %} {%- if doc.enable_admission_application -%}

                                                            diff --git a/erpnext/templates/includes/cart/address_picker_card.html b/erpnext/templates/includes/cart/address_picker_card.html index 2334ea2955d..646210e65f1 100644 --- a/erpnext/templates/includes/cart/address_picker_card.html +++ b/erpnext/templates/includes/cart/address_picker_card.html @@ -9,4 +9,4 @@

                                                            {{ _('Edit') }} - \ No newline at end of file + diff --git a/erpnext/templates/includes/cart/cart_address_picker.html b/erpnext/templates/includes/cart/cart_address_picker.html index 72cc5f51423..66a50ecc9f3 100644 --- a/erpnext/templates/includes/cart/cart_address_picker.html +++ b/erpnext/templates/includes/cart/cart_address_picker.html @@ -1,4 +1,3 @@
                                                            {{ _("Shipping Address") }}
                                                            - diff --git a/erpnext/templates/includes/cart/cart_items_dropdown.html b/erpnext/templates/includes/cart/cart_items_dropdown.html index b2ba4312d6c..5d107fc0d06 100644 --- a/erpnext/templates/includes/cart/cart_items_dropdown.html +++ b/erpnext/templates/includes/cart/cart_items_dropdown.html @@ -9,4 +9,4 @@ {{ d.get_formatted("amount") }} -{% endfor %} \ No newline at end of file +{% endfor %} diff --git a/erpnext/templates/includes/course/macros.html b/erpnext/templates/includes/course/macros.html index c80dca4bcc9..334b5ea200a 100644 --- a/erpnext/templates/includes/course/macros.html +++ b/erpnext/templates/includes/course/macros.html @@ -1 +1 @@ -{% macro back_link(doc) %}&back-to=/courses?course={{ doc.name }}&back-to-title={{ doc.course_name }}{% endmacro %} \ No newline at end of file +{% macro back_link(doc) %}&back-to=/courses?course={{ doc.name }}&back-to-title={{ doc.course_name }}{% endmacro %} diff --git a/erpnext/templates/includes/itemised_tax_breakup.html b/erpnext/templates/includes/itemised_tax_breakup.html index c2f13539cdf..5652bb1dddd 100644 --- a/erpnext/templates/includes/itemised_tax_breakup.html +++ b/erpnext/templates/includes/itemised_tax_breakup.html @@ -43,4 +43,4 @@ {% endfor %} - \ No newline at end of file + diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html index c44bfb53848..be0d47f3715 100644 --- a/erpnext/templates/includes/macros.html +++ b/erpnext/templates/includes/macros.html @@ -120,4 +120,4 @@ {% endif %} -{%- endmacro -%} \ No newline at end of file +{%- endmacro -%} diff --git a/erpnext/templates/includes/navbar/navbar_items.html b/erpnext/templates/includes/navbar/navbar_items.html index 133d99e5eb9..291220629c9 100644 --- a/erpnext/templates/includes/navbar/navbar_items.html +++ b/erpnext/templates/includes/navbar/navbar_items.html @@ -9,4 +9,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/includes/order/order_macros.html b/erpnext/templates/includes/order/order_macros.html index da4fb8c0460..7b3c9a41318 100644 --- a/erpnext/templates/includes/order/order_macros.html +++ b/erpnext/templates/includes/order/order_macros.html @@ -40,4 +40,4 @@ -{% endmacro %} \ No newline at end of file +{% endmacro %} diff --git a/erpnext/templates/includes/projects.css b/erpnext/templates/includes/projects.css index 5a717fc6699..5d9fc50385e 100644 --- a/erpnext/templates/includes/projects.css +++ b/erpnext/templates/includes/projects.css @@ -86,4 +86,4 @@ .progress-hg{ margin-bottom: 30!important; height:2px; -} \ No newline at end of file +} diff --git a/erpnext/templates/includes/projects/project_search_box.html b/erpnext/templates/includes/projects/project_search_box.html index 6f53bae2e80..d7466873dda 100644 --- a/erpnext/templates/includes/projects/project_search_box.html +++ b/erpnext/templates/includes/projects/project_search_box.html @@ -27,4 +27,4 @@ frappe.ready(function() { }); $(".form-search").on("submit", function() { return false; }); }); - \ No newline at end of file + diff --git a/erpnext/templates/includes/salary_slip_log.html b/erpnext/templates/includes/salary_slip_log.html index 107df51dd8b..d36ee6e23bb 100644 --- a/erpnext/templates/includes/salary_slip_log.html +++ b/erpnext/templates/includes/salary_slip_log.html @@ -16,4 +16,4 @@ {% endfor %} - \ No newline at end of file + diff --git a/erpnext/templates/includes/topic/topic_row.html b/erpnext/templates/includes/topic/topic_row.html index 3401bd39371..38d46b7fe00 100644 --- a/erpnext/templates/includes/topic/topic_row.html +++ b/erpnext/templates/includes/topic/topic_row.html @@ -1,4 +1,4 @@ -
                                                            + \ No newline at end of file +
                                                            diff --git a/erpnext/templates/pages/cart_terms.html b/erpnext/templates/pages/cart_terms.html index 521c583cb60..6d84fb86a78 100644 --- a/erpnext/templates/pages/cart_terms.html +++ b/erpnext/templates/pages/cart_terms.html @@ -1,2 +1,2 @@ -
                                                            {{doc.terms}}
                                                            \ No newline at end of file +
                                                            {{doc.terms}}
                                                            diff --git a/erpnext/templates/pages/courses.html b/erpnext/templates/pages/courses.html index 42e7f3e70b7..6592f7a2e5c 100644 --- a/erpnext/templates/pages/courses.html +++ b/erpnext/templates/pages/courses.html @@ -8,4 +8,4 @@

                                                            {{ intro }}

                                                            -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/courses.py b/erpnext/templates/pages/courses.py index c80d8e7d229..92c38f6fcae 100644 --- a/erpnext/templates/pages/courses.py +++ b/erpnext/templates/pages/courses.py @@ -17,4 +17,3 @@ def get_context(context): context.doc = course context.sidebar_title = sidebar_title context.intro = course.course_intro - diff --git a/erpnext/templates/pages/home.css b/erpnext/templates/pages/home.css index cf5476635bd..785d8059ba0 100644 --- a/erpnext/templates/pages/home.css +++ b/erpnext/templates/pages/home.css @@ -6,4 +6,4 @@ padding: 10rem 0; } {% endif %} -/* csslint ignore:end */ \ No newline at end of file +/* csslint ignore:end */ diff --git a/erpnext/templates/pages/home.html b/erpnext/templates/pages/home.html index 2ef9c105347..9a61eabaf8c 100644 --- a/erpnext/templates/pages/home.html +++ b/erpnext/templates/pages/home.html @@ -72,4 +72,4 @@ {{ render_homepage_section(section) }} {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/integrations/gocardless_checkout.html b/erpnext/templates/pages/integrations/gocardless_checkout.html index 7193d755a1e..6072db49ea9 100644 --- a/erpnext/templates/pages/integrations/gocardless_checkout.html +++ b/erpnext/templates/pages/integrations/gocardless_checkout.html @@ -13,4 +13,4 @@ {{ _("Loading Payment System") }}

                                                            -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/integrations/gocardless_checkout.py b/erpnext/templates/pages/integrations/gocardless_checkout.py index 96a0f42a05c..bdef79cfbed 100644 --- a/erpnext/templates/pages/integrations/gocardless_checkout.py +++ b/erpnext/templates/pages/integrations/gocardless_checkout.py @@ -74,4 +74,4 @@ def check_mandate(data, reference_doctype, reference_docname): except Exception as e: frappe.log_error(e, "GoCardless Payment Error") - return {"redirect_to": '/integrations/payment-failed'} \ No newline at end of file + return {"redirect_to": '/integrations/payment-failed'} diff --git a/erpnext/templates/pages/integrations/gocardless_confirmation.html b/erpnext/templates/pages/integrations/gocardless_confirmation.html index 6ba154a06c7..d961c6344af 100644 --- a/erpnext/templates/pages/integrations/gocardless_confirmation.html +++ b/erpnext/templates/pages/integrations/gocardless_confirmation.html @@ -13,4 +13,4 @@ {{ _("Payment Confirmation") }}

                                                            -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/integrations/gocardless_confirmation.py b/erpnext/templates/pages/integrations/gocardless_confirmation.py index cfaa1a15cfc..0b72e9f8b60 100644 --- a/erpnext/templates/pages/integrations/gocardless_confirmation.py +++ b/erpnext/templates/pages/integrations/gocardless_confirmation.py @@ -86,4 +86,4 @@ def create_mandate(data): }).insert(ignore_permissions=True) except Exception: - frappe.log_error(frappe.get_traceback()) \ No newline at end of file + frappe.log_error(frappe.get_traceback()) diff --git a/erpnext/templates/pages/material_request_info.html b/erpnext/templates/pages/material_request_info.html index 0c2772e4d82..151d029ee47 100644 --- a/erpnext/templates/pages/material_request_info.html +++ b/erpnext/templates/pages/material_request_info.html @@ -71,4 +71,4 @@ {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/material_request_info.py b/erpnext/templates/pages/material_request_info.py index 28e541a5d92..e29860ddd67 100644 --- a/erpnext/templates/pages/material_request_info.py +++ b/erpnext/templates/pages/material_request_info.py @@ -19,7 +19,7 @@ def get_context(context): if not frappe.has_website_permission(context.doc): frappe.throw(_("Not Permitted"), frappe.PermissionError) - + default_print_format = frappe.db.get_value('Property Setter', dict(property='default_print_format', doc_type=frappe.form_dict.doctype), "value") if default_print_format: context.print_format = default_print_format @@ -45,5 +45,5 @@ def get_more_items_info(items, material_request): item.delivered_qty = flt(frappe.db.sql("""select sum(transfer_qty) from `tabStock Entry Detail` where material_request = %s and item_code = %s and docstatus = 1""", - (material_request, item.item_code))[0][0]) - return items \ No newline at end of file + (material_request, item.item_code))[0][0]) + return items diff --git a/erpnext/templates/pages/non_profit/join-chapter.html b/erpnext/templates/pages/non_profit/join-chapter.html index 89a7d2aace8..4923efc4e8c 100644 --- a/erpnext/templates/pages/non_profit/join-chapter.html +++ b/erpnext/templates/pages/non_profit/join-chapter.html @@ -56,4 +56,4 @@ {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/non_profit/leave-chapter.html b/erpnext/templates/pages/non_profit/leave-chapter.html index bc4242f9196..fd7658b3b1e 100644 --- a/erpnext/templates/pages/non_profit/leave-chapter.html +++ b/erpnext/templates/pages/non_profit/leave-chapter.html @@ -39,4 +39,4 @@ }); }) -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py index 34985d94ea7..816a25963f5 100644 --- a/erpnext/templates/pages/order.py +++ b/erpnext/templates/pages/order.py @@ -32,9 +32,9 @@ def get_context(context): if not frappe.has_website_permission(context.doc): frappe.throw(_("Not Permitted"), frappe.PermissionError) - + # check for the loyalty program of the customer - customer_loyalty_program = frappe.db.get_value("Customer", context.doc.customer, "loyalty_program") + customer_loyalty_program = frappe.db.get_value("Customer", context.doc.customer, "loyalty_program") if customer_loyalty_program: from erpnext.accounts.doctype.loyalty_program.loyalty_program import get_loyalty_program_details_with_points loyalty_program_details = get_loyalty_program_details_with_points(context.doc.customer, customer_loyalty_program) diff --git a/erpnext/templates/pages/product_search.py b/erpnext/templates/pages/product_search.py index d0d72f073a9..9ab76deff73 100644 --- a/erpnext/templates/pages/product_search.py +++ b/erpnext/templates/pages/product_search.py @@ -47,4 +47,3 @@ def get_product_list(search=None, start=0, limit=12): set_product_info_for_website(item) return [get_item_for_list_in_html(r) for r in data] - diff --git a/erpnext/templates/pages/projects.js b/erpnext/templates/pages/projects.js index 262167fc0b9..bd6bcea7ca0 100644 --- a/erpnext/templates/pages/projects.js +++ b/erpnext/templates/pages/projects.js @@ -117,4 +117,4 @@ frappe.ready(function() { }) return false; } -}); \ No newline at end of file +}); diff --git a/erpnext/templates/pages/task_info.html b/erpnext/templates/pages/task_info.html index 6cd6a7e51af..fe4d304a398 100644 --- a/erpnext/templates/pages/task_info.html +++ b/erpnext/templates/pages/task_info.html @@ -147,4 +147,4 @@ }); -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/task_info.py b/erpnext/templates/pages/task_info.py index b832b88048b..260e2788cd0 100644 --- a/erpnext/templates/pages/task_info.py +++ b/erpnext/templates/pages/task_info.py @@ -7,8 +7,8 @@ def get_context(context): context.no_cache = 1 task = frappe.get_doc('Task', frappe.form_dict.task) - + context.comments = frappe.get_all('Communication', filters={'reference_name': task.name, 'comment_type': 'comment'}, fields=['subject', 'sender_full_name', 'communication_date']) - - context.doc = task \ No newline at end of file + + context.doc = task diff --git a/erpnext/templates/pages/timelog_info.html b/erpnext/templates/pages/timelog_info.html index 22ea3e45d38..be13826444c 100644 --- a/erpnext/templates/pages/timelog_info.html +++ b/erpnext/templates/pages/timelog_info.html @@ -45,4 +45,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/timelog_info.py b/erpnext/templates/pages/timelog_info.py index 7a3361c2ef5..ee86483fa29 100644 --- a/erpnext/templates/pages/timelog_info.py +++ b/erpnext/templates/pages/timelog_info.py @@ -7,5 +7,5 @@ def get_context(context): context.no_cache = 1 timelog = frappe.get_doc('Time Log', frappe.form_dict.timelog) - - context.doc = timelog \ No newline at end of file + + context.doc = timelog diff --git a/erpnext/templates/print_formats/includes/item_table_qty.html b/erpnext/templates/print_formats/includes/item_table_qty.html index 8e68f1cc638..aaa949192ca 100644 --- a/erpnext/templates/print_formats/includes/item_table_qty.html +++ b/erpnext/templates/print_formats/includes/item_table_qty.html @@ -12,4 +12,3 @@ {%- endif %} {{ doc.get_formatted("qty", doc) }} {%- endif %} - diff --git a/erpnext/tests/test_regional.py b/erpnext/tests/test_regional.py index 282fc6454b7..5b3f45a1af7 100644 --- a/erpnext/tests/test_regional.py +++ b/erpnext/tests/test_regional.py @@ -14,4 +14,4 @@ class TestInit(unittest.TestCase): self.assertEqual(test_method(), 'original') frappe.flags.country = 'France' - self.assertEqual(test_method(), 'overridden') \ No newline at end of file + self.assertEqual(test_method(), 'overridden') diff --git a/erpnext/tests/test_subcontracting.py b/erpnext/tests/test_subcontracting.py index 8b0ce0957d4..f55137bc9cf 100644 --- a/erpnext/tests/test_subcontracting.py +++ b/erpnext/tests/test_subcontracting.py @@ -874,4 +874,4 @@ def make_bom_for_subcontracted_items(): def set_backflush_based_on(based_on): frappe.db.set_value('Buying Settings', None, - 'backflush_raw_materials_of_subcontract_based_on', based_on) \ No newline at end of file + 'backflush_raw_materials_of_subcontract_based_on', based_on) diff --git a/erpnext/tests/ui/setup_wizard.js b/erpnext/tests/ui/setup_wizard.js index aeb8d2a1167..ccff785ec94 100644 --- a/erpnext/tests/ui/setup_wizard.js +++ b/erpnext/tests/ui/setup_wizard.js @@ -44,4 +44,4 @@ module.exports = { after: browser => { browser.end(); }, -}; \ No newline at end of file +}; diff --git a/erpnext/tests/ui_test_helpers.py b/erpnext/tests/ui_test_helpers.py index fc3aa298242..902fd64d686 100644 --- a/erpnext/tests/ui_test_helpers.py +++ b/erpnext/tests/ui_test_helpers.py @@ -56,4 +56,4 @@ def create_missing_designation(): frappe.get_doc({ 'doctype': 'Designation', 'designation_name': 'CTO' - }).insert() \ No newline at end of file + }).insert() diff --git a/erpnext/utilities/activation.py b/erpnext/utilities/activation.py index 50c4b255ce1..0f9f2f886de 100644 --- a/erpnext/utilities/activation.py +++ b/erpnext/utilities/activation.py @@ -13,33 +13,33 @@ def get_level(): min_count = 0 doctypes = { "Asset": 5, - "BOM": 3, - "Customer": 5, + "BOM": 3, + "Customer": 5, "Delivery Note": 5, - "Employee": 3, - "Instructor": 5, + "Employee": 3, + "Instructor": 5, "Issue": 5, - "Item": 5, - "Journal Entry": 3, + "Item": 5, + "Journal Entry": 3, "Lead": 3, "Leave Application": 5, "Material Request": 5, - "Opportunity": 5, - "Payment Entry": 2, + "Opportunity": 5, + "Payment Entry": 2, "Project": 5, - "Purchase Order": 2, + "Purchase Order": 2, "Purchase Invoice": 5, "Purchase Receipt": 5, "Quotation": 3, "Salary Slip": 5, "Salary Structure": 5, - "Sales Order": 2, - "Sales Invoice": 2, + "Sales Order": 2, + "Sales Invoice": 2, "Stock Entry": 3, - "Student": 5, + "Student": 5, "Supplier": 5, "Task": 5, - "User": 5, + "User": 5, "Work Order": 5 } diff --git a/erpnext/utilities/bot.py b/erpnext/utilities/bot.py index b2e74da9215..485b0b3383f 100644 --- a/erpnext/utilities/bot.py +++ b/erpnext/utilities/bot.py @@ -36,4 +36,4 @@ class FindItemBot(BotParser): return "\n\n".join(out) else: - return _("Did not find any item called {0}").format(item) \ No newline at end of file + return _("Did not find any item called {0}").format(item) diff --git a/erpnext/utilities/doctype/rename_tool/rename_tool.py b/erpnext/utilities/doctype/rename_tool/rename_tool.py index 0f8a7a385c1..5e3ac1a4c92 100644 --- a/erpnext/utilities/doctype/rename_tool/rename_tool.py +++ b/erpnext/utilities/doctype/rename_tool/rename_tool.py @@ -29,4 +29,3 @@ def upload(select_doctype=None, rows=None): rows = read_csv_content_from_attached_file(frappe.get_doc("Rename Tool", "Rename Tool")) return bulk_rename(select_doctype, rows=rows) - diff --git a/erpnext/utilities/doctype/video/video_list.js b/erpnext/utilities/doctype/video/video_list.js index 8273a4a781f..6f78f6ee127 100644 --- a/erpnext/utilities/doctype/video/video_list.js +++ b/erpnext/utilities/doctype/video/video_list.js @@ -4,4 +4,4 @@ frappe.listview_settings["Video"] = { frappe.set_route("Form","Video Settings", "Video Settings"); }); } -} \ No newline at end of file +} diff --git a/erpnext/utilities/doctype/video_settings/video_settings.py b/erpnext/utilities/doctype/video_settings/video_settings.py index 36fb54f0150..db021b473a4 100644 --- a/erpnext/utilities/doctype/video_settings/video_settings.py +++ b/erpnext/utilities/doctype/video_settings/video_settings.py @@ -19,4 +19,4 @@ class VideoSettings(Document): except Exception: title = _("Failed to Authenticate the API key.") frappe.log_error(title + "\n\n" + frappe.get_traceback(), title=title) - frappe.throw(title + " Please check the error logs.", title=_("Invalid Credentials")) \ No newline at end of file + frappe.throw(title + " Please check the error logs.", title=_("Invalid Credentials")) diff --git a/erpnext/utilities/hierarchy_chart.py b/erpnext/utilities/hierarchy_chart.py index fb58a5d5867..384d84194bb 100644 --- a/erpnext/utilities/hierarchy_chart.py +++ b/erpnext/utilities/hierarchy_chart.py @@ -26,4 +26,4 @@ def get_all_nodes(parent, parent_name, method, company): if d.get('expandable'): nodes_to_expand.append({'id': d.get('id'), 'name': d.get('name')}) - return result \ No newline at end of file + return result diff --git a/erpnext/utilities/report/youtube_interactions/youtube_interactions.py b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py index 3516a35097a..29a489ddcc7 100644 --- a/erpnext/utilities/report/youtube_interactions/youtube_interactions.py +++ b/erpnext/utilities/report/youtube_interactions/youtube_interactions.py @@ -110,4 +110,4 @@ def get_chart_summary_data(data): "datatype": "Float", } ] - return chart_data, summary \ No newline at end of file + return chart_data, summary diff --git a/erpnext/utilities/web_form/addresses/addresses.js b/erpnext/utilities/web_form/addresses/addresses.js index 699703c5792..ffc5e984253 100644 --- a/erpnext/utilities/web_form/addresses/addresses.js +++ b/erpnext/utilities/web_form/addresses/addresses.js @@ -1,3 +1,3 @@ frappe.ready(function() { // bind events here -}) \ No newline at end of file +}) diff --git a/erpnext/www/all-products/index.html b/erpnext/www/all-products/index.html index 92c76ad8790..7c18ecc41fe 100644 --- a/erpnext/www/all-products/index.html +++ b/erpnext/www/all-products/index.html @@ -164,4 +164,4 @@ }); -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/all-products/item_row.html b/erpnext/www/all-products/item_row.html index 20fc9a4878c..a7e994c1e3f 100644 --- a/erpnext/www/all-products/item_row.html +++ b/erpnext/www/all-products/item_row.html @@ -4,4 +4,3 @@ item.item_name or item.name, item.website_image or item.image, item.route, item.website_description or item.description, item.formatted_price, item.item_group ) }} - diff --git a/erpnext/www/all-products/not_found.html b/erpnext/www/all-products/not_found.html index e1986b44154..91989a9ef48 100644 --- a/erpnext/www/all-products/not_found.html +++ b/erpnext/www/all-products/not_found.html @@ -1 +1 @@ -
                                                            {{ _('No products found') }}
                                                            \ No newline at end of file +
                                                            {{ _('No products found') }}
                                                            diff --git a/erpnext/www/book_appointment/index.css b/erpnext/www/book_appointment/index.css index 6c49fde739e..277610876f7 100644 --- a/erpnext/www/book_appointment/index.css +++ b/erpnext/www/book_appointment/index.css @@ -12,7 +12,7 @@ @media (max-width: 768px) { #submit-button-area { display: grid; - grid-template-areas: + grid-template-areas: "submit" "back"; } diff --git a/erpnext/www/book_appointment/index.html b/erpnext/www/book_appointment/index.html index f242f43ad54..207175f89dc 100644 --- a/erpnext/www/book_appointment/index.html +++ b/erpnext/www/book_appointment/index.html @@ -63,4 +63,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/book_appointment/verify/index.html b/erpnext/www/book_appointment/verify/index.html index ebb65b1f24e..9bcd3d202e2 100644 --- a/erpnext/www/book_appointment/verify/index.html +++ b/erpnext/www/book_appointment/verify/index.html @@ -3,7 +3,7 @@ {% block title %} {{ _("Verify Email") }} {% endblock%} - + {% block page_content %} {% if success==True %} @@ -15,4 +15,4 @@ Verification failed please check the link {% endif %} -{% endblock%} \ No newline at end of file +{% endblock%} diff --git a/erpnext/www/book_appointment/verify/index.py b/erpnext/www/book_appointment/verify/index.py index d4478ae34a8..bd766c0ea8a 100644 --- a/erpnext/www/book_appointment/verify/index.py +++ b/erpnext/www/book_appointment/verify/index.py @@ -17,4 +17,4 @@ def get_context(context): return context else: context.success = False - return context \ No newline at end of file + return context diff --git a/erpnext/www/lms/content.py b/erpnext/www/lms/content.py index 0c048453625..05cbb16d3cb 100644 --- a/erpnext/www/lms/content.py +++ b/erpnext/www/lms/content.py @@ -65,4 +65,4 @@ def allowed_content_access(program, content, content_type): and `tabTopic Content`.parent = `tabCourse Topic`.topic and `tabProgram Course`.parent = %(program)s""", {'program': program}) - return (content, content_type) in contents_of_program \ No newline at end of file + return (content, content_type) in contents_of_program diff --git a/erpnext/www/lms/course.html b/erpnext/www/lms/course.html index 0d70ed5cefd..c07b9402b10 100644 --- a/erpnext/www/lms/course.html +++ b/erpnext/www/lms/course.html @@ -103,4 +103,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/index.py b/erpnext/www/lms/index.py index 26f59a2395e..c14b94326b5 100644 --- a/erpnext/www/lms/index.py +++ b/erpnext/www/lms/index.py @@ -13,4 +13,4 @@ def get_context(context): def get_featured_programs(): - return utils.get_portal_programs() or [] \ No newline at end of file + return utils.get_portal_programs() or [] diff --git a/erpnext/www/lms/macros/card.html b/erpnext/www/lms/macros/card.html index dc8fc5c72c7..3cbdec61aa0 100644 --- a/erpnext/www/lms/macros/card.html +++ b/erpnext/www/lms/macros/card.html @@ -31,4 +31,4 @@
                                                            -{% endmacro %} \ No newline at end of file +{% endmacro %} diff --git a/erpnext/www/lms/macros/hero.html b/erpnext/www/lms/macros/hero.html index 94f239eb8ed..e72bfc8175b 100644 --- a/erpnext/www/lms/macros/hero.html +++ b/erpnext/www/lms/macros/hero.html @@ -52,4 +52,4 @@ } {% endblock %} -{% endmacro %} \ No newline at end of file +{% endmacro %} diff --git a/erpnext/www/lms/profile.py b/erpnext/www/lms/profile.py index 4788ea6e70b..7e338e38f13 100644 --- a/erpnext/www/lms/profile.py +++ b/erpnext/www/lms/profile.py @@ -23,4 +23,4 @@ def get_program_progress(student): completion = utils.get_program_completion(program) student_progress.append({'program': program.program_name, 'name': program.name, 'progress':progress, 'completion': completion}) - return student_progress \ No newline at end of file + return student_progress diff --git a/erpnext/www/lms/program.html b/erpnext/www/lms/program.html index 7ad618630a4..30528c667dd 100644 --- a/erpnext/www/lms/program.html +++ b/erpnext/www/lms/program.html @@ -84,4 +84,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/program.py b/erpnext/www/lms/program.py index 104d3fa315a..a4f588ccf18 100644 --- a/erpnext/www/lms/program.py +++ b/erpnext/www/lms/program.py @@ -26,4 +26,4 @@ def get_program(program_name): def get_course_progress(courses, program): progress = {course.name: utils.get_course_progress(course, program) for course in courses} - return progress or {} \ No newline at end of file + return progress or {} diff --git a/erpnext/www/lms/topic.html b/erpnext/www/lms/topic.html index cd24616cd45..dc69599112a 100644 --- a/erpnext/www/lms/topic.html +++ b/erpnext/www/lms/topic.html @@ -55,4 +55,4 @@ -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/topic.py b/erpnext/www/lms/topic.py index 8abbc72e918..993828090ce 100644 --- a/erpnext/www/lms/topic.py +++ b/erpnext/www/lms/topic.py @@ -42,4 +42,4 @@ def get_contents(topic, course, program): result = None progress.append({'content': content, 'content_type': content.doctype, 'completed': status, 'score': score, 'result': result}) - return progress \ No newline at end of file + return progress diff --git a/erpnext/www/support/index.html b/erpnext/www/support/index.html index 12b4c2c0819..3c19198cc16 100644 --- a/erpnext/www/support/index.html +++ b/erpnext/www/support/index.html @@ -96,6 +96,6 @@ .search-container { margin-top: 1.2rem; max-width: 500px; - } + } {%- endblock -%} diff --git a/erpnext/www/support/index.py b/erpnext/www/support/index.py index 5d267430c16..70090c7805d 100644 --- a/erpnext/www/support/index.py +++ b/erpnext/www/support/index.py @@ -8,7 +8,7 @@ def get_context(context): context.greeting_title = setting.greeting_title context.greeting_subtitle = setting.greeting_subtitle - + # Support content favorite_articles = get_favorite_articles_by_page_view() if len(favorite_articles) < 6: @@ -16,15 +16,15 @@ def get_context(context): if favorite_articles: for article in favorite_articles: name_list.append(article.name) - for record in (frappe.get_all("Help Article", - fields=["title", "content", "route", "category"], - filters={"name": ['not in', tuple(name_list)], "published": 1}, + for record in (frappe.get_all("Help Article", + fields=["title", "content", "route", "category"], + filters={"name": ['not in', tuple(name_list)], "published": 1}, order_by="creation desc", limit=(6-len(favorite_articles)))): favorite_articles.append(record) - + context.favorite_article_list = get_favorite_articles(favorite_articles) context.help_article_list = get_help_article_list() - + def get_favorite_articles_by_page_view(): return frappe.db.sql( """ @@ -34,13 +34,13 @@ def get_favorite_articles_by_page_view(): t1.content as content, t1.route as route, t1.category as category, - count(t1.route) as count - FROM `tabHelp Article` AS t1 + count(t1.route) as count + FROM `tabHelp Article` AS t1 INNER JOIN - `tabWeb Page View` AS t2 - ON t1.route = t2.path + `tabWeb Page View` AS t2 + ON t1.route = t2.path WHERE t1.published = 1 - GROUP BY route + GROUP BY route ORDER BY count DESC LIMIT 6; """, as_dict=True) @@ -71,4 +71,4 @@ def get_help_article_list(): 'articles': help_articles, } help_article_list.append(help_aricles_per_caetgory) - return help_article_list \ No newline at end of file + return help_article_list From 49db369d21315a49a09f6024b8eee19f250833fd Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 19 Aug 2021 13:43:01 +0530 Subject: [PATCH 367/369] chore: update git-blame-ignore-revs --- .git-blame-ignore-revs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index e7fa354a2e9..566323a9e97 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -13,3 +13,6 @@ # This commit just changes spaces to tabs for indentation in some files 5f473611bd6ed57703716244a054d3fb5ba9cd23 + +# Whitespace fix throughout codebase +4551d7d6029b6f587f6c99d4f8df5519241c6a86 From 2c40b7c206adaecccbd487b0b3df7406a625c7d9 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 19 Aug 2021 14:12:09 +0530 Subject: [PATCH 368/369] chore: ignore whitespace fixes from git blame --- .git-blame-ignore-revs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 566323a9e97..068672a790f 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -15,4 +15,4 @@ 5f473611bd6ed57703716244a054d3fb5ba9cd23 # Whitespace fix throughout codebase -4551d7d6029b6f587f6c99d4f8df5519241c6a86 +0ef5247e2f6e9562fe837394243b71642d1d98d6 From 5b54d0436b4dce36a0a1e10cb6a8dc2347672762 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 19 Aug 2021 14:18:39 +0530 Subject: [PATCH 369/369] chore: revert "chore: ignore whitespace fixes from git blame" This reverts commit 2c40b7c206adaecccbd487b0b3df7406a625c7d9. --- .git-blame-ignore-revs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 068672a790f..566323a9e97 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -15,4 +15,4 @@ 5f473611bd6ed57703716244a054d3fb5ba9cd23 # Whitespace fix throughout codebase -0ef5247e2f6e9562fe837394243b71642d1d98d6 +4551d7d6029b6f587f6c99d4f8df5519241c6a86