From 01b37ad704d22508b37e416870ca9dd2eae693dd Mon Sep 17 00:00:00 2001 From: Anupam K Date: Tue, 25 Aug 2020 00:46:35 +0530 Subject: [PATCH 001/691] fix: multiselect recipients in Email Digest --- erpnext/patches.txt | 1 + .../v13_0/update_recipient_email_digest.py | 20 + .../doctype/email_digest/email_digest.js | 95 +- .../doctype/email_digest/email_digest.json | 1800 +++-------------- .../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, 425 insertions(+), 1566 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 e17e949b3bcd..049fef65eba2 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 000000000000..583a04d03c2b --- /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 1071ea202092..73fce454292a 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(); - } +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(); + } + }); }); - }, "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')); + 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 125aca17c75d..06c98e5ff943 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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 - }, - { - "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 + "fieldname": "settings", + "fieldtype": "Section Break", + "label": "Email Digest Settings" + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "enabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Enabled" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "For Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "fieldname": "frequency", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "How frequently?", + "options": "Daily\nWeekly\nMonthly", + "reqd": 1 + }, + { + "depends_on": "eval:doc.enabled", + "fieldname": "next_send", + "fieldtype": "Data", + "label": "Next email will be sent on:", + "read_only": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break" + }, + { + "fieldname": "accounts", + "fieldtype": "Section Break", + "label": "Accounts" + }, + { + "fieldname": "accounts_module", + "fieldtype": "Column Break", + "hidden": 1, + "label": "Profit & Loss" + }, + { + "default": "0", + "fieldname": "income", + "fieldtype": "Check", + "label": "New Income" + }, + { + "default": "0", + "fieldname": "expenses_booked", + "fieldtype": "Check", + "label": "New Expenses" + }, + { + "default": "0", + "fieldname": "income_year_to_date", + "fieldtype": "Check", + "label": "Annual Income" + }, + { + "default": "0", + "fieldname": "expense_year_to_date", + "fieldtype": "Check", + "label": "Annual Expenses" + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break", + "label": "Balance Sheet" + }, + { + "default": "0", + "fieldname": "bank_balance", + "fieldtype": "Check", + "label": "Bank Balance" + }, + { + "default": "0", + "fieldname": "credit_balance", + "fieldtype": "Check", + "label": "Bank Credit Balance" + }, + { + "default": "0", + "fieldname": "invoiced_amount", + "fieldtype": "Check", + "label": "Receivables" + }, + { + "default": "0", + "fieldname": "payables", + "fieldtype": "Check", + "label": "Payables" + }, + { + "fieldname": "work_in_progress", + "fieldtype": "Column Break", + "label": "Work in Progress" + }, + { + "default": "0", + "fieldname": "sales_orders_to_bill", + "fieldtype": "Check", + "label": "Sales Orders to Bill" + }, + { + "default": "0", + "fieldname": "purchase_orders_to_bill", + "fieldtype": "Check", + "label": "Purchase Orders to Bill" + }, + { + "fieldname": "operation", + "fieldtype": "Section Break", + "label": "Operations" + }, + { + "fieldname": "column_break_21", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "sales_order", + "fieldtype": "Check", + "label": "New Sales Orders" + }, + { + "default": "0", + "fieldname": "purchase_order", + "fieldtype": "Check", + "label": "New Purchase Orders" + }, + { + "default": "0", + "fieldname": "sales_orders_to_deliver", + "fieldtype": "Check", + "label": "Sales Orders to Deliver" + }, + { + "default": "0", + "fieldname": "purchase_orders_to_receive", + "fieldtype": "Check", + "label": "Purchase Orders to Receive" + }, + { + "default": "0", + "fieldname": "sales_invoice", + "fieldtype": "Check", + "label": "New Sales Invoice" + }, + { + "default": "0", + "fieldname": "purchase_invoice", + "fieldtype": "Check", + "label": "New Purchase Invoice" + }, + { + "fieldname": "column_break_operation", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "new_quotations", + "fieldtype": "Check", + "label": "New Quotations" + }, + { + "default": "0", + "fieldname": "pending_quotations", + "fieldtype": "Check", + "label": "Open Quotations" + }, + { + "default": "0", + "fieldname": "issue", + "fieldtype": "Check", + "label": "Open Issues" + }, + { + "default": "0", + "fieldname": "project", + "fieldtype": "Check", + "label": "Open Projects" + }, + { + "default": "0", + "fieldname": "purchase_orders_items_overdue", + "fieldtype": "Check", + "label": "Purchase Orders Items Overdue" + }, + { + "fieldname": "other", + "fieldtype": "Section Break", + "label": "Other" + }, + { + "fieldname": "tools", + "fieldtype": "Column Break", + "label": "Tools" + }, + { + "default": "0", + "fieldname": "calendar_events", + "fieldtype": "Check", + "label": "Upcoming Calendar Events" + }, + { + "default": "0", + "fieldname": "todo_list", + "fieldtype": "Check", + "label": "Open To Do" + }, + { + "default": "0", + "fieldname": "notifications", + "fieldtype": "Check", + "label": "Open Notifications" + }, + { + "fieldname": "column_break_32", + "fieldtype": "Column Break", + "label": " " + }, + { + "default": "0", + "fieldname": "add_quote", + "fieldtype": "Check", + "label": "Add Quote" + }, + { + "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 b30bd7814b6b..90f90913ba46 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -24,40 +24,26 @@ def __init__(self, *args, **kwargs): 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 000000000000..e69de29bb2d1 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 000000000000..8b2a6dcf49fc --- /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 000000000000..968c51c345b0 --- /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/691] 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 583a04d03c2b..d9aa03f0fd68 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 @@ 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/691] 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 90f90913ba46..6c66d030289e 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.py +++ b/erpnext/setup/doctype/email_digest/email_digest.py @@ -28,27 +28,22 @@ 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 = 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 67519d6b79f0bdc1136ccaeb87a4e5fde2421689 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 3 Jun 2021 18:07:00 +0530 Subject: [PATCH 004/691] fix: boarding status in Employee Onboarding and Separation - make boarding status read-only as its dependent on project - update boarding status in onboarding/separation on project update - update tests to check status changes --- .../employee_onboarding.js | 15 ---- .../employee_onboarding.json | 72 +++++---------- .../test_employee_onboarding.py | 87 ++++++++++++------- .../employee_separation.js | 14 --- .../employee_separation.json | 7 +- .../test_employee_separation.py | 48 +++++++--- erpnext/hr/utils.py | 25 ++++-- erpnext/projects/doctype/project/project.py | 3 + 8 files changed, 133 insertions(+), 138 deletions(-) diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js index d6047e1846fa..bd72629c0dae 100644 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js +++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js @@ -50,21 +50,6 @@ frappe.ui.form.on('Employee Onboarding', { }, __('Create')); frm.page.set_inner_btn_group_as_primary(__('Create')); } - if (frm.doc.docstatus === 1 && frm.doc.project) { - frappe.call({ - method: "erpnext.hr.utils.get_boarding_status", - args: { - "project": frm.doc.project - }, - callback: function(r) { - if (r.message) { - frm.set_value('boarding_status', r.message); - } - refresh_field("boarding_status"); - } - }); - } - }, employee_onboarding_template: function(frm) { diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json index 783c7574efdf..673e228395e8 100644 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json +++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.json @@ -30,18 +30,14 @@ "fieldtype": "Link", "label": "Job Applicant", "options": "Job Applicant", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "job_offer", "fieldtype": "Link", "label": "Job Offer", "options": "Job Offer", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fetch_from": "job_applicant.applicant_name", @@ -49,116 +45,90 @@ "fieldtype": "Data", "in_list_view": 1, "label": "Employee Name", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "employee", "fieldtype": "Link", "label": "Employee", "options": "Employee", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "date_of_joining", "fieldtype": "Date", "in_list_view": 1, - "label": "Date of Joining", - "show_days": 1, - "show_seconds": 1 + "label": "Date of Joining" }, { "allow_on_submit": 1, + "default": "Pending", "fieldname": "boarding_status", "fieldtype": "Select", "label": "Status", - "options": "\nPending\nIn Process\nCompleted", - "show_days": 1, - "show_seconds": 1 + "options": "Pending\nIn Process\nCompleted", + "read_only": 1 }, { "allow_on_submit": 1, "default": "0", "fieldname": "notify_users_by_email", "fieldtype": "Check", - "label": "Notify users by email", - "show_days": 1, - "show_seconds": 1 + "label": "Notify users by email" }, { "fieldname": "column_break_7", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "employee_onboarding_template", "fieldtype": "Link", "label": "Employee Onboarding Template", - "options": "Employee Onboarding Template", - "show_days": 1, - "show_seconds": 1 + "options": "Employee Onboarding Template" }, { "fieldname": "company", "fieldtype": "Link", "label": "Company", - "options": "Company", - "show_days": 1, - "show_seconds": 1 + "options": "Company" }, { "fieldname": "department", "fieldtype": "Link", "in_list_view": 1, "label": "Department", - "options": "Department", - "show_days": 1, - "show_seconds": 1 + "options": "Department" }, { "fieldname": "designation", "fieldtype": "Link", "in_list_view": 1, "label": "Designation", - "options": "Designation", - "show_days": 1, - "show_seconds": 1 + "options": "Designation" }, { "fieldname": "employee_grade", "fieldtype": "Link", "label": "Employee Grade", - "options": "Employee Grade", - "show_days": 1, - "show_seconds": 1 + "options": "Employee Grade" }, { "fieldname": "project", "fieldtype": "Link", "label": "Project", "options": "Project", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "table_for_activity", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "allow_on_submit": 1, "fieldname": "activities", "fieldtype": "Table", "label": "Activities", - "options": "Employee Boarding Activity", - "show_days": 1, - "show_seconds": 1 + "options": "Employee Boarding Activity" }, { "fieldname": "amended_from", @@ -167,14 +137,12 @@ "no_copy": 1, "options": "Employee Onboarding", "print_hide": 1, - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-06-25 15:22:24.923835", + "modified": "2021-06-03 18:01:51.097927", "modified_by": "Administrator", "module": "HR", "name": "Employee Onboarding", diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py index 336e13c9b773..5f7756bcada1 100644 --- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py +++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py @@ -11,39 +11,26 @@ from erpnext.hr.doctype.job_offer.test_job_offer import create_job_offer class TestEmployeeOnboarding(unittest.TestCase): - def test_employee_onboarding_incomplete_task(self): + def setUp(self): if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}): frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'}) - _set_up() - applicant = get_job_applicant() - - job_offer = create_job_offer(job_applicant=applicant.name) - job_offer.submit() - - onboarding = frappe.new_doc('Employee Onboarding') - onboarding.job_applicant = applicant.name - onboarding.job_offer = job_offer.name - onboarding.company = '_Test Company' - onboarding.designation = 'Researcher' - onboarding.append('activities', { - 'activity_name': 'Assign ID Card', - 'role': 'HR User', - 'required_for_employee_creation': 1 - }) - onboarding.append('activities', { - 'activity_name': 'Assign a laptop', - 'role': 'HR User' - }) - onboarding.status = 'Pending' - onboarding.insert() - onboarding.submit() - - project_name = frappe.db.get_value("Project", onboarding.project, "project_name") + + project = "Employee Onboarding : Test Researcher - test@researcher.com" + frappe.db.sql("delete from tabProject where name=%s", project) + frappe.db.sql("delete from tabTask where project=%s", project) + + def test_employee_onboarding_incomplete_task(self): + onboarding = create_employee_onboarding() + + project_name = frappe.db.get_value('Project', onboarding.project, 'project_name') self.assertEqual(project_name, 'Employee Onboarding : Test Researcher - test@researcher.com') # don't allow making employee if onboarding is not complete self.assertRaises(IncompleteTaskError, make_employee, onboarding.name) + # boarding status + self.assertEqual(onboarding.boarding_status, 'Pending') + # complete the task project = frappe.get_doc('Project', onboarding.project) for task in frappe.get_all('Task', dict(project=project.name)): @@ -51,6 +38,10 @@ def test_employee_onboarding_incomplete_task(self): task.status = 'Completed' task.save() + # boarding status + onboarding.reload() + self.assertEqual(onboarding.boarding_status, 'Completed') + # make employee onboarding.reload() employee = make_employee(onboarding.name) @@ -61,6 +52,13 @@ def test_employee_onboarding_incomplete_task(self): employee.insert() self.assertEqual(employee.employee_name, 'Test Researcher') + def tearDown(self): + for entry in frappe.get_all('Employee Onboarding'): + doc = frappe.get_doc('Employee Onboarding', entry.name) + doc.cancel() + doc.delete() + + def get_job_applicant(): if frappe.db.exists('Job Applicant', 'Test Researcher - test@researcher.com'): return frappe.get_doc('Job Applicant', 'Test Researcher - test@researcher.com') @@ -72,10 +70,35 @@ def get_job_applicant(): applicant.insert() return applicant -def _set_up(): - for doctype in ["Employee Onboarding"]: - frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype)) +def get_job_offer(applicant_name): + job_offer = frappe.db.exists('Job Offer', {'job_applicant': applicant_name}) + if job_offer: + return frappe.get_doc('Job Offer', job_offer) + + job_offer = create_job_offer(job_applicant=applicant_name) + job_offer.submit() + return job_offer + +def create_employee_onboarding(): + applicant = get_job_applicant() + job_offer = get_job_offer(applicant.name) + + onboarding = frappe.new_doc('Employee Onboarding') + onboarding.job_applicant = applicant.name + onboarding.job_offer = job_offer.name + onboarding.company = '_Test Company' + onboarding.designation = 'Researcher' + onboarding.append('activities', { + 'activity_name': 'Assign ID Card', + 'role': 'HR User', + 'required_for_employee_creation': 1 + }) + onboarding.append('activities', { + 'activity_name': 'Assign a laptop', + 'role': 'HR User' + }) + onboarding.status = 'Pending' + onboarding.insert() + onboarding.submit() - project = "Employee Onboarding : Test Researcher - test@researcher.com" - frappe.db.sql("delete from tabProject where name=%s", project) - frappe.db.sql("delete from tabTask where project=%s", project) + return onboarding \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.js b/erpnext/hr/doctype/employee_separation/employee_separation.js index 9a75c16317ba..33830796b6ca 100644 --- a/erpnext/hr/doctype/employee_separation/employee_separation.js +++ b/erpnext/hr/doctype/employee_separation/employee_separation.js @@ -23,20 +23,6 @@ frappe.ui.form.on('Employee Separation', { frappe.set_route('List', 'Task', {project: frm.doc.project}); },__("View")); } - if (frm.doc.docstatus === 1 && frm.doc.project) { - frappe.call({ - method: "erpnext.hr.utils.get_boarding_status", - args: { - "project": frm.doc.project - }, - callback: function(r) { - if (r.message) { - frm.set_value('boarding_status', r.message); - } - refresh_field("boarding_status"); - } - }); - } }, employee_separation_template: function(frm) { diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.json b/erpnext/hr/doctype/employee_separation/employee_separation.json index 7af209887f03..c10da5c35e77 100644 --- a/erpnext/hr/doctype/employee_separation/employee_separation.json +++ b/erpnext/hr/doctype/employee_separation/employee_separation.json @@ -50,11 +50,12 @@ }, { "allow_on_submit": 1, + "default": "Pending", "fieldname": "boarding_status", "fieldtype": "Select", "label": "Status", - "options": "\nPending\nIn Process\nCompleted", - "reqd": 1 + "options": "Pending\nIn Process\nCompleted", + "read_only": 1 }, { "allow_on_submit": 1, @@ -147,7 +148,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2021-04-28 15:58:36.020196", + "modified": "2021-06-03 18:02:54.007313", "modified_by": "Administrator", "module": "HR", "name": "Employee Separation", diff --git a/erpnext/hr/doctype/employee_separation/test_employee_separation.py b/erpnext/hr/doctype/employee_separation/test_employee_separation.py index 713fcf526b5a..f787d9c65683 100644 --- a/erpnext/hr/doctype/employee_separation/test_employee_separation.py +++ b/erpnext/hr/doctype/employee_separation/test_employee_separation.py @@ -6,21 +6,43 @@ import frappe import unittest -test_dependencies = ["Employee Onboarding"] +test_dependencies = ['Employee Onboarding'] class TestEmployeeSeparation(unittest.TestCase): def test_employee_separation(self): - employee = frappe.db.get_value("Employee", {"status": "Active"}) - separation = frappe.new_doc('Employee Separation') - separation.employee = employee - separation.company = '_Test Company' - separation.append('activities', { - 'activity_name': 'Deactivate Employee', - 'role': 'HR User' - }) - separation.boarding_status = 'Pending' - separation.insert() - separation.submit() + separation = create_employee_separation() + self.assertEqual(separation.docstatus, 1) + self.assertEqual(separation.boarding_status, 'Pending') + + project = frappe.get_doc('Project', separation.project) + project.percent_complete_method = 'Manual' + project.status = 'Completed' + project.save() + + separation.reload() + self.assertEqual(separation.boarding_status, 'Completed') + separation.cancel() - self.assertEqual(separation.project, "") \ No newline at end of file + self.assertEqual(separation.project, '') + + def tearDown(self): + for entry in frappe.get_all('Employee Separation'): + doc = frappe.get_doc('Employee Separation', entry.name) + if doc.docstatus == 1: + doc.cancel() + doc.delete() + +def create_employee_separation(): + employee = frappe.db.get_value('Employee', {'status': 'Active'}) + separation = frappe.new_doc('Employee Separation') + separation.employee = employee + separation.company = '_Test Company' + separation.append('activities', { + 'activity_name': 'Deactivate Employee', + 'role': 'HR User' + }) + separation.boarding_status = 'Pending' + separation.insert() + separation.submit() + return separation \ No newline at end of file diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 80189e87b7a0..8af00b36ca7a 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -114,16 +114,23 @@ def get_onboarding_details(parent, parenttype): filters={"parent": parent, "parenttype": parenttype}, order_by= "idx") -@frappe.whitelist() -def get_boarding_status(project): +def update_employee_boarding_status(project): + employee_onboarding = frappe.db.exists('Employee Onboarding', {'project': project.name}) + employee_separation = frappe.db.exists('Employee Separation', {'project': project.name}) + + if not (employee_onboarding or employee_separation): + return + status = 'Pending' - if project: - doc = frappe.get_doc('Project', project) - if flt(doc.percent_complete) > 0.0 and flt(doc.percent_complete) < 100.0: - status = 'In Process' - elif flt(doc.percent_complete) == 100.0: - status = 'Completed' - return status + if flt(project.percent_complete) > 0.0 and flt(project.percent_complete) < 100.0: + status = 'In Process' + elif flt(project.percent_complete) == 100.0: + status = 'Completed' + + if employee_onboarding: + frappe.db.set_value('Employee Onboarding', employee_onboarding, 'boarding_status', status) + elif employee_separation: + frappe.db.set_value('Employee Separation', employee_separation, 'boarding_status', status) def set_employee_name(doc): if doc.employee and not doc.employee_name: diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index c8fbe0bf7be8..0ee999062096 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -14,6 +14,7 @@ from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday from frappe.model.document import Document from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list +from erpnext.hr.utils import update_employee_boarding_status class Project(Document): def get_feed(self): @@ -37,6 +38,7 @@ def validate(self): self.send_welcome_email() self.update_costing() self.update_percent_complete() + update_employee_boarding_status(self) def copy_from_template(self): ''' @@ -132,6 +134,7 @@ def is_row_updated(self, row, existing_task_data, fields): def update_project(self): '''Called externally by Task''' self.update_percent_complete() + update_employee_boarding_status(self) self.update_costing() self.db_update() From 672c8bb11230692cf24c81b85d9d0fd84f27d910 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Fri, 4 Jun 2021 16:44:30 +0530 Subject: [PATCH 005/691] feature: report for cost of goods sold by item group --- .../report/cogs_by_item_group/__init__.py | 0 .../cogs_by_item_group/cogs_by_item_group.js | 46 ++++++ .../cogs_by_item_group.json | 32 ++++ .../cogs_by_item_group/cogs_by_item_group.py | 155 ++++++++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 erpnext/stock/report/cogs_by_item_group/__init__.py create mode 100644 erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js create mode 100644 erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.json create mode 100644 erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py diff --git a/erpnext/stock/report/cogs_by_item_group/__init__.py b/erpnext/stock/report/cogs_by_item_group/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js new file mode 100644 index 000000000000..c17da4ed97b9 --- /dev/null +++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js @@ -0,0 +1,46 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["COGS By Item Group"] = { + "filters": [ + { + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + mandatory: true, + default: frappe.defaults.get_user_default("Company"), + }, + { + label: __("Account"), + fieldname: "account", + fieldtype: "Link", + options: "Account", + mandatory: true, + get_query() { + var company = frappe.query_report.get_filter_value('company'); + return { + "doctype": "Account", + "filters": { + "company": company, + } + } + }, + }, + { + label: __("From Date"), + fieldname: "from_date", + fieldtype: "Date", + mandatory: true, + default: frappe.datetime.year_start(), + }, + { + label: __("To Date"), + fieldname: "to_date", + fieldtype: "Date", + mandatory: true, + default: frappe.datetime.get_today(), + }, + ] +}; diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.json b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.json new file mode 100644 index 000000000000..a14adf8a453a --- /dev/null +++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.json @@ -0,0 +1,32 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2021-06-02 18:59:19.830928", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2021-06-02 18:59:55.470621", + "modified_by": "Administrator", + "module": "Stock", + "name": "COGS By Item Group", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "GL Entry", + "report_name": "COGS By Item Group", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ No newline at end of file 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 new file mode 100644 index 000000000000..d4ddd595d905 --- /dev/null +++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.py @@ -0,0 +1,155 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.utils import date_diff +from collections import OrderedDict +from erpnext.accounts.report.general_ledger.general_ledger import get_gl_entries + + +def execute(filters=None): + print(filters) + validate_filters(filters) + columns = get_columns() + data = get_data(filters) + return columns, data + + +def validate_filters(filters): + 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")) + + +def get_columns(): + return [ + { + 'label': 'Item Group', + 'fieldname': 'item_group', + 'fieldtype': 'Data', + 'width': '200' + }, + { + 'label': 'COGS Debit', + 'fieldname': 'cogs_debit', + 'fieldtype': 'Currency', + 'width': '200' + } + ] + + +def get_data(filters): + entries = get_filtered_entries(filters) + item_groups_list = frappe.get_all("Item Group", fields=("name", "is_group", "lft", "rgt")) + item_groups_dict = get_item_groups_dict(item_groups_list) + levels_dict = get_levels_dict(item_groups_dict) + + update_levels_dict(levels_dict) + assign_self_values(levels_dict, entries) + assign_agg_values(levels_dict) + + data = [] + for _, i in levels_dict.items(): + if i['agg_value'] == 0: + continue + data.append(get_row(i['name'], i['agg_value'], i['is_group'], i['level'])) + if i['self_value'] < i['agg_value'] and i['self_value'] > 0: + data.append(get_row(i['name'], i['self_value'], 0, i['level'] + 1)) + return data + + +def get_filtered_entries(filters): + gl_entries = get_gl_entries(filters, []) + entries = [frappe.get_doc(gle.voucher_type, gle.voucher_no)for gle in gl_entries] + filtered_entries = [] + for entry in entries: + posting_date = entry.get("posting_date") + from_date = filters.get("from_date") + if date_diff(from_date, posting_date) > 0: + continue + filtered_entries.append(entry) + return filtered_entries + + +def append_blank(data): + if len(data) == 0: + data.append(get_row("", 0, 0, 0)) + + +def get_item_groups_dict(item_groups_list): + return { (i['lft'],i['rgt']):{'name':i['name'], 'is_group':i['is_group']} + for i in item_groups_list } + + +def get_levels_dict(item_groups_dict): + lr_list = sorted(item_groups_dict, key=lambda x : x[0]) + levels = OrderedDict() + current_level = 0 + nesting_r = [] + for l,r in lr_list: + while current_level > 0 and nesting_r[-1] < l: + nesting_r.pop() + current_level -= 1 + + levels[(l,r)] = { + 'level' : current_level, + 'name' : item_groups_dict[(l,r)]['name'], + 'is_group' : item_groups_dict[(l,r)]['is_group'] + } + + if r - l > 1: + current_level += 1 + nesting_r.append(r) + return levels + + +def update_levels_dict(levels_dict): + for k in levels_dict: levels_dict[k].update({'self_value':0, 'agg_value':0}) + + +def assign_self_values(levels_dict, entries): + names_dict = {v['name']:k for k, v in levels_dict.items()} + for entry in entries: + items = entry.get("items") + items = [] if items is None else items + for item in items: + qty = item.get("qty") + incoming_rate = item.get("incoming_rate") + item_group = item.get("item_group") + key = names_dict[item_group] + levels_dict[key]['self_value'] += (incoming_rate * qty) + + +def assign_agg_values(levels_dict): + keys = list(levels_dict.keys())[::-1] + prev_level = levels_dict[keys[-1]]['level'] + accu = [0] + for k in keys[:-1]: + curr_level = levels_dict[k]['level'] + if curr_level == prev_level: + accu[-1] += levels_dict[k]['self_value'] + levels_dict[k]['agg_value'] = levels_dict[k]['self_value'] + + elif curr_level > prev_level: + accu.append(levels_dict[k]['self_value']) + levels_dict[k]['agg_value'] = accu[-1] + + elif curr_level < prev_level: + accu[-1] += levels_dict[k]['self_value'] + levels_dict[k]['agg_value'] = accu[-1] + + prev_level = curr_level + + # root node + rk = keys[-1] + levels_dict[rk]['agg_value'] = sum(accu) + levels_dict[rk]['self_value'] + + +def get_row(name:str, value:float, is_bold:int, indent:int): + item_group = name + if is_bold: + item_group = frappe.bold(item_group) + return frappe._dict(item_group=item_group, cogs_debit=value, indent=indent) From 23b907df1af0a84a25954079afeac1179eccdea4 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Mon, 7 Jun 2021 13:52:26 +0530 Subject: [PATCH 006/691] fix: use stock value diff for calculation --- .../cogs_by_item_group/cogs_by_item_group.py | 135 ++++++++++-------- 1 file changed, 75 insertions(+), 60 deletions(-) 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 d4ddd595d905..7599da432290 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 @@ -9,7 +9,6 @@ def execute(filters=None): - print(filters) validate_filters(filters) columns = get_columns() data = get_data(filters) @@ -17,9 +16,6 @@ def execute(filters=None): def validate_filters(filters): - 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")) @@ -42,110 +38,100 @@ def get_columns(): def get_data(filters): - entries = get_filtered_entries(filters) - item_groups_list = frappe.get_all("Item Group", fields=("name", "is_group", "lft", "rgt")) - item_groups_dict = get_item_groups_dict(item_groups_list) - levels_dict = get_levels_dict(item_groups_dict) + filtered_entries = get_filtered_entries(filters) + svd_list = get_stock_value_difference_list(filtered_entries) + leveled_dict = get_leveled_dict() - update_levels_dict(levels_dict) - assign_self_values(levels_dict, entries) - assign_agg_values(levels_dict) + assign_self_values(leveled_dict, svd_list) + assign_agg_values(leveled_dict) data = [] - for _, i in levels_dict.items(): + for _, i in leveled_dict.items(): if i['agg_value'] == 0: continue data.append(get_row(i['name'], i['agg_value'], i['is_group'], i['level'])) if i['self_value'] < i['agg_value'] and i['self_value'] > 0: data.append(get_row(i['name'], i['self_value'], 0, i['level'] + 1)) + # append_blank() return data def get_filtered_entries(filters): gl_entries = get_gl_entries(filters, []) - entries = [frappe.get_doc(gle.voucher_type, gle.voucher_no)for gle in gl_entries] filtered_entries = [] - for entry in entries: - posting_date = entry.get("posting_date") - from_date = filters.get("from_date") + for entry in gl_entries: + posting_date = entry.get('posting_date') + from_date = filters.get('from_date') if date_diff(from_date, posting_date) > 0: continue filtered_entries.append(entry) return filtered_entries -def append_blank(data): - if len(data) == 0: - data.append(get_row("", 0, 0, 0)) - +def get_stock_value_difference_list(filtered_entries): + voucher_nos = [fe.get('voucher_no') for fe in filtered_entries] + svd_list = frappe.get_list('Stock Ledger Entry', + fields=['item_code','stock_value_difference'], + filters=[('voucher_no', 'in', voucher_nos)]) + assign_item_groups_to_svd_list(svd_list) + return svd_list -def get_item_groups_dict(item_groups_list): - return { (i['lft'],i['rgt']):{'name':i['name'], 'is_group':i['is_group']} - for i in item_groups_list } - -def get_levels_dict(item_groups_dict): - lr_list = sorted(item_groups_dict, key=lambda x : x[0]) - levels = OrderedDict() +def get_leveled_dict(): + item_groups_dict = get_item_groups_dict() + lr_list = sorted(item_groups_dict, key=lambda x : int(x[0])) + leveled_dict = OrderedDict() current_level = 0 nesting_r = [] - for l,r in lr_list: + for l, r in lr_list: while current_level > 0 and nesting_r[-1] < l: nesting_r.pop() current_level -= 1 - levels[(l,r)] = { + leveled_dict[(l,r)] = { 'level' : current_level, 'name' : item_groups_dict[(l,r)]['name'], 'is_group' : item_groups_dict[(l,r)]['is_group'] } - if r - l > 1: + if int(r) - int(l) > 1: current_level += 1 nesting_r.append(r) - return levels - -def update_levels_dict(levels_dict): - for k in levels_dict: levels_dict[k].update({'self_value':0, 'agg_value':0}) - - -def assign_self_values(levels_dict, entries): - names_dict = {v['name']:k for k, v in levels_dict.items()} - for entry in entries: - items = entry.get("items") - items = [] if items is None else items - for item in items: - qty = item.get("qty") - incoming_rate = item.get("incoming_rate") - item_group = item.get("item_group") - key = names_dict[item_group] - levels_dict[key]['self_value'] += (incoming_rate * qty) - - -def assign_agg_values(levels_dict): - keys = list(levels_dict.keys())[::-1] - prev_level = levels_dict[keys[-1]]['level'] + update_leveled_dict(leveled_dict) + return leveled_dict + + +def assign_self_values(leveled_dict, svd_list): + key_dict = {v['name']:k for k, v in leveled_dict.items()} + for item in svd_list: + key = key_dict[item.get("item_group")] + leveled_dict[key]['self_value'] += -item.get("stock_value_difference") + + +def assign_agg_values(leveled_dict): + keys = list(leveled_dict.keys())[::-1] + prev_level = leveled_dict[keys[-1]]['level'] accu = [0] for k in keys[:-1]: - curr_level = levels_dict[k]['level'] + curr_level = leveled_dict[k]['level'] if curr_level == prev_level: - accu[-1] += levels_dict[k]['self_value'] - levels_dict[k]['agg_value'] = levels_dict[k]['self_value'] + accu[-1] += leveled_dict[k]['self_value'] + leveled_dict[k]['agg_value'] = leveled_dict[k]['self_value'] elif curr_level > prev_level: - accu.append(levels_dict[k]['self_value']) - levels_dict[k]['agg_value'] = accu[-1] + accu.append(leveled_dict[k]['self_value']) + leveled_dict[k]['agg_value'] = accu[-1] elif curr_level < prev_level: - accu[-1] += levels_dict[k]['self_value'] - levels_dict[k]['agg_value'] = accu[-1] + accu[-1] += leveled_dict[k]['self_value'] + leveled_dict[k]['agg_value'] = accu[-1] prev_level = curr_level # root node rk = keys[-1] - levels_dict[rk]['agg_value'] = sum(accu) + levels_dict[rk]['self_value'] + leveled_dict[rk]['agg_value'] = sum(accu) + leveled_dict[rk]['self_value'] def get_row(name:str, value:float, is_bold:int, indent:int): @@ -153,3 +139,32 @@ def get_row(name:str, value:float, is_bold:int, indent:int): 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): + ig_map = get_item_groups_map(svd_list) + for item in svd_list: + item.item_group = ig_map[item.get("item_code")] + +def get_item_groups_map(svd_list): + # for items in svd_list: [{'item_code':'item_group'}] + item_codes = set([i['item_code'] for i in svd_list]) + ig_list = frappe.get_list('Item', + fields=['item_code','item_group'], + filters=[('item_code', 'in', item_codes)]) + return {i['item_code']:i['item_group'] for i in ig_list} + + +def append_blank(data): + if len(data) == 0: + data.append(get_row("", 0, 0, 0)) + + +def get_item_groups_dict(): + item_groups_list = frappe.get_all("Item Group", fields=("name", "is_group", "lft", "rgt")) + return { (i['lft'],i['rgt']):{'name':i['name'], 'is_group':i['is_group']} + for i in item_groups_list } + + +def update_leveled_dict(leveled_dict): + for k in leveled_dict: leveled_dict[k].update({'self_value':0, 'agg_value':0}) From 6f79c4c3481b89fa080e69e5ce5a567b1610ea13 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Mon, 7 Jun 2021 13:58:45 +0530 Subject: [PATCH 007/691] fix: add account filter --- .../cogs_by_item_group/cogs_by_item_group.js | 35 ++++++++++--------- .../cogs_by_item_group/cogs_by_item_group.py | 6 ++++ 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js index c17da4ed97b9..bb780e50b24a 100644 --- a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js +++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js @@ -2,8 +2,9 @@ // For license information, please see license.txt /* eslint-disable */ + frappe.query_reports["COGS By Item Group"] = { - "filters": [ + filters: [ { label: __("Company"), fieldname: "company", @@ -12,22 +13,22 @@ frappe.query_reports["COGS By Item Group"] = { mandatory: true, default: frappe.defaults.get_user_default("Company"), }, - { - label: __("Account"), - fieldname: "account", - fieldtype: "Link", - options: "Account", - mandatory: true, - get_query() { - var company = frappe.query_report.get_filter_value('company'); - return { - "doctype": "Account", - "filters": { - "company": company, - } - } - }, - }, + // { + // label: __("Account"), + // fieldname: "account", + // fieldtype: "Link", + // options: "Account", + // mandatory: true, + // get_query() { + // const company = frappe.query_report.get_filter_value('company'); + // return { + // "doctype": "Account", + // "filters": { + // "company": company, + // } + // } + // }, + // }, { label: __("From Date"), fieldname: "from_date", 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 7599da432290..e2c6f7928c8e 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 @@ -9,12 +9,18 @@ def execute(filters=None): + update_filters_with_account(filters) validate_filters(filters) columns = get_columns() data = get_data(filters) return columns, data +def update_filters_with_account(filters): + account = frappe.get_value("Company", filters.get("company"), "default_expense_account") + filters.update(dict(account=account)) + + def validate_filters(filters): if filters.from_date > filters.to_date: frappe.throw(_("From Date must be before To Date")) From dc448c2f51210e4b7481ad229cf9c339e81ffd69 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 17 Jun 2021 00:28:03 +0530 Subject: [PATCH 008/691] refactor: lead --- erpnext/crm/doctype/lead/lead.js | 5 - erpnext/crm/doctype/lead/lead.json | 180 +++++++++++++++++------------ erpnext/crm/doctype/lead/lead.py | 84 +------------- 3 files changed, 104 insertions(+), 165 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index ebe85241d282..815bb41a76ef 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -68,11 +68,6 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller }) } - organization_lead () { - this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead); - this.frm.toggle_reqd("company_name", this.frm.doc.organization_lead); - } - company_name () { if (this.frm.doc.organization_lead && !this.frm.doc.lead_name) { this.frm.set_value("lead_name", this.frm.doc.company_name); diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index 1b33fd73acf8..ed33d896f8a9 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -9,30 +9,32 @@ "email_append_to": 1, "engine": "InnoDB", "field_order": [ - "organization_lead", "lead_details", "naming_series", + "salutation", + "first_name", + "middle_name", + "last_name", "lead_name", - "company_name", "email_id", + "mobile_no", + "phone", "col_break123", - "lead_owner", "status", - "salutation", + "company_name", "designation", "gender", - "source", - "customer", - "campaign_name", - "image", - "section_break_12", - "contact_by", - "column_break_14", - "contact_date", - "ends_on", - "notes_section", - "notes", - "address_info", + "additional_information_section", + "no_of_employees", + "industry", + "market_segment", + "type", + "request_type", + "column_break_22", + "whatsapp_no", + "fax", + "website", + "address_section", "address_html", "address_type", "address_title", @@ -45,35 +47,33 @@ "state", "country", "pincode", - "contact_section", - "phone", - "mobile_no", - "fax", - "website", - "more_info", - "type", - "market_segment", - "industry", - "request_type", - "column_break3", + "section_break_12", + "lead_owner", + "ends_on", + "column_break_14", + "contact_by", + "contact_date", + "lead_source_details_section", "company", "territory", "language", + "column_break_50", + "source", + "campaign_name", "unsubscribed", "blog_subscriber", + "notes_section", + "notes", + "other_information_section", + "customer", + "image", "title" ], "fields": [ - { - "default": "0", - "fieldname": "organization_lead", - "fieldtype": "Check", - "label": "Lead is an Organization", - "set_only_once": 1 - }, { "fieldname": "lead_details", "fieldtype": "Section Break", + "label": "Lead Details", "options": "fa fa-user" }, { @@ -90,7 +90,8 @@ "fieldname": "lead_name", "fieldtype": "Data", "in_global_search": 1, - "label": "Person Name", + "label": "Full Name", + "mandatory_depends_on": "eval: !(doc.company_name)", "oldfieldname": "lead_name", "oldfieldtype": "Data", "search_index": 1 @@ -99,7 +100,9 @@ "fieldname": "company_name", "fieldtype": "Data", "in_list_view": 1, + "in_standard_filter": 1, "label": "Organization Name", + "mandatory_depends_on": "eval: !(doc.lead_name)", "oldfieldname": "company_name", "oldfieldtype": "Data" }, @@ -121,7 +124,6 @@ "default": "__user", "fieldname": "lead_owner", "fieldtype": "Link", - "in_list_view": 1, "label": "Lead Owner", "oldfieldname": "lead_owner", "oldfieldtype": "Link", @@ -241,46 +243,39 @@ "read_only": 1 }, { - "depends_on": "eval: doc.__islocal", "description": "Home, Work, etc.", "fieldname": "address_title", "fieldtype": "Data", "label": "Address Title" }, { - "depends_on": "eval: doc.__islocal", "fieldname": "address_line1", "fieldtype": "Data", "label": "Address Line 1", "mandatory_depends_on": "eval: doc.address_title && doc.address_type" }, { - "depends_on": "eval: doc.__islocal", "fieldname": "address_line2", "fieldtype": "Data", "label": "Address Line 2" }, { - "depends_on": "eval: doc.__islocal", "fieldname": "city", "fieldtype": "Data", "label": "City/Town", "mandatory_depends_on": "eval: doc.address_title && doc.address_type" }, { - "depends_on": "eval: doc.__islocal", "fieldname": "county", "fieldtype": "Data", "label": "County" }, { - "depends_on": "eval: doc.__islocal", "fieldname": "state", "fieldtype": "Data", "label": "State" }, { - "depends_on": "eval: doc.__islocal", "fieldname": "country", "fieldtype": "Link", "label": "Country", @@ -288,7 +283,7 @@ "options": "Country" }, { - "depends_on": "eval: doc.__islocal", + "collapsible_depends_on": "eval: doc.__islocal", "fieldname": "pincode", "fieldtype": "Data", "label": "Postal Code" @@ -329,14 +324,6 @@ "oldfieldname": "fax", "oldfieldtype": "Data" }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text" - }, { "fieldname": "type", "fieldtype": "Select", @@ -369,12 +356,6 @@ "oldfieldtype": "Select", "options": "\nProduct Enquiry\nRequest for Information\nSuggestions\nOther" }, - { - "fieldname": "column_break3", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, { "fieldname": "company", "fieldtype": "Link", @@ -389,11 +370,14 @@ "fieldtype": "Data", "label": "Website", "oldfieldname": "website", - "oldfieldtype": "Data" + "oldfieldtype": "Data", + "options": "URL" }, { "fieldname": "territory", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Territory", "oldfieldname": "territory", "oldfieldtype": "Link", @@ -422,28 +406,13 @@ { "fieldname": "designation", "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, "label": "Designation", "options": "Designation" }, - { - "collapsible": 1, - "collapsible_depends_on": "eval: doc.__islocal", - "fieldname": "address_info", - "fieldtype": "Section Break", - "label": "Address & Contact", - "oldfieldtype": "Column Break", - "options": "fa fa-map-marker" - }, - { - "collapsible": 1, - "collapsible_depends_on": "eval: doc.__islocal", - "fieldname": "contact_section", - "fieldtype": "Section Break", - "label": "Contact" - }, { "default": "Billing", - "depends_on": "eval: doc.__islocal", "fieldname": "address_type", "fieldtype": "Select", "label": "Address Type", @@ -454,13 +423,70 @@ "fieldtype": "Link", "label": "Print Language", "options": "Language" + }, + { + "fieldname": "first_name", + "fieldtype": "Data", + "label": "First Name" + }, + { + "fieldname": "middle_name", + "fieldtype": "Data", + "label": "Middle Name" + }, + { + "fieldname": "last_name", + "fieldtype": "Data", + "label": "Last Name" + }, + { + "collapsible": 1, + "fieldname": "additional_information_section", + "fieldtype": "Section Break", + "label": "Additional Information" + }, + { + "fieldname": "no_of_employees", + "fieldtype": "Int", + "label": "No. of Employees" + }, + { + "fieldname": "column_break_22", + "fieldtype": "Column Break" + }, + { + "fieldname": "whatsapp_no", + "fieldtype": "Data", + "label": "WhatsApp No.", + "options": "Phone" + }, + { + "collapsible": 1, + "depends_on": "eval:!doc.__islocal", + "fieldname": "address_section", + "fieldtype": "Section Break", + "label": "Address" + }, + { + "fieldname": "lead_source_details_section", + "fieldtype": "Section Break", + "label": "Lead Source Details" + }, + { + "fieldname": "column_break_50", + "fieldtype": "Column Break" + }, + { + "fieldname": "other_information_section", + "fieldtype": "Section Break", + "label": "Other Information" } ], "icon": "fa fa-user", "idx": 5, "image_field": "image", "links": [], - "modified": "2021-01-06 19:39:58.748978", + "modified": "2021-06-17 00:20:37.768449", "modified_by": "Administrator", "module": "CRM", "name": "Lead", diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index d1d096843bf3..b18129b14fc8 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -21,14 +21,6 @@ def onload(self): self.get("__onload").is_customer = customer load_address_and_contact(self) - def before_insert(self): - if self.address_title and self.address_type: - self.address_doc = self.create_address() - self.contact_doc = self.create_contact() - - def after_insert(self): - self.update_links() - def validate(self): self.set_lead_name() self.set_title() @@ -120,85 +112,11 @@ def set_lead_name(self): self.lead_name = self.email_id.split("@")[0] def set_title(self): - if self.organization_lead: + if self.company_name: self.title = self.company_name else: self.title = self.lead_name - def create_address(self): - address_fields = ["address_type", "address_title", "address_line1", "address_line2", - "city", "county", "state", "country", "pincode"] - info_fields = ["email_id", "phone", "fax"] - - # do not create an address if no fields are available, - # skipping country since the system auto-sets it from system defaults - address = frappe.new_doc("Address") - - address.update({addr_field: self.get(addr_field) for addr_field in address_fields}) - address.update({info_field: self.get(info_field) for info_field in info_fields}) - address.insert() - - return address - - def create_contact(self): - if not self.lead_name: - self.set_lead_name() - - names = self.lead_name.strip().split(" ") - if len(names) > 1: - first_name, last_name = names[0], " ".join(names[1:]) - else: - first_name, last_name = self.lead_name, None - - contact = frappe.new_doc("Contact") - contact.update({ - "first_name": first_name, - "last_name": last_name, - "salutation": self.salutation, - "gender": self.gender, - "designation": self.designation, - }) - - if self.email_id: - contact.append("email_ids", { - "email_id": self.email_id, - "is_primary": 1 - }) - - if self.phone: - contact.append("phone_nos", { - "phone": self.phone, - "is_primary": 1 - }) - - if self.mobile_no: - contact.append("phone_nos", { - "phone": self.mobile_no - }) - - contact.insert(ignore_permissions=True) - - return contact - - def update_links(self): - # update address links - if hasattr(self, 'address_doc'): - self.address_doc.append("links", { - "link_doctype": "Lead", - "link_name": self.name, - "link_title": self.lead_name - }) - self.address_doc.save() - - # update contact links - if self.contact_doc: - self.contact_doc.append("links", { - "link_doctype": "Lead", - "link_name": self.name, - "link_title": self.lead_name - }) - self.contact_doc.save() - @frappe.whitelist() def make_customer(source_name, target_doc=None): return _make_customer(source_name, target_doc) From c1ba35b25b056053c886b732cd59ab6c08ba1484 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 20 Jun 2021 12:42:06 +0530 Subject: [PATCH 009/691] 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 ed7d58843476..7917e3abf590 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 000000000000..e69de29bb2d1 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 000000000000..057c45ee8645 --- /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 000000000000..04bd9422bd9b --- /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 000000000000..d802781320ad --- /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 000000000000..be2964530b8d --- /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 7a3cb838a990..d3ebcdf7e7b7 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 000000000000..62f6ddcb6e01 --- /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 010/691] 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 057c45ee8645..e42e54f690c2 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 04bd9422bd9b..5739a112de97 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 be2964530b8d..ae91a919b2b3 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,7 +2,7 @@ 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 62f6ddcb6e01..02446be11a16 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 357657fa7333a97f2cd8c5196bb361a86883a583 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 24 Jun 2021 13:00:50 +0530 Subject: [PATCH 011/691] chore: create a separate controller file for EmployeeBoardingController --- .../employee_boarding_controller.py | 128 ++++++++++++++++++ .../employee_onboarding.js | 2 +- .../employee_onboarding.py | 8 +- .../employee_separation.js | 2 +- .../employee_separation.py | 2 +- erpnext/hr/utils.py | 119 ---------------- erpnext/projects/doctype/project/project.py | 2 +- 7 files changed, 136 insertions(+), 127 deletions(-) create mode 100644 erpnext/controllers/employee_boarding_controller.py diff --git a/erpnext/controllers/employee_boarding_controller.py b/erpnext/controllers/employee_boarding_controller.py new file mode 100644 index 000000000000..e7f713040709 --- /dev/null +++ b/erpnext/controllers/employee_boarding_controller.py @@ -0,0 +1,128 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +import erpnext +import frappe +from frappe import _ +from frappe.desk.form import assign_to +from frappe.model.document import Document +from frappe.utils import flt, unique + +class EmployeeBoardingController(Document): + ''' + Create the project and the task for the boarding process + Assign to the concerned person and roles as per the onboarding/separation template + ''' + def validate(self): + # remove the task if linked before submitting the form + if self.amended_from: + for activity in self.activities: + activity.task = '' + + def on_submit(self): + # create the project for the given employee onboarding + project_name = _(self.doctype) + ' : ' + if self.doctype == 'Employee Onboarding': + project_name += self.job_applicant + else: + project_name += self.employee + + project = frappe.get_doc({ + 'doctype': 'Project', + 'project_name': project_name, + 'expected_start_date': self.date_of_joining if self.doctype == 'Employee Onboarding' else self.resignation_letter_date, + 'department': self.department, + 'company': self.company + }).insert(ignore_permissions=True, ignore_mandatory=True) + + self.db_set('project', project.name) + self.db_set('boarding_status', 'Pending') + self.reload() + self.create_task_and_notify_user() + + def create_task_and_notify_user(self): + # create the task for the given project and assign to the concerned person + for activity in self.activities: + if activity.task: + continue + + task = frappe.get_doc({ + 'doctype': 'Task', + 'project': self.project, + 'subject': activity.activity_name + ' : ' + self.employee_name, + 'description': activity.description, + 'department': self.department, + 'company': self.company, + 'task_weight': activity.task_weight + }).insert(ignore_permissions=True) + activity.db_set('task', task.name) + + users = [activity.user] if activity.user else [] + if activity.role: + user_list = frappe.db.sql_list(''' + SELECT + DISTINCT(has_role.parent) + FROM + `tabHas Role` has_role + LEFT JOIN `tabUser` user + ON has_role.parent = user.name + WHERE + has_role.parenttype = 'User' + AND user.enabled = 1 + AND has_role.role = %s + ''', activity.role) + users = unique(users + user_list) + + if 'Administrator' in users: + users.remove('Administrator') + + # assign the task the users + if users: + self.assign_task_to_users(task, users) + + def assign_task_to_users(self, task, users): + for user in users: + args = { + 'assign_to': [user], + 'doctype': task.doctype, + 'name': task.name, + 'description': task.description or task.subject, + 'notify': self.notify_users_by_email + } + assign_to.add(args) + + def on_cancel(self): + # delete task project + for task in frappe.get_all('Task', filters={'project': self.project}): + frappe.delete_doc('Task', task.name, force=1) + frappe.delete_doc('Project', self.project, force=1) + self.db_set('project', '') + for activity in self.activities: + activity.db_set('task', '') + + +@frappe.whitelist() +def get_onboarding_details(parent, parenttype): + return frappe.get_all('Employee Boarding Activity', + fields=['activity_name', 'role', 'user', 'required_for_employee_creation', 'description', 'task_weight'], + filters={'parent': parent, 'parenttype': parenttype}, + order_by= 'idx') + + +def update_employee_boarding_status(project): + employee_onboarding = frappe.db.exists('Employee Onboarding', {'project': project.name}) + employee_separation = frappe.db.exists('Employee Separation', {'project': project.name}) + + if not (employee_onboarding or employee_separation): + return + + status = 'Pending' + if flt(project.percent_complete) > 0.0 and flt(project.percent_complete) < 100.0: + status = 'In Process' + elif flt(project.percent_complete) == 100.0: + status = 'Completed' + + if employee_onboarding: + frappe.db.set_value('Employee Onboarding', employee_onboarding, 'boarding_status', status) + elif employee_separation: + frappe.db.set_value('Employee Separation', employee_separation, 'boarding_status', status) diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js index bd72629c0dae..5d1a024ebb3f 100644 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js +++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js @@ -56,7 +56,7 @@ frappe.ui.form.on('Employee Onboarding', { frm.set_value("activities" ,""); if (frm.doc.employee_onboarding_template) { frappe.call({ - method: "erpnext.hr.utils.get_onboarding_details", + method: "erpnext.controllers.employee_boarding_controller.get_onboarding_details", args: { "parent": frm.doc.employee_onboarding_template, "parenttype": "Employee Onboarding Template" diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py index 6cc2bf5cd85b..55fe317b9eef 100644 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py +++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from erpnext.hr.utils import EmployeeBoardingController +from erpnext.controllers.employee_boarding_controller import EmployeeBoardingController from frappe.model.mapper import get_mapped_doc class IncompleteTaskError(frappe.ValidationError): pass @@ -16,9 +16,9 @@ def validate(self): self.validate_duplicate_employee_onboarding() def validate_duplicate_employee_onboarding(self): - emp_onboarding = frappe.db.exists("Employee Onboarding",{"job_applicant": self.job_applicant}) + emp_onboarding = frappe.db.exists("Employee Onboarding", {"job_applicant": self.job_applicant}) if emp_onboarding and emp_onboarding != self.name: - frappe.throw(_("Employee Onboarding: {0} is already for Job Applicant: {1}").format(frappe.bold(emp_onboarding), frappe.bold(self.job_applicant))) + frappe.throw(_("Employee Onboarding: {0} already exists for Job Applicant: {1}").format(frappe.bold(emp_onboarding), frappe.bold(self.job_applicant))) def validate_employee_creation(self): if self.docstatus != 1: @@ -30,7 +30,7 @@ def validate_employee_creation(self): else: task_status = frappe.db.get_value("Task", activity.task, "status") if task_status not in ["Completed", "Cancelled"]: - frappe.throw(_("All the mandatory Task for employee creation hasn't been done yet."), IncompleteTaskError) + frappe.throw(_("All the mandatory tasks for employee creation are not completed yet."), IncompleteTaskError) def on_submit(self): super(EmployeeOnboarding, self).on_submit() diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.js b/erpnext/hr/doctype/employee_separation/employee_separation.js index 33830796b6ca..d9011b2001b9 100644 --- a/erpnext/hr/doctype/employee_separation/employee_separation.js +++ b/erpnext/hr/doctype/employee_separation/employee_separation.js @@ -29,7 +29,7 @@ frappe.ui.form.on('Employee Separation', { frm.set_value("activities" ,""); if (frm.doc.employee_separation_template) { frappe.call({ - method: "erpnext.hr.utils.get_onboarding_details", + method: "erpnext.controllers.employee_boarding_controller.get_onboarding_details", args: { "parent": frm.doc.employee_separation_template, "parenttype": "Employee Separation Template" diff --git a/erpnext/hr/doctype/employee_separation/employee_separation.py b/erpnext/hr/doctype/employee_separation/employee_separation.py index b64668157b0b..8afee25d31c5 100644 --- a/erpnext/hr/doctype/employee_separation/employee_separation.py +++ b/erpnext/hr/doctype/employee_separation/employee_separation.py @@ -3,7 +3,7 @@ # For license information, please see license.txt from __future__ import unicode_literals -from erpnext.hr.utils import EmployeeBoardingController +from erpnext.controllers.employee_boarding_controller import EmployeeBoardingController class EmployeeSeparation(EmployeeBoardingController): def validate(self): diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 97420efe643d..3cc1a014d76f 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -13,125 +13,6 @@ class DuplicateDeclarationError(frappe.ValidationError): pass - -class EmployeeBoardingController(Document): - ''' - Create the project and the task for the boarding process - Assign to the concerned person and roles as per the onboarding/separation template - ''' - def validate(self): - # remove the task if linked before submitting the form - if self.amended_from: - for activity in self.activities: - activity.task = '' - - def on_submit(self): - # create the project for the given employee onboarding - project_name = _(self.doctype) + " : " - if self.doctype == "Employee Onboarding": - project_name += self.job_applicant - else: - project_name += self.employee - - project = frappe.get_doc({ - "doctype": "Project", - "project_name": project_name, - "expected_start_date": self.date_of_joining if self.doctype == "Employee Onboarding" else self.resignation_letter_date, - "department": self.department, - "company": self.company - }).insert(ignore_permissions=True, ignore_mandatory=True) - - self.db_set("project", project.name) - self.db_set("boarding_status", "Pending") - self.reload() - self.create_task_and_notify_user() - - def create_task_and_notify_user(self): - # create the task for the given project and assign to the concerned person - for activity in self.activities: - if activity.task: - continue - - task = frappe.get_doc({ - "doctype": "Task", - "project": self.project, - "subject": activity.activity_name + " : " + self.employee_name, - "description": activity.description, - "department": self.department, - "company": self.company, - "task_weight": activity.task_weight - }).insert(ignore_permissions=True) - activity.db_set("task", task.name) - - users = [activity.user] if activity.user else [] - if activity.role: - user_list = frappe.db.sql_list(''' - SELECT - DISTINCT(has_role.parent) - FROM - `tabHas Role` has_role - LEFT JOIN `tabUser` user - ON has_role.parent = user.name - WHERE - has_role.parenttype = 'User' - AND user.enabled = 1 - AND has_role.role = %s - ''', activity.role) - users = unique(users + user_list) - - if "Administrator" in users: - users.remove("Administrator") - - # assign the task the users - if users: - self.assign_task_to_users(task, users) - - def assign_task_to_users(self, task, users): - for user in users: - args = { - 'assign_to': [user], - 'doctype': task.doctype, - 'name': task.name, - 'description': task.description or task.subject, - 'notify': self.notify_users_by_email - } - assign_to.add(args) - - def on_cancel(self): - # delete task project - for task in frappe.get_all("Task", filters={"project": self.project}): - frappe.delete_doc("Task", task.name, force=1) - frappe.delete_doc("Project", self.project, force=1) - self.db_set('project', '') - for activity in self.activities: - activity.db_set("task", "") - - -@frappe.whitelist() -def get_onboarding_details(parent, parenttype): - return frappe.get_all("Employee Boarding Activity", - fields=["activity_name", "role", "user", "required_for_employee_creation", "description", "task_weight"], - filters={"parent": parent, "parenttype": parenttype}, - order_by= "idx") - -def update_employee_boarding_status(project): - employee_onboarding = frappe.db.exists('Employee Onboarding', {'project': project.name}) - employee_separation = frappe.db.exists('Employee Separation', {'project': project.name}) - - if not (employee_onboarding or employee_separation): - return - - status = 'Pending' - if flt(project.percent_complete) > 0.0 and flt(project.percent_complete) < 100.0: - status = 'In Process' - elif flt(project.percent_complete) == 100.0: - status = 'Completed' - - if employee_onboarding: - frappe.db.set_value('Employee Onboarding', employee_onboarding, 'boarding_status', status) - elif employee_separation: - frappe.db.set_value('Employee Separation', employee_separation, 'boarding_status', status) - def set_employee_name(doc): if doc.employee and not doc.employee_name: doc.employee_name = frappe.db.get_value("Employee", doc.employee, "employee_name") diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 0ee999062096..1e4b2b0b8653 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -14,7 +14,7 @@ from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday from frappe.model.document import Document from erpnext.education.doctype.student_attendance.student_attendance import get_holiday_list -from erpnext.hr.utils import update_employee_boarding_status +from erpnext.controllers.employee_boarding_controller import update_employee_boarding_status class Project(Document): def get_feed(self): From e81775d6f7cdc352a913db931882c0cd025150ce Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 24 Jun 2021 13:18:49 +0530 Subject: [PATCH 012/691] chore: remove unused import --- erpnext/controllers/employee_boarding_controller.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/controllers/employee_boarding_controller.py b/erpnext/controllers/employee_boarding_controller.py index e7f713040709..1898222916b3 100644 --- a/erpnext/controllers/employee_boarding_controller.py +++ b/erpnext/controllers/employee_boarding_controller.py @@ -1,7 +1,6 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -import erpnext import frappe from frappe import _ from frappe.desk.form import assign_to From 98c9b0e9edf8031b1afdb1647f4a3d48b3b39834 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Fri, 25 Jun 2021 16:11:17 +0530 Subject: [PATCH 013/691] refactor: remove unused func, sider fixes --- .../cogs_by_item_group/cogs_by_item_group.js | 18 +---------- .../cogs_by_item_group/cogs_by_item_group.py | 30 +++++++++---------- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js index bb780e50b24a..d7c50a66979f 100644 --- a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js +++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js @@ -11,24 +11,8 @@ frappe.query_reports["COGS By Item Group"] = { fieldtype: "Link", options: "Company", mandatory: true, - default: frappe.defaults.get_user_default("Company"), + default: frappe.defaults.get_user_default("Company"), }, - // { - // label: __("Account"), - // fieldname: "account", - // fieldtype: "Link", - // options: "Account", - // mandatory: true, - // get_query() { - // const company = frappe.query_report.get_filter_value('company'); - // return { - // "doctype": "Account", - // "filters": { - // "company": company, - // } - // } - // }, - // }, { label: __("From Date"), fieldname: "from_date", 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 e2c6f7928c8e..0d601738ff0d 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 @@ -52,13 +52,13 @@ def get_data(filters): assign_agg_values(leveled_dict) data = [] - for _, i in leveled_dict.items(): + for item in leveled_dict.items(): + i = item[1] if i['agg_value'] == 0: continue data.append(get_row(i['name'], i['agg_value'], i['is_group'], i['level'])) if i['self_value'] < i['agg_value'] and i['self_value'] > 0: data.append(get_row(i['name'], i['self_value'], 0, i['level'] + 1)) - # append_blank() return data @@ -76,9 +76,10 @@ def get_filtered_entries(filters): def get_stock_value_difference_list(filtered_entries): voucher_nos = [fe.get('voucher_no') for fe in filtered_entries] - svd_list = frappe.get_list('Stock Ledger Entry', - fields=['item_code','stock_value_difference'], - filters=[('voucher_no', 'in', voucher_nos)]) + svd_list = frappe.get_list( + 'Stock Ledger Entry', fields=['item_code','stock_value_difference'], + filters=[('voucher_no', 'in', voucher_nos)] + ) assign_item_groups_to_svd_list(svd_list) return svd_list @@ -155,22 +156,19 @@ def assign_item_groups_to_svd_list(svd_list): def get_item_groups_map(svd_list): # for items in svd_list: [{'item_code':'item_group'}] item_codes = set([i['item_code'] for i in svd_list]) - ig_list = frappe.get_list('Item', - fields=['item_code','item_group'], - filters=[('item_code', 'in', item_codes)]) + ig_list = frappe.get_list( + 'Item', fields=['item_code','item_group'], + filters=[('item_code', 'in', item_codes)] + ) return {i['item_code']:i['item_group'] for i in ig_list} -def append_blank(data): - if len(data) == 0: - data.append(get_row("", 0, 0, 0)) - - def get_item_groups_dict(): item_groups_list = frappe.get_all("Item Group", fields=("name", "is_group", "lft", "rgt")) - return { (i['lft'],i['rgt']):{'name':i['name'], 'is_group':i['is_group']} - for i in item_groups_list } + return {(i['lft'],i['rgt']):{'name':i['name'], 'is_group':i['is_group']} + for i in item_groups_list} def update_leveled_dict(leveled_dict): - for k in leveled_dict: leveled_dict[k].update({'self_value':0, 'agg_value':0}) + for k in leveled_dict: + leveled_dict[k].update({'self_value':0, 'agg_value':0}) From e9bc2f354b9d85f7810c30f46e0590b8ac5d17da Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 28 Jun 2021 19:01:52 +0530 Subject: [PATCH 014/691] feat: Increase number of supported currency exchanges Switch from frankfurter.app to exchange rate.host to accommodate more currency usage. --- erpnext/setup/utils.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 13269a828237..33af28687c61 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -93,21 +93,21 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No try: cache = frappe.cache() - key = "currency_exchange_rate_{0}:{1}:{2}".format(transaction_date,from_currency, to_currency) + key = "currency_exchange_rate_{0}:{1}:{2}".format(transaction_date, from_currency, to_currency) value = cache.get(key) if not value: import requests - api_url = "https://frankfurter.app/{0}".format(transaction_date) + api_url = "https://api.exchangerate.host/convert" response = requests.get(api_url, params={ - "base": from_currency, - "symbols": to_currency + "date": transaction_date, + "from": from_currency, + "to": to_currency }) # expire in 6 hours response.raise_for_status() - value = response.json()["rates"][to_currency] - - cache.set_value(key, value, expires_in_sec=6 * 60 * 60) + value = response.json()["result"] + cache.setex(key, value, 6 * 60 * 60) return flt(value) except: frappe.log_error(title="Get Exchange Rate") From 31afc61190b882515c2da3ce92c1bce1fb7f85b0 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 28 Jun 2021 19:05:51 +0530 Subject: [PATCH 015/691] fix: Update test cases to suit values from exchangerate.host --- .../test_currency_exchange.py | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py index c5c01c577584..4ff2dd7e0e97 100644 --- a/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py +++ b/erpnext/setup/doctype/currency_exchange/test_currency_exchange.py @@ -62,12 +62,12 @@ def test_exchange_rate(self): exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling") self.assertEqual(exchange_rate, 62.9) - - # Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io + + # Exchange rate as on 15th Dec, 2015 self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_selling") self.assertFalse(exchange_rate == 60) - self.assertEqual(flt(exchange_rate, 3), 66.894) + self.assertEqual(flt(exchange_rate, 3), 66.999) def test_exchange_rate_strict(self): # strict currency settings @@ -77,28 +77,17 @@ def test_exchange_rate_strict(self): exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01", "for_buying") self.assertEqual(exchange_rate, 60.0) - # Will fetch from fixer.io self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying") - self.assertEqual(flt(exchange_rate, 3), 67.79) + self.assertEqual(flt(exchange_rate, 3), 67.235) exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30", "for_selling") self.assertEqual(exchange_rate, 62.9) - # Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io + # Exchange rate as on 15th Dec, 2015 self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15", "for_buying") - self.assertEqual(flt(exchange_rate, 3), 66.894) - - exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-10", "for_selling") - self.assertEqual(exchange_rate, 65.1) - - # NGN is not available on fixer.io so these should return 0 - exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-09", "for_selling") - self.assertEqual(exchange_rate, 0) - - exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-11", "for_selling") - self.assertEqual(exchange_rate, 0) + self.assertEqual(flt(exchange_rate, 3), 66.999) def test_exchange_rate_strict_switched(self): # Start with allow_stale is True @@ -111,4 +100,4 @@ def test_exchange_rate_strict_switched(self): # Will fetch from fixer.io self.clear_cache() exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15", "for_buying") - self.assertEqual(flt(exchange_rate, 3), 67.79) + self.assertEqual(flt(exchange_rate, 3), 67.235) From 6e3a7b4a751d6fa53b8659695753cf17518f1579 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 29 Jun 2021 11:12:47 +0530 Subject: [PATCH 016/691] 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 5739a112de97..edaf46162e27 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} +
        +
        `); + + if (collapsed) + $node_group.addClass('collapsed'); + else + $node_group.addClass('mb-4'); - return $node_group; + 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 02446be11a16..b6d50a0470a0 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 017/691] 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 edaf46162e27..f693cf6ba67a 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 b6d50a0470a0..6012c015733b 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 018/691] 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 f693cf6ba67a..15334bd4ca55 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 6012c015733b..16b8792432e0 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 019/691] 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 15334bd4ca55..efb367ad44c3 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 020/691] 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 efb367ad44c3..0fe724c78e43 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); - }); - } - - setup_node_group_action() { - let me = this; - - $('.node-group').on('click', function() { - let parent = $(this).attr('data-parent'); - me.expand_sibling_group_node(parent); + organizational_chart.show(); }); - } - - 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 d3ebcdf7e7b7..3c60e3ee500d 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 000000000000..fd84d4ea5caa --- /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 000000000000..c705681438dc --- /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 021/691] 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 0fe724c78e43..ca9855286ca8 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 ae91a919b2b3..f3aa13897d5d 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 fd84d4ea5caa..052f140c1398 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 c705681438dc..1b8bc2e8e0f1 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 022/691] 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 ca9855286ca8..a1388867687c 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 052f140c1398..0823ec77a80b 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 1b8bc2e8e0f1..4b09714d0ad3 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 e42e54f690c2..30aedab4bb76 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 023/691] 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 7917e3abf590..ed7d58843476 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 024/691] refactor: use arcs instead of bezier curves for cleaner connectors --- .../hierarchy_chart_desktop.js | 52 ++++++++++++++++--- erpnext/public/scss/hierarchy_chart.scss | 2 +- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 0823ec77a80b..ba811be58698 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" + + 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) + " " + - "C" + - (pos_parent_right.x + 100) + "," + (pos_parent_right.y) + " " + - (pos_child_left.x - 100) + "," + (pos_child_left.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; + } - path.setAttribute("d", connector); + 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 16b8792432e0..16137fdb5f0c 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 025/691] 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 4b09714d0ad3..b09b428b3064 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 026/691] 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 30aedab4bb76..c3d8e010b53f 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 16137fdb5f0c..eefc14d67970 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 03f7609a8b14e951bf51ba093b3a8001a11a348b Mon Sep 17 00:00:00 2001 From: Anupam Date: Wed, 30 Jun 2021 18:18:03 +0530 Subject: [PATCH 027/691] fix: moving campaign from selling to CRM --- erpnext/crm/doctype/campaign/__init__.py | 0 erpnext/crm/doctype/campaign/campaign.js | 18 ++++++++++ .../doctype/campaign/campaign.json | 34 ++++++++++--------- .../doctype/campaign/campaign.py | 7 ++-- erpnext/crm/doctype/campaign/test_campaign.py | 8 +++++ erpnext/selling/doctype/campaign/README.md | 1 - erpnext/selling/doctype/campaign/__init__.py | 1 - erpnext/selling/doctype/campaign/campaign.js | 15 -------- .../doctype/campaign/campaign_dashboard.py | 17 ---------- .../selling/doctype/campaign/test_campaign.py | 7 ---- .../doctype/campaign/test_records.json | 10 ------ 11 files changed, 46 insertions(+), 72 deletions(-) create mode 100644 erpnext/crm/doctype/campaign/__init__.py create mode 100644 erpnext/crm/doctype/campaign/campaign.js rename erpnext/{selling => crm}/doctype/campaign/campaign.json (95%) rename erpnext/{selling => crm}/doctype/campaign/campaign.py (54%) create mode 100644 erpnext/crm/doctype/campaign/test_campaign.py delete mode 100644 erpnext/selling/doctype/campaign/README.md delete mode 100644 erpnext/selling/doctype/campaign/__init__.py delete mode 100644 erpnext/selling/doctype/campaign/campaign.js delete mode 100644 erpnext/selling/doctype/campaign/campaign_dashboard.py delete mode 100644 erpnext/selling/doctype/campaign/test_campaign.py delete mode 100644 erpnext/selling/doctype/campaign/test_records.json diff --git a/erpnext/crm/doctype/campaign/__init__.py b/erpnext/crm/doctype/campaign/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/crm/doctype/campaign/campaign.js b/erpnext/crm/doctype/campaign/campaign.js new file mode 100644 index 000000000000..04876541ba2d --- /dev/null +++ b/erpnext/crm/doctype/campaign/campaign.js @@ -0,0 +1,18 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Campaign', { + refresh: function(frm) { + erpnext.toggle_naming_series(); + + if(frm.doc.__islocal) { + frm.toggle_display("naming_series", frappe.boot.sysdefaults.campaign_naming_by=="Naming Series"); + } + else { + cur_frm.add_custom_button(__("View Leads"), function() { + frappe.route_options = {"source": "Campaign","campaign_name": frm.doc.name} + frappe.set_route("List", "Lead"); + }, "fa fa-list", true); + } + } +}); diff --git a/erpnext/selling/doctype/campaign/campaign.json b/erpnext/crm/doctype/campaign/campaign.json similarity index 95% rename from erpnext/selling/doctype/campaign/campaign.json rename to erpnext/crm/doctype/campaign/campaign.json index 986ac1306cd3..f833f4c9d11f 100644 --- a/erpnext/selling/doctype/campaign/campaign.json +++ b/erpnext/crm/doctype/campaign/campaign.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "naming_series:", @@ -39,17 +40,9 @@ "set_only_once": 1 }, { - "fieldname": "description", - "fieldtype": "Text", - "in_list_view": 1, - "label": "Description", - "oldfieldname": "description", - "oldfieldtype": "Text", - "width": "300px" - }, - { - "fieldname": "description_section", - "fieldtype": "Section Break" + "fieldname": "campaign_schedules_section", + "fieldtype": "Section Break", + "label": "Campaign Schedules" }, { "fieldname": "campaign_schedules", @@ -58,16 +51,25 @@ "options": "Campaign Email Schedule" }, { - "fieldname": "campaign_schedules_section", - "fieldtype": "Section Break", - "label": "Campaign Schedules" + "fieldname": "description_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "description", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", + "width": "300px" } ], "icon": "fa fa-bullhorn", "idx": 1, - "modified": "2019-07-22 12:03:39.832342", + "links": [], + "modified": "2021-06-30 18:05:06.412712", "modified_by": "Administrator", - "module": "Selling", + "module": "CRM", "name": "Campaign", "owner": "Administrator", "permissions": [ diff --git a/erpnext/selling/doctype/campaign/campaign.py b/erpnext/crm/doctype/campaign/campaign.py similarity index 54% rename from erpnext/selling/doctype/campaign/campaign.py rename to erpnext/crm/doctype/campaign/campaign.py index 10945428aee4..34331952c0b2 100644 --- a/erpnext/selling/doctype/campaign/campaign.py +++ b/erpnext/crm/doctype/campaign/campaign.py @@ -1,11 +1,8 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt +# 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 frappe.model.naming import set_name_by_naming_series class Campaign(Document): def autoname(self): diff --git a/erpnext/crm/doctype/campaign/test_campaign.py b/erpnext/crm/doctype/campaign/test_campaign.py new file mode 100644 index 000000000000..939bb8f46434 --- /dev/null +++ b/erpnext/crm/doctype/campaign/test_campaign.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +import frappe +import unittest + +class TestCampaign(unittest.TestCase): + pass diff --git a/erpnext/selling/doctype/campaign/README.md b/erpnext/selling/doctype/campaign/README.md deleted file mode 100644 index a83731840276..000000000000 --- a/erpnext/selling/doctype/campaign/README.md +++ /dev/null @@ -1 +0,0 @@ -Sales campaign / promotion, like special discount, exhibition, newsletter etc. \ No newline at end of file diff --git a/erpnext/selling/doctype/campaign/__init__.py b/erpnext/selling/doctype/campaign/__init__.py deleted file mode 100644 index baffc4882521..000000000000 --- a/erpnext/selling/doctype/campaign/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/selling/doctype/campaign/campaign.js b/erpnext/selling/doctype/campaign/campaign.js deleted file mode 100644 index 72a90d053f2b..000000000000 --- a/erpnext/selling/doctype/campaign/campaign.js +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.ui.form.on("Campaign", "refresh", function(frm) { - erpnext.toggle_naming_series(); - if(frm.doc.__islocal) { - frm.toggle_display("naming_series", frappe.boot.sysdefaults.campaign_naming_by=="Naming Series"); - } - else{ - cur_frm.add_custom_button(__("View Leads"), function() { - frappe.route_options = {"source": "Campaign","campaign_name": frm.doc.name} - frappe.set_route("List", "Lead"); - }, "fa fa-list", true); - } -}) diff --git a/erpnext/selling/doctype/campaign/campaign_dashboard.py b/erpnext/selling/doctype/campaign/campaign_dashboard.py deleted file mode 100644 index 3cef560c32f8..000000000000 --- a/erpnext/selling/doctype/campaign/campaign_dashboard.py +++ /dev/null @@ -1,17 +0,0 @@ -from __future__ import unicode_literals -from frappe import _ - -def get_data(): - return { - 'fieldname': 'campaign_name', - 'transactions': [ - { - 'label': _('Email Campaigns'), - 'items': ['Email Campaign'] - }, - { - 'label': _('Social Media Campaigns'), - 'items': ['Social Media Post'] - } - ] - } diff --git a/erpnext/selling/doctype/campaign/test_campaign.py b/erpnext/selling/doctype/campaign/test_campaign.py deleted file mode 100644 index 4d062ff84c6f..000000000000 --- a/erpnext/selling/doctype/campaign/test_campaign.py +++ /dev/null @@ -1,7 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt -from __future__ import unicode_literals - - -import frappe -test_records = frappe.get_test_records('Campaign') \ No newline at end of file diff --git a/erpnext/selling/doctype/campaign/test_records.json b/erpnext/selling/doctype/campaign/test_records.json deleted file mode 100644 index 625d3b377b2a..000000000000 --- a/erpnext/selling/doctype/campaign/test_records.json +++ /dev/null @@ -1,10 +0,0 @@ -[ - { - "campaign_name": "_Test Campaign", - "doctype": "Campaign" - }, - { - "campaign_name": "_Test Campaign 1", - "doctype": "Campaign" - } -] \ No newline at end of file From 865900fd2d634491e61f4f9191faac7fc880b07f Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Mon, 28 Jun 2021 12:52:22 +0530 Subject: [PATCH 028/691] refactor: add type hints, remove comment, sort imports --- .../cogs_by_item_group/cogs_by_item_group.py | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) 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 0d601738ff0d..9e5e63e37e2e 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 @@ -1,14 +1,28 @@ # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +from collections import OrderedDict +import datetime +from typing import Dict, List, Tuple, Union + import frappe from frappe import _ from frappe.utils import date_diff -from collections import OrderedDict + from erpnext.accounts.report.general_ledger.general_ledger import get_gl_entries -def execute(filters=None): +Filters = frappe._dict +Row = frappe._dict +Data = List[Row] +Columns = List[Dict[str, str]] +DateTime = Union[datetime.date, datetime.datetime] +FilteredEntries = List[Dict[str, Union[str, float, DateTime, None]]] +ItemGroupsDict = Dict[Tuple[int, int], Dict[str, Union[str, int]]] +SVDList = List[frappe._dict] + + +def execute(filters: Filters) -> Tuple[Columns, Data]: update_filters_with_account(filters) validate_filters(filters) columns = get_columns() @@ -16,17 +30,17 @@ def execute(filters=None): return columns, data -def update_filters_with_account(filters): +def update_filters_with_account(filters: Filters) -> None: account = frappe.get_value("Company", filters.get("company"), "default_expense_account") filters.update(dict(account=account)) -def validate_filters(filters): +def validate_filters(filters: Filters) -> None: if filters.from_date > filters.to_date: frappe.throw(_("From Date must be before To Date")) -def get_columns(): +def get_columns() -> Columns: return [ { 'label': 'Item Group', @@ -43,7 +57,7 @@ def get_columns(): ] -def get_data(filters): +def get_data(filters: Filters) -> Data: filtered_entries = get_filtered_entries(filters) svd_list = get_stock_value_difference_list(filtered_entries) leveled_dict = get_leveled_dict() @@ -62,7 +76,7 @@ def get_data(filters): return data -def get_filtered_entries(filters): +def get_filtered_entries(filters: Filters) -> FilteredEntries: gl_entries = get_gl_entries(filters, []) filtered_entries = [] for entry in gl_entries: @@ -74,7 +88,7 @@ def get_filtered_entries(filters): return filtered_entries -def get_stock_value_difference_list(filtered_entries): +def get_stock_value_difference_list(filtered_entries: FilteredEntries) -> SVDList: voucher_nos = [fe.get('voucher_no') for fe in filtered_entries] svd_list = frappe.get_list( 'Stock Ledger Entry', fields=['item_code','stock_value_difference'], @@ -84,7 +98,7 @@ def get_stock_value_difference_list(filtered_entries): return svd_list -def get_leveled_dict(): +def get_leveled_dict() -> OrderedDict: item_groups_dict = get_item_groups_dict() lr_list = sorted(item_groups_dict, key=lambda x : int(x[0])) leveled_dict = OrderedDict() @@ -109,14 +123,14 @@ def get_leveled_dict(): return leveled_dict -def assign_self_values(leveled_dict, svd_list): +def assign_self_values(leveled_dict: OrderedDict, svd_list: SVDList) -> None: key_dict = {v['name']:k for k, v in leveled_dict.items()} for item in svd_list: key = key_dict[item.get("item_group")] leveled_dict[key]['self_value'] += -item.get("stock_value_difference") -def assign_agg_values(leveled_dict): +def assign_agg_values(leveled_dict: OrderedDict) -> None: keys = list(leveled_dict.keys())[::-1] prev_level = leveled_dict[keys[-1]]['level'] accu = [0] @@ -141,21 +155,21 @@ def assign_agg_values(leveled_dict): leveled_dict[rk]['agg_value'] = sum(accu) + leveled_dict[rk]['self_value'] -def get_row(name:str, value:float, is_bold:int, indent:int): +def get_row(name:str, value:float, is_bold:int, indent:int) -> Row: item_group = name 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): +def assign_item_groups_to_svd_list(svd_list: SVDList) -> None: ig_map = get_item_groups_map(svd_list) for item in svd_list: item.item_group = ig_map[item.get("item_code")] -def get_item_groups_map(svd_list): - # for items in svd_list: [{'item_code':'item_group'}] - item_codes = set([i['item_code'] for i in svd_list]) + +def get_item_groups_map(svd_list: SVDList) -> Dict[str, str]: + item_codes = set(i['item_code'] for i in svd_list) ig_list = frappe.get_list( 'Item', fields=['item_code','item_group'], filters=[('item_code', 'in', item_codes)] @@ -163,12 +177,12 @@ def get_item_groups_map(svd_list): return {i['item_code']:i['item_group'] for i in ig_list} -def get_item_groups_dict(): +def get_item_groups_dict() -> ItemGroupsDict: item_groups_list = frappe.get_all("Item Group", fields=("name", "is_group", "lft", "rgt")) return {(i['lft'],i['rgt']):{'name':i['name'], 'is_group':i['is_group']} for i in item_groups_list} -def update_leveled_dict(leveled_dict): +def update_leveled_dict(leveled_dict: OrderedDict) -> None: for k in leveled_dict: leveled_dict[k].update({'self_value':0, 'agg_value':0}) From 991d3cdd76a0f4c20f405799ddc54d98951b28e7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 1 Jul 2021 21:17:17 +0530 Subject: [PATCH 029/691] fix: Incorrect discount amount on amended document --- erpnext/public/js/controllers/taxes_and_totals.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 1de9ec1a7dfe..52efbb5f6cda 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -67,6 +67,8 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ 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(); } From 2fcd05aa82042456b50c9dad42d1e97d57afb80f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 2 Jul 2021 18:15:18 +0530 Subject: [PATCH 030/691] 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 ba811be58698..9e82fb20024d 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 b09b428b3064..2ff00baa7c6c 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 eefc14d67970..a54bf6f332ee 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 be15e16b84f6f2b8bd3cf421c0a7ac06cc599988 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 5 Jul 2021 14:58:32 +0530 Subject: [PATCH 031/691] 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 703e93c0757c..676c6a8b479a 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 6e0d83d925241f04a9897fa39227624a043fe33f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 5 Jul 2021 15:09:05 +0530 Subject: [PATCH 032/691] 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 8e6952a93c43..b65903bf5e8a 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 cf7579e29f1bbf58eb13562016188f0552d64c93 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 6 Jul 2021 00:36:06 +0530 Subject: [PATCH 033/691] 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 55a5b99907b0..15e795135933 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -845,6 +845,7 @@ def get_gl_entries(self, warehouse_account=None): 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) @@ -958,6 +959,40 @@ def make_item_gl_entries(self, gl_entries): 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 b08bb1f1a2e679c3c0c01f4a304937243c84121d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 6 Jul 2021 16:28:19 +0530 Subject: [PATCH 034/691] 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 bb5565167022..8672e7b1a699 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 ef667a9a64b5e4b23551c4b150b4371b6f0e72b7 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 6 Jul 2021 16:29:19 +0530 Subject: [PATCH 035/691] 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 6fed9efa63fb..f1c413e00a0e 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 657e9b5320d42f2622c0663c358bd08221282de3 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 6 Jul 2021 16:51:12 +0530 Subject: [PATCH 036/691] 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 ca174a3f63c0..662ca379096e 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -288,6 +288,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, @@ -590,6 +591,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 ad066033925ed8f8e5bd6c2e8b7f9c3cc54e5e27 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 6 Jul 2021 18:16:49 +0530 Subject: [PATCH 037/691] 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 9e82fb20024d..1896ac7c39ec 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 2ff00baa7c6c..102cbb03b0ab 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 3ea4f993b799685c4d11ddde003547faaba5da42 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 6 Jul 2021 20:22:13 +0530 Subject: [PATCH 038/691] 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 ac4a2d6f16de..9e33eb395b76 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -21,6 +21,7 @@ def validate(self): 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 @@ def enable_payment_schedule_in_print(self): 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 6d5ee25bba0222b122d873340784cc82ee8309fc Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 7 Jul 2021 09:43:28 +0530 Subject: [PATCH 039/691] 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 1896ac7c39ec..8d0685f80d56 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 040/691] feat: handle multiple root / orphan nodes --- .../organizational_chart.py | 3 +- .../hierarchy_chart_desktop.js | 50 +++++++++---------- .../hierarchy_chart/hierarchy_chart_mobile.js | 37 +++++++------- erpnext/public/scss/hierarchy_chart.scss | 4 ++ 4 files changed, 51 insertions(+), 43 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index f3aa13897d5d..77b8df7520bf 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 8d0685f80d56..e89a98ac4f8a 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 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, + let nodes = r.message; + 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; }); - 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 102cbb03b0ab..5eee27b5fc33 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 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, + 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 + }); }); - - 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 a54bf6f332ee..dd523c344398 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 041/691] 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 | 84 +++++++++---------- .../hierarchy_chart/hierarchy_chart_mobile.js | 65 +++++++------- 3 files changed, 79 insertions(+), 92 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 77b8df7520bf..46578f3aaf75 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,33 +2,25 @@ 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 e89a98ac4f8a..bf366792a9cf 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; - - $.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; + } + }).then(r => { + if (r.message.length) { + let node = undefined; + let first_root = undefined; + + $.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 5eee27b5fc33..17062e258576 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 042/691] 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 46578f3aaf75..ce84b3c7444d 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 bf366792a9cf..374787c6ef24 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 043/691] 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 17062e258576..19852993789f 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 c3d8e010b53f..fb94df85ed8f 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 044/691] 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 ce84b3c7444d..1e03e3d06add 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,8 +2,7 @@ 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 045/691] 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 19852993789f..d48b4c8f362c 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 5a62467a3e028bb6d857a27a661d9bbeaee9d184 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 9 Jul 2021 00:53:42 +0530 Subject: [PATCH 046/691] feat: Fetch Payment Terms from linked Purchase Order --- .../purchase_invoice/purchase_invoice.js | 21 +++++++++- .../purchase_invoice/purchase_invoice.py | 39 +++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 7562418fd2ff..10b83c027f7c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -508,6 +508,8 @@ frappe.ui.form.on("Purchase Invoice", { } } } + + frm.events.set_payment_terms(frm); }, refresh: function(frm) { @@ -570,4 +572,21 @@ frappe.ui.form.on("Purchase Invoice", { company: function(frm) { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, -}) + + set_payment_terms: function (frm) { + frappe.call({ + 'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.set_payment_terms_from_po', + 'args': { + doc: frm.doc + }, + 'callback': (r) => { + if (r.message) { + var doc = frappe.model.sync(r.message)[0]; + console.log("doc: ", doc) + frappe.set_route("Form", doc.doctype, doc.name); + } + } + + }); + }, +}) \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c1cc092554da..fb143b45cefe 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -7,6 +7,8 @@ from frappe.utils import cint, cstr, formatdate, flt, getdate, nowdate, get_link_to_form from frappe import _, throw import frappe.defaults +import json +import six from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.controllers.buying_controller import BuyingController @@ -1171,6 +1173,43 @@ def set_status(self, update=False, status=None, update_modified=True): 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 linked_po_has_payment_terms(doc, purchase_order) and all_items_have_same_po(doc, purchase_order): + purchase_order = frappe.get_cached_doc('Purchase Order', purchase_order) + doc['payment_terms_template'] = purchase_order.payment_terms_template + doc['terms'] = 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 linked_po_has_payment_terms(doc, purchase_order): + return not doc.get('payment_schedule') and purchase_order + +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 + # 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': From 24b31c0bf93a53c4f98fe5020b9575288b2a9aab Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 9 Jul 2021 01:03:02 +0530 Subject: [PATCH 047/691] 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 d48b4c8f362c..58530eaaf911 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 048/691] 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 58530eaaf911..5a6f16887673 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 bf245e5e39cdeb2ccdd51a63ad1e2385fbedc24d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 9 Jul 2021 01:44:34 +0530 Subject: [PATCH 049/691] fix: Clear Payment Schedule if PI has default Payment Schedule, but linked PO doensn't --- .../doctype/purchase_invoice/purchase_invoice.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index fb143b45cefe..f30af0c349ed 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1180,10 +1180,14 @@ def set_payment_terms_from_po(doc): purchase_order = doc.get('items')[0].get('purchase_order') - if linked_po_has_payment_terms(doc, purchase_order) and all_items_have_same_po(doc, 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 - doc['terms'] = purchase_order.payment_terms_template for schedule in purchase_order.payment_schedule: payment_schedule = { @@ -1200,9 +1204,6 @@ def set_payment_terms_from_po(doc): return doc -def linked_po_has_payment_terms(doc, purchase_order): - return not doc.get('payment_schedule') and purchase_order - def all_items_have_same_po(doc, purchase_order): for item in doc.get('items'): if item.get('purchase_order') != purchase_order: @@ -1210,6 +1211,11 @@ def all_items_have_same_po(doc, purchase_order): 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': From 38fa3a3f8927abf7242481d61c2d72e6879af608 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 9 Jul 2021 18:04:24 +0530 Subject: [PATCH 050/691] fix: Unallocated amount in Payment Entry after taxes --- .../doctype/payment_entry/payment_entry.py | 56 +++++++++---------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index adaf99a79002..889c59762cf5 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -404,9 +404,15 @@ def set_tax_withholding(self): if not self.advance_tax_account: frappe.throw(_("Advance TDS account is mandatory for advance TDS deduction")) - reference_doclist = [] net_total = self.paid_amount - included_in_paid_amount = 0 + + for reference in self.get("references"): + 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 # Adding args as purchase invoice to get TDS amount args = frappe._dict({ @@ -423,7 +429,6 @@ def set_tax_withholding(self): return tax_withholding_details.update({ - 'included_in_paid_amount': included_in_paid_amount, 'cost_center': self.cost_center or erpnext.get_default_cost_center(self.company) }) @@ -509,18 +514,17 @@ def set_total_allocated_amount(self): self.base_total_allocated_amount = abs(base_total_allocated_amount) def set_unallocated_amount(self): - self.unallocated_amount = 0 if self.party: total_deductions = sum(flt(d.amount) for d in self.get("deductions")) if self.payment_type == "Receive" \ - and self.base_total_allocated_amount < self.base_received_amount_after_tax + total_deductions \ - and self.total_allocated_amount < self.paid_amount_after_tax + (total_deductions / self.source_exchange_rate): - self.unallocated_amount = (self.received_amount_after_tax + total_deductions - + 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.base_total_allocated_amount) / self.source_exchange_rate elif self.payment_type == "Pay" \ - and self.base_total_allocated_amount < (self.base_paid_amount_after_tax - total_deductions) \ - and self.total_allocated_amount < self.received_amount_after_tax + (total_deductions / self.target_exchange_rate): - self.unallocated_amount = (self.base_paid_amount_after_tax - (total_deductions + + and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \ + and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate): + self.unallocated_amount = (self.base_paid_amount - (total_deductions + self.base_total_allocated_amount)) / self.target_exchange_rate def set_difference_amount(self): @@ -530,11 +534,11 @@ def set_difference_amount(self): base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount) if self.payment_type == "Receive": - self.difference_amount = base_party_amount - self.base_received_amount_after_tax + self.difference_amount = base_party_amount - self.base_received_amount elif self.payment_type == "Pay": - self.difference_amount = self.base_paid_amount_after_tax - base_party_amount + self.difference_amount = self.base_paid_amount - base_party_amount else: - self.difference_amount = self.base_paid_amount_after_tax - flt(self.base_received_amount_after_tax) + self.difference_amount = self.base_paid_amount - flt(self.base_received_amount) total_deductions = sum(flt(d.amount) for d in self.get("deductions")) @@ -683,8 +687,8 @@ def add_bank_gl_entries(self, gl_entries): "account": self.paid_from, "account_currency": self.paid_from_account_currency, "against": self.party if self.payment_type=="Pay" else self.paid_to, - "credit_in_account_currency": self.paid_amount_after_tax, - "credit": self.base_paid_amount_after_tax, + "credit_in_account_currency": self.paid_amount, + "credit": self.base_paid_amount, "cost_center": self.cost_center }, item=self) ) @@ -694,8 +698,8 @@ def add_bank_gl_entries(self, gl_entries): "account": self.paid_to, "account_currency": self.paid_to_account_currency, "against": self.party if self.payment_type=="Receive" else self.paid_from, - "debit_in_account_currency": self.received_amount_after_tax, - "debit": self.base_received_amount_after_tax, + "debit_in_account_currency": self.received_amount, + "debit": self.base_received_amount, "cost_center": self.cost_center }, item=self) ) @@ -708,15 +712,17 @@ def add_tax_gl_entries(self, gl_entries): if self.payment_type in ('Pay', 'Internal Transfer'): dr_or_cr = "debit" if d.add_deduct_tax == "Add" else "credit" + against = self.party or self.paid_from elif self.payment_type == 'Receive': dr_or_cr = "credit" if d.add_deduct_tax == "Add" else "debit" + against = self.party or self.paid_to payment_or_advance_account = self.get_party_account_for_taxes() gl_entries.append( self.get_gl_dict({ "account": d.account_head, - "against": self.party if self.payment_type=="Receive" else self.paid_from, + "against": against, dr_or_cr: d.base_tax_amount, dr_or_cr + "_in_account_currency": d.base_tax_amount if account_currency==self.company_currency @@ -728,14 +734,12 @@ def add_tax_gl_entries(self, gl_entries): gl_entries.append( self.get_gl_dict({ "account": payment_or_advance_account, - "against": self.party if self.payment_type=="Receive" else self.paid_from, + "against": against, dr_or_cr: -1 * d.base_tax_amount, dr_or_cr + "_in_account_currency": -1*d.base_tax_amount if account_currency==self.company_currency else d.tax_amount, "cost_center": self.cost_center, - "party_type": self.party_type, - "party": self.party }, account_currency, item=d)) def add_deductions_gl_entries(self, gl_entries): @@ -760,9 +764,9 @@ def get_party_account_for_taxes(self): if self.advance_tax_account: return self.advance_tax_account elif self.payment_type == 'Receive': - return self.paid_from - elif self.payment_type in ('Pay', 'Internal Transfer'): return self.paid_to + elif self.payment_type in ('Pay', 'Internal Transfer'): + return self.paid_from def update_advance_paid(self): if self.payment_type in ("Receive", "Pay") and self.party: @@ -1634,12 +1638,6 @@ def set_paid_amount_and_received_amount(dt, party_account_currency, bank, outsta if dt == "Employee Advance": paid_amount = received_amount * doc.get('exchange_rate', 1) - if dt == "Purchase Order" and doc.apply_tds: - if party_account_currency == bank.account_currency: - paid_amount = received_amount = doc.base_net_total - else: - paid_amount = received_amount = doc.base_net_total * doc.get('exchange_rate', 1) - return paid_amount, received_amount def apply_early_payment_discount(paid_amount, received_amount, doc): From 171ee515074e183ff3a59c120ea29b2ea84b96e9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 9 Jul 2021 18:52:12 +0530 Subject: [PATCH 051/691] fix: Hide amount after tax fields --- .../accounts/doctype/payment_entry/payment_entry.json | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 51f18a5a4e37..6f362c1fbb97 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -667,6 +667,7 @@ { "fieldname": "base_paid_amount_after_tax", "fieldtype": "Currency", + "hidden": 1, "label": "Paid Amount After Tax (Company Currency)", "options": "Company:company:default_currency", "read_only": 1 @@ -693,21 +694,25 @@ "depends_on": "eval:doc.received_amount && doc.payment_type != 'Internal Transfer'", "fieldname": "received_amount_after_tax", "fieldtype": "Currency", + "hidden": 1, "label": "Received Amount After Tax", - "options": "paid_to_account_currency" + "options": "paid_to_account_currency", + "read_only": 1 }, { "depends_on": "doc.received_amount", "fieldname": "base_received_amount_after_tax", "fieldtype": "Currency", + "hidden": 1, "label": "Received Amount After Tax (Company Currency)", - "options": "Company:company:default_currency" + "options": "Company:company:default_currency", + "read_only": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-06-22 20:37:06.154206", + "modified": "2021-07-09 08:58:15.008761", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", From c13ac4ab11495b5067e41355c14140dc19c8782c Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 9 Jul 2021 20:00:55 +0530 Subject: [PATCH 052/691] fix: Remove unintentional changes --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 889c59762cf5..e3dbc22ca0fc 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -521,6 +521,7 @@ def set_unallocated_amount(self): and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate): self.unallocated_amount = (self.received_amount + total_deductions - self.base_total_allocated_amount) / self.source_exchange_rate + print(self.unallocated_amount, "#@#@#@#@#") elif self.payment_type == "Pay" \ and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \ and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate): From eae7c1891fa434f380487e7f51db68a92b04596d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 9 Jul 2021 20:08:29 +0530 Subject: [PATCH 053/691] fix: Remove unintentional changes --- 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 e3dbc22ca0fc..85b98843ee30 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -514,6 +514,7 @@ def set_total_allocated_amount(self): self.base_total_allocated_amount = abs(base_total_allocated_amount) def set_unallocated_amount(self): + self.unallocated_amount = 0 if self.party: total_deductions = sum(flt(d.amount) for d in self.get("deductions")) if self.payment_type == "Receive" \ @@ -521,7 +522,6 @@ def set_unallocated_amount(self): and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate): self.unallocated_amount = (self.received_amount + total_deductions - self.base_total_allocated_amount) / self.source_exchange_rate - print(self.unallocated_amount, "#@#@#@#@#") elif self.payment_type == "Pay" \ and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \ and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate): From 9e26f2d797ee17b332463e9aace65b516c0c2deb Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 9 Jul 2021 22:08:56 +0530 Subject: [PATCH 054/691] fix: Organize buttons --- erpnext/assets/doctype/asset/asset.js | 65 ++++++++++++------- erpnext/assets/doctype/asset/asset.py | 11 +++- .../doctype/asset_repair/asset_repair.json | 42 +++++------- 3 files changed, 69 insertions(+), 49 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 6f1bb28f3709..2a57183a8027 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -82,52 +82,57 @@ 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); - }); - } - - if (frm.doc.purchase_receipt || !frm.doc.is_existing_asset) { - frm.add_custom_button("General Ledger", function() { - frappe.route_options = { - "voucher_no": frm.doc.name, - "from_date": frm.doc.available_for_use_date, - "to_date": frm.doc.available_for_use_date, - "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.add_custom_button(__("Maintain Asset"), function() { frm.trigger("create_asset_maintenance"); - }, __('Create')); + }, __("Manage")); } + + frm.add_custom_button(__("Repair Asset"), function() { + frm.trigger("create_asset_repair"); + }, __("Manage")); + if (frm.doc.status != 'Fully Depreciated') { - frm.add_custom_button(__("Asset Value Adjustment"), function() { + frm.add_custom_button(__("Adjust Asset Value"), function() { frm.trigger("create_asset_adjustment"); - }, __('Create')); + }, __("Manage")); } if (!frm.doc.calculate_depreciation) { - frm.add_custom_button(__("Depreciation Entry"), function() { + frm.add_custom_button(__("Create Depreciation Entry"), function() { frm.trigger("make_journal_entry"); - }, __('Create')); + }, __("Manage")); + } + + if (frm.doc.purchase_receipt || !frm.doc.is_existing_asset) { + frm.add_custom_button("View General Ledger", function() { + frappe.route_options = { + "voucher_no": frm.doc.name, + "from_date": frm.doc.available_for_use_date, + "to_date": frm.doc.available_for_use_date, + "company": frm.doc.company + }; + frappe.set_route("query-report", "General Ledger"); + }, __("Manage")); } - 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 8799275fc4e8..456649fa07e2 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 d338fc0fb794..853534eb3197 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 055/691] 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 4ba2b4474a91..f5eeeda5fb12 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 853534eb3197..4ed9916392a2 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 049b931b5e8e..884dc1958826 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -13,6 +13,10 @@ def validate(self): 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 061986d92d76..2d2f336e932f 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 056/691] 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 2a57183a8027..1e67ec816b65 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 421b9a6c3782..8a0e3ad2a662 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 456649fa07e2..e8cfe0ae17f5 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -96,6 +96,9 @@ def set_missing_values(self): 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 @@ def set_depreciation_rate(self): 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 @@ def make_depreciation_schedule(self): 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 @@ def make_depreciation_schedule(self): # 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 @@ def set_accumulated_depreciation(self, ignore_booked_entry = False): 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 70b8654509f7..3830d1168c43 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 c0c2566fe236..da2fd754512d 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 a506deec93ee..e3e654c398b7 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -19,10 +19,45 @@ def validate(self): 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 f5eeeda5fb12..7633a595a2f3 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 4ed9916392a2..522f2874d999 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 884dc1958826..8fd019febd00 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 000000000000..e69de29bb2d1 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 000000000000..b1f05db39502 --- /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 000000000000..0e3cc3f8ba7b --- /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 2d2f336e932f..e6ec496a65e7 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 057/691] 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 e8cfe0ae17f5..3bd20023abf9 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -183,13 +183,12 @@ def make_depreciation_schedule(self): 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 8fd019febd00..9973afd80ad8 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -152,7 +152,6 @@ def modify_depreciation_schedule(self): 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 058/691] 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 1e67ec816b65..922cc4a7b26d 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 3bd20023abf9..b3d3a198c332 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -181,7 +181,7 @@ def make_depreciation_schedule(self): 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 3830d1168c43..19393b7e9db9 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 9973afd80ad8..3e81ba55b007 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 059/691] 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 3e81ba55b007..e3093df7fb01 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -47,7 +47,8 @@ def on_submit(self): 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 060/691] 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 e3093df7fb01..070e7b0a9b86 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -72,7 +72,7 @@ def increase_asset_value(self): 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 061/691] 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 070e7b0a9b86..9f096438642b 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -117,7 +117,8 @@ def get_gl_entries(self): "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 @@ def get_gl_entries(self): "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 062/691] 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 8845f24d1049..29fbc9f15d64 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 063/691] 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 9f096438642b..4ca6fbc5d04b 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -172,7 +172,6 @@ def calculate_last_schedule_date(self, asset, row, extra_months): 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 3d325a9683c8..9c9dd4497132 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 064/691] 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 4ca6fbc5d04b..6a5776a83578 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -76,7 +76,8 @@ def increase_asset_value(self): 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 065/691] 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 522f2874d999..640762d82c0b 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 066/691] 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 7633a595a2f3..fdb8d0af67f1 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 640762d82c0b..baae93d468ea 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 067/691] 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 6a5776a83578..6565f356aef7 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -56,9 +56,9 @@ def check_repair_status(self): 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 068/691] 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 baae93d468ea..17d33c41eccb 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 069/691] 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 17d33c41eccb..840589ca2ed7 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 6565f356aef7..bd5e462f1323 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -77,7 +77,7 @@ def decrease_stock_quantity(self): 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 @@ def make_gl_entries(self, cancel=False): 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 @@ def get_gl_entries(self): "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 @@ def get_gl_entries(self): "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 070/691] 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 fdb8d0af67f1..1e87722179d3 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 071/691] 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 840589ca2ed7..d3335c5afba6 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 bd5e462f1323..01eeb36e3a9f 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -45,7 +45,6 @@ def on_submit(self): 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 @@ def decrease_stock_quantity(self): 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 072/691] 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 01eeb36e3a9f..63cdaf5cdeb6 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.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 @@ def make_gl_entries(self, cancel=False): 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 @@ def get_gl_entries(self): }) 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 073/691] 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 d3335c5afba6..88e75a168fee 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 074/691] 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 63cdaf5cdeb6..5f4c382079c2 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -38,7 +38,6 @@ def calculate_total_repair_cost(self): 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 @@ def check_for_stock_items_and_warehouse(self): 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 075/691] 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 1e87722179d3..2319b069b0c0 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 076/691] 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 5f4c382079c2..92f7408ba75a 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.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 @@ def make_gl_entries(self, cancel=False): 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 077/691] 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 2319b069b0c0..efa6a9d4949b 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 078/691] 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 d9b7b695f7f3..ee3a2072f0f7 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 079/691] 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 8a0e3ad2a662..d55258c8f67d 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 b3d3a198c332..4820f8b48799 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -96,9 +96,6 @@ def set_missing_values(self): 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 @@ def make_depreciation_schedule(self): 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 ee3a2072f0f7..e5a5f194c1bc 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 92f7408ba75a..2d039190e360 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -61,13 +61,19 @@ def check_for_stock_items_and_warehouse(self): 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 080/691] 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 2d039190e360..7864cb70a5e1 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -120,19 +120,23 @@ def get_gl_entries(self): }, 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 081/691] 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 4820f8b48799..9273f01da759 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -176,13 +176,8 @@ def make_depreciation_schedule(self): 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 @@ def make_depreciation_schedule(self): "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 082/691] 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 9273f01da759..18e3ffc8a6f2 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -179,7 +179,8 @@ def make_depreciation_schedule(self): 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 @@ def make_depreciation_schedule(self): "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 @@ def clear_depreciation_schedule(self): 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 7864cb70a5e1..0befee70cb9b 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -47,7 +47,7 @@ def on_submit(self): 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 @@ def get_gl_entries(self): 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 083/691] 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 18e3ffc8a6f2..93b05ebd5c89 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 a4466e78f28d..11b19ae69681 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 084/691] 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 d55258c8f67d..d77eb104184b 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 93b05ebd5c89..63b70f661321 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -223,7 +223,7 @@ def make_depreciation_schedule(self): # 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 0befee70cb9b..da237f09f077 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -162,7 +162,7 @@ def modify_depreciation_schedule(self): 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 @@ def modify_depreciation_schedule(self): # 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 11b19ae69681..81c0918b99a7 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 085/691] 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 88e75a168fee..89f7fa3bca85 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 086/691] 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 89f7fa3bca85..ee18c4bdc4d1 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 da237f09f077..c074cc930e60 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -92,6 +92,8 @@ def decrease_stock_quantity(self): 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 @@ def get_gl_entries(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') + 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 087/691] 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 ee18c4bdc4d1..cfa084e6063a 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 088/691] 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 cfa084e6063a..a0fe63280276 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 089/691] 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 efa6a9d4949b..ced3dad1e5fc 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 090/691] 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 a0fe63280276..6bcddbfb9463 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 c074cc930e60..5fccfb76a520 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -6,74 +6,72 @@ 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 @@ def decrease_stock_quantity(self): "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 @@ def get_gl_entries(self): }, 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 @@ def get_gl_entries(self): 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 9c9dd4497132..d1b417fd384b 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 @@ def test_increase_in_asset_life(self): 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 091/691] 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 da2fd754512d..63a55389d816 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 6bcddbfb9463..14f18b5309c4 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 b1f05db39502..528f0ec986a7 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 0e3cc3f8ba7b..fa22a5712f4b 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 092/691] 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 ced3dad1e5fc..91bed4fdd03c 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 5fccfb76a520..79b9a6a2b570 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 @@ def update_status(self): 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 093/691] 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 79b9a6a2b570..e7b8b45b7ee2 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -24,9 +24,9 @@ def update_status(self): 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 @@ def on_submit(self): 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 @@ def check_for_stock_items_and_warehouse(self): 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 @@ def increase_asset_value(self): 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 @@ def decrease_stock_quantity(self): 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 094/691] 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 e7b8b45b7ee2..01b36880be06 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -42,15 +42,25 @@ def on_submit(self): 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 @@ def decrease_stock_quantity(self): 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 @@ def increase_stock_quantity(self): 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 @@ def increase_stock_quantity(self): 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 @@ def calculate_last_schedule_date(self, asset, row, extra_months): 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 095/691] 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 29fbc9f15d64..f3667c7b95fe 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 096/691] 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 01b36880be06..0049dcaded50 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -118,26 +118,11 @@ def decrease_stock_quantity(self): 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 097/691] 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 19393b7e9db9..70b8654509f7 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 63a55389d816..c0c2566fe236 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 e3e654c398b7..a506deec93ee 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.py @@ -19,45 +19,10 @@ def validate(self): 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 098/691] 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 0049dcaded50..6054258ea69d 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 @@ def update_status(self): 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 099/691] 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 d1b417fd384b..52a960e85097 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 @@ def test_decrease_stock_quantity(self): 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 @@ def test_increase_in_asset_life(self): 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 100/691] 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 63b70f661321..110922e66b9f 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -182,10 +182,10 @@ def make_depreciation_schedule(self): # 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 101/691] 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 52a960e85097..b3d78b3bfbbe 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 @@ def setUp(self): 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 102/691] 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 f3667c7b95fe..32bdb5224a78 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -125,7 +125,6 @@ def test_schedule_for_straight_line_method_for_existing_asset(self): "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 103/691] 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 32bdb5224a78..59fbe3b0301c 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -153,9 +153,8 @@ def test_schedule_for_double_declining_method(self): "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 @@ def test_schedule_for_double_declining_method_for_existing_asset(self): "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 @@ def test_schedule_for_prorated_straight_line_method(self): "depreciation_start_date": "2030-12-31" }) - asset.insert() asset.save() expected_schedules = [ @@ -246,7 +244,6 @@ def test_depreciation(self): "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 @@ def test_depreciation_entry_cancellation(self): "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 @@ def test_scrap_asset(self): "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 @@ def test_asset_sale(self): "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 @@ def test_asset_expected_value_after_useful_life(self): "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 104/691] 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 6054258ea69d..64c51fd8c3e3 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -35,7 +35,7 @@ def calculate_total_repair_cost(self): 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 @@ def on_submit(self): 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 105/691] 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 d77eb104184b..de060757e2e6 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 106/691] 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 110922e66b9f..0e1bba63621f 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -196,7 +196,7 @@ def make_depreciation_schedule(self): 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 107/691] 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 0e1bba63621f..2fe71672b37d 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -224,6 +224,7 @@ def make_depreciation_schedule(self): # 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 108/691] 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 91bed4fdd03c..1cebfff66e51 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 109/691] 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 14f18b5309c4..3f62443bda4d 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 110/691] 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 b3d78b3bfbbe..30bbb37851e1 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 111/691] 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 3f62443bda4d..19528a26ccd7 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 112/691] 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 64c51fd8c3e3..d32fdf7054f2 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -30,7 +30,7 @@ def set_total_value(self): 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 @@ def decrease_stock_quantity(self): 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 @@ def calculate_last_schedule_date_before_modification(self, asset, row, extra_mon @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 77f2d2d01ea19c53cb3e726cbfaaa0b2fceb0eb4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 10 Jul 2021 10:06:38 +0530 Subject: [PATCH 113/691] fix: Unable to download GSTR-1 json --- erpnext/regional/report/gstr_1/gstr_1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 10961593e1cc..cfcb8c3444f1 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -584,7 +584,7 @@ def get_columns(self): def get_json(filters, report_name, data): filters = json.loads(filters) report_data = json.loads(data) - gstin = get_company_gstin_number(filters["company"], filters["company_address"]) + gstin = get_company_gstin_number(filters.get("company"), filters.get("company_address")) fp = "%02d%s" % (getdate(filters["to_date"]).month, getdate(filters["to_date"]).year) From e282effaed59e27e82875f90c6237ae4f99ba668 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 10 Jul 2021 20:23:52 +0530 Subject: [PATCH 114/691] fix: Error on creation of company for India --- erpnext/regional/india/setup.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 5f9d5ed0d611..5ef04b66c7de 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -122,10 +122,12 @@ def add_print_formats(): def make_property_setters(patch=False): # GST rules do not allow for an invoice no. bigger than 16 characters journal_entry_types = frappe.get_meta("Journal Entry").get_options("voucher_type").split("\n") + ['Reversal Of ITC'] + sales_invoice_series = frappe.get_meta("Sales Invoice").get_options("naming_series").split("\n") + ['SINV-.YY.-', 'SRET-.YY.-', ''] + purchase_invoice_series = frappe.get_meta("Purchase Invoice").get_options("naming_series").split("\n") + ['PINV-.YY.-', 'PRET-.YY.-', ''] if not patch: - make_property_setter('Sales Invoice', 'naming_series', 'options', 'SINV-.YY.-\nSRET-.YY.-', '') - make_property_setter('Purchase Invoice', 'naming_series', 'options', 'PINV-.YY.-\nPRET-.YY.-', '') + make_property_setter('Sales Invoice', 'naming_series', 'options', '\n'.join(sales_invoice_series), '') + make_property_setter('Purchase Invoice', 'naming_series', 'options', '\n'.join(purchase_invoice_series), '') make_property_setter('Journal Entry', 'voucher_type', 'options', '\n'.join(journal_entry_types), '') def make_custom_fields(update=True): From 38994bd49480c55484c07e71aa52eb30ca91b485 Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Mon, 12 Jul 2021 13:01:31 +0530 Subject: [PATCH 115/691] fix: Added Company filters for Loan (#26294) * fix: loan validations * fix: added company filter while fetching loans * fix: tests --- erpnext/loan_management/doctype/loan/loan.js | 3 +- .../loan_application/loan_application.js | 7 ++++ .../doctype/salary_slip/salary_slip.py | 1 + .../doctype/salary_slip/test_salary_slip.py | 15 ++++--- .../salary_structure/test_salary_structure.py | 39 +++++++++---------- 5 files changed, 39 insertions(+), 26 deletions(-) diff --git a/erpnext/loan_management/doctype/loan/loan.js b/erpnext/loan_management/doctype/loan/loan.js index 28af3a9c4152..f9c201ab6038 100644 --- a/erpnext/loan_management/doctype/loan/loan.js +++ b/erpnext/loan_management/doctype/loan/loan.js @@ -28,7 +28,8 @@ frappe.ui.form.on('Loan', { frm.set_query("loan_type", function () { return { "filters": { - "docstatus": 1 + "docstatus": 1, + "company": frm.doc.company } }; }); diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.js b/erpnext/loan_management/doctype/loan_application/loan_application.js index 13652749711d..eccbdc3e919b 100644 --- a/erpnext/loan_management/doctype/loan_application/loan_application.js +++ b/erpnext/loan_management/doctype/loan_application/loan_application.js @@ -14,6 +14,13 @@ frappe.ui.form.on('Loan Application', { refresh: function(frm) { frm.trigger("toggle_fields"); frm.trigger("add_toolbar_buttons"); + frm.set_query('loan_type', () => { + return { + filters: { + company: frm.doc.company + } + }; + }); }, repayment_method: function(frm) { frm.doc.repayment_amount = frm.doc.repayment_periods = "" diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 877503b41cbf..bead880ef70f 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -1091,6 +1091,7 @@ def get_loan_details(self): "applicant": self.employee, "docstatus": 1, "repay_from_salary": 1, + "company": self.company }) def make_loan_repayment_entry(self): diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py index ce88cc3f1e19..6e8d3b3f3067 100644 --- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py @@ -482,14 +482,19 @@ def make_employee_salary_slip(user, payroll_frequency, salary_structure=None): salary_structure = payroll_frequency + " Salary Structure Test for Salary Slip" - employee = frappe.db.get_value("Employee", {"user_id": user}) - salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee=employee) + employee = frappe.db.get_value("Employee", + { + "user_id": user + }, + ["name", "company", "employee_name"], + as_dict=True) + + salary_structure_doc = make_salary_structure(salary_structure, payroll_frequency, employee=employee.name, company=employee.company) salary_slip_name = frappe.db.get_value("Salary Slip", {"employee": frappe.db.get_value("Employee", {"user_id": user})}) if not salary_slip_name: - salary_slip = make_salary_slip(salary_structure_doc.name, employee = employee) - salary_slip.employee_name = frappe.get_value("Employee", - {"name":frappe.db.get_value("Employee", {"user_id": user})}, "employee_name") + salary_slip = make_salary_slip(salary_structure_doc.name, employee = employee.name) + salary_slip.employee_name = employee.employee_name salary_slip.payroll_frequency = payroll_frequency salary_slip.posting_date = nowdate() salary_slip.insert() diff --git a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py index e7d123c9960e..3957d834d33d 100644 --- a/erpnext/payroll/doctype/salary_structure/test_salary_structure.py +++ b/erpnext/payroll/doctype/salary_structure/test_salary_structure.py @@ -119,26 +119,25 @@ def make_salary_structure(salary_structure, payroll_frequency, employee=None, if test_tax: frappe.db.sql("""delete from `tabSalary Structure` where name=%s""",(salary_structure)) - if not frappe.db.exists('Salary Structure', salary_structure): - details = { - "doctype": "Salary Structure", - "name": salary_structure, - "company": company or erpnext.get_default_company(), - "earnings": make_earning_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]), - "deductions": make_deduction_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]), - "payroll_frequency": payroll_frequency, - "payment_account": get_random("Account", filters={'account_currency': currency}), - "currency": currency - } - if other_details and isinstance(other_details, dict): - details.update(other_details) - salary_structure_doc = frappe.get_doc(details) - salary_structure_doc.insert() - if not dont_submit: - salary_structure_doc.submit() - - else: - salary_structure_doc = frappe.get_doc("Salary Structure", salary_structure) + if frappe.db.exists("Salary Structure", salary_structure): + frappe.db.delete("Salary Structure", salary_structure) + + details = { + "doctype": "Salary Structure", + "name": salary_structure, + "company": company or erpnext.get_default_company(), + "earnings": make_earning_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]), + "deductions": make_deduction_salary_component(setup=True, test_tax=test_tax, company_list=["_Test Company"]), + "payroll_frequency": payroll_frequency, + "payment_account": get_random("Account", filters={'account_currency': currency}), + "currency": currency + } + if other_details and isinstance(other_details, dict): + details.update(other_details) + salary_structure_doc = frappe.get_doc(details) + salary_structure_doc.insert() + if not dont_submit: + salary_structure_doc.submit() filters = {'employee':employee, 'docstatus': 1} if not from_date and payroll_period: From 45e6cffa4f9eacd4d299e4a5bf220b970a6c9105 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 Jul 2021 13:24:43 +0530 Subject: [PATCH 116/691] refactor: Optimized code for reposting item valuation --- .../stock/doctype/stock_entry/stock_entry.py | 2 +- .../stock_ledger_entry/stock_ledger_entry.py | 1 + erpnext/stock/stock_ledger.py | 63 +++++++++++++++---- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 8f27ef4356c1..90b81ddb1dc4 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -529,7 +529,7 @@ def get_basic_rate_for_manufactured_item(self, finished_item_qty, outgoing_items scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_scrap_item]) # Get raw materials cost from BOM if multiple material consumption entries - if frappe.db.get_single_value("Manufacturing Settings", "material_consumption"): + if frappe.db.get_single_value("Manufacturing Settings", "material_consumption", cache=True): bom_items = self.get_bom_raw_materials(finished_item_qty) outgoing_items_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()]) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 0febcb68910d..cb939e63c281 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -178,3 +178,4 @@ def on_doctype_update(): frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"]) frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"]) + frappe.db.add_index("Stock Ledger Entry", ["voucher_detail_no"]) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 4e9c7689ae46..c15d1eda7dcf 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -6,13 +6,14 @@ import erpnext import copy from frappe import _ -from frappe.utils import cint, flt, cstr, now, get_link_to_form +from frappe.utils import cint, flt, cstr, now, get_link_to_form, getdate from frappe.model.meta import get_field_precision from erpnext.stock.utils import get_valuation_method, get_incoming_outgoing_rate_for_cancel from erpnext.stock.utils import get_bin import json from six import iteritems + # future reposting class NegativeStockError(frappe.ValidationError): pass class SerialNoExistsInFutureTransaction(frappe.ValidationError): @@ -130,7 +131,13 @@ def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negat if not args and voucher_type and voucher_no: args = get_args_for_voucher(voucher_type, voucher_no) - distinct_item_warehouses = [(d.item_code, d.warehouse) for d in args] + 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 + })) i = 0 while i < len(args): @@ -139,13 +146,21 @@ def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negat "warehouse": args[i].warehouse, "posting_date": args[i].posting_date, "posting_time": args[i].posting_time, - "creation": args[i].get("creation") + "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) - for item_wh, new_sle in iteritems(obj.new_items): - if item_wh not in distinct_item_warehouses: - args.append(new_sle) - + distinct_item_warehouses[(args[i].item_code, args[i].warehouse)].reposting_status = True + + if obj.new_items_found: + for item_wh, data in iteritems(distinct_item_warehouses): + if ('args_idx' not in data and not data.reposting_status) or (data.sle_changed and data.reposting_status): + data.args_idx = len(args) + 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): @@ -186,11 +201,12 @@ def __init__(self, args, allow_zero_rate=False, allow_negative_stock=None, via_l self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company") self.get_precision() self.valuation_method = get_valuation_method(self.item_code) - self.new_items = {} + + self.new_items_found = False + self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict()) self.data = frappe._dict() self.initialize_previous_data(self.args) - self.build() def get_precision(self): @@ -296,11 +312,29 @@ def get_dependent_entries_to_fix(self, entries_to_fix, sle): elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse: return entries_to_fix elif dependant_sle.item_code != self.item_code: - if (dependant_sle.item_code, dependant_sle.warehouse) not in self.new_items: - self.new_items[(dependant_sle.item_code, dependant_sle.warehouse)] = dependant_sle + self.update_distinct_item_warehouses(dependant_sle) return entries_to_fix elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse in self.data: return entries_to_fix + else: + return self.append_future_sle_for_dependant(dependant_sle, entries_to_fix) + + def update_distinct_item_warehouses(self, dependant_sle): + key = (dependant_sle.item_code, dependant_sle.warehouse) + val = frappe._dict({ + "sle": dependant_sle + }) + if key not in self.distinct_item_warehouses: + self.distinct_item_warehouses[key] = val + self.new_items_found = True + else: + existing_sle_posting_date = self.distinct_item_warehouses[key].get("sle", {}).get("posting_date") + if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date): + val.sle_changed = True + self.distinct_item_warehouses[key] = val + self.new_items_found = True + + def append_future_sle_for_dependant(self, dependant_sle, entries_to_fix): self.initialize_previous_data(dependant_sle) args = self.data[dependant_sle.warehouse].previous_sle \ @@ -393,6 +427,7 @@ def get_incoming_outgoing_rate_from_transaction(self, sle): rate = 0 # Material Transfer, Repack, Manufacturing if sle.voucher_type == "Stock Entry": + self.recalculate_amounts_in_stock_entry(sle.voucher_no) rate = frappe.db.get_value("Stock Entry Detail", sle.voucher_detail_no, "valuation_rate") # Sales and Purchase Return elif sle.voucher_type in ("Purchase Receipt", "Purchase Invoice", "Delivery Note", "Sales Invoice"): @@ -442,7 +477,11 @@ def update_rate_on_stock_entry(self, sle, outgoing_rate): frappe.db.set_value("Stock Entry Detail", sle.voucher_detail_no, "basic_rate", outgoing_rate) # Update outgoing item's rate, recalculate FG Item's rate and total incoming/outgoing amount - stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no, for_update=True) + if not sle.dependant_sle_voucher_detail_no: + self.recalculate_amounts_in_stock_entry(sle.voucher_no) + + def recalculate_amounts_in_stock_entry(self, voucher_no): + stock_entry = frappe.get_doc("Stock Entry", voucher_no, for_update=True) stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False) stock_entry.db_update() for d in stock_entry.items: From b75b556bbbe3760d3bcd75c378cb081f349836e2 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 12 Jul 2021 14:32:37 +0530 Subject: [PATCH 117/691] fix: move the rename abbreviation job to long queue (#26435) --- erpnext/setup/doctype/company/company.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 915e6a4f316a..36a7d20a8ffb 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -395,7 +395,7 @@ def on_trash(self): @frappe.whitelist() def enqueue_replace_abbr(company, old, new): - kwargs = dict(company=company, old=old, new=new) + kwargs = dict(queue="long", company=company, old=old, new=new) frappe.enqueue('erpnext.setup.doctype.company.company.replace_abbr', **kwargs) From 1298956482515f6067781f7eb0b404fa25f512a9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 12 Jul 2021 18:29:52 +0530 Subject: [PATCH 118/691] fix: Use update flag for company dependant fixtures --- erpnext/regional/india/setup.py | 11 +++++++---- erpnext/setup/doctype/company/company.py | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 5ef04b66c7de..92654608da52 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -12,7 +12,10 @@ from frappe.utils import today def setup(company=None, patch=True): - setup_company_independent_fixtures(patch=patch) + # Company independent fixtures should be called only once at the first company setup + if frappe.db.count('Company', {'country': 'India'}) <=1: + setup_company_independent_fixtures(patch=patch) + if not patch: make_fixtures(company) @@ -122,8 +125,8 @@ def add_print_formats(): def make_property_setters(patch=False): # GST rules do not allow for an invoice no. bigger than 16 characters journal_entry_types = frappe.get_meta("Journal Entry").get_options("voucher_type").split("\n") + ['Reversal Of ITC'] - sales_invoice_series = frappe.get_meta("Sales Invoice").get_options("naming_series").split("\n") + ['SINV-.YY.-', 'SRET-.YY.-', ''] - purchase_invoice_series = frappe.get_meta("Purchase Invoice").get_options("naming_series").split("\n") + ['PINV-.YY.-', 'PRET-.YY.-', ''] + sales_invoice_series = ['SINV-.YY.-', 'SRET-.YY.-', ''] + frappe.get_meta("Sales Invoice").get_options("naming_series").split("\n") + purchase_invoice_series = ['PINV-.YY.-', 'PRET-.YY.-', ''] + frappe.get_meta("Purchase Invoice").get_options("naming_series").split("\n") if not patch: make_property_setter('Sales Invoice', 'naming_series', 'options', '\n'.join(sales_invoice_series), '') @@ -788,7 +791,7 @@ def set_tax_withholding_category(company): doc.flags.ignore_mandatory = True doc.insert() else: - doc = frappe.get_doc("Tax Withholding Category", d.get("name")) + doc = frappe.get_doc("Tax Withholding Category", d.get("name"), for_update=True) if accounts: doc.append("accounts", accounts[0]) diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 915e6a4f316a..382510d0bef1 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -291,7 +291,7 @@ def set_mode_of_payment_account(self): cash = frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name') if cash and self.default_cash_account \ and not frappe.db.get_value('Mode of Payment Account', {'company': self.name, 'parent': cash}): - mode_of_payment = frappe.get_doc('Mode of Payment', cash) + mode_of_payment = frappe.get_doc('Mode of Payment', cash, for_update=True) mode_of_payment.append('accounts', { 'company': self.name, 'default_account': self.default_cash_account From 7fb64d1645f65c4b1789cb0ed4e41ecd8893bd3d Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 12 Jul 2021 18:33:16 +0530 Subject: [PATCH 119/691] fix: exchange gain loss not set for advances linked with invoices (#26436) --- .../doctype/payment_entry/payment_entry.py | 18 +- .../payment_entry_reference.json | 12 +- .../purchase_invoice/purchase_invoice.py | 1 + .../purchase_invoice/test_purchase_invoice.py | 103 ++++++ .../purchase_invoice_advance.json | 330 ++++++----------- .../doctype/sales_invoice/sales_invoice.py | 1 + .../sales_invoice_advance.json | 331 ++++++------------ erpnext/accounts/utils.py | 14 +- erpnext/controllers/accounts_controller.py | 86 ++++- 9 files changed, 441 insertions(+), 455 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 0c21aae944c8..ff00fde523f2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -183,6 +183,13 @@ def set_missing_ref_details(self, force=False): d.reference_name, self.party_account_currency) 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 + # and row.exchange_rate is already set to payment entry's exchange rate + # refer -> `update_reference_in_payment_entry()` in utils.py + continue + if field == 'exchange_rate' or not d.get(field) or force: d.db_set(field, value) @@ -664,8 +671,8 @@ def add_party_gl_entries(self, gl_entries): gl_entries.append(gle) if self.unallocated_amount: - base_unallocated_amount = self.unallocated_amount * \ - (self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate) + exchange_rate = self.get_exchange_rate() + base_unallocated_amount = (self.unallocated_amount * exchange_rate) gle = party_gl_dict.copy() @@ -806,10 +813,17 @@ def set_gain_or_loss(self, account_details=None): if account_details: row.update(account_details) + + if not row.get('amount'): + # if no difference amount + return self.append('deductions', row) self.set_unallocated_amount() + def get_exchange_rate(self): + return self.source_exchange_rate if self.payment_type=="Receive" else self.target_exchange_rate + def initialize_taxes(self): for tax in self.get("taxes"): validate_taxes_and_charges(tax) diff --git a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json index 912ad0977a23..43eb0b6e2aa7 100644 --- a/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json +++ b/erpnext/accounts/doctype/payment_entry_reference/payment_entry_reference.json @@ -14,7 +14,8 @@ "total_amount", "outstanding_amount", "allocated_amount", - "exchange_rate" + "exchange_rate", + "exchange_gain_loss" ], "fields": [ { @@ -90,12 +91,19 @@ "fieldtype": "Link", "label": "Payment Term", "options": "Payment Term" + }, + { + "fieldname": "exchange_gain_loss", + "fieldtype": "Currency", + "label": "Exchange Gain/Loss", + "options": "Company:company:default_currency", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-02-10 11:25:47.144392", + "modified": "2021-04-21 13:30:11.605388", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry Reference", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 45d89ad1c87c..f7992797ed43 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -451,6 +451,7 @@ def get_gl_entries(self, warehouse_account=None): self.get_asset_gl_entry(gl_entries) self.make_tax_gl_entries(gl_entries) + self.make_exchange_gain_loss_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) self.allocate_advance_taxes(gl_entries) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 311745d3cd84..c9384be6eb30 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -953,6 +953,109 @@ def test_deferred_expense_via_journal_entry(self): acc_settings.submit_journal_entriessubmit_journal_entries = 0 acc_settings.save() + def test_gain_loss_with_advance_entry(self): + unlink_enabled = frappe.db.get_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice") + frappe.db.set_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", 1) + pay = frappe.get_doc({ + 'doctype': 'Payment Entry', + 'company': '_Test Company', + 'payment_type': 'Pay', + 'party_type': 'Supplier', + 'party': '_Test Supplier USD', + 'paid_to': '_Test Payable USD - _TC', + 'paid_from': 'Cash - _TC', + 'paid_amount': 70000, + 'target_exchange_rate': 70, + 'received_amount': 1000, + }) + pay.insert() + pay.submit() + + pi = make_purchase_invoice(supplier='_Test Supplier USD', currency="USD", + conversion_rate=75, rate=500, do_not_save=1, qty=1) + pi.cost_center = "_Test Cost Center - _TC" + pi.advances = [] + pi.append("advances", { + "reference_type": "Payment Entry", + "reference_name": pay.name, + "advance_amount": 1000, + "remarks": pay.remarks, + "allocated_amount": 500, + "ref_exchange_rate": 70 + }) + pi.save() + pi.submit() + + expected_gle = [ + ["_Test Account Cost for Goods Sold - _TC", 37500.0], + ["_Test Payable USD - _TC", -40000.0], + ["Exchange Gain/Loss - _TC", 2500.0] + ] + + gl_entries = frappe.db.sql(""" + select account, sum(debit - credit) as balance from `tabGL Entry` + where voucher_no=%s + group by account order by account asc""", (pi.name), as_dict=1) + + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_gle[i][0], gle.account) + self.assertEqual(expected_gle[i][1], gle.balance) + + pi_2 = make_purchase_invoice(supplier='_Test Supplier USD', currency="USD", + conversion_rate=73, rate=500, do_not_save=1, qty=1) + pi_2.cost_center = "_Test Cost Center - _TC" + pi_2.advances = [] + pi_2.append("advances", { + "reference_type": "Payment Entry", + "reference_name": pay.name, + "advance_amount": 500, + "remarks": pay.remarks, + "allocated_amount": 500, + "ref_exchange_rate": 70 + }) + pi_2.save() + pi_2.submit() + + expected_gle = [ + ["_Test Account Cost for Goods Sold - _TC", 36500.0], + ["_Test Payable USD - _TC", -38000.0], + ["Exchange Gain/Loss - _TC", 1500.0] + ] + + gl_entries = frappe.db.sql(""" + select account, sum(debit - credit) as balance from `tabGL Entry` + where voucher_no=%s + group by account order by account asc""", (pi_2.name), as_dict=1) + + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_gle[i][0], gle.account) + self.assertEqual(expected_gle[i][1], gle.balance) + + expected_gle = [ + ["_Test Payable USD - _TC", 70000.0], + ["Cash - _TC", -70000.0] + ] + + gl_entries = frappe.db.sql(""" + select account, sum(debit - credit) as balance from `tabGL Entry` + where voucher_no=%s and is_cancelled=0 + group by account order by account asc""", (pay.name), as_dict=1) + + for i, gle in enumerate(gl_entries): + self.assertEqual(expected_gle[i][0], gle.account) + self.assertEqual(expected_gle[i][1], gle.balance) + + pi.reload() + pi.cancel() + + pi_2.reload() + pi_2.cancel() + + pay.reload() + pay.cancel() + + frappe.db.set_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled) + def test_purchase_invoice_advance_taxes(self): from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry diff --git a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json index 5801b17f66f9..63dfff8921f9 100644 --- a/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json +++ b/erpnext/accounts/doctype/purchase_invoice_advance/purchase_invoice_advance.json @@ -1,235 +1,127 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-03-08 15:36:46", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "actions": [], + "creation": "2013-03-08 15:36:46", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "reference_type", + "reference_name", + "remarks", + "reference_row", + "col_break1", + "advance_amount", + "allocated_amount", + "exchange_gain_loss", + "ref_exchange_rate" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_type", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Reference Type", - "length": 0, - "no_copy": 1, - "oldfieldname": "journal_voucher", - "oldfieldtype": "Link", - "options": "DocType", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "180px", - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "fieldname": "reference_type", + "fieldtype": "Link", + "label": "Reference Type", + "no_copy": 1, + "oldfieldname": "journal_voucher", + "oldfieldtype": "Link", + "options": "DocType", + "print_width": "180px", + "read_only": 1, "width": "180px" - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Reference Name", - "length": 0, - "no_copy": 1, - "options": "reference_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 3, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference Name", + "no_copy": 1, + "options": "reference_type", + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "remarks", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Remarks", - "length": 0, - "no_copy": 1, - "oldfieldname": "remarks", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 3, + "fieldname": "remarks", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Remarks", + "no_copy": 1, + "oldfieldname": "remarks", + "oldfieldtype": "Small Text", + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_row", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Reference Row", - "length": 0, - "no_copy": 1, - "oldfieldname": "jv_detail_no", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "80px", - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "fieldname": "reference_row", + "fieldtype": "Data", + "hidden": 1, + "label": "Reference Row", + "no_copy": 1, + "oldfieldname": "jv_detail_no", + "oldfieldtype": "Date", + "print_hide": 1, + "print_width": "80px", + "read_only": 1, "width": "80px" - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "advance_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Advance Amount", - "length": 0, - "no_copy": 1, - "oldfieldname": "advance_amount", - "oldfieldtype": "Currency", - "options": "party_account_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 2, + "fieldname": "advance_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Advance Amount", + "no_copy": 1, + "oldfieldname": "advance_amount", + "oldfieldtype": "Currency", + "options": "party_account_currency", + "print_width": "100px", + "read_only": 1, "width": "100px" - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "allocated_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Allocated Amount", - "length": 0, - "no_copy": 1, - "oldfieldname": "allocated_amount", - "oldfieldtype": "Currency", - "options": "party_account_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 2, + "fieldname": "allocated_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Allocated Amount", + "no_copy": 1, + "oldfieldname": "allocated_amount", + "oldfieldtype": "Currency", + "options": "party_account_currency", + "print_width": "100px", "width": "100px" + }, + { + "fieldname": "exchange_gain_loss", + "fieldtype": "Currency", + "label": "Exchange Gain/Loss", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "ref_exchange_rate", + "fieldtype": "Float", + "label": "Reference Exchange Rate", + "non_negative": 1, + "read_only": 1 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "menu_index": 0, - "modified": "2016-08-26 02:30:54.407138", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Purchase Invoice Advance", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_order": "DESC", - "track_seen": 0 + ], + "idx": 1, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-04-20 16:26:53.820530", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Purchase Invoice Advance", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 55a5b99907b0..6d1f6249c139 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -840,6 +840,7 @@ def get_gl_entries(self, warehouse_account=None): self.make_customer_gl_entry(gl_entries) self.make_tax_gl_entries(gl_entries) + self.make_exchange_gain_loss_gl_entries(gl_entries) self.make_internal_transfer_gl_entries(gl_entries) self.allocate_advance_taxes(gl_entries) diff --git a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json index 14bf4d81330a..29422d68cf6c 100644 --- a/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json +++ b/erpnext/accounts/doctype/sales_invoice_advance/sales_invoice_advance.json @@ -1,235 +1,128 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-02-22 01:27:41", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, + "actions": [], + "creation": "2013-02-22 01:27:41", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "reference_type", + "reference_name", + "remarks", + "reference_row", + "col_break1", + "advance_amount", + "allocated_amount", + "exchange_gain_loss", + "ref_exchange_rate" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_type", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Reference Type", - "length": 0, - "no_copy": 1, - "oldfieldname": "journal_voucher", - "oldfieldtype": "Link", - "options": "DocType", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "250px", - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "fieldname": "reference_type", + "fieldtype": "Link", + "label": "Reference Type", + "no_copy": 1, + "oldfieldname": "journal_voucher", + "oldfieldtype": "Link", + "options": "DocType", + "print_width": "250px", + "read_only": 1, "width": "250px" - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Reference Name", - "length": 0, - "no_copy": 1, - "options": "reference_type", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 3, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Reference Name", + "no_copy": 1, + "options": "reference_type", + "print_hide": 1, + "read_only": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "remarks", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Remarks", - "length": 0, - "no_copy": 1, - "oldfieldname": "remarks", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 3, + "fieldname": "remarks", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Remarks", + "no_copy": 1, + "oldfieldname": "remarks", + "oldfieldtype": "Small Text", + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_row", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Reference Row", - "length": 0, - "no_copy": 1, - "oldfieldname": "jv_detail_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "120px", - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "fieldname": "reference_row", + "fieldtype": "Data", + "hidden": 1, + "label": "Reference Row", + "no_copy": 1, + "oldfieldname": "jv_detail_no", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "120px", + "read_only": 1, "width": "120px" - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "advance_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Advance amount", - "length": 0, - "no_copy": 1, - "oldfieldname": "advance_amount", - "oldfieldtype": "Currency", - "options": "party_account_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "120px", - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 2, + "fieldname": "advance_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Advance amount", + "no_copy": 1, + "oldfieldname": "advance_amount", + "oldfieldtype": "Currency", + "options": "party_account_currency", + "print_width": "120px", + "read_only": 1, "width": "120px" - }, + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "allocated_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Allocated amount", - "length": 0, - "no_copy": 1, - "oldfieldname": "allocated_amount", - "oldfieldtype": "Currency", - "options": "party_account_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "120px", - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 2, + "fieldname": "allocated_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Allocated amount", + "no_copy": 1, + "oldfieldname": "allocated_amount", + "oldfieldtype": "Currency", + "options": "party_account_currency", + "print_width": "120px", "width": "120px" + }, + { + "fieldname": "exchange_gain_loss", + "fieldtype": "Currency", + "label": "Exchange Gain/Loss", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "ref_exchange_rate", + "fieldtype": "Float", + "label": "Reference Exchange Rate", + "non_negative": 1, + "read_only": 1 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "menu_index": 0, - "modified": "2016-08-26 02:36:10.718057", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Sales Invoice Advance", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_order": "DESC", - "track_seen": 0 + ], + "idx": 1, + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-06-04 20:25:49.832052", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Sales Invoice Advance", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index ed6e28da1e6b..1cdbd8d38a61 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -472,7 +472,8 @@ def update_reference_in_payment_entry(d, payment_entry, do_not_save=False): "total_amount": d.grand_total, "outstanding_amount": d.outstanding_amount, "allocated_amount": d.allocated_amount, - "exchange_rate": d.exchange_rate + "exchange_rate": d.exchange_rate if not d.exchange_gain_loss else payment_entry.get_exchange_rate(), + "exchange_gain_loss": d.exchange_gain_loss # only populated from invoice in case of advance allocation } if d.voucher_detail_no: @@ -498,12 +499,15 @@ def update_reference_in_payment_entry(d, payment_entry, do_not_save=False): payment_entry.set_amounts() if d.difference_amount and d.difference_account: - payment_entry.set_gain_or_loss(account_details={ + account_details = { 'account': d.difference_account, 'cost_center': payment_entry.cost_center or frappe.get_cached_value('Company', - payment_entry.company, "cost_center"), - 'amount': d.difference_amount - }) + payment_entry.company, "cost_center") + } + if d.difference_amount: + account_details['amount'] = d.difference_amount + + payment_entry.set_gain_or_loss(account_details=account_details) if not do_not_save: payment_entry.save(ignore_permissions=True) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 1c086e9edcda..a9860ed2f057 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -124,6 +124,8 @@ def validate(self): if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)): self.set_advances() + self.set_advance_gain_or_loss() + if self.is_return: self.validate_qty() else: @@ -584,15 +586,18 @@ def set_advances(self): allocated_amount = min(amount - advance_allocated, d.amount) advance_allocated += flt(allocated_amount) - self.append("advances", { + advance_row = { "doctype": self.doctype + " Advance", "reference_type": d.reference_type, "reference_name": d.reference_name, "reference_row": d.reference_row, "remarks": d.remarks, "advance_amount": flt(d.amount), - "allocated_amount": allocated_amount - }) + "allocated_amount": allocated_amount, + "ref_exchange_rate": flt(d.exchange_rate) # exchange_rate of advance entry + } + + self.append("advances", advance_row) def get_advance_entries(self, include_unallocated=True): if self.doctype == "Sales Invoice": @@ -650,6 +655,66 @@ def validate_advance_entries(self): "Payment Entry {0} is linked against Order {1}, check if it should be pulled as advance in this invoice.") .format(d.reference_name, d.against_order)) + def set_advance_gain_or_loss(self): + if not self.get("advances"): + return + + for d in self.get("advances"): + advance_exchange_rate = d.ref_exchange_rate + if (d.allocated_amount and self.conversion_rate != 1 + and self.conversion_rate != advance_exchange_rate): + + base_allocated_amount_in_ref_rate = advance_exchange_rate * d.allocated_amount + base_allocated_amount_in_inv_rate = self.conversion_rate * d.allocated_amount + difference = base_allocated_amount_in_ref_rate - base_allocated_amount_in_inv_rate + + d.exchange_gain_loss = difference + + def make_exchange_gain_loss_gl_entries(self, gl_entries): + 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" + + gain_loss_account = frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account') + 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)) + + # 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' + + gl_entries.append( + self.get_gl_dict({ + "account": gain_loss_account, + "account_currency": account_currency, + "against": party, + dr_or_cr + "_in_account_currency": abs(d.exchange_gain_loss), + dr_or_cr: abs(d.exchange_gain_loss), + "cost_center": self.cost_center, + "project": self.project + }, item=d) + ) + + dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit' + + gl_entries.append( + self.get_gl_dict({ + "account": party_account, + "party_type": party_type, + "party": party, + "against": gain_loss_account, + dr_or_cr + "_in_account_currency": flt(abs(d.exchange_gain_loss) / self.conversion_rate), + dr_or_cr: abs(d.exchange_gain_loss), + "cost_center": self.cost_center, + "project": self.project + }, self.party_account_currency, item=self) + ) + def update_against_document_in_jv(self): """ Links invoice and advance voucher: @@ -690,7 +755,9 @@ def update_against_document_in_jv(self): if self.party_account_currency != self.company_currency else 1), 'grand_total': (self.base_grand_total if self.party_account_currency == self.company_currency else self.grand_total), - 'outstanding_amount': self.outstanding_amount + 'outstanding_amount': self.outstanding_amount, + 'difference_account': frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account'), + 'exchange_gain_loss': flt(d.get('exchange_gain_loss')) }) lst.append(args) @@ -1289,6 +1356,8 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype, party_account_field = "paid_from" if party_type == "Customer" else "paid_to" currency_field = "paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency" payment_type = "Receive" if party_type == "Customer" else "Pay" + exchange_rate_field = "source_exchange_rate" if payment_type == "Receive" else "target_exchange_rate" + payment_entries_against_order, unallocated_payment_entries = [], [] limit_cond = "limit %s" % limit if limit else "" @@ -1305,27 +1374,28 @@ def get_advance_payment_entries(party_type, party, party_account, order_doctype, "Payment Entry" as reference_type, t1.name as reference_name, t1.remarks, t2.allocated_amount as amount, t2.name as reference_row, t2.reference_name as against_order, t1.posting_date, - t1.{0} as currency + t1.{0} as currency, t1.{4} as exchange_rate from `tabPayment Entry` t1, `tabPayment Entry Reference` t2 where t1.name = t2.parent and t1.{1} = %s and t1.payment_type = %s and t1.party_type = %s and t1.party = %s and t1.docstatus = 1 and t2.reference_doctype = %s {2} order by t1.posting_date {3} - """.format(currency_field, party_account_field, reference_condition, limit_cond), + """.format(currency_field, party_account_field, reference_condition, limit_cond, exchange_rate_field), [party_account, payment_type, party_type, party, order_doctype] + order_list, as_dict=1) if include_unallocated: unallocated_payment_entries = frappe.db.sql(""" select "Payment Entry" as reference_type, name as reference_name, - remarks, unallocated_amount as amount + remarks, unallocated_amount as amount, {2} as exchange_rate from `tabPayment Entry` where {0} = %s and party_type = %s and party = %s and payment_type = %s and docstatus = 1 and unallocated_amount > 0 order by posting_date {1} - """.format(party_account_field, limit_cond), (party_account, party_type, party, payment_type), as_dict=1) + """.format(party_account_field, limit_cond, exchange_rate_field), + (party_account, party_type, party, payment_type), as_dict=1) return list(payment_entries_against_order) + list(unallocated_payment_entries) From 855e9030f2097c77ebc7c6114d2da48d9bc878c6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 12 Jul 2021 22:11:57 +0530 Subject: [PATCH 120/691] fix: Deduct included taxes from unallocated amount --- .../doctype/payment_entry/payment_entry.py | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 85b98843ee30..cf40e9cf2f7e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -517,16 +517,19 @@ def set_unallocated_amount(self): self.unallocated_amount = 0 if self.party: total_deductions = sum(flt(d.amount) for d in self.get("deductions")) + included_taxes = self.get_included_taxes() 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.base_total_allocated_amount) / self.source_exchange_rate + self.unallocated_amount -= included_taxes elif self.payment_type == "Pay" \ and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions) \ and self.total_allocated_amount < self.received_amount + (total_deductions / self.target_exchange_rate): self.unallocated_amount = (self.base_paid_amount - (total_deductions + self.base_total_allocated_amount)) / self.target_exchange_rate + self.unallocated_amount -= included_taxes def set_difference_amount(self): base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate) @@ -542,10 +545,22 @@ def set_difference_amount(self): self.difference_amount = self.base_paid_amount - flt(self.base_received_amount) total_deductions = sum(flt(d.amount) for d in self.get("deductions")) + included_taxes = self.get_included_taxes() - self.difference_amount = flt(self.difference_amount - total_deductions, + self.difference_amount = flt(self.difference_amount - total_deductions - included_taxes, self.precision("difference_amount")) + def get_included_taxes(self): + included_taxes = 0 + for tax in self.get('taxes'): + if tax.included_in_paid_amount: + if tax.add_deduct_tax == 'Add': + included_taxes += tax.base_tax_amount + else: + included_taxes -= tax.base_tax_amount + + return included_taxes + # Paid amount is auto allocated in the reference document by default. # Clear the reference document which doesn't have allocated amount on validate so that form can be loaded fast def clear_unallocated_reference_document_rows(self): @@ -719,6 +734,10 @@ def add_tax_gl_entries(self, gl_entries): against = self.party or self.paid_to payment_or_advance_account = self.get_party_account_for_taxes() + tax_amount = d.tax_amount + + if self.advance_tax_account: + tax_amount = -1* tax_amount gl_entries.append( self.get_gl_dict({ @@ -732,16 +751,17 @@ def add_tax_gl_entries(self, gl_entries): }, account_currency, item=d)) #Intentionally use -1 to get net values in party account - gl_entries.append( - self.get_gl_dict({ - "account": payment_or_advance_account, - "against": against, - dr_or_cr: -1 * d.base_tax_amount, - dr_or_cr + "_in_account_currency": -1*d.base_tax_amount - if account_currency==self.company_currency - else d.tax_amount, - "cost_center": self.cost_center, - }, account_currency, item=d)) + if not d.included_in_paid_amount or self.advance_tax_account: + gl_entries.append( + self.get_gl_dict({ + "account": payment_or_advance_account, + "against": against, + dr_or_cr: -1 * d.base_tax_amount, + dr_or_cr + "_in_account_currency": -1*d.base_tax_amount + if account_currency==self.company_currency + else d.tax_amount, + "cost_center": self.cost_center, + }, account_currency, item=d)) def add_deductions_gl_entries(self, gl_entries): for d in self.get("deductions"): From 0d5ad3b1bb3933e225fdf98d6b04afae6647e357 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 12 Jul 2021 18:56:06 +0530 Subject: [PATCH 121/691] 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 676c6a8b479a..49a2afee85f8 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 b22dba1146b9a5d869b885cb47a1d989ecdd3b64 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 12 Jul 2021 19:07:54 +0530 Subject: [PATCH 122/691] 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 8672e7b1a699..f84b6463979d 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 4fcdc5d129ab3f3655108c2a0bbde4b48f15c3e2 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 12 Jul 2021 22:32:38 +0530 Subject: [PATCH 123/691] fix: Copy discount account from first row to all Items --- 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 f84b6463979d..288e2a7566fe 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -347,7 +347,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e items_add(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() { From 0b7d8fb3afb4ffa5662d7e657c357a49fde323b1 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 01:37:30 +0530 Subject: [PATCH 124/691] 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 f1c413e00a0e..f662bbd1c79c 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 96b5dfdc8f78..bc171604f43c 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 662ca379096e..cec485cc1100 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -591,8 +591,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 b85d3017a11a87cf69383229b25694c175ce30c9 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 01:43:41 +0530 Subject: [PATCH 125/691] 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 45e3c21b27d6..4d8749683c55 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -275,6 +275,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 555852c5ef22ef18b609a8e8a667e42b7f325d19 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 02:06:03 +0530 Subject: [PATCH 126/691] 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 b39022dd7587..922b567d1521 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:43:51.099386", + "modified": "2021-07-13 02:04:37.787882", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", From 4b8918e8a774d48804e2e3670294c279e0ebfd3e Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 02:07:46 +0530 Subject: [PATCH 127/691] 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 9e33eb395b76..d1abdba37927 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -38,5 +38,7 @@ def enable_payment_schedule_in_print(self): 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 81375aec1f05669503ff57437bd532e10ea9deaa Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 02:09:40 +0530 Subject: [PATCH 128/691] 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 7562418fd2ff..69c50b452cea 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 = class PurchaseInvoice extends erpnext.buying. items_add(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() { From 65e2b9fee6406b2981baae5a9d2e90c96504089d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 02:14:18 +0530 Subject: [PATCH 129/691] 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 69c50b452cea..66fa15bc12ec 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 8f7b0a17534faefc3b28dc3976e5a23e9d4597d3 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 03:01:02 +0530 Subject: [PATCH 130/691] 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 c1cc092554da..fdaa7f091b45 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -446,6 +446,7 @@ def get_gl_entries(self, warehouse_account=None): 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 15e795135933..02d847031fcf 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -959,40 +959,6 @@ def make_item_gl_entries(self, gl_entries): 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 1c086e9edcda..dac7f0bd9e35 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -741,6 +741,51 @@ def update_allocated_advance_taxes_on_cancel(self): 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 4a2e4748ac8a815214a6de9c5b1ce54abda5d807 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 13 Jul 2021 11:22:55 +0530 Subject: [PATCH 131/691] fix: Unallocated amount for inclusive charges --- .../accounts/doctype/payment_entry/payment_entry.py | 13 ++++++++----- erpnext/controllers/accounts_controller.py | 6 +++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 0bc3d94d2c59..46904f7c5718 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -436,6 +436,7 @@ def set_tax_withholding(self): return tax_withholding_details.update({ + 'add_deduct_tax': 'Add', 'cost_center': self.cost_center or erpnext.get_default_cost_center(self.company) }) @@ -742,16 +743,18 @@ def add_tax_gl_entries(self, gl_entries): payment_or_advance_account = self.get_party_account_for_taxes() tax_amount = d.tax_amount + base_tax_amount = d.base_tax_amount if self.advance_tax_account: - tax_amount = -1* tax_amount + tax_amount = -1 * tax_amount + base_tax_amount = -1 * base_tax_amount gl_entries.append( self.get_gl_dict({ "account": d.account_head, "against": against, - dr_or_cr: d.base_tax_amount, - dr_or_cr + "_in_account_currency": d.base_tax_amount + dr_or_cr: tax_amount, + dr_or_cr + "_in_account_currency": base_tax_amount if account_currency==self.company_currency else d.tax_amount, "cost_center": d.cost_center @@ -763,8 +766,8 @@ def add_tax_gl_entries(self, gl_entries): self.get_gl_dict({ "account": payment_or_advance_account, "against": against, - dr_or_cr: -1 * d.base_tax_amount, - dr_or_cr + "_in_account_currency": -1*d.base_tax_amount + dr_or_cr: -1 * tax_amount, + dr_or_cr + "_in_account_currency": -1 * base_tax_amount if account_currency==self.company_currency else d.tax_amount, "cost_center": self.cost_center, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a9860ed2f057..4c313c43a720 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -818,11 +818,11 @@ def allocate_advance_taxes(self, gl_entries): account_currency = get_account_currency(tax.account_head) if self.doctype == "Purchase Invoice": - dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" - rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" - else: dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" rev_dr_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" + else: + dr_or_cr = "credit" if tax.add_deduct_tax == "Add" else "debit" + rev_dr_cr = "debit" if tax.add_deduct_tax == "Add" else "credit" party = self.supplier if self.doctype == "Purchase Invoice" else self.customer unallocated_amount = tax.tax_amount - tax.allocated_amount From 40793f4a18c4a23d1414654116657323d7ea800c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 13 Jul 2021 13:17:19 +0530 Subject: [PATCH 132/691] 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 3b6ab7498d97..ecfaab23eed7 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 7b0f944c6693..a6a6069d3589 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 000000000000..412a05b0a153 --- /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 000000000000..2f5026f62ccc --- /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 000000000000..da18d9352a17 --- /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 000000000000..3d6ed5d0d89d --- /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 000000000000..07d9804a733c --- /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 000000000000..7ddc80ab8dd8 --- /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 000000000000..72070cc81c44 --- /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 000000000000..d90ebf6856d7 --- /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 0d190bb930a990932c6537c0c56fddb7d5d0eb87 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 13 Jul 2021 11:45:41 +0530 Subject: [PATCH 133/691] fix: multi-currency issue --- erpnext/manufacturing/doctype/bom/bom.py | 3 ++- erpnext/stock/get_item_details.py | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index c32a8a95a173..9da461f49716 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -713,7 +713,8 @@ def get_bom_item_rate(args, bom_doc): "conversion_rate": 1, # Passed conversion rate as 1 purposefully, as conversion rate is applied at the end of the function "conversion_factor": args.get("conversion_factor") or 1, "plc_conversion_rate": 1, - "ignore_party": True + "ignore_party": True, + "ignore_conversion_rate": True }) item_doc = frappe.get_cached_doc("Item", args.get("item_code")) out = frappe._dict() diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index ca174a3f63c0..4657700dbb41 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -441,7 +441,7 @@ def get_item_tax_info(company, tax_category, item_codes, item_rates=None, item_t if item_tax_templates is None: item_tax_templates = {} - + if item_rates is None: item_rates = {} @@ -807,10 +807,14 @@ def check_packing_list(price_list_rate_name, desired_qty, item_code): def validate_conversion_rate(args, meta): from erpnext.controllers.accounts_controller import validate_conversion_rate - if (not args.conversion_rate - and args.currency==frappe.get_cached_value('Company', args.company, "default_currency")): + company_currency = frappe.get_cached_value('Company', args.company, "default_currency") + if (not args.conversion_rate and args.currency==company_currency): args.conversion_rate = 1.0 + if (not args.ignore_conversion_rate and args.conversion_rate == 1 and args.currency!=company_currency): + args.conversion_rate = get_exchange_rate(args.currency, + company_currency, args.transaction_date, "for_buying") or 1.0 + # validate currency conversion rate validate_conversion_rate(args.currency, args.conversion_rate, meta.get_label("conversion_rate"), args.company) From 2f4c607fc86ac7780491b52f5ef1540a0c8e3598 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 13 Jul 2021 17:41:29 +0530 Subject: [PATCH 134/691] 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 189260a29daf..1dc048ade025 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -251,6 +251,16 @@ def test_purchase_invoice_with_exchange_rate_difference(self): self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount) + 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() @@ -1077,6 +1087,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) @@ -1099,6 +1114,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", @@ -1107,7 +1123,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 fe531d3b227d..d90a00941f83 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 @@ def test_item_tax_net_range(self): 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-.#####' @@ -2151,6 +2163,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", @@ -2164,6 +2177,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 adfdc71844a94a68884c50f45c5f90f3d1640a95 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Wed, 14 Jul 2021 09:59:41 +0530 Subject: [PATCH 135/691] fix: Tax calculation for Recurring additional salary (#24206) * fix: Tax calculation for Recurring additional salary * fix: conflicts --- .../additional_salary/additional_salary.py | 6 +++--- .../doctype/salary_detail/salary_detail.json | 11 +++++++++- .../doctype/salary_slip/salary_slip.py | 21 ++++++++++++++----- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py index ebeddf97f9e3..7db4b8686a09 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py @@ -110,11 +110,11 @@ def get_amount(self, sal_start_date, sal_end_date): 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 + select name, salary_component as component, type, amount, overwrite_salary_structure_amount as overwrite, + deduct_full_tax_on_selected_payroll_date, is_recurring 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 393f647cc88c..97608d72f3e3 100644 --- a/erpnext/payroll/doctype/salary_detail/salary_detail.json +++ b/erpnext/payroll/doctype/salary_detail/salary_detail.json @@ -12,6 +12,7 @@ "year_to_date", "section_break_5", "additional_salary", + "is_recurring_additional_salary", "statistical_component", "depends_on_payment_days", "exempted_from_income_tax", @@ -235,11 +236,19 @@ "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-01-14 13:39:15.847158", + "modified": "2021-03-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 bead880ef70f..81e5dc9f87dd 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -7,12 +7,12 @@ 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 @@ -616,7 +616,8 @@ def add_additional_salary_components(self, component_type): get_salary_component_data(additional_salary.component), additional_salary.amount, component_type, - additional_salary + additional_salary, + is_recurring = additional_salary.is_recurring ) def add_tax_components(self, payroll_period): @@ -637,7 +638,7 @@ def add_tax_components(self, payroll_period): 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): + def update_component_row(self, component_data, amount, component_type, additional_salary=None, is_recurring = 0): component_row = None for d in self.get(component_type): if d.salary_component != component_data.salary_component: @@ -678,6 +679,7 @@ def update_component_row(self, component_data, amount, component_type, additiona 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 @@ -711,6 +713,7 @@ def calculate_variable_tax(self, payroll_period, tax_component): # 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) @@ -870,8 +873,16 @@ def get_taxable_earnings(self, allow_tax_exemption=False, based_on_payment_days= if earning.is_tax_applicable: if additional_amount: - taxable_earnings += (amount - additional_amount) - additional_income += 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 + if earning.deduct_full_tax_on_selected_payroll_date: additional_income_with_full_tax += additional_amount continue From cbf7e1b676d5f89f1be85c6633fe083c31ebe690 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 14 Jul 2021 11:40:47 +0530 Subject: [PATCH 136/691] fix: pos item cart dom updates (#26460) --- .../selling/page/point_of_sale/pos_item_cart.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 38508c219b31..f7b2c1d93c38 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -965,8 +965,23 @@ erpnext.PointOfSale.ItemCart = class { }); } + attach_refresh_field_event(frm) { + $(frm.wrapper).off('refresh-fields'); + $(frm.wrapper).on('refresh-fields', () => { + if (frm.doc.items.length) { + frm.doc.items.forEach(item => { + this.update_item_html(item); + }); + } + this.update_totals_section(frm); + }); + } + load_invoice() { const frm = this.events.get_frm(); + + this.attach_refresh_field_event(frm); + this.fetch_customer_details(frm.doc.customer).then(() => { this.events.customer_details_updated(this.customer_info); this.update_customer_section(); From 1dfb5eb5355eef7dbfbc511288be54c012765e12 Mon Sep 17 00:00:00 2001 From: Frappe Date: Wed, 14 Jul 2021 12:33:45 +0530 Subject: [PATCH 137/691] feat: South Africa VAT report --- .../south_africa_vat_account/__init__.py | 0 .../south_africa_vat_account.json | 34 +++ .../south_africa_vat_account.py | 8 + .../south_africa_vat_settings/__init__.py | 0 .../south_africa_vat_settings.js | 25 ++ .../south_africa_vat_settings.json | 76 ++++++ .../south_africa_vat_settings.py | 8 + .../test_south_africa_vat_settings.py | 8 + .../report/vat_audit_report/__init__.py | 0 .../vat_audit_report/vat_audit_report.js | 31 +++ .../vat_audit_report/vat_audit_report.json | 32 +++ .../vat_audit_report/vat_audit_report.py | 246 ++++++++++++++++++ erpnext/regional/south_africa/__init__.py | 0 erpnext/regional/south_africa/setup.py | 19 ++ 14 files changed, 487 insertions(+) create mode 100644 erpnext/accounts/doctype/south_africa_vat_account/__init__.py create mode 100644 erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json create mode 100644 erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py create mode 100644 erpnext/regional/doctype/south_africa_vat_settings/__init__.py create mode 100644 erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js create mode 100644 erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json create mode 100644 erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py create mode 100644 erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py create mode 100644 erpnext/regional/report/vat_audit_report/__init__.py create mode 100644 erpnext/regional/report/vat_audit_report/vat_audit_report.js create mode 100644 erpnext/regional/report/vat_audit_report/vat_audit_report.json create mode 100644 erpnext/regional/report/vat_audit_report/vat_audit_report.py create mode 100644 erpnext/regional/south_africa/__init__.py create mode 100644 erpnext/regional/south_africa/setup.py diff --git a/erpnext/accounts/doctype/south_africa_vat_account/__init__.py b/erpnext/accounts/doctype/south_africa_vat_account/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json new file mode 100644 index 000000000000..fa1aa7da5948 --- /dev/null +++ b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json @@ -0,0 +1,34 @@ +{ + "actions": [], + "autoname": "account", + "creation": "2021-07-08 22:04:24.634967", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "account" + ], + "fields": [ + { + "allow_in_quick_entry": 1, + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "in_preview": 1, + "label": "Account", + "options": "Account" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-07-08 22:35:33.202911", + "modified_by": "Administrator", + "module": "Accounts", + "name": "South Africa VAT Account", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py new file mode 100644 index 000000000000..4bd8c65a046c --- /dev/null +++ b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.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 SouthAfricaVATAccount(Document): + pass diff --git a/erpnext/regional/doctype/south_africa_vat_settings/__init__.py b/erpnext/regional/doctype/south_africa_vat_settings/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js new file mode 100644 index 000000000000..3f0b466d8258 --- /dev/null +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js @@ -0,0 +1,25 @@ +// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('South Africa VAT Settings', { + refresh: function(frm) { + frm.set_query("company", function() { + return { + filters: { + country: "South Africa", + } + } + }); + frm.set_query("account", "vat_accounts", function(doc, cdt, cdn) { + var row = locals[cdt][cdn]; + console.log(row); + return { + filters: { + company: frm.doc.company, + account_type: "Tax", + is_group: 0 + } + } + }) + } +}); diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json new file mode 100644 index 000000000000..8a51829c4195 --- /dev/null +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.json @@ -0,0 +1,76 @@ +{ + "actions": [], + "autoname": "field:company", + "creation": "2021-07-08 22:34:33.668015", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "vat_accounts" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "vat_accounts", + "fieldtype": "Table", + "label": "VAT Accounts", + "options": "South Africa VAT Account", + "reqd": 1 + } + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-07-14 02:17:52.476762", + "modified_by": "Administrator", + "module": "Regional", + "name": "South Africa VAT Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor", + "share": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.py new file mode 100644 index 000000000000..d74154bfe786 --- /dev/null +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.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 SouthAfricaVATSettings(Document): + pass diff --git a/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py b/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py new file mode 100644 index 000000000000..1c36652ad6e6 --- /dev/null +++ b/erpnext/regional/doctype/south_africa_vat_settings/test_south_africa_vat_settings.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +import unittest + +class TestSouthAfricaVATSettings(unittest.TestCase): + pass diff --git a/erpnext/regional/report/vat_audit_report/__init__.py b/erpnext/regional/report/vat_audit_report/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.js b/erpnext/regional/report/vat_audit_report/vat_audit_report.js new file mode 100644 index 000000000000..39ef9b563ac7 --- /dev/null +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.js @@ -0,0 +1,31 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["VAT Audit Report"] = { + "filters": [ + { + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "reqd": 1, + "default": frappe.defaults.get_user_default("Company") + }, + { + "fieldname": "from_date", + "label": __("From Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -2), + "width": "80" + }, + { + "fieldname": "to_date", + "label": __("To Date"), + "fieldtype": "Date", + "reqd": 1, + "default": frappe.datetime.get_today() + } + ] +}; diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.json b/erpnext/regional/report/vat_audit_report/vat_audit_report.json new file mode 100644 index 000000000000..8917e8f3c7e1 --- /dev/null +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.json @@ -0,0 +1,32 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2021-07-09 11:07:43.473518", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "modified": "2021-07-09 11:07:43.473518", + "modified_by": "Administrator", + "module": "Regional", + "name": "VAT Audit Report", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "GL Entry", + "report_name": "VAT Audit Report", + "report_type": "Script Report", + "roles": [ + { + "role": "Accounts User" + }, + { + "role": "Accounts Manager" + }, + { + "role": "Auditor" + } + ] +} \ 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 new file mode 100644 index 000000000000..e66212689ee7 --- /dev/null +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -0,0 +1,246 @@ +# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe, json +from frappe import _ +from frappe.utils import flt, formatdate, now_datetime, getdate +from datetime import date + +def execute(filters=None): + return VATAuditReport(filters).run() + +class VATAuditReport(object): + + def __init__(self, filters=None): + self.filters = frappe._dict(filters or {}) + self.columns = [] + self.data = [] + self.doctypes = ["Purchase Invoice", "Sales Invoice"] + + def run(self): + self.get_sa_vat_accounts() + for doctype in self.doctypes: + self.get_columns(doctype) + self.select_columns = """ + name as invoice_number, + posting_date, remarks""" + columns = ", supplier as party, credit_to as account" if doctype=="Purchase Invoice" \ + else ", customer as party, debit_to as account" + self.select_columns += columns + + self.get_invoice_data(doctype) + + if self.invoices: + self.get_invoice_items(doctype) + self.get_items_based_on_tax_rate(doctype) + self.get_data(doctype) + + return self.columns, self.data + + def get_sa_vat_accounts(self): + self.sa_vat_accounts = frappe.get_list("South Africa VAT Account", \ + filters = {"parent":self.filters.company}, pluck="account") + if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate: + frappe.throw(_("Please set VAT Accounts in South Africa VAT Settings")) + + def get_invoice_data(self,doctype): + conditions = self.get_conditions() + self.invoices = frappe._dict() + + invoice_data = frappe.db.sql(""" + SELECT + {select_columns} + FROM + `tab{doctype}` + WHERE + docstatus = 1 {where_conditions} + and is_opening = 'No' + ORDER BY + posting_date DESC + """.format(select_columns=self.select_columns, doctype=doctype, + where_conditions=conditions), self.filters, as_dict=1) + + for d in invoice_data: + self.invoices.setdefault(d.invoice_number, d) + + def get_invoice_items(self,doctype): + self.invoice_items = frappe._dict() + self.item_tax_rate = frappe._dict() + + items = frappe.db.sql(""" + SELECT + item_code, parent, taxable_value, base_net_amount, item_tax_rate + FROM + `tab%s Item` + WHERE + parent in (%s) + """ % (doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1) + 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)) + + def get_items_based_on_tax_rate(self,doctype): + self.items_based_on_tax_rate = frappe._dict() + self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \ + else "Sales Taxes and Charges" + self.tax_details = frappe.db.sql(""" + SELECT + parent, account_head, item_wise_tax_detail, base_tax_amount_after_discount_amount + FROM + `tab%s` + WHERE + parenttype = %s and docstatus = 1 + and parent in (%s) + ORDER BY + account_head + """ % (self.tax_doctype, '%s', ', '.join(['%s']*len(self.invoices.keys()))), + tuple([doctype] + list(self.invoices.keys()))) + + for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: + if item_wise_tax_detail: + try: + if account in self.sa_vat_accounts: + item_wise_tax_detail = json.loads(item_wise_tax_detail) + else: + continue + for item_code, taxes in item_wise_tax_detail.items(): + tax_rate, item_amount_map = self.get_item_amount_map(parent, item_code, taxes) + + if tax_rate is not None: + rate_based_dict = self.items_based_on_tax_rate.setdefault(parent, {}) \ + .setdefault(tax_rate, []) + if item_code not in rate_based_dict: + rate_based_dict.append(item_code) + except ValueError: + continue + + def get_item_amount_map(self, parent, item_code, taxes): + net_amount = abs(self.invoice_items.get(parent).get(item_code)) + tax_rate = taxes[0] + tax_amount = taxes[1] + gross_amount = net_amount + tax_amount + item_amount_map = self.item_tax_rate.setdefault(parent, {}) \ + .setdefault(item_code, []) + amount_dict = { + "tax_rate": tax_rate, + "gross_amount": gross_amount, + "tax_amount": tax_amount, + "net_amount": net_amount + } + item_amount_map.append(amount_dict) + + return tax_rate, item_amount_map + + def get_conditions(self): + conditions = "" + for opts in (("company", " and company=%(company)s"), + ("from_date", " and posting_date>=%(from_date)s"), + ("to_date", " and posting_date<=%(to_date)s")): + if self.filters.get(opts[0]): + conditions += opts[1] + + return conditions + + def get_data(self,doctype): + consolidated_data = self.get_consolidated_data() + section_name = _("Purchases ") if doctype == "Purchase Invoice" else _("Sales ") + + for rate, section in consolidated_data.items(): + rate = int(rate) + label = frappe.bold(_("Standard Rate ") + section_name + str(rate) + "%") + section_head = {"posting_date": label } + total_gross = total_tax = total_net = 0 + self.data.append(section_head) + for row in section.get("data"): + self.data.append(row) + total_gross += row["gross_amount"] + total_tax += row["tax_amount"] + total_net += row["net_amount"] + + total = { + "posting_date": frappe.bold(_("Total")), + "gross_amount": total_gross, + "tax_amount": total_tax, + "net_amount": total_net, + "bold":1 + } + self.data.append(total) + self.data.append({}) + + def get_consolidated_data(self): + consolidated_data_map={} + for inv, inv_data in self.invoices.items(): + for rate, items in self.items_based_on_tax_rate.get(inv).items(): + consolidated_data_map.setdefault(rate, {"data": []}) + for item in items: + row = {} + item_details = self.item_tax_rate.get(inv).get(item) + row["account"] = inv_data.get("account") + row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') + row["invoice_number"] = inv + row["party"] = inv_data.get("party") + row["remarks"] = inv_data.get("remarks") + row["gross_amount"]= item_details[0].get("gross_amount") + row["tax_amount"]= item_details[0].get("tax_amount") + row["net_amount"]= item_details[0].get("net_amount") + consolidated_data_map[rate]["data"].append(row) + + return consolidated_data_map + + def get_columns(self,doctype): + self.columns = [ + { + "fieldname": "posting_date", + "label": "Posting Date", + "fieldtype": "Data", + "width": 200 + }, + { + "fieldname": "account", + "label": "Account", + "fieldtype": "Link", + "options": "Account", + "width": 140 + }, + { + "fieldname": "invoice_number", + "label": "Reference", + "fieldtype": "Link", + "options": doctype, + "width": 140 + }, + { + "fieldname": "party", + "label": "Party", + "fieldtype": "Link", + "options": "Supplier" if doctype == "Purchase Invoice" else "Customer", + "width": 140 + }, + { + "fieldname": "remarks", + "label": "Details", + "fieldtype": "Data", + "width": 140 + }, + { + "fieldname": "net_amount", + "label": "Net Amount", + "fieldtype": "Currency", + "width": 140 + }, + { + "fieldname": "tax_amount", + "label": "Tax Amount", + "fieldtype": "Currency", + "width": 140 + }, + { + "fieldname": "gross_amount", + "label": "Gross Amount", + "fieldtype": "Currency", + "width": 140 + }, + ] diff --git a/erpnext/regional/south_africa/__init__.py b/erpnext/regional/south_africa/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py new file mode 100644 index 000000000000..65e9f5bc330f --- /dev/null +++ b/erpnext/regional/south_africa/setup.py @@ -0,0 +1,19 @@ +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe, os, json +from frappe.permissions import add_permission, update_permission_property + +def setup(): + add_permissions() + +def add_permissions(): + """Add Permissions for South Africa VAT Settings and South Africa VAT Account""" + for doctype in ('South Africa VAT Settings', 'South Africa VAT Account'): + add_permission(doctype, 'All', 0) + for role in ('Accounts Manager', 'Accounts User', 'System Manager'): + add_permission(doctype, role, 0) + update_permission_property(doctype, role, 0, 'write', 1) + update_permission_property(doctype, role, 0, 'create', 1) \ No newline at end of file From 9168bb369a3186b9efc09c0039e6b38a624d1359 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 14 Jul 2021 13:57:14 +0530 Subject: [PATCH 138/691] fix: filter by accounts with group by accounts (#26439) --- erpnext/accounts/report/general_ledger/general_ledger.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index e724e9b51b63..1759fa3a48f9 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -55,9 +55,11 @@ def validate_filters(filters, account_details): if not account_details.get(account): frappe.throw(_("Account {0} does not exists").format(account)) - if (filters.get("account") and filters.get("group_by") == _('Group by Account') - and account_details[filters.account].is_group == 0): - frappe.throw(_("Can not filter based on Account, if grouped by Account")) + if (filters.get("account") and filters.get("group_by") == _('Group by Account')): + filters.account = frappe.parse_json(filters.get('account')) + for account in filters.account: + if account_details[account].is_group == 0: + frappe.throw(_("Can not filter based on Child Account, if grouped by Account")) if (filters.get("voucher_no") and filters.get("group_by") in [_('Group by Voucher')]): From 9c04079d04962607b9a8bdaebd0cbe907f1fd28e Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 14 Jul 2021 14:45:11 +0530 Subject: [PATCH 139/691] fix: test fails due to improper gain loss account set (#26482) (#26484) --- .../purchase_invoice/test_purchase_invoice.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index c9384be6eb30..ca4d009956d3 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -954,8 +954,17 @@ def test_deferred_expense_via_journal_entry(self): acc_settings.save() def test_gain_loss_with_advance_entry(self): - unlink_enabled = frappe.db.get_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice") - frappe.db.set_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", 1) + unlink_enabled = frappe.db.get_value( + "Accounts Settings", "Accounts Settings", + "unlink_payment_on_cancel_of_invoice") + + frappe.db.set_value( + "Accounts Settings", "Accounts Settings", + "unlink_payment_on_cancel_of_invoice", 1) + + original_account = frappe.db.get_value("Company", "_Test Company", "exchange_gain_loss_account") + frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", "Exchange Gain/Loss - _TC") + pay = frappe.get_doc({ 'doctype': 'Payment Entry', 'company': '_Test Company', @@ -995,7 +1004,8 @@ def test_gain_loss_with_advance_entry(self): gl_entries = frappe.db.sql(""" select account, sum(debit - credit) as balance from `tabGL Entry` where voucher_no=%s - group by account order by account asc""", (pi.name), as_dict=1) + group by account + order by account asc""", (pi.name), as_dict=1) for i, gle in enumerate(gl_entries): self.assertEqual(expected_gle[i][0], gle.account) @@ -1055,6 +1065,7 @@ def test_gain_loss_with_advance_entry(self): pay.cancel() frappe.db.set_value("Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled) + frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", original_account) def test_purchase_invoice_advance_taxes(self): from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order From b37ff0d3cf3a05734e15a7fc31079b64237ac6e1 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Wed, 14 Jul 2021 15:06:56 +0530 Subject: [PATCH 140/691] fix: sider fixes --- .../south_africa_vat_settings.js | 7 ++-- .../vat_audit_report/vat_audit_report.py | 34 +++++++++---------- erpnext/regional/south_africa/setup.py | 2 +- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js index 3f0b466d8258..7d4ef12a9a97 100644 --- a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js @@ -8,18 +8,17 @@ frappe.ui.form.on('South Africa VAT Settings', { filters: { country: "South Africa", } - } + }; }); frm.set_query("account", "vat_accounts", function(doc, cdt, cdn) { var row = locals[cdt][cdn]; - console.log(row); return { filters: { company: frm.doc.company, account_type: "Tax", is_group: 0 } - } - }) + }; + }); } }); 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 e66212689ee7..6918aa1e642c 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -2,10 +2,10 @@ # For license information, please see license.txt from __future__ import unicode_literals -import frappe, json +import frappe +import json from frappe import _ -from frappe.utils import flt, formatdate, now_datetime, getdate -from datetime import date +from frappe.utils import formatdate def execute(filters=None): return VATAuditReport(filters).run() @@ -39,7 +39,7 @@ def run(self): return self.columns, self.data def get_sa_vat_accounts(self): - self.sa_vat_accounts = frappe.get_list("South Africa VAT Account", \ + self.sa_vat_accounts = frappe.get_list("South Africa VAT Account", filters = {"parent":self.filters.company}, pluck="account") if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate: frappe.throw(_("Please set VAT Accounts in South Africa VAT Settings")) @@ -84,7 +84,7 @@ def get_invoice_items(self,doctype): def get_items_based_on_tax_rate(self,doctype): self.items_based_on_tax_rate = frappe._dict() - self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \ + self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \ else "Sales Taxes and Charges" self.tax_details = frappe.db.sql(""" SELECT @@ -125,10 +125,10 @@ def get_item_amount_map(self, parent, item_code, taxes): item_amount_map = self.item_tax_rate.setdefault(parent, {}) \ .setdefault(item_code, []) amount_dict = { - "tax_rate": tax_rate, - "gross_amount": gross_amount, - "tax_amount": tax_amount, - "net_amount": net_amount + "tax_rate": tax_rate, + "gross_amount": gross_amount, + "tax_amount": tax_amount, + "net_amount": net_amount } item_amount_map.append(amount_dict) @@ -139,19 +139,19 @@ def get_conditions(self): for opts in (("company", " and company=%(company)s"), ("from_date", " and posting_date>=%(from_date)s"), ("to_date", " and posting_date<=%(to_date)s")): - if self.filters.get(opts[0]): - conditions += opts[1] + if self.filters.get(opts[0]): + conditions += opts[1] return conditions def get_data(self,doctype): consolidated_data = self.get_consolidated_data() - section_name = _("Purchases ") if doctype == "Purchase Invoice" else _("Sales ") + section_name = _("Purchases") if doctype == "Purchase Invoice" else _("Sales") for rate, section in consolidated_data.items(): rate = int(rate) - label = frappe.bold(_("Standard Rate ") + section_name + str(rate) + "%") - section_head = {"posting_date": label } + label = frappe.bold(_("Standard Rate ") + section_name + " " + str(rate) + "%") + section_head = {"posting_date": label} total_gross = total_tax = total_net = 0 self.data.append(section_head) for row in section.get("data"): @@ -178,11 +178,11 @@ def get_consolidated_data(self): for item in items: row = {} item_details = self.item_tax_rate.get(inv).get(item) - row["account"] = inv_data.get("account") + row["account"] = inv_data.get("account") row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') row["invoice_number"] = inv - row["party"] = inv_data.get("party") - row["remarks"] = inv_data.get("remarks") + row["party"] = inv_data.get("party") + row["remarks"] = inv_data.get("remarks") row["gross_amount"]= item_details[0].get("gross_amount") row["tax_amount"]= item_details[0].get("tax_amount") row["net_amount"]= item_details[0].get("net_amount") diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py index 65e9f5bc330f..2c44c6e6b17d 100644 --- a/erpnext/regional/south_africa/setup.py +++ b/erpnext/regional/south_africa/setup.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals -import frappe, os, json +# import frappe, os, json from frappe.permissions import add_permission, update_permission_property def setup(): From ac721ae1470923eb4c1a551ff0034998c31c4df9 Mon Sep 17 00:00:00 2001 From: Saqib Date: Wed, 14 Jul 2021 15:20:14 +0530 Subject: [PATCH 141/691] fix: tds computation summary shows cancelled invoices (#26485) --- .../report/tds_computation_summary/tds_computation_summary.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py index e15715dccd8a..6b9df41f54e5 100644 --- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py +++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.py @@ -75,7 +75,8 @@ def get_invoice_and_tds_amount(supplier, account, company, from_date, to_date, f select voucher_no, credit from `tabGL Entry` where party in (%s) and credit > 0 - and company=%s and posting_date between %s and %s + and company=%s and is_cancelled = 0 + and posting_date between %s and %s """, (supplier, company, from_date, to_date), as_dict=1) supplier_credit_amount = flt(sum(d.credit for d in entries)) From c5d7a13513ea46d5e72ad643cec3d06cb156d760 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Wed, 14 Jul 2021 15:49:22 +0530 Subject: [PATCH 142/691] fix: sider & translation fixes --- .../south_africa_vat_settings/south_africa_vat_settings.js | 1 - erpnext/regional/report/vat_audit_report/vat_audit_report.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js index 7d4ef12a9a97..393f4ec10e32 100644 --- a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js @@ -11,7 +11,6 @@ frappe.ui.form.on('South Africa VAT Settings', { }; }); frm.set_query("account", "vat_accounts", function(doc, cdt, cdn) { - var row = locals[cdt][cdn]; return { filters: { company: frm.doc.company, 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 6918aa1e642c..5633b64bfb8d 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -150,7 +150,7 @@ def get_data(self,doctype): for rate, section in consolidated_data.items(): rate = int(rate) - label = frappe.bold(_("Standard Rate ") + section_name + " " + str(rate) + "%") + label = frappe.bold(_("Standard Rate") + " " + section_name + " " + str(rate) + "%") section_head = {"posting_date": label} total_gross = total_tax = total_net = 0 self.data.append(section_head) From 7a890331631533521c03e3991911d09f5158f454 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> Date: Wed, 14 Jul 2021 16:02:49 +0530 Subject: [PATCH 143/691] fix: update integration links in help.js (#26483) --- erpnext/public/js/help_links.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js index aa9bba17c77b..140c9da2ee13 100644 --- a/erpnext/public/js/help_links.js +++ b/erpnext/public/js/help_links.js @@ -54,7 +54,7 @@ frappe.help.help_links["permission-manager"] = [ frappe.help.help_links["Form/System Settings"] = [ { - label: "Naming Series", + label: "System Settings", url: docsUrl + "user/manual/en/setting-up/settings/system-settings", }, ]; @@ -206,7 +206,7 @@ frappe.help.help_links["Form/PayPal Settings"] = [ label: "PayPal Settings", url: docsUrl + - "user/manual/en/setting-up/integrations/paypal-integration", + "user/manual/en/erpnext_integration/paypal-integration", }, ]; @@ -215,14 +215,14 @@ frappe.help.help_links["Form/Razorpay Settings"] = [ label: "Razorpay Settings", url: docsUrl + - "user/manual/en/setting-up/integrations/razorpay-integration", + "user/manual/en/erpnext_integration/razorpay-integration", }, ]; frappe.help.help_links["Form/Dropbox Settings"] = [ { label: "Dropbox Settings", - url: docsUrl + "user/manual/en/setting-up/integrations/dropbox-backup", + url: docsUrl + "user/manual/en/erpnext_integration/dropbox-backup", }, ]; @@ -230,7 +230,7 @@ frappe.help.help_links["Form/LDAP Settings"] = [ { label: "LDAP Settings", url: - docsUrl + "user/manual/en/setting-up/integrations/ldap-integration", + docsUrl + "user/manual/en/erpnext_integration/ldap-integration", }, ]; @@ -239,7 +239,7 @@ frappe.help.help_links["Form/Stripe Settings"] = [ label: "Stripe Settings", url: docsUrl + - "user/manual/en/setting-up/integrations/stripe-integration", + "user/manual/en/erpnext_integration/stripe-integration", }, ]; From 513375f264034b2eaa5e0b563811aa1c12aca790 Mon Sep 17 00:00:00 2001 From: Kenneth Sequeira <33246109+kennethsequeira@users.noreply.github.com> Date: Fri, 9 Jul 2021 21:52:50 +0530 Subject: [PATCH 144/691] fix: Nested/Multi-level BOM help link (#26409) Updated the link for multi-level boms. Current link is broken. --- erpnext/public/js/help_links.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js index 140c9da2ee13..d0c935f48871 100644 --- a/erpnext/public/js/help_links.js +++ b/erpnext/public/js/help_links.js @@ -991,7 +991,7 @@ frappe.help.help_links["Form/BOM"] = [ label: "Nested BOM Structure", url: docsUrl + - "user/manual/en/manufacturing/articles/nested-bom-structure", + "user/manual/en/manufacturing/articles/managing-multi-level-bom", }, ]; From 2c67894135544c04dddeab014ab0c854f8aaef9e Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Wed, 14 Jul 2021 16:28:40 +0530 Subject: [PATCH 145/691] fix: validation check for batch for stock reconciliation type in stock entry(bp #26370 ) (#26487) * fix(ux): added filter for valid batch nos. * fix: not validating batch no if entry type stock reconciliation * test: validate batch_no --- .../stock_ledger_entry/stock_ledger_entry.py | 21 +++++++++---------- .../stock_reconciliation.js | 8 +++++++ .../test_stock_reconciliation.py | 21 +++++++++++++++++++ 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index cb939e63c281..93482e8beab4 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -89,17 +89,16 @@ def validate_item(self): if item_det.is_stock_item != 1: frappe.throw(_("Item {0} must be a stock Item").format(self.item_code)) - # check if batch number is required - if self.voucher_type != 'Stock Reconciliation': - if item_det.has_batch_no == 1: - batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" + item_det.item_name - if not self.batch_no: - frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item)) - elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}): - frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item)) - - elif item_det.has_batch_no == 0 and self.batch_no and self.is_cancelled == 0: - frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code)) + # check if batch number is valid + if item_det.has_batch_no == 1: + batch_item = self.item_code if self.item_code == item_det.item_name else self.item_code + ":" + item_det.item_name + if not self.batch_no: + frappe.throw(_("Batch number is mandatory for Item {0}").format(batch_item)) + elif not frappe.db.get_value("Batch",{"item": self.item_code, "name": self.batch_no}): + frappe.throw(_("{0} is not a valid Batch Number for Item {1}").format(self.batch_no, batch_item)) + + elif item_det.has_batch_no == 0 and self.batch_no and self.is_cancelled == 0: + frappe.throw(_("The Item {0} cannot have Batch").format(self.item_code)) if item_det.has_variants: frappe.throw(_("Stock cannot exist for Item {0} since has variants").format(self.item_code), diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index a01db80da4ac..349e59f31d19 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -17,6 +17,14 @@ frappe.ui.form.on("Stock Reconciliation", { } } }); + frm.set_query("batch_no", "items", function(doc, cdt, cdn) { + var item = locals[cdt][cdn]; + return { + filters: { + 'item': item.item_code + } + }; + }); if (frm.doc.company) { erpnext.queries.setup_queries(frm, "Warehouse", function() { diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 84cdc491282c..c192582531ae 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -16,6 +16,7 @@ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt + class TestStockReconciliation(unittest.TestCase): @classmethod def setUpClass(self): @@ -352,6 +353,26 @@ def test_backdated_stock_reco_future_negative_stock(self): dn2.cancel() pr1.cancel() + def test_valid_batch(self): + create_batch_item_with_batch("Testing Batch Item 1", "001") + create_batch_item_with_batch("Testing Batch Item 2", "002") + sr = create_stock_reconciliation(item_code="Testing Batch Item 1", qty=1, rate=100, batch_no="002" + , do_not_submit=True) + self.assertRaises(frappe.ValidationError, sr.submit) + +def create_batch_item_with_batch(item_name, batch_id): + batch_item_doc = create_item(item_name, is_stock_item=1) + if not batch_item_doc.has_batch_no: + batch_item_doc.has_batch_no = 1 + batch_item_doc.create_new_batch = 1 + batch_item_doc.save(ignore_permissions=True) + + if not frappe.db.exists('Batch', batch_id): + b = frappe.new_doc('Batch') + b.item = item_name + b.batch_id = batch_id + b.save() + def insert_existing_sle(warehouse): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry From 7558e7f1157db7456521b8071b93aa4c0e8970c7 Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Tue, 13 Jul 2021 15:34:25 +0530 Subject: [PATCH 146/691] fix: show child item group items on portal --- erpnext/setup/doctype/item_group/item_group.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 1c72cebfa9d1..5fcad00af160 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -87,8 +87,8 @@ def get_context(self, context): if not field_filters: field_filters = {} - # Ensure the query remains within current item group - field_filters['item_group'] = self.name + # Ensure the query remains within current item group & sub group + field_filters['item_group'] = [ig[0] for ig in get_child_groups(self.name)] engine = ProductQuery() context.items = engine.query(attribute_filters, field_filters, search, start, item_group=self.name) From e244560fb96b66899fe123dcb5799c7fefe053da Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Tue, 13 Jul 2021 17:27:55 +0530 Subject: [PATCH 147/691] fix: set item group as a persistent filter --- erpnext/portal/product_configurator/utils.py | 6 ++++++ erpnext/templates/generators/item_group.html | 2 +- erpnext/www/all-products/index.js | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index d77eb2c39666..211b94a9cfde 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -2,6 +2,7 @@ from frappe.utils import cint from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager from erpnext.shopping_cart.product_info import get_product_info_for_website +from erpnext.setup.doctype.item_group.item_group import get_child_groups def get_field_filter_data(): product_settings = get_product_settings() @@ -89,6 +90,7 @@ def get_products_for_website(field_filters=None, attribute_filters=None, search= def get_products_html_for_website(field_filters=None, attribute_filters=None): field_filters = frappe.parse_json(field_filters) attribute_filters = frappe.parse_json(attribute_filters) + set_item_group_filters(field_filters) items = get_products_for_website(field_filters, attribute_filters) html = ''.join(get_html_for_items(items)) @@ -98,6 +100,10 @@ def get_products_html_for_website(field_filters=None, attribute_filters=None): return html +def set_item_group_filters(field_filters): + if 'item_group' in field_filters: + field_filters['item_group'] = [ig[0] for ig in get_child_groups(field_filters['item_group'])] + def get_item_codes_by_attributes(attribute_filters, template_item_code=None): items = [] diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html index 393c3a43afbb..95eb8f493f65 100644 --- a/erpnext/templates/generators/item_group.html +++ b/erpnext/templates/generators/item_group.html @@ -9,7 +9,7 @@ {% endblock %} {% block page_content %} -
                      +
                      {% if slideshow %} {{ web_block( diff --git a/erpnext/www/all-products/index.js b/erpnext/www/all-products/index.js index 0721056816b4..1c641b59ad14 100644 --- a/erpnext/www/all-products/index.js +++ b/erpnext/www/all-products/index.js @@ -124,6 +124,10 @@ $(() => { attribute_filters: if_key_exists(attribute_filters) }; + const item_group = $(".item-group-content").data('item-group'); + if (item_group) { + Object.assign(field_filters, { item_group }); + } return new Promise((resolve, reject) => { frappe.call('erpnext.portal.product_configurator.utils.get_products_html_for_website', args) .then(r => { From b43974a9a6d072b511d8f9f77e7c312e74fd9546 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Wed, 14 Jul 2021 20:37:48 +0530 Subject: [PATCH 148/691] fix: more sider fixes --- .../south_africa_vat_settings/south_africa_vat_settings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js index 393f4ec10e32..e37a61ac853c 100644 --- a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js @@ -10,7 +10,7 @@ frappe.ui.form.on('South Africa VAT Settings', { } }; }); - frm.set_query("account", "vat_accounts", function(doc, cdt, cdn) { + frm.set_query("account", "vat_accounts", function() { return { filters: { company: frm.doc.company, From 219623279ffd50f3410382de6ca255b6743f30dc Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 14 Jul 2021 20:01:36 +0530 Subject: [PATCH 149/691] fix: Paging buttons not working on item group portal page --- erpnext/templates/generators/item_group.html | 29 +++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html index 95eb8f493f65..9050cc388ae3 100644 --- a/erpnext/templates/generators/item_group.html +++ b/erpnext/templates/generators/item_group.html @@ -127,15 +127,36 @@

                      {{ title }}

                      -
                      -
                      +
                      +
                      +
                      +
                      {% if frappe.form_dict.start|int > 0 %} - + {% endif %} {% if items|length >= page_length %} - + {% endif %}
                      + + {% endblock %} \ 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 150/691] 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 000000000000..d50d55133019 --- /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 374787c6ef24..fe4d17c2102b 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 000000000000..8e67b1cd3468 --- /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 12f7befa13882f04cfcb23afec0c363e7103420b Mon Sep 17 00:00:00 2001 From: 18alantom <2.alan.tom@gmail.com> Date: Thu, 15 Jul 2021 12:03:41 +0530 Subject: [PATCH 151/691] fix: check if field_filters is None --- erpnext/portal/product_configurator/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 211b94a9cfde..d60b1a2b05ea 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -101,7 +101,7 @@ def get_products_html_for_website(field_filters=None, attribute_filters=None): return html def set_item_group_filters(field_filters): - if 'item_group' in field_filters: + if field_filters is not None and 'item_group' in field_filters: field_filters['item_group'] = [ig[0] for ig in get_child_groups(field_filters['item_group'])] From 74b97b5ec9a16ae10a988b782c1818ce7f823ebd Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 15 Jul 2021 14:08:58 +0530 Subject: [PATCH 152/691] fix: FG item not fetched in manufacture entry --- .../doctype/work_order/test_work_order.py | 54 +++++++++++++++++++ .../stock/doctype/stock_entry/stock_entry.py | 22 +++++--- 2 files changed, 70 insertions(+), 6 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 68de0b29d3e4..bf1ccb715942 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -513,6 +513,60 @@ def test_operation_time_with_batch_size(self): work_order1.save() self.assertEqual(work_order1.operations[0].time_in_mins, 40.0) + def test_batch_size_for_fg_item(self): + fg_item = "Test Batch Size Item For BOM 3" + rm1 = "Test Batch Size Item RM 1 For BOM 3" + + frappe.db.set_value('Manufacturing Settings', None, 'make_serial_no_batch_from_work_order', 0) + for item in ["Test Batch Size Item For BOM 3", "Test Batch Size Item RM 1 For BOM 3"]: + item_args = { + "include_item_in_manufacturing": 1, + "is_stock_item": 1 + } + + if item == fg_item: + item_args['has_batch_no'] = 1 + item_args['create_new_batch'] = 1 + item_args['batch_number_series'] = 'TBSI3.#####' + + make_item(item, item_args) + + bom_name = frappe.db.get_value("BOM", + {"item": fg_item, "is_active": 1, "with_operations": 1}, "name") + + if not bom_name: + bom = make_bom(item=fg_item, rate=1000, raw_materials = [rm1], do_not_save=True) + bom.save() + bom.submit() + bom_name = bom.name + + work_order = make_wo_order_test_record(item=fg_item, skip_transfer=True, planned_start_date=now(), qty=1) + ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1)) + for row in ste1.get('items'): + if row.is_finished_item: + self.assertEqual(row.item_code, fg_item) + + work_order = make_wo_order_test_record(item=fg_item, skip_transfer=True, planned_start_date=now(), qty=1) + frappe.db.set_value('Manufacturing Settings', None, 'make_serial_no_batch_from_work_order', 1) + ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 1)) + for row in ste1.get('items'): + if row.is_finished_item: + self.assertEqual(row.item_code, fg_item) + + work_order = make_wo_order_test_record(item=fg_item, skip_transfer=True, planned_start_date=now(), + qty=30, do_not_save = True) + work_order.batch_size = 10 + work_order.insert() + work_order.submit() + self.assertEqual(work_order.has_batch_no, 1) + ste1 = frappe.get_doc(make_stock_entry(work_order.name, "Manufacture", 30)) + for row in ste1.get('items'): + if row.is_finished_item: + self.assertEqual(row.item_code, fg_item) + self.assertEqual(row.qty, 10) + + frappe.db.set_value('Manufacturing Settings', None, 'make_serial_no_batch_from_work_order', 0) + def test_partial_material_consumption(self): frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 1) wo_order = make_wo_order_test_record(planned_start_date=now(), qty=4) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 90b81ddb1dc4..c9838d75f1d7 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1090,13 +1090,13 @@ def load_items_from_bom(self): "is_finished_item": 1 } - if self.work_order and self.pro_doc.has_batch_no: + if self.work_order and self.pro_doc.has_batch_no and cint(frappe.db.get_single_value('Manufacturing Settings', + 'make_serial_no_batch_from_work_order', cache=True)): self.set_batchwise_finished_goods(args, item) else: - self.add_finisged_goods(args, item) + self.add_finished_goods(args, item) def set_batchwise_finished_goods(self, args, item): - qty = flt(self.fg_completed_qty) filters = { "reference_name": self.pro_doc.name, "reference_doctype": self.pro_doc.doctype, @@ -1105,7 +1105,17 @@ def set_batchwise_finished_goods(self, args, item): fields = ["qty_to_produce as qty", "produced_qty", "name"] - for row in frappe.get_all("Batch", filters = filters, fields = fields, order_by="creation asc"): + data = frappe.get_all("Batch", filters = filters, fields = fields, order_by="creation asc") + + if not data: + self.add_finished_goods(args, item) + else: + self.add_batchwise_finished_good(data, args, item) + + def add_batchwise_finished_good(self, data, args, item): + qty = flt(self.fg_completed_qty) + + for row in data: batch_qty = flt(row.qty) - flt(row.produced_qty) if not batch_qty: continue @@ -1121,9 +1131,9 @@ def set_batchwise_finished_goods(self, args, item): args["qty"] = fg_qty args["batch_no"] = row.name - self.add_finisged_goods(args, item) + self.add_finished_goods(args, item) - def add_finisged_goods(self, args, item): + def add_finished_goods(self, args, item): self.add_to_stock_entry_detail({ item.name: args }, bom_no = self.bom_no) From d319e1088352cc911b54b57b4e3c89c8200a52fe Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 15 Jul 2021 16:49:55 +0530 Subject: [PATCH 153/691] fix: set default operation time to 0 (#26511) --- erpnext/manufacturing/doctype/sub_operation/sub_operation.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/sub_operation/sub_operation.json b/erpnext/manufacturing/doctype/sub_operation/sub_operation.json index f63d2b986415..10cee32398af 100644 --- a/erpnext/manufacturing/doctype/sub_operation/sub_operation.json +++ b/erpnext/manufacturing/doctype/sub_operation/sub_operation.json @@ -19,6 +19,7 @@ "options": "Operation" }, { + "default": "0", "description": "Time in mins", "fieldname": "time_in_mins", "fieldtype": "Float", @@ -38,7 +39,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-12-07 18:09:18.005578", + "modified": "2021-07-15 16:39:41.635362", "modified_by": "Administrator", "module": "Manufacturing", "name": "Sub Operation", From 26a9d385472d90dccf067ded8b0929447fad8033 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 15 Jul 2021 16:50:41 +0530 Subject: [PATCH 154/691] fix: WIP needs to be set before submit on skip_transfer (#26500) --- erpnext/manufacturing/doctype/work_order/work_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 779ae42d6531..0a8e5329c151 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -239,7 +239,7 @@ def before_submit(self): self.create_serial_no_batch_no() def on_submit(self): - if not self.wip_warehouse: + if not self.wip_warehouse and not self.skip_transfer: frappe.throw(_("Work-in-Progress Warehouse is required before Submit")) if not self.fg_warehouse: frappe.throw(_("For Warehouse is required before Submit")) From 9b9b18c28622a84795e656a203ae6470ab8c7ba7 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 15 Jul 2021 18:11:22 +0530 Subject: [PATCH 155/691] fix: improving ux for additional discount field (#26502) --- erpnext/selling/page/point_of_sale/pos_item_cart.js | 5 +++-- 1 file changed, 3 insertions(+), 2 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 f7b2c1d93c38..6e36d2809ae3 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -367,15 +367,16 @@ erpnext.PointOfSale.ItemCart = class { `
                      ` ); const me = this; + const frm = me.events.get_frm(); + let discount = frm.doc.additional_discount_percentage; this.discount_field = frappe.ui.form.make_control({ df: { label: __('Discount'), fieldtype: 'Data', - placeholder: __('Enter discount percentage.'), + placeholder: ( discount ? discount + '%' : __('Enter discount percentage.') ), input_class: 'input-xs', onchange: function() { - const frm = me.events.get_frm(); if (flt(this.value) != 0) { frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'additional_discount_percentage', flt(this.value)); me.hide_discount_control(this.value); From ee7eaf9c70484dec3db6f282ca2f74a4a4ac2a2d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 15 Jul 2021 19:19:09 +0530 Subject: [PATCH 156/691] 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 000000000000..656051289f10 --- /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 5a6f16887673..bd7946a1e139 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 8e67b1cd3468..99748dca02e9 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 b164070a4f0e73ca82bdc2b0c4a673e8e6a602e3 Mon Sep 17 00:00:00 2001 From: Ankush Date: Thu, 15 Jul 2021 19:31:59 +0530 Subject: [PATCH 157/691] ci: make semgrep ignore existing errors (bp #26516) --- .../semgrep_rules/frappe_correctness.yml | 2 - .github/workflows/semgrep.yml | 38 ++++++------------- 2 files changed, 12 insertions(+), 28 deletions(-) diff --git a/.github/helper/semgrep_rules/frappe_correctness.yml b/.github/helper/semgrep_rules/frappe_correctness.yml index faab3344a62b..d9603e89aa44 100644 --- a/.github/helper/semgrep_rules/frappe_correctness.yml +++ b/.github/helper/semgrep_rules/frappe_correctness.yml @@ -98,8 +98,6 @@ rules: languages: [python] severity: WARNING paths: - exclude: - - test_*.py include: - "*/**/doctype/*" diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 389524e9684d..701c5c7cbea0 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -1,34 +1,20 @@ name: Semgrep on: - pull_request: - branches: - - develop - - version-13-hotfix - - version-13-pre-release + pull_request: { } + push: + branches: ["develop"] + jobs: semgrep: name: Frappe Linter runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Setup python3 - uses: actions/setup-python@v2 - with: - python-version: 3.8 - - - name: Setup semgrep - run: | - python -m pip install -q semgrep - git fetch origin $GITHUB_BASE_REF:$GITHUB_BASE_REF -q - - - name: Semgrep errors - run: | - files=$(git diff --name-only --diff-filter=d $GITHUB_BASE_REF) - [[ -d .github/helper/semgrep_rules ]] && semgrep --severity ERROR --config=.github/helper/semgrep_rules --quiet --error $files - semgrep --config="r/python.lang.correctness" --quiet --error $files - - - name: Semgrep warnings - run: | - files=$(git diff --name-only --diff-filter=d $GITHUB_BASE_REF) - [[ -d .github/helper/semgrep_rules ]] && semgrep --severity WARNING --severity INFO --config=.github/helper/semgrep_rules --quiet $files + - uses: actions/checkout@v2 + - uses: returntocorp/semgrep-action@v1 + env: + SEMGREP_TIMEOUT: 120 + with: + config: >- + r/python.lang.correctness + .github/helper/semgrep_rules From 8961a267f649f487eb9f9e3fc63aa6d40765e193 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 15 Jul 2021 19:32:15 +0530 Subject: [PATCH 158/691] 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 d50d55133019..807ef5731a58 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 656051289f10..f48972bdc68e 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 99748dca02e9..f66d69ba232f 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 4f2bf989667948a736738189ca8ff400902a26b4 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 15 Jul 2021 22:01:02 +0530 Subject: [PATCH 159/691] 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 e7dd6b8a6062..5c09b71cf35c 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 8e96e2d5a24121c74078b214690aeac4e5ff3a7f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 15 Jul 2021 22:01:38 +0530 Subject: [PATCH 160/691] 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 288e2a7566fe..9beb593b4c08 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 40412f7e618fd3f36eaa5b33c7805c0acc5c5127 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 15 Jul 2021 22:02:43 +0530 Subject: [PATCH 161/691] 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 d1abdba37927..053f061acc7d 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -41,4 +41,5 @@ def toggle_discount_accounting_fields(self): 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 857501cbe1e0ba8e5c6f13d3c7ad29038cdc084e Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 15 Jul 2021 22:03:46 +0530 Subject: [PATCH 162/691] 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 dac7f0bd9e35..64ed67c40a68 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -785,6 +785,42 @@ def make_discount_gl_entries(self, gl_entries): "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 6bff653cf0a0c8820967346bfd4ed1566c85c877 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 16 Jul 2021 02:18:45 +0530 Subject: [PATCH 163/691] 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 4d8749683c55..77ffa4b71d46 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -283,8 +283,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 56cdcebbf4d4daa0bffeead320c1187507cc40f6 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 16 Jul 2021 02:23:55 +0530 Subject: [PATCH 164/691] 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 2fe71672b37d..ecc35b05b35f 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -228,7 +228,7 @@ def make_depreciation_schedule(self): 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 165/691] 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 412a05b0a153..0f13e653ecde 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 627a8a8cfd86ed5cd990d455d145d357f1ec8f8d Mon Sep 17 00:00:00 2001 From: Ankush Date: Fri, 16 Jul 2021 13:03:18 +0530 Subject: [PATCH 166/691] chore: disable semgrep on push events (bp #26523) --- .github/workflows/semgrep.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 701c5c7cbea0..e27b406df05c 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -2,8 +2,6 @@ name: Semgrep on: pull_request: { } - push: - branches: ["develop"] jobs: semgrep: From 13e9aa59564d5230aee949049bff32170646b486 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 15 Jul 2021 16:32:23 +0530 Subject: [PATCH 167/691] fix: added patch to fix missing FG item --- erpnext/patches.txt | 1 + .../add_missing_fg_item_for_stock_entry.py | 110 ++++++++++++++++++ .../repost_item_valuation.py | 2 +- .../stock/doctype/stock_entry/stock_entry.py | 4 + 4 files changed, 116 insertions(+), 1 deletion(-) create mode 100644 erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 29376f00a1ca..f63c7edea2f9 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -291,3 +291,4 @@ erpnext.patches.v13_0.rename_issue_status_hold_to_on_hold erpnext.patches.v13_0.bill_for_rejected_quantity_in_purchase_invoice 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 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 new file mode 100644 index 000000000000..48999e6f9931 --- /dev/null +++ b/erpnext/patches/v13_0/add_missing_fg_item_for_stock_entry.py @@ -0,0 +1,110 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from frappe.utils import cstr, flt, cint +from erpnext.stock.stock_ledger import make_sl_entries +from erpnext.controllers.stock_controller import create_repost_item_valuation_entry + +def execute(): + if not frappe.db.has_column('Work Order', 'has_batch_no'): + return + + if cint(frappe.db.get_single_value('Manufacturing Settings', 'make_serial_no_batch_from_work_order')): + return + + frappe.reload_doc('manufacturing', 'doctype', 'work_order') + filters = { + 'docstatus': 1, + 'produced_qty': ('>', 0), + 'creation': ('>=', '2021-06-29 00:00:00'), + 'has_batch_no': 1 + } + + fields = ['name', 'production_item'] + + work_orders = [d.name for d in frappe.get_all('Work Order', filters = filters, fields=fields)] + + if not work_orders: + 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} + and not exists( + select name from `tabStock Entry Detail` sed where sed.parent = se.name and sed.is_finished_item = 1 + ) + Order BY + se.posting_date, se.posting_time + '''.format(work_orders=tuple(work_orders))) + + if stock_entries: + print('Length of stock entries', len(stock_entries)) + + for stock_entry in stock_entries: + doc = frappe.get_doc('Stock Entry', stock_entry) + doc.set_work_order_details() + doc.load_items_from_bom() + doc.calculate_rate_and_amount() + set_expense_account(doc) + doc.make_batches('t_warehouse') + + if doc.docstatus == 0: + doc.save() + else: + repost_stock_entry(doc) + repost_stock_entries.append(doc) + + for repost_doc in repost_stock_entries: + repost_future_sle_and_gle(repost_doc) + +def set_expense_account(doc): + for row in doc.items: + if row.is_finished_item and not row.expense_account: + row.expense_account = frappe.get_cached_value('Company', doc.company, 'stock_adjustment_account') + +def repost_stock_entry(doc): + doc.db_update() + for child_row in doc.items: + if child_row.is_finished_item: + child_row.db_update() + + sl_entries = [] + finished_item_row = doc.get_finished_item_row() + get_sle_for_target_warehouse(doc, sl_entries, finished_item_row) + + if sl_entries: + try: + make_sl_entries(sl_entries, True) + except Exception: + print(f'SLE entries not posted for the stock entry {doc.name}') + traceback = frappe.get_traceback() + frappe.log_error(traceback) + +def get_sle_for_target_warehouse(doc, sl_entries, finished_item_row): + for d in doc.get('items'): + if cstr(d.t_warehouse) and finished_item_row and d.name == finished_item_row.name: + sle = doc.get_sl_entries(d, { + "warehouse": cstr(d.t_warehouse), + "actual_qty": flt(d.transfer_qty), + "incoming_rate": flt(d.valuation_rate) + }) + + sle.recalculate_rate = 1 + sl_entries.append(sle) + +def repost_future_sle_and_gle(doc): + args = frappe._dict({ + "posting_date": doc.posting_date, + "posting_time": doc.posting_time, + "voucher_type": doc.doctype, + "voucher_no": doc.name, + "company": doc.company + }) + + create_repost_item_valuation_entry(args) \ No newline at end of file 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 55f2ebb2241e..5f31d9caf0de 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -133,6 +133,6 @@ def repost_entries(): def get_repost_item_valuation_entries(): return frappe.db.sql(""" SELECT name from `tabRepost Item Valuation` - WHERE status != 'Completed' and creation <= %s and docstatus = 1 + WHERE status in ('Queued', 'In Progress') and creation <= %s and docstatus = 1 ORDER BY timestamp(posting_date, posting_time) asc, creation asc """, now(), as_dict=1) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index c9838d75f1d7..872b1d051691 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -719,6 +719,10 @@ def validate_finished_goods(self): frappe.throw(_("Multiple items cannot be marked as finished item")) if self.purpose == "Manufacture": + if not finished_items: + frappe.throw(_('Finished Good has not set in the stock entry {0}') + .format(self.name)) + allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", "overproduction_percentage_for_work_order")) From 3362c080b634401f4cd26c2775320ac3b2c8910c Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Fri, 16 Jul 2021 15:00:08 +0530 Subject: [PATCH 168/691] fix: validation check when no conversion_factor (#26527) --- erpnext/stock/utils.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index 8a6a3a3e4a07..b57b2aa6b8f6 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -314,13 +314,16 @@ def update_included_uom_in_report(columns, result, include_uom, conversion_facto for row_idx, row in enumerate(result): data = row.items() if is_dict_obj else enumerate(row) for key, value in data: - if key not in convertible_columns or not conversion_factors[row_idx-1]: + if key not in convertible_columns: continue + # If no conversion factor for the UOM, defaults to 1 + if not conversion_factors[row_idx]: + conversion_factors[row_idx] = 1 if convertible_columns.get(key) == 'rate': - new_value = flt(value) * conversion_factors[row_idx-1] + new_value = flt(value) * conversion_factors[row_idx] else: - new_value = flt(value) / conversion_factors[row_idx-1] + new_value = flt(value) / conversion_factors[row_idx] if not is_dict_obj: row.insert(key+1, new_value) @@ -386,4 +389,4 @@ def is_reposting_item_valuation_in_progress(): reposting_in_progress = frappe.db.exists("Repost Item Valuation", {'docstatus': 1, 'status': ['in', ['Queued','In Progress']]}) if reposting_in_progress: - frappe.msgprint(_("Item valuation reposting in progress. Report might show incorrect item valuation."), alert=1) \ No newline at end of file + frappe.msgprint(_("Item valuation reposting in progress. Report might show incorrect item valuation."), alert=1) From d8ed9dfcf476214a3171ef4f7aaeac884fb99c25 Mon Sep 17 00:00:00 2001 From: Ankush Date: Sat, 17 Jul 2021 12:49:56 +0530 Subject: [PATCH 169/691] chore: update CODEOWNERS (#26536) (#26537) --- CODEOWNERS | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 7cf65a7a732f..219b6bb7821a 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -3,16 +3,33 @@ # These owners will be the default owners for everything in # the repo. Unless a later match takes precedence, -manufacturing/ @rohitwaghchaure @marination -accounts/ @deepeshgarg007 @nextchamp-saqib -loan_management/ @deepeshgarg007 @rohitwaghchaure -pos* @nextchamp-saqib @rohitwaghchaure -assets/ @nextchamp-saqib @deepeshgarg007 -stock/ @marination @rohitwaghchaure -buying/ @marination @deepeshgarg007 -hr/ @Anurag810 @rohitwaghchaure -projects/ @hrwX @nextchamp-saqib -support/ @hrwX @marination -healthcare/ @ruchamahabal @marination -erpnext_integrations/ @Mangesh-Khairnar @nextchamp-saqib -requirements.txt @gavindsouza +erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 +erpnext/assets/ @nextchamp-saqib @deepeshgarg007 +erpnext/erpnext_integrations/ @nextchamp-saqib +erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007 +erpnext/regional @nextchamp-saqib @deepeshgarg007 +erpnext/selling @nextchamp-saqib @deepeshgarg007 +erpnext/support/ @nextchamp-saqib @deepeshgarg007 +pos* @nextchamp-saqib + +erpnext/buying/ @marination @rohitwaghchaure @ankush +erpnext/e_commerce/ @marination +erpnext/maintenance/ @marination @rohitwaghchaure +erpnext/manufacturing/ @marination @rohitwaghchaure @ankush +erpnext/portal/ @marination +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/non_profit/ @ruchamahabal +erpnext/payroll @ruchamahabal +erpnext/projects/ @ruchamahabal + +erpnext/controllers @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination + +.github/ @surajshetty3416 @ankush +requirements.txt @gavindsouza From b0f21824bc08e1c84aac9cab494d7fdd89df7b86 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:34:50 +0530 Subject: [PATCH 170/691] 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 1dc048ade025..0ac816ec726d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -252,14 +252,21 @@ def test_purchase_invoice_with_exchange_rate_difference(self): self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount) 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 d90a00941f83..4699732aca09 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 @@ def test_sales_invoice_with_discount_accounting_enabled(self): 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 87d448e03943778ad0ddc8e47f0ee9049b39620d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:40:43 +0530 Subject: [PATCH 171/691] 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 00ef7d5c184c..96ae828f4645 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 7bbc9a886da783f1149db8bed570c16b72c24ab7 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:41:06 +0530 Subject: [PATCH 172/691] 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 66fa15bc12ec..435fc1e0c1dd 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 fa4c03e7a111602fbdf6175f9960bbce0add9a0c Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:42:36 +0530 Subject: [PATCH 173/691] 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 fdaa7f091b45..cdb9bb292491 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -917,6 +917,11 @@ def make_tax_gl_entries(self, gl_entries): "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 02d847031fcf..5ebc5e6339b4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -901,6 +901,11 @@ def make_tax_gl_entries(self, gl_entries): }, 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 64ed67c40a68..700a0e6645bc 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -785,9 +785,6 @@ def make_discount_gl_entries(self, gl_entries): "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 c2ba6897b09d2936fa2768cfb4cb720fafc2a99f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:45:35 +0530 Subject: [PATCH 174/691] 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 053f061acc7d..24b0ec4d4a88 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -41,5 +41,7 @@ def toggle_discount_accounting_fields(self): 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 c4d2dc0da336ff1811b0daca07e1386ebeea382f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 17:47:20 +0530 Subject: [PATCH 175/691] 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 9beb593b4c08..3fb20d9d5102 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 99551e9b7f25bb7092112a40b5282e087ecd3ecd Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 18:45:21 +0530 Subject: [PATCH 176/691] 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 4699732aca09..8615963e4adb 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 @@ def test_sales_invoice_with_discount_accounting_enabled(self): 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 228499369cc05f9aefed76f41db6b9faad7457b7 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 19:49:16 +0530 Subject: [PATCH 177/691] 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 700a0e6645bc..f32f5bdc9540 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -791,14 +791,22 @@ def make_gle_for_additional_discount_applied_on_taxes(self, gl_entries): 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 @@ -808,17 +816,16 @@ def make_gle_for_additional_discount_applied_on_taxes(self, gl_entries): 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 d6956ff075219293a761ff4c039f7bf476bb6a45 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 19:49:42 +0530 Subject: [PATCH 178/691] 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 0ac816ec726d..3a09c96d8250 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -268,6 +268,35 @@ def test_purchase_invoice_with_discount_accounting_enabled(self): 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 cde0cc06785bb56521d4d1a13d23476070b3ac94 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 22:53:21 +0530 Subject: [PATCH 179/691] fix: Fetch Payment Terms from Sales/Purchase Orders --- .../purchase_invoice/purchase_invoice.js | 19 -------- .../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 +++ 7 files changed, 66 insertions(+), 68 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 10b83c027f7c..54b10f583fda 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -508,8 +508,6 @@ frappe.ui.form.on("Purchase Invoice", { } } } - - frm.events.set_payment_terms(frm); }, refresh: function(frm) { @@ -572,21 +570,4 @@ frappe.ui.form.on("Purchase Invoice", { company: function(frm) { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, - - set_payment_terms: function (frm) { - frappe.call({ - 'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.set_payment_terms_from_po', - 'args': { - doc: frm.doc - }, - 'callback': (r) => { - if (r.message) { - var doc = frappe.model.sync(r.message)[0]; - console.log("doc: ", doc) - frappe.set_route("Form", doc.doctype, doc.name); - } - } - - }); - }, }) \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index f30af0c349ed..10c6cd6a49ea 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1173,49 +1173,6 @@ def set_status(self, update=False, status=None, update_modified=True): 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 eaa502ff7f02..a0bac51046fd 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 update_item(obj, target, source_parent): }, } - 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 1c086e9edcda..d38c2cbdb5ae 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1730,3 +1730,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 41f57a34d3d2..a58c381df352 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -621,6 +621,8 @@ def update_item(source, target, source_parent): @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 update_item(source, target, source_parent): } }, 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 4808e948fc6f..1628f9301917 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 get_pending_qty(item_row): } }, 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 41800e37151a..6d72a5fa0bb1 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -604,6 +604,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) @@ -675,6 +676,10 @@ def get_pending_qty(item_row): } }, 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 831f5edd027173f843d4475986bda3fd31589201 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 22:55:24 +0530 Subject: [PATCH 180/691] fix: Remove unused imports --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 10c6cd6a49ea..c1cc092554da 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -7,8 +7,6 @@ from frappe.utils import cint, cstr, formatdate, flt, getdate, nowdate, get_link_to_form from frappe import _, throw import frappe.defaults -import json -import six from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.controllers.buying_controller import BuyingController From d5ff6361594e771c399c7b8f8384253f49ba434b Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Sat, 17 Jul 2021 14:42:38 -0400 Subject: [PATCH 181/691] fix: missing parameter 'country' --- .../chart_of_accounts_importer/chart_of_accounts_importer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index 4fd8413d838f..8456b49c8eec 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -391,5 +391,5 @@ def set_default_accounts(company): }) company.save() - install_country_fixtures(company.name) + install_country_fixtures(company.name, company.country) company.create_default_tax_template() From cad11707822064aa6fa85cebe4d6d54d53b186ee Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 19 Jul 2021 14:36:54 +0530 Subject: [PATCH 182/691] 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 641520437fb2..6de228fbc7eb 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 @@ def set_outward_taxable_supplies(self): 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 183/691] 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 2f5026f62ccc..afcd657c53d0 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 807ef5731a58..b11b9ea6ab9f 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 f48972bdc68e..a42562ff2e1f 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 184/691] 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 b11b9ea6ab9f..516d254b8139 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 a42562ff2e1f..503db68c18c8 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 185/691] 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 516d254b8139..cb12eb5c0c46 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 503db68c18c8..a1d3c0083cac 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 186/691] 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 cb12eb5c0c46..95592e2f6a82 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 a1d3c0083cac..632d15ba6c88 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 23b1c25ff5652b120585bab11d1a0e33a3ba24a2 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 19 Jul 2021 20:09:37 +0530 Subject: [PATCH 187/691] 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 6635128f9efd..d788d91855ec 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 ff9d631f1525fe4f27caac4afc65eddb165cc27b Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 19 Jul 2021 20:09:37 +0530 Subject: [PATCH 188/691] 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 6635128f9efd..d788d91855ec 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 92273cade025ec4fc782905a702fe6a2d454474e Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 19 Jul 2021 20:44:05 +0530 Subject: [PATCH 189/691] fix(ux): item description should fall back to name (#26339) (#26552) Don't set item description = item code from front end. This is already being set to item_name in before_insert and item_name is better fallback than item code for description. Also fixed wrong condition for erasing description while duplicating item. --- erpnext/stock/doctype/item/item.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index b55374b8d815..264baeaa4726 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -100,10 +100,11 @@ frappe.ui.form.on("Item", { frm.add_custom_button(__('Duplicate'), function() { var new_item = frappe.model.copy_doc(frm.doc); - if(new_item.item_name===new_item.item_code) { + // Duplicate item could have different name, causing "copy paste" error. + if (new_item.item_name===new_item.item_code) { new_item.item_name = null; } - if(new_item.description===new_item.description) { + if (new_item.item_code===new_item.description || new_item.item_code===new_item.description) { new_item.description = null; } frappe.set_route('Form', 'Item', new_item.name); @@ -186,8 +187,6 @@ frappe.ui.form.on("Item", { item_code: function(frm) { if(!frm.doc.item_name) frm.set_value("item_name", frm.doc.item_code); - if(!frm.doc.description) - frm.set_value("description", frm.doc.item_code); }, is_stock_item: function(frm) { From 89c5bb6066737418a01c18ec202c8fa78424a9b2 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 19 Jul 2021 22:19:28 +0530 Subject: [PATCH 190/691] 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 95592e2f6a82..0493732812fe 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 632d15ba6c88..1dcfbcfeb17b 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 f66d69ba232f..fc3aa298242b 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 191/691] 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 0493732812fe..52863f18e056 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 1dcfbcfeb17b..2272a31046e5 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 980798c6fd676c240632956fee182d21869c04b1 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 19 Jul 2021 23:43:36 +0530 Subject: [PATCH 192/691] 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 aa2fe29bc697..65dbe17ec1dd 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -836,7 +836,7 @@ def make_discount_gl_entries(self, gl_entries): "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 63b7ecd0fed3c8bbfd5766c18d28ab621781fac3 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 19 Jul 2021 23:44:21 +0530 Subject: [PATCH 193/691] 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 65dbe17ec1dd..2aac4968a28f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -837,7 +837,7 @@ def make_discount_gl_entries(self, gl_entries): 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 0ea2934cd51e2d0c8af9cc22a8db16d60dd0dd3f Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 19 Jul 2021 23:44:55 +0530 Subject: [PATCH 194/691] 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 2aac4968a28f..9b3336cde50c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -838,7 +838,7 @@ def make_discount_gl_entries(self, gl_entries): 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 8fc9c13734e033ddb5327b29a3fe175ed5ded823 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 19 Jul 2021 23:46:38 +0530 Subject: [PATCH 195/691] 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 3fb20d9d5102..2751b5509cc6 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 1d830dfd92ca49a00b46915b13558489883d5979 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 19 Jul 2021 23:47:58 +0530 Subject: [PATCH 196/691] 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 435fc1e0c1dd..d6bb69bcbf01 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 4105e2713828dc6bd204b516dfed0ba9d322871a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 20 Jul 2021 03:46:02 +0530 Subject: [PATCH 197/691] fix: Create GL Entries for Additional Discount Account --- .../purchase_invoice/purchase_invoice.py | 11 ++-- .../doctype/sales_invoice/sales_invoice.py | 25 +++++--- erpnext/controllers/accounts_controller.py | 60 ++++++------------- 3 files changed, 39 insertions(+), 57 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 95ad653c3766..53ad849b1e14 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -611,7 +611,11 @@ def make_item_gl_entries(self, gl_entries): 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")) @@ -918,11 +922,6 @@ def make_tax_gl_entries(self, gl_entries): "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 955f223eccbc..7862f82f6f66 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -902,11 +902,6 @@ def make_tax_gl_entries(self, gl_entries): }, 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) @@ -957,15 +952,17 @@ def make_item_gl_entries(self, gl_entries): 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) @@ -976,6 +973,18 @@ def make_item_gl_entries(self, gl_entries): 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 9b3336cde50c..59879e0df58c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -812,19 +812,23 @@ 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: + 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 @@ def make_discount_gl_entries(self, gl_entries): }, 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 59ee6958aa7e67cbe00491093729ff260874efce Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 20 Jul 2021 03:52:39 +0530 Subject: [PATCH 198/691] 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 24b0ec4d4a88..a3a32d5e9750 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -40,6 +40,7 @@ def toggle_discount_accounting_fields(self): 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 82d147ea625f7e24959a63b47b11138f95fde536 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 20 Jul 2021 05:16:33 +0530 Subject: [PATCH 199/691] 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 87bdb7cd4f04..7c26007b0726 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -252,8 +252,6 @@ def test_purchase_invoice_with_exchange_rate_difference(self): self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount) 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", @@ -261,38 +259,45 @@ def test_purchase_invoice_with_discount_accounting_enabled(self): 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()) @@ -1207,6 +1212,18 @@ def test_purchase_invoice_advance_taxes(self): 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 00831febac9e..eccc69fdb273 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2025,12 +2025,12 @@ def test_sales_invoice_with_discount_accounting_enabled(self): 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 @@ -2039,27 +2039,28 @@ def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled( 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 eb65ce662a5bdde4bd8a54a8268363d2651a3c09 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 20 Jul 2021 10:23:52 +0530 Subject: [PATCH 200/691] 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 52863f18e056..0da4e560a72d 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 2272a31046e5..0374678a1ad1 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 85c8daae9c83b3045735c85d31b14e0e1949bafd Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 20 Jul 2021 09:57:18 +0530 Subject: [PATCH 201/691] fix: Pass doc and other parameters to properly prefill information - while creating customer from form dashboard --- erpnext/public/js/utils/customer_quick_entry.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/utils/customer_quick_entry.js b/erpnext/public/js/utils/customer_quick_entry.js index ebe6cd98f81e..7bd21df67bc2 100644 --- a/erpnext/public/js/utils/customer_quick_entry.js +++ b/erpnext/public/js/utils/customer_quick_entry.js @@ -1,9 +1,9 @@ frappe.provide('frappe.ui.form'); frappe.ui.form.CustomerQuickEntryForm = frappe.ui.form.QuickEntryForm.extend({ - init: function(doctype, after_insert) { + init: function(doctype, after_insert, init_callback, doc, force) { + this._super(doctype, after_insert, init_callback, doc, force); this.skip_redirect_on_error = true; - this._super(doctype, after_insert); }, render_dialog: function() { From 41dd0c5a8a75d674f151f09fe9412fdd42347d82 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 20 Jul 2021 10:55:05 +0530 Subject: [PATCH 202/691] fix: sider --- .../test_organizational_chart_desktop.js | 3 +- .../test_organizational_chart_mobile.js | 38 +++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 0da4e560a72d..57b7f7dced28 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 0374678a1ad1..214229f6f6c0 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 }); - - 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 }); + + 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 203/691] 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 57b7f7dced28..fb46bbb43312 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 214229f6f6c0..df90dbfa22fe 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 c14aa45720507515f6e8ea73f7053ca5371df656 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 20 Jul 2021 18:19:15 +0530 Subject: [PATCH 204/691] fix: incorrect valuation rate calculation in gross profit report --- erpnext/accounts/report/gross_profit/gross_profit.py | 3 ++- erpnext/controllers/queries.py | 1 + erpnext/manufacturing/doctype/bom/bom.py | 2 +- erpnext/stock/doctype/batch/batch.py | 6 +++--- erpnext/stock/doctype/pick_list/pick_list.py | 1 + erpnext/stock/doctype/stock_entry/stock_entry.py | 2 +- .../stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 4 ++-- .../supplier_wise_sales_analytics.py | 2 +- 8 files changed, 12 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 84c74543dae6..6d8623c189d7 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -241,6 +241,7 @@ def get_buying_amount(self, row, item_code): sle.voucher_detail_no == row.item_row: previous_stock_value = len(my_sle) > i+1 and \ flt(my_sle[i+1].stock_value) or 0.0 + if previous_stock_value: return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty)) else: @@ -335,7 +336,7 @@ def load_stock_ledger_entries(self): res = frappe.db.sql("""select item_code, voucher_type, voucher_no, voucher_detail_no, stock_value, warehouse, actual_qty as qty from `tabStock Ledger Entry` - where company=%(company)s + where company=%(company)s and is_cancelled = 0 order by item_code desc, warehouse desc, posting_date desc, posting_time desc, creation desc""", self.filters, as_dict=True) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 280319321f21..21c052a39127 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -407,6 +407,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): INNER JOIN `tabBatch` batch on sle.batch_no = batch.name where batch.disabled = 0 + and sle.is_cancelled = 0 and sle.item_code = %(item_code)s and sle.warehouse = %(warehouse)s and (sle.batch_no like %(txt)s diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 9da461f49716..2fbbca4b1921 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -748,7 +748,7 @@ def get_valuation_rate(args): if valuation_rate <= 0: last_valuation_rate = frappe.db.sql("""select valuation_rate from `tabStock Ledger Entry` - where item_code = %s and valuation_rate > 0 + where item_code = %s and valuation_rate > 0 and is_cancelled = 0 order by posting_date desc, posting_time desc, creation desc limit 1""", args['item_code']) valuation_rate = flt(last_valuation_rate[0][0]) if last_valuation_rate else 0 diff --git a/erpnext/stock/doctype/batch/batch.py b/erpnext/stock/doctype/batch/batch.py index b6eef6ca4841..b37ae3f4f617 100644 --- a/erpnext/stock/doctype/batch/batch.py +++ b/erpnext/stock/doctype/batch/batch.py @@ -162,19 +162,19 @@ def get_batch_qty(batch_no=None, warehouse=None, item_code=None, posting_date=No out = float(frappe.db.sql("""select sum(actual_qty) from `tabStock Ledger Entry` - where warehouse=%s and batch_no=%s {0}""".format(cond), + where is_cancelled = 0 and warehouse=%s and batch_no=%s {0}""".format(cond), (warehouse, batch_no))[0][0] or 0) if batch_no and not warehouse: out = frappe.db.sql('''select warehouse, sum(actual_qty) as qty from `tabStock Ledger Entry` - where batch_no=%s + where is_cancelled = 0 and batch_no=%s group by warehouse''', batch_no, as_dict=1) if not batch_no and item_code and warehouse: out = frappe.db.sql('''select batch_no, sum(actual_qty) as qty from `tabStock Ledger Entry` - where item_code = %s and warehouse=%s + where is_cancelled = 0 and item_code = %s and warehouse=%s group by batch_no''', (item_code, warehouse), as_dict=1) return out diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index e795742ea4d5..516ae43089b6 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -239,6 +239,7 @@ def get_available_item_locations_for_batched_item(item_code, from_warehouses, re and sle.`item_code`=%(item_code)s and sle.`company` = %(company)s and batch.disabled = 0 + and sle.is_cancelled=0 and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s {warehouse_condition} GROUP BY diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 872b1d051691..654755ec2fbe 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1789,7 +1789,7 @@ def get_expired_batch_items(): from `tabBatch` b, `tabStock Ledger Entry` sle where b.expiry_date <= %s and b.expiry_date is not NULL - and b.batch_id = sle.batch_no + and b.batch_id = sle.batch_no and sle.is_cancelled = 0 group by sle.warehouse, sle.item_code, sle.batch_no""",(nowdate()), as_dict=1) @frappe.whitelist() diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index 93482e8beab4..b4f458388b37 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -60,7 +60,7 @@ def actual_amt_check(self): if self.batch_no and not self.get("allow_negative_stock"): batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty) from `tabStock Ledger Entry` - where warehouse=%s and item_code=%s and batch_no=%s""", + where is_cancelled =0 and warehouse=%s and item_code=%s and batch_no=%s""", (self.warehouse, self.item_code, self.batch_no))[0][0]) if batch_bal_after_transaction < 0: @@ -152,7 +152,7 @@ def validate_with_last_transaction_posting_time(self): last_transaction_time = frappe.db.sql(""" select MAX(timestamp(posting_date, posting_time)) as posting_time from `tabStock Ledger Entry` - where docstatus = 1 and item_code = %s + where docstatus = 1 and is_cancelled = 0 and item_code = %s and warehouse = %s""", (self.item_code, self.warehouse))[0][0] cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00") diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py index 5873a7a3008d..4108a5755429 100644 --- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py +++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py @@ -69,7 +69,7 @@ def get_consumed_details(filters): i.stock_uom, sle.actual_qty, sle.stock_value_difference, sle.voucher_no, sle.voucher_type from `tabStock Ledger Entry` sle, `tabItem` i - where sle.item_code=i.name and sle.actual_qty < 0 %s""" % conditions, values, as_dict=1): + where sle.is_cancelled = 0 and sle.item_code=i.name and sle.actual_qty < 0 %s""" % conditions, values, as_dict=1): consumed_details.setdefault(d.item_code, []).append(d) return consumed_details From 2d225e621fe54db10cb4f18f097e8f11d4c5e517 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 20 Jul 2021 20:41:04 +0530 Subject: [PATCH 205/691] fix: Price list rate not fetched for return sales invoice fixed (#26560) Co-authored-by: Subin Tom --- erpnext/stock/get_item_details.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 4657700dbb41..cf52803fca85 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -74,9 +74,8 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru update_party_blanket_order(args, out) - if not doc or cint(doc.get('is_return')) == 0: - # get price list rate only if the invoice is not a credit or debit note - get_price_list_rate(args, item, out) + + get_price_list_rate(args, item, out) if args.customer and cint(args.is_pos): out.update(get_pos_profile_item_details(args.company, args, update_data=True)) From 41705acbd91be747f75b7eec583c5c5264164ed4 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Tue, 20 Jul 2021 20:48:57 +0530 Subject: [PATCH 206/691] fix: delete child docs when parent doc is deleted (#26518) * fix: Make code more readable * fix: Delete child table info when parent doc is deleted * fix: Sider issues * fix: Remove trailing whitespace --- .../transaction_deletion_record.py | 160 +++++++++++------- 1 file changed, 99 insertions(+), 61 deletions(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py index ece9fb569923..c3db27f81c38 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -12,10 +12,14 @@ class TransactionDeletionRecord(Document): def validate(self): frappe.only_for('System Manager') + self.validate_doctypes_to_be_ignored() + + def validate_doctypes_to_be_ignored(self): doctypes_to_be_ignored_list = get_doctypes_to_be_ignored() for doctype in self.doctypes_to_be_ignored: if doctype.doctype_name not in doctypes_to_be_ignored_list: - frappe.throw(_("DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it. "), title=_("Not Allowed")) + frappe.throw(_("DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it."), + title=_("Not Allowed")) def before_submit(self): if not self.doctypes_to_be_ignored: @@ -23,61 +27,121 @@ def before_submit(self): self.delete_bins() self.delete_lead_addresses() - + self.reset_company_values() + clear_notifications() + self.delete_company_transactions() + + def populate_doctypes_to_be_ignored_table(self): + doctypes_to_be_ignored_list = get_doctypes_to_be_ignored() + for doctype in doctypes_to_be_ignored_list: + self.append('doctypes_to_be_ignored', { + 'doctype_name' : doctype + }) + + def delete_bins(self): + frappe.db.sql("""delete from tabBin where warehouse in + (select name from tabWarehouse where company=%s)""", self.company) + + def delete_lead_addresses(self): + """Delete addresses to which leads are linked""" + leads = frappe.get_all('Lead', filters={'company': self.company}) + leads = ["'%s'" % row.get("name") for row in leads] + addresses = [] + if leads: + addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name + in ({leads})""".format(leads=",".join(leads))) + + if addresses: + addresses = ["%s" % frappe.db.escape(addr) for addr in addresses] + + frappe.db.sql("""delete from tabAddress where name in ({addresses}) and + name not in (select distinct dl1.parent from `tabDynamic Link` dl1 + inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent + and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses))) + + frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead' + and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads))) + + frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads))) + + def reset_company_values(self): company_obj = frappe.get_doc('Company', self.company) - # reset company values company_obj.total_monthly_sales = 0 company_obj.sales_monthly_history = None company_obj.save() - # Clear notification counts - clear_notifications() + def delete_company_transactions(self): + doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list() + docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list) + + tables = self.get_all_child_doctypes() + for docfield in docfields: + if docfield['parent'] != self.doctype: + no_of_docs = self.get_number_of_docs_linked_with_specified_company(docfield['parent'], docfield['fieldname']) + + if no_of_docs > 0: + self.delete_version_log(docfield['parent'], docfield['fieldname']) + self.delete_communications(docfield['parent'], docfield['fieldname']) + self.populate_doctypes_table(tables, docfield['parent'], no_of_docs) + + self.delete_child_tables(docfield['parent'], docfield['fieldname']) + self.delete_docs_linked_with_specified_company(docfield['parent'], docfield['fieldname']) + + naming_series = frappe.db.get_value('DocType', docfield['parent'], 'autoname') + if naming_series: + if '#' in naming_series: + self.update_naming_series(naming_series, docfield['parent']) + + def get_doctypes_to_be_ignored_list(self): singles = frappe.get_all('DocType', filters = {'issingle': 1}, pluck = 'name') - tables = frappe.get_all('DocType', filters = {'istable': 1}, pluck = 'name') doctypes_to_be_ignored_list = singles for doctype in self.doctypes_to_be_ignored: doctypes_to_be_ignored_list.append(doctype.doctype_name) + return doctypes_to_be_ignored_list + + def get_doctypes_with_company_field(self, doctypes_to_be_ignored_list): docfields = frappe.get_all('DocField', filters = { 'fieldtype': 'Link', 'options': 'Company', 'parent': ['not in', doctypes_to_be_ignored_list]}, fields=['parent', 'fieldname']) - - for docfield in docfields: - if docfield['parent'] != self.doctype: - no_of_docs = frappe.db.count(docfield['parent'], { - docfield['fieldname'] : self.company - }) - if no_of_docs > 0: - self.delete_version_log(docfield['parent'], docfield['fieldname']) - self.delete_communications(docfield['parent'], docfield['fieldname']) + return docfields - # populate DocTypes table - if docfield['parent'] not in tables: - self.append('doctypes', { - 'doctype_name' : docfield['parent'], - 'no_of_docs' : no_of_docs - }) + def get_all_child_doctypes(self): + return frappe.get_all('DocType', filters = {'istable': 1}, pluck = 'name') - # delete the docs linked with the specified company - frappe.db.delete(docfield['parent'], { - docfield['fieldname'] : self.company - }) + def get_number_of_docs_linked_with_specified_company(self, doctype, company_fieldname): + return frappe.db.count(doctype, {company_fieldname : self.company}) - naming_series = frappe.db.get_value('DocType', docfield['parent'], 'autoname') - if naming_series: - if '#' in naming_series: - self.update_naming_series(naming_series, docfield['parent']) + def populate_doctypes_table(self, tables, doctype, no_of_docs): + if doctype not in tables: + self.append('doctypes', { + 'doctype_name' : doctype, + 'no_of_docs' : no_of_docs + }) - def populate_doctypes_to_be_ignored_table(self): - doctypes_to_be_ignored_list = get_doctypes_to_be_ignored() - for doctype in doctypes_to_be_ignored_list: - self.append('doctypes_to_be_ignored', { - 'doctype_name' : doctype - }) + def delete_child_tables(self, doctype, company_fieldname): + parent_docs_to_be_deleted = frappe.get_all(doctype, { + company_fieldname : self.company + }, pluck = 'name') + + child_tables = frappe.get_all('DocField', filters = { + 'fieldtype': 'Table', + 'parent': doctype + }, pluck = 'options') + + for table in child_tables: + frappe.db.delete(table, { + 'parent': ['in', parent_docs_to_be_deleted] + }) + + def delete_docs_linked_with_specified_company(self, doctype, company_fieldname): + frappe.db.delete(doctype, { + company_fieldname : self.company + }) def update_naming_series(self, naming_series, doctype_name): if '.' in naming_series: @@ -107,32 +171,6 @@ def delete_communications(self, doctype, company_fieldname): frappe.delete_doc('Communication', communication_names, ignore_permissions=True) - def delete_bins(self): - frappe.db.sql("""delete from tabBin where warehouse in - (select name from tabWarehouse where company=%s)""", self.company) - - def delete_lead_addresses(self): - """Delete addresses to which leads are linked""" - leads = frappe.get_all('Lead', filters={'company': self.company}) - leads = ["'%s'" % row.get("name") for row in leads] - addresses = [] - if leads: - addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name - in ({leads})""".format(leads=",".join(leads))) - - if addresses: - addresses = ["%s" % frappe.db.escape(addr) for addr in addresses] - - frappe.db.sql("""delete from tabAddress where name in ({addresses}) and - name not in (select distinct dl1.parent from `tabDynamic Link` dl1 - inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent - and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses))) - - frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead' - and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads))) - - frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads))) - @frappe.whitelist() def get_doctypes_to_be_ignored(): doctypes_to_be_ignored_list = ['Account', 'Cost Center', 'Warehouse', 'Budget', From 46bed5e6cca5eade886d85a9300fc5ebf00de18f Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Tue, 20 Jul 2021 22:03:44 +0530 Subject: [PATCH 207/691] 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 a3a32d5e9750..55449132928e 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -40,9 +40,16 @@ def toggle_discount_accounting_fields(self): 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 57514f7b1dbb94171e7d7426f6554eee334bcc39 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 21 Jul 2021 00:46:34 +0530 Subject: [PATCH 208/691] feat(Non Profit): API Endpoint to update halted Razorpay subscriptions (#26427) (#26564) * feat: Update Subscription Activated field to Subscription Status to accomodate Halted status * feat: API Endpoint to halt Razorpay subscription * fix: sider * fix: validation message * test: halted razorpay subscription --- erpnext/non_profit/doctype/member/member.json | 16 ++-- erpnext/non_profit/doctype/member/member.py | 4 +- .../doctype/membership/membership.py | 92 ++++++++++++++----- .../doctype/membership/test_membership.py | 56 +++++++++-- erpnext/patches.txt | 1 + ...date_subscription_status_in_memberships.py | 9 ++ 6 files changed, 142 insertions(+), 36 deletions(-) create mode 100644 erpnext/patches/v13_0/update_subscription_status_in_memberships.py diff --git a/erpnext/non_profit/doctype/member/member.json b/erpnext/non_profit/doctype/member/member.json index f190cfae7559..7c1baf1a8d10 100644 --- a/erpnext/non_profit/doctype/member/member.json +++ b/erpnext/non_profit/doctype/member/member.json @@ -26,7 +26,7 @@ "razorpay_details_section", "subscription_id", "customer_id", - "subscription_activated", + "subscription_status", "column_break_21", "subscription_start", "subscription_end" @@ -151,12 +151,6 @@ "fieldname": "column_break_21", "fieldtype": "Column Break" }, - { - "default": "0", - "fieldname": "subscription_activated", - "fieldtype": "Check", - "label": "Subscription Activated" - }, { "fieldname": "subscription_start", "fieldtype": "Date", @@ -166,11 +160,17 @@ "fieldname": "subscription_end", "fieldtype": "Date", "label": "Subscription End" + }, + { + "fieldname": "subscription_status", + "fieldtype": "Select", + "label": "Subscription Status", + "options": "\nActive\nHalted" } ], "image_field": "image", "links": [], - "modified": "2020-11-09 12:12:10.174647", + "modified": "2021-07-11 14:27:26.368039", "modified_by": "Administrator", "module": "Non Profit", "name": "Member", diff --git a/erpnext/non_profit/doctype/member/member.py b/erpnext/non_profit/doctype/member/member.py index 30be585e9a75..67828d6efc81 100644 --- a/erpnext/non_profit/doctype/member/member.py +++ b/erpnext/non_profit/doctype/member/member.py @@ -84,7 +84,9 @@ def create_member(user_details): "email_id": user_details.email, "pan_number": user_details.pan or None, "membership_type": user_details.plan_id, - "subscription_id": user_details.subscription_id or None + "customer_id": user_details.customer_id or None, + "subscription_id": user_details.subscription_id or None, + "subscription_status": user_details.subscription_status or "" }) member.insert(ignore_permissions=True) diff --git a/erpnext/non_profit/doctype/membership/membership.py b/erpnext/non_profit/doctype/membership/membership.py index e8ae6187b7e9..b584116df3cc 100644 --- a/erpnext/non_profit/doctype/membership/membership.py +++ b/erpnext/non_profit/doctype/membership/membership.py @@ -196,11 +196,14 @@ def make_invoice(membership, member, plan, settings): return invoice -def get_member_based_on_subscription(subscription_id, email): - members = frappe.get_all("Member", filters={ - "subscription_id": subscription_id, - "email_id": email - }, order_by="creation desc") +def get_member_based_on_subscription(subscription_id, email=None, customer_id=None): + filters = {"subscription_id": subscription_id} + if email: + filters.update({"email_id": email}) + if customer_id: + filters.update({"customer_id": customer_id}) + + members = frappe.get_all("Member", filters=filters, order_by="creation desc") try: return frappe.get_doc("Member", members[0]["name"]) @@ -209,8 +212,6 @@ def get_member_based_on_subscription(subscription_id, email): def verify_signature(data, endpoint="Membership"): - if frappe.flags.in_test or os.environ.get("CI"): - return True signature = frappe.request.headers.get("X-Razorpay-Signature") settings = frappe.get_doc("Non Profit Settings") @@ -225,16 +226,7 @@ def verify_signature(data, endpoint="Membership"): @frappe.whitelist(allow_guest=True) def trigger_razorpay_subscription(*args, **kwargs): data = frappe.request.get_data(as_text=True) - try: - verify_signature(data) - except Exception as e: - log = frappe.log_error(e, "Membership Webhook Verification Error") - notify_failure(log) - return { "status": "Failed", "reason": e} - - if isinstance(data, six.string_types): - data = json.loads(data) - data = frappe._dict(data) + data = process_request_data(data) subscription = data.payload.get("subscription", {}).get("entity", {}) subscription = frappe._dict(subscription) @@ -281,7 +273,7 @@ def trigger_razorpay_subscription(*args, **kwargs): # Update membership values member.subscription_start = datetime.fromtimestamp(subscription.start_at) member.subscription_end = datetime.fromtimestamp(subscription.end_at) - member.subscription_activated = 1 + member.subscription_status = "Active" member.flags.ignore_mandatory = True member.save() @@ -294,9 +286,67 @@ def trigger_razorpay_subscription(*args, **kwargs): message = "{0}\n\n{1}\n\n{2}: {3}".format(e, frappe.get_traceback(), _("Payment ID"), payment.id) log = frappe.log_error(message, _("Error creating membership entry for {0}").format(member.name)) notify_failure(log) - return { "status": "Failed", "reason": e} + return {"status": "Failed", "reason": e} + + return {"status": "Success"} + + +@frappe.whitelist(allow_guest=True) +def update_halted_razorpay_subscription(*args, **kwargs): + """ + When all retries have been exhausted, Razorpay moves the subscription to the halted state. + The customer has to manually retry the charge or change the card linked to the subscription, + for the subscription to move back to the active state. + """ + if frappe.request: + data = frappe.request.get_data(as_text=True) + data = process_request_data(data) + elif frappe.flags.in_test: + data = kwargs.get("data") + data = frappe._dict(data) + else: + return + + if not data.event == "subscription.halted": + return + + subscription = data.payload.get("subscription", {}).get("entity", {}) + subscription = frappe._dict(subscription) + + try: + member = get_member_based_on_subscription(subscription.id, customer_id=subscription.customer_id) + if not member: + frappe.throw(_("Member with Razorpay Subscription ID {0} not found").format(subscription.id)) + + member.subscription_status = "Halted" + member.flags.ignore_mandatory = True + member.save() + + if subscription.get("notes"): + member = get_additional_notes(member, subscription) + + except Exception as e: + message = "{0}\n\n{1}".format(e, frappe.get_traceback()) + log = frappe.log_error(message, _("Error updating halted status for member {0}").format(member.name)) + notify_failure(log) + return {"status": "Failed", "reason": e} + + return {"status": "Success"} + + +def process_request_data(data): + try: + verify_signature(data) + except Exception as e: + log = frappe.log_error(e, "Membership Webhook Verification Error") + notify_failure(log) + return {"status": "Failed", "reason": e} + + if isinstance(data, six.string_types): + data = json.loads(data) + data = frappe._dict(data) - return { "status": "Success" } + return data def get_company_for_memberships(): @@ -362,4 +412,4 @@ def set_expired_status(): `tabMembership` SET `status` = 'Expired' WHERE `status` not in ('Cancelled') AND `to_date` < %s - """, (nowdate())) \ No newline at end of file + """, (nowdate())) diff --git a/erpnext/non_profit/doctype/membership/test_membership.py b/erpnext/non_profit/doctype/membership/test_membership.py index 31da792e534e..0f5a9bed826e 100644 --- a/erpnext/non_profit/doctype/membership/test_membership.py +++ b/erpnext/non_profit/doctype/membership/test_membership.py @@ -6,6 +6,7 @@ import frappe import erpnext from erpnext.non_profit.doctype.member.member import create_member +from erpnext.non_profit.doctype.membership.membership import update_halted_razorpay_subscription from frappe.utils import nowdate, add_months class TestMembership(unittest.TestCase): @@ -13,11 +14,16 @@ def setUp(self): plan = setup_membership() # make test member - self.member_doc = create_member(frappe._dict({ - 'fullname': "_Test_Member", - 'email': "_test_member_erpnext@example.com", - 'plan_id': plan.name - })) + self.member_doc = create_member( + frappe._dict({ + "fullname": "_Test_Member", + "email": "_test_member_erpnext@example.com", + "plan_id": plan.name, + "subscription_id": "sub_DEX6xcJ1HSW4CR", + "customer_id": "cust_C0WlbKhp3aLA7W", + "subscription_status": "Active" + }) + ) self.member_doc.make_customer_and_link() self.member = self.member_doc.name @@ -51,6 +57,20 @@ def test_renew_within_30_days(self): "to_date": add_months(nowdate(), 3), }) + def test_halted_memberships(self): + make_membership(self.member, { + "from_date": add_months(nowdate(), 2), + "to_date": add_months(nowdate(), 3) + }) + + self.assertEqual(frappe.db.get_value("Member", self.member, "subscription_status"), "Active") + payload = get_subscription_payload() + update_halted_razorpay_subscription(data=payload) + self.assertEqual(frappe.db.get_value("Member", self.member, "subscription_status"), "Halted") + + def tearDown(self): + frappe.db.rollback() + def set_config(key, value): frappe.db.set_value("Non Profit Settings", None, key, value) @@ -115,4 +135,28 @@ def setup_membership(): else: plan = frappe.get_doc("Membership Type", "_rzpy_test_milythm") - return plan \ No newline at end of file + return plan + +def get_subscription_payload(): + return { + "entity": "event", + "account_id": "acc_BFQ7uQEaa7j2z7", + "event": "subscription.halted", + "contains": [ + "subscription" + ], + "payload": { + "subscription": { + "entity": { + "id": "sub_DEX6xcJ1HSW4CR", + "entity": "subscription", + "plan_id": "_rzpy_test_milythm", + "customer_id": "cust_C0WlbKhp3aLA7W", + "status": "halted", + "notes": { + "Important": "Notes for Internal Reference" + }, + } + } + } + } \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f63c7edea2f9..2a8363511773 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -292,3 +292,4 @@ erpnext.patches.v13_0.bill_for_rejected_quantity_in_purchase_invoice 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 diff --git a/erpnext/patches/v13_0/update_subscription_status_in_memberships.py b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py new file mode 100644 index 000000000000..28e650e9cedf --- /dev/null +++ b/erpnext/patches/v13_0/update_subscription_status_in_memberships.py @@ -0,0 +1,9 @@ +import frappe + +def execute(): + if frappe.db.exists('DocType', 'Member'): + frappe.reload_doc('Non Profit', 'doctype', 'Member') + + 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 From 8a64a84d1afdfd18cefcc99a0860ed63fff34b84 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 13:25:53 +0530 Subject: [PATCH 209/691] 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 641520437fb2..6a61ae2b4222 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 @@ def get_outward_items(self, doctype): 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 cfcb8c3444f1..b81fa810fe1f 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -217,9 +217,8 @@ def get_invoice_items(self): 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 210/691] 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 703e93c0757c..676c6a8b479a 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 211/691] 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 8e6952a93c43..b65903bf5e8a 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 212/691] 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 6d1f6249c139..04c789d30a60 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -846,6 +846,7 @@ def get_gl_entries(self, warehouse_account=None): 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 @@ def make_item_gl_entries(self, gl_entries): 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 213/691] 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 f813425e6b5c..dc0d9da97b6e 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 214/691] 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 6fed9efa63fb..f1c413e00a0e 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 215/691] 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 cf52803fca85..bf082f89be73 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 216/691] 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 ac4a2d6f16de..9e33eb395b76 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -21,6 +21,7 @@ def validate(self): 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 @@ def enable_payment_schedule_in_print(self): 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 217/691] 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 676c6a8b479a..49a2afee85f8 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 218/691] 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 dc0d9da97b6e..17228a3c2834 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 219/691] 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 17228a3c2834..2b48d66dca97 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 220/691] 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 f1c413e00a0e..f662bbd1c79c 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 96b5dfdc8f78..bc171604f43c 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 bf082f89be73..cadb4aa97b1e 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 221/691] 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 264baeaa4726..2fee57f2d1b8 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 222/691] 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 8a55ff87e39b..922b567d1521 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 223/691] 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 9e33eb395b76..d1abdba37927 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -38,5 +38,7 @@ def enable_payment_schedule_in_print(self): 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 224/691] 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 dc9094c3e915..81e3aaa8a583 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 225/691] 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 81e3aaa8a583..a1ee5d0076e3 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 226/691] 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 f7992797ed43..78d1ee972f06 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -446,6 +446,7 @@ def get_gl_entries(self, warehouse_account=None): 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 04c789d30a60..15fae84b799a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -960,40 +960,6 @@ def make_item_gl_entries(self, gl_entries): 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 4c313c43a720..0a1158250864 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -808,6 +808,51 @@ def update_allocated_advance_taxes_on_cancel(self): 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 227/691] 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 ca4d009956d3..bd7ded269c6d 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 @@ def check_gle_for_pi(self, pi): 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 dbc7f8632fca..d03a874c286a 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 @@ def test_item_tax_net_range(self): 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 228/691] 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 e7dd6b8a6062..5c09b71cf35c 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 229/691] 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 2b48d66dca97..fab0e1132408 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 230/691] 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 d1abdba37927..053f061acc7d 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -41,4 +41,5 @@ def toggle_discount_accounting_fields(self): 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 231/691] 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 0a1158250864..8fc4023f2e2c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -852,6 +852,42 @@ def make_discount_gl_entries(self, gl_entries): "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 232/691] 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 2fee57f2d1b8..45666183856b 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 233/691] 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 bd7ded269c6d..36fbc7e35131 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 @@ def check_gle_for_pi(self, pi): 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 d03a874c286a..2ddfad9c3102 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 @@ def test_sales_invoice_with_discount_accounting_enabled(self): 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 234/691] 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 00ef7d5c184c..96ae828f4645 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 235/691] 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 a1ee5d0076e3..4ed31cc22723 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 236/691] 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 78d1ee972f06..006f5bb0e86a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -888,6 +888,11 @@ def make_tax_gl_entries(self, gl_entries): "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 15fae84b799a..ee9b59e769d8 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -902,6 +902,11 @@ def make_tax_gl_entries(self, gl_entries): }, 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 8fc4023f2e2c..f4593c2a762d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -852,9 +852,6 @@ def make_discount_gl_entries(self, gl_entries): "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 237/691] 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 053f061acc7d..24b0ec4d4a88 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -41,5 +41,7 @@ def toggle_discount_accounting_fields(self): 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 238/691] 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 fab0e1132408..a5341df2e389 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 239/691] 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 2ddfad9c3102..1e2e92f60936 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 @@ def test_sales_invoice_with_discount_accounting_enabled(self): 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 240/691] 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 f4593c2a762d..aa2fe29bc697 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -858,14 +858,22 @@ def make_gle_for_additional_discount_applied_on_taxes(self, gl_entries): 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 @@ def make_gle_for_additional_discount_applied_on_taxes(self, gl_entries): 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 241/691] 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 36fbc7e35131..4eb71f8e704f 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 @@ def test_purchase_invoice_with_discount_accounting_enabled(self): 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 242/691] 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 aa2fe29bc697..65dbe17ec1dd 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -836,7 +836,7 @@ def make_discount_gl_entries(self, gl_entries): "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 243/691] 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 65dbe17ec1dd..2aac4968a28f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -837,7 +837,7 @@ def make_discount_gl_entries(self, gl_entries): 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 244/691] 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 2aac4968a28f..9b3336cde50c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -838,7 +838,7 @@ def make_discount_gl_entries(self, gl_entries): 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 245/691] 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 a5341df2e389..02562278863f 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 246/691] 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 4ed31cc22723..bf78c028c81d 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 247/691] 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 006f5bb0e86a..feae213d92d6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -609,7 +609,11 @@ def make_item_gl_entries(self, gl_entries): 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 @@ def make_tax_gl_entries(self, gl_entries): "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 ee9b59e769d8..52c2a9c42c3d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -902,11 +902,6 @@ def make_tax_gl_entries(self, gl_entries): }, 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 @@ def make_item_gl_entries(self, gl_entries): 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 @@ def make_item_gl_entries(self, gl_entries): 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 9b3336cde50c..59879e0df58c 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -812,19 +812,23 @@ 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: + 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 @@ def make_discount_gl_entries(self, gl_entries): }, 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 248/691] 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 24b0ec4d4a88..a3a32d5e9750 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -40,6 +40,7 @@ def toggle_discount_accounting_fields(self): 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 249/691] 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 4eb71f8e704f..0487ef644434 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 @@ def check_gle_for_pi(self, pi): 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 @@ def test_purchase_invoice_with_discount_accounting_enabled(self): 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 @@ def test_purchase_invoice_advance_taxes(self): 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 1e2e92f60936..9f7d18bdb4a7 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 @@ def test_sales_invoice_with_discount_accounting_enabled(self): 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 @@ def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled( 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 250/691] 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 a3a32d5e9750..55449132928e 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -40,9 +40,16 @@ def toggle_discount_accounting_fields(self): 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 251/691] 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 02562278863f..56f11650ffa4 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 252/691] 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 52c2a9c42c3d..2539de7b1247 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -886,18 +886,22 @@ def make_customer_gl_entry(self, gl_entries): ) 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 @@ def make_internal_transfer_gl_entries(self, gl_entries): 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 @@ def make_item_gl_entries(self, gl_entries): 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 @@ def make_item_gl_entries(self, gl_entries): 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 59879e0df58c..3d048c36865b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -808,6 +808,27 @@ def update_allocated_advance_taxes_on_cancel(self): 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 f3158ea448ea9c545db6fd2ebc97110b9d77f998 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 21 Jul 2021 19:47:41 +0530 Subject: [PATCH 253/691] fix: removed Remarks column from AR/AP report --- .../report/accounts_receivable/accounts_receivable.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index a11b77a6f645..b54646fd27af 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -99,7 +99,6 @@ def init_voucher_balance(self): voucher_no = gle.voucher_no, party = gle.party, posting_date = gle.posting_date, - remarks = gle.remarks, account_currency = gle.account_currency, invoiced = 0.0, paid = 0.0, @@ -579,7 +578,7 @@ def get_gl_entries(self): self.gl_entries = frappe.db.sql(""" select name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center, - against_voucher_type, against_voucher, account_currency, remarks, {0} + against_voucher_type, against_voucher, account_currency, {0} from `tabGL Entry` where @@ -792,8 +791,6 @@ def get_columns(self): self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link', options='Supplier Group') - self.add_column(label=_('Remarks'), fieldname='remarks', fieldtype='Text', width=200) - def add_column(self, label, fieldname=None, fieldtype='Currency', options=None, width=120): if not fieldname: fieldname = scrub(label) if fieldtype=='Currency': options='currency' From 6928fc17c695c05c25d52be70b9e5ed8110cb4dd Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 21 Jul 2021 19:54:06 +0530 Subject: [PATCH 254/691] chore: remove warning rules semgrep-action doesn't consider severity, hence ignoring these rules for now. --- .github/helper/semgrep_rules/security.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/helper/semgrep_rules/security.yml b/.github/helper/semgrep_rules/security.yml index 5a5098bf5061..8b2197920807 100644 --- a/.github/helper/semgrep_rules/security.yml +++ b/.github/helper/semgrep_rules/security.yml @@ -8,18 +8,3 @@ rules: dynamic content. Avoid it or use safe_eval(). languages: [python] severity: ERROR - -- id: frappe-sqli-format-strings - patterns: - - pattern-inside: | - @frappe.whitelist() - def $FUNC(...): - ... - - pattern-either: - - pattern: frappe.db.sql("..." % ...) - - pattern: frappe.db.sql(f"...", ...) - - pattern: frappe.db.sql("...".format(...), ...) - message: | - Detected use of raw string formatting for SQL queries. This can lead to sql injection vulnerabilities. Refer security guidelines - https://github.com/frappe/erpnext/wiki/Code-Security-Guidelines - languages: [python] - severity: WARNING From c765073c2a56688835b04d72fc335f654e2ef86a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 22:28:32 +0530 Subject: [PATCH 255/691] 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 0487ef644434..e20e385f63cd 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 @@ def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabl 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 @@ def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabl # 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 9f7d18bdb4a7..1c0177d3d086 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 @@ def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled( 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 256/691] 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 fe4d17c2102b..694c26567aa7 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 dd523c344398..1c2f9421faa7 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 000000000000..9b0279351f29 --- /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 4323f4bcac77f4c5e06439550cc94fee32fad32c Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 22 Jul 2021 05:57:42 +0530 Subject: [PATCH 257/691] 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 a0bac51046fd..f68d81909a3e 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 update_item(obj, target, source_parent): 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 d38c2cbdb5ae..77234aa999f8 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1038,7 +1038,14 @@ def set_payment_schedule(self): 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: @@ -1048,6 +1055,63 @@ def set_payment_schedule(self): d.base_payment_amount = flt(base_grand_total * flt(d.invoice_portion / 100), d.precision('payment_amount')) d.outstanding = d.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: @@ -1729,47 +1793,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 a58c381df352..2b9d516e217b 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -621,8 +621,6 @@ def update_item(source, target, source_parent): @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 update_item(source, target, source_parent): 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 1628f9301917..f99a01b8202b 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 get_pending_qty(item_row): 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 6d72a5fa0bb1..36f21465b5b0 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -604,7 +604,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) @@ -678,7 +677,7 @@ def get_pending_qty(item_row): 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 2ff0d3e0ebf5424ad8a50f65ad64b531a13f4b40 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jul 2021 10:43:16 +0530 Subject: [PATCH 258/691] 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 e20e385f63cd..9d2acdc9779a 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 @@ def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabl }) 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 1c0177d3d086..a7fbbdd5caa4 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 @@ def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled( 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 @@ def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled( "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 eecfc4c0aef57eb8146ff15d1b9210c62c73c489 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 22 Jul 2021 13:23:54 +0530 Subject: [PATCH 259/691] 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 2526e6df0ef7..f15af0a69746 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -27,6 +27,7 @@ def validate(self): 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() @@ -67,6 +68,12 @@ def validate_serialized_batch(self): 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 8360337ef730..e75e0a32fc6b 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -752,7 +752,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe 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 bad7b608acf8..70312bc543b5 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -165,8 +165,14 @@ def get_stock_ledger_entries(self, serial_no=None): ) 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 fcb6f0f4c2f0..395f33978700 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -76,6 +76,7 @@ def validate(self): 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 987549159391..07c32a943d69 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -31,6 +31,7 @@ def validate(self): 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 0d968fabfecafe544fe5700922eae206a4cb5816 Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Sat, 17 Jul 2021 14:42:38 -0400 Subject: [PATCH 260/691] fix: missing parameter 'country' --- .../chart_of_accounts_importer/chart_of_accounts_importer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index 4fd8413d838f..8456b49c8eec 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -391,5 +391,5 @@ def set_default_accounts(company): }) company.save() - install_country_fixtures(company.name) + install_country_fixtures(company.name, company.country) company.create_default_tax_template() From 5b32fa5ccd8c4bf5a017a9ff7ad7b4112daaabd0 Mon Sep 17 00:00:00 2001 From: Ankush Date: Thu, 22 Jul 2021 14:00:01 +0530 Subject: [PATCH 261/691] fix: SQL error on fetching RM in production plan (bp #26592) * fix: SQL error on fetching RM in production plan * refactor: avoid passing by reference and mutations --- .../production_plan/production_plan.py | 15 ++++-------- .../production_plan/test_production_plan.py | 23 ++++++++++++++++++- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 38a0ee77ad79..6a024f275aa2 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -747,9 +747,8 @@ def get_bin_details(row, company, for_warehouse=None, all_warehouse=False): group by item_code, warehouse """.format(conditions=conditions), { "item_code": row['item_code'] }, as_dict=1) -def get_warehouse_list(warehouses, warehouse_list=None): - if not warehouse_list: - warehouse_list = [] +def get_warehouse_list(warehouses): + warehouse_list = [] if isinstance(warehouses, str): warehouses = json.loads(warehouses) @@ -761,23 +760,19 @@ def get_warehouse_list(warehouses, warehouse_list=None): else: warehouse_list.append(row.get("warehouse")) + return warehouse_list + @frappe.whitelist() def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_data=None): if isinstance(doc, str): doc = frappe._dict(json.loads(doc)) - warehouse_list = [] if warehouses: - get_warehouse_list(warehouses, warehouse_list) - - if warehouse_list: - warehouses = list(set(warehouse_list)) + warehouses = list(set(get_warehouse_list(warehouses))) if doc.get("for_warehouse") and not get_parent_warehouse_data and doc.get("for_warehouse") in warehouses: warehouses.remove(doc.get("for_warehouse")) - warehouse_list = None - doc['mr_items'] = [] po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items') diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index cce1bb61b6b7..93e6d7a97f43 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -10,7 +10,7 @@ from erpnext.manufacturing.doctype.production_plan.production_plan import get_sales_orders 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 +from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests, get_warehouse_list class TestProductionPlan(unittest.TestCase): def setUp(self): @@ -251,6 +251,27 @@ def test_production_plan_with_multi_level_bom(self): pln.cancel() frappe.delete_doc("Production Plan", pln.name) + def test_get_warehouse_list_group(self): + """Check if required warehouses are returned""" + warehouse_json = '[{\"warehouse\":\"_Test Warehouse Group - _TC\"}]' + + warehouses = set(get_warehouse_list(warehouse_json)) + expected_warehouses = {"_Test Warehouse Group-C1 - _TC", "_Test Warehouse Group-C2 - _TC"} + + missing_warehouse = expected_warehouses - warehouses + + self.assertTrue(len(missing_warehouse) == 0, + msg=f"Following warehouses were expected {', '.join(missing_warehouse)}") + + def test_get_warehouse_list_single(self): + warehouse_json = '[{\"warehouse\":\"_Test Scrap Warehouse - _TC\"}]' + + warehouses = set(get_warehouse_list(warehouse_json)) + expected_warehouses = {"_Test Scrap Warehouse - _TC", } + + self.assertEqual(warehouses, expected_warehouses) + + def create_production_plan(**args): args = frappe._dict(args) From 56c67743abe7520d3e0df19b807636abbee54d2d Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 22 Jul 2021 16:10:06 +0530 Subject: [PATCH 262/691] fix: incorrect bom name (#26600) --- erpnext/manufacturing/doctype/bom/bom.js | 7 ++++--- erpnext/manufacturing/doctype/bom/bom.py | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 15a7c316c91d..bfbc6790b291 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -83,7 +83,7 @@ frappe.ui.form.on("BOM", { if (!frm.doc.__islocal && frm.doc.docstatus<2) { frm.add_custom_button(__("Update Cost"), function() { - frm.events.update_cost(frm); + frm.events.update_cost(frm, true); }); frm.add_custom_button(__("Browse BOM"), function() { frappe.route_options = { @@ -318,14 +318,15 @@ frappe.ui.form.on("BOM", { }) }, - update_cost: function(frm) { + update_cost: function(frm, save_doc=false) { return frappe.call({ doc: frm.doc, method: "update_cost", freeze: true, args: { update_parent: true, - from_child_bom:false + save: save_doc, + from_child_bom: false }, callback: function(r) { refresh_field("items"); diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 2fbbca4b1921..af081c449c62 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -330,7 +330,7 @@ def update_cost(self, update_parent=True, from_child_bom=False, update_hour_rate frappe.get_doc("BOM", bom).update_cost(from_child_bom=True) if not from_child_bom: - frappe.msgprint(_("Cost Updated")) + frappe.msgprint(_("Cost Updated"), alert=True) def update_parent_cost(self): if self.total_cost: From caa106b3bd2d33c3c6952f6b475e7fa5cf67a016 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 22 Jul 2021 22:58:45 +0530 Subject: [PATCH 263/691] 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 974648d6d443..bf6925473ad9 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 @@ def test_so_cancellation_when_si_drafted(self): 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 9c07454f91e4eb619c7bf196b9e233d7eadaffec Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 23 Jul 2021 03:23:29 +0530 Subject: [PATCH 264/691] 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 f981aeb13bba..8b1245eb4034 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.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 @@ def test_delivery_note_bundle_with_batched_item(self): 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 de6d960381ff807c8632fb3fdf5321cff969a2ee Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 23 Jul 2021 03:36:37 +0530 Subject: [PATCH 265/691] 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 8563b97ab74e..11cf39e5e2d4 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 @@ def test_po_optional_blanket_order(self): # 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 13e7103ee096547f2b5288346f6d3f54b9cfa8a9 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 23 Jul 2021 03:44:56 +0530 Subject: [PATCH 266/691] fix: Add test to check if payment terms are fetched when creating a Purchase Invoice --- .../purchase_receipt/test_purchase_receipt.py | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index dbba21fde1b7..89809733604f 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1079,6 +1079,31 @@ def test_purchase_receipt_with_exchange_rate_difference(self): 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 aed39a23dc00396a23d49a8d9dbadcd054c76a4b Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 23 Jul 2021 03:45:59 +0530 Subject: [PATCH 267/691] 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 11cf39e5e2d4..474c9cf3df97 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 @@ def test_po_optional_blanket_order(self): # 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 bf6925473ad9..3fbe0afd10e8 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 @@ def test_so_cancellation_when_si_drafted(self): 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 8b1245eb4034..ca8d8b99d31f 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 @@ def test_delivery_note_bundle_with_batched_item(self): 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 89809733604f..875814dc6443 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 @@ def test_purchase_receipt_with_exchange_rate_difference(self): 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 add2030553e1a6c633682856de78cd1873ca071d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 23 Jul 2021 03:58:27 +0530 Subject: [PATCH 268/691] 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 3fbe0afd10e8..f4a089bcef2d 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 @@ def test_payment_terms_are_fetched_when_creating_sales_invoice(self): 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 45d506c489df5b37d599c1c72ae6b16cc8809002 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 23 Jul 2021 16:40:45 +0530 Subject: [PATCH 269/691] fix: serial no and batch validation --- erpnext/controllers/stock_controller.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2526e6df0ef7..17bd7354f93a 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -53,12 +53,17 @@ def validate_serialized_batch(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos for d in self.get("items"): if hasattr(d, 'serial_no') and hasattr(d, 'batch_no') and d.serial_no and d.batch_no: - serial_nos = get_serial_nos(d.serial_no) - for serial_no_data in frappe.get_all("Serial No", - filters={"name": ("in", serial_nos)}, fields=["batch_no", "name"]): - if serial_no_data.batch_no != d.batch_no: + serial_nos = frappe.get_all("Serial No", + fields=["batch_no", "name", "warehouse"], + filters={ + "name": ("in", get_serial_nos(d.serial_no)) + } + ) + + for row in serial_nos: + if row.warehouse and row.batch_no != d.batch_no: frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}") - .format(d.idx, serial_no_data.name, d.batch_no)) + .format(d.idx, row.name, d.batch_no)) if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2: expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date") From 328444b530afea225103660a0c5fea9124e0f014 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 23 Jul 2021 20:28:02 +0530 Subject: [PATCH 270/691] 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 2a8363511773..b891719b02d8 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 000000000000..478a2a6c8068 --- /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 92654608da52..e9372f9b8fc2 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 9ef157b23b566369fd04ad68533288c41445b527 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 23 Jul 2021 20:44:34 +0530 Subject: [PATCH 271/691] fix: wrong operation time in Work Order (#26613) * fix: wrong operation time in Work Order Top level item time operation was not considering the BOM.quantity Co-authored-by: Ankush Menat --- .../doctype/work_order/work_order.py | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 0a8e5329c151..69812c7452ce 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -487,21 +487,20 @@ def _get_operations(bom_no, qty=1): return operations = [] - if not self.use_multi_level_bom: - bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity") - operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty)) - else: + + if self.use_multi_level_bom: bom_tree = frappe.get_doc("BOM", self.bom_no).get_tree_representation() - bom_traversal = list(reversed(bom_tree.level_order_traversal())) - bom_traversal.append(bom_tree) # add operation on top level item last + bom_traversal = reversed(bom_tree.level_order_traversal()) - for d in bom_traversal: - if d.is_bom: - operations.extend(_get_operations(d.name, qty=d.exploded_qty)) + for node in bom_traversal: + if node.is_bom: + operations.extend(_get_operations(node.name, qty=node.exploded_qty)) - for correct_index, operation in enumerate(operations, start=1): - operation.idx = correct_index + bom_qty = frappe.db.get_value("BOM", self.bom_no, "quantity") + operations.extend(_get_operations(self.bom_no, qty=1.0/bom_qty)) + for correct_index, operation in enumerate(operations, start=1): + operation.idx = correct_index self.set('operations', operations) self.calculate_time() From fac88a3329425fe075e0114fd3ca4ef8f613cf85 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Fri, 23 Jul 2021 21:23:48 +0530 Subject: [PATCH 272/691] fix: Supplier Invoice Importer fix --- .../chart_of_accounts_importer/chart_of_accounts_importer.py | 2 +- erpnext/controllers/accounts_controller.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index 8456b49c8eec..4fd8413d838f 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -391,5 +391,5 @@ def set_default_accounts(company): }) company.save() - install_country_fixtures(company.name, company.country) + install_country_fixtures(company.name) company.create_default_tax_template() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 4c313c43a720..cdd865ac4ac9 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1112,8 +1112,11 @@ def set_payment_schedule(self): 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 017ed3f5c11b8e3467c24544f0df4dc7a36b6271 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sat, 24 Jul 2021 00:08:02 +0530 Subject: [PATCH 273/691] fix: employee status server-side validation (#26615) --- erpnext/hr/doctype/appraisal/appraisal.py | 3 +- erpnext/hr/doctype/attendance/attendance.py | 2 ++ .../attendance_request/attendance_request.py | 3 +- .../compensatory_leave_request.py | 3 +- erpnext/hr/doctype/employee/employee.py | 8 +++-- erpnext/hr/doctype/employee/test_employee.py | 30 +++++++++++++++++-- .../employee_advance/employee_advance.py | 6 ++-- .../employee_checkin/employee_checkin.py | 4 ++- .../employee_promotion/employee_promotion.py | 5 ++-- .../employee_referral/employee_referral.py | 2 ++ .../employee_transfer/employee_transfer.py | 4 --- .../hr/doctype/expense_claim/expense_claim.py | 3 +- .../leave_application/leave_application.py | 3 +- .../leave_encashment/leave_encashment.py | 3 +- .../shift_assignment/shift_assignment.py | 2 ++ .../hr/doctype/shift_request/shift_request.py | 3 +- .../doctype/travel_request/travel_request.py | 4 ++- erpnext/hr/utils.py | 11 +++++-- .../additional_salary/additional_salary.py | 2 ++ .../employee_benefit_application.py | 3 +- .../employee_benefit_claim.py | 3 +- .../employee_incentive/employee_incentive.py | 2 ++ .../employee_tax_exemption_declaration.py | 3 +- ...employee_tax_exemption_proof_submission.py | 3 +- .../retention_bonus/retention_bonus.py | 5 ++-- .../doctype/salary_slip/salary_slip.py | 2 ++ .../projects/doctype/timesheet/timesheet.py | 3 ++ 27 files changed, 91 insertions(+), 34 deletions(-) diff --git a/erpnext/hr/doctype/appraisal/appraisal.py b/erpnext/hr/doctype/appraisal/appraisal.py index f7601870fac3..c2ed4579844e 100644 --- a/erpnext/hr/doctype/appraisal/appraisal.py +++ b/erpnext/hr/doctype/appraisal/appraisal.py @@ -9,7 +9,7 @@ from frappe import _ from frappe.model.mapper import get_mapped_doc from frappe.model.document import Document -from erpnext.hr.utils import set_employee_name +from erpnext.hr.utils import set_employee_name, validate_active_employee class Appraisal(Document): def validate(self): @@ -19,6 +19,7 @@ def validate(self): if not self.goals: frappe.throw(_("Goals cannot be empty")) + validate_active_employee(self.employee) set_employee_name(self) self.validate_dates() self.validate_existing_appraisal() diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index 3412675d8119..f79f0fe4180f 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -8,11 +8,13 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import cstr, get_datetime, formatdate +from erpnext.hr.utils import validate_active_employee class Attendance(Document): def validate(self): from erpnext.controllers.status_updater import validate_status validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"]) + validate_active_employee(self.employee) self.validate_attendance_date() self.validate_duplicate_record() self.validate_employee_status() diff --git a/erpnext/hr/doctype/attendance_request/attendance_request.py b/erpnext/hr/doctype/attendance_request/attendance_request.py index 090d53262cf5..7f88fed73a2e 100644 --- a/erpnext/hr/doctype/attendance_request/attendance_request.py +++ b/erpnext/hr/doctype/attendance_request/attendance_request.py @@ -8,10 +8,11 @@ from frappe.model.document import Document from frappe.utils import date_diff, add_days, getdate from erpnext.hr.doctype.employee.employee import is_holiday -from erpnext.hr.utils import validate_dates +from erpnext.hr.utils import validate_dates, validate_active_employee class AttendanceRequest(Document): def validate(self): + validate_active_employee(self.employee) validate_dates(self, self.from_date, self.to_date) if self.half_day: if not getdate(self.from_date)<=getdate(self.half_day_date)<=getdate(self.to_date): diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py index a6fe429be178..0d7fded921b4 100644 --- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py +++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py @@ -7,12 +7,13 @@ from frappe import _ from frappe.utils import date_diff, add_days, getdate, cint, format_date from frappe.model.document import Document -from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, \ +from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, validate_active_employee, \ get_holidays_for_employee, create_additional_leave_ledger_entry class CompensatoryLeaveRequest(Document): def validate(self): + validate_active_employee(self.employee) validate_dates(self, self.work_from_date, self.work_end_date) if self.half_day: if not self.half_day_date: diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index fa017d9d4c2b..5ca47560b106 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -13,8 +13,10 @@ from erpnext.utilities.transaction_base import delete_events from frappe.utils.nestedset import NestedSet -class EmployeeUserDisabledError(frappe.ValidationError): pass -class EmployeeLeftValidationError(frappe.ValidationError): pass +class EmployeeUserDisabledError(frappe.ValidationError): + pass +class InactiveEmployeeStatusError(frappe.ValidationError): + pass class Employee(NestedSet): nsm_parent_field = 'reports_to' @@ -196,7 +198,7 @@ def validate_status(self): message += "

                              • " + "
                              • ".join(link_to_employees) message += "

                              " message += _("Please make sure the employees above report to another Active employee.") - throw(message, EmployeeLeftValidationError, _("Cannot Relieve Employee")) + throw(message, InactiveEmployeeStatusError, _("Cannot Relieve Employee")) if not self.relieving_date: throw(_("Please enter relieving date.")) diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py index 7d652a7366a9..8fc7cf193435 100644 --- a/erpnext/hr/doctype/employee/test_employee.py +++ b/erpnext/hr/doctype/employee/test_employee.py @@ -7,7 +7,7 @@ import erpnext import unittest import frappe.utils -from erpnext.hr.doctype.employee.employee import EmployeeLeftValidationError +from erpnext.hr.doctype.employee.employee import InactiveEmployeeStatusError test_records = frappe.get_test_records('Employee') @@ -45,10 +45,33 @@ def test_employee_status_left(self): employee2_doc.save() employee1_doc.reload() employee1_doc.status = 'Left' - self.assertRaises(EmployeeLeftValidationError, employee1_doc.save) + self.assertRaises(InactiveEmployeeStatusError, employee1_doc.save) + + def test_employee_status_inactive(self): + from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip + from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list + + employee = make_employee("test_employee_status@company.com") + employee_doc = frappe.get_doc("Employee", employee) + employee_doc.status = "Inactive" + employee_doc.save() + employee_doc.reload() + + make_holiday_list() + frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List") + + frappe.db.sql("""delete from `tabSalary Structure` where name='Test Inactive Employee Salary Slip'""") + salary_structure = make_salary_structure("Test Inactive Employee Salary Slip", "Monthly", + employee=employee_doc.name, company=employee_doc.company) + salary_slip = make_salary_slip(salary_structure.name, employee=employee_doc.name) + + self.assertRaises(InactiveEmployeeStatusError, salary_slip.save) + + def tearDown(self): + frappe.db.rollback() def make_employee(user, company=None, **kwargs): - "" if not frappe.db.get_value("User", user): frappe.get_doc({ "doctype": "User", @@ -80,4 +103,5 @@ def make_employee(user, company=None, **kwargs): employee.insert() return employee.name else: + frappe.db.set_value("Employee", {"employee_name":user}, "status", "Active") return frappe.get_value("Employee", {"employee_name":user}, "name") diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index cb72f6b6d96e..ece627c50de9 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -8,6 +8,7 @@ from frappe.model.document import Document from frappe.utils import flt, nowdate from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account +from erpnext.hr.utils import validate_active_employee class EmployeeAdvanceOverPayment(frappe.ValidationError): pass @@ -18,6 +19,7 @@ def onload(self): 'make_payment_via_journal_entry') def validate(self): + validate_active_employee(self.employee) self.set_status() def on_cancel(self): @@ -183,9 +185,9 @@ def make_return_entry(employee, company, employee_advance_name, return_amount, bank_cash_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment) if not bank_cash_account: frappe.throw(_("Please set a Default Cash Account in Company defaults")) - + advance_account_currency = frappe.db.get_value('Account', advance_account, 'account_currency') - + je = frappe.new_doc('Journal Entry') je.posting_date = nowdate() je.voucher_type = get_voucher_type(mode_of_payment) diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py index 15fbd4e01530..60ea0f9895de 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py @@ -9,9 +9,11 @@ from frappe import _ from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift +from erpnext.hr.utils import validate_active_employee class EmployeeCheckin(Document): def validate(self): + validate_active_employee(self.employee) self.validate_duplicate_log() self.fetch_shift() @@ -122,7 +124,7 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type): """Given a set of logs in chronological order calculates the total working hours based on the parameters. Zero is returned for all invalid cases. - + :param logs: The List of 'Employee Checkin'. :param check_in_out_type: One of: 'Alternating entries as IN and OUT during the same shift', 'Strictly based on Log Type in Employee Checkin' :param working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out' diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py index 83fb235f92c1..a3a61834c8c4 100644 --- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py +++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py @@ -7,12 +7,11 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import getdate -from erpnext.hr.utils import update_employee +from erpnext.hr.utils import update_employee, validate_active_employee class EmployeePromotion(Document): def validate(self): - if frappe.get_value("Employee", self.employee, "status") != "Active": - frappe.throw(_("Cannot promote Employee with status Left or Inactive")) + validate_active_employee(self.employee) def before_submit(self): if getdate(self.promotion_date) > getdate(): diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.py b/erpnext/hr/doctype/employee_referral/employee_referral.py index 45d68729ce67..0493306166f8 100644 --- a/erpnext/hr/doctype/employee_referral/employee_referral.py +++ b/erpnext/hr/doctype/employee_referral/employee_referral.py @@ -7,9 +7,11 @@ from frappe import _ from frappe.utils import get_link_to_form from frappe.model.document import Document +from erpnext.hr.utils import validate_active_employee class EmployeeReferral(Document): def validate(self): + validate_active_employee(self.referrer) self.set_full_name() self.set_referral_bonus_payment_status() diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py index 6eec9fa12a93..c2007747fb3d 100644 --- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py +++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py @@ -10,10 +10,6 @@ from erpnext.hr.utils import update_employee class EmployeeTransfer(Document): - def validate(self): - if frappe.get_value("Employee", self.employee, "status") != "Active": - frappe.throw(_("Cannot transfer Employee with status Left or Inactive")) - def before_submit(self): if getdate(self.transfer_date) > getdate(): frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"), diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index 5010fc3f75c7..8f8dbb222459 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -6,7 +6,7 @@ from frappe import _ from frappe.utils import get_fullname, flt, cstr, get_link_to_form from frappe.model.document import Document -from erpnext.hr.utils import set_employee_name, share_doc_with_approver +from erpnext.hr.utils import set_employee_name, share_doc_with_approver, validate_active_employee from erpnext.accounts.party import get_party_account from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account @@ -23,6 +23,7 @@ def onload(self): 'make_payment_via_journal_entry') def validate(self): + validate_active_employee(self.employee) self.validate_advances() self.validate_sanctioned_amount() self.calculate_total_amount() diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index cee6f374fdc7..93fb19f4a191 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -5,7 +5,7 @@ import frappe from frappe import _ from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, get_fullname, add_days, nowdate -from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver +from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver, validate_active_employee from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange @@ -22,6 +22,7 @@ def get_feed(self): return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type) def validate(self): + validate_active_employee(self.employee) set_employee_name(self) self.validate_dates() self.validate_balance_leaves() diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index e041b7fb8f81..912bd8ad92f8 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -7,7 +7,7 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import getdate, nowdate, flt -from erpnext.hr.utils import set_employee_name +from erpnext.hr.utils import set_employee_name, validate_active_employee from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves @@ -15,6 +15,7 @@ class LeaveEncashment(Document): def validate(self): set_employee_name(self) + validate_active_employee(self.employee) self.get_leave_details_for_encashment() self.validate_salary_structure() diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py index ab65260c0918..89ae4d535d45 100644 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py +++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py @@ -9,10 +9,12 @@ from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, now_datetime, nowdate from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday +from erpnext.hr.utils import validate_active_employee from datetime import timedelta, datetime class ShiftAssignment(Document): def validate(self): + validate_active_employee(self.employee) self.validate_overlapping_dates() if self.end_date and self.end_date <= self.start_date: diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py index 177c45edc657..6461f07552b1 100644 --- a/erpnext/hr/doctype/shift_request/shift_request.py +++ b/erpnext/hr/doctype/shift_request/shift_request.py @@ -7,12 +7,13 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import formatdate, getdate -from erpnext.hr.utils import share_doc_with_approver +from erpnext.hr.utils import share_doc_with_approver, validate_active_employee class OverlapError(frappe.ValidationError): pass class ShiftRequest(Document): def validate(self): + validate_active_employee(self.employee) self.validate_dates() self.validate_shift_request_overlap_dates() self.validate_approver() diff --git a/erpnext/hr/doctype/travel_request/travel_request.py b/erpnext/hr/doctype/travel_request/travel_request.py index 01d3f3470614..60834d3f4a64 100644 --- a/erpnext/hr/doctype/travel_request/travel_request.py +++ b/erpnext/hr/doctype/travel_request/travel_request.py @@ -5,6 +5,8 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from erpnext.hr.utils import validate_active_employee class TravelRequest(Document): - pass + def validate(self): + validate_active_employee(self.employee) diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index ebb17343471c..a6a84068037a 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -3,13 +3,12 @@ import erpnext import frappe -from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee +from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee, InactiveEmployeeStatusError from frappe import _ from frappe.desk.form import assign_to from frappe.model.document import Document from frappe.utils import (add_days, cstr, flt, format_datetime, formatdate, - get_datetime, getdate, nowdate, today, unique) - + get_datetime, getdate, nowdate, today, unique, get_link_to_form) class DuplicateDeclarationError(frappe.ValidationError): pass @@ -20,6 +19,7 @@ class EmployeeBoardingController(Document): Assign to the concerned person and roles as per the onboarding/separation template ''' def validate(self): + validate_active_employee(self.employee) # remove the task if linked before submitting the form if self.amended_from: for activity in self.activities: @@ -522,3 +522,8 @@ def share_doc_with_approver(doc, user): approver = approvers.get(doc.doctype) if doc_before_save.get(approver) != doc.get(approver): frappe.share.remove(doc.doctype, doc.name, doc_before_save.get(approver)) + +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 diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py index 7db4b8686a09..b978cbe2b572 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py @@ -7,6 +7,7 @@ from frappe.model.document import Document from frappe import _, bold from frappe.utils import getdate, date_diff, comma_and, formatdate +from erpnext.hr.utils import validate_active_employee class AdditionalSalary(Document): def on_submit(self): @@ -19,6 +20,7 @@ def on_cancel(self): self.update_employee_referral(cancel=True) def validate(self): + validate_active_employee(self.employee) self.validate_dates() self.validate_salary_structure() self.validate_recurring_additional_salary_overlap() 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 27df30a459cf..5ebe514ac052 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py @@ -9,10 +9,11 @@ from frappe.model.document import Document from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure -from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount +from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount, validate_active_employee class EmployeeBenefitApplication(Document): def validate(self): + validate_active_employee(self.employee) self.validate_duplicate_on_payroll_period() if not self.max_benefits: self.max_benefits = get_max_benefits_remaining(self.employee, self.date, self.payroll_period) diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py index d9937a7bb978..c6713f3aa46b 100644 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -8,12 +8,13 @@ from frappe.utils import flt from frappe.model.document import Document from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits -from erpnext.hr.utils import get_previous_claimed_amount +from erpnext.hr.utils import get_previous_claimed_amount, validate_active_employee from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure class EmployeeBenefitClaim(Document): def validate(self): + validate_active_employee(self.employee) max_benefits = get_max_benefits(self.employee, self.claim_date) if not max_benefits or max_benefits <= 0: frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee)) diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py index ead3db126f72..6b918ba76d15 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py @@ -6,9 +6,11 @@ import frappe from frappe import _ from frappe.model.document import Document +from erpnext.hr.utils import validate_active_employee class EmployeeIncentive(Document): def validate(self): + validate_active_employee(self.employee) self.validate_salary_structure() def validate_salary_structure(self): diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index fb71a2877a1b..e11d60a4649b 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -8,11 +8,12 @@ from frappe import _ from frappe.utils import flt from frappe.model.mapper import get_mapped_doc -from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \ +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \ calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period class EmployeeTaxExemptionDeclaration(Document): def validate(self): + validate_active_employee(self.employee) validate_tax_declaration(self.declarations) validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee) self.set_total_declared_amount() diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py index 5bc33a65f2c1..8131ae0fa854 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py @@ -7,11 +7,12 @@ from frappe.model.document import Document from frappe import _ from frappe.utils import flt -from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \ +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \ calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period class EmployeeTaxExemptionProofSubmission(Document): def validate(self): + validate_active_employee(self.employee) validate_tax_declaration(self.tax_exemption_proofs) self.set_total_actual_amount() self.set_total_exemption_amount() diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py index 049ea265cce2..055bea741081 100644 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py @@ -7,11 +7,10 @@ from frappe.model.document import Document from frappe import _ from frappe.utils import getdate - +from erpnext.hr.utils import validate_active_employee class RetentionBonus(Document): def validate(self): - if frappe.get_value('Employee', self.employee, 'status') != 'Active': - frappe.throw(_('Cannot create Retention Bonus for Left or Inactive Employees')) + validate_active_employee(self.employee) if getdate(self.bonus_payment_date) < getdate(): frappe.throw(_('Bonus Payment Date cannot be a past date')) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index 81e5dc9f87dd..3e82c0d428aa 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -19,6 +19,7 @@ from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry from erpnext.accounts.utils import get_fiscal_year +from erpnext.hr.utils import validate_active_employee from six import iteritems class SalarySlip(TransactionBase): @@ -39,6 +40,7 @@ def autoname(self): def validate(self): self.status = self.get_status() + validate_active_employee(self.employee) self.validate_dates() self.check_existing() if not self.salary_slip_based_on_timesheet: diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index c8bd80fca0a8..ae38d4ca1925 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -15,12 +15,15 @@ WorkstationHolidayError) from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations from erpnext.setup.utils import get_exchange_rate +from erpnext.hr.utils import validate_active_employee class OverlapError(frappe.ValidationError): pass class OverWorkLoggedError(frappe.ValidationError): pass class Timesheet(Document): def validate(self): + if self.employee: + validate_active_employee(self.employee) self.set_employee_name() self.set_status() self.validate_dates() From f4701c174a79b848ad30325036832235458fec85 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 25 Jul 2021 19:46:20 +0530 Subject: [PATCH 274/691] 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 56193216a227..e94875f2d72c 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 @@ def get_accounts_from_gle(self): 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 @@ def make_jv_entry(self): "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 @@ def make_jv_entry(self): "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 c485d5c3b7503136ad03cc29dd69317e2ad17696 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 25 Jul 2021 19:46:50 +0530 Subject: [PATCH 275/691] 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 e94875f2d72c..c8d5737d7530 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 @@ def validate_mandatory(self): 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 57cb3ac023544c618b2c7c9fa5927a81cd31a848 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 20:23:20 +0530 Subject: [PATCH 276/691] 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 ecfaab23eed7..46fb354c11c4 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 c9ee7a622c49..5bc1e56a2100 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 0a2ac1affc88..cc01d89344a8 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 277/691] feat: Export chart option in desktop view --- .../hierarchy_chart_desktop.js | 99 +++++++++++++------ erpnext/public/scss/hierarchy_chart.scss | 10 +- erpnext/utilities/hierarchy_chart.py | 4 +- 3 files changed, 83 insertions(+), 30 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 694c26567aa7..57d34d822528 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 1c2f9421faa7..44288fe1552f 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 9b0279351f29..22d3f28faa7b 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 278/691] 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 44288fe1552f..7f1077dbbd29 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 67273b955123778e4018aeb8dcde01bf1a60cbd4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 25 Jul 2021 21:26:22 +0530 Subject: [PATCH 279/691] 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 c8d5737d7530..f2b0a8c08a62 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 @@ def get_accounts_from_gle(self): 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 6bca87ddb9c39cd18abb30bc1b9f4cb5f07b451c Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 25 Jul 2021 21:34:51 +0530 Subject: [PATCH 280/691] 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 22d3f28faa7b..fb58a5d58679 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 281/691] 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 57d34d822528..89fb8d579253 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 2d439f235522b55e236a7f31e407bf56f89d153e Mon Sep 17 00:00:00 2001 From: ChillarAnand Date: Mon, 26 Jul 2021 11:08:31 +0530 Subject: [PATCH 282/691] chore: Updated CODEOWNERS --- CODEOWNERS | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 219b6bb7821a..330a8dba0157 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/crm/ @ruchamahabal @pateljannat erpnext/education/ @ruchamahabal -erpnext/healthcare/ @ruchamahabal -erpnext/hr/ @ruchamahabal +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 4209b3bda9dce91efa816d7dd43a655210ef02a5 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 26 Jul 2021 13:00:32 +0530 Subject: [PATCH 283/691] 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 52daec91805d..1ba752a14645 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -24,7 +24,8 @@ "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 000000000000..41a0e8a9f999 --- /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 64454a5dc8510c6a9b76c9a53ef8a00900a62f4d Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Mon, 26 Jul 2021 12:54:35 +0530 Subject: [PATCH 284/691] fix: included company in Link Document Type filters for contact (#26576) (cherry picked from commit cbddedab7bf2fc7637b861214c3373a742da830b) --- 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 52daec91805d..1ba752a14645 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -24,7 +24,8 @@ "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 000000000000..41a0e8a9f999 --- /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 285/691] 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 4c313c43a720..cdd865ac4ac9 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1112,8 +1112,11 @@ def set_payment_schedule(self): 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 ed68f11a4670f0ee9d080dbf293818b026e36f50 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 26 Jul 2021 16:47:36 +0530 Subject: [PATCH 286/691] fix: Supplier invoice importer fix pre release (#26636) * 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 4c313c43a720..cdd865ac4ac9 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1112,8 +1112,11 @@ def set_payment_schedule(self): 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 50b76d04bfae7a03472c0d08b104744fc8b941ac Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Mon, 19 Jul 2021 20:09:37 +0530 Subject: [PATCH 287/691] 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 6635128f9efd..d788d91855ec 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 00fd319531438cd6eb31db5b80584ccdf17687d3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 19 Jul 2021 14:36:54 +0530 Subject: [PATCH 288/691] 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 641520437fb2..6de228fbc7eb 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 @@ def set_outward_taxable_supplies(self): 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 c468e4a93d991000ce3e4e86b30567c99b1a5fbc Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 19 Jul 2021 14:36:54 +0530 Subject: [PATCH 289/691] 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 641520437fb2..6de228fbc7eb 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 @@ def set_outward_taxable_supplies(self): 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 19589b1e21083c42486e70ea405e8d29fdb75b1d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 13:25:53 +0530 Subject: [PATCH 290/691] 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 641520437fb2..6a61ae2b4222 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 @@ def get_outward_items(self, doctype): 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 cfcb8c3444f1..b81fa810fe1f 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -217,9 +217,8 @@ def get_invoice_items(self): 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 5fe7d80a6453b4dd7d33be059eeee62bc88814e8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 13:25:53 +0530 Subject: [PATCH 291/691] 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 641520437fb2..6a61ae2b4222 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 @@ def get_outward_items(self, doctype): 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 cfcb8c3444f1..b81fa810fe1f 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -217,9 +217,8 @@ def get_invoice_items(self): 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 abc63cffa838fb83b565411bfa61cfdc8018ed79 Mon Sep 17 00:00:00 2001 From: Anuja Pawar Date: Mon, 26 Jul 2021 18:11:38 +0530 Subject: [PATCH 292/691] fix: suggested changes --- .../vat_audit_report/vat_audit_report.py | 33 ++++++++++--------- erpnext/regional/south_africa/setup.py | 2 +- 2 files changed, 18 insertions(+), 17 deletions(-) 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 5633b64bfb8d..feb2a1620d49 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -40,7 +40,7 @@ def run(self): def get_sa_vat_accounts(self): self.sa_vat_accounts = frappe.get_list("South Africa VAT Account", - filters = {"parent":self.filters.company}, pluck="account") + filters = {"parent": self.filters.company}, pluck="account") if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate: frappe.throw(_("Please set VAT Accounts in South Africa VAT Settings")) @@ -144,7 +144,7 @@ def get_conditions(self): return conditions - def get_data(self,doctype): + def get_data(self, doctype): consolidated_data = self.get_consolidated_data() section_name = _("Purchases") if doctype == "Purchase Invoice" else _("Sales") @@ -173,20 +173,21 @@ def get_data(self,doctype): def get_consolidated_data(self): consolidated_data_map={} for inv, inv_data in self.invoices.items(): - for rate, items in self.items_based_on_tax_rate.get(inv).items(): - consolidated_data_map.setdefault(rate, {"data": []}) - for item in items: - row = {} - item_details = self.item_tax_rate.get(inv).get(item) - row["account"] = inv_data.get("account") - row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') - row["invoice_number"] = inv - row["party"] = inv_data.get("party") - row["remarks"] = inv_data.get("remarks") - row["gross_amount"]= item_details[0].get("gross_amount") - row["tax_amount"]= item_details[0].get("tax_amount") - row["net_amount"]= item_details[0].get("net_amount") - consolidated_data_map[rate]["data"].append(row) + if self.items_based_on_tax_rate.get(inv): + for rate, items in self.items_based_on_tax_rate.get(inv).items(): + consolidated_data_map.setdefault(rate, {"data": []}) + for item in items: + row = {} + item_details = self.item_tax_rate.get(inv).get(item) + row["account"] = inv_data.get("account") + row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') + row["invoice_number"] = inv + row["party"] = inv_data.get("party") + row["remarks"] = inv_data.get("remarks") + row["gross_amount"]= item_details[0].get("gross_amount") + row["tax_amount"]= item_details[0].get("tax_amount") + row["net_amount"]= item_details[0].get("net_amount") + consolidated_data_map[rate]["data"].append(row) return consolidated_data_map diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py index 2c44c6e6b17d..a4335bddef2e 100644 --- a/erpnext/regional/south_africa/setup.py +++ b/erpnext/regional/south_africa/setup.py @@ -6,7 +6,7 @@ # import frappe, os, json from frappe.permissions import add_permission, update_permission_property -def setup(): +def setup(company=None, patch=True): add_permissions() def add_permissions(): From bc82344fc7242ba537c1a30879ec304efb30ac08 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Mon, 28 Jun 2021 16:50:20 +0530 Subject: [PATCH 293/691] 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 3ad9909ad07e..026b85e26d25 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -162,8 +162,15 @@ def update_completed_qty(self, mr_items=None, update_modified=True): 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 2a9dcfb67ed0..f75cb5613853 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 ba18a96b0cfa452ffcd6bd810ef08142dc4edfb3 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Wed, 30 Jun 2021 20:16:50 +0530 Subject: [PATCH 294/691] 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 72a3a5e67c71..b4776ba24920 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 @@ def test_completed_qty_for_transfer(self): 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 16feebf6856600cca43a4c1a524fbd98b0de9be7 Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Mon, 26 Jul 2021 18:17:30 +0530 Subject: [PATCH 295/691] Update CODEOWNERS chore: update code owner for education module --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 330a8dba0157..a4a14de1b8e2 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -22,7 +22,7 @@ erpnext/shopping_cart/ @marination erpnext/stock/ @marination @rohitwaghchaure @ankush erpnext/crm/ @ruchamahabal @pateljannat -erpnext/education/ @ruchamahabal +erpnext/education/ @ruchamahabal @pateljannat erpnext/healthcare/ @ruchamahabal @pateljannat @chillaranand erpnext/hr/ @ruchamahabal @pateljannat erpnext/non_profit/ @ruchamahabal From d066eab6cd15ded671cbb5c34d8f4a8847a7ba64 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 26 Jul 2021 18:38:50 +0530 Subject: [PATCH 296/691] 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 641520437fb2..6fd135d5606b 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 @@ def get_outward_tax_details(self, doctype): 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 cfcb8c3444f1..f9de2d527ba0 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -287,7 +287,8 @@ def get_items_based_on_tax_rate(self): # 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 a661667e2a340afdc495cd78cb4dfb59fda5546e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 23 Jul 2021 20:28:02 +0530 Subject: [PATCH 297/691] 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 2a8363511773..b891719b02d8 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 000000000000..478a2a6c8068 --- /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 92654608da52..e9372f9b8fc2 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 cba847b0517b65fdf415da4224f8c55ea69d6b43 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 26 Jul 2021 18:38:50 +0530 Subject: [PATCH 298/691] 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 641520437fb2..6fd135d5606b 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 @@ def get_outward_tax_details(self, doctype): 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 cfcb8c3444f1..f9de2d527ba0 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -287,7 +287,8 @@ def get_items_based_on_tax_rate(self): # 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 299/691] 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 52efbb5f6cda..53d5278bbf45 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 b3af3d67eaa9..5475383759f5 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 300/691] 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 56193216a227..e94875f2d72c 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 @@ def get_accounts_from_gle(self): 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 @@ def make_jv_entry(self): "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 @@ def make_jv_entry(self): "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 301/691] 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 e94875f2d72c..c8d5737d7530 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 @@ def validate_mandatory(self): 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 302/691] 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 c8d5737d7530..f2b0a8c08a62 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 @@ def get_accounts_from_gle(self): 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 356a55258ee4248deea931db7a7ee1b6252116d3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 25 Jul 2021 19:46:20 +0530 Subject: [PATCH 303/691] 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 56193216a227..e94875f2d72c 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 @@ def get_accounts_from_gle(self): 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 @@ def make_jv_entry(self): "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 @@ def make_jv_entry(self): "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 19a0ca1980b69d04310cbbe27e05f117622c3894 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 25 Jul 2021 19:46:50 +0530 Subject: [PATCH 304/691] 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 e94875f2d72c..c8d5737d7530 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 @@ def validate_mandatory(self): 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 3fcc5e3134f0aa2d29d6baabbc38050437fa42e1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 25 Jul 2021 21:26:22 +0530 Subject: [PATCH 305/691] 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 c8d5737d7530..f2b0a8c08a62 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 @@ def get_accounts_from_gle(self): 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 5c82af50f92e54382cda072735716f3ed45d9efe Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 26 Jul 2021 19:41:53 +0530 Subject: [PATCH 306/691] fix: Set Expense account from warehouse only if warehouse exists --- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index b99d75ec496b..1d80184abf18 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -246,7 +246,7 @@ def set_expense_account(self, for_validate=False): and (not item.po_detail or not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")): - if self.update_stock and (not item.from_warehouse): + if self.update_stock and item.warehouse and (not item.from_warehouse): if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]: msg = _("Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account").format( item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]), frappe.bold(item.expense_account), frappe.bold(item.warehouse)) @@ -657,7 +657,7 @@ def make_item_gl_entries(self, gl_entries): ) gl_entries.append( self.get_gl_dict({ - "account": self.get_company_default("exchange_gain_loss_account"), + "account": self.get_company_default("exchange_gain_loss_account"), "against": self.supplier, "credit": discrepancy_caused_by_exchange_rate_difference, "cost_center": item.cost_center, @@ -1193,7 +1193,7 @@ def get_purchase_document_details(doc): 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)) From cfd73ed554b21550592dd57abc561bd8eb72f3a9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 26 Jul 2021 19:42:19 +0530 Subject: [PATCH 307/691] 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 7c6b8432b877..cc98f4544f8d 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 77f9f048b672330e48f46685e38aa965e98d4660 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 26 Jul 2021 21:32:15 +0530 Subject: [PATCH 308/691] chore: test case for missing default warehouse on mapping --- .../purchase_invoice/purchase_invoice.py | 6 +- .../sales_invoice/test_sales_invoice.py | 92 ++++++++++++++++++- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 1d80184abf18..863c104dff4e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -27,6 +27,8 @@ 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 +class WarehouseMissingError(frappe.ValidationError): pass + form_grid_templates = { "items": "templates/form_grid/item_grid.html" } @@ -207,8 +209,8 @@ def validate_warehouse(self, for_validate=True): if self.update_stock and for_validate: for d in self.get('items'): if not d.warehouse: - frappe.throw(_("Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}"). - format(d.idx, d.item_code, self.company)) + frappe.throw(_("Row No {0}: Warehouse is required. Please set a Default Warehouse for Item {1} and Company {2}"). + format(d.idx, d.item_code, self.company), exc=WarehouseMissingError) super(PurchaseInvoice, self).validate_warehouse() diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index c6e6e3da6f16..be20b18beada 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2,13 +2,14 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext import unittest, copy, time from frappe.utils import nowdate, flt, getdate, cint, add_days, add_months from frappe.model.dynamic_links import get_dynamic_link_map from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice +from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import WarehouseMissingError from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency @@ -1073,7 +1074,7 @@ def test_return_sales_invoice(self): def test_gle_made_when_asset_is_returned(self): create_asset_data() asset = create_asset(item_code="Macbook Pro") - + si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000) return_si = create_sales_invoice(is_return=1, return_against=si.name, item_code="Macbook Pro", asset=asset.name, qty=-1, rate=90000) @@ -1081,7 +1082,7 @@ def test_gle_made_when_asset_is_returned(self): # Asset value is 100,000 but it was sold for 90,000, so there should be a loss of 10,000 loss_for_si = frappe.get_all( - "GL Entry", + "GL Entry", filters = { "voucher_no": si.name, "account": disposal_account @@ -1090,7 +1091,7 @@ def test_gle_made_when_asset_is_returned(self): )[0] loss_for_return_si = frappe.get_all( - "GL Entry", + "GL Entry", filters = { "voucher_no": return_si.name, "account": disposal_account @@ -1836,6 +1837,89 @@ def test_inter_company_transaction(self): self.assertEqual(target_doc.company, "_Test Company 1") self.assertEqual(target_doc.supplier, "_Test Internal Supplier") + def test_inter_company_transaction_without_default_warehouse(self): + "Check mapping (expense account) of inter company SI to PI in absence of default warehouse." + # setup + old_negative_stock = frappe.db.get_single_value("Stock Settings", "allow_negative_stock") + frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) + + old_perpetual_inventory = erpnext.is_perpetual_inventory_enabled('_Test Company 1') + frappe.local.enable_perpetual_inventory['_Test Company 1'] = 1 + + frappe.db.set_value("Company", '_Test Company 1', "stock_received_but_not_billed", "Stock Received But Not Billed - _TC1") + frappe.db.set_value("Company", '_Test Company 1', "expenses_included_in_valuation", "Expenses Included In Valuation - _TC1") + + + if not frappe.db.exists("Customer", "_Test Internal Customer"): + customer = frappe.get_doc({ + "customer_group": "_Test Customer Group", + "customer_name": "_Test Internal Customer", + "customer_type": "Individual", + "doctype": "Customer", + "territory": "_Test Territory", + "is_internal_customer": 1, + "represents_company": "_Test Company 1" + }) + + customer.append("companies", { + "company": "Wind Power LLC" + }) + + customer.insert() + + if not frappe.db.exists("Supplier", "_Test Internal Supplier"): + supplier = frappe.get_doc({ + "supplier_group": "_Test Supplier Group", + "supplier_name": "_Test Internal Supplier", + "doctype": "Supplier", + "is_internal_supplier": 1, + "represents_company": "Wind Power LLC" + }) + + supplier.append("companies", { + "company": "_Test Company 1" + }) + + supplier.insert() + + # begin test + si = create_sales_invoice( + company = "Wind Power LLC", + customer = "_Test Internal Customer", + debit_to = "Debtors - WP", + warehouse = "Stores - WP", + income_account = "Sales - WP", + expense_account = "Cost of Goods Sold - WP", + cost_center = "Main - WP", + currency = "USD", + update_stock = 1, + do_not_save = 1 + ) + si.selling_price_list = "_Test Price List Rest of the World" + si.submit() + + target_doc = make_inter_company_transaction("Sales Invoice", si.name) + + # in absence of warehouse Stock Received But Not Billed is set as expense account while mapping + # mapping is not obstructed + self.assertIsNone(target_doc.items[0].warehouse) + self.assertEqual(target_doc.items[0].expense_account, "Stock Received But Not Billed - _TC1") + + target_doc.items[0].update({"cost_center": "Main - _TC1"}) + + # missing warehouse is validated on save, after mapping + self.assertRaises(WarehouseMissingError, target_doc.save) + + target_doc.items[0].update({"warehouse": "Stores - _TC1"}) + target_doc.save() + + # after warehouse is set, linked account or default inventory account is set + self.assertEqual(target_doc.items[0].expense_account, 'Stock In Hand - _TC1') + + # tear down + frappe.local.enable_perpetual_inventory['_Test Company 1'] = old_perpetual_inventory + frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock) + def test_internal_transfer_gl_entry(self): ## Create internal transfer account account = create_account(account_name="Unrealized Profit", From 03a6c38f06034383b57593145f8c9e77c42d6370 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 26 Jul 2021 22:24:25 +0530 Subject: [PATCH 309/691] test: fix test due to rename change --- erpnext/selling/doctype/sales_order/test_sales_order.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 974648d6d443..1de1e0009752 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -673,6 +673,8 @@ def test_block_delivery_note_against_cancelled_sales_order(self): so.cancel() + dn.load_from_db() + self.assertRaises(frappe.CancelledLinkError, dn.submit) def test_service_type_product_bundle(self): 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 310/691] 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 dbf75140ac14..e9e6f81862c1 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 ae9d1d9617015b6e2714a9fcd027a2957053d99a Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Tue, 27 Jul 2021 09:40:12 +0530 Subject: [PATCH 311/691] fix: Salary component account filter (#26604) * 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 dbf75140ac14..e9e6f81862c1 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 312/691] 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 ece627c50de9..cbb3cc813b4d 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -24,7 +24,6 @@ def validate(self): 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 8f8dbb222459..95e2806aedcd 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -36,8 +36,8 @@ def validate(self): 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 @@ def set_status(self): 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 @@ def on_submit(self): 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 @@ def on_cancel(self): 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 34353df48c3bf58f61157ebb58accca886fc0b1a Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Tue, 27 Jul 2021 09:47:44 +0530 Subject: [PATCH 313/691] fix: sales pipeline graph issue (#26626) --- erpnext/selling/page/sales_funnel/sales_funnel.css | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.css b/erpnext/selling/page/sales_funnel/sales_funnel.css index 89e904fcfc9f..455d37cb2380 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.css +++ b/erpnext/selling/page/sales_funnel/sales_funnel.css @@ -1,3 +1,4 @@ .funnel-wrapper { margin: 15px; + width: 100%; } \ No newline at end of file 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 314/691] chore: code owners updated (#26659) --- CODEOWNERS | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 219b6bb7821a..a4a14de1b8e2 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 315/691] 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 f9160e281dac..331770fbe84c 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 + }, + { + "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 + }, { - "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": "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 b9ee4a0963fe..45c8e1b49fca 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 @@ 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 dd26be7c992b..2ba22ca43534 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 @@ def test_single_threshold_tds(self): 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 6b482ebb0f8b8ce6d7efe4704f853c4508954c45 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 23 Jul 2021 16:40:45 +0530 Subject: [PATCH 316/691] fix: serial no and batch validation --- erpnext/controllers/stock_controller.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 2526e6df0ef7..17bd7354f93a 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -53,12 +53,17 @@ def validate_serialized_batch(self): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos for d in self.get("items"): if hasattr(d, 'serial_no') and hasattr(d, 'batch_no') and d.serial_no and d.batch_no: - serial_nos = get_serial_nos(d.serial_no) - for serial_no_data in frappe.get_all("Serial No", - filters={"name": ("in", serial_nos)}, fields=["batch_no", "name"]): - if serial_no_data.batch_no != d.batch_no: + serial_nos = frappe.get_all("Serial No", + fields=["batch_no", "name", "warehouse"], + filters={ + "name": ("in", get_serial_nos(d.serial_no)) + } + ) + + for row in serial_nos: + if row.warehouse and row.batch_no != d.batch_no: frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}") - .format(d.idx, serial_no_data.name, d.batch_no)) + .format(d.idx, row.name, d.batch_no)) if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2: expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date") From dfdd1c6e02e74abeb3a9d67d59f33f1e0dbeab48 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 23 Jul 2021 15:50:37 +0530 Subject: [PATCH 317/691] feat: don't recompute taxes --- .../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 1b7a0fe562e8..cfdb167bbca2 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 56da5b71da0d..099c7d43463d 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -152,7 +152,7 @@ def initialize_taxes(self): 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 @@ def get_current_tax_amount(self, item, tax, item_tax_map): 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 @@ def set_rounded_total(self): 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 c8d7a8c781f6c448fd872427d611ffab70c136db Mon Sep 17 00:00:00 2001 From: Ankush Date: Tue, 27 Jul 2021 16:39:38 +0530 Subject: [PATCH 318/691] fix: reload manufacturing setting before patch (#26641) --- 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 48999e6f9931..d7ad1fc69625 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 f1c697ca758ea710b685f147cc0399c0d0d7e777 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 27 Jul 2021 16:49:04 +0530 Subject: [PATCH 319/691] fix: sider issue --- erpnext/crm/doctype/campaign/campaign.js | 7 +++---- erpnext/crm/doctype/campaign/campaign.py | 1 + erpnext/crm/doctype/campaign/test_campaign.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/crm/doctype/campaign/campaign.js b/erpnext/crm/doctype/campaign/campaign.js index 04876541ba2d..11bfa74b29cd 100644 --- a/erpnext/crm/doctype/campaign/campaign.js +++ b/erpnext/crm/doctype/campaign/campaign.js @@ -5,12 +5,11 @@ frappe.ui.form.on('Campaign', { refresh: function(frm) { erpnext.toggle_naming_series(); - if(frm.doc.__islocal) { + if (frm.doc.__islocal) { frm.toggle_display("naming_series", frappe.boot.sysdefaults.campaign_naming_by=="Naming Series"); - } - else { + } else { cur_frm.add_custom_button(__("View Leads"), function() { - frappe.route_options = {"source": "Campaign","campaign_name": frm.doc.name} + frappe.route_options = {"source": "Campaign", "campaign_name": frm.doc.name}; frappe.set_route("List", "Lead"); }, "fa fa-list", true); } diff --git a/erpnext/crm/doctype/campaign/campaign.py b/erpnext/crm/doctype/campaign/campaign.py index 34331952c0b2..e32799f34ebd 100644 --- a/erpnext/crm/doctype/campaign/campaign.py +++ b/erpnext/crm/doctype/campaign/campaign.py @@ -3,6 +3,7 @@ import frappe from frappe.model.document import Document +from frappe.model.naming import set_name_by_naming_series class Campaign(Document): def autoname(self): diff --git a/erpnext/crm/doctype/campaign/test_campaign.py b/erpnext/crm/doctype/campaign/test_campaign.py index 939bb8f46434..7124b8c7d606 100644 --- a/erpnext/crm/doctype/campaign/test_campaign.py +++ b/erpnext/crm/doctype/campaign/test_campaign.py @@ -1,7 +1,7 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -import frappe +# import frappe import unittest class TestCampaign(unittest.TestCase): From 7a97b6d6a831e5bf77f2b5d28264f998aa5a933f Mon Sep 17 00:00:00 2001 From: Ankush Date: Tue, 27 Jul 2021 16:39:38 +0530 Subject: [PATCH 320/691] fix: reload manufacturing setting before patch (#26641) (cherry picked from commit c8d7a8c781f6c448fd872427d611ffab70c136db) --- 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 48999e6f9931..d7ad1fc69625 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 b122a1eaa094c6e320dddd6c5b5f6716932f8028 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 27 Jul 2021 16:59:06 +0530 Subject: [PATCH 321/691] 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 48999e6f9931..d7ad1fc69625 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 322/691] 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 420bb0080396..69c7f5c614b8 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -192,11 +192,11 @@ def add_time_log(self, args): "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 940356d28a0073f139d3b0dbdc9b0b4dee974a16 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 27 Jul 2021 18:43:20 +0530 Subject: [PATCH 323/691] 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 420bb0080396..69c7f5c614b8 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -192,11 +192,11 @@ def add_time_log(self, args): "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 5a7fad8a6ab5ac16d6ad3ded583c092cc6f49c37 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 27 Jul 2021 10:59:25 +0530 Subject: [PATCH 324/691] 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 f9160e281dac..331770fbe84c 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 + }, + { + "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 + }, { - "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": "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 b9ee4a0963fe..45c8e1b49fca 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 @@ 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 dd26be7c992b..2ba22ca43534 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 @@ def test_single_threshold_tds(self): 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 56b81565fa3f2b1503c98678846372275635ce90 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 23 Jul 2021 15:19:53 +0530 Subject: [PATCH 325/691] 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 | 73 ++++++++++++++----- 4 files changed, 125 insertions(+), 21 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 b3e4286bccb5..4cd40bf38ec8 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 071fc86d9b33..a800bf870130 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 5f31d9caf0de..2e454a515965 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 c15d1eda7dcf..8f9ec465e5d4 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 - })) - - i = 0 + distinct_item_warehouses = get_distinct_item_warehouse(args, doc) + + 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 7903aeca7e8bd413994d13177b7905353f7d710f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 27 Jul 2021 18:43:20 +0530 Subject: [PATCH 326/691] 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 420bb0080396..69c7f5c614b8 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -192,11 +192,11 @@ def add_time_log(self, args): "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 5d121c41f33a0e108665457af2dd0acab9279aa2 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 21 Jul 2021 19:47:41 +0530 Subject: [PATCH 327/691] fix: removed Remarks column from AR/AP report --- .../report/accounts_receivable/accounts_receivable.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index a11b77a6f645..b54646fd27af 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -99,7 +99,6 @@ def init_voucher_balance(self): voucher_no = gle.voucher_no, party = gle.party, posting_date = gle.posting_date, - remarks = gle.remarks, account_currency = gle.account_currency, invoiced = 0.0, paid = 0.0, @@ -579,7 +578,7 @@ def get_gl_entries(self): self.gl_entries = frappe.db.sql(""" select name, posting_date, account, party_type, party, voucher_type, voucher_no, cost_center, - against_voucher_type, against_voucher, account_currency, remarks, {0} + against_voucher_type, against_voucher, account_currency, {0} from `tabGL Entry` where @@ -792,8 +791,6 @@ def get_columns(self): self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link', options='Supplier Group') - self.add_column(label=_('Remarks'), fieldname='remarks', fieldtype='Text', width=200) - def add_column(self, label, fieldname=None, fieldtype='Currency', options=None, width=120): if not fieldname: fieldname = scrub(label) if fieldtype=='Currency': options='currency' 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 328/691] 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 fa1dfed64354..41c51c36dcb7 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 3a39c0f19ae22632aa4010102b08933eea2de4ee Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 28 Jul 2021 00:19:32 +0530 Subject: [PATCH 329/691] fix: force reload of Opportunity in patch (#26668) (#26681) (cherry picked from commit ac2e139d5bdfe58fb03ee73ef88cf70d855b8caf) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- 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 fa1dfed64354..41c51c36dcb7 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 330/691] 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 45c8e1b49fca..020de3c3f341 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 441adf763f23385d1e8f6a2db092721956ea8187 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Jul 2021 10:43:02 +0530 Subject: [PATCH 331/691] 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 45c8e1b49fca..020de3c3f341 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 332/691] 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 3d048c36865b..8199b1040f0b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -845,6 +845,7 @@ def make_discount_gl_entries(self, gl_entries): 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 @@ def make_discount_gl_entries(self, gl_entries): 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 @@ def make_discount_gl_entries(self, gl_entries): 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 333/691] 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 331770fbe84c..153906ffe973 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 b891719b02d8..32763754d222 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 000000000000..16bf76d530cc --- /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 868a6cb26dc513708c36957d7d2537b3b0d14d4f Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 28 Jul 2021 13:42:13 +0530 Subject: [PATCH 334/691] fix: documentation link for E Invoicing (#26686) --- .../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 cc2d9f06d2d5..54e488610df8 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 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 335/691] 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 cc2d9f06d2d5..54e488610df8 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 42bc0a7db81fa493e33abae0f879e9786bcf3bb1 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Wed, 28 Jul 2021 13:42:53 +0530 Subject: [PATCH 336/691] fix: documentation link for E Invoicing (#26684) --- .../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 cc2d9f06d2d5..54e488610df8 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 655b5dc1906be3836e1a8e0e0b51958605e434ec Mon Sep 17 00:00:00 2001 From: walstanb Date: Wed, 28 Jul 2021 14:30:11 +0530 Subject: [PATCH 337/691] fix: date as reference_date from bank transactions --- erpnext/public/js/bank_reconciliation_tool/dialog_manager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js index 142fe79ccdce..239fbb92b11d 100644 --- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js @@ -16,7 +16,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { doctype: "Bank Transaction", filters: { name: this.bank_transaction_name }, fieldname: [ - "date", + "date as reference_date", "deposit", "withdrawal", "currency", From fa8e6ac7cd604fffaeecb4a4e73f4b08a20ad2b2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Jul 2021 15:30:05 +0530 Subject: [PATCH 338/691] 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 16bf76d530cc..3d149586a046 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 d95f16ac8fb084e33ab936545fc60acd6a4ff618 Mon Sep 17 00:00:00 2001 From: Ankush Date: Wed, 28 Jul 2021 16:38:59 +0530 Subject: [PATCH 339/691] fix(bom): remove manual permission checking (#26689) get_list does the permission checking. --- 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 bc092ef14f99..c68198b0e2d0 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 8ed7a21cd515ef6e9e109a62195088b13d0a06af Mon Sep 17 00:00:00 2001 From: Ankush Date: Wed, 28 Jul 2021 16:38:59 +0530 Subject: [PATCH 340/691] fix(bom): remove manual permission checking (#26689) get_list does the permission checking. (cherry picked from commit d95f16ac8fb084e33ab936545fc60acd6a4ff618) --- 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 af081c449c62..ebd9ae2dc54a 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 8c7d9efa9d93ee42306ada4de58bfcb560b77038 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Jul 2021 12:57:59 +0530 Subject: [PATCH 341/691] 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 331770fbe84c..153906ffe973 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 b891719b02d8..32763754d222 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 000000000000..16bf76d530cc --- /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 6ac68f3bc74843e7f703a86063238221534267fd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Jul 2021 15:30:05 +0530 Subject: [PATCH 342/691] 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 16bf76d530cc..3d149586a046 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 49cfac0ef0eb2333e5ff6291a60550d26faf3930 Mon Sep 17 00:00:00 2001 From: Anupam Date: Wed, 28 Jul 2021 17:51:35 +0530 Subject: [PATCH 343/691] feat: added basic info. of lead in header part --- erpnext/crm/doctype/lead/lead.js | 40 ++++++++++++++++++++++++++++++ erpnext/crm/doctype/lead/lead.json | 2 +- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 815bb41a76ef..bd1639bb12fd 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -42,6 +42,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller if (!this.frm.is_new()) { frappe.contacts.render_address_and_contact(this.frm); + cur_frm.trigger('render_basic_info_html'); } else { frappe.contacts.clear_address_and_contact(this.frm); } @@ -81,6 +82,45 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller this.frm.set_value("ends_on", d.format(frappe.defaultDatetimeFormat)); } } + + render_basic_info_html() { + let html=''; + if (cur_frm.doc.lead_owner){ + html += `
                                + Lead Owner +
                                +
                                + ${cur_frm.doc.lead_owner} +
                                ` ; + } + + if (cur_frm.doc.email_id){ + html += `
                                + Email +
                                +
                                + ${cur_frm.doc.email_id} +
                                ` ; + } + + if (cur_frm.doc.mobile_no){ + html += `
                                + Mobile +
                                +
                                + ${cur_frm.doc.mobile_no} +
                                ` ; + } + + html += `
                                + Status +
                                +
                                + ${cur_frm.doc.status} +
                                ` ; + html = `
                                ${html}
                                `; + cur_frm.dashboard.set_headline_alert(html); + } }; extend_cscript(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm })); diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index ed33d896f8a9..dc030fe81979 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -486,7 +486,7 @@ "idx": 5, "image_field": "image", "links": [], - "modified": "2021-06-17 00:20:37.768449", + "modified": "2021-07-28 00:20:37.768449", "modified_by": "Administrator", "module": "CRM", "name": "Lead", From 1b6dd84c0a25e143ec3ac92b3f439d6d90ca6573 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 28 Jul 2021 18:11:11 +0530 Subject: [PATCH 344/691] 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 af081c449c62..ebd9ae2dc54a 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 47a651a80fe5be8ec9100b68f33097f2e349a086 Mon Sep 17 00:00:00 2001 From: Anupam Date: Wed, 28 Jul 2021 18:21:19 +0530 Subject: [PATCH 345/691] fix: removing depends_on for contact fields --- erpnext/crm/doctype/lead/lead.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index dc030fe81979..f9a500fa974a 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -145,7 +145,6 @@ "search_index": 1 }, { - "depends_on": "eval: doc.__islocal", "fieldname": "salutation", "fieldtype": "Link", "label": "Salutation", @@ -283,7 +282,6 @@ "options": "Country" }, { - "collapsible_depends_on": "eval: doc.__islocal", "fieldname": "pincode", "fieldtype": "Data", "label": "Postal Code" @@ -299,7 +297,6 @@ "read_only": 1 }, { - "depends_on": "eval: doc.__islocal", "fieldname": "phone", "fieldtype": "Data", "label": "Phone", @@ -308,7 +305,6 @@ "options": "Phone" }, { - "depends_on": "eval: doc.__islocal", "fieldname": "mobile_no", "fieldtype": "Data", "label": "Mobile No.", @@ -317,7 +313,6 @@ "options": "Phone" }, { - "depends_on": "eval: doc.__islocal", "fieldname": "fax", "fieldtype": "Data", "label": "Fax", From b1350af1f64868244ec7dc9838fb81fa4bbec1b4 Mon Sep 17 00:00:00 2001 From: Anupam Date: Wed, 28 Jul 2021 18:51:19 +0530 Subject: [PATCH 346/691] 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 4edf9485dc15..4833d93c4a78 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 5dcd5e48e7f84ed9cdcfb2a01d57a7ad5b4b7e76 Mon Sep 17 00:00:00 2001 From: Anuja Date: Thu, 29 Jul 2021 00:54:48 +0530 Subject: [PATCH 347/691] fix: fixed zero tax rate issue by adding custom field --- .../vat_audit_report/vat_audit_report.py | 57 ++++++++++--------- erpnext/regional/south_africa/setup.py | 17 ++++++ 2 files changed, 47 insertions(+), 27 deletions(-) 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 feb2a1620d49..bce8586720d5 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -20,10 +20,10 @@ def __init__(self, filters=None): def run(self): self.get_sa_vat_accounts() + self.get_columns() for doctype in self.doctypes: - self.get_columns(doctype) self.select_columns = """ - name as invoice_number, + name as voucher_no, posting_date, remarks""" columns = ", supplier as party, credit_to as account" if doctype=="Purchase Invoice" \ else ", customer as party, debit_to as account" @@ -44,7 +44,7 @@ def get_sa_vat_accounts(self): if not self.sa_vat_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate: frappe.throw(_("Please set VAT Accounts in South Africa VAT Settings")) - def get_invoice_data(self,doctype): + def get_invoice_data(self, doctype): conditions = self.get_conditions() self.invoices = frappe._dict() @@ -62,9 +62,9 @@ def get_invoice_data(self,doctype): where_conditions=conditions), self.filters, as_dict=1) for d in invoice_data: - self.invoices.setdefault(d.invoice_number, d) + self.invoices.setdefault(d.voucher_no, d) - def get_invoice_items(self,doctype): + def get_invoice_items(self, doctype): self.invoice_items = frappe._dict() self.item_tax_rate = frappe._dict() @@ -82,7 +82,7 @@ def get_invoice_items(self,doctype): 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)) - def get_items_based_on_tax_rate(self,doctype): + def get_items_based_on_tax_rate(self, doctype): self.items_based_on_tax_rate = frappe._dict() self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \ else "Sales Taxes and Charges" @@ -107,6 +107,9 @@ def get_items_based_on_tax_rate(self,doctype): else: continue for item_code, taxes in item_wise_tax_detail.items(): + is_zero_rated = frappe.get_value("Item", item_code, "is_zero_rated") + if taxes[0] == 0 and not is_zero_rated: + continue tax_rate, item_amount_map = self.get_item_amount_map(parent, item_code, taxes) if tax_rate is not None: @@ -145,12 +148,12 @@ def get_conditions(self): return conditions def get_data(self, doctype): - consolidated_data = self.get_consolidated_data() + consolidated_data = self.get_consolidated_data(doctype) section_name = _("Purchases") if doctype == "Purchase Invoice" else _("Sales") for rate, section in consolidated_data.items(): rate = int(rate) - label = frappe.bold(_("Standard Rate") + " " + section_name + " " + str(rate) + "%") + label = frappe.bold(section_name + "- " + "Rate" + " " + str(rate) + "%") section_head = {"posting_date": label} total_gross = total_tax = total_net = 0 self.data.append(section_head) @@ -170,7 +173,7 @@ def get_data(self, doctype): self.data.append(total) self.data.append({}) - def get_consolidated_data(self): + def get_consolidated_data(self, doctype): consolidated_data_map={} for inv, inv_data in self.invoices.items(): if self.items_based_on_tax_rate.get(inv): @@ -181,8 +184,8 @@ def get_consolidated_data(self): item_details = self.item_tax_rate.get(inv).get(item) row["account"] = inv_data.get("account") row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') - row["invoice_number"] = inv - row["party"] = inv_data.get("party") + row["voucher_type"] = doctype + row["voucher_no"] = inv row["remarks"] = inv_data.get("remarks") row["gross_amount"]= item_details[0].get("gross_amount") row["tax_amount"]= item_details[0].get("tax_amount") @@ -191,7 +194,7 @@ def get_consolidated_data(self): return consolidated_data_map - def get_columns(self,doctype): + def get_columns(self): self.columns = [ { "fieldname": "posting_date", @@ -204,44 +207,44 @@ def get_columns(self,doctype): "label": "Account", "fieldtype": "Link", "options": "Account", - "width": 140 + "width": 150 }, { - "fieldname": "invoice_number", - "label": "Reference", - "fieldtype": "Link", - "options": doctype, - "width": 140 + "fieldname": "voucher_type", + "label": "Voucher Type", + "fieldtype": "Data", + "width": 140, + "hidden": 1 }, { - "fieldname": "party", - "label": "Party", - "fieldtype": "Link", - "options": "Supplier" if doctype == "Purchase Invoice" else "Customer", - "width": 140 + "fieldname": "voucher_no", + "label": "Reference", + "fieldtype": "Dynamic Link", + "options": "voucher_type", + "width": 150 }, { "fieldname": "remarks", "label": "Details", "fieldtype": "Data", - "width": 140 + "width": 150 }, { "fieldname": "net_amount", "label": "Net Amount", "fieldtype": "Currency", - "width": 140 + "width": 150 }, { "fieldname": "tax_amount", "label": "Tax Amount", "fieldtype": "Currency", - "width": 140 + "width": 150 }, { "fieldname": "gross_amount", "label": "Gross Amount", "fieldtype": "Currency", - "width": 140 + "width": 150 }, ] diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py index a4335bddef2e..ac783b848848 100644 --- a/erpnext/regional/south_africa/setup.py +++ b/erpnext/regional/south_africa/setup.py @@ -4,11 +4,28 @@ from __future__ import unicode_literals # import frappe, os, json +from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.permissions import add_permission, update_permission_property def setup(company=None, patch=True): add_permissions() +def make_custom_fields(update=True): + is_zero_rated = dict(fieldname='is_zero_rated', label='Is Zero Rated', + fieldtype='Check', fetch_from='item_code.is_zero_rated', + insert_after='description', print_hide=1) + custom_fields = { + 'Item': [ + dict(fieldname='is_zero_rated', label='Is Zero Rated', + fieldtype='Check', insert_after='item_group', + print_hide=1) + ], + 'Sales Invoice Item': is_zero_rated, + 'Purchase Invoice Item': is_zero_rated + } + + create_custom_fields(custom_fields, update=update) + def add_permissions(): """Add Permissions for South Africa VAT Settings and South Africa VAT Account""" for doctype in ('South Africa VAT Settings', 'South Africa VAT Account'): From a6ce1244a07279d601702ece31243d9d6e857557 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 29 Jul 2021 10:43:21 +0530 Subject: [PATCH 348/691] fix: sider issues --- erpnext/crm/doctype/lead/lead.js | 6 +++--- erpnext/crm/doctype/lead/lead.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index bd1639bb12fd..ad24a6e2225d 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -85,7 +85,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller render_basic_info_html() { let html=''; - if (cur_frm.doc.lead_owner){ + if (cur_frm.doc.lead_owner) { html += `
                                Lead Owner
                                @@ -94,7 +94,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller
                                ` ; } - if (cur_frm.doc.email_id){ + if (cur_frm.doc.email_id) { html += `
                                Email
                                @@ -103,7 +103,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller
                                ` ; } - if (cur_frm.doc.mobile_no){ + if (cur_frm.doc.mobile_no) { html += `
                                Mobile
                                diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index ce36069052e5..f09a8145401c 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -95,7 +95,7 @@ def unlink_dynamic_links(self): if d.link_doctype == self.doctype and d.link_name == self.name: to_remove = d if to_remove: - link_doctype.remove(to_remove) + linked_doc.remove(to_remove) def has_customer(self): return frappe.db.get_value("Customer", {"lead_name": self.name}) From e5fea372afb1c1295811ff18a570c9acf1506c50 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 29 Jul 2021 11:05:38 +0530 Subject: [PATCH 349/691] 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 84e2d0d52a25..2e415af28287 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 350/691] 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 1b7a0fe562e8..cfdb167bbca2 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 56da5b71da0d..099c7d43463d 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -152,7 +152,7 @@ def initialize_taxes(self): 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 @@ def get_current_tax_amount(self, item, tax, item_tax_map): 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 @@ def set_rounded_total(self): 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 f8343890b9e065ced4ba87a065624d01f388b86e Mon Sep 17 00:00:00 2001 From: Ankush Date: Thu, 29 Jul 2021 11:09:34 +0530 Subject: [PATCH 351/691] feat: don't recompute taxes (#26695) --- .../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 1b7a0fe562e8..cfdb167bbca2 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 56da5b71da0d..099c7d43463d 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -152,7 +152,7 @@ def initialize_taxes(self): 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 @@ def get_current_tax_amount(self, item, tax, item_tax_map): 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 @@ def set_rounded_total(self): 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 e906acdc49a5131680301eb056f16c7821b0b539 Mon Sep 17 00:00:00 2001 From: Ankush Date: Thu, 29 Jul 2021 13:56:21 +0530 Subject: [PATCH 352/691] chore: change location of backport action (#26705) --- .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 cc98f4544f8d..1d180f251e1c 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 6a71955b998f30e3477b9847cd8fec4b806b69bf Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 29 Jul 2021 14:28:13 +0530 Subject: [PATCH 353/691] 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 cc98f4544f8d..1d180f251e1c 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 354/691] 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 020de3c3f341..481ef285e720 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 c3e739caf81c2eb63f3c17c045c6ec4ccf2f1f4a Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 29 Jul 2021 15:34:18 +0530 Subject: [PATCH 355/691] fix: correct field for GLE against account in PR --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 41800e37151a..48da78516efc 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -436,7 +436,7 @@ def add_gl_entry(self, gl_entries, account, cost_center, debit, credit, remarks, "cost_center": cost_center, "debit": debit, "credit": credit, - "against_account": against_account, + "against": against_account, "remarks": remarks, } From 5764b49767f7aa02699c407f9d00c01987e434f2 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 29 Jul 2021 15:41:22 +0530 Subject: [PATCH 356/691] fix: remove incorrect field check from reposting --- erpnext/accounts/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 1cdbd8d38a61..9afe365f7476 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))): From e39bbc85e11d848a2dd248c546b723f914d3bede Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 29 Jul 2021 15:46:25 +0530 Subject: [PATCH 357/691] 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 d3ac3a667604..439b1edbce62 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 909995a441c3fe9d1c757bede91a66c021f1e451 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Thu, 29 Jul 2021 15:58:27 +0530 Subject: [PATCH 358/691] fix: issue with cache.setex --- erpnext/setup/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py index 33af28687c61..27237bf2cbef 100644 --- a/erpnext/setup/utils.py +++ b/erpnext/setup/utils.py @@ -107,7 +107,7 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No # expire in 6 hours response.raise_for_status() value = response.json()["result"] - cache.setex(key, value, 6 * 60 * 60) + cache.setex(name=key, time=21600, value=flt(value)) return flt(value) except: frappe.log_error(title="Get Exchange Rate") From 0bb60b37df4517c81cf684042bc8d42ea2a67364 Mon Sep 17 00:00:00 2001 From: Ankush Date: Thu, 29 Jul 2021 17:15:12 +0530 Subject: [PATCH 359/691] chore: add timeout to GHA workflows (#26714) --- .github/workflows/backport.yml | 1 + .github/workflows/docs-checker.yml | 1 + .github/workflows/patch.yml | 1 + .github/workflows/server-tests.yml | 1 + .github/workflows/ui-tests.yml | 1 + 5 files changed, 5 insertions(+) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index 1d180f251e1c..bd622275d6d6 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -8,6 +8,7 @@ on: jobs: main: runs-on: ubuntu-latest + timeout-minutes: 60 steps: - name: Checkout Actions uses: actions/checkout@v2 diff --git a/.github/workflows/docs-checker.yml b/.github/workflows/docs-checker.yml index cdf676dd6747..db46c5621b2b 100644 --- a/.github/workflows/docs-checker.yml +++ b/.github/workflows/docs-checker.yml @@ -6,6 +6,7 @@ on: jobs: build: runs-on: ubuntu-latest + timeout-minutes: 10 steps: - name: 'Setup Environment' diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index b96a3d6bbeda..dc72987a41a6 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -5,6 +5,7 @@ on: [pull_request, workflow_dispatch] jobs: test: runs-on: ubuntu-18.04 + timeout-minutes: 60 name: Patch Test diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml index 69afa15187df..606002e3cdf4 100644 --- a/.github/workflows/server-tests.yml +++ b/.github/workflows/server-tests.yml @@ -9,6 +9,7 @@ on: jobs: test: runs-on: ubuntu-18.04 + timeout-minutes: 60 strategy: fail-fast: false diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 412a05b0a153..9e29b6f1d2a9 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -7,6 +7,7 @@ on: jobs: test: runs-on: ubuntu-18.04 + timeout-minutes: 60 strategy: fail-fast: false From 379ce70126205d3e489680549aec51cc6722aa6a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 29 Jul 2021 17:02:06 +0530 Subject: [PATCH 360/691] 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 14d543b17403..bfc4471b9afc 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 e99f68fd7ed9e9acacac86775ac6f8ed9030d459 Mon Sep 17 00:00:00 2001 From: Jannat Patel <31363128+pateljannat@users.noreply.github.com> Date: Thu, 29 Jul 2021 18:31:38 +0530 Subject: [PATCH 361/691] fix: expense claim semgrep issue (#26709) * fix: expense claim semgrep issue * fix: sider --- .../employee_advance/employee_advance.py | 7 +++--- .../hr/doctype/expense_claim/expense_claim.py | 23 +++++++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index cb72f6b6d96e..08e0b24a2023 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -22,7 +22,6 @@ def validate(self): def on_cancel(self): self.ignore_linked_doctypes = ('GL Entry') - self.set_status() def set_status(self): if self.docstatus == 0: @@ -183,9 +182,9 @@ def make_return_entry(employee, company, employee_advance_name, return_amount, bank_cash_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment) if not bank_cash_account: frappe.throw(_("Please set a Default Cash Account in Company defaults")) - + advance_account_currency = frappe.db.get_value('Account', advance_account, 'account_currency') - + je = frappe.new_doc('Journal Entry') je.posting_date = nowdate() je.voucher_type = get_voucher_type(mode_of_payment) @@ -229,4 +228,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 5010fc3f75c7..8cef14383532 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -35,8 +35,8 @@ def validate(self): 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" @@ -44,14 +44,18 @@ def set_status(self): 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) @@ -74,7 +78,7 @@ def on_submit(self): 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): @@ -86,7 +90,6 @@ def on_cancel(self): 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 19a6d809272ab4efeb6ef57d45d47b71f6d8fec6 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 29 Jul 2021 18:47:16 +0530 Subject: [PATCH 362/691] 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 b54d0e73a8c2..94abf3b3c06f 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 c1c5a4ae17aaba4781c4a0da29de5083c9d710d1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 29 Jul 2021 17:02:06 +0530 Subject: [PATCH 363/691] 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 14d543b17403..bfc4471b9afc 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 a6d276a06fe02fa4a16bdbad2ce31432701a9f3f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 29 Jul 2021 17:02:06 +0530 Subject: [PATCH 364/691] 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 14d543b17403..bfc4471b9afc 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 c7c90244feb2381d343711e3d1d322b5e56ccecc Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 29 Jul 2021 19:18:35 +0530 Subject: [PATCH 365/691] 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 2bf5c77e61ce..913e70b30c56 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1109,7 +1109,7 @@ def set_payment_schedule(self): 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 @@ def get_order_details(self): 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 @@ def all_items_have_same_po_or_so(self, po_or_so, fieldname): 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 1011c1b01acf5f606de2f9936bf9ea7411f1f53e Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 9 Jul 2021 01:44:34 +0530 Subject: [PATCH 366/691] 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 f7992797ed43..147785a08067 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1142,6 +1142,79 @@ def set_status(self, update=False, status=None, update_modified=True): 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 367/691] 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 147785a08067..28a9bddc420a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1142,49 +1142,6 @@ def set_status(self, update=False, status=None, update_modified=True): 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 eaa502ff7f02..a0bac51046fd 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 update_item(obj, target, source_parent): }, } - 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 cdd865ac4ac9..f7ea77bae44f 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 41f57a34d3d2..a58c381df352 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -621,6 +621,8 @@ def update_item(source, target, source_parent): @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 update_item(source, target, source_parent): } }, 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 4808e948fc6f..1628f9301917 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 get_pending_qty(item_row): } }, 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 82c87a83a50e..cf6cac273f02 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 get_pending_qty(item_row): } }, 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 6333c3bee5b4aaa95bc289519e8341361af19ff6 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Sat, 17 Jul 2021 22:55:24 +0530 Subject: [PATCH 368/691] fix: Remove unused imports From def7cc6cb3584ce32e8351e8b7faeb49a955a052 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 22 Jul 2021 05:57:42 +0530 Subject: [PATCH 369/691] 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 a0bac51046fd..f68d81909a3e 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 update_item(obj, target, source_parent): 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 f7ea77bae44f..2bf5c77e61ce 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1105,7 +1105,14 @@ def set_payment_schedule(self): 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 @@ def set_payment_schedule(self): 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 a58c381df352..2b9d516e217b 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -621,8 +621,6 @@ def update_item(source, target, source_parent): @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 update_item(source, target, source_parent): 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 1628f9301917..f99a01b8202b 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 get_pending_qty(item_row): 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 cf6cac273f02..b05cc7875cf3 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 get_pending_qty(item_row): 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 370/691] 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 974648d6d443..bf6925473ad9 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 @@ def test_so_cancellation_when_si_drafted(self): 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 371/691] 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 f981aeb13bba..8b1245eb4034 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.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 @@ def test_delivery_note_bundle_with_batched_item(self): 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 372/691] 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 8563b97ab74e..11cf39e5e2d4 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 @@ def test_po_optional_blanket_order(self): # 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 373/691] 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 2eb8bfd5d2f1..44e3e1e70e04 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 @@ def test_service_item_purchase_with_perpetual_inventory(self): 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 374/691] 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 11cf39e5e2d4..474c9cf3df97 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 @@ def test_po_optional_blanket_order(self): # 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 bf6925473ad9..3fbe0afd10e8 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 @@ def test_so_cancellation_when_si_drafted(self): 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 8b1245eb4034..ca8d8b99d31f 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 @@ def test_delivery_note_bundle_with_batched_item(self): 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 44e3e1e70e04..9a5064fcc713 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 @@ def test_purchase_receipt_with_exchange_rate_difference(self): 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 375/691] 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 3fbe0afd10e8..f4a089bcef2d 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 @@ def test_payment_terms_are_fetched_when_creating_sales_invoice(self): 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 376/691] 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 2bf5c77e61ce..913e70b30c56 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1109,7 +1109,7 @@ def set_payment_schedule(self): 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 @@ def get_order_details(self): 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 @@ def all_items_have_same_po_or_so(self, po_or_so, fieldname): 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 533ee9a401d45aa265445bffd1b5d42ab7992500 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 29 Jul 2021 19:39:32 +0530 Subject: [PATCH 377/691] feat: (Stock Reco) Ignore Empty Stock while fetching items from warehouse - Added checkbox to `Fetch Items from Warehouse` dialog to ignore empty stock - fix: Items fetched twice due to Item Defaults - Improved code readability --- .../stock_reconciliation.js | 72 +++++++++++-------- .../stock_reconciliation.py | 65 ++++++++++++----- 2 files changed, 89 insertions(+), 48 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index 454095448942..84f65a077e0c 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -56,25 +56,40 @@ frappe.ui.form.on("Stock Reconciliation", { }, get_items: function(frm) { - let fields = [{ - label: 'Warehouse', fieldname: 'warehouse', fieldtype: 'Link', options: 'Warehouse', reqd: 1, - "get_query": function() { - return { - "filters": { - "company": frm.doc.company, - } - }; - } - }, { - label: "Item Code", fieldname: "item_code", fieldtype: "Link", options: "Item", - "get_query": function() { - return { - "filters": { - "disabled": 0, - } - }; + let fields = [ + { + label: 'Warehouse', + fieldname: 'warehouse', + fieldtype: 'Link', + options: 'Warehouse', + reqd: 1, + "get_query": function() { + return { + "filters": { + "company": frm.doc.company, + } + }; + } + }, + { + label: "Item Code", + fieldname: "item_code", + fieldtype: "Link", + options: "Item", + "get_query": function() { + return { + "filters": { + "disabled": 0, + } + }; + } + }, + { + label: __("Ignore Empty Stock"), + fieldname: "ignore_empty_stock", + fieldtype: "Check" } - }]; + ]; frappe.prompt(fields, function(data) { frappe.call({ @@ -84,22 +99,21 @@ frappe.ui.form.on("Stock Reconciliation", { posting_date: frm.doc.posting_date, posting_time: frm.doc.posting_time, company: frm.doc.company, - item_code: data.item_code + item_code: data.item_code, + ignore_empty_stock: data.ignore_empty_stock }, callback: function(r) { + if (r.exc || !r.message || !r.message.length) return; + frm.clear_table("items"); - for (var i=0; i { + let item = frm.add_child("items"); + $.extend(item, row); - if (!d.valuation_rate) { - d.valuation_rate = 0; - } - } + item.qty = item.qty || 0; + item.valuation_rate = item.valuation_rate || 0; + }); frm.refresh_field("items"); } }); diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 987549159391..0bae7cfe259c 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -483,7 +483,8 @@ def cancel(self): self._cancel() @frappe.whitelist() -def get_items(warehouse, posting_date, posting_time, company, item_code=None): +def get_items(warehouse, posting_date, posting_time, company, item_code=None, ignore_empty_stock=False): + ignore_empty_stock = cint(ignore_empty_stock) items = [frappe._dict({ 'item_code': item_code, 'warehouse': warehouse @@ -497,18 +498,24 @@ def get_items(warehouse, posting_date, posting_time, company, item_code=None): for d in items: if d.item_code in itemwise_batch_data: - stock_bal = get_stock_balance(d.item_code, d.warehouse, - posting_date, posting_time, with_valuation_rate=True) + valuation_rate = get_stock_balance(d.item_code, d.warehouse, + posting_date, posting_time, with_valuation_rate=True)[1] for row in itemwise_batch_data.get(d.item_code): - args = get_item_data(row, row.qty, stock_bal[1]) + if ignore_empty_stock and not row.qty: + continue + + args = get_item_data(row, row.qty, valuation_rate) res.append(args) else: stock_bal = get_stock_balance(d.item_code, d.warehouse, posting_date, posting_time, with_valuation_rate=True , with_serial_no=cint(d.has_serial_no)) + qty, valuation_rate, serial_no = stock_bal[0], stock_bal[1], stock_bal[2] if cint(d.has_serial_no) else '' + + if ignore_empty_stock and not stock_bal[0]: + continue - args = get_item_data(d, stock_bal[0], stock_bal[1], - stock_bal[2] if cint(d.has_serial_no) else '') + args = get_item_data(d, qty, valuation_rate, serial_no) res.append(args) @@ -516,24 +523,44 @@ def get_items(warehouse, posting_date, posting_time, company, item_code=None): def get_items_for_stock_reco(warehouse, company): lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) - items = frappe.db.sql(""" - select i.name as item_code, i.item_name, bin.warehouse as warehouse, i.has_serial_no, i.has_batch_no - from tabBin bin, tabItem i - where i.name=bin.item_code and IFNULL(i.disabled, 0) = 0 and i.is_stock_item = 1 - and i.has_variants = 0 and exists( - select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=bin.warehouse - ) - """, (lft, rgt), as_dict=1) + items = frappe.db.sql(f""" + select + i.name as item_code, i.item_name, bin.warehouse as warehouse, i.has_serial_no, i.has_batch_no + from + tabBin bin, tabItem i + where + i.name = bin.item_code + and IFNULL(i.disabled, 0) = 0 + and i.is_stock_item = 1 + and i.has_variants = 0 + and exists( + select name from `tabWarehouse` where lft >= {lft} and rgt <= {rgt} and name = bin.warehouse + ) + """, as_dict=1) items += frappe.db.sql(""" - select i.name as item_code, i.item_name, id.default_warehouse as warehouse, i.has_serial_no, i.has_batch_no - from tabItem i, `tabItem Default` id - where i.name = id.parent - and exists(select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=id.default_warehouse) - and i.is_stock_item = 1 and i.has_variants = 0 and IFNULL(i.disabled, 0) = 0 and id.company=%s + select + i.name as item_code, i.item_name, id.default_warehouse as warehouse, i.has_serial_no, i.has_batch_no + from + tabItem i, `tabItem Default` id + where + i.name = id.parent + and exists( + select name from `tabWarehouse` where lft >= %s and rgt <= %s and name=id.default_warehouse + ) + and i.is_stock_item = 1 + and i.has_variants = 0 + and IFNULL(i.disabled, 0) = 0 + and id.company = %s group by i.name """, (lft, rgt, company), as_dict=1) + # remove duplicates + # check if item-warehouse key extracted from each entry exists in set iw_keys + # and update iw_keys + iw_keys = set() + items = [item for item in items if [(item.item_code, item.warehouse) not in iw_keys, iw_keys.add((item.item_code, item.warehouse))][0]] + return items def get_item_data(row, qty, valuation_rate, serial_no=None): From c7df7593248be42c1a0a3bd15a1905d8864cc1b5 Mon Sep 17 00:00:00 2001 From: Ankush Date: Thu, 29 Jul 2021 19:49:12 +0530 Subject: [PATCH 378/691] 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 1cdbd8d38a61..9afe365f7476 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 82c87a83a50e..26ea11e01d94 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -415,7 +415,7 @@ def add_gl_entry(self, gl_entries, account, cost_center, debit, credit, remarks, "cost_center": cost_center, "debit": debit, "credit": credit, - "against_account": against_account, + "against": against_account, "remarks": remarks, } From 57cd273f7c20510985162cde175ae4aa424f56b0 Mon Sep 17 00:00:00 2001 From: Ankush Date: Thu, 29 Jul 2021 19:49:36 +0530 Subject: [PATCH 379/691] fix: empty "against account" in Purchase Receipt GLE bp #26712 (#26719) * 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 1cdbd8d38a61..9afe365f7476 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 82c87a83a50e..26ea11e01d94 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -415,7 +415,7 @@ def add_gl_entry(self, gl_entries, account, cost_center, debit, credit, remarks, "cost_center": cost_center, "debit": debit, "credit": credit, - "against_account": against_account, + "against": against_account, "remarks": remarks, } From 9c7a9f3a1374bbba0693295f444db3158c06e6bd Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 29 Jul 2021 18:47:16 +0530 Subject: [PATCH 380/691] 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 b54d0e73a8c2..94abf3b3c06f 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 821db5cce7052c663717132a0ab6149adcdd67c2 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 30 Jul 2021 10:21:42 +0530 Subject: [PATCH 381/691] fix: employee status server-side validation (#26442) * fix: employee status server-side validation * fix: test * test: employee inactive status * fix: test --- erpnext/hr/doctype/appraisal/appraisal.py | 3 +- erpnext/hr/doctype/attendance/attendance.py | 2 ++ .../attendance_request/attendance_request.py | 3 +- .../compensatory_leave_request.py | 3 +- erpnext/hr/doctype/employee/employee.py | 8 +++-- erpnext/hr/doctype/employee/test_employee.py | 30 +++++++++++++++++-- .../employee_advance/employee_advance.py | 2 ++ .../employee_checkin/employee_checkin.py | 4 ++- .../employee_promotion/employee_promotion.py | 5 ++-- .../employee_referral/employee_referral.py | 2 ++ .../employee_transfer/employee_transfer.py | 4 --- .../hr/doctype/expense_claim/expense_claim.py | 3 +- .../leave_application/leave_application.py | 3 +- .../leave_encashment/leave_encashment.py | 3 +- .../shift_assignment/shift_assignment.py | 2 ++ .../hr/doctype/shift_request/shift_request.py | 3 +- .../doctype/travel_request/travel_request.py | 4 ++- erpnext/hr/utils.py | 10 +++++-- .../additional_salary/additional_salary.py | 2 ++ .../employee_benefit_application.py | 3 +- .../employee_benefit_claim.py | 3 +- .../employee_incentive/employee_incentive.py | 2 ++ .../employee_tax_exemption_declaration.py | 3 +- ...employee_tax_exemption_proof_submission.py | 3 +- .../retention_bonus/retention_bonus.py | 5 ++-- .../doctype/salary_slip/salary_slip.py | 2 ++ .../projects/doctype/timesheet/timesheet.py | 3 ++ 27 files changed, 88 insertions(+), 32 deletions(-) diff --git a/erpnext/hr/doctype/appraisal/appraisal.py b/erpnext/hr/doctype/appraisal/appraisal.py index f7601870fac3..c2ed4579844e 100644 --- a/erpnext/hr/doctype/appraisal/appraisal.py +++ b/erpnext/hr/doctype/appraisal/appraisal.py @@ -9,7 +9,7 @@ from frappe import _ from frappe.model.mapper import get_mapped_doc from frappe.model.document import Document -from erpnext.hr.utils import set_employee_name +from erpnext.hr.utils import set_employee_name, validate_active_employee class Appraisal(Document): def validate(self): @@ -19,6 +19,7 @@ def validate(self): if not self.goals: frappe.throw(_("Goals cannot be empty")) + validate_active_employee(self.employee) set_employee_name(self) self.validate_dates() self.validate_existing_appraisal() diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index 3412675d8119..f79f0fe4180f 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -8,11 +8,13 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import cstr, get_datetime, formatdate +from erpnext.hr.utils import validate_active_employee class Attendance(Document): def validate(self): from erpnext.controllers.status_updater import validate_status validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"]) + validate_active_employee(self.employee) self.validate_attendance_date() self.validate_duplicate_record() self.validate_employee_status() diff --git a/erpnext/hr/doctype/attendance_request/attendance_request.py b/erpnext/hr/doctype/attendance_request/attendance_request.py index 090d53262cf5..7f88fed73a2e 100644 --- a/erpnext/hr/doctype/attendance_request/attendance_request.py +++ b/erpnext/hr/doctype/attendance_request/attendance_request.py @@ -8,10 +8,11 @@ from frappe.model.document import Document from frappe.utils import date_diff, add_days, getdate from erpnext.hr.doctype.employee.employee import is_holiday -from erpnext.hr.utils import validate_dates +from erpnext.hr.utils import validate_dates, validate_active_employee class AttendanceRequest(Document): def validate(self): + validate_active_employee(self.employee) validate_dates(self, self.from_date, self.to_date) if self.half_day: if not getdate(self.from_date)<=getdate(self.half_day_date)<=getdate(self.to_date): diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py index a6fe429be178..0d7fded921b4 100644 --- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py +++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py @@ -7,12 +7,13 @@ from frappe import _ from frappe.utils import date_diff, add_days, getdate, cint, format_date from frappe.model.document import Document -from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, \ +from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, validate_active_employee, \ get_holidays_for_employee, create_additional_leave_ledger_entry class CompensatoryLeaveRequest(Document): def validate(self): + validate_active_employee(self.employee) validate_dates(self, self.work_from_date, self.work_end_date) if self.half_day: if not self.half_day_date: diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index fa017d9d4c2b..5ca47560b106 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -13,8 +13,10 @@ from erpnext.utilities.transaction_base import delete_events from frappe.utils.nestedset import NestedSet -class EmployeeUserDisabledError(frappe.ValidationError): pass -class EmployeeLeftValidationError(frappe.ValidationError): pass +class EmployeeUserDisabledError(frappe.ValidationError): + pass +class InactiveEmployeeStatusError(frappe.ValidationError): + pass class Employee(NestedSet): nsm_parent_field = 'reports_to' @@ -196,7 +198,7 @@ def validate_status(self): message += "

                                • " + "
                                • ".join(link_to_employees) message += "

                                " message += _("Please make sure the employees above report to another Active employee.") - throw(message, EmployeeLeftValidationError, _("Cannot Relieve Employee")) + throw(message, InactiveEmployeeStatusError, _("Cannot Relieve Employee")) if not self.relieving_date: throw(_("Please enter relieving date.")) diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py index 7d652a7366a9..8fc7cf193435 100644 --- a/erpnext/hr/doctype/employee/test_employee.py +++ b/erpnext/hr/doctype/employee/test_employee.py @@ -7,7 +7,7 @@ import erpnext import unittest import frappe.utils -from erpnext.hr.doctype.employee.employee import EmployeeLeftValidationError +from erpnext.hr.doctype.employee.employee import InactiveEmployeeStatusError test_records = frappe.get_test_records('Employee') @@ -45,10 +45,33 @@ def test_employee_status_left(self): employee2_doc.save() employee1_doc.reload() employee1_doc.status = 'Left' - self.assertRaises(EmployeeLeftValidationError, employee1_doc.save) + self.assertRaises(InactiveEmployeeStatusError, employee1_doc.save) + + def test_employee_status_inactive(self): + from erpnext.payroll.doctype.salary_structure.test_salary_structure import make_salary_structure + from erpnext.payroll.doctype.salary_structure.salary_structure import make_salary_slip + from erpnext.payroll.doctype.salary_slip.test_salary_slip import make_holiday_list + + employee = make_employee("test_employee_status@company.com") + employee_doc = frappe.get_doc("Employee", employee) + employee_doc.status = "Inactive" + employee_doc.save() + employee_doc.reload() + + make_holiday_list() + frappe.db.set_value("Company", erpnext.get_default_company(), "default_holiday_list", "Salary Slip Test Holiday List") + + frappe.db.sql("""delete from `tabSalary Structure` where name='Test Inactive Employee Salary Slip'""") + salary_structure = make_salary_structure("Test Inactive Employee Salary Slip", "Monthly", + employee=employee_doc.name, company=employee_doc.company) + salary_slip = make_salary_slip(salary_structure.name, employee=employee_doc.name) + + self.assertRaises(InactiveEmployeeStatusError, salary_slip.save) + + def tearDown(self): + frappe.db.rollback() def make_employee(user, company=None, **kwargs): - "" if not frappe.db.get_value("User", user): frappe.get_doc({ "doctype": "User", @@ -80,4 +103,5 @@ def make_employee(user, company=None, **kwargs): employee.insert() return employee.name else: + frappe.db.set_value("Employee", {"employee_name":user}, "status", "Active") return frappe.get_value("Employee", {"employee_name":user}, "name") diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index 08e0b24a2023..cbb3cc813b4d 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -8,6 +8,7 @@ from frappe.model.document import Document from frappe.utils import flt, nowdate from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account +from erpnext.hr.utils import validate_active_employee class EmployeeAdvanceOverPayment(frappe.ValidationError): pass @@ -18,6 +19,7 @@ def onload(self): 'make_payment_via_journal_entry') def validate(self): + validate_active_employee(self.employee) self.set_status() def on_cancel(self): diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.py b/erpnext/hr/doctype/employee_checkin/employee_checkin.py index 15fbd4e01530..60ea0f9895de 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.py +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.py @@ -9,9 +9,11 @@ from frappe import _ from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift +from erpnext.hr.utils import validate_active_employee class EmployeeCheckin(Document): def validate(self): + validate_active_employee(self.employee) self.validate_duplicate_log() self.fetch_shift() @@ -122,7 +124,7 @@ def mark_attendance_and_link_log(logs, attendance_status, attendance_date, worki def calculate_working_hours(logs, check_in_out_type, working_hours_calc_type): """Given a set of logs in chronological order calculates the total working hours based on the parameters. Zero is returned for all invalid cases. - + :param logs: The List of 'Employee Checkin'. :param check_in_out_type: One of: 'Alternating entries as IN and OUT during the same shift', 'Strictly based on Log Type in Employee Checkin' :param working_hours_calc_type: One of: 'First Check-in and Last Check-out', 'Every Valid Check-in and Check-out' diff --git a/erpnext/hr/doctype/employee_promotion/employee_promotion.py b/erpnext/hr/doctype/employee_promotion/employee_promotion.py index 83fb235f92c1..a3a61834c8c4 100644 --- a/erpnext/hr/doctype/employee_promotion/employee_promotion.py +++ b/erpnext/hr/doctype/employee_promotion/employee_promotion.py @@ -7,12 +7,11 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import getdate -from erpnext.hr.utils import update_employee +from erpnext.hr.utils import update_employee, validate_active_employee class EmployeePromotion(Document): def validate(self): - if frappe.get_value("Employee", self.employee, "status") != "Active": - frappe.throw(_("Cannot promote Employee with status Left or Inactive")) + validate_active_employee(self.employee) def before_submit(self): if getdate(self.promotion_date) > getdate(): diff --git a/erpnext/hr/doctype/employee_referral/employee_referral.py b/erpnext/hr/doctype/employee_referral/employee_referral.py index 45d68729ce67..0493306166f8 100644 --- a/erpnext/hr/doctype/employee_referral/employee_referral.py +++ b/erpnext/hr/doctype/employee_referral/employee_referral.py @@ -7,9 +7,11 @@ from frappe import _ from frappe.utils import get_link_to_form from frappe.model.document import Document +from erpnext.hr.utils import validate_active_employee class EmployeeReferral(Document): def validate(self): + validate_active_employee(self.referrer) self.set_full_name() self.set_referral_bonus_payment_status() diff --git a/erpnext/hr/doctype/employee_transfer/employee_transfer.py b/erpnext/hr/doctype/employee_transfer/employee_transfer.py index 6eec9fa12a93..c2007747fb3d 100644 --- a/erpnext/hr/doctype/employee_transfer/employee_transfer.py +++ b/erpnext/hr/doctype/employee_transfer/employee_transfer.py @@ -10,10 +10,6 @@ from erpnext.hr.utils import update_employee class EmployeeTransfer(Document): - def validate(self): - if frappe.get_value("Employee", self.employee, "status") != "Active": - frappe.throw(_("Cannot transfer Employee with status Left or Inactive")) - def before_submit(self): if getdate(self.transfer_date) > getdate(): frappe.throw(_("Employee Transfer cannot be submitted before Transfer Date"), diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index 8cef14383532..95e2806aedcd 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -6,7 +6,7 @@ from frappe import _ from frappe.utils import get_fullname, flt, cstr, get_link_to_form from frappe.model.document import Document -from erpnext.hr.utils import set_employee_name, share_doc_with_approver +from erpnext.hr.utils import set_employee_name, share_doc_with_approver, validate_active_employee from erpnext.accounts.party import get_party_account from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account @@ -23,6 +23,7 @@ def onload(self): 'make_payment_via_journal_entry') def validate(self): + validate_active_employee(self.employee) self.validate_advances() self.validate_sanctioned_amount() self.calculate_total_amount() diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index cee6f374fdc7..93fb19f4a191 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -5,7 +5,7 @@ import frappe from frappe import _ from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, get_link_to_form, get_fullname, add_days, nowdate -from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver +from erpnext.hr.utils import set_employee_name, get_leave_period, share_doc_with_approver, validate_active_employee from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_block_dates from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.buying.doctype.supplier_scorecard.supplier_scorecard import daterange @@ -22,6 +22,7 @@ def get_feed(self): return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type) def validate(self): + validate_active_employee(self.employee) set_employee_name(self) self.validate_dates() self.validate_balance_leaves() diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.py b/erpnext/hr/doctype/leave_encashment/leave_encashment.py index e041b7fb8f81..912bd8ad92f8 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.py +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.py @@ -7,7 +7,7 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import getdate, nowdate, flt -from erpnext.hr.utils import set_employee_name +from erpnext.hr.utils import set_employee_name, validate_active_employee from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure from erpnext.hr.doctype.leave_ledger_entry.leave_ledger_entry import create_leave_ledger_entry from erpnext.hr.doctype.leave_allocation.leave_allocation import get_unused_leaves @@ -15,6 +15,7 @@ class LeaveEncashment(Document): def validate(self): set_employee_name(self) + validate_active_employee(self.employee) self.get_leave_details_for_encashment() self.validate_salary_structure() diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.py b/erpnext/hr/doctype/shift_assignment/shift_assignment.py index ab65260c0918..89ae4d535d45 100644 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment.py +++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.py @@ -9,10 +9,12 @@ from frappe.utils import cint, cstr, date_diff, flt, formatdate, getdate, now_datetime, nowdate from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee from erpnext.hr.doctype.holiday_list.holiday_list import is_holiday +from erpnext.hr.utils import validate_active_employee from datetime import timedelta, datetime class ShiftAssignment(Document): def validate(self): + validate_active_employee(self.employee) self.validate_overlapping_dates() if self.end_date and self.end_date <= self.start_date: diff --git a/erpnext/hr/doctype/shift_request/shift_request.py b/erpnext/hr/doctype/shift_request/shift_request.py index 177c45edc657..6461f07552b1 100644 --- a/erpnext/hr/doctype/shift_request/shift_request.py +++ b/erpnext/hr/doctype/shift_request/shift_request.py @@ -7,12 +7,13 @@ from frappe import _ from frappe.model.document import Document from frappe.utils import formatdate, getdate -from erpnext.hr.utils import share_doc_with_approver +from erpnext.hr.utils import share_doc_with_approver, validate_active_employee class OverlapError(frappe.ValidationError): pass class ShiftRequest(Document): def validate(self): + validate_active_employee(self.employee) self.validate_dates() self.validate_shift_request_overlap_dates() self.validate_approver() diff --git a/erpnext/hr/doctype/travel_request/travel_request.py b/erpnext/hr/doctype/travel_request/travel_request.py index 01d3f3470614..60834d3f4a64 100644 --- a/erpnext/hr/doctype/travel_request/travel_request.py +++ b/erpnext/hr/doctype/travel_request/travel_request.py @@ -5,6 +5,8 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from erpnext.hr.utils import validate_active_employee class TravelRequest(Document): - pass + def validate(self): + validate_active_employee(self.employee) diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 3cc1a014d76f..992b18d37a6e 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -3,13 +3,12 @@ import erpnext import frappe -from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee +from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee, InactiveEmployeeStatusError from frappe import _ from frappe.desk.form import assign_to from frappe.model.document import Document from frappe.utils import (add_days, cstr, flt, format_datetime, formatdate, - get_datetime, getdate, nowdate, today, unique) - + get_datetime, getdate, nowdate, today, unique, get_link_to_form) class DuplicateDeclarationError(frappe.ValidationError): pass @@ -410,3 +409,8 @@ def share_doc_with_approver(doc, user): approver = approvers.get(doc.doctype) if doc_before_save.get(approver) != doc.get(approver): frappe.share.remove(doc.doctype, doc.name, doc_before_save.get(approver)) + +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 diff --git a/erpnext/payroll/doctype/additional_salary/additional_salary.py b/erpnext/payroll/doctype/additional_salary/additional_salary.py index ebeddf97f9e3..381f399e9fa3 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py @@ -7,6 +7,7 @@ from frappe.model.document import Document from frappe import _, bold from frappe.utils import getdate, date_diff, comma_and, formatdate +from erpnext.hr.utils import validate_active_employee class AdditionalSalary(Document): def on_submit(self): @@ -19,6 +20,7 @@ def on_cancel(self): self.update_employee_referral(cancel=True) def validate(self): + validate_active_employee(self.employee) self.validate_dates() self.validate_salary_structure() self.validate_recurring_additional_salary_overlap() 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 27df30a459cf..5ebe514ac052 100644 --- a/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py +++ b/erpnext/payroll/doctype/employee_benefit_application/employee_benefit_application.py @@ -9,10 +9,11 @@ from frappe.model.document import Document from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure -from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount +from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount, validate_active_employee class EmployeeBenefitApplication(Document): def validate(self): + validate_active_employee(self.employee) self.validate_duplicate_on_payroll_period() if not self.max_benefits: self.max_benefits = get_max_benefits_remaining(self.employee, self.date, self.payroll_period) diff --git a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py index d9937a7bb978..c6713f3aa46b 100644 --- a/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py +++ b/erpnext/payroll/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -8,12 +8,13 @@ from frappe.utils import flt from frappe.model.document import Document from erpnext.payroll.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits -from erpnext.hr.utils import get_previous_claimed_amount +from erpnext.hr.utils import get_previous_claimed_amount, validate_active_employee from erpnext.payroll.doctype.payroll_period.payroll_period import get_payroll_period from erpnext.payroll.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure class EmployeeBenefitClaim(Document): def validate(self): + validate_active_employee(self.employee) max_benefits = get_max_benefits(self.employee, self.claim_date) if not max_benefits or max_benefits <= 0: frappe.throw(_("Employee {0} has no maximum benefit amount").format(self.employee)) diff --git a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py index ead3db126f72..6b918ba76d15 100644 --- a/erpnext/payroll/doctype/employee_incentive/employee_incentive.py +++ b/erpnext/payroll/doctype/employee_incentive/employee_incentive.py @@ -6,9 +6,11 @@ import frappe from frappe import _ from frappe.model.document import Document +from erpnext.hr.utils import validate_active_employee class EmployeeIncentive(Document): def validate(self): + validate_active_employee(self.employee) self.validate_salary_structure() def validate_salary_structure(self): diff --git a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py index fb71a2877a1b..e11d60a4649b 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_declaration/employee_tax_exemption_declaration.py @@ -8,11 +8,12 @@ from frappe import _ from frappe.utils import flt from frappe.model.mapper import get_mapped_doc -from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \ +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \ calculate_annual_eligible_hra_exemption, validate_duplicate_exemption_for_payroll_period class EmployeeTaxExemptionDeclaration(Document): def validate(self): + validate_active_employee(self.employee) validate_tax_declaration(self.declarations) validate_duplicate_exemption_for_payroll_period(self.doctype, self.name, self.payroll_period, self.employee) self.set_total_declared_amount() diff --git a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py index 5bc33a65f2c1..8131ae0fa854 100644 --- a/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py +++ b/erpnext/payroll/doctype/employee_tax_exemption_proof_submission/employee_tax_exemption_proof_submission.py @@ -7,11 +7,12 @@ from frappe.model.document import Document from frappe import _ from frappe.utils import flt -from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, \ +from erpnext.hr.utils import validate_tax_declaration, get_total_exemption_amount, validate_active_employee, \ calculate_hra_exemption_for_period, validate_duplicate_exemption_for_payroll_period class EmployeeTaxExemptionProofSubmission(Document): def validate(self): + validate_active_employee(self.employee) validate_tax_declaration(self.tax_exemption_proofs) self.set_total_actual_amount() self.set_total_exemption_amount() diff --git a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py index 049ea265cce2..055bea741081 100644 --- a/erpnext/payroll/doctype/retention_bonus/retention_bonus.py +++ b/erpnext/payroll/doctype/retention_bonus/retention_bonus.py @@ -7,11 +7,10 @@ from frappe.model.document import Document from frappe import _ from frappe.utils import getdate - +from erpnext.hr.utils import validate_active_employee class RetentionBonus(Document): def validate(self): - if frappe.get_value('Employee', self.employee, 'status') != 'Active': - frappe.throw(_('Cannot create Retention Bonus for Left or Inactive Employees')) + validate_active_employee(self.employee) if getdate(self.bonus_payment_date) < getdate(): frappe.throw(_('Bonus Payment Date cannot be a past date')) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index f82b0d51bb12..c321f6f60f5d 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -19,6 +19,7 @@ from erpnext.payroll.doctype.employee_benefit_claim.employee_benefit_claim import get_benefit_claim_amount, get_last_payroll_period_benefits from erpnext.loan_management.doctype.loan_repayment.loan_repayment import calculate_amounts, create_repayment_entry from erpnext.accounts.utils import get_fiscal_year +from erpnext.hr.utils import validate_active_employee from six import iteritems class SalarySlip(TransactionBase): @@ -39,6 +40,7 @@ def autoname(self): def validate(self): self.status = self.get_status() + validate_active_employee(self.employee) self.validate_dates() self.check_existing() if not self.salary_slip_based_on_timesheet: diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index c8bd80fca0a8..ae38d4ca1925 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -15,12 +15,15 @@ WorkstationHolidayError) from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations from erpnext.setup.utils import get_exchange_rate +from erpnext.hr.utils import validate_active_employee class OverlapError(frappe.ValidationError): pass class OverWorkLoggedError(frappe.ValidationError): pass class Timesheet(Document): def validate(self): + if self.employee: + validate_active_employee(self.employee) self.set_employee_name() self.set_status() self.validate_dates() From 2d6f2fea5b42286e4a12d26d219448f9a298c0ed Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 30 Jul 2021 10:55:53 +0530 Subject: [PATCH 382/691] 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 ca4d009956d3..4bc22a544d17 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 @@ def test_gain_loss_with_advance_entry(self): 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 @@ def test_gain_loss_with_advance_entry(self): 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 cdd865ac4ac9..a9b7efbe980b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -674,19 +674,24 @@ def make_exchange_gain_loss_gl_entries(self, gl_entries): 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 d4ae1febe3b174469c8f86f281523974fff02ca3 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 30 Jul 2021 11:21:49 +0530 Subject: [PATCH 383/691] fix: gl entries for exchange gain loss (#26734) --- .../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 ca4d009956d3..4bc22a544d17 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 @@ def test_gain_loss_with_advance_entry(self): 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 @@ def test_gain_loss_with_advance_entry(self): 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 cdd865ac4ac9..a9b7efbe980b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -674,19 +674,24 @@ def make_exchange_gain_loss_gl_entries(self, gl_entries): 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 384/691] 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 b97dc401e6a3..c3b78a4f4821 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 1cdbd8d38a61..d569e1cbca2d 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 1a2332a81cd7999e1f9266337fb48462ebfcbcc4 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 30 Jul 2021 18:30:15 +0530 Subject: [PATCH 385/691] fix: student category mapping from the program enrollment tool (#26716) Co-authored-by: Jannat Patel <31363128+pateljannat@users.noreply.github.com> --- 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 afa0be9b9f3a..4493a3fef170 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 9be292b65ea8..1d7497387f91 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 b5c7fce68922a846be2b644980b9dfad8a596e92 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 30 Jul 2021 18:54:35 +0530 Subject: [PATCH 386/691] 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 afa0be9b9f3a..4493a3fef170 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 9be292b65ea8..1d7497387f91 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 6eded547f5e6935c01ec24caf96ba03e70a860d9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 29 Jul 2021 15:26:19 +0530 Subject: [PATCH 387/691] 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 020de3c3f341..481ef285e720 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 2a14f255cfa8d85815cc22d0bcae76d7e4de8634 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 30 Jul 2021 12:36:35 +0530 Subject: [PATCH 388/691] fix: COGS account in purchase receipt --- .../doctype/pricing_rule/test_pricing_rule.py | 10 ++++++++++ erpnext/accounts/utils.py | 6 +++--- erpnext/controllers/accounts_controller.py | 4 ++-- .../doctype/purchase_receipt/purchase_receipt.py | 2 +- .../purchase_receipt/test_purchase_receipt.py | 15 +++++++++------ 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index ffe8be1162f8..3173db13af33 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -15,6 +15,7 @@ class TestPricingRule(unittest.TestCase): def setUp(self): delete_existing_pricing_rules() + setup_pricing_rule_data() def tearDown(self): delete_existing_pricing_rules() @@ -554,6 +555,8 @@ def test_pricing_rule_for_transaction(self): for doc in [si, si1]: doc.delete() +test_dependencies = ["Campaign"] + def make_pricing_rule(**args): args = frappe._dict(args) @@ -600,6 +603,13 @@ def make_pricing_rule(**args): if args.get(applicable_for): doc.db_set(applicable_for, args.get(applicable_for)) +def setup_pricing_rule_data(): + if not frappe.db.exists('Campaign', '_Test Campaign'): + frappe.get_doc({ + 'doctype': 'Campaign', + 'campaign_name': '_Test Campaign', + 'name': '_Test Campaign' + }).insert() def delete_existing_pricing_rules(): for doctype in ["Pricing Rule", "Pricing Rule Item Code", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9afe365f7476..9272bc4fcee4 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -566,10 +566,10 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): frappe.msgprint(_("Payment Entries {0} are un-linked").format("\n".join(linked_pe))) @frappe.whitelist() -def get_company_default(company, fieldname): - value = frappe.get_cached_value('Company', company, fieldname) +def get_company_default(company, fieldname, ignore_validation=False): + value = frappe.get_cached_value('Company', company, fieldname) - if not value: + if not ignore_validation and not value: throw(_("Please set default {0} in Company {1}") .format(frappe.get_meta("Company").get_label(fieldname), company)) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index cdd865ac4ac9..d5c5d4291063 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -904,9 +904,9 @@ def throw_overbill_exception(self, item, max_allowed_amt): frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings") .format(item.item_code, item.idx, max_allowed_amt)) - def get_company_default(self, fieldname): + def get_company_default(self, fieldname, ignore_validation=False): from erpnext.accounts.utils import get_company_default - return get_company_default(self.company, fieldname) + return get_company_default(self.company, fieldname, ignore_validation=ignore_validation) def get_stock_items(self): stock_items = [] diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 48da78516efc..899d7e8e6666 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -352,7 +352,7 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): if self.is_return or flt(d.item_tax_amount): loss_account = expenses_included_in_valuation else: - loss_account = self.get_company_default("default_expense_account") + loss_account = self.get_company_default("default_expense_account", ignore_validation=True) or stock_rbnb cost_center = d.cost_center or frappe.get_cached_value("Company", self.company, "cost_center") diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index dbba21fde1b7..82461cb843c2 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -336,10 +336,13 @@ def test_subcontracting_over_receipt(self): se3.cancel() po.reload() pr2.load_from_db() - pr2.cancel() - po.load_from_db() - po.cancel() + if pr2.docstatus == 1 and frappe.db.get_value('Stock Ledger Entry', + {'voucher_no': pr2.name, 'is_cancelled': 0}, 'name'): + pr2.cancel() + + po.load_from_db() + po.cancel() def test_serial_no_supplier(self): pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1) @@ -1044,7 +1047,7 @@ def test_service_item_purchase_with_perpetual_inventory(self): '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) @@ -1055,13 +1058,13 @@ def test_service_item_purchase_with_perpetual_inventory(self): 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 From 6fffc90b46b9b0dd59b78837e5bab7b0d5c2651c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 31 Jul 2021 16:05:41 +0530 Subject: [PATCH 389/691] chore: Added change log for v13.8.0 --- erpnext/change_log/v13/v13_8_0.md | 39 +++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 erpnext/change_log/v13/v13_8_0.md diff --git a/erpnext/change_log/v13/v13_8_0.md b/erpnext/change_log/v13/v13_8_0.md new file mode 100644 index 000000000000..98ed95ae04a9 --- /dev/null +++ b/erpnext/change_log/v13/v13_8_0.md @@ -0,0 +1,39 @@ +# Version 13.8.0 Release Notes + +### Features & Enhancements +- Report to show COGS by item groups ([#26222](https://github.com/frappe/erpnext/pull/26222)) +- Enhancements in TDS ([#26677](https://github.com/frappe/erpnext/pull/26677)) +- API Endpoint to update halted Razorpay subscriptions ([#26564](https://github.com/frappe/erpnext/pull/26564)) + +### Fixes +- Incorrect bom name ([#26600](https://github.com/frappe/erpnext/pull/26600)) +- Exchange rate revaluation posting date and precision fixes ([#26651](https://github.com/frappe/erpnext/pull/26651)) +- POS item cart dom updates ([#26460](https://github.com/frappe/erpnext/pull/26460)) +- General Ledger report not working with filter group by ([#26439](https://github.com/frappe/erpnext/pull/26438)) +- Tax calculation for Recurring additional salary ([#24206](https://github.com/frappe/erpnext/pull/24206)) +- Validation check for batch for stock reconciliation type in stock entry ([#26487](https://github.com/frappe/erpnext/pull/26487)) +- Improved UX for additional discount field ([#26502](https://github.com/frappe/erpnext/pull/26502)) +- Add missing cess amount in GSTR-3B report ([#26644](https://github.com/frappe/erpnext/pull/26644)) +- Optimized code for reposting item valuation ([#26431](https://github.com/frappe/erpnext/pull/26431)) +- FG item not fetched in manufacture entry ([#26508](https://github.com/frappe/erpnext/pull/26508)) +- Errors on parallel requests creation of company for India ([#26420](https://github.com/frappe/erpnext/pull/26420)) +- Incorrect valuation rate calculation in gross profit report ([#26558](https://github.com/frappe/erpnext/pull/26558)) +- Empty "against account" in Purchase Receipt GLE ([#26712](https://github.com/frappe/erpnext/pull/26712)) +- Remove cancelled entries from Stock and Account Value comparison report ([#26721](https://github.com/frappe/erpnext/pull/26721)) +- Remove manual permission checking ([#26691](https://github.com/frappe/erpnext/pull/26691)) +- Delete child docs when parent doc is deleted ([#26518](https://github.com/frappe/erpnext/pull/26518)) +- GST Reports timeout issue ([#26646](https://github.com/frappe/erpnext/pull/26646)) +- Parent condition in pricing rules ([#26727](https://github.com/frappe/erpnext/pull/26727)) +- Added Company filters for Loan ([#26294](https://github.com/frappe/erpnext/pull/26294)) +- Incorrect discount amount on amended document ([#26292](https://github.com/frappe/erpnext/pull/26292)) +- Exchange gain loss not set for advances linked with invoices ([#26436](https://github.com/frappe/erpnext/pull/26436)) +- Unallocated amount in Payment Entry after taxes ([#26412](https://github.com/frappe/erpnext/pull/26412)) +- Wrong operation time in Work Order ([#26613](https://github.com/frappe/erpnext/pull/26613)) +- Serial No and Batch validation ([#26614](https://github.com/frappe/erpnext/pull/26614)) +- Gl Entries for exchange gain loss ([#26734](https://github.com/frappe/erpnext/pull/26734)) +- TDS computation summary shows cancelled invoices ([#26485](https://github.com/frappe/erpnext/pull/26485)) +- Price List rate not fetched for return sales invoice fixed ([#26560](https://github.com/frappe/erpnext/pull/26560)) +- Included company in link document type filters for contact ([#26576](https://github.com/frappe/erpnext/pull/26576)) +- Ignore mandatory fields while creating payment reconciliation Journal Entry ([#26643](https://github.com/frappe/erpnext/pull/26643)) +- Unable to download GSTR-1 json ([#26418](https://github.com/frappe/erpnext/pull/26418)) +- Paging buttons not working on item group portal page ([#26498](https://github.com/frappe/erpnext/pull/26498)) From 8cb560c7534b9dc2f47333f4dcbea078b9090740 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 1 Aug 2021 14:55:09 +0550 Subject: [PATCH 390/691] bumped to version 13.8.0 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a181c2d42cb4..c90e01cfbd68 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '13.7.1' +__version__ = '13.8.0' def get_default_company(user=None): '''Get default company for user''' From 493029195c81dd5317de23649486dd61aa8e8765 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sun, 1 Aug 2021 19:47:42 +0530 Subject: [PATCH 391/691] fix: Additional salary processing --- .../doctype/salary_slip/salary_slip.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py index b39cef8bbd44..f0ca64fdf262 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -647,10 +647,13 @@ def update_component_row(self, component_data, amount, component_type, additiona continue if ( - (not d.additional_salary - and (not additional_salary or additional_salary.overwrite)) - or (additional_salary - and additional_salary.name == d.additional_salary) + ( + not d.additional_salary + and (not additional_salary or additional_salary.overwrite) + ) or ( + additional_salary + and additional_salary.name == d.additional_salary + ) ): component_row = d break @@ -679,8 +682,12 @@ def update_component_row(self, component_data, amount, component_type, additiona if additional_salary: component_row.is_recurring_additional_salary = is_recurring - component_row.default_amount = 0 - component_row.additional_amount = amount + if additional_salary.overwrite: + component_row.additional_amount = flt(flt(amount) - flt(component_row.get("default_amount", 0)), + component_row.precision("additional_amount")) + else: + component_row.default_amount = 0 + component_row.additional_amount = amount component_row.additional_salary = additional_salary.name component_row.deduct_full_tax_on_selected_payroll_date = \ additional_salary.deduct_full_tax_on_selected_payroll_date From 74bb55bfd26583b8e12b5dc2dd89eb8f51dbd45d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sun, 1 Aug 2021 20:03:38 +0530 Subject: [PATCH 392/691] 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 b978cbe2b572..381f399e9fa3 100644 --- a/erpnext/payroll/doctype/additional_salary/additional_salary.py +++ b/erpnext/payroll/doctype/additional_salary/additional_salary.py @@ -112,11 +112,11 @@ def get_amount(self, sal_start_date, sal_end_date): 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 97608d72f3e3..393f647cc88c 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 3e82c0d428aa..7e1fb0616d0b 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -7,12 +7,12 @@ 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 @@ def add_additional_salary_components(self, component_type): 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 @@ def add_tax_components(self, payroll_period): 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 @@ def update_component_row(self, component_data, amount, component_type, additiona 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 @@ def calculate_variable_tax(self, payroll_period, tax_component): # 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 @@ def get_taxable_earnings(self, allow_tax_exemption=False, based_on_payment_days= 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 31fe5f5b92d071d4b963a40bf46a58dff23e79d2 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 2 Aug 2021 11:01:30 +0530 Subject: [PATCH 393/691] fix: added progress bar in repost item valuation (#26680) --- .../repost_item_valuation.js | 37 ++++++++ .../repost_item_valuation.json | 34 +++++++- .../repost_item_valuation.py | 2 +- erpnext/stock/stock_ledger.py | 85 ++++++++++++++----- 4 files changed, 135 insertions(+), 23 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 b3e4286bccb5..4cd40bf38ec8 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 071fc86d9b33..a800bf870130 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 5f31d9caf0de..b22759d3b722 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -81,7 +81,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, - allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher) + allow_negative_stock=doc.allow_negative_stock, via_landed_cost_voucher=doc.via_landed_cost_voucher, doc=doc) else: repost_future_sle(args=[frappe._dict({ "item_code": doc.item_code, diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index c15d1eda7dcf..f990ce06be56 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -127,30 +127,26 @@ 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, voucher_type=None, voucher_no=None, allow_negative_stock=None, via_landed_cost_voucher=False, doc=None): 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 - })) - - i = 0 + distinct_item_warehouses = get_distinct_item_warehouse(args, doc) + + i = get_current_index(doc) or 0 while i < len(args): + validate_item_warehouse(args[i]) + 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, - "creation": args[i].get("creation"), - "distinct_item_warehouses": distinct_item_warehouses + '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 +155,41 @@ 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 validate_item_warehouse(args): + for field in ['item_code', 'warehouse', 'posting_date', 'posting_time']: + if not args.get(field): + validation_msg = f'The field {frappe.unscrub(args.get(field))} is required for the reposting' + frappe.throw(_(validation_msg)) + +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 +197,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 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 394/691] 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 ea600d909735..cd85f6b026fe 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -966,7 +966,7 @@ def attach_qrcode_image(self): "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 58118d7830f16c72a95188a2ab29caea4ea21e2b Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 2 Aug 2021 11:34:43 +0530 Subject: [PATCH 395/691] fix: missing QR Code in auto email attachment (#26598) --- 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 81c7a6b9a075..f8a230d0b87f 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -969,7 +969,7 @@ def attach_qrcode_image(self): "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 3b7615750aa780671e75fa637a360218184095b8 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 2 Aug 2021 11:37:38 +0530 Subject: [PATCH 396/691] fix: POS Item Cart non-stop scroll issue (#26692) --- 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 6e36d2809ae3..a4a4b0e0ed2b 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 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 397/691] 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 6e36d2809ae3..a4a4b0e0ed2b 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 e99cd2e9daa659ff006b91f2b16f2bc3e9fc6100 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 2 Aug 2021 11:39:20 +0530 Subject: [PATCH 398/691] fix: POS Invoice consolidated Sales Invoice field set to no copy (#26706) --- 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 7459c11d4d98..33c3e0432bc6 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 a9474a9fbd407806ae7cd5f06169501c0c75bdfa Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 2 Aug 2021 12:25:27 +0530 Subject: [PATCH 399/691] 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 7459c11d4d98..33c3e0432bc6 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 b7267f8f5f82dccc237615901e043baaaf58589a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 15:25:09 +0530 Subject: [PATCH 400/691] 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 2751b5509cc6..dbc42de583ec 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 = class SalesInvoiceController extends e items_add(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() { super.set_dynamic_labels(); From 92f7a5a390aa6760e6fa27a867ff289628d504a9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 15:25:40 +0530 Subject: [PATCH 401/691] 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 7862f82f6f66..23d3064069c6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -886,18 +886,22 @@ def make_customer_gl_entry(self, gl_entries): ) 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 @@ def make_internal_transfer_gl_entries(self, gl_entries): 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: @@ -952,7 +958,7 @@ def make_item_gl_entries(self, gl_entries): 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( @@ -973,18 +979,6 @@ def make_item_gl_entries(self, gl_entries): 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 59879e0df58c..3d048c36865b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -808,6 +808,27 @@ def update_allocated_advance_taxes_on_cancel(self): 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 c525b372c6582d09b20b061f8795ee51bf7aed13 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 21 Jul 2021 22:28:32 +0530 Subject: [PATCH 402/691] 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 7c26007b0726..9de3012554fe 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -271,17 +271,16 @@ def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabl 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() @@ -294,10 +293,10 @@ def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabl # 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()) @@ -1218,6 +1217,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) @@ -1281,7 +1281,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 eccc69fdb273..6ee16f8e78a7 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2045,16 +2045,17 @@ def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled( 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()] @@ -2229,7 +2230,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 c677d47a4a62234693d1803355b75608803d7584 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 22 Jul 2021 10:43:16 +0530 Subject: [PATCH 403/691] 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 9de3012554fe..ed5c4af1a930 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -284,14 +284,6 @@ def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabl }) 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()], @@ -1217,7 +1209,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 6ee16f8e78a7..a55f708ab663 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2039,7 +2039,7 @@ def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled( 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 @@ -2047,7 +2047,6 @@ def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled( "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 ad7bb316c1dbe630c19d4e0146782e2fca108e6d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 28 Jul 2021 11:38:44 +0530 Subject: [PATCH 404/691] 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 3d048c36865b..8199b1040f0b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -845,6 +845,7 @@ def make_discount_gl_entries(self, gl_entries): 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 @@ def make_discount_gl_entries(self, gl_entries): 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 @@ def make_discount_gl_entries(self, gl_entries): 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 f9fc3bbfa83ac3d90ce3636cc2fae4f319c4a340 Mon Sep 17 00:00:00 2001 From: Anuja Date: Mon, 2 Aug 2021 12:54:30 +0530 Subject: [PATCH 405/691] fix: added patch for custom field and minor fixes --- erpnext/patches.txt | 1 + .../add_custom_field_for_south_africa.py | 13 +++++++++ .../vat_audit_report/vat_audit_report.py | 28 +++++++++++-------- 3 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 erpnext/patches/v13_0/add_custom_field_for_south_africa.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index a029627ab123..942acd59a53e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -297,3 +297,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_amt_in_work_order_required_items +erpnext.patches.v13_0.add_custom_field_for_south_africa diff --git a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py new file mode 100644 index 000000000000..73e9af9e4b9b --- /dev/null +++ b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py @@ -0,0 +1,13 @@ +# Copyright (c) 2020, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from erpnext.regional.south_africa.setup import make_custom_fields + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'South Africa'}) + if not company: + return + + make_custom_fields() 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 bce8586720d5..0c716cdab241 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -55,7 +55,7 @@ def get_invoice_data(self, doctype): `tab{doctype}` WHERE docstatus = 1 {where_conditions} - and is_opening = 'No' + and is_opening = "No" ORDER BY posting_date DESC """.format(select_columns=self.select_columns, doctype=doctype, @@ -66,26 +66,31 @@ def get_invoice_data(self, doctype): def get_invoice_items(self, doctype): self.invoice_items = frappe._dict() - self.item_tax_rate = frappe._dict() items = frappe.db.sql(""" SELECT - item_code, parent, taxable_value, base_net_amount, item_tax_rate + item_code, parent, taxable_value, base_net_amount, is_zero_rated FROM `tab%s Item` WHERE parent in (%s) - """ % (doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1) + """ % (doctype, ", ".join(["%s"]*len(self.invoices))), tuple(self.invoices), as_dict=1) 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, {}) \ + .setdefault("net_amount", 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, {}) \ + .setdefault("is_zero_rated", d.is_zero_rated) def get_items_based_on_tax_rate(self, doctype): self.items_based_on_tax_rate = frappe._dict() + self.item_tax_rate = frappe._dict() self.tax_doctype = "Purchase Taxes and Charges" if doctype=="Purchase Invoice" \ else "Sales Taxes and Charges" + self.tax_details = frappe.db.sql(""" SELECT parent, account_head, item_wise_tax_detail, base_tax_amount_after_discount_amount @@ -96,7 +101,7 @@ def get_items_based_on_tax_rate(self, doctype): and parent in (%s) ORDER BY account_head - """ % (self.tax_doctype, '%s', ', '.join(['%s']*len(self.invoices.keys()))), + """ % (self.tax_doctype, "%s", ", ".join(["%s"]*len(self.invoices.keys()))), tuple([doctype] + list(self.invoices.keys()))) for parent, account, item_wise_tax_detail, tax_amount in self.tax_details: @@ -107,7 +112,8 @@ def get_items_based_on_tax_rate(self, doctype): else: continue for item_code, taxes in item_wise_tax_detail.items(): - is_zero_rated = frappe.get_value("Item", item_code, "is_zero_rated") + is_zero_rated = self.invoice_items.get(parent).get(item_code).get("is_zero_rated") + #to skip items with non-zero tax rate in multiple rows if taxes[0] == 0 and not is_zero_rated: continue tax_rate, item_amount_map = self.get_item_amount_map(parent, item_code, taxes) @@ -121,7 +127,7 @@ def get_items_based_on_tax_rate(self, doctype): continue def get_item_amount_map(self, parent, item_code, taxes): - net_amount = abs(self.invoice_items.get(parent).get(item_code)) + net_amount = self.invoice_items.get(parent).get(item_code).get("net_amount") tax_rate = taxes[0] tax_amount = taxes[1] gross_amount = net_amount + tax_amount @@ -183,7 +189,7 @@ def get_consolidated_data(self, doctype): row = {} item_details = self.item_tax_rate.get(inv).get(item) row["account"] = inv_data.get("account") - row["posting_date"] = formatdate(inv_data.get("posting_date"), 'dd-mm-yyyy') + row["posting_date"] = formatdate(inv_data.get("posting_date"), "dd-mm-yyyy") row["voucher_type"] = doctype row["voucher_no"] = inv row["remarks"] = inv_data.get("remarks") From e43bdf76a5b8a76e590dd1c15605b06c534187fb Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 2 Aug 2021 18:57:48 +0530 Subject: [PATCH 406/691] 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 d3fe7d2b4d6f..12faeecc87fb 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 1574795dfad3..a926a7e52a5d 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 b2597470d415..0012641b663d 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 000000000000..8b0f1935cfb4 --- /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 7fe588e236051b9e03cd1b1934fa0a88379716b7 Mon Sep 17 00:00:00 2001 From: Alan <2.alan.tom@gmail.com> Date: Mon, 2 Aug 2021 20:07:55 +0530 Subject: [PATCH 407/691] fix: change format string to percent string interpolation (#26774) --- .../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 d7ad1fc69625..0d8109c41ad9 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 b3740e9afc375624dc478c97a97f757e06de084f Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 2 Aug 2021 20:13:26 +0530 Subject: [PATCH 408/691] fix: remove limit from stock balance report (#26773) --- 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 b6a806318928..9e56ad413069 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 78762850ee5cd900ead9422453d49823163a01cb Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 2 Aug 2021 20:15:54 +0530 Subject: [PATCH 409/691] fix: gl entries for exchange gain loss (#26711) From 8800aaaee7e9928ddc0148aad47c2d50d5f83030 Mon Sep 17 00:00:00 2001 From: Subin Tom Date: Tue, 13 Jul 2021 14:58:17 +0530 Subject: [PATCH 410/691] 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 e7dd6b8a6062..0a9a105b7cac 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 a4466e78f28d..657fd380b49a 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 762b6f1d6c96..d31db820abc5 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 f20e76f5bf6f..dbfeb4a10b7e 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 411/691] 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 dbc7f8632fca..70bccd7166e4 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 @@ def test_eway_bill_json(self): 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 657fd380b49a..04db9b3abece 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 efb037b566b68d26c269623bcd16069d72e7f2c9 Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 2 Aug 2021 21:57:52 +0530 Subject: [PATCH 412/691] refactor!: drop shopify integration from ERPNext (#26700) BREAKING CHANGE: remove Shopify integration. The integration is moved to a separate app with additional enhancements. The app is still FOSS and licensed under the same license as ERPNext. Any data migration required is taken care of after installing the app and enabling it. New app: https://github.com/frappe/ecommerce_integrations --- .../connectors/shopify_connection.py | 353 ------------ .../doctype/shopify_log/__init__.py | 0 .../doctype/shopify_log/shopify_log.js | 22 - .../doctype/shopify_log/shopify_log.json | 268 --------- .../doctype/shopify_log/shopify_log.py | 68 --- .../doctype/shopify_log/shopify_log_list.js | 12 - .../doctype/shopify_log/test_shopify_log.js | 23 - .../doctype/shopify_log/test_shopify_log.py | 12 - .../doctype/shopify_settings/__init__.py | 0 .../shopify_settings/shopify_settings.js | 90 --- .../shopify_settings/shopify_settings.json | 353 ------------ .../shopify_settings/shopify_settings.py | 144 ----- .../doctype/shopify_settings/sync_customer.py | 71 --- .../doctype/shopify_settings/sync_product.py | 309 ---------- .../test_data/custom_field.json | 527 ------------------ .../test_data/shopify_customer.json | 59 -- .../test_data/shopify_item.json | 125 ----- .../test_data/shopify_order.json | 270 --------- .../shopify_settings/test_shopify_settings.js | 23 - .../shopify_settings/test_shopify_settings.py | 107 ---- .../doctype/shopify_tax_account/__init__.py | 0 .../shopify_tax_account.json | 133 ----- .../shopify_tax_account.py | 10 - .../shopify_webhook_detail/__init__.py | 0 .../shopify_webhook_detail.json | 103 ---- .../shopify_webhook_detail.py | 10 - .../erpnext_integrations.json | 12 +- .../erpnext_integrations_settings.json | 12 +- erpnext/hooks.py | 1 - erpnext/patches.txt | 4 +- .../patches/v11_0/refactor_erpnext_shopify.py | 43 -- .../v12_0/set_default_shopify_app_type.py | 6 - .../v13_0/shopify_deprecation_warning.py | 10 + .../v13_0/update_custom_fields_for_shopify.py | 10 - 34 files changed, 13 insertions(+), 3177 deletions(-) delete mode 100644 erpnext/erpnext_integrations/connectors/shopify_connection.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_log/__init__.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.json delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_log/shopify_log_list.js delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.js delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/__init__.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_customer.json delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_item.json delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_order.json delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.js delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_tax_account/__init__.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.json delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_webhook_detail/__init__.py delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.json delete mode 100644 erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.py delete mode 100644 erpnext/patches/v11_0/refactor_erpnext_shopify.py delete mode 100644 erpnext/patches/v12_0/set_default_shopify_app_type.py create mode 100644 erpnext/patches/v13_0/shopify_deprecation_warning.py delete mode 100644 erpnext/patches/v13_0/update_custom_fields_for_shopify.py diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py deleted file mode 100644 index 5d5b2e19ce3f..000000000000 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ /dev/null @@ -1,353 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe import _ -import json -from frappe.utils import cstr, cint, nowdate, getdate, flt, get_request_session, get_datetime -from erpnext.erpnext_integrations.utils import validate_webhooks_request -from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice -from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import sync_item_from_shopify -from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer -from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log, dump_request_data -from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header - -@frappe.whitelist(allow_guest=True) -@validate_webhooks_request("Shopify Settings", 'X-Shopify-Hmac-Sha256', secret_key='shared_secret') -def store_request_data(order=None, event=None): - if frappe.request: - order = json.loads(frappe.request.data) - event = frappe.request.headers.get('X-Shopify-Topic') - - dump_request_data(order, event) - -def sync_sales_order(order, request_id=None, old_order_sync=False): - frappe.set_user('Administrator') - shopify_settings = frappe.get_doc("Shopify Settings") - frappe.flags.request_id = request_id - - if not frappe.db.get_value("Sales Order", filters={"shopify_order_id": cstr(order['id'])}): - try: - validate_customer(order, shopify_settings) - validate_item(order, shopify_settings) - create_order(order, shopify_settings, old_order_sync=old_order_sync) - except Exception as e: - make_shopify_log(status="Error", exception=e) - - else: - make_shopify_log(status="Success") - -def prepare_sales_invoice(order, request_id=None): - frappe.set_user('Administrator') - shopify_settings = frappe.get_doc("Shopify Settings") - frappe.flags.request_id = request_id - - try: - sales_order = get_sales_order(cstr(order['id'])) - if sales_order: - create_sales_invoice(order, shopify_settings, sales_order) - make_shopify_log(status="Success") - except Exception as e: - make_shopify_log(status="Error", exception=e, rollback=True) - -def prepare_delivery_note(order, request_id=None): - frappe.set_user('Administrator') - shopify_settings = frappe.get_doc("Shopify Settings") - frappe.flags.request_id = request_id - - try: - sales_order = get_sales_order(cstr(order['id'])) - if sales_order: - create_delivery_note(order, shopify_settings, sales_order) - make_shopify_log(status="Success") - except Exception as e: - make_shopify_log(status="Error", exception=e, rollback=True) - -def get_sales_order(shopify_order_id): - sales_order = frappe.db.get_value("Sales Order", filters={"shopify_order_id": shopify_order_id}) - if sales_order: - so = frappe.get_doc("Sales Order", sales_order) - return so - -def validate_customer(order, shopify_settings): - customer_id = order.get("customer", {}).get("id") - if customer_id: - if not frappe.db.get_value("Customer", {"shopify_customer_id": customer_id}, "name"): - create_customer(order.get("customer"), shopify_settings) - -def validate_item(order, shopify_settings): - for item in order.get("line_items"): - if item.get("product_id") and not frappe.db.get_value("Item", {"shopify_product_id": item.get("product_id")}, "name"): - sync_item_from_shopify(shopify_settings, item) - -def create_order(order, shopify_settings, old_order_sync=False, company=None): - so = create_sales_order(order, shopify_settings, company) - if so: - if order.get("financial_status") == "paid": - create_sales_invoice(order, shopify_settings, so, old_order_sync=old_order_sync) - - if order.get("fulfillments") and not old_order_sync: - create_delivery_note(order, shopify_settings, so) - -def create_sales_order(shopify_order, shopify_settings, company=None): - product_not_exists = [] - customer = frappe.db.get_value("Customer", {"shopify_customer_id": shopify_order.get("customer", {}).get("id")}, "name") - so = frappe.db.get_value("Sales Order", {"shopify_order_id": shopify_order.get("id")}, "name") - - if not so: - items = get_order_items(shopify_order.get("line_items"), shopify_settings, getdate(shopify_order.get('created_at'))) - - if not items: - message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master' - message += "\n" + ", ".join(product_not_exists) - - make_shopify_log(status="Error", exception=message, rollback=True) - - return '' - - so = frappe.get_doc({ - "doctype": "Sales Order", - "naming_series": shopify_settings.sales_order_series or "SO-Shopify-", - "shopify_order_id": shopify_order.get("id"), - "shopify_order_number": shopify_order.get("name"), - "customer": customer or shopify_settings.default_customer, - "transaction_date": getdate(shopify_order.get("created_at")) or nowdate(), - "delivery_date": getdate(shopify_order.get("created_at")) or nowdate(), - "company": shopify_settings.company, - "selling_price_list": shopify_settings.price_list, - "ignore_pricing_rule": 1, - "items": items, - "taxes": get_order_taxes(shopify_order, shopify_settings), - "apply_discount_on": "Grand Total", - "discount_amount": get_discounted_amount(shopify_order), - }) - - if company: - so.update({ - "company": company, - "status": "Draft" - }) - so.flags.ignore_mandatory = True - so.save(ignore_permissions=True) - so.submit() - - else: - so = frappe.get_doc("Sales Order", so) - - frappe.db.commit() - return so - -def create_sales_invoice(shopify_order, shopify_settings, so, old_order_sync=False): - if not frappe.db.get_value("Sales Invoice", {"shopify_order_id": shopify_order.get("id")}, "name")\ - and so.docstatus==1 and not so.per_billed and cint(shopify_settings.sync_sales_invoice): - - if old_order_sync: - posting_date = getdate(shopify_order.get('created_at')) - else: - posting_date = nowdate() - - si = make_sales_invoice(so.name, ignore_permissions=True) - si.shopify_order_id = shopify_order.get("id") - si.shopify_order_number = shopify_order.get("name") - si.set_posting_time = 1 - si.posting_date = posting_date - si.due_date = posting_date - si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-" - si.flags.ignore_mandatory = True - set_cost_center(si.items, shopify_settings.cost_center) - si.insert(ignore_mandatory=True) - si.submit() - make_payament_entry_against_sales_invoice(si, shopify_settings, posting_date) - frappe.db.commit() - -def set_cost_center(items, cost_center): - for item in items: - item.cost_center = cost_center - -def make_payament_entry_against_sales_invoice(doc, shopify_settings, posting_date=None): - from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry - payment_entry = get_payment_entry(doc.doctype, doc.name, bank_account=shopify_settings.cash_bank_account) - payment_entry.flags.ignore_mandatory = True - payment_entry.reference_no = doc.name - payment_entry.posting_date = posting_date or nowdate() - payment_entry.reference_date = posting_date or nowdate() - payment_entry.insert(ignore_permissions=True) - payment_entry.submit() - -def create_delivery_note(shopify_order, shopify_settings, so): - if not cint(shopify_settings.sync_delivery_note): - return - - for fulfillment in shopify_order.get("fulfillments"): - if not frappe.db.get_value("Delivery Note", {"shopify_fulfillment_id": fulfillment.get("id")}, "name")\ - and so.docstatus==1: - - dn = make_delivery_note(so.name) - dn.shopify_order_id = fulfillment.get("order_id") - dn.shopify_order_number = shopify_order.get("name") - dn.set_posting_time = 1 - dn.posting_date = getdate(fulfillment.get("created_at")) - dn.shopify_fulfillment_id = fulfillment.get("id") - dn.naming_series = shopify_settings.delivery_note_series or "DN-Shopify-" - dn.items = get_fulfillment_items(dn.items, fulfillment.get("line_items"), shopify_settings) - dn.flags.ignore_mandatory = True - dn.save() - dn.submit() - frappe.db.commit() - -def get_fulfillment_items(dn_items, fulfillment_items, shopify_settings): - return [dn_item.update({"qty": item.get("quantity")}) for item in fulfillment_items for dn_item in dn_items\ - if get_item_code(item) == dn_item.item_code] - -def get_discounted_amount(order): - discounted_amount = 0.0 - for discount in order.get("discount_codes"): - discounted_amount += flt(discount.get("amount")) - return discounted_amount - -def get_order_items(order_items, shopify_settings, delivery_date): - items = [] - all_product_exists = True - product_not_exists = [] - - for shopify_item in order_items: - if not shopify_item.get('product_exists'): - all_product_exists = False - product_not_exists.append({'title':shopify_item.get('title'), - 'shopify_order_id': shopify_item.get('id')}) - continue - - if all_product_exists: - item_code = get_item_code(shopify_item) - items.append({ - "item_code": item_code, - "item_name": shopify_item.get("name"), - "rate": shopify_item.get("price"), - "delivery_date": delivery_date, - "qty": shopify_item.get("quantity"), - "stock_uom": shopify_item.get("uom") or _("Nos"), - "warehouse": shopify_settings.warehouse - }) - else: - items = [] - - return items - -def get_item_code(shopify_item): - item_code = frappe.db.get_value("Item", {"shopify_variant_id": shopify_item.get("variant_id")}, "item_code") - if not item_code: - item_code = frappe.db.get_value("Item", {"shopify_product_id": shopify_item.get("product_id")}, "item_code") - if not item_code: - item_code = frappe.db.get_value("Item", {"item_name": shopify_item.get("title")}, "item_code") - - return item_code - -def get_order_taxes(shopify_order, shopify_settings): - taxes = [] - for tax in shopify_order.get("tax_lines"): - taxes.append({ - "charge_type": _("On Net Total"), - "account_head": get_tax_account_head(tax), - "description": "{0} - {1}%".format(tax.get("title"), tax.get("rate") * 100.0), - "rate": tax.get("rate") * 100.00, - "included_in_print_rate": 1 if shopify_order.get("taxes_included") else 0, - "cost_center": shopify_settings.cost_center - }) - - taxes = update_taxes_with_shipping_lines(taxes, shopify_order.get("shipping_lines"), shopify_settings) - - return taxes - -def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings): - """Shipping lines represents the shipping details, - each such shipping detail consists of a list of tax_lines""" - for shipping_charge in shipping_lines: - if shipping_charge.get("price"): - taxes.append({ - "charge_type": _("Actual"), - "account_head": get_tax_account_head(shipping_charge), - "description": shipping_charge["title"], - "tax_amount": shipping_charge["price"], - "cost_center": shopify_settings.cost_center - }) - - for tax in shipping_charge.get("tax_lines"): - taxes.append({ - "charge_type": _("Actual"), - "account_head": get_tax_account_head(tax), - "description": tax["title"], - "tax_amount": tax["price"], - "cost_center": shopify_settings.cost_center - }) - - return taxes - -def get_tax_account_head(tax): - tax_title = tax.get("title").encode("utf-8") - - tax_account = frappe.db.get_value("Shopify Tax Account", \ - {"parent": "Shopify Settings", "shopify_tax": tax_title}, "tax_account") - - if not tax_account: - frappe.throw(_("Tax Account not specified for Shopify Tax {0}").format(tax.get("title"))) - - return tax_account - -@frappe.whitelist(allow_guest=True) -def sync_old_orders(): - frappe.set_user('Administrator') - shopify_settings = frappe.get_doc('Shopify Settings') - - if not shopify_settings.sync_missing_orders: - return - - url = get_url(shopify_settings) - session = get_request_session() - - try: - res = session.get(url, headers=get_header(shopify_settings)) - res.raise_for_status() - orders = res.json()["orders"] - - for order in orders: - if is_sync_complete(shopify_settings, order): - stop_sync(shopify_settings) - return - - sync_sales_order(order=order, old_order_sync=True) - last_order_id = order.get('id') - - if last_order_id: - shopify_settings.load_from_db() - shopify_settings.last_order_id = last_order_id - shopify_settings.save() - frappe.db.commit() - - except Exception as e: - raise e - -def stop_sync(shopify_settings): - shopify_settings.sync_missing_orders = 0 - shopify_settings.last_order_id = '' - shopify_settings.save() - frappe.db.commit() - -def get_url(shopify_settings): - last_order_id = shopify_settings.last_order_id - - if not last_order_id: - if shopify_settings.sync_based_on == 'Date': - url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&created_at_min={0}&since_id=0".format( - get_datetime(shopify_settings.from_date)), shopify_settings) - else: - url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format( - shopify_settings.from_order_id), shopify_settings) - else: - url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings) - - return url - -def is_sync_complete(shopify_settings, order): - if shopify_settings.sync_based_on == 'Date': - return getdate(shopify_settings.to_date) < getdate(order.get('created_at')) - else: - return cstr(order.get('id')) == cstr(shopify_settings.to_order_id) - diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/__init__.py b/erpnext/erpnext_integrations/doctype/shopify_log/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js deleted file mode 100644 index d3fe7d2b4d6f..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Shopify Log', { - refresh: function(frm) { - if (frm.doc.request_data && frm.doc.status=='Error'){ - frm.add_custom_button('Resync', function() { - frappe.call({ - method:"erpnext.erpnext_integrations.doctype.shopify_log.shopify_log.resync", - args:{ - method:frm.doc.method, - name: frm.doc.name, - request_data: frm.doc.request_data - }, - callback: function(r){ - frappe.msgprint(__("Order rescheduled for sync")) - } - }) - }).addClass('btn-primary'); - } - } -}); diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.json b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.json deleted file mode 100644 index ab373eedb4e9..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.json +++ /dev/null @@ -1,268 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-03-14 10:02:06.227184", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "editable_grid": 0, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "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": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Queued", - "fieldname": "status", - "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": "Status", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "method", - "fieldtype": "Small Text", - "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": "Method", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "message", - "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": "Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "traceback", - "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": "Traceback", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "request_data", - "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": "Request Data", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-04-20 16:23:36.862381", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Shopify Log", - "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": "Administrator", - "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": "System Manager", - "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", - "title_field": "title", - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py deleted file mode 100644 index a2b6af99b215..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -import json -from frappe.model.document import Document -from erpnext.erpnext_integrations.utils import get_webhook_address - -class ShopifyLog(Document): - pass - - -def make_shopify_log(status="Queued", exception=None, rollback=False): - # if name not provided by log calling method then fetch existing queued state log - make_new = False - - if not frappe.flags.request_id: - make_new = True - - if rollback: - frappe.db.rollback() - - if make_new: - log = frappe.get_doc({"doctype":"Shopify Log"}).insert(ignore_permissions=True) - else: - log = log = frappe.get_doc("Shopify Log", frappe.flags.request_id) - - log.message = get_message(exception) - log.traceback = frappe.get_traceback() - log.status = status - log.save(ignore_permissions=True) - frappe.db.commit() - -def get_message(exception): - message = None - - if hasattr(exception, 'message'): - message = exception.message - elif hasattr(exception, '__str__'): - message = exception.__str__() - else: - message = "Something went wrong while syncing" - return message - -def dump_request_data(data, event="create/order"): - event_mapper = { - "orders/create": get_webhook_address(connector_name='shopify_connection', method="sync_sales_order", exclude_uri=True), - "orders/paid" : get_webhook_address(connector_name='shopify_connection', method="prepare_sales_invoice", exclude_uri=True), - "orders/fulfilled": get_webhook_address(connector_name='shopify_connection', method="prepare_delivery_note", exclude_uri=True) - } - - log = frappe.get_doc({ - "doctype": "Shopify Log", - "request_data": json.dumps(data, indent=1), - "method": event_mapper[event] - }).insert(ignore_permissions=True) - - frappe.db.commit() - frappe.enqueue(method=event_mapper[event], queue='short', timeout=300, is_async=True, - **{"order": data, "request_id": log.name}) - -@frappe.whitelist() -def resync(method, name, request_data): - frappe.db.set_value("Shopify Log", name, "status", "Queued", update_modified=False) - frappe.enqueue(method=method, queue='short', timeout=300, is_async=True, - **{"order": json.loads(request_data), "request_id": name}) diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log_list.js b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log_list.js deleted file mode 100644 index 0913ce4ef3cf..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log_list.js +++ /dev/null @@ -1,12 +0,0 @@ -frappe.listview_settings['Shopify Log'] = { - add_fields: ["status"], - get_indicator: function(doc) { - if(doc.status==="Success"){ - return [__("Success"), "green", "status,=,Success"]; - } else if(doc.status ==="Error"){ - return [__("Error"), "red", "status,=,Error"]; - } else if(doc.status ==="Queued"){ - return [__("Queued"), "orange", "status,=,Queued"]; - } - } -} diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.js b/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.js deleted file mode 100644 index d22b6d52402a..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Shopify Log", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Shopify Log - () => frappe.tests.make('Shopify Log', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.py b/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.py deleted file mode 100644 index 5892e1d6c4e4..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest - -# test_records = frappe.get_test_records('Shopify Log') - -class TestShopifyLog(unittest.TestCase): - pass diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/__init__.py b/erpnext/erpnext_integrations/doctype/shopify_settings/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js deleted file mode 100644 index 1574795dfad3..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.provide("erpnext_integrations.shopify_settings"); - -frappe.ui.form.on("Shopify Settings", "onload", function(frm){ - frappe.call({ - method:"erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings.get_series", - callback:function(r){ - $.each(r.message, function(key, value){ - set_field_options(key, value); - }); - } - }); - erpnext_integrations.shopify_settings.setup_queries(frm); -}) - -frappe.ui.form.on("Shopify Settings", "app_type", function(frm) { - frm.toggle_reqd("api_key", (frm.doc.app_type == "Private")); - frm.toggle_reqd("password", (frm.doc.app_type == "Private")); -}) - -frappe.ui.form.on("Shopify Settings", "refresh", function(frm){ - if(!frm.doc.__islocal && frm.doc.enable_shopify === 1){ - frm.toggle_reqd("price_list", true); - frm.toggle_reqd("warehouse", true); - frm.toggle_reqd("taxes", true); - frm.toggle_reqd("company", true); - frm.toggle_reqd("cost_center", true); - frm.toggle_reqd("cash_bank_account", true); - frm.toggle_reqd("sales_order_series", true); - frm.toggle_reqd("customer_group", true); - frm.toggle_reqd("shared_secret", true); - - frm.toggle_reqd("sales_invoice_series", frm.doc.sync_sales_invoice); - frm.toggle_reqd("delivery_note_series", frm.doc.sync_delivery_note); - - } -}) - -$.extend(erpnext_integrations.shopify_settings, { - setup_queries: function(frm) { - frm.fields_dict["warehouse"].get_query = function(doc) { - return { - filters:{ - "company": doc.company, - "is_group": "No" - } - } - } - - frm.fields_dict["taxes"].grid.get_field("tax_account").get_query = function(doc){ - return { - "query": "erpnext.controllers.queries.tax_account_query", - "filters": { - "account_type": ["Tax", "Chargeable", "Expense Account"], - "company": doc.company - } - } - } - - frm.fields_dict["cash_bank_account"].get_query = function(doc) { - return { - filters: [ - ["Account", "account_type", "in", ["Cash", "Bank"]], - ["Account", "root_type", "=", "Asset"], - ["Account", "is_group", "=",0], - ["Account", "company", "=", doc.company] - ] - } - } - - frm.fields_dict["cost_center"].get_query = function(doc) { - return { - filters:{ - "company": doc.company, - "is_group": "No" - } - } - } - - frm.fields_dict["price_list"].get_query = function() { - return { - filters:{ - "selling": 1 - } - } - } - } -}) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json deleted file mode 100644 index 308e7d163f3d..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ /dev/null @@ -1,353 +0,0 @@ -{ - "actions": [], - "creation": "2015-05-18 05:21:07.270859", - "doctype": "DocType", - "document_type": "System", - "engine": "InnoDB", - "field_order": [ - "status_html", - "enable_shopify", - "app_type", - "column_break_4", - "last_sync_datetime", - "section_break_2", - "shopify_url", - "api_key", - "column_break_3", - "password", - "shared_secret", - "access_token", - "section_break_38", - "webhooks", - "section_break_15", - "default_customer", - "column_break_19", - "customer_group", - "company_dependent_settings", - "company", - "cash_bank_account", - "column_break_20", - "cost_center", - "erp_settings", - "price_list", - "update_price_in_erpnext_price_list", - "column_break_26", - "warehouse", - "section_break_25", - "sales_order_series", - "column_break_27", - "sync_delivery_note", - "delivery_note_series", - "sync_sales_invoice", - "sales_invoice_series", - "section_break_22", - "html_16", - "taxes", - "syncing_details_section", - "sync_missing_orders", - "sync_based_on", - "column_break_41", - "from_date", - "to_date", - "from_order_id", - "to_order_id", - "last_order_id" - ], - "fields": [ - { - "fieldname": "status_html", - "fieldtype": "HTML", - "label": "status html", - "read_only": 1 - }, - { - "default": "0", - "fieldname": "enable_shopify", - "fieldtype": "Check", - "label": "Enable Shopify" - }, - { - "default": "Private", - "fieldname": "app_type", - "fieldtype": "Data", - "in_list_view": 1, - "label": "App Type", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "last_sync_datetime", - "fieldtype": "Datetime", - "label": "Last Sync Datetime", - "read_only": 1 - }, - { - "fieldname": "section_break_2", - "fieldtype": "Section Break" - }, - { - "description": "eg: frappe.myshopify.com", - "fieldname": "shopify_url", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Shop URL", - "reqd": 1 - }, - { - "depends_on": "eval:doc.app_type==\"Private\"", - "fieldname": "api_key", - "fieldtype": "Data", - "label": "API Key" - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval:doc.app_type==\"Private\"", - "fieldname": "password", - "fieldtype": "Password", - "label": "Password" - }, - { - "fieldname": "shared_secret", - "fieldtype": "Data", - "label": "Shared secret" - }, - { - "fieldname": "access_token", - "fieldtype": "Data", - "hidden": 1, - "label": "Access Token", - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "section_break_38", - "fieldtype": "Section Break", - "label": "Webhooks Details" - }, - { - "fieldname": "webhooks", - "fieldtype": "Table", - "label": "Webhooks", - "options": "Shopify Webhook Detail", - "read_only": 1 - }, - { - "fieldname": "section_break_15", - "fieldtype": "Section Break", - "label": "Customer Settings" - }, - { - "description": "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order", - "fieldname": "default_customer", - "fieldtype": "Link", - "label": "Default Customer", - "options": "Customer" - }, - { - "fieldname": "column_break_19", - "fieldtype": "Column Break" - }, - { - "description": "Customer Group will set to selected group while syncing customers from Shopify", - "fieldname": "customer_group", - "fieldtype": "Link", - "label": "Customer Group", - "options": "Customer Group" - }, - { - "fieldname": "company_dependent_settings", - "fieldtype": "Section Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "For Company", - "options": "Company" - }, - { - "description": "Cash Account will used for Sales Invoice creation", - "fieldname": "cash_bank_account", - "fieldtype": "Link", - "label": "Cash/Bank Account", - "options": "Account" - }, - { - "fieldname": "column_break_20", - "fieldtype": "Column Break" - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "erp_settings", - "fieldtype": "Section Break" - }, - { - "fieldname": "price_list", - "fieldtype": "Link", - "label": "Price List", - "options": "Price List" - }, - { - "default": "0", - "fieldname": "update_price_in_erpnext_price_list", - "fieldtype": "Check", - "label": "Update Price from Shopify To ERPNext Price List" - }, - { - "fieldname": "column_break_26", - "fieldtype": "Column Break" - }, - { - "description": "Default Warehouse to to create Sales Order and Delivery Note", - "fieldname": "warehouse", - "fieldtype": "Link", - "label": "Warehouse", - "options": "Warehouse" - }, - { - "fieldname": "section_break_25", - "fieldtype": "Section Break" - }, - { - "fieldname": "sales_order_series", - "fieldtype": "Select", - "label": "Sales Order Series" - }, - { - "fieldname": "column_break_27", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "sync_delivery_note", - "fieldtype": "Check", - "label": "Import Delivery Notes from Shopify on Shipment" - }, - { - "depends_on": "eval:doc.sync_delivery_note==1", - "fieldname": "delivery_note_series", - "fieldtype": "Select", - "label": "Delivery Note Series" - }, - { - "default": "0", - "fieldname": "sync_sales_invoice", - "fieldtype": "Check", - "label": "Import Sales Invoice from Shopify if Payment is marked" - }, - { - "depends_on": "eval:doc.sync_sales_invoice==1", - "fieldname": "sales_invoice_series", - "fieldtype": "Select", - "label": "Sales Invoice Series" - }, - { - "fieldname": "section_break_22", - "fieldtype": "Section Break" - }, - { - "fieldname": "html_16", - "fieldtype": "HTML", - "options": "Map Shopify Taxes / Shipping Charges to ERPNext Account" - }, - { - "fieldname": "taxes", - "fieldtype": "Table", - "label": "Shopify Tax Account", - "options": "Shopify Tax Account" - }, - { - "collapsible": 1, - "fieldname": "syncing_details_section", - "fieldtype": "Section Break", - "label": "Syncing Missing Orders" - }, - { - "depends_on": "eval:doc.sync_missing_orders", - "fieldname": "last_order_id", - "fieldtype": "Data", - "label": "Last Order Id", - "read_only": 1 - }, - { - "fieldname": "column_break_41", - "fieldtype": "Column Break" - }, - { - "default": "0", - "description": "On checking this Order from the ", - "fieldname": "sync_missing_orders", - "fieldtype": "Check", - "label": "Sync Missing Old Shopify Orders" - }, - { - "depends_on": "eval:doc.sync_missing_orders", - "fieldname": "sync_based_on", - "fieldtype": "Select", - "label": "Sync Based On", - "mandatory_depends_on": "eval:doc.sync_missing_orders", - "options": "\nDate\nShopify Order Id" - }, - { - "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders", - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Date", - "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders" - }, - { - "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders", - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date", - "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders" - }, - { - "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders", - "fieldname": "from_order_id", - "fieldtype": "Data", - "label": "From Order Id", - "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders" - }, - { - "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders", - "fieldname": "to_order_id", - "fieldtype": "Data", - "label": "To Order Id", - "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders" - } - ], - "issingle": 1, - "links": [], - "modified": "2021-03-02 17:35:41.953317", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Shopify Settings", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py deleted file mode 100644 index 381c5e5dec4e..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ /dev/null @@ -1,144 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -import json -from frappe import _ -from frappe.model.document import Document -from frappe.utils import get_request_session -from requests.exceptions import HTTPError -from frappe.custom.doctype.custom_field.custom_field import create_custom_fields -from erpnext.erpnext_integrations.utils import get_webhook_address -from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log - -class ShopifySettings(Document): - def validate(self): - if self.enable_shopify == 1: - setup_custom_fields() - self.validate_access_credentials() - self.register_webhooks() - else: - self.unregister_webhooks() - - def validate_access_credentials(self): - if not (self.get_password(raise_exception=False) and self.api_key and self.shopify_url): - frappe.msgprint(_("Missing value for Password, API Key or Shopify URL"), raise_exception=frappe.ValidationError) - - def register_webhooks(self): - webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] - # url = get_shopify_url('admin/webhooks.json', self) - created_webhooks = [d.method for d in self.webhooks] - url = get_shopify_url('admin/api/2021-04/webhooks.json', self) - for method in webhooks: - session = get_request_session() - try: - res = session.post(url, data=json.dumps({ - "webhook": { - "topic": method, - "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data', force_https=True), - "format": "json" - } - }), headers=get_header(self)) - res.raise_for_status() - self.update_webhook_table(method, res.json()) - - except HTTPError as e: - error_message = res.json().get('errors', e) - make_shopify_log(status="Warning", exception=error_message, rollback=True) - - except Exception as e: - make_shopify_log(status="Warning", exception=e, rollback=True) - - def unregister_webhooks(self): - session = get_request_session() - deleted_webhooks = [] - - for d in self.webhooks: - url = get_shopify_url('admin/api/2021-04/webhooks/{0}.json'.format(d.webhook_id), self) - try: - res = session.delete(url, headers=get_header(self)) - res.raise_for_status() - deleted_webhooks.append(d) - - except HTTPError as e: - error_message = res.json().get('errors', e) - make_shopify_log(status="Warning", exception=error_message, rollback=True) - - except Exception as e: - frappe.log_error(message=e, title='Shopify Webhooks Issue') - - for d in deleted_webhooks: - self.remove(d) - - def update_webhook_table(self, method, res): - self.append("webhooks", { - "webhook_id": res['webhook']['id'], - "method": method - }) - -def get_shopify_url(path, settings): - if settings.app_type == "Private": - return 'https://{}:{}@{}/{}'.format(settings.api_key, settings.get_password('password'), settings.shopify_url, path) - else: - return 'https://{}/{}'.format(settings.shopify_url, path) - -def get_header(settings): - header = {'Content-Type': 'application/json'} - - return header - -@frappe.whitelist() -def get_series(): - return { - "sales_order_series" : frappe.get_meta("Sales Order").get_options("naming_series") or "SO-Shopify-", - "sales_invoice_series" : frappe.get_meta("Sales Invoice").get_options("naming_series") or "SI-Shopify-", - "delivery_note_series" : frappe.get_meta("Delivery Note").get_options("naming_series") or "DN-Shopify-" - } - -def setup_custom_fields(): - custom_fields = { - "Customer": [ - dict(fieldname='shopify_customer_id', label='Shopify Customer Id', - fieldtype='Data', insert_after='series', read_only=1, print_hide=1) - ], - "Supplier": [ - dict(fieldname='shopify_supplier_id', label='Shopify Supplier Id', - fieldtype='Data', insert_after='supplier_name', read_only=1, print_hide=1) - ], - "Address": [ - dict(fieldname='shopify_address_id', label='Shopify Address Id', - fieldtype='Data', insert_after='fax', read_only=1, print_hide=1) - ], - "Item": [ - dict(fieldname='shopify_variant_id', label='Shopify Variant Id', - fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1), - dict(fieldname='shopify_product_id', label='Shopify Product Id', - fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1), - dict(fieldname='shopify_description', label='Shopify Description', - fieldtype='Text Editor', insert_after='description', read_only=1, print_hide=1) - ], - "Sales Order": [ - dict(fieldname='shopify_order_id', label='Shopify Order Id', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1), - dict(fieldname='shopify_order_number', label='Shopify Order Number', - fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1) - ], - "Delivery Note":[ - dict(fieldname='shopify_order_id', label='Shopify Order Id', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1), - dict(fieldname='shopify_order_number', label='Shopify Order Number', - fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1), - dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1) - ], - "Sales Invoice": [ - dict(fieldname='shopify_order_id', label='Shopify Order Id', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1), - dict(fieldname='shopify_order_number', label='Shopify Order Number', - fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1) - ] - } - - create_custom_fields(custom_fields) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py deleted file mode 100644 index 2af57f4c8915..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe import _ - -def create_customer(shopify_customer, shopify_settings): - import frappe.utils.nestedset - - cust_name = (shopify_customer.get("first_name") + " " + (shopify_customer.get("last_name") \ - and shopify_customer.get("last_name") or "")) if shopify_customer.get("first_name")\ - else shopify_customer.get("email") - - try: - customer = frappe.get_doc({ - "doctype": "Customer", - "name": shopify_customer.get("id"), - "customer_name" : cust_name, - "shopify_customer_id": shopify_customer.get("id"), - "sync_with_shopify": 1, - "customer_group": shopify_settings.customer_group, - "territory": frappe.utils.nestedset.get_root_of("Territory"), - "customer_type": _("Individual") - }) - customer.flags.ignore_mandatory = True - customer.insert(ignore_permissions=True) - - if customer: - create_customer_address(customer, shopify_customer) - - frappe.db.commit() - - except Exception as e: - raise e - -def create_customer_address(customer, shopify_customer): - addresses = shopify_customer.get("addresses", []) - - if not addresses and "default_address" in shopify_customer: - addresses.append(shopify_customer["default_address"]) - - for i, address in enumerate(addresses): - address_title, address_type = get_address_title_and_type(customer.customer_name, i) - try : - frappe.get_doc({ - "doctype": "Address", - "shopify_address_id": address.get("id"), - "address_title": address_title, - "address_type": address_type, - "address_line1": address.get("address1") or "Address 1", - "address_line2": address.get("address2"), - "city": address.get("city") or "City", - "state": address.get("province"), - "pincode": address.get("zip"), - "country": address.get("country"), - "phone": address.get("phone"), - "email_id": shopify_customer.get("email"), - "links": [{ - "link_doctype": "Customer", - "link_name": customer.name - }] - }).insert(ignore_mandatory=True) - - except Exception as e: - raise e - -def get_address_title_and_type(customer_name, index): - address_type = _("Billing") - address_title = customer_name - if frappe.db.get_value("Address", "{0}-{1}".format(customer_name.strip(), address_type)): - address_title = "{0}-{1}".format(customer_name.strip(), index) - - return address_title, address_type diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py deleted file mode 100644 index 16efb6caee1a..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py +++ /dev/null @@ -1,309 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe import _ -from erpnext import get_default_company -from frappe.utils import cstr, cint, get_request_session -from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header - -shopify_variants_attr_list = ["option1", "option2", "option3"] - -def sync_item_from_shopify(shopify_settings, item): - url = get_shopify_url("admin/api/2021-04/products/{0}.json".format(item.get("product_id")), shopify_settings) - session = get_request_session() - - try: - res = session.get(url, headers=get_header(shopify_settings)) - res.raise_for_status() - - shopify_item = res.json()["product"] - make_item(shopify_settings.warehouse, shopify_item) - except Exception as e: - raise e - -def make_item(warehouse, shopify_item): - add_item_weight(shopify_item) - - if has_variants(shopify_item): - attributes = create_attribute(shopify_item) - create_item(shopify_item, warehouse, 1, attributes) - create_item_variants(shopify_item, warehouse, attributes, shopify_variants_attr_list) - - else: - shopify_item["variant_id"] = shopify_item['variants'][0]["id"] - create_item(shopify_item, warehouse) - -def add_item_weight(shopify_item): - shopify_item["weight"] = shopify_item['variants'][0]["weight"] - shopify_item["weight_unit"] = shopify_item['variants'][0]["weight_unit"] - -def has_variants(shopify_item): - if len(shopify_item.get("options")) >= 1 and "Default Title" not in shopify_item.get("options")[0]["values"]: - return True - return False - -def create_attribute(shopify_item): - attribute = [] - # shopify item dict - for attr in shopify_item.get('options'): - if not frappe.db.get_value("Item Attribute", attr.get("name"), "name"): - frappe.get_doc({ - "doctype": "Item Attribute", - "attribute_name": attr.get("name"), - "item_attribute_values": [ - { - "attribute_value": attr_value, - "abbr":attr_value - } - for attr_value in attr.get("values") - ] - }).insert() - attribute.append({"attribute": attr.get("name")}) - - else: - # check for attribute values - item_attr = frappe.get_doc("Item Attribute", attr.get("name")) - if not item_attr.numeric_values: - set_new_attribute_values(item_attr, attr.get("values")) - item_attr.save() - attribute.append({"attribute": attr.get("name")}) - - else: - attribute.append({ - "attribute": attr.get("name"), - "from_range": item_attr.get("from_range"), - "to_range": item_attr.get("to_range"), - "increment": item_attr.get("increment"), - "numeric_values": item_attr.get("numeric_values") - }) - - return attribute - -def set_new_attribute_values(item_attr, values): - for attr_value in values: - if not any((d.abbr.lower() == attr_value.lower() or d.attribute_value.lower() == attr_value.lower())\ - for d in item_attr.item_attribute_values): - item_attr.append("item_attribute_values", { - "attribute_value": attr_value, - "abbr": attr_value - }) - -def create_item(shopify_item, warehouse, has_variant=0, attributes=None,variant_of=None): - item_dict = { - "doctype": "Item", - "shopify_product_id": shopify_item.get("id"), - "shopify_variant_id": shopify_item.get("variant_id"), - "variant_of": variant_of, - "sync_with_shopify": 1, - "is_stock_item": 1, - "item_code": cstr(shopify_item.get("item_code")) or cstr(shopify_item.get("id")), - "item_name": shopify_item.get("title", '').strip(), - "description": shopify_item.get("body_html") or shopify_item.get("title"), - "shopify_description": shopify_item.get("body_html") or shopify_item.get("title"), - "item_group": get_item_group(shopify_item.get("product_type")), - "has_variants": has_variant, - "attributes":attributes or [], - "stock_uom": shopify_item.get("uom") or _("Nos"), - "stock_keeping_unit": shopify_item.get("sku") or get_sku(shopify_item), - "default_warehouse": warehouse, - "image": get_item_image(shopify_item), - "weight_uom": shopify_item.get("weight_unit"), - "weight_per_unit": shopify_item.get("weight"), - "default_supplier": get_supplier(shopify_item), - "item_defaults": [ - { - "company": get_default_company() - } - ] - } - - if not is_item_exists(item_dict, attributes, variant_of=variant_of): - item_details = get_item_details(shopify_item) - name = '' - - if not item_details: - new_item = frappe.get_doc(item_dict) - new_item.insert(ignore_permissions=True, ignore_mandatory=True) - name = new_item.name - - if not name: - name = item_details.name - - if not has_variant: - add_to_price_list(shopify_item, name) - - frappe.db.commit() - -def create_item_variants(shopify_item, warehouse, attributes, shopify_variants_attr_list): - template_item = frappe.db.get_value("Item", filters={"shopify_product_id": shopify_item.get("id")}, - fieldname=["name", "stock_uom"], as_dict=True) - - if template_item: - for variant in shopify_item.get("variants"): - shopify_item_variant = { - "id" : variant.get("id"), - "item_code": variant.get("id"), - "title": variant.get("title"), - "product_type": shopify_item.get("product_type"), - "sku": variant.get("sku"), - "uom": template_item.stock_uom or _("Nos"), - "item_price": variant.get("price"), - "variant_id": variant.get("id"), - "weight_unit": variant.get("weight_unit"), - "weight": variant.get("weight") - } - - for i, variant_attr in enumerate(shopify_variants_attr_list): - if variant.get(variant_attr): - attributes[i].update({"attribute_value": get_attribute_value(variant.get(variant_attr), attributes[i])}) - create_item(shopify_item_variant, warehouse, 0, attributes, template_item.name) - -def get_attribute_value(variant_attr_val, attribute): - attribute_value = frappe.db.sql("""select attribute_value from `tabItem Attribute Value` - where parent = %s and (abbr = %s or attribute_value = %s)""", (attribute["attribute"], variant_attr_val, - variant_attr_val), as_list=1) - return attribute_value[0][0] if len(attribute_value)>0 else cint(variant_attr_val) - -def get_item_group(product_type=None): - import frappe.utils.nestedset - parent_item_group = frappe.utils.nestedset.get_root_of("Item Group") - - if product_type: - if not frappe.db.get_value("Item Group", product_type, "name"): - item_group = frappe.get_doc({ - "doctype": "Item Group", - "item_group_name": product_type, - "parent_item_group": parent_item_group, - "is_group": "No" - }).insert() - return item_group.name - else: - return product_type - else: - return parent_item_group - - -def get_sku(item): - if item.get("variants"): - return item.get("variants")[0].get("sku") - return "" - -def add_to_price_list(item, name): - shopify_settings = frappe.db.get_value("Shopify Settings", None, ["price_list", "update_price_in_erpnext_price_list"], as_dict=1) - if not shopify_settings.update_price_in_erpnext_price_list: - return - - item_price_name = frappe.db.get_value("Item Price", - {"item_code": name, "price_list": shopify_settings.price_list}, "name") - - if not item_price_name: - frappe.get_doc({ - "doctype": "Item Price", - "price_list": shopify_settings.price_list, - "item_code": name, - "price_list_rate": item.get("item_price") or item.get("variants")[0].get("price") - }).insert() - else: - item_rate = frappe.get_doc("Item Price", item_price_name) - item_rate.price_list_rate = item.get("item_price") or item.get("variants")[0].get("price") - item_rate.save() - -def get_item_image(shopify_item): - if shopify_item.get("image"): - return shopify_item.get("image").get("src") - return None - -def get_supplier(shopify_item): - if shopify_item.get("vendor"): - supplier = frappe.db.sql("""select name from tabSupplier - where name = %s or shopify_supplier_id = %s """, (shopify_item.get("vendor"), - shopify_item.get("vendor").lower()), as_list=1) - - if not supplier: - supplier = frappe.get_doc({ - "doctype": "Supplier", - "supplier_name": shopify_item.get("vendor"), - "shopify_supplier_id": shopify_item.get("vendor").lower(), - "supplier_group": get_supplier_group() - }).insert() - return supplier.name - else: - return shopify_item.get("vendor") - else: - return "" - -def get_supplier_group(): - supplier_group = frappe.db.get_value("Supplier Group", _("Shopify Supplier")) - if not supplier_group: - supplier_group = frappe.get_doc({ - "doctype": "Supplier Group", - "supplier_group_name": _("Shopify Supplier") - }).insert() - return supplier_group.name - return supplier_group - -def get_item_details(shopify_item): - item_details = {} - - item_details = frappe.db.get_value("Item", {"shopify_product_id": shopify_item.get("id")}, - ["name", "stock_uom", "item_name"], as_dict=1) - - if item_details: - return item_details - - else: - item_details = frappe.db.get_value("Item", {"shopify_variant_id": shopify_item.get("id")}, - ["name", "stock_uom", "item_name"], as_dict=1) - return item_details - -def is_item_exists(shopify_item, attributes=None, variant_of=None): - if variant_of: - name = variant_of - else: - name = frappe.db.get_value("Item", {"item_name": shopify_item.get("item_name")}) - - if name: - item = frappe.get_doc("Item", name) - item.flags.ignore_mandatory=True - - if not variant_of and not item.shopify_product_id: - item.shopify_product_id = shopify_item.get("shopify_product_id") - item.shopify_variant_id = shopify_item.get("shopify_variant_id") - item.save() - return True - - if item.shopify_product_id and attributes and attributes[0].get("attribute_value"): - if not variant_of: - variant_of = frappe.db.get_value("Item", - {"shopify_product_id": item.shopify_product_id}, "variant_of") - - # create conditions for all item attributes, - # as we are putting condition basis on OR it will fetch all items matching either of conditions - # thus comparing matching conditions with len(attributes) - # which will give exact matching variant item. - - conditions = ["(iv.attribute='{0}' and iv.attribute_value = '{1}')"\ - .format(attr.get("attribute"), attr.get("attribute_value")) for attr in attributes] - - conditions = "( {0} ) and iv.parent = it.name ) = {1}".format(" or ".join(conditions), len(attributes)) - - parent = frappe.db.sql(""" select * from tabItem it where - ( select count(*) from `tabItem Variant Attribute` iv - where {conditions} and it.variant_of = %s """.format(conditions=conditions) , - variant_of, as_list=1) - - if parent: - variant = frappe.get_doc("Item", parent[0][0]) - variant.flags.ignore_mandatory = True - - variant.shopify_product_id = shopify_item.get("shopify_product_id") - variant.shopify_variant_id = shopify_item.get("shopify_variant_id") - variant.save() - return False - - if item.shopify_product_id and item.shopify_product_id != shopify_item.get("shopify_product_id"): - return False - - return True - - else: - return False diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json deleted file mode 100644 index db6c3d5aa369..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json +++ /dev/null @@ -1,527 +0,0 @@ -[ - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Print Settings", - "fieldname": "compact_item_print", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "with_letterhead", - "label": "Compact Item Print", - "modified": "2016-06-06 15:18:17.025602", - "name": "Print Settings-compact_item_print", - "no_copy": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Customer", - "fieldname": "shopify_customer_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "naming_series", - "label": "Shopify Customer Id", - "modified": "2016-01-15 17:25:28.991818", - "name": "Customer-shopify_customer_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Address", - "fieldname": "shopify_address_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "fax", - "label": "Shopify Address Id", - "modified": "2016-01-15 17:50:52.213743", - "name": "Address-shopify_address_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Sales Order", - "fieldname": "shopify_order_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "title", - "label": "Shopify Order Id", - "modified": "2016-01-18 09:55:50.764524", - "name": "Sales Order-shopify_order_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "shopify_product_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "item_code", - "label": "Shopify Product Id", - "modified": "2016-01-19 15:44:16.132952", - "name": "Item-shopify_product_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Sales Invoice", - "fieldname": "shopify_order_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "naming_series", - "label": "Shopify Order Id", - "modified": "2016-01-19 16:30:12.261797", - "name": "Sales Invoice-shopify_order_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Delivery Note", - "fieldname": "shopify_order_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "title", - "label": "Shopify Order Id", - "modified": "2016-01-19 16:30:31.201198", - "name": "Delivery Note-shopify_order_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "stock_keeping_unit", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "stock_uom", - "label": "Stock Keeping Unit", - "modified": "2015-11-10 09:29:10.854943", - "name": "Item-stock_keeping_unit", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": "0", - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "sync_with_shopify", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "is_stock_item", - "label": "Sync With Shopify", - "modified": "2015-10-12 15:54:31.997714", - "name": "Item-sync_with_shopify", - "no_copy": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Customer", - "fieldname": "sync_with_shopify", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "is_frozen", - "label": "Sync With Shopify", - "modified": "2015-10-01 17:31:55.758826", - "name": "Customer-sync_with_shopify", - "no_copy": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "shopify_variant_id", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "item_code", - "label": "Variant Id", - "modified": "2015-11-09 18:26:50.825858", - "name": "Item-shopify_variant_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "sync_qty_with_shopify", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "item_code", - "label": "Sync Quantity With Shopify", - "modified": "2015-12-29 08:37:46.183295", - "name": "Item-sync_qty_with_shopify", - "no_copy": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Delivery Note", - "fieldname": "shopify_fulfillment_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "title", - "label": "Shopify Fulfillment Id", - "modified": "2016-01-20 23:50:35.609543", - "name": "Delivery Note-shopify_fulfillment_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Supplier", - "fieldname": "shopify_supplier_id", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "supplier_name", - "label": "Shopify Supplier Id", - "modified": "2016-02-01 15:41:25.818306", - "name": "Supplier-shopify_supplier_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "shopify_description", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "section_break_11", - "label": "shopify_description", - "modified": "2016-06-15 12:15:36.325581", - "name": "Item-shopify_description", - "no_copy": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - } -] \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_customer.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_customer.json deleted file mode 100644 index e91ce9abf81b..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_customer.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "customer": { - "id": 2324518599, - "email": "andrew@wyatt.co.in", - "accepts_marketing": false, - "created_at": "2016-01-20T17:18:35+05:30", - "updated_at": "2016-01-20T17:22:23+05:30", - "first_name": "Andrew", - "last_name": "Wyatt", - "orders_count": 0, - "state": "disabled", - "total_spent": "0.00", - "last_order_id": null, - "note": "", - "verified_email": true, - "multipass_identifier": null, - "tax_exempt": false, - "tags": "", - "last_order_name": null, - "default_address": { - "id": 2476804295, - "first_name": "Andrew", - "last_name": "Wyatt", - "company": "Wyatt Inc.", - "address1": "B-11, Betahouse", - "address2": "Street 11, Sector 52", - "city": "Manhattan", - "province": "New York", - "country": "United States", - "zip": "10027", - "phone": "145-112211", - "name": "Andrew Wyatt", - "province_code": "NY", - "country_code": "US", - "country_name": "United States", - "default": true - }, - "addresses": [ - { - "id": 2476804295, - "first_name": "Andrew", - "last_name": "Wyatt", - "company": "Wyatt Inc.", - "address1": "B-11, Betahouse", - "address2": "Street 11, Sector 52", - "city": "Manhattan", - "province": "New York", - "country": "United States", - "zip": "10027", - "phone": "145-112211", - "name": "Andrew Wyatt", - "province_code": "NY", - "country_code": "US", - "country_name": "United States", - "default": true - } - ] - } -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_item.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_item.json deleted file mode 100644 index 296dede78683..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_item.json +++ /dev/null @@ -1,125 +0,0 @@ -{ - "product": { - "id": 4059739520, - "title": "Shopify Test Item", - "body_html": "
                                Hold back Spin Medallion-Set of 2
                                \n
                                \n
                                Finish: Plated/ Powder Coated
                                \n
                                Material: Iron
                                \n
                                Color Finish: Satin Silver, Brown Oil Rubbed, Roman Bronze
                                \n
                                Qty: 1 Set
                                ", - "vendor": "Boa casa", - "product_type": "Curtain Accessories", - "created_at": "2016-01-18T17:16:37+05:30", - "handle": "1001624-01", - "updated_at": "2016-01-20T17:26:44+05:30", - "published_at": "2016-01-18T17:16:37+05:30", - "template_suffix": null, - "published_scope": "global", - "tags": "Category_Curtain Accessories, Type_Holdback", - "variants": [{ - "id": 13917612359, - "product_id": 4059739520, - "title": "Test BALCK Item", - "price": "499.00", - "sku": "", - "position": 1, - "grams": 0, - "inventory_policy": "continue", - "compare_at_price": null, - "fulfillment_service": "manual", - "inventory_management": "shopify", - "option1": "BLACK", - "option2": null, - "option3": null, - "created_at": "2016-01-18T17:16:37+05:30", - "updated_at": "2016-01-20T17:26:44+05:30", - "requires_shipping": true, - "taxable": true, - "barcode": "", - "inventory_quantity": -1, - "old_inventory_quantity": -1, - "image_id": 8539321735, - "weight": 0, - "weight_unit": "kg" - }, { - "id": 13917612423, - "product_id": 4059739520, - "title": "Test BLUE Item", - "price": "499.00", - "sku": "", - "position": 2, - "grams": 0, - "inventory_policy": "continue", - "compare_at_price": null, - "fulfillment_service": "manual", - "inventory_management": "shopify", - "option1": "BLUE", - "option2": null, - "option3": null, - "created_at": "2016-01-18T17:16:37+05:30", - "updated_at": "2016-01-20T17:26:44+05:30", - "requires_shipping": true, - "taxable": true, - "barcode": "", - "inventory_quantity": -1, - "old_inventory_quantity": -1, - "image_id": null, - "weight": 0, - "weight_unit": "kg" - }, { - "id": 13917612487, - "product_id": 4059739520, - "title": "Test White Item", - "price": "499.00", - "sku": "", - "position": 3, - "grams": 0, - "inventory_policy": "continue", - "compare_at_price": null, - "fulfillment_service": "manual", - "inventory_management": "shopify", - "option1": "White", - "option2": null, - "option3": null, - "created_at": "2016-01-18T17:16:37+05:30", - "updated_at": "2016-01-18T17:16:37+05:30", - "requires_shipping": true, - "taxable": true, - "barcode": "", - "inventory_quantity": 0, - "old_inventory_quantity": 0, - "image_id": null, - "weight": 0, - "weight_unit": "kg" - }], - "options": [{ - "id": 4985027399, - "product_id": 4059739520, - "name": "Colour", - "position": 1, - "values": [ - "BLACK", - "BLUE", - "White" - ] - }], - "images": [{ - "id": 8539321735, - "product_id": 4059739520, - "position": 1, - "created_at": "2016-01-18T17:16:37+05:30", - "updated_at": "2016-01-18T17:16:37+05:30", - "src": "https://cdn.shopify.com/s/files/1/1123/0654/products/2015-12-17_6.png?v=1453117597", - "variant_ids": [ - 13917612359 - ] - }], - "image": { - "id": 8539321735, - "product_id": 4059739520, - "position": 1, - "created_at": "2016-01-18T17:16:37+05:30", - "updated_at": "2016-01-18T17:16:37+05:30", - "src": "https://cdn.shopify.com/s/files/1/1123/0654/products/2015-12-17_6.png?v=1453117597", - "variant_ids": [ - 13917612359 - ] - } - } -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_order.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_order.json deleted file mode 100644 index 988a2f0423d8..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_order.json +++ /dev/null @@ -1,270 +0,0 @@ -{ - "order": { - "id": 2414345735, - "email": "andrew@wyatt.co.in", - "closed_at": null, - "created_at": "2016-01-20T17:26:39+05:30", - "updated_at": "2016-01-20T17:27:15+05:30", - "number": 5, - "note": "", - "token": "660fed25987517b733644a8c9ec7c8e0", - "gateway": "manual", - "test": false, - "total_price": "1018.00", - "subtotal_price": "998.00", - "total_weight": 0, - "total_tax": "0.00", - "taxes_included": false, - "currency": "INR", - "financial_status": "paid", - "confirmed": true, - "total_discounts": "0.00", - "total_line_items_price": "998.00", - "cart_token": null, - "buyer_accepts_marketing": false, - "name": "#1005", - "referring_site": null, - "landing_site": null, - "cancelled_at": null, - "cancel_reason": null, - "total_price_usd": "15.02", - "checkout_token": null, - "reference": null, - "user_id": 55391175, - "location_id": null, - "source_identifier": null, - "source_url": null, - "processed_at": "2016-01-20T17:26:39+05:30", - "device_id": null, - "browser_ip": null, - "landing_site_ref": null, - "order_number": 1005, - "discount_codes": [], - "note_attributes": [], - "payment_gateway_names": [ - "manual" - ], - "processing_method": "manual", - "checkout_id": null, - "source_name": "shopify_draft_order", - "fulfillment_status": "fulfilled", - "tax_lines": [], - "tags": "", - "contact_email": "andrew@wyatt.co.in", - "line_items": [ - { - "id": 4125768135, - "variant_id": 13917612359, - "title": "Shopify Test Item", - "quantity": 1, - "price": "499.00", - "grams": 0, - "sku": "", - "variant_title": "Roman BALCK 1", - "vendor": "Boa casa", - "fulfillment_service": "manual", - "product_id": 4059739527, - "requires_shipping": true, - "taxable": true, - "gift_card": false, - "name": "Roman BALCK 1", - "variant_inventory_management": "shopify", - "properties": [], - "product_exists": true, - "fulfillable_quantity": 0, - "total_discount": "0.00", - "fulfillment_status": "fulfilled", - "tax_lines": [] - }, - { - "id": 4125768199, - "variant_id": 13917612423, - "title": "Shopify Test Item", - "quantity": 1, - "price": "499.00", - "grams": 0, - "sku": "", - "variant_title": "Satin BLUE 1", - "vendor": "Boa casa", - "fulfillment_service": "manual", - "product_id": 4059739527, - "requires_shipping": true, - "taxable": true, - "gift_card": false, - "name": "Satin BLUE 1", - "variant_inventory_management": "shopify", - "properties": [], - "product_exists": true, - "fulfillable_quantity": 0, - "total_discount": "0.00", - "fulfillment_status": "fulfilled", - "tax_lines": [] - } - ], - "shipping_lines": [ - { - "id": 2108906247, - "title": "International Shipping", - "price": "20.00", - "code": "International Shipping", - "source": "shopify", - "phone": null, - "tax_lines": [] - } - ], - "billing_address": { - "first_name": "Andrew", - "address1": "B-11, Betahouse", - "phone": "145-112211", - "city": "Manhattan", - "zip": "10027", - "province": "New York", - "country": "United States", - "last_name": "Wyatt", - "address2": "Street 11, Sector 52", - "company": "Wyatt Inc.", - "latitude": 40.8138912, - "longitude": -73.96243270000001, - "name": "Andrew Wyatt", - "country_code": "US", - "province_code": "NY" - }, - "shipping_address": { - "first_name": "Andrew", - "address1": "B-11, Betahouse", - "phone": "145-112211", - "city": "Manhattan", - "zip": "10027", - "province": "New York", - "country": "United States", - "last_name": "Wyatt", - "address2": "Street 11, Sector 52", - "company": "Wyatt Inc.", - "latitude": 40.8138912, - "longitude": -73.96243270000001, - "name": "Andrew Wyatt", - "country_code": "US", - "province_code": "NY" - }, - "fulfillments": [ - { - "id": 1849629255, - "order_id": 2414345735, - "status": "success", - "created_at": "2016-01-20T17:27:15+05:30", - "service": "manual", - "updated_at": "2016-01-20T17:27:15+05:30", - "tracking_company": null, - "tracking_number": null, - "tracking_numbers": [], - "tracking_url": null, - "tracking_urls": [], - "receipt": {}, - "line_items": [ - { - "id": 4125768199, - "variant_id": 13917612423, - "title": "1001624/01", - "quantity": 1, - "price": "499.00", - "grams": 0, - "sku": "", - "variant_title": "Satin Silver", - "vendor": "Boa casa", - "fulfillment_service": "manual", - "product_id": 4059739527, - "requires_shipping": true, - "taxable": true, - "gift_card": false, - "name": "1001624/01 - Satin Silver", - "variant_inventory_management": "shopify", - "properties": [], - "product_exists": true, - "fulfillable_quantity": 0, - "total_discount": "0.00", - "fulfillment_status": "fulfilled", - "tax_lines": [] - } - ] - }, - { - "id": 1849628167, - "order_id": 2414345735, - "status": "success", - "created_at": "2016-01-20T17:26:58+05:30", - "service": "manual", - "updated_at": "2016-01-20T17:26:58+05:30", - "tracking_company": null, - "tracking_number": null, - "tracking_numbers": [], - "tracking_url": null, - "tracking_urls": [], - "receipt": {}, - "line_items": [ - { - "id": 4125768135, - "variant_id": 13917612359, - "title": "1001624/01", - "quantity": 1, - "price": "499.00", - "grams": 0, - "sku": "", - "variant_title": "Roman Bronze", - "vendor": "Boa casa", - "fulfillment_service": "manual", - "product_id": 4059739527, - "requires_shipping": true, - "taxable": true, - "gift_card": false, - "name": "1001624/01 - Roman Bronze", - "variant_inventory_management": "shopify", - "properties": [], - "product_exists": true, - "fulfillable_quantity": 0, - "total_discount": "0.00", - "fulfillment_status": "fulfilled", - "tax_lines": [] - } - ] - } - ], - "refunds": [], - "customer": { - "id": 2324518599, - "email": "andrew@wyatt.co.in", - "accepts_marketing": false, - "created_at": "2016-01-20T17:18:35+05:30", - "updated_at": "2016-01-20T17:26:39+05:30", - "first_name": "Andrew", - "last_name": "Wyatt", - "orders_count": 1, - "state": "disabled", - "total_spent": "1018.00", - "last_order_id": 2414345735, - "note": "", - "verified_email": true, - "multipass_identifier": null, - "tax_exempt": false, - "tags": "", - "last_order_name": "#1005", - "default_address": { - "id": 2476804295, - "first_name": "Andrew", - "last_name": "Wyatt", - "company": "Wyatt Inc.", - "address1": "B-11, Betahouse", - "address2": "Street 11, Sector 52", - "city": "Manhattan", - "province": "New York", - "country": "United States", - "zip": "10027", - "phone": "145-112211", - "name": "Andrew Wyatt", - "province_code": "NY", - "country_code": "US", - "country_name": "United States", - "default": true - } - } - } -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.js b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.js deleted file mode 100644 index b2f82d5a27d8..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Shopify Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Shopify Settings - () => frappe.tests.make('Shopify Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py deleted file mode 100644 index 6bec301b8e75..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_shopify_settings.py +++ /dev/null @@ -1,107 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals -import frappe - -import unittest, os, json -from frappe.utils import cstr, cint -from erpnext.erpnext_integrations.connectors.shopify_connection import create_order -from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import make_item -from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer -from frappe.core.doctype.data_import.data_import import import_doc - - -class ShopifySettings(unittest.TestCase): - @classmethod - def setUpClass(cls): - frappe.set_user("Administrator") - - cls.allow_negative_stock = cint(frappe.db.get_value('Stock Settings', None, 'allow_negative_stock')) - if not cls.allow_negative_stock: - frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1) - - # use the fixture data - import_doc(path=frappe.get_app_path("erpnext", "erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json")) - - frappe.reload_doctype("Customer") - frappe.reload_doctype("Sales Order") - frappe.reload_doctype("Delivery Note") - frappe.reload_doctype("Sales Invoice") - - cls.setup_shopify() - - @classmethod - def tearDownClass(cls): - if not cls.allow_negative_stock: - frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0) - - @classmethod - def setup_shopify(cls): - shopify_settings = frappe.get_doc("Shopify Settings") - shopify_settings.taxes = [] - - shopify_settings.update({ - "app_type": "Private", - "shopify_url": "test.myshopify.com", - "api_key": "17702c7c4452b9c5d235240b6e7a39da", - "password": "17702c7c4452b9c5d235240b6e7a39da", - "shared_secret": "17702c7c4452b9c5d235240b6e7a39da", - "price_list": "_Test Price List", - "warehouse": "_Test Warehouse - _TC", - "cash_bank_account": "Cash - _TC", - "account": "Cash - _TC", - "customer_group": "_Test Customer Group", - "cost_center": "Main - _TC", - "taxes": [ - { - "shopify_tax": "International Shipping", - "tax_account":"Legal Expenses - _TC" - } - ], - "enable_shopify": 0, - "sales_order_series": "SO-", - "sync_sales_invoice": 1, - "sales_invoice_series": "SINV-", - "sync_delivery_note": 1, - "delivery_note_series": "DN-" - }).save(ignore_permissions=True) - - cls.shopify_settings = shopify_settings - - def test_order(self): - # Create Customer - with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_customer.json")) as shopify_customer: - shopify_customer = json.load(shopify_customer) - create_customer(shopify_customer.get("customer"), self.shopify_settings) - - # Create Item - with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_item.json")) as shopify_item: - shopify_item = json.load(shopify_item) - make_item("_Test Warehouse - _TC", shopify_item.get("product")) - - # Create Order - with open (os.path.join(os.path.dirname(__file__), "test_data", "shopify_order.json")) as shopify_order: - shopify_order = json.load(shopify_order) - - create_order(shopify_order.get("order"), self.shopify_settings, False, company="_Test Company") - - sales_order = frappe.get_doc("Sales Order", {"shopify_order_id": cstr(shopify_order.get("order").get("id"))}) - - self.assertEqual(cstr(shopify_order.get("order").get("id")), sales_order.shopify_order_id) - - # Check for customer - shopify_order_customer_id = cstr(shopify_order.get("order").get("customer").get("id")) - sales_order_customer_id = frappe.get_value("Customer", sales_order.customer, "shopify_customer_id") - - self.assertEqual(shopify_order_customer_id, sales_order_customer_id) - - # Check sales invoice - sales_invoice = frappe.get_doc("Sales Invoice", {"shopify_order_id": sales_order.shopify_order_id}) - self.assertEqual(sales_invoice.rounded_total, sales_order.rounded_total) - - # Check delivery note - delivery_note_count = frappe.db.sql("""select count(*) from `tabDelivery Note` - where shopify_order_id = %s""", sales_order.shopify_order_id)[0][0] - - self.assertEqual(delivery_note_count, len(shopify_order.get("order").get("fulfillments"))) diff --git a/erpnext/erpnext_integrations/doctype/shopify_tax_account/__init__.py b/erpnext/erpnext_integrations/doctype/shopify_tax_account/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.json b/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.json deleted file mode 100644 index 63c674c5c4bf..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2015-10-05 16:55:20.455371", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "shopify_tax", - "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": "Shopify Tax/Shipping Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tax_account", - "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": "ERPNext 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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-04-09 11:36:49.272815", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Shopify Tax Account", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "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 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.py b/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.py deleted file mode 100644 index 74c13c0f6ccf..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_tax_account/shopify_tax_account.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, 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 ShopifyTaxAccount(Document): - pass diff --git a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/__init__.py b/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.json b/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.json deleted file mode 100644 index e47ecdcc508b..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-04-10 17:06:22.697427", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "webhook_id", - "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": "Webhook ID", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "method", - "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": "Method", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "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-04-11 12:43:09.456449", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Shopify Webhook Detail", - "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 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.py b/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.py deleted file mode 100644 index e127989ce34f..000000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_webhook_detail/shopify_webhook_detail.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, 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 ShopifyWebhookDetail(Document): - pass diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json index 4a5e54edd2f1..24b8e48ed6f5 100644 --- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json @@ -40,16 +40,6 @@ "onboard": 0, "type": "Link" }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Shopify Settings", - "link_to": "Shopify Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, { "hidden": 0, "is_query_report": 0, @@ -113,4 +103,4 @@ "pin_to_bottom": 0, "pin_to_top": 0, "shortcuts": [] -} \ No newline at end of file +} diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json index d258d5713189..d656b3c4fee9 100644 --- a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json +++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json @@ -30,16 +30,6 @@ "onboard": 0, "type": "Link" }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Shopify Settings", - "link_to": "Shopify Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, { "dependencies": "", "hidden": 0, @@ -79,4 +69,4 @@ "pin_to_bottom": 0, "pin_to_top": 0, "shortcuts": [] -} \ No newline at end of file +} diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 59b011d1a979..8e98675dab04 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -341,7 +341,6 @@ "erpnext.projects.doctype.project.project.hourly_reminder", "erpnext.projects.doctype.project.project.collect_project_status", "erpnext.hr.doctype.shift_type.shift_type.process_auto_attendance_for_all_shifts", - "erpnext.erpnext_integrations.connectors.shopify_connection.sync_old_orders", "erpnext.support.doctype.service_level_agreement.service_level_agreement.set_service_level_agreement_variance" ], "hourly_long": [ diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 744196a7c6b9..da96d95e4e28 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -45,7 +45,6 @@ erpnext.patches.v11_0.make_location_from_warehouse erpnext.patches.v11_0.make_asset_finance_book_against_old_entries erpnext.patches.v11_0.check_buying_selling_in_currency_exchange erpnext.patches.v11_0.move_item_defaults_to_child_table_for_multicompany #02-07-2018 #19-06-2019 -erpnext.patches.v11_0.refactor_erpnext_shopify #2018-09-07 erpnext.patches.v11_0.rename_overproduction_percent_field erpnext.patches.v11_0.update_backflush_subcontract_rm_based_on_bom erpnext.patches.v11_0.inter_state_field_for_gst @@ -143,7 +142,6 @@ erpnext.patches.v12_0.add_variant_of_in_item_attribute_table erpnext.patches.v12_0.rename_bank_account_field_in_journal_entry_account erpnext.patches.v12_0.create_default_energy_point_rules erpnext.patches.v12_0.set_produced_qty_field_in_sales_order_for_work_order -erpnext.patches.v12_0.set_default_shopify_app_type erpnext.patches.v12_0.set_cwip_and_delete_asset_settings erpnext.patches.v12_0.set_expense_account_in_landed_cost_voucher_taxes erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings @@ -244,7 +242,6 @@ erpnext.patches.v13_0.updates_for_multi_currency_payroll erpnext.patches.v13_0.update_reason_for_resignation_in_employee execute:frappe.delete_doc("Report", "Quoted Item Comparison") erpnext.patches.v13_0.update_member_email_address -erpnext.patches.v13_0.update_custom_fields_for_shopify erpnext.patches.v13_0.updates_for_multi_currency_payroll erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy erpnext.patches.v13_0.update_pos_closing_entry_in_merge_log @@ -299,3 +296,4 @@ 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.shopify_deprecation_warning \ No newline at end of file diff --git a/erpnext/patches/v11_0/refactor_erpnext_shopify.py b/erpnext/patches/v11_0/refactor_erpnext_shopify.py deleted file mode 100644 index 340e9fc8bf7b..000000000000 --- a/erpnext/patches/v11_0/refactor_erpnext_shopify.py +++ /dev/null @@ -1,43 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.installer import remove_from_installed_apps - -def execute(): - frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_settings') - frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_tax_account') - frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_log') - frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_webhook_detail') - - if 'erpnext_shopify' in frappe.get_installed_apps(): - remove_from_installed_apps('erpnext_shopify') - - frappe.delete_doc("Module Def", 'erpnext_shopify') - - frappe.db.commit() - - frappe.db.sql("truncate `tabShopify Log`") - - setup_app_type() - else: - disable_shopify() - -def setup_app_type(): - try: - shopify_settings = frappe.get_doc("Shopify Settings") - shopify_settings.app_type = 'Private' - shopify_settings.update_price_in_erpnext_price_list = 0 if getattr(shopify_settings, 'push_prices_to_shopify', None) else 1 - shopify_settings.flags.ignore_mandatory = True - shopify_settings.ignore_permissions = True - shopify_settings.save() - except Exception: - frappe.db.set_value("Shopify Settings", None, "enable_shopify", 0) - frappe.log_error(frappe.get_traceback()) - -def disable_shopify(): - # due to frappe.db.set_value wrongly written and enable_shopify being default 1 - # Shopify Settings isn't properly configured and leads to error - shopify = frappe.get_doc('Shopify Settings') - - if shopify.app_type == "Public" or shopify.app_type == None or \ - (shopify.enable_shopify and not (shopify.shopify_url or shopify.api_key)): - frappe.db.set_value("Shopify Settings", None, "enable_shopify", 0) diff --git a/erpnext/patches/v12_0/set_default_shopify_app_type.py b/erpnext/patches/v12_0/set_default_shopify_app_type.py deleted file mode 100644 index d040ea7f71c2..000000000000 --- a/erpnext/patches/v12_0/set_default_shopify_app_type.py +++ /dev/null @@ -1,6 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - frappe.reload_doc('erpnext_integrations', 'doctype', 'shopify_settings') - frappe.db.set_value('Shopify Settings', None, 'app_type', 'Private') \ 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 000000000000..245d1a962507 --- /dev/null +++ b/erpnext/patches/v13_0/shopify_deprecation_warning.py @@ -0,0 +1,10 @@ +import click + + +def execute(): + + 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", + ) diff --git a/erpnext/patches/v13_0/update_custom_fields_for_shopify.py b/erpnext/patches/v13_0/update_custom_fields_for_shopify.py deleted file mode 100644 index f1d2ea2d747c..000000000000 --- a/erpnext/patches/v13_0/update_custom_fields_for_shopify.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals -import frappe -from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import setup_custom_fields - -def execute(): - if frappe.db.get_single_value('Shopify Settings', 'enable_shopify'): - setup_custom_fields() From f2ee1155c0e398ec85094c1c7e27c047ba9e4a95 Mon Sep 17 00:00:00 2001 From: Anuja Date: Mon, 2 Aug 2021 23:13:21 +0530 Subject: [PATCH 413/691] fix: sider fixes --- erpnext/patches/v13_0/add_custom_field_for_south_africa.py | 2 +- .../regional/report/vat_audit_report/vat_audit_report.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py index 73e9af9e4b9b..f882fdedf383 100644 --- a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py +++ b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from erpnext.regional.south_africa.setup import make_custom_fields +from erpnext.regional.south_africa.setup import make_custom_fields def execute(): company = frappe.get_all('Company', filters = {'country': 'South Africa'}) 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 0c716cdab241..a9c3858bb13b 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -78,9 +78,9 @@ def get_invoice_items(self, doctype): 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, {}) \ - .setdefault("net_amount", 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))) + .setdefault("net_amount", 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, {}) \ .setdefault("is_zero_rated", d.is_zero_rated) From f99696b75d3819214b3849bdc29d43d16142e4ce Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 2 Aug 2021 23:15:44 +0530 Subject: [PATCH 414/691] 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 913e70b30c56..f475bc2fe2d1 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1101,16 +1101,16 @@ def set_payment_schedule(self): 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 415/691] 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 b6a806318928..9e56ad413069 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 416/691] 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 d7ad1fc69625..0d8109c41ad9 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 471f48f64db0f8da9d4703f4b82b6f9517fbacae Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Tue, 3 Aug 2021 14:39:38 +0530 Subject: [PATCH 417/691] fix: Reset weight_per_unit on replacing Item (#26619) * 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 --- 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 cf52803fca85..2ed7a04ba808 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 4e1a205c119012752e2eba60a7550b2ed36f4e95 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 3 Aug 2021 14:58:45 +0530 Subject: [PATCH 418/691] fix: Optimize item updation --- .../report/vat_audit_report/vat_audit_report.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) 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 a9c3858bb13b..f45ba01dea55 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -77,13 +77,10 @@ def get_invoice_items(self, doctype): """ % (doctype, ", ".join(["%s"]*len(self.invoices))), tuple(self.invoices), as_dict=1) 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, {}) \ - .setdefault("net_amount", 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, {}) \ - .setdefault("is_zero_rated", d.is_zero_rated) + self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code, { + 'net_amount': 0.0}) + self.invoice_items[d.parent][d.item_code]['net_amount'] += d.get('taxable_value', 0) or d.get('base_net_amount', 0) + self.invoice_items[d.parent][d.item_code]['is_zero_rated'] = d.is_zero_rated def get_items_based_on_tax_rate(self, doctype): self.items_based_on_tax_rate = frappe._dict() From 3a50490c04398c30663cab77f722153ec0b220e1 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 3 Aug 2021 15:57:11 +0530 Subject: [PATCH 419/691] fix: fetching of item tax from hsn code (#26736) * fix: fetching of item tax from hsn code --- 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 8e98675dab04..8f7c7db208ea 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -437,7 +437,8 @@ '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 fbe47d0532bd..88c350ac8997 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -859,4 +859,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 87bd9e61feb3..fd080fddd934 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 42cc67c5cd4f..614c53abb573 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -123,6 +123,7 @@ def validate(self): 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 000000000000..77ae51fa3418 --- /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 85815f989c6ab5fd5a89bf4d5ea71ffdb8c4eb0d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 3 Aug 2021 20:06:07 +0530 Subject: [PATCH 420/691] 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 cf52803fca85..2ed7a04ba808 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 421/691] 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 ebd9ae2dc54a..4e93fc679976 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 69812c7452ce..282b5d0afe4d 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -655,7 +655,7 @@ def set_required_items(self, reset_only_qty=False): 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 0012641b663d..ae01496f02af 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 000000000000..eae5ff60b90b --- /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 422/691] 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 4d553df08b86..c00dfa905667 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 035ce304fe3b62aaccf060be39baf45c5cbaadf6 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Wed, 4 Aug 2021 13:57:55 +0530 Subject: [PATCH 423/691] fix: POS payment modes displayed wrong total (#26708) --- 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 f1a166b5230d..63306adc6fa0 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 df477dcae6de059c7a05bcf16f81867469cd1ff1 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Wed, 4 Aug 2021 14:02:43 +0530 Subject: [PATCH 424/691] 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 500543ceb028..05a5366a5c1e 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 e5d8ba65ca1c982ff4a8db0352e81939ae64665f Mon Sep 17 00:00:00 2001 From: Mohammed Yusuf Shaikh <49878143+mohammedyusufshaikh@users.noreply.github.com> Date: Wed, 4 Aug 2021 16:35:38 +0530 Subject: [PATCH 425/691] fix: trigger lost reason dialog when status is changed to lost (#26811) --- 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 43e1b99f3ad3..e9a7a95fc7d4 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 13192e1db1606c2db289360d6e0de1c535875bc3 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 4 Aug 2021 17:07:22 +0530 Subject: [PATCH 426/691] 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 ac374a95f4ee..089a63fc1cd3 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 427/691] 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 474c9cf3df97..d7db27cb54cb 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 @@ def test_po_for_blocked_supplier_payments_past_date(self): 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 428/691] 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 3ad9909ad07e..026b85e26d25 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -162,8 +162,15 @@ def update_completed_qty(self, mr_items=None, update_modified=True): 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 2a9dcfb67ed0..f75cb5613853 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 429/691] 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 72a3a5e67c71..b4776ba24920 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 @@ def test_completed_qty_for_transfer(self): 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 005291e6dd4088fca23fa36b85f1755510939823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20de=20Ryckel?= Date: Wed, 4 Aug 2021 19:29:18 +0300 Subject: [PATCH 430/691] fix: typo in error message (#26816) --- 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 1be2fbf5c817..f763df0852b1 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -230,7 +230,7 @@ def convert_ledger_to_group(self): 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 bf8d0c256df51dd4495f8a0bbf80b8ce3b5253ab Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 4 Aug 2021 22:01:17 +0530 Subject: [PATCH 431/691] 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 1be2fbf5c817..f763df0852b1 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -230,7 +230,7 @@ def convert_ledger_to_group(self): 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 432/691] 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 28a9bddc420a..f7992797ed43 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1142,36 +1142,6 @@ def set_status(self, update=False, status=None, update_modified=True): 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 9a5064fcc713..fd6ac16fa314 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 @@ def test_service_item_purchase_with_perpetual_inventory(self): 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 2732490cbefd0e8af56ae4e473e6a2fb9c72b1a3 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 5 Aug 2021 00:30:01 +0530 Subject: [PATCH 433/691] fix: review changes --- erpnext/crm/doctype/lead/lead.js | 47 +++++--------------- erpnext/crm/doctype/lead/lead.json | 70 +++++++++++++----------------- erpnext/crm/doctype/lead/lead.py | 18 +++++--- 3 files changed, 54 insertions(+), 81 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index ad24a6e2225d..10e3f7da6be0 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -12,7 +12,8 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller 'Opportunity': this.make_opportunity }; - this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead); + // For avoiding integration issues. + this.frm.set_df_property('first_name', 'reqd', true); } onload () { @@ -84,42 +85,16 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller } render_basic_info_html() { - let html=''; - if (cur_frm.doc.lead_owner) { - html += `
                                - Lead Owner -
                                -
                                - ${cur_frm.doc.lead_owner} -
                                ` ; + if (cur_frm.doc.contact_date) { + let contact_date = frappe.datetime.obj_to_str(cur_frm.doc.contact_date) + let diff_days = frappe.datetime.get_day_diff(contact_date, frappe.datetime.get_today()); + let color = diff_days > 0 ? "orange" : "green"; + let message = diff_days > 0 ? __("Next Contact Date") : __("Last Contact Date"); + let html = `
                                + ${message} : ${contact_date} +
                                ` ; + cur_frm.dashboard.set_headline_alert(html); } - - if (cur_frm.doc.email_id) { - html += `
                                - Email -
                                -
                                - ${cur_frm.doc.email_id} -
                                ` ; - } - - if (cur_frm.doc.mobile_no) { - html += `
                                - Mobile -
                                -
                                - ${cur_frm.doc.mobile_no} -
                                ` ; - } - - html += `
                                - Status -
                                -
                                - ${cur_frm.doc.status} -
                                ` ; - html = `
                                ${html}
                                `; - cur_frm.dashboard.set_headline_alert(html); } }; diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index f9a500fa974a..542977e689b2 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -16,37 +16,36 @@ "middle_name", "last_name", "lead_name", - "email_id", - "mobile_no", - "phone", "col_break123", "status", "company_name", "designation", "gender", + "contact_details_section", + "email_id", + "mobile_no", + "whatsapp_no", + "column_break_16", + "phone", + "phone_ext", "additional_information_section", "no_of_employees", "industry", "market_segment", - "type", - "request_type", "column_break_22", - "whatsapp_no", "fax", "website", + "type", + "request_type", "address_section", "address_html", - "address_type", - "address_title", - "address_line1", - "address_line2", "city", + "pincode", "county", "column_break2", "contact_html", "state", "country", - "pincode", "section_break_12", "lead_owner", "ends_on", @@ -91,9 +90,9 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Full Name", - "mandatory_depends_on": "eval: !(doc.company_name)", "oldfieldname": "lead_name", "oldfieldtype": "Data", + "read_only": 1, "search_index": 1 }, { @@ -102,7 +101,7 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Organization Name", - "mandatory_depends_on": "eval: !(doc.lead_name)", + "mandatory_depends_on": "eval: !(doc.first_name)", "oldfieldname": "company_name", "oldfieldtype": "Data" }, @@ -241,23 +240,6 @@ "label": "Address HTML", "read_only": 1 }, - { - "description": "Home, Work, etc.", - "fieldname": "address_title", - "fieldtype": "Data", - "label": "Address Title" - }, - { - "fieldname": "address_line1", - "fieldtype": "Data", - "label": "Address Line 1", - "mandatory_depends_on": "eval: doc.address_title && doc.address_type" - }, - { - "fieldname": "address_line2", - "fieldtype": "Data", - "label": "Address Line 2" - }, { "fieldname": "city", "fieldtype": "Data", @@ -406,13 +388,6 @@ "label": "Designation", "options": "Designation" }, - { - "default": "Billing", - "fieldname": "address_type", - "fieldtype": "Select", - "label": "Address Type", - "options": "Billing\nShipping\nOffice\nPersonal\nPlant\nPostal\nShop\nSubsidiary\nWarehouse\nCurrent\nPermanent\nOther" - }, { "fieldname": "language", "fieldtype": "Link", @@ -422,7 +397,8 @@ { "fieldname": "first_name", "fieldtype": "Data", - "label": "First Name" + "label": "First Name", + "mandatory_depends_on": "eval: !(doc.company_name)" }, { "fieldname": "middle_name", @@ -457,7 +433,7 @@ }, { "collapsible": 1, - "depends_on": "eval:!doc.__islocal", + "depends_on": "eval: !doc.__islocal", "fieldname": "address_section", "fieldtype": "Section Break", "label": "Address" @@ -475,13 +451,27 @@ "fieldname": "other_information_section", "fieldtype": "Section Break", "label": "Other Information" + }, + { + "fieldname": "contact_details_section", + "fieldtype": "Section Break", + "label": "Contact Details" + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break" + }, + { + "fieldname": "phone_ext", + "fieldtype": "Data", + "label": "Phone Ext." } ], "icon": "fa fa-user", "idx": 5, "image_field": "image", "links": [], - "modified": "2021-07-28 00:20:37.768449", + "modified": "2021-08-04 00:24:57.208590", "modified_by": "Administrator", "module": "CRM", "name": "Lead", diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index f09a8145401c..33fda89f28ba 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -21,18 +21,24 @@ def onload(self): self.get("__onload").is_customer = customer load_address_and_contact(self) + def set_full_name(self): + self.lead_name = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) + def validate(self): + self.set_full_name() self.set_lead_name() self.set_title() + self.set_status() + self.check_email_id_is_unique() + self.validate_email_id() + self.validate_contact_date() self._prev = frappe._dict({ "contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if (not cint(self.is_new())) else None, "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, }) - - self.set_status() - self.check_email_id_is_unique() - + + def validate_email_id(self): if self.email_id: if not self.flags.ignore_email_validation: validate_email_address(self.email_id, throw=True) @@ -46,6 +52,7 @@ def validate(self): if self.is_new() or not self.image: self.image = has_gravatar(self.email_id) + def validate_contact_date(self): if self.contact_date and getdate(self.contact_date) < getdate(nowdate()): frappe.throw(_("Next Contact Date cannot be in the past")) @@ -88,7 +95,7 @@ def unlink_dynamic_links(self): linked_doc = frappe.get_doc(link['parenttype'], link['parent']) if len(linked_doc.get('links')) == 1: - linked_doc.delete() + linked_doc.delete(ignore_permissions=True) else: to_remove = None for d in linked_doc.get('links'): @@ -96,6 +103,7 @@ def unlink_dynamic_links(self): to_remove = d if to_remove: linked_doc.remove(to_remove) + linked_doc.save(ignore_permissions=True) def has_customer(self): return frappe.db.get_value("Customer", {"lead_name": self.name}) From 0b11420147066cd11616e474554dd474171e9078 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 00:35:45 +0530 Subject: [PATCH 434/691] 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 d7db27cb54cb..0db54e42068c 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 @@ def test_payment_terms_are_fetched_when_creating_purchase_invoice(self): # 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 f4a089bcef2d..5639ee8069ec 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 @@ def test_payment_terms_are_fetched_when_creating_sales_invoice(self): 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 ca8d8b99d31f..756825e826d2 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 @@ def test_payment_terms_are_fetched_when_creating_sales_invoice(self): 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 fd6ac16fa314..34ea3257d570 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 @@ def test_payment_terms_are_fetched_when_creating_purchase_invoice(self): # 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 435/691] 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 5639ee8069ec..a226da75cd8c 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 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 884d8cf0653794af68b377e2f02ac60968a7ead1 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Thu, 5 Aug 2021 11:14:46 +0530 Subject: [PATCH 436/691] fix: Let all System Managers be able to delete Company transactions (#26815) --- .../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 9313f955167a..23e59472a6d7 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 5b5a365aafead7d9d26fee0591765da41b85e581 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 5 Aug 2021 11:15:16 +0530 Subject: [PATCH 437/691] 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 f1a166b5230d..63306adc6fa0 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 c35a526dd826e91e764194ad45a1b6ac52b98b2a Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 5 Aug 2021 14:42:15 +0530 Subject: [PATCH 438/691] fix: adding test cases --- erpnext/crm/doctype/lead/lead.js | 6 ++-- erpnext/crm/doctype/lead/lead.py | 6 ++-- erpnext/crm/doctype/lead/test_lead.py | 49 +++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 10e3f7da6be0..3363d8c02324 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -43,7 +43,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller if (!this.frm.is_new()) { frappe.contacts.render_address_and_contact(this.frm); - cur_frm.trigger('render_basic_info_html'); + cur_frm.trigger('render_contact_day_html'); } else { frappe.contacts.clear_address_and_contact(this.frm); } @@ -84,14 +84,14 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller } } - render_basic_info_html() { + render_contact_day_html() { if (cur_frm.doc.contact_date) { let contact_date = frappe.datetime.obj_to_str(cur_frm.doc.contact_date) let diff_days = frappe.datetime.get_day_diff(contact_date, frappe.datetime.get_today()); let color = diff_days > 0 ? "orange" : "green"; let message = diff_days > 0 ? __("Next Contact Date") : __("Last Contact Date"); let html = `
                                - ${message} : ${contact_date} + ${message} : ${frappe.datetime.global_date_format(contact_date)}
                                ` ; cur_frm.dashboard.set_headline_alert(html); } diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 33fda89f28ba..49b682c12fd0 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -21,9 +21,6 @@ def onload(self): self.get("__onload").is_customer = customer load_address_and_contact(self) - def set_full_name(self): - self.lead_name = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) - def validate(self): self.set_full_name() self.set_lead_name() @@ -38,6 +35,9 @@ def validate(self): "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])) + def validate_email_id(self): if self.email_id: if not self.flags.ignore_email_validation: diff --git a/erpnext/crm/doctype/lead/test_lead.py b/erpnext/crm/doctype/lead/test_lead.py index d428a453f9fc..174b1c98bd4a 100644 --- a/erpnext/crm/doctype/lead/test_lead.py +++ b/erpnext/crm/doctype/lead/test_lead.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe +from frappe.utils import random_string import unittest test_records = frappe.get_test_records('Lead') @@ -32,3 +33,51 @@ def test_make_customer_from_organization(self): customer.company = "_Test Company" customer.customer_group = "_Test Customer Group" customer.insert() + + def test_create_lead_and_unlinking_dynamic_links(self): + lead_doc = make_lead(first_name = "Lorem", last_name="Ipsum") + lead_doc_1 = make_lead() + address = frappe.get_doc({ + "doctype": "Address", + "address_type": "Billing", + "city": "Mumbai", + "address_line1": "Vidya Vihar West", + "country": "India", + "links": [{ + "link_doctype": "Lead", + "link_name": lead_doc.name + }] + }).insert() + + address_1 = frappe.get_doc({ + "doctype": "Address", + "address_type": "Billing", + "address_line1": "Baner", + "city": "Pune", + "country": "India", + "links": [{ + "link_doctype": "Lead", + "link_name": lead_doc.name + }, + { + "link_doctype": "Lead", + "link_name": lead_doc_1.name + }] + }).insert() + + lead_doc.delete() + address_1.reload() + self.assertEqual(frappe.db.exists("Lead",lead_doc.name), None) + self.assertEqual(len(address_1.get('links')), 1) + +def make_lead(**args): + args = frappe._dict(args) + + lead_doc = frappe.get_doc({ + "doctype": "Lead", + "first_name": args.first_name or "Test", + "last_name": args.last_name or "Lead", + "email_id": args.email_id or "new_lead{}@example.com".format(random_string(5)), + }).insert() + + return lead_doc \ No newline at end of file From cf4078756dea4ac073af278e86510b2f94cee08b Mon Sep 17 00:00:00 2001 From: noahjacob Date: Tue, 25 May 2021 16:40:39 +0530 Subject: [PATCH 439/691] 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 6a024f275aa2..99b69263e05f 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -118,16 +118,20 @@ def get_so_items(self): 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 440/691] 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 99b69263e05f..efb2d9235fdf 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -116,21 +116,21 @@ def get_so_items(self): 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 441/691] 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 efb2d9235fdf..c2a365adb1d5 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -109,6 +109,15 @@ def get_so_mr_list(self, field, table): 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 @@ def get_so_items(self): 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 442/691] 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 c2a365adb1d5..8c27d6ccc964 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -131,15 +131,22 @@ def get_so_items(self): 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 1d90f7684e0fce8227fb566fa4110b96003d9ee5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Thu, 5 Aug 2021 19:17:32 +0530 Subject: [PATCH 443/691] fix: Do not fetch fully return issued purchase receipts (#26809) --- 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 7562418fd2ff..bda9f419a980 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 = class PurchaseInvoice extends erpnext.buying. }, 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 6e07ec26173ae3e18f155fcf6013bd01b035c074 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 5 Aug 2021 20:13:28 +0530 Subject: [PATCH 444/691] 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 dc9094c3e915..c588d45a9fd9 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 445/691] 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 f475bc2fe2d1..7f389cc32a33 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1175,10 +1175,7 @@ def fetch_payment_terms_from_order(self, po_or_so, po_or_so_doctype): '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 446/691] 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 7f389cc32a33..78e4ad31adde 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1174,9 +1174,14 @@ def fetch_payment_terms_from_order(self, po_or_so, po_or_so_doctype): '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 447/691] 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 b97dc401e6a3..19a394f74392 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 448/691] 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 19a394f74392..b97dc401e6a3 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 449/691] 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 9313f955167a..23e59472a6d7 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 450/691] 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 603e21ea2480..6c25f0024d5e 100644 --- a/erpnext/accounts/doctype/budget/test_budget.py +++ b/erpnext/accounts/doctype/budget/test_budget.py @@ -249,7 +249,7 @@ def test_monthly_budget_against_parent_group_cost_center(self): 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 451/691] 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 1ba752a14645..e6b6cc4a8a03 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -430,7 +430,8 @@ '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 04db9b3abece..ac195a6c762e 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 264baeaa4726..3ca98738cb9c 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 fbd30cf6c781..9bf4dbf2e859 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -123,6 +123,7 @@ def validate(self): 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 000000000000..77ae51fa3418 --- /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 e7fa2e582649edc141800645bc364bf3d74edf13 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 6 Aug 2021 11:03:57 +0530 Subject: [PATCH 452/691] fix(e-invoicing): cannot generate IRNs for standalone credit notes (#26824) --- 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 f8a230d0b87f..f4976138ac89 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') @@ -438,7 +434,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 cb44aed78b3e8a427bd8d1387664e9c3037745ce Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 5 Aug 2021 16:11:36 +0530 Subject: [PATCH 453/691] 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 93e6d7a97f43..af8de8ee0e36 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.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 @@ def test_get_warehouse_list_single(self): 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 4723e18f9e3dec0846fd13240dd7594095b7cbc4 Mon Sep 17 00:00:00 2001 From: Anupam Date: Thu, 5 Aug 2021 19:44:00 +0530 Subject: [PATCH 454/691] fix: sider issue --- erpnext/crm/doctype/lead/lead.js | 2 +- erpnext/crm/doctype/lead/test_lead.py | 20 +++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 3363d8c02324..97e6315eff55 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -86,7 +86,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller render_contact_day_html() { if (cur_frm.doc.contact_date) { - let contact_date = frappe.datetime.obj_to_str(cur_frm.doc.contact_date) + let contact_date = frappe.datetime.obj_to_str(cur_frm.doc.contact_date); let diff_days = frappe.datetime.get_day_diff(contact_date, frappe.datetime.get_today()); let color = diff_days > 0 ? "orange" : "green"; let message = diff_days > 0 ? __("Next Contact Date") : __("Last Contact Date"); diff --git a/erpnext/crm/doctype/lead/test_lead.py b/erpnext/crm/doctype/lead/test_lead.py index 174b1c98bd4a..18e0692c5e9b 100644 --- a/erpnext/crm/doctype/lead/test_lead.py +++ b/erpnext/crm/doctype/lead/test_lead.py @@ -37,7 +37,7 @@ def test_make_customer_from_organization(self): def test_create_lead_and_unlinking_dynamic_links(self): lead_doc = make_lead(first_name = "Lorem", last_name="Ipsum") lead_doc_1 = make_lead() - address = frappe.get_doc({ + frappe.get_doc({ "doctype": "Address", "address_type": "Billing", "city": "Mumbai", @@ -55,14 +55,16 @@ def test_create_lead_and_unlinking_dynamic_links(self): "address_line1": "Baner", "city": "Pune", "country": "India", - "links": [{ - "link_doctype": "Lead", - "link_name": lead_doc.name - }, - { - "link_doctype": "Lead", - "link_name": lead_doc_1.name - }] + "links": [ + { + "link_doctype": "Lead", + "link_name": lead_doc.name + }, + { + "link_doctype": "Lead", + "link_name": lead_doc_1.name + } + ] }).insert() lead_doc.delete() From 01a0585ba0bbfe68b650a1090edb20f6071bfaf0 Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 6 Aug 2021 11:47:07 +0530 Subject: [PATCH 455/691] fix: removing organization_lead traceback --- erpnext/crm/doctype/lead/lead.js | 2 +- erpnext/crm/doctype/lead/test_lead.py | 4 ++-- erpnext/crm/doctype/lead/test_records.json | 1 - erpnext/crm/doctype/lead/tests/test_lead_organization.js | 1 - erpnext/selling/doctype/customer/customer.py | 4 ++-- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 97e6315eff55..75af93799005 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -71,7 +71,7 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller } company_name () { - if (this.frm.doc.organization_lead && !this.frm.doc.lead_name) { + if (!this.frm.doc.lead_name) { this.frm.set_value("lead_name", this.frm.doc.company_name); } } diff --git a/erpnext/crm/doctype/lead/test_lead.py b/erpnext/crm/doctype/lead/test_lead.py index 18e0692c5e9b..cadc1a28a0e7 100644 --- a/erpnext/crm/doctype/lead/test_lead.py +++ b/erpnext/crm/doctype/lead/test_lead.py @@ -35,7 +35,7 @@ def test_make_customer_from_organization(self): customer.insert() def test_create_lead_and_unlinking_dynamic_links(self): - lead_doc = make_lead(first_name = "Lorem", last_name="Ipsum") + lead_doc = make_lead(first_name = "Lorem", last_name="Ipsum", email_id="lorem_ipsum@example.com") lead_doc_1 = make_lead() frappe.get_doc({ "doctype": "Address", @@ -79,7 +79,7 @@ def make_lead(**args): "doctype": "Lead", "first_name": args.first_name or "Test", "last_name": args.last_name or "Lead", - "email_id": args.email_id or "new_lead{}@example.com".format(random_string(5)), + "email_id": args.email_id or "new_lead_{}@example.com".format(random_string(5)), }).insert() return lead_doc \ No newline at end of file diff --git a/erpnext/crm/doctype/lead/test_records.json b/erpnext/crm/doctype/lead/test_records.json index 39864e2e3e22..3158add0f238 100644 --- a/erpnext/crm/doctype/lead/test_records.json +++ b/erpnext/crm/doctype/lead/test_records.json @@ -27,7 +27,6 @@ { "doctype": "Lead", "email_id": "test_lead4@example.com", - "organization_lead": 1, "lead_name": "_Test Lead 4", "company_name": "_Test Lead 4", "status": "Open" diff --git a/erpnext/crm/doctype/lead/tests/test_lead_organization.js b/erpnext/crm/doctype/lead/tests/test_lead_organization.js index 43959356b14e..7fb957370b0b 100644 --- a/erpnext/crm/doctype/lead/tests/test_lead_organization.js +++ b/erpnext/crm/doctype/lead/tests/test_lead_organization.js @@ -9,7 +9,6 @@ QUnit.test("test: lead", function (assert) { () => frappe.set_route("List", "Lead"), () => frappe.new_doc("Lead"), () => frappe.timeout(1), - () => cur_frm.set_value("organization_lead", "1"), () => cur_frm.set_value("company_name", lead_name), () => cur_frm.save(), () => frappe.timeout(1), diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 3b62081e24cc..66edcd018867 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -176,12 +176,12 @@ def create_lead_address_contact(self): address.append('links', dict(link_doctype='Customer', link_name=self.name)) address.save(ignore_permissions=self.flags.ignore_permissions) - lead = frappe.db.get_value("Lead", self.lead_name, ["organization_lead", "lead_name", "email_id", "phone", "mobile_no", "gender", "salutation"], as_dict=True) + lead = frappe.db.get_value("Lead", self.lead_name, ["company_name", "lead_name", "email_id", "phone", "mobile_no", "gender", "salutation"], as_dict=True) if not lead.lead_name: frappe.throw(_("Please mention the Lead Name in Lead {0}").format(self.lead_name)) - if lead.organization_lead: + if lead.company_name: contact_names = frappe.get_all('Dynamic Link', filters={ "parenttype":"Contact", "link_doctype":"Lead", From c8e6c070328d11e2c8db68c366c53f703f53388a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 6 Aug 2021 12:33:18 +0530 Subject: [PATCH 456/691] 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 cd85f6b026fe..e65442dbff4e 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 59c971015abf1e90452da2e8752d47b34ed76ee4 Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 6 Aug 2021 14:02:57 +0530 Subject: [PATCH 457/691] fix: test case --- erpnext/crm/doctype/lead/test_lead.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/lead/test_lead.py b/erpnext/crm/doctype/lead/test_lead.py index cadc1a28a0e7..d4886d35067c 100644 --- a/erpnext/crm/doctype/lead/test_lead.py +++ b/erpnext/crm/doctype/lead/test_lead.py @@ -77,7 +77,7 @@ def make_lead(**args): lead_doc = frappe.get_doc({ "doctype": "Lead", - "first_name": args.first_name or "Test", + "first_name": args.first_name or "_Test", "last_name": args.last_name or "Lead", "email_id": args.email_id or "new_lead_{}@example.com".format(random_string(5)), }).insert() From b4e720f8ecd8ad5966fb8c7f399c1347f11a84d2 Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 6 Aug 2021 14:39:13 +0530 Subject: [PATCH 458/691] fix: test case --- erpnext/crm/doctype/appointment/test_appointment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py index 50c98c59de6a..bcab7bd70814 100644 --- a/erpnext/crm/doctype/appointment/test_appointment.py +++ b/erpnext/crm/doctype/appointment/test_appointment.py @@ -9,7 +9,7 @@ def create_test_lead(): - test_lead = frappe.db.exists({'doctype': 'Lead', 'lead_name': 'Test Lead'}) + test_lead = frappe.db.exists({'doctype': 'Lead', 'email':'test@example.com'}) if test_lead: return frappe.get_doc('Lead', test_lead[0][0]) test_lead = frappe.get_doc({ From f1141e775642e501f8547d01050001d2e020aac9 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 6 Aug 2021 17:37:17 +0530 Subject: [PATCH 459/691] fix: failing budget test due to project naming (#26834) --- 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 603e21ea2480..6c25f0024d5e 100644 --- a/erpnext/accounts/doctype/budget/test_budget.py +++ b/erpnext/accounts/doctype/budget/test_budget.py @@ -249,7 +249,7 @@ def test_monthly_budget_against_parent_group_cost_center(self): 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 614336fe1d2f2d136809e29d394800994e7ae4c9 Mon Sep 17 00:00:00 2001 From: Ankush Date: Fri, 6 Aug 2021 19:36:21 +0530 Subject: [PATCH 460/691] test: use item that allows fractional UOM in test (#26837) --- .../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 82461cb843c2..bb4a710a0469 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 @@ def setUp(self): 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 @@ def test_reverse_purchase_receipt_sle(self): 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 9ea24db20a215e637ef497d1f4594474f8adde6a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 6 Aug 2021 21:14:40 +0530 Subject: [PATCH 461/691] 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 2eb8bfd5d2f1..ca6e61fe6b03 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 @@ def setUp(self): 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 @@ def test_reverse_purchase_receipt_sle(self): 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 27a29eb6bc67fc8dce482c0b38239929cf3fc6fb Mon Sep 17 00:00:00 2001 From: Ankush Date: Fri, 6 Aug 2021 21:34:44 +0530 Subject: [PATCH 462/691] test: fix pricelist tests (#26839) problem: exchange rate API is returning exchange rates for "_Test currency". These tests were relying on failure of that function. --- 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 cbd272df4b8e..a85a0222b55a 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -269,11 +269,14 @@ def test_batch_wise_item_price(self): 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 922049f1447e..7a9985d7f071 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -83,14 +83,17 @@ def test_get_item_details(self): 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 aec784640796a73261abf6d0511483a1b7031de9 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Fri, 6 Aug 2021 21:55:01 +0530 Subject: [PATCH 463/691] 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 cbd272df4b8e..a85a0222b55a 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -269,11 +269,14 @@ def test_batch_wise_item_price(self): 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 c7467a5a0f50..9ec44d2e2e41 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -83,14 +83,17 @@ def test_get_item_details(self): 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 464/691] 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 dc9094c3e915..299531d9f390 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 00ef7d5c184c..f1bf595a3949 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 e7dd6b8a6062..23e8091761cf 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 b97dc401e6a3..329f9a97b867 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -8,7 +8,7 @@ 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 f68d81909a3e..94684bff1cd2 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -447,10 +447,11 @@ def postprocess(source, target): 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 update_item(obj, target, source_parent): 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 78e4ad31adde..00f40cda2c69 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1091,6 +1091,8 @@ def set_payment_schedule(self): 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 @@ def set_payment_schedule(self): 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 6a550e0e975c..974ade358493 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -72,7 +72,8 @@ def set_missing_values(self, for_validate=False): # 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 a79eadc76193..54df0d63cba4 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 b05cc7875cf3..d071f010478e 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -598,6 +598,7 @@ def set_missing_values(source, target): 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 get_pending_qty(item_row): } }, 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 465/691] 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 405216bf6fe9..d967100806be 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1108,7 +1108,8 @@ def set_payment_schedule(self): 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 54df0d63cba4..4d432e3d5cc7 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 466/691] 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 94684bff1cd2..a0b1e073cc65 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -471,6 +471,7 @@ def update_item(obj, target, source_parent): "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 0db54e42068c..d06f9d202742 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 @@ def test_po_for_blocked_supplier_payments_past_date(self): 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 2b9d516e217b..bba54018aefe 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -670,6 +670,7 @@ def update_item(source, target, source_parent): "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 467/691] 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 d06f9d202742..d668c76b6b9a 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 @@ def test_purchase_order_on_hold(self): 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 @@ def test_make_purchase_invoice_with_terms(self): 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 @@ def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked( 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 @@ def test_payment_terms_are_fetched_when_creating_purchase_invoice(self): # 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 07e65ab5895f166b6dceb38dfbea1cb90f6015b9 Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Mon, 9 Aug 2021 07:02:31 +0200 Subject: [PATCH 468/691] feat: add french address template (#26316) * add french address template Co-authored-by: Ankush --- 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 000000000000..752331eeec95 --- /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 16d4de5130097bc2dfdc7e073f1e13f0a22481d1 Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 9 Aug 2021 10:41:24 +0530 Subject: [PATCH 469/691] fix: price list with 0 value are ignored (#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 --- 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 4e93fc679976..0ba85078ead8 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 2ed7a04ba808..be8508a000bf 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 7e0c57fa3fe62417ad3be75412e0c031d6486bb8 Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 9 Aug 2021 10:46:29 +0530 Subject: [PATCH 470/691] fix: allow alternative items when using job card (#26724) --- 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 69c7f5c614b8..66e2394b847c 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -608,6 +608,11 @@ def set_missing_values(source, target): 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 set_missing_values(source, target): } }, target_doc, set_missing_values) - return doclist \ No newline at end of file + return doclist From bba9aac9c0fd3d5c047ec6eccca098654e240bb0 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 9 Aug 2021 10:58:39 +0530 Subject: [PATCH 471/691] 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 000000000000..752331eeec95 --- /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 472/691] 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 69c7f5c614b8..66e2394b847c 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -608,6 +608,11 @@ def set_missing_values(source, target): 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 set_missing_values(source, target): } }, 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 473/691] 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 4e93fc679976..0ba85078ead8 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 2ed7a04ba808..be8508a000bf 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 ab8f0cab4d0a934c200665a6916ecb7de032519b Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 9 Aug 2021 12:24:04 +0530 Subject: [PATCH 474/691] 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 | 183 ++++++++++++++---- 2 files changed, 183 insertions(+), 35 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 32b08f60c4a5..128a2ab62ffd 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.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 @@ def test_multi_currency_lcv(self): 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 899d7e8e6666..bcf605288f64 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -290,8 +290,16 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): 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 @@ -304,9 +312,17 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): 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) # check if the exchange rate has changed if d.get('purchase_invoice'): @@ -317,13 +333,29 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): discrepancy_caused_by_exchange_rate_difference = (d.qty * d.net_rate) * \ (exchange_rate_map[d.purchase_invoice] - self.conversion_rate) - self.add_gl_entry(gl_entries, account, d.cost_center, 0.0, discrepancy_caused_by_exchange_rate_difference, - remarks, self.supplier, debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, - account_currency=credit_currency, item=d) - - self.add_gl_entry(gl_entries, self.get_company_default("exchange_gain_loss_account"), d.cost_center, discrepancy_caused_by_exchange_rate_difference, 0.0, - remarks, self.supplier, debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, - account_currency=credit_currency, item=d) + self.add_gl_entry( + gl_entries=gl_entries, + account=account, + cost_center=d.cost_center, + debit=0.0, + credit=discrepancy_caused_by_exchange_rate_difference, + remarks=remarks, + against_account=self.supplier, + debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, + account_currency=credit_currency, + item=d) + + self.add_gl_entry( + gl_entries=gl_entries, + account=self.get_company_default("exchange_gain_loss_account"), + cost_center=d.cost_center, + debit=discrepancy_caused_by_exchange_rate_difference, + credit=0.0, + remarks=remarks, + against_account=self.supplier, + debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, + account_currency=credit_currency, + item=d) # Amount added through landed-cos-voucher if d.landed_cost_voucher_amount and landed_cost_entries: @@ -332,14 +364,31 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): 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")) + \ @@ -356,8 +405,17 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): 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: @@ -368,12 +426,30 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): 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" + @@ -423,8 +499,15 @@ def make_tax_gl_entries(self, gl_entries): 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 @@ -477,15 +560,31 @@ def add_asset_gl_entries(self, item, gl_entries): # 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") @@ -498,11 +597,27 @@ def add_lcv_gl_entries(self, item, gl_entries): 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, 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=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=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 a8166c06c7e471cfcf67d4ec6cacdc2d67e12485 Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 9 Aug 2021 12:24:04 +0530 Subject: [PATCH 475/691] 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 | 153 ++++++++++++++---- 2 files changed, 160 insertions(+), 28 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 32b08f60c4a5..128a2ab62ffd 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.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 @@ def test_multi_currency_lcv(self): 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 26ea11e01d94..96e14ef759a1 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -286,8 +286,16 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): 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 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): 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 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): 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 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): 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 @@ def make_item_gl_entries(self, gl_entries, warehouse_account=None): 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 @@ def make_tax_gl_entries(self, gl_entries): 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 @@ def add_asset_gl_entries(self, item, gl_entries): # 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 @@ def add_lcv_gl_entries(self, item, gl_entries): 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, 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=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=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 ef8539fd606b1fc00b0d5b3ea124b11df765e32e Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Mon, 9 Aug 2021 12:38:14 +0530 Subject: [PATCH 476/691] refactor: Selling Settings form cleanup (#26841) --- .../selling_settings/selling_settings.json | 61 +++++++++++++++---- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json index f01934b7e6b4..717fd9b92e30 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.json +++ b/erpnext/selling/doctype/selling_settings/selling_settings.json @@ -6,24 +6,31 @@ "document_type": "Other", "engine": "InnoDB", "field_order": [ + "customer_defaults_section", "cust_master_name", - "campaign_naming_by", "customer_group", + "column_break_4", "territory", - "selling_price_list", - "close_opportunity_after_days", + "crm_settings_section", + "campaign_naming_by", "default_valid_till", - "column_break_5", + "column_break_9", + "close_opportunity_after_days", + "item_price_settings_section", + "selling_price_list", + "column_break_15", + "maintain_same_sales_rate", + "maintain_same_rate_action", + "editable_price_list_rate", + "validate_selling_price", + "sales_transactions_settings_section", "so_required", "dn_required", "sales_update_frequency", - "maintain_same_sales_rate", - "maintain_same_rate_action", + "column_break_5", "role_to_override_stop_action", - "editable_price_list_rate", "allow_multiple_items", "allow_against_multiple_purchase_orders", - "validate_selling_price", "hide_tax_id" ], "fields": [ @@ -116,7 +123,7 @@ "default": "0", "fieldname": "allow_multiple_items", "fieldtype": "Check", - "label": "Allow Item to Be Added Multiple Times in a Transaction" + "label": "Allow Item to be Added Multiple Times in a Transaction" }, { "default": "0", @@ -142,7 +149,7 @@ "description": "Configure the action to stop the transaction or just warn if the same rate is not maintained.", "fieldname": "maintain_same_rate_action", "fieldtype": "Select", - "label": "Action If Same Rate is Not Maintained", + "label": "Action if Same Rate is Not Maintained", "mandatory_depends_on": "maintain_same_sales_rate", "options": "Stop\nWarn" }, @@ -152,6 +159,38 @@ "fieldtype": "Link", "label": "Role Allowed to Override Stop Action", "options": "Role" + }, + { + "fieldname": "customer_defaults_section", + "fieldtype": "Section Break", + "label": "Customer Defaults" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "crm_settings_section", + "fieldtype": "Section Break", + "label": "CRM Settings" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "item_price_settings_section", + "fieldtype": "Section Break", + "label": "Item Price Settings" + }, + { + "fieldname": "column_break_15", + "fieldtype": "Column Break" + }, + { + "fieldname": "sales_transactions_settings_section", + "fieldtype": "Section Break", + "label": "Transaction Settings" } ], "icon": "fa fa-cog", @@ -159,7 +198,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-04-04 20:18:12.814624", + "modified": "2021-08-06 22:25:50.119458", "modified_by": "Administrator", "module": "Selling", "name": "Selling Settings", From 07337d5c78c14119ae9fc5d010080a1db61d0bdd Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 9 Aug 2021 12:38:40 +0530 Subject: [PATCH 477/691] fix: validate python expressions (#26835) --- 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 428989aa9655..0be41b40635c 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 9b1a47eed6c7..5de45cbcad98 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 b0f3c0f3fd86ed6d75e0234075402362b1941681 Mon Sep 17 00:00:00 2001 From: harshpwctech <84438948+harshpwctech@users.noreply.github.com> Date: Mon, 9 Aug 2021 13:42:05 +0530 Subject: [PATCH 478/691] fix: JWT decoding error (#26624) --- erpnext/regional/india/e_invoice/utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index f4976138ac89..4276946ebb3b 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -928,7 +928,7 @@ def raise_error(self, raise_exception=False, errors=[]): def set_einvoice_data(self, res): enc_signed_invoice = res.get('SignedInvoice') - dec_signed_invoice = jwt.decode(enc_signed_invoice, verify=False)['data'] + dec_signed_invoice = jwt.decode(enc_signed_invoice, options={"verify_signature": False})['data'] self.invoice.irn = res.get('Irn') self.invoice.ewaybill = res.get('EwbNo') @@ -1126,4 +1126,4 @@ def check_scheduler_status(): def job_already_enqueued(job_name): enqueued_jobs = [d.get("job_name") for d in get_info()] if job_name in enqueued_jobs: - return True \ No newline at end of file + return True From 9c04c297b8906978c5931223f1f56c75246e7709 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Mon, 9 Aug 2021 14:01:02 +0530 Subject: [PATCH 479/691] refactor: Accounts Settings form cleanup (#26842) --- .../doctype/accounts_settings/accounts_settings.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 703e93c0757c..7bcc6ee53b3b 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -10,6 +10,7 @@ "accounts_transactions_settings_section", "over_billing_allowance", "role_allowed_to_over_bill", + "credit_controller", "make_payment_via_journal_entry", "column_break_11", "check_supplier_invoice_uniqueness", @@ -27,7 +28,6 @@ "acc_frozen_upto", "frozen_accounts_modifier", "column_break_4", - "credit_controller", "deferred_accounting_settings_section", "book_deferred_entries_based_on", "column_break_18", @@ -73,11 +73,10 @@ "fieldtype": "Column Break" }, { - "description": "This role is allowed to submit transactions that exceed credit limits", "fieldname": "credit_controller", "fieldtype": "Link", "in_list_view": 1, - "label": "Credit Controller", + "label": "Role allowed to bypass Credit Limit", "options": "Role" }, { @@ -268,7 +267,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-06-17 20:26:03.721202", + "modified": "2021-08-09 13:08:01.335416", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", From b3bbebd27c73827d5a88ff47d0d16fb73dcf6de2 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Mon, 9 Aug 2021 14:39:32 +0530 Subject: [PATCH 480/691] fix: add parameter for db insert while adding item tax (#26855) --- 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 bf4ab1a848fa..9e2e5968c6db 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 ea83e2b45fb4219bf643e844ba85d682b6c44556 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 9 Aug 2021 14:48:59 +0530 Subject: [PATCH 481/691] 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 428989aa9655..0be41b40635c 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 9b1a47eed6c7..5de45cbcad98 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 42bb77bf80a494e6ed4fae35e1e283dbadf2c581 Mon Sep 17 00:00:00 2001 From: Anupam Date: Mon, 9 Aug 2021 15:11:13 +0530 Subject: [PATCH 482/691] fix: creating contact on creation of lead --- erpnext/crm/doctype/lead/lead.py | 58 +++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 49b682c12fd0..7f028cb31604 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -63,6 +63,22 @@ def validate_contact_date(self): def on_update(self): self.add_calendar_event() + def before_insert(self): + self.contact_doc = self.create_contact() + + def after_insert(self): + self.update_links() + + def update_links(self): + # update contact links + if self.contact_doc: + self.contact_doc.append("links", { + "link_doctype": "Lead", + "link_name": self.name, + "link_title": self.lead_name + }) + self.contact_doc.save() + def add_calendar_event(self, opts=None, force=False): super(Lead, self).add_calendar_event({ "owner": self.lead_owner, @@ -116,7 +132,6 @@ def has_quotation(self): "party_name": self.name, "docstatus": 1, "status": ["!=", "Lost"] - }) def has_lost_quotation(self): @@ -137,10 +152,43 @@ def set_lead_name(self): self.lead_name = self.email_id.split("@")[0] def set_title(self): - if self.company_name: - self.title = self.company_name - else: - self.title = self.lead_name + self.title = self.company_name or self.lead_name + + def create_contact(self): + if not self.lead_name: + self.set_full_name() + self.set_lead_name() + + contact = frappe.new_doc("Contact") + contact.update({ + "first_name": self.first_name or self.lead_name, + "last_name": self.last_name, + "salutation": self.salutation, + "gender": self.gender, + "designation": self.designation, + }) + + if self.email_id: + contact.append("email_ids", { + "email_id": self.email_id, + "is_primary": 1 + }) + + if self.phone: + contact.append("phone_nos", { + "phone": self.phone, + "is_primary_phone": 1 + }) + + if self.mobile_no: + contact.append("phone_nos", { + "phone": self.mobile_no, + "is_primary_mobile_no":1 + }) + + contact.insert(ignore_permissions=True) + + return contact @frappe.whitelist() def make_customer(source_name, target_doc=None): From 7ba8c821a1659185df524c466fd8906d44649876 Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 9 Aug 2021 15:14:26 +0530 Subject: [PATCH 483/691] test: fix flaky purchase receipt test (#26859) --- .../purchase_receipt/test_purchase_receipt.py | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index bb4a710a0469..d40d78184d50 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -324,21 +324,8 @@ def test_subcontracting_over_receipt(self): pr1.submit() self.assertRaises(frappe.ValidationError, pr2.submit) + frappe.db.rollback() - pr1.cancel() - se.cancel() - se1.cancel() - se2.cancel() - se3.cancel() - po.reload() - pr2.load_from_db() - - if pr2.docstatus == 1 and frappe.db.get_value('Stock Ledger Entry', - {'voucher_no': pr2.name, 'is_cancelled': 0}, 'name'): - pr2.cancel() - - po.load_from_db() - po.cancel() def test_serial_no_supplier(self): pr = make_purchase_receipt(item_code="_Test Serialized Item With Series", qty=1) From db3ee314138ee40653c562d89d5aff0764a03ad7 Mon Sep 17 00:00:00 2001 From: Anupam Date: Mon, 9 Aug 2021 15:34:50 +0530 Subject: [PATCH 484/691] fix: test case --- erpnext/crm/doctype/appointment/test_appointment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py index bcab7bd70814..c7563e9d15be 100644 --- a/erpnext/crm/doctype/appointment/test_appointment.py +++ b/erpnext/crm/doctype/appointment/test_appointment.py @@ -9,7 +9,7 @@ def create_test_lead(): - test_lead = frappe.db.exists({'doctype': 'Lead', 'email':'test@example.com'}) + test_lead = frappe.db.exists({'doctype': 'Lead', 'email_id':'test@example.com'}) if test_lead: return frappe.get_doc('Lead', test_lead[0][0]) test_lead = frappe.get_doc({ From 9f5111809d2264c555b2990a2dbe4340dc2fbad5 Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 9 Aug 2021 15:35:26 +0530 Subject: [PATCH 485/691] 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 ca6e61fe6b03..b4abeff94f1b 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 @@ def test_subcontracting_over_receipt(self): 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 @@ def test_service_item_purchase_with_perpetual_inventory(self): '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 458f7f119416d380a63fe69e8a2d74b25234c468 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Mon, 9 Aug 2021 17:16:48 +0530 Subject: [PATCH 486/691] patch: delete all orphaned tables docs (#26743) --- 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 04f05eda13de..30486242c1b1 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.add_custom_field_for_south_africa 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 000000000000..1d6eebe0398f --- /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 0ff9ef673c0ec99667f6ade2e3de41bf61224ad0 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 9 Aug 2021 17:58:18 +0530 Subject: [PATCH 487/691] 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 a9b7efbe980b..498f3cf0e97e 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 08ae49cd118bf24beff9908c162ae2abcb544337 Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Mon, 9 Aug 2021 17:58:52 +0530 Subject: [PATCH 488/691] fix(regional): add permissions for VAT Audit report (#26851) fix(regional): add permissions for VAT Audit report --- erpnext/patches.txt | 2 +- .../add_custom_field_for_south_africa.py | 3 ++- .../vat_audit_report/vat_audit_report.json | 12 +--------- .../vat_audit_report/vat_audit_report.py | 22 ++++++++++++++++--- erpnext/regional/south_africa/setup.py | 20 ++++++++++++++--- 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 30486242c1b1..35b248c08ed0 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -297,5 +297,5 @@ 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.add_custom_field_for_south_africa +erpnext.patches.v13_0.add_custom_field_for_south_africa #2 erpnext.patches.v13_0.shopify_deprecation_warning diff --git a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py index f882fdedf383..73ff1cad5b62 100644 --- a/erpnext/patches/v13_0/add_custom_field_for_south_africa.py +++ b/erpnext/patches/v13_0/add_custom_field_for_south_africa.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals import frappe -from erpnext.regional.south_africa.setup import make_custom_fields +from erpnext.regional.south_africa.setup import make_custom_fields, add_permissions def execute(): company = frappe.get_all('Company', filters = {'country': 'South Africa'}) @@ -11,3 +11,4 @@ def execute(): return make_custom_fields() + add_permissions() diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.json b/erpnext/regional/report/vat_audit_report/vat_audit_report.json index 8917e8f3c7e1..a8be7bf64c09 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.json +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.json @@ -18,15 +18,5 @@ "ref_doctype": "GL Entry", "report_name": "VAT Audit Report", "report_type": "Script Report", - "roles": [ - { - "role": "Accounts User" - }, - { - "role": "Accounts Manager" - }, - { - "role": "Auditor" - } - ] + "roles": [] } \ 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 f45ba01dea55..292605ef13d4 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.py +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.py @@ -189,6 +189,8 @@ def get_consolidated_data(self, doctype): 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"] = inv_data.get("party") row["remarks"] = inv_data.get("remarks") row["gross_amount"]= item_details[0].get("gross_amount") row["tax_amount"]= item_details[0].get("tax_amount") @@ -226,6 +228,20 @@ def get_columns(self): "options": "voucher_type", "width": 150 }, + { + "fieldname": "party_type", + "label": "Party Type", + "fieldtype": "Data", + "width": 140, + "hidden": 1 + }, + { + "fieldname": "party", + "label": "Party", + "fieldtype": "Dynamic Link", + "options": "party_type", + "width": 150 + }, { "fieldname": "remarks", "label": "Details", @@ -236,18 +252,18 @@ def get_columns(self): "fieldname": "net_amount", "label": "Net Amount", "fieldtype": "Currency", - "width": 150 + "width": 130 }, { "fieldname": "tax_amount", "label": "Tax Amount", "fieldtype": "Currency", - "width": 150 + "width": 130 }, { "fieldname": "gross_amount", "label": "Gross Amount", "fieldtype": "Currency", - "width": 150 + "width": 130 }, ] diff --git a/erpnext/regional/south_africa/setup.py b/erpnext/regional/south_africa/setup.py index ac783b848848..4657ff833ddf 100644 --- a/erpnext/regional/south_africa/setup.py +++ b/erpnext/regional/south_africa/setup.py @@ -3,11 +3,12 @@ from __future__ import unicode_literals -# import frappe, os, json +import frappe from frappe.custom.doctype.custom_field.custom_field import create_custom_fields from frappe.permissions import add_permission, update_permission_property def setup(company=None, patch=True): + make_custom_fields() add_permissions() def make_custom_fields(update=True): @@ -27,10 +28,23 @@ def make_custom_fields(update=True): create_custom_fields(custom_fields, update=update) def add_permissions(): - """Add Permissions for South Africa VAT Settings and South Africa VAT Account""" + """Add Permissions for South Africa VAT Settings and South Africa VAT Account + and VAT Audit Report""" for doctype in ('South Africa VAT Settings', 'South Africa VAT Account'): add_permission(doctype, 'All', 0) for role in ('Accounts Manager', 'Accounts User', 'System Manager'): add_permission(doctype, role, 0) update_permission_property(doctype, role, 0, 'write', 1) - update_permission_property(doctype, role, 0, 'create', 1) \ No newline at end of file + 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( + doctype='Custom Role', + report="VAT Audit Report", + roles= [ + dict(role='Accounts User'), + dict(role='Accounts Manager'), + dict(role='Auditor') + ] + )).insert() \ No newline at end of file From 18bd182f615ab8894ab505184ad9f3e604913977 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 9 Aug 2021 18:34:51 +0530 Subject: [PATCH 489/691] 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 ae01496f02af..ada3badd7c7e 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 000000000000..1d6eebe0398f --- /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 54d1336d113de3962db6e6e55e0fa4225c84d924 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Mon, 2 Aug 2021 23:15:44 +0530 Subject: [PATCH 490/691] 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 c793c19a92ec..5f3d3ce1e636 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1106,16 +1106,16 @@ def set_payment_schedule(self): 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 7244afe129b836b54af2a2376694bdbd705ac83a Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Wed, 4 Aug 2021 03:15:13 +0530 Subject: [PATCH 491/691] 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 474c9cf3df97..d7db27cb54cb 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 @@ def test_po_for_blocked_supplier_payments_past_date(self): 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 1200872c7e83bbe2c518d2642447539837534c69 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 00:35:45 +0530 Subject: [PATCH 492/691] 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 d7db27cb54cb..0db54e42068c 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 @@ def test_payment_terms_are_fetched_when_creating_purchase_invoice(self): # 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 f4a089bcef2d..5639ee8069ec 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 @@ def test_payment_terms_are_fetched_when_creating_sales_invoice(self): 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 ca8d8b99d31f..756825e826d2 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 @@ def test_payment_terms_are_fetched_when_creating_sales_invoice(self): 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 ee7fe4c9bd22..0ae7e125e467 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1107,6 +1107,8 @@ def test_payment_terms_are_fetched_when_creating_purchase_invoice(self): # 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 55cb82c74d4f536891e862f452971f9269e0af15 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 00:52:55 +0530 Subject: [PATCH 493/691] 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 5639ee8069ec..a226da75cd8c 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 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 0588382c38642bf5240deca16ef82dca351608a2 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 21:42:09 +0530 Subject: [PATCH 494/691] 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 5f3d3ce1e636..35ebebc84cf0 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1180,10 +1180,7 @@ def fetch_payment_terms_from_order(self, po_or_so, po_or_so_doctype): '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 5b33e75c6564f7afe02a44628b6f44f478af2aac Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 21:50:09 +0530 Subject: [PATCH 495/691] 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 35ebebc84cf0..b0e24606c666 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1179,9 +1179,14 @@ def fetch_payment_terms_from_order(self, po_or_so, po_or_so_doctype): '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 e247e3a4b282f5dab116276827be60dcc0092b2d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 22:04:11 +0530 Subject: [PATCH 496/691] 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 b97dc401e6a3..19a394f74392 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 072f63b32434672ef97dbd6ba7f2e3b3a3138933 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Thu, 5 Aug 2021 23:04:58 +0530 Subject: [PATCH 497/691] 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 19a394f74392..b97dc401e6a3 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 bcf56e64ba41af8000a2371cb37bff12c3dc702e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 6 Aug 2021 23:53:16 +0530 Subject: [PATCH 498/691] 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 | 727 +++++++++++++----- 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, 1057 insertions(+), 361 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 87ab31f0d5d4..c953d5ce0cf2 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 = class PurchaseInvoice extends erpnext.buying. // 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 = class PurchaseInvoice extends erpnext.buying. 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 00ef7d5c184c..f1bf595a3949 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 0a9a105b7cac..f5ed87d02a7a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -127,6 +127,7 @@ "get_advances", "advances", "payment_schedule_section", + "ignore_default_payment_terms_template", "payment_terms_template", "payment_schedule", "payments_section", @@ -197,7 +198,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, @@ -209,7 +212,9 @@ "hide_seconds": 1, "label": "Title", "no_copy": 1, - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -224,7 +229,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, @@ -238,7 +245,9 @@ "oldfieldtype": "Link", "options": "Customer", "print_hide": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -252,7 +261,9 @@ "label": "Customer Name", "oldfieldname": "customer_name", "oldfieldtype": "Data", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_id", @@ -261,7 +272,9 @@ "hide_seconds": 1, "label": "Tax Id", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "project", @@ -273,7 +286,9 @@ "oldfieldname": "project_name", "oldfieldtype": "Link", "options": "Project", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -284,7 +299,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", @@ -294,7 +311,9 @@ "hide_seconds": 1, "label": "POS Profile", "options": "POS Profile", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -304,14 +323,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", @@ -325,7 +348,9 @@ "options": "Company", "print_hide": 1, "remember_last_selected_value": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "cost_center", @@ -333,7 +358,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Cost Center", - "options": "Cost Center" + "options": "Cost Center", + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -347,7 +374,9 @@ "oldfieldname": "posting_date", "oldfieldtype": "Date", "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "posting_time", @@ -358,7 +387,9 @@ "no_copy": 1, "oldfieldname": "posting_time", "oldfieldtype": "Time", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -368,7 +399,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", @@ -378,7 +411,9 @@ "label": "Payment Due Date", "no_copy": 1, "oldfieldname": "due_date", - "oldfieldtype": "Date" + "oldfieldtype": "Date", + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "amended_from", @@ -392,7 +427,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", @@ -405,7 +442,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", @@ -414,7 +453,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, @@ -423,7 +464,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, @@ -433,13 +476,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, @@ -447,7 +494,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, @@ -455,7 +504,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", @@ -464,7 +515,9 @@ "hide_seconds": 1, "label": "Customer Address", "options": "Address", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "address_display", @@ -472,7 +525,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Address", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_person", @@ -482,7 +537,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", @@ -490,7 +547,9 @@ "hide_days": 1, "hide_seconds": 1, "label": "Contact", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "contact_mobile", @@ -499,7 +558,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", @@ -510,7 +571,9 @@ "label": "Contact Email", "options": "Email", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "territory", @@ -519,13 +582,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", @@ -534,7 +601,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", @@ -543,7 +612,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", @@ -552,7 +623,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", @@ -562,7 +635,9 @@ "hide_seconds": 1, "label": "Company Address", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -571,7 +646,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", @@ -583,7 +660,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", @@ -596,13 +675,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%" }, { @@ -615,7 +698,9 @@ "oldfieldtype": "Select", "options": "Price List", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "price_list_currency", @@ -626,7 +711,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", @@ -637,7 +724,9 @@ "label": "Price List Exchange Rate", "precision": "9", "print_hide": 1, - "reqd": 1 + "reqd": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -648,14 +737,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", @@ -665,7 +758,9 @@ "hide_seconds": 1, "label": "Source Warehouse", "options": "Warehouse", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "items_section", @@ -674,7 +769,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", @@ -685,14 +782,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, @@ -704,14 +805,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", @@ -720,7 +825,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", @@ -729,7 +836,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", @@ -738,7 +847,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", @@ -746,7 +857,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, @@ -756,7 +869,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", @@ -765,7 +880,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", @@ -776,13 +893,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", @@ -790,7 +911,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", @@ -800,7 +923,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", @@ -813,13 +938,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", @@ -828,7 +957,9 @@ "hide_seconds": 1, "label": "Total", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "net_total", @@ -838,7 +969,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", @@ -847,7 +980,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", @@ -855,7 +990,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", @@ -866,13 +1003,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", @@ -882,7 +1023,9 @@ "label": "Shipping Rule", "oldfieldtype": "Button", "options": "Shipping Rule", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "tax_category", @@ -891,13 +1034,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", @@ -907,7 +1054,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, @@ -915,7 +1064,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", @@ -926,13 +1077,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", @@ -944,13 +1099,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", @@ -960,7 +1119,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, @@ -968,7 +1129,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", @@ -978,7 +1141,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", @@ -990,7 +1155,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", @@ -1000,13 +1167,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", @@ -1018,7 +1189,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", @@ -1028,7 +1201,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", @@ -1038,7 +1213,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, @@ -1047,7 +1224,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", @@ -1057,7 +1236,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", @@ -1067,13 +1248,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", @@ -1081,7 +1266,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", @@ -1090,7 +1277,9 @@ "hide_seconds": 1, "label": "Additional Discount Amount", "options": "currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "totals", @@ -1099,7 +1288,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", @@ -1112,7 +1303,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", @@ -1124,7 +1317,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", @@ -1137,7 +1332,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.", @@ -1150,7 +1347,9 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break5", @@ -1159,6 +1358,8 @@ "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1173,7 +1374,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", @@ -1185,7 +1388,9 @@ "no_copy": 1, "options": "currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "bold": 1, @@ -1198,7 +1403,9 @@ "oldfieldname": "rounded_total_export", "oldfieldtype": "Currency", "options": "currency", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "in_words", @@ -1210,7 +1417,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", @@ -1222,7 +1431,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", @@ -1235,7 +1446,9 @@ "oldfieldtype": "Currency", "options": "party_account_currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1247,7 +1460,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", @@ -1255,7 +1470,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", @@ -1264,7 +1481,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", @@ -1275,7 +1494,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, @@ -1284,7 +1505,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)", @@ -1295,7 +1518,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)", @@ -1306,7 +1531,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)", @@ -1315,7 +1542,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", @@ -1328,7 +1557,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", @@ -1338,13 +1569,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", @@ -1355,13 +1590,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", @@ -1375,13 +1614,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", @@ -1393,13 +1636,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", @@ -1410,7 +1657,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", @@ -1420,7 +1669,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, @@ -1431,6 +1682,8 @@ "hide_days": 1, "hide_seconds": 1, "label": "Write Off", + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1441,7 +1694,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", @@ -1452,7 +1707,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", @@ -1462,13 +1719,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", @@ -1477,7 +1738,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", @@ -1486,7 +1749,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, @@ -1496,7 +1761,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", @@ -1507,7 +1774,9 @@ "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "terms", @@ -1516,7 +1785,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, @@ -1524,7 +1795,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, @@ -1536,7 +1809,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, @@ -1546,7 +1821,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", @@ -1555,13 +1832,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, @@ -1575,7 +1856,9 @@ "oldfieldtype": "Link", "options": "Print Heading", "print_hide": 1, - "report_hide": 1 + "report_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1584,7 +1867,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", @@ -1593,7 +1878,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", @@ -1603,7 +1890,9 @@ "hide_seconds": 1, "label": "Customer Group", "options": "Customer Group", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "campaign", @@ -1614,7 +1903,9 @@ "oldfieldname": "campaign", "oldfieldtype": "Link", "options": "Campaign", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1624,13 +1915,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%" }, { @@ -1644,7 +1939,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", @@ -1655,7 +1952,9 @@ "oldfieldname": "source", "oldfieldtype": "Select", "options": "Lead Source", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1666,7 +1965,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", @@ -1679,7 +1980,9 @@ "options": "Account", "print_hide": 1, "reqd": 1, - "search_index": 1 + "search_index": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "party_account_currency", @@ -1691,7 +1994,9 @@ "no_copy": 1, "options": "Currency", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "No", @@ -1703,7 +2008,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", @@ -1713,7 +2020,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", @@ -1724,7 +2033,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", @@ -1732,7 +2043,9 @@ "hide_days": 1, "hide_seconds": 1, "oldfieldtype": "Column Break", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "remarks", @@ -1743,7 +2056,9 @@ "no_copy": 1, "oldfieldname": "remarks", "oldfieldtype": "Text", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1755,7 +2070,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", @@ -1766,7 +2083,9 @@ "oldfieldname": "sales_partner", "oldfieldtype": "Link", "options": "Sales Partner", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "column_break10", @@ -1775,6 +2094,8 @@ "hide_seconds": 1, "oldfieldtype": "Column Break", "print_hide": 1, + "show_days": 1, + "show_seconds": 1, "width": "50%" }, { @@ -1785,7 +2106,9 @@ "label": "Commission Rate (%)", "oldfieldname": "commission_rate", "oldfieldtype": "Currency", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "fieldname": "total_commission", @@ -1796,7 +2119,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, @@ -1806,7 +2131,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, @@ -1818,7 +2145,9 @@ "oldfieldname": "sales_team", "oldfieldtype": "Table", "options": "Sales Team", - "print_hide": 1 + "print_hide": 1, + "show_days": 1, + "show_seconds": 1 }, { "collapsible": 1, @@ -1826,7 +2155,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, @@ -1836,7 +2167,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, @@ -1846,13 +2179,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, @@ -1864,7 +2201,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, @@ -1873,7 +2212,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", @@ -1886,7 +2227,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, @@ -1894,13 +2237,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", @@ -1908,7 +2255,9 @@ "fieldname": "is_consolidated", "fieldtype": "Check", "label": "Is Consolidated", - "read_only": 1 + "read_only": 1, + "show_days": 1, + "show_seconds": 1 }, { "default": "0", @@ -1918,14 +2267,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", @@ -1933,7 +2286,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", @@ -1943,24 +2298,32 @@ "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", @@ -1983,6 +2346,16 @@ "fieldtype": "Small Text", "label": "Dispatch Address", "read_only": 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", @@ -1995,7 +2368,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-07-08 14:03:55.502522", + "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 b97dc401e6a3..329f9a97b867 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -8,7 +8,7 @@ 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 f68d81909a3e..94684bff1cd2 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -447,10 +447,11 @@ def postprocess(source, target): 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 update_item(obj, target, source_parent): 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 b0e24606c666..ae76dea384b3 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1096,6 +1096,8 @@ def set_payment_schedule(self): 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: @@ -1106,28 +1108,25 @@ def set_payment_schedule(self): 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 6a550e0e975c..974ade358493 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -72,7 +72,8 @@ def set_missing_values(self, for_validate=False): # 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 a79eadc76193..54df0d63cba4 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 6b8a410f58e4..21a6e16a450a 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -619,6 +619,7 @@ def set_missing_values(source, target): 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) @@ -675,10 +676,6 @@ def get_pending_qty(item_row): } }, 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 802c6b37385546f666a03874cc0d420a6f4142d8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 7 Aug 2021 17:39:40 +0530 Subject: [PATCH 499/691] 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 94684bff1cd2..a0b1e073cc65 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -471,6 +471,7 @@ def update_item(obj, target, source_parent): "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 0db54e42068c..d06f9d202742 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 @@ def test_po_for_blocked_supplier_payments_past_date(self): 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 2b9d516e217b..bba54018aefe 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -670,6 +670,7 @@ def update_item(source, target, source_parent): "party_account_currency": "party_account_currency", "payment_terms_template": "payment_terms_template" }, + "field_no_map": ["payment_terms_template"], "validation": { "docstatus": ["=", 1] } From 8b9c04aae8eb89af21f5f7680966c092271d58b9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 8 Aug 2021 19:17:38 +0530 Subject: [PATCH 500/691] 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 d06f9d202742..d668c76b6b9a 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 @@ def test_purchase_order_on_hold(self): 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 @@ def test_make_purchase_invoice_with_terms(self): 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 @@ def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked( 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 @@ def test_payment_terms_are_fetched_when_creating_purchase_invoice(self): # 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 cb539b7a6a30fe48eb523c3288b889f6805903bc Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 5 Aug 2021 22:38:00 +0530 Subject: [PATCH 501/691] fix: Add discount account handling in Purchase Invoice --- .../purchase_invoice/purchase_invoice.py | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 53ad849b1e14..76ef23e87815 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -520,6 +520,8 @@ def make_item_gl_entries(self, gl_entries): 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): @@ -611,11 +613,7 @@ def make_item_gl_entries(self, gl_entries): 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")) @@ -857,8 +855,11 @@ def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value 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" @@ -867,21 +868,21 @@ def make_tax_gl_entries(self, gl_entries): 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 75a832715c09b5e9da28aedee23a4b15b3f483da Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 5 Aug 2021 22:38:24 +0530 Subject: [PATCH 502/691] 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 ed5c4af1a930..c211e50548a0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -256,39 +256,41 @@ def test_purchase_invoice_with_discount_accounting_enabled(self): 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()) @@ -1281,6 +1283,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 a55f708ab663..bde11d2566b3 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2022,15 +2022,16 @@ def test_sales_invoice_with_discount_accounting_enabled(self): 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 @@ -2039,28 +2040,28 @@ def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled( 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() @@ -2241,6 +2242,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 7e3dd9e8ee05611f5192a674a8ed6f87545ad389 Mon Sep 17 00:00:00 2001 From: Francisco Roldan Date: Mon, 9 Aug 2021 11:21:41 -0300 Subject: [PATCH 503/691] 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 46ce0939e4fd..771611a78601 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 0a90302170e499b468ced0a437ed1c2a7fab6dbe Mon Sep 17 00:00:00 2001 From: Francisco Roldan Date: Mon, 9 Aug 2021 11:41:56 -0300 Subject: [PATCH 504/691] 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 46ce0939e4fd..771611a78601 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 5e428f0447e2b0de930efe0f7924090e54543045 Mon Sep 17 00:00:00 2001 From: Ankush Date: Mon, 9 Aug 2021 22:15:49 +0530 Subject: [PATCH 505/691] perf(cache): fix active SLA doctype caching (#26861) If no SLA is configured then this query runs on EVERY `validate` call. Root cause: if not active SLA doctypes exist then `not []` evalutes to true and causes query to run again. --- .../service_level_agreement/service_level_agreement.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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 8739cb2364c4..cfa264feb54f 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -281,15 +281,18 @@ def get_repeated(values): def get_documents_with_active_service_level_agreement(): - if not frappe.cache().hget("service_level_agreement", "active"): - set_documents_with_active_service_level_agreement() + sla_doctypes = frappe.cache().hget("service_level_agreement", "active") + + if sla_doctypes is None: + return set_documents_with_active_service_level_agreement() - return frappe.cache().hget("service_level_agreement", "active") + return sla_doctypes def set_documents_with_active_service_level_agreement(): active = [sla.document_type for sla in frappe.get_all("Service Level Agreement", fields=["document_type"])] frappe.cache().hset("service_level_agreement", "active", active) + return active def apply(doc, method=None): From b73bb3e5013e08276f7af4f83321cc600b081ad0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 5 Aug 2021 22:38:00 +0530 Subject: [PATCH 506/691] 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 feae213d92d6..d0f4e9654144 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -519,6 +519,10 @@ def make_item_gl_entries(self, gl_entries): 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 @@ def make_item_gl_entries(self, gl_entries): 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 @@ def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value 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 @@ def make_tax_gl_entries(self, gl_entries): 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 507/691] 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 d60f0a40b151..f0f5a58d1539 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 @@ def test_purchase_invoice_with_discount_accounting_enabled(self): 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 af317db71249..12f03be2881a 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 @@ def test_sales_invoice_with_discount_accounting_enabled(self): 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 @@ def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled( 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 508/691] 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 d0f4e9654144..d7d9a3886a5a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -519,8 +519,6 @@ def make_item_gl_entries(self, gl_entries): 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 @@ def make_tax_gl_entries(self, gl_entries): "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 509/691] 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 000000000000..e69de29bb2d1 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 000000000000..69383a482b45 --- /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 000000000000..e5ca7e536847 --- /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 000000000000..e69de29bb2d1 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 000000000000..bd1229d4e09c --- /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 000000000000..ea24788ad560 --- /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 000000000000..e69de29bb2d1 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 000000000000..f3dac02f9422 --- /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 000000000000..4aad84ec140d --- /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 cc71324dbc38..1d68b23d6c7a 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 7d9302382f16..ec42eda63ac5 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -30,17 +30,17 @@ def validate(self): 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)) - - 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, []) + for i in range(len(args.get(applicable_for))) : - 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 000000000000..e69de29bb2d1 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 000000000000..c176e4d173dd --- /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 000000000000..9e6564845b86 --- /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 000000000000..e69de29bb2d1 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 000000000000..67fac4584565 --- /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 000000000000..5ea2635482de --- /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 000000000000..e69de29bb2d1 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 000000000000..95c4dc6db366 --- /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 000000000000..b58b5dc057b4 --- /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 000000000000..e69de29bb2d1 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 000000000000..0f0fdea8c012 --- /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 000000000000..9adfc0057211 --- /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 510/691] 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 8dc049977966..9c756258db3d 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 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 511/691] 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 ec42eda63ac5..ff3f9c5638d8 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 512/691] 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 9c756258db3d..940b76a9ade6 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 513/691] 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 ff3f9c5638d8..69ed94676dd7 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 514/691] 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 940b76a9ade6..99f9ed47e4a3 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 @@ def test_promotional_scheme(self): 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 @@ def test_promotional_scheme(self): 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 515/691] 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 69ed94676dd7..2b7f15663816 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 99f9ed47e4a3..7354ef036c54 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 @@ def test_promotional_scheme(self): 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 @@ def test_promotional_scheme(self): 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 516/691] 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 2b7f15663816..53239feb4298 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -30,8 +30,15 @@ def validate(self): 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 517/691] 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 e5ca7e536847..4f5fd7f7d78c 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 ea24788ad560..df782ac9e0c6 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 4aad84ec140d..a577145e4ee0 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 9e6564845b86..9796c7b0ccf7 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 5ea2635482de..de0444ee193c 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 b58b5dc057b4..ad66e230c805 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 9adfc0057211..d46edc9dca81 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 518/691] 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 0be41b40635c..99c5b34fa352 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 53239feb4298..f4ee1887c498 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 8db0f07115676c63c40c38d092ca352a794d0f67 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 10 Aug 2021 11:43:43 +0530 Subject: [PATCH 519/691] fix: Missing method reset_issue_metrics added back to Issue doctype (#26573) --- 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 b9a65b6749e4..24dadd5a8c6e 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -116,6 +116,10 @@ def split_issue(self, subject, communication_id): }).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 get_list_context(context=None): return { 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 520/691] 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 9c69deb6a487..e4c4af03651d 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -222,6 +222,10 @@ def split_issue(self, subject, communication_id): }).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 9ca2febd880c62be7f009b4c29ff456155636ca9 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 10 Aug 2021 13:10:46 +0530 Subject: [PATCH 521/691] 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 128a2ab62ffd..cb09d9338016 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 @@ def test_multi_currency_lcv(self): 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 @@ def test_asset_lcv(self): 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 06b6b7e3cca968abc18e7136b8567ade9192003a Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 10 Aug 2021 13:10:46 +0530 Subject: [PATCH 522/691] 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 128a2ab62ffd..cb09d9338016 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 @@ def test_multi_currency_lcv(self): 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 @@ def test_asset_lcv(self): 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 94beda65ca01724fb3d0d2b57190672083740c61 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 10 Aug 2021 13:16:46 +0530 Subject: [PATCH 523/691] fix: updating lead status while customer creation (#26606) * 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 66edcd018867..abf146c43f4f 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -157,9 +157,7 @@ def update_lead_status(self): '''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 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 524/691] 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 818888c0c127..9785f6c7a9f0 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -134,9 +134,7 @@ def update_lead_status(self): '''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 525/691] 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 e4c4af03651d..b48925d33ad3 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -235,8 +235,7 @@ def before_insert(self): 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 @@ def set_response_and_resolution_time(self, priority=None, service_level_agreemen 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 939c19998288..1678f04def8d 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 70c469663b78..ec0237e2eace 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 @@ def validate(self): self.validate_doc() self.check_priorities() self.check_support_and_resolution() + self.validate_condition() def check_priorities(self): default_priority = [] @@ -92,6 +95,14 @@ def validate_doc(self): 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"]) + + 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) + + # if any default sla + filtered_agreements += default_sla - agreement = frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters, - fields=["name", "default_priority"]) + return filtered_agreements[0] if filtered_agreements else None - return agreement[0] if agreement 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 948386d85db8fea500443f055d89b75c837b8285 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 10 Aug 2021 14:00:55 +0530 Subject: [PATCH 526/691] 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 cde7fe07c63b..b9a58cf43e45 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 @@ def test_inter_company_transfer_fallback_on_cancel(self): 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 af2f5277d5352ab2fc710200e152f4f9c468439c Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 10 Aug 2021 14:16:16 +0530 Subject: [PATCH 527/691] fix: pos profile not mandatory for Sales Invoice (#26853) --- 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 88899130a248..4e4e5b5aa784 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -480,7 +480,7 @@ def set_pos_fields(self, for_validate=False): 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 f22b85825370fe75b8df694f3c8f86a572dc411b Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 22 Jul 2021 13:23:54 +0530 Subject: [PATCH 528/691] 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 17bd7354f93a..17707ecae7f1 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -27,6 +27,7 @@ def validate(self): 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 @@ def validate_serialized_batch(self): 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 5475383759f5..b9fa9b7ebbdd 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 bad7b608acf8..70312bc543b5 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -165,8 +165,14 @@ def get_stock_ledger_entries(self, serial_no=None): ) 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 654755ec2fbe..3ff42bf97426 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -76,6 +76,7 @@ def validate(self): 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 2e092865075c..324bb7a62d9c 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -31,6 +31,7 @@ def validate(self): 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 529/691] 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 cde7fe07c63b..b9a58cf43e45 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 @@ def test_inter_company_transfer_fallback_on_cancel(self): 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 530/691] 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 2539de7b1247..eba8ba830fc7 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -480,7 +480,7 @@ def set_pos_fields(self, for_validate=False): 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 531/691] 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 53d5278bbf45..9d8fcb64f7fb 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 532/691] 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 59fbe3b0301c..e23a71545244 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -639,7 +639,7 @@ def test_discounted_wdv_depreciation_rate_for_indian_region(self): 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 @@ def test_discounted_wdv_depreciation_rate_for_indian_region(self): 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 88c350ac8997..a152797a5d47 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 2ae2580706774135813a158db989ca74d7dc69de Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 10 Aug 2021 16:37:48 +0530 Subject: [PATCH 533/691] fix(asset): incorrect date difference calculation (#26793) --- 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 59fbe3b0301c..e23a71545244 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -639,7 +639,7 @@ def test_discounted_wdv_depreciation_rate_for_indian_region(self): 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 @@ def test_discounted_wdv_depreciation_rate_for_indian_region(self): 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 88c350ac8997..a152797a5d47 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 84c4161c001b446dda25b8070d8da16203eec07d Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 10 Aug 2021 16:46:44 +0530 Subject: [PATCH 534/691] fix(e-invoicing): cannot cancel invoice if IRN cancelled on portal (#26638) --- 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 35b248c08ed0..86356e302691 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 000000000000..2319c17b34c6 --- /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 e9372f9b8fc2..b4f146ce57e7 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 a7e0805039c997caf15f777d8a0a12ae83c91243 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Aug 2021 17:16:15 +0530 Subject: [PATCH 535/691] 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 ada3badd7c7e..a2b99dc934cb 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 000000000000..2319c17b34c6 --- /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 e9372f9b8fc2..b4f146ce57e7 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 1cba77cfbd1e3684603795eabd52d6af4284167a Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Tue, 10 Aug 2021 17:25:12 +0530 Subject: [PATCH 536/691] fix: Sales Return cancellation if linked with Payment Entry (#26551) --- .../purchase_invoice/purchase_invoice.py | 4 +- .../doctype/sales_invoice/sales_invoice.py | 46 +++++++++++++++++-- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 863c104dff4e..25f42bce8457 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -22,7 +22,7 @@ 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 @@ -1014,6 +1014,8 @@ def make_gle_for_rounding_adjustment(self, gl_entries): }, 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 4e4e5b5aa784..cecc1a18df4a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -290,6 +290,8 @@ def before_cancel(self): 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") @@ -922,7 +924,7 @@ def make_item_gl_entries(self, gl_entries): asset = frappe.get_doc("Asset", item.asset) else: frappe.throw(_( - "Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name), + "Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name), title=_("Missing Asset") ) if (len(asset.finance_books) > 1 and not item.finance_book @@ -944,7 +946,7 @@ def make_item_gl_entries(self, gl_entries): gl_entries.append(self.get_gl_dict(gle, item=item)) self.set_asset_status(asset) - + else: # Do not book income for transfer within same company if not self.is_internal_transfer(): @@ -973,7 +975,7 @@ def make_item_gl_entries(self, gl_entries): 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): @@ -1941,3 +1943,41 @@ def set_missing_values(source, target): } }, 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 0e337be06573d40ad1f239a38e95953761fe497d Mon Sep 17 00:00:00 2001 From: Anuja Pawar <60467153+Anuja-pawar@users.noreply.github.com> Date: Tue, 10 Aug 2021 17:26:35 +0530 Subject: [PATCH 537/691] fix: cost center & account validation in Sales/Purchase Taxes and Charges (#25929) --- .../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 52d19d54a8b3..8f9eb6577b8b 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 @@ 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 2b737b980484..74db08d5b866 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 9e2e5968c6db..bc8e4cea2d79 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1288,6 +1288,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 7b997a11530b..84c717676c71 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 8755125c810a..95cbf5150cdb 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -108,6 +108,9 @@ def on_update(self): 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 @@ def on_update(self): 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 cbb3dc881fb3..bacada9f5cce 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 506ecb421676d53c90819df021131d715845dae5 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Sun, 20 Jun 2021 12:42:06 +0530 Subject: [PATCH 538/691] 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 5ca47560b106..96046fba8176 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 000000000000..e69de29bb2d1 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 000000000000..057c45ee8645 --- /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 000000000000..04bd9422bd9b --- /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 000000000000..d802781320ad --- /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 000000000000..be2964530b8d --- /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 7a3cb838a990..d3ebcdf7e7b7 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 000000000000..62f6ddcb6e01 --- /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 539/691] 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 057c45ee8645..e42e54f690c2 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 04bd9422bd9b..5739a112de97 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 be2964530b8d..ae91a919b2b3 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,7 +2,7 @@ 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 62f6ddcb6e01..02446be11a16 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 540/691] 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 5739a112de97..edaf46162e27 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} +
                                    +
                                    `); + + if (collapsed) + $node_group.addClass('collapsed'); + else + $node_group.addClass('mb-4'); - return $node_group; + 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 02446be11a16..b6d50a0470a0 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 541/691] 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 edaf46162e27..f693cf6ba67a 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 b6d50a0470a0..6012c015733b 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 542/691] 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 f693cf6ba67a..15334bd4ca55 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 6012c015733b..16b8792432e0 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 543/691] 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 15334bd4ca55..efb367ad44c3 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 544/691] 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 efb367ad44c3..0fe724c78e43 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); - }); - } - - setup_node_group_action() { - let me = this; - - $('.node-group').on('click', function() { - let parent = $(this).attr('data-parent'); - me.expand_sibling_group_node(parent); + organizational_chart.show(); }); - } - - 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 d3ebcdf7e7b7..3c60e3ee500d 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 000000000000..fd84d4ea5caa --- /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 000000000000..c705681438dc --- /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 545/691] 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 0fe724c78e43..ca9855286ca8 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 ae91a919b2b3..f3aa13897d5d 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 fd84d4ea5caa..052f140c1398 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 c705681438dc..1b8bc2e8e0f1 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 546/691] 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 ca9855286ca8..a1388867687c 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 052f140c1398..0823ec77a80b 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 1b8bc2e8e0f1..4b09714d0ad3 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 e42e54f690c2..30aedab4bb76 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 547/691] 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 96046fba8176..5ca47560b106 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 548/691] refactor: use arcs instead of bezier curves for cleaner connectors --- .../hierarchy_chart_desktop.js | 52 ++++++++++++++++--- erpnext/public/scss/hierarchy_chart.scss | 2 +- 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 0823ec77a80b..ba811be58698 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" + + 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) + " " + - "C" + - (pos_parent_right.x + 100) + "," + (pos_parent_right.y) + " " + - (pos_child_left.x - 100) + "," + (pos_child_left.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; + } - path.setAttribute("d", connector); + 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 16b8792432e0..16137fdb5f0c 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 549/691] 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 4b09714d0ad3..b09b428b3064 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 550/691] 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 30aedab4bb76..c3d8e010b53f 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 16137fdb5f0c..eefc14d67970 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 551/691] 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 ba811be58698..9e82fb20024d 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 b09b428b3064..2ff00baa7c6c 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 eefc14d67970..a54bf6f332ee 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 552/691] 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 9e82fb20024d..1896ac7c39ec 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 2ff00baa7c6c..102cbb03b0ab 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 553/691] 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 1896ac7c39ec..8d0685f80d56 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 554/691] feat: handle multiple root / orphan nodes --- .../organizational_chart.py | 3 +- .../hierarchy_chart_desktop.js | 50 +++++++++---------- .../hierarchy_chart/hierarchy_chart_mobile.js | 37 +++++++------- erpnext/public/scss/hierarchy_chart.scss | 4 ++ 4 files changed, 51 insertions(+), 43 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index f3aa13897d5d..77b8df7520bf 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 8d0685f80d56..e89a98ac4f8a 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 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, + let nodes = r.message; + 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; }); - 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 102cbb03b0ab..5eee27b5fc33 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 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, + 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 + }); }); - - 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 a54bf6f332ee..dd523c344398 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 555/691] 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 | 84 +++++++++---------- .../hierarchy_chart/hierarchy_chart_mobile.js | 65 +++++++------- 3 files changed, 79 insertions(+), 92 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 77b8df7520bf..46578f3aaf75 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,33 +2,25 @@ 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 e89a98ac4f8a..bf366792a9cf 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; - - $.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; + } + }).then(r => { + if (r.message.length) { + let node = undefined; + let first_root = undefined; + + $.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 5eee27b5fc33..17062e258576 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 556/691] 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 46578f3aaf75..ce84b3c7444d 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 bf366792a9cf..374787c6ef24 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 557/691] 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 17062e258576..19852993789f 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 c3d8e010b53f..fb94df85ed8f 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 558/691] 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 ce84b3c7444d..1e03e3d06add 100644 --- a/erpnext/hr/page/organizational_chart/organizational_chart.py +++ b/erpnext/hr/page/organizational_chart/organizational_chart.py @@ -2,8 +2,7 @@ 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 559/691] 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 19852993789f..d48b4c8f362c 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 560/691] 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 d48b4c8f362c..58530eaaf911 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 561/691] 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 58530eaaf911..5a6f16887673 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 562/691] 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 000000000000..d50d55133019 --- /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 374787c6ef24..fe4d17c2102b 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 000000000000..8e67b1cd3468 --- /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 563/691] 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 000000000000..656051289f10 --- /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 5a6f16887673..bd7946a1e139 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 8e67b1cd3468..99748dca02e9 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 564/691] 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 d50d55133019..807ef5731a58 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 656051289f10..f48972bdc68e 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 99748dca02e9..f66d69ba232f 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 565/691] 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 9e29b6f1d2a9..661525bedc12 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 566/691] 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 807ef5731a58..ae8e75f219c3 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 f48972bdc68e..093e808da01d 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 567/691] 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 ae8e75f219c3..cb12eb5c0c46 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 093e808da01d..a1d3c0083cac 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 568/691] 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 cb12eb5c0c46..95592e2f6a82 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 a1d3c0083cac..632d15ba6c88 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 569/691] 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 95592e2f6a82..0493732812fe 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 632d15ba6c88..1dcfbcfeb17b 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 f66d69ba232f..fc3aa298242b 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 570/691] 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 0493732812fe..52863f18e056 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 1dcfbcfeb17b..2272a31046e5 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 571/691] 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 52863f18e056..0da4e560a72d 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 2272a31046e5..0374678a1ad1 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 572/691] fix: sider --- .../test_organizational_chart_desktop.js | 3 +- .../test_organizational_chart_mobile.js | 38 +++++++++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/cypress/integration/test_organizational_chart_desktop.js b/cypress/integration/test_organizational_chart_desktop.js index 0da4e560a72d..57b7f7dced28 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 0374678a1ad1..214229f6f6c0 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 }); - - 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 }); + + 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 573/691] 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 57b7f7dced28..fb46bbb43312 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 214229f6f6c0..df90dbfa22fe 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 574/691] 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 fe4d17c2102b..694c26567aa7 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 dd523c344398..1c2f9421faa7 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 000000000000..9b0279351f29 --- /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 575/691] 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 cb45ce5f692e..321af084ae94 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 c9ee7a622c49..5bc1e56a2100 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 242695c4b85e..82e98215d1eb 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 576/691] feat: Export chart option in desktop view --- .../hierarchy_chart_desktop.js | 99 +++++++++++++------ erpnext/public/scss/hierarchy_chart.scss | 10 +- erpnext/utilities/hierarchy_chart.py | 4 +- 3 files changed, 83 insertions(+), 30 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 694c26567aa7..57d34d822528 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 1c2f9421faa7..44288fe1552f 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 9b0279351f29..22d3f28faa7b 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 577/691] 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 44288fe1552f..7f1077dbbd29 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 578/691] 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 22d3f28faa7b..fb58a5d58679 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 579/691] 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 57d34d822528..89fb8d579253 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 580/691] 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 622bd33e20aa..5af12cde06d2 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 @@ def setUp(self): 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 581/691] 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 f4ee1887c498..0fade84cfd12 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -25,12 +25,14 @@ 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 @@ def on_update(self): }, 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 @@ def update_pricing_rules(self, data): 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 582/691] 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 0fade84cfd12..3d7a891f3338 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.py @@ -26,7 +26,7 @@ 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 55d8aaf0b66f8cf3178a28c65455aabf186ab96b Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 27 Jul 2021 16:53:55 +0530 Subject: [PATCH 583/691] fix: Stock Analytics Report must consider warehouse during calculation --- .../report/stock_analytics/stock_analytics.py | 44 +++++++++++++++---- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index 0cc8ca48aace..d44685060c73 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 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 584/691] 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 d7d9a3886a5a..85df22586811 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -22,7 +22,7 @@ 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 @@ def make_gle_for_rounding_adjustment(self, gl_entries): }, 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 eba8ba830fc7..ba1e729a46b1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -290,6 +290,8 @@ def before_cancel(self): 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 @@ def make_item_gl_entries(self, gl_entries): 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 set_missing_values(source, target): } }, 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 585/691] 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 52d19d54a8b3..8f9eb6577b8b 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 @@ 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 2b737b980484..74db08d5b866 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 d65efa24b4e6..73d2411edef2 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 7b997a11530b..84c717676c71 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 8755125c810a..95cbf5150cdb 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -108,6 +108,9 @@ def on_update(self): 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 @@ def on_update(self): 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 c7cc00051863..c69f3d0b3fa8 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 586/691] 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 a1388867687c..08f2c94ad448 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 000000000000..26ab6d92b9d4 --- /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 d3313c7cee2f..b68ddf52b299 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 587/691] 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 56f11650ffa4..568e7721a395 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 f069e8dd0b88..c90297328ee5 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 75f7478ed18c..be6771e56f9a 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 ae38d4ca1925..a0042eb7d1a2 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 eb2050b40731131a3c578c50edc74f193a7ebf3b Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 10 Aug 2021 20:37:09 +0530 Subject: [PATCH 588/691] fix: Brand filter in Stock Analytics --- erpnext/stock/report/stock_balance/stock_balance.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 9e56ad413069..fc3d719a7809 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 b614834efedbef572e0567828f0d9d82e81331ee Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 10 Aug 2021 21:33:58 +0530 Subject: [PATCH 589/691] fix: unseting of payment if no pos profile found (#26884) --- 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 099c7d43463d..05edb2530c22 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -679,17 +679,13 @@ def update_paid_amount_for_return(self, total_amount_to_pay): 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 a495a9b0c11b..84697e0f008b 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -751,8 +751,6 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { 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 b7b111c3edd3dd42470b61061e866dfa1850d46c Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 10 Aug 2021 22:52:37 +0530 Subject: [PATCH 590/691] 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 099c7d43463d..05edb2530c22 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -679,17 +679,13 @@ def update_paid_amount_for_return(self, total_amount_to_pay): 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 53d5278bbf45..891ec6edc180 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 591/691] 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 099c7d43463d..05edb2530c22 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -679,17 +679,13 @@ def update_paid_amount_for_return(self, total_amount_to_pay): 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 9d8fcb64f7fb..90cb5559394a 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 592/691] 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 7f1077dbbd29..8a1ec4992b0d 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 593/691] 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 7f1077dbbd29..8a1ec4992b0d 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 bff3b0962a903dddf13f80eb1e8b8dc028c35559 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 7 Aug 2021 00:12:57 +0530 Subject: [PATCH 594/691] 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 bc22ff7495c1..e10a9e7bfddd 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1108,7 +1108,8 @@ def set_payment_schedule(self): 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 54df0d63cba4..4d432e3d5cc7 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 9152715f9049585e8d59967dd5c4fef15f04428c Mon Sep 17 00:00:00 2001 From: Ankush Date: Wed, 11 Aug 2021 11:17:50 +0530 Subject: [PATCH 595/691] perf: various minor perf fixes for ledger postings (#26775) * perf: only validate if voucher is journal entry * perf: optimize merge GLE - Order fields such that comparison will fail faster - Break out of loops if not matched * perf: don't try to match SLE if count mismatch * refactor: simplify initialize_previous_data * perf: use cache for fetching valuation_method These are set only once fields * refactor: simplify get_future_stock_vouchers * refactor: simplify get_voucherwise_gl_entries * perf: fetch only required fields for GL comparison `select *` fetches all fields, output of this function is only used for comparing. * perf: reorder conditions in PL cost center check * perf: reduce query while validating new gle * perf: use cache for validating warehouse props These properties don't change often, no need to query everytime. * perf: use cached stock settings to validate SLE * docs: update misleading docstring Co-authored-by: Marica --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 22 ++++++----- erpnext/accounts/general_ledger.py | 25 ++++++++----- erpnext/accounts/utils.py | 37 +++++++++++++------ .../stock_ledger_entry/stock_ledger_entry.py | 4 +- erpnext/stock/stock_ledger.py | 12 +++--- erpnext/stock/utils.py | 8 ++-- 6 files changed, 65 insertions(+), 43 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 11465b711e34..0844995f296e 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -58,8 +58,8 @@ def check_mandatory(self): if not self.get(k): frappe.throw(_("{0} is required").format(_(self.meta.get_label(k)))) - account_type = frappe.get_cached_value("Account", self.account, "account_type") if not (self.party_type and self.party): + account_type = frappe.get_cached_value("Account", self.account, "account_type") if account_type == "Receivable": frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}") .format(self.voucher_type, self.voucher_no, self.account)) @@ -73,15 +73,19 @@ def check_mandatory(self): .format(self.voucher_type, self.voucher_no, self.account)) def pl_must_have_cost_center(self): + """Validate that profit and loss type account GL entries have a cost center.""" + + if self.cost_center or self.voucher_type == 'Period Closing Voucher': + return + if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss": - if not self.cost_center and self.voucher_type != 'Period Closing Voucher': - msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format( - self.voucher_type, self.voucher_no, self.account) - msg += " " - msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format( - self.voucher_type) - - frappe.throw(msg, title=_("Missing Cost Center")) + msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format( + self.voucher_type, self.voucher_no, self.account) + msg += " " + msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format( + self.voucher_type) + + frappe.throw(msg, title=_("Missing Cost Center")) def validate_dimensions_for_pl_and_bs(self): account_type = frappe.db.get_value("Account", self.account, "report_type") diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 25d2cf10bd45..4c7c567b42a7 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -100,8 +100,8 @@ def merge_similar_entries(gl_map, precision=None): return merged_gl_map def check_if_in_list(gle, gl_map, dimensions=None): - account_head_fieldnames = ['party_type', 'party', 'against_voucher', 'against_voucher_type', - 'cost_center', 'project', 'voucher_detail_no'] + account_head_fieldnames = ['voucher_detail_no', 'party', 'against_voucher', + 'cost_center', 'against_voucher_type', 'party_type', 'project'] if dimensions: account_head_fieldnames = account_head_fieldnames + dimensions @@ -110,10 +110,12 @@ def check_if_in_list(gle, gl_map, dimensions=None): same_head = True if e.account != gle.account: same_head = False + continue for fieldname in account_head_fieldnames: if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)): same_head = False + break if same_head: return e @@ -143,16 +145,19 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False): validate_expense_against_budget(args) def validate_cwip_accounts(gl_map): - cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")) + """Validate that CWIP account are not used in Journal Entry""" + if gl_map and gl_map[0].voucher_type != "Journal Entry": + return - if cwip_enabled and gl_map[0].voucher_type == "Journal Entry": - cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount - where account_type = 'Capital Work in Progress' and is_group=0""")] + cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting")) + if cwip_enabled: + cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount + where account_type = 'Capital Work in Progress' and is_group=0""")] - for entry in gl_map: - if entry.account in cwip_accounts: - frappe.throw( - _("Account: {0} is capital Work in progress and can not be updated by Journal Entry").format(entry.account)) + for entry in gl_map: + if entry.account in cwip_accounts: + frappe.throw( + _("Account: {0} is capital Work in progress and can not be updated by Journal Entry").format(entry.account)) def round_off_debit_credit(gl_map): precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9272bc4fcee4..5b58e874fede 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -920,7 +920,6 @@ def _delete_gl_entries(voucher_type, voucher_no): _delete_gl_entries(voucher_type, voucher_no) def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None, company=None): - future_stock_vouchers = [] values = [] condition = "" @@ -936,30 +935,46 @@ def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, f condition += " and company = %s" values.append(company) - for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no + future_stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no from `tabStock Ledger Entry` sle where timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s) and is_cancelled = 0 {condition} order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition), - tuple([posting_date, posting_time] + values), as_dict=True): - future_stock_vouchers.append([d.voucher_type, d.voucher_no]) + tuple([posting_date, posting_time] + values), as_dict=True) - return future_stock_vouchers + return [(d.voucher_type, d.voucher_no) for d in future_stock_vouchers] def get_voucherwise_gl_entries(future_stock_vouchers, posting_date): + """ Get voucherwise list of GL entries. + + Only fetches GLE fields required for comparing with new GLE. + Check compare_existing_and_expected_gle function below. + """ gl_entries = {} - if future_stock_vouchers: - for d in frappe.db.sql("""select * from `tabGL Entry` - where posting_date >= %s and voucher_no in (%s)""" % - ('%s', ', '.join(['%s']*len(future_stock_vouchers))), - tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1): - gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d) + if not future_stock_vouchers: + return gl_entries + + voucher_nos = [d[1] for d in future_stock_vouchers] + + gles = frappe.db.sql(""" + select name, account, credit, debit, cost_center, project + from `tabGL Entry` + where + posting_date >= %s and voucher_no in (%s)""" % + ('%s', ', '.join(['%s'] * len(voucher_nos))), + tuple([posting_date] + voucher_nos), as_dict=1) + + for d in gles: + gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d) return gl_entries def compare_existing_and_expected_gle(existing_gle, expected_gle, precision): + if len(existing_gle) != len(expected_gle): + return False + matched = True for entry in expected_gle: account_existed = False diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py index b4f458388b37..be1f00e37fa7 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -55,8 +55,8 @@ def calculate_batch_qty(self): "sum(actual_qty)") or 0 frappe.db.set_value("Batch", self.batch_no, "batch_qty", batch_qty) - #check for item quantity available in stock def actual_amt_check(self): + """Validate that qty at warehouse for selected batch is >=0""" if self.batch_no and not self.get("allow_negative_stock"): batch_bal_after_transaction = flt(frappe.db.sql("""select sum(actual_qty) from `tabStock Ledger Entry` @@ -107,7 +107,7 @@ def validate_item(self): self.stock_uom = item_det.stock_uom def check_stock_frozen_date(self): - stock_settings = frappe.get_doc('Stock Settings', 'Stock Settings') + stock_settings = frappe.get_cached_doc('Stock Settings') if stock_settings.stock_frozen_upto: if (getdate(self.posting_date) <= getdate(stock_settings.stock_frozen_upto) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index f990ce06be56..eddd048c74e7 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -279,15 +279,13 @@ def initialize_previous_data(self, args): } """ - self.data.setdefault(args.warehouse, frappe._dict()) - warehouse_dict = self.data[args.warehouse] previous_sle = get_previous_sle_of_current_voucher(args) - warehouse_dict.previous_sle = previous_sle - for key in ("qty_after_transaction", "valuation_rate", "stock_value"): - setattr(warehouse_dict, key, flt(previous_sle.get(key))) - - warehouse_dict.update({ + self.data[args.warehouse] = frappe._dict({ + "previous_sle": previous_sle, + "qty_after_transaction": flt(previous_sle.qty_after_transaction), + "valuation_rate": flt(previous_sle.valuation_rate), + "stock_value": flt(previous_sle.stock_value), "prev_stock_value": previous_sle.stock_value or 0.0, "stock_queue": json.loads(previous_sle.stock_queue or "[]"), "stock_value_difference": 0.0 diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index b57b2aa6b8f6..9f6d0a8addd8 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -224,7 +224,7 @@ def get_avg_purchase_rate(serial_nos): def get_valuation_method(item_code): """get valuation method from item or default""" - val_method = frappe.db.get_value('Item', item_code, 'valuation_method') + val_method = frappe.db.get_value('Item', item_code, 'valuation_method', cache=True) if not val_method: val_method = frappe.db.get_value("Stock Settings", None, "valuation_method") or "FIFO" return val_method @@ -275,17 +275,17 @@ def get_valid_serial_nos(sr_nos, qty=0, item_code=''): return valid_serial_nos def validate_warehouse_company(warehouse, company): - warehouse_company = frappe.db.get_value("Warehouse", warehouse, "company") + warehouse_company = frappe.db.get_value("Warehouse", warehouse, "company", cache=True) if warehouse_company and warehouse_company != company: frappe.throw(_("Warehouse {0} does not belong to company {1}").format(warehouse, company), InvalidWarehouseCompany) def is_group_warehouse(warehouse): - if frappe.db.get_value("Warehouse", warehouse, "is_group"): + if frappe.db.get_value("Warehouse", warehouse, "is_group", cache=True): frappe.throw(_("Group node warehouse is not allowed to select for transactions")) def validate_disabled_warehouse(warehouse): - if frappe.db.get_value("Warehouse", warehouse, "disabled"): + if frappe.db.get_value("Warehouse", warehouse, "disabled", cache=True): frappe.throw(_("Disabled Warehouse {0} cannot be used for this transaction.").format(get_link_to_form('Warehouse', warehouse))) def update_included_uom_in_report(columns, result, include_uom, conversion_factors): From a57d13e1e9538e56b401d651c52d2b9c5b84250f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 29 Jul 2021 19:46:17 +0530 Subject: [PATCH 596/691] fix: Multiple fixes in payment entry --- .../doctype/payment_entry/payment_entry.js | 4 ++-- .../doctype/payment_entry/payment_entry.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 439b1edbce62..d96bc271efa1 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -533,8 +533,8 @@ frappe.ui.form.on('Payment Entry', { source_exchange_rate: function(frm) { if (frm.doc.paid_amount) { frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate)); - if(!frm.set_paid_amount_based_on_received_amount && - (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) { + // target exchange rate should always be same as source if both account currencies is same + if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); frm.set_value("base_received_amount", frm.doc.base_paid_amount); } diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 46904f7c5718..a131a810e1fa 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -55,8 +55,9 @@ def validate(self): self.validate_mandatory() self.validate_reference_documents() self.set_tax_withholding() - self.apply_taxes() self.set_amounts() + self.validate_amounts() + self.apply_taxes() self.clear_unallocated_reference_document_rows() self.validate_payment_against_negative_invoice() self.validate_transaction_reference() @@ -236,7 +237,9 @@ def set_source_exchange_rate(self, ref_doc=None): self.company_currency, self.posting_date) def set_target_exchange_rate(self, ref_doc=None): - if self.paid_to and not self.target_exchange_rate: + if self.paid_from_account_currency == self.paid_to_account_currency: + self.target_exchange_rate = self.source_exchange_rate + elif self.paid_to and not self.target_exchange_rate: if ref_doc: if self.paid_to_account_currency == ref_doc.currency: self.target_exchange_rate = ref_doc.get("exchange_rate") @@ -473,6 +476,14 @@ def set_amounts(self): self.set_unallocated_amount() self.set_difference_amount() + def validate_amounts(self): + self.validate_received_amount() + + def validate_received_amount(self): + if self.paid_from_account_currency == self.paid_to_account_currency: + if self.paid_amount != self.received_amount: + frappe.throw(_("Received Amount cannot be greater than Paid Amount")) + def set_received_amount(self): self.base_received_amount = self.base_paid_amount From c5276f3fd36764e5e90b0b068d0d9937a88e4447 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 1 Aug 2021 17:48:50 +0530 Subject: [PATCH 597/691] fix: Multiple fixes in payment entry --- .../accounts/doctype/payment_entry/payment_entry.py | 13 ++++++++----- erpnext/accounts/utils.py | 12 ++++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index a131a810e1fa..2231b47d9ccd 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -45,7 +45,7 @@ def setup_party_account_field(self): self.party_account = self.paid_to self.party_account_currency = self.paid_to_account_currency - def validate(self): + def validate(self, on_reference_unlink=False): self.setup_party_account_field() self.set_missing_values() self.validate_payment_type() @@ -64,8 +64,9 @@ def validate(self): self.set_title() self.set_remarks() self.validate_duplicate_entry() - self.validate_allocated_amount() - self.validate_paid_invoices() + if not on_reference_unlink: + self.validate_allocated_amount() + self.validate_paid_invoices() self.ensure_supplier_is_not_blocked() self.set_status() @@ -529,8 +530,10 @@ def set_total_allocated_amount(self): base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")) - self.total_allocated_amount = abs(total_allocated_amount) - self.base_total_allocated_amount = abs(base_total_allocated_amount) + # Do not use absolute values as only credit notes could be allocated + # and total allocated should be negative in that scenario + self.total_allocated_amount = total_allocated_amount + self.base_total_allocated_amount = base_total_allocated_amount def set_unallocated_amount(self): self.unallocated_amount = 0 diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9272bc4fcee4..11e113d0003c 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -553,10 +553,14 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no)) for pe in linked_pe: - pe_doc = frappe.get_doc("Payment Entry", pe) - pe_doc.set_total_allocated_amount() - pe_doc.set_unallocated_amount() - pe_doc.clear_unallocated_reference_document_rows() + try: + pe_doc = frappe.get_doc("Payment Entry", pe) + pe_doc.validate(on_reference_unlink=True) + except Exception as e: + msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name) + msg += '
                                                            ' + msg += _("Please cancel payment entry manually first and then resubmit") + frappe.throw(msg, title=_("Payment Unlink Error")) frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s, base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s From 188bba8feb64519de2c1ded0d5432308c397f04a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Aug 2021 14:04:31 +0530 Subject: [PATCH 598/691] fix: Validation for receivingfrom customer against negative outstanding --- .../accounts/doctype/payment_entry/payment_entry.py | 12 ++++++++---- erpnext/accounts/utils.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 2231b47d9ccd..66b46675dc8a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -64,6 +64,7 @@ def validate(self, on_reference_unlink=False): self.set_title() self.set_remarks() self.validate_duplicate_entry() + self.validate_payment_type_with_outstanding() if not on_reference_unlink: self.validate_allocated_amount() self.validate_paid_invoices() @@ -120,6 +121,11 @@ def set_bank_account_data(self): if not self.get(field): self.set(field, bank_data.account) + def validate_payment_type_with_outstanding(self): + total_outstanding = sum(d.allocated_amount for d in self.get('references')) + if total_outstanding < 0 and self.party_type == 'Customer' and self.payment_type == 'Receive': + frappe.throw(_("Cannot receive from customer against negative outstanding"), title=_("Incorrect Payment Type")) + def validate_allocated_amount(self): for d in self.get("references"): if (flt(d.allocated_amount))> 0: @@ -530,10 +536,8 @@ def set_total_allocated_amount(self): base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")) - # Do not use absolute values as only credit notes could be allocated - # and total allocated should be negative in that scenario - self.total_allocated_amount = total_allocated_amount - self.base_total_allocated_amount = base_total_allocated_amount + self.total_allocated_amount = abs(total_allocated_amount) + self.base_total_allocated_amount = abs(base_total_allocated_amount) def set_unallocated_amount(self): self.unallocated_amount = 0 diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 11e113d0003c..9d84d9407416 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -559,7 +559,7 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): except Exception as e: msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name) msg += '
                                                            ' - msg += _("Please cancel payment entry manually first and then resubmit") + msg += _("Please cancel payment entry manually first") frappe.throw(msg, title=_("Payment Unlink Error")) frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s, From b5162390e5881a110ca460ad4720de40ff7d4e1d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Aug 2021 14:52:24 +0530 Subject: [PATCH 599/691] fix: Only do specific validations on reference unlink --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 +++---- erpnext/accounts/utils.py | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 66b46675dc8a..16b4720b37de 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -45,7 +45,7 @@ def setup_party_account_field(self): self.party_account = self.paid_to self.party_account_currency = self.paid_to_account_currency - def validate(self, on_reference_unlink=False): + def validate(self): self.setup_party_account_field() self.set_missing_values() self.validate_payment_type() @@ -65,9 +65,8 @@ def validate(self, on_reference_unlink=False): self.set_remarks() self.validate_duplicate_entry() self.validate_payment_type_with_outstanding() - if not on_reference_unlink: - self.validate_allocated_amount() - self.validate_paid_invoices() + self.validate_allocated_amount() + self.validate_paid_invoices() self.ensure_supplier_is_not_blocked() self.set_status() diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9d84d9407416..0d3aa8f0efda 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -555,7 +555,10 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): for pe in linked_pe: try: pe_doc = frappe.get_doc("Payment Entry", pe) - pe_doc.validate(on_reference_unlink=True) + pe_doc.set_total_allocated_amount() + pe_doc.set_unallocated_amount() + pe_doc.clear_unallocated_reference_document_rows() + pe_doc.validate_payment_type_with_outstanding() except Exception as e: msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name) msg += '
                                                            ' From 7141860c04fd1541f8d346a48e3e058f4ad443c9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Aug 2021 22:21:28 +0530 Subject: [PATCH 600/691] test: Update exchange rate in test cases --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 +++- .../accounts/doctype/payment_entry/test_payment_entry.py | 6 +++--- erpnext/accounts/utils.py | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 16b4720b37de..a991c0679bb7 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -58,6 +58,7 @@ def validate(self): self.set_amounts() self.validate_amounts() self.apply_taxes() + self.set_amounts_after_tax() self.clear_unallocated_reference_document_rows() self.validate_payment_against_negative_invoice() self.validate_transaction_reference() @@ -477,7 +478,6 @@ def apply_taxes(self): def set_amounts(self): self.set_received_amount() self.set_amounts_in_company_currency() - self.set_amounts_after_tax() self.set_total_allocated_amount() self.set_unallocated_amount() self.set_difference_amount() @@ -492,6 +492,8 @@ def validate_received_amount(self): def set_received_amount(self): self.base_received_amount = self.base_paid_amount + if self.paid_from_account_currency == self.paid_to_account_currency: + self.received_amount = self.paid_amount def set_amounts_after_tax(self): applicable_tax = 0 diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index d1302f5ae78b..420e8583eac7 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -107,7 +107,7 @@ def test_payment_entry_against_si_usd_to_usd(self): pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") pe.reference_no = "1" pe.reference_date = "2016-01-01" - pe.target_exchange_rate = 50 + pe.source_exchange_rate = 50 pe.insert() pe.submit() @@ -154,7 +154,7 @@ def test_payment_against_sales_invoice_to_check_status(self): pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") pe.reference_no = "1" pe.reference_date = "2016-01-01" - pe.target_exchange_rate = 50 + pe.source_exchange_rate = 50 pe.insert() pe.submit() @@ -463,7 +463,7 @@ def test_payment_entry_exchange_gain_loss(self): pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") pe.reference_no = "1" pe.reference_date = "2016-01-01" - pe.target_exchange_rate = 55 + pe.source_exchange_rate = 55 pe.append("deductions", { "account": "_Test Exchange Gain/Loss - _TC", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 0d3aa8f0efda..73e2c2ea8634 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -19,6 +19,7 @@ class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass class FiscalYearError(frappe.ValidationError): pass +class PaymentEntryUnlinkError(frappe.ValidationError): pass @frappe.whitelist() def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False): @@ -555,15 +556,14 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): for pe in linked_pe: try: pe_doc = frappe.get_doc("Payment Entry", pe) - pe_doc.set_total_allocated_amount() - pe_doc.set_unallocated_amount() + pe_doc.set_amounts() pe_doc.clear_unallocated_reference_document_rows() pe_doc.validate_payment_type_with_outstanding() except Exception as e: msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name) msg += '
                                                            ' msg += _("Please cancel payment entry manually first") - frappe.throw(msg, title=_("Payment Unlink Error")) + frappe.throw(msg, exc=PaymentEntryUnlinkError, title=_("Payment Unlink Error")) frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s, base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s From 02f44528f2b43c16fc8660b3e353533ba1bc3976 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Aug 2021 22:21:52 +0530 Subject: [PATCH 601/691] test: Add test case for payment entry unlink --- .../sales_invoice/test_sales_invoice.py | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index be20b18beada..623ab3f6f285 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -25,6 +25,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice from erpnext.stock.utils import get_incoming_rate +from erpnext.accounts.utils import PaymentEntryUnlinkError class TestSalesInvoice(unittest.TestCase): def make(self): @@ -135,7 +136,7 @@ def test_payment_entry_unlink_against_invoice(self): pe.paid_to_account_currency = si.currency pe.source_exchange_rate = 1 pe.target_exchange_rate = 1 - pe.paid_amount = si.grand_total + pe.paid_amount = si.outstanding_amount pe.insert() pe.submit() @@ -144,6 +145,44 @@ def test_payment_entry_unlink_against_invoice(self): self.assertRaises(frappe.LinkExistsError, si.cancel) unlink_payment_on_cancel_of_invoice() + def test_payment_entry_unlink_against_standalone_credit_note(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + si1 = create_sales_invoice(rate=1000) + si2 = create_sales_invoice(rate=300) + si3 = create_sales_invoice(qty=-1, rate=300, is_return=1) + + + pe = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Bank - _TC") + pe.append('references', { + 'reference_doctype': 'Sales Invoice', + 'reference_name': si2.name, + 'total_amount': si2.grand_total, + 'outstanding_amount': si2.outstanding_amount, + 'allocated_amount': si2.outstanding_amount + }) + + pe.append('references', { + 'reference_doctype': 'Sales Invoice', + 'reference_name': si3.name, + 'total_amount': si3.grand_total, + 'outstanding_amount': si3.outstanding_amount, + 'allocated_amount': si3.outstanding_amount + }) + + pe.reference_no = 'Test001' + pe.reference_date = nowdate() + pe.save() + pe.submit() + + unlink_payment_on_cancel_of_invoice() + si2.load_from_db() + si2.cancel() + + si1.load_from_db() + self.assertRaises(PaymentEntryUnlinkError, si1.cancel) + unlink_payment_on_cancel_of_invoice(0) + + def test_sales_invoice_calculation_export_currency(self): si = frappe.copy_doc(test_records[2]) si.currency = "USD" From a1f0cebda5f21bf4d1dba4214fd77e0dec210578 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 11 Aug 2021 11:36:49 +0530 Subject: [PATCH 602/691] fix: Do not update settings for test --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 -- 1 file changed, 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 623ab3f6f285..92bf746ad8ac 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -174,13 +174,11 @@ def test_payment_entry_unlink_against_standalone_credit_note(self): pe.save() pe.submit() - unlink_payment_on_cancel_of_invoice() si2.load_from_db() si2.cancel() si1.load_from_db() self.assertRaises(PaymentEntryUnlinkError, si1.cancel) - unlink_payment_on_cancel_of_invoice(0) def test_sales_invoice_calculation_export_currency(self): From e5b02216933a71c06457920e5f3a58a06ffa5bbc Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 11 Aug 2021 13:02:49 +0530 Subject: [PATCH 603/691] test: fix attendance request tests - Use `frappe.db.get_value` instead of `get_doc` for asserting values - Get values after cancellation as reloading attendance doc breaks due to stale doc (primary key changed after cancel of attendance request) - rollback everything on tearDown --- .../test_attendance_request.py | 68 ++++++++++++++----- 1 file changed, 52 insertions(+), 16 deletions(-) diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.py b/erpnext/hr/doctype/attendance_request/test_attendance_request.py index 3c42bd9fc354..0898476edf4a 100644 --- a/erpnext/hr/doctype/attendance_request/test_attendance_request.py +++ b/erpnext/hr/doctype/attendance_request/test_attendance_request.py @@ -15,6 +15,9 @@ def setUp(self): for doctype in ["Attendance Request", "Attendance"]: frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype)) + def tearDown(self): + frappe.db.rollback() + def test_on_duty_attendance_request(self): today = nowdate() employee = get_employee() @@ -26,15 +29,33 @@ def test_on_duty_attendance_request(self): attendance_request.company = "_Test Company" attendance_request.insert() attendance_request.submit() - attendance = frappe.get_doc('Attendance', { - 'employee': employee.name, - 'attendance_date': date(date.today().year, 1, 1), - 'docstatus': 1 - }) - self.assertEqual(attendance.status, 'Present') + + attendance = frappe.db.get_value( + "Attendance", + filters={ + "attendance_request": attendance_request.name, + "attendance_date": date(date.today().year, 1, 1) + }, + fieldname=["status", "docstatus"], + as_dict=True + ) + self.assertEqual(attendance.status, "Present") + self.assertEqual(attendance.docstatus, 1) + + # cancelling attendance request cancels linked attendances attendance_request.cancel() - attendance.reload() - self.assertEqual(attendance.docstatus, 2) + + # cancellation alters docname + # fetch attendance value again to avoid stale docname + attendance_docstatus = frappe.db.get_value( + "Attendance", + filters={ + "attendance_request": attendance_request.name, + "attendance_date": date(date.today().year, 1, 1) + }, + fieldname="docstatus" + ) + self.assertEqual(attendance_docstatus, 2) def test_work_from_home_attendance_request(self): today = nowdate() @@ -47,15 +68,30 @@ def test_work_from_home_attendance_request(self): attendance_request.company = "_Test Company" attendance_request.insert() attendance_request.submit() - attendance = frappe.get_doc('Attendance', { - 'employee': employee.name, - 'attendance_date': date(date.today().year, 1, 1), - 'docstatus': 1 - }) - self.assertEqual(attendance.status, 'Work From Home') + + attendance_status = frappe.db.get_value( + "Attendance", + filters={ + "attendance_request": attendance_request.name, + "attendance_date": date(date.today().year, 1, 1) + }, + fieldname="status" + ) + self.assertEqual(attendance_status, 'Work From Home') + attendance_request.cancel() - attendance.reload() - self.assertEqual(attendance.docstatus, 2) + + # cancellation alters docname + # fetch attendance value again to avoid stale docname + attendance_docstatus = frappe.db.get_value( + "Attendance", + filters={ + "attendance_request": attendance_request.name, + "attendance_date": date(date.today().year, 1, 1) + }, + fieldname="docstatus" + ) + self.assertEqual(attendance_docstatus, 2) def get_employee(): return frappe.get_doc("Employee", "_T-Employee-00001") From bca30d6101f539b4f68c536e83a82c1cd29b1bb7 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 11 Aug 2021 13:29:44 +0530 Subject: [PATCH 604/691] test: fix Shift Request test - Use `get_value` instead of `get_doc` - Remove unnecessary loop, only one shift assignment is made against a shift request - Get value after cancel again. Get doc is not reliable since primary key changed after cancel --- .../test_attendance_request.py | 2 ++ .../shift_request/test_shift_request.py | 33 ++++++++++++------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.py b/erpnext/hr/doctype/attendance_request/test_attendance_request.py index 0898476edf4a..9e668aa72fb3 100644 --- a/erpnext/hr/doctype/attendance_request/test_attendance_request.py +++ b/erpnext/hr/doctype/attendance_request/test_attendance_request.py @@ -19,6 +19,7 @@ def tearDown(self): frappe.db.rollback() def test_on_duty_attendance_request(self): + "Test creation/updation of Attendace from Attendance Request, on duty." today = nowdate() employee = get_employee() attendance_request = frappe.new_doc("Attendance Request") @@ -58,6 +59,7 @@ def test_on_duty_attendance_request(self): self.assertEqual(attendance_docstatus, 2) def test_work_from_home_attendance_request(self): + "Test creation/updation of Attendace from Attendance Request, work from home." today = nowdate() employee = get_employee() attendance_request = frappe.new_doc("Attendance Request") diff --git a/erpnext/hr/doctype/shift_request/test_shift_request.py b/erpnext/hr/doctype/shift_request/test_shift_request.py index 9c0d8e319858..3525540cdfd9 100644 --- a/erpnext/hr/doctype/shift_request/test_shift_request.py +++ b/erpnext/hr/doctype/shift_request/test_shift_request.py @@ -15,24 +15,35 @@ def setUp(self): for doctype in ["Shift Request", "Shift Assignment"]: frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype)) + def tearDown(self): + frappe.db.rollback() + def test_make_shift_request(self): + "Test creation/updation of Shift Assignment from Shift Request." department = frappe.get_value("Employee", "_T-Employee-00001", 'department') set_shift_approver(department) approver = frappe.db.sql("""select approver from `tabDepartment Approver` where parent= %s and parentfield = 'shift_request_approver'""", (department))[0][0] shift_request = make_shift_request(approver) - shift_assignments = frappe.db.sql(''' - SELECT shift_request, employee - FROM `tabShift Assignment` - WHERE shift_request = '{0}' - '''.format(shift_request.name), as_dict=1) - for d in shift_assignments: - employee = d.get('employee') - self.assertEqual(shift_request.employee, employee) - shift_request.cancel() - shift_assignment_doc = frappe.get_doc("Shift Assignment", {"shift_request": d.get('shift_request')}) - self.assertEqual(shift_assignment_doc.docstatus, 2) + # Only one shift assignment is created against a shift request + shift_assignment = frappe.db.get_value( + "Shift Assignment", + filters={"shift_request": shift_request.name}, + fieldname=["employee", "docstatus"], + as_dict=True + ) + self.assertEqual(shift_request.employee, shift_assignment.employee) + self.assertEqual(shift_assignment.docstatus, 1) + + shift_request.cancel() + + shift_assignment_docstatus = frappe.db.get_value( + "Shift Assignment", + filters={"shift_request": shift_request.name}, + fieldname="docstatus" + ) + self.assertEqual(shift_assignment_docstatus, 2) def test_shift_request_approver_perms(self): employee = frappe.get_doc("Employee", "_T-Employee-00001") From 99658ceb4e743c4758a306f70f5497a8818738e0 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 11 Aug 2021 14:19:07 +0530 Subject: [PATCH 605/691] 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 8a1ec4992b0d..a66d6474e0d7 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 8f1a3aef2e3f14e179f4ac91718e9f376b34198c Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 11 Aug 2021 15:17:06 +0530 Subject: [PATCH 606/691] test: fix POS Closing Entry Test - Separated into two tests, one checks if SI cancelling is blocked, the other checks PCE cancel impact - This is done because after cancel via assertRaises, damage done by cancel still exists or is partially comitted - Dont use this partially cancelled doc for any assertions further, end test at exception assertion - Use `get_value` to check SI docstatus, as its primary key changes after cancel --- .../test_pos_closing_entry.py | 43 +++++++++++++++++-- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index b596c0cf25a1..0265f43476f4 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -19,6 +19,7 @@ def setUp(self): def tearDown(self): frappe.set_user("Administrator") frappe.db.sql("delete from `tabPOS Profile`") + frappe.db.rollback() def test_pos_closing_entry(self): test_user, pos_profile = init_user_and_profile() @@ -50,7 +51,8 @@ def test_pos_closing_entry(self): self.assertEqual(pcv_doc.total_quantity, 2) self.assertEqual(pcv_doc.net_total, 6700) - def test_cancelling_of_pos_closing_entry(self): + def test_cancelling_of_consolidated_sales_invoice(self): + "Check if cancelling consolidated Sales Invoice with submitted POS Closing Entry is blocked." test_user, pos_profile = init_user_and_profile() opening_entry = create_opening_entry(pos_profile, test_user.name) @@ -83,13 +85,46 @@ def test_cancelling_of_pos_closing_entry(self): si_doc = frappe.get_doc("Sales Invoice", pos_inv1.consolidated_invoice) self.assertRaises(frappe.ValidationError, si_doc.cancel) + def test_cancelling_of_pos_closing_entry(self): + "Check impact of cancelling POS Closing Entry." + test_user, pos_profile = init_user_and_profile() + opening_entry = create_opening_entry(pos_profile, test_user.name) + + pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1) + pos_inv1.append('payments', { + 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3500 + }) + pos_inv1.submit() + + pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) + pos_inv2.append('payments', { + 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200 + }) + pos_inv2.submit() + + pcv_doc = make_closing_entry_from_opening(opening_entry) + payment = pcv_doc.payment_reconciliation[0] + + for d in pcv_doc.payment_reconciliation: + if d.mode_of_payment == 'Cash': + d.closing_amount = 6700 + + pcv_doc.submit() + + pos_inv1.load_from_db() + si_name = pos_inv1.consolidated_invoice + pcv_doc.load_from_db() pcv_doc.cancel() - si_doc.load_from_db() pos_inv1.load_from_db() - self.assertEqual(si_doc.docstatus, 2) - self.assertEqual(pos_inv1.status, 'Paid') + # After POS Closing Entry cancel, SI doc gets cancelled, unlinked and renamed + # There's no reference doc to fetch SI with new cancelled name + # which is why we are hardcoding suffix + si_docstatus = frappe.db.get_value("Sales Invoice", si_name+"-CANC-0", "docstatus") + self.assertEqual(si_docstatus, 2) + self.assertEqual(pos_inv1.status, 'Paid') + self.assertIsNone(pos_inv1.consolidated_invoice) def init_user_and_profile(**args): user = 'test@example.com' From 867939fcae12c689c145c0d8610a469508f1b900 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 11 Aug 2021 16:40:07 +0530 Subject: [PATCH 607/691] test: fixed asset movement tests - set cwip account in company to avoid value missing - removed unused statement - removed trailing spaces --- .../test_pos_closing_entry.py | 1 - .../asset_movement/test_asset_movement.py | 19 ++++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index 0265f43476f4..c585f310b16e 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -103,7 +103,6 @@ def test_cancelling_of_pos_closing_entry(self): pos_inv2.submit() pcv_doc = make_closing_entry_from_opening(opening_entry) - payment = pcv_doc.payment_reconciliation[0] for d in pcv_doc.payment_reconciliation: if d.mode_of_payment == 'Cash': diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py index cddee5fa0f11..985bca9d0de8 100644 --- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py @@ -15,6 +15,7 @@ class TestAssetMovement(unittest.TestCase): def setUp(self): + frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC") create_asset_data() make_location() @@ -45,12 +46,12 @@ def test_movement(self): 'location_name': 'Test Location 2' }).insert() - movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company, + movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company, assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}], reference_doctype = 'Purchase Receipt', reference_name = pr.name) self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2") - movement2 = create_asset_movement(purpose = 'Transfer', company = asset.company, + movement2 = create_asset_movement(purpose = 'Transfer', company = asset.company, assets = [{ 'asset': asset.name , 'source_location': 'Test Location 2', 'target_location': 'Test Location'}], reference_doctype = 'Purchase Receipt', reference_name = pr.name) self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location") @@ -59,18 +60,18 @@ def test_movement(self): self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location") employee = make_employee("testassetmovemp@example.com", company="_Test Company") - movement3 = create_asset_movement(purpose = 'Issue', company = asset.company, + movement3 = create_asset_movement(purpose = 'Issue', company = asset.company, assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'to_employee': employee}], reference_doctype = 'Purchase Receipt', reference_name = pr.name) - + # after issuing asset should belong to an employee not at a location self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), None) self.assertEqual(frappe.db.get_value("Asset", asset.name, "custodian"), employee) - + def test_last_movement_cancellation(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") - + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') asset = frappe.get_doc('Asset', asset_name) asset.calculate_depreciation = 1 @@ -85,17 +86,17 @@ def test_last_movement_cancellation(self): }) if asset.docstatus == 0: asset.submit() - + if not frappe.db.exists("Location", "Test Location 2"): frappe.get_doc({ 'doctype': 'Location', 'location_name': 'Test Location 2' }).insert() - + movement = frappe.get_doc({'doctype': 'Asset Movement', 'reference_name': pr.name }) self.assertRaises(frappe.ValidationError, movement.cancel) - movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company, + movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company, assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}], reference_doctype = 'Purchase Receipt', reference_name = pr.name) self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2") From 455d300fca044643eb37a78568e123a62a94ef38 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 11 Aug 2021 17:00:46 +0530 Subject: [PATCH 608/691] Revert "test: fix POS Closing Entry Test" This reverts commit 8f1a3aef2e3f14e179f4ac91718e9f376b34198c. --- .../test_pos_closing_entry.py | 42 ++----------------- .../asset_movement/test_asset_movement.py | 4 +- 2 files changed, 6 insertions(+), 40 deletions(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index c585f310b16e..b596c0cf25a1 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -19,7 +19,6 @@ def setUp(self): def tearDown(self): frappe.set_user("Administrator") frappe.db.sql("delete from `tabPOS Profile`") - frappe.db.rollback() def test_pos_closing_entry(self): test_user, pos_profile = init_user_and_profile() @@ -51,8 +50,7 @@ def test_pos_closing_entry(self): self.assertEqual(pcv_doc.total_quantity, 2) self.assertEqual(pcv_doc.net_total, 6700) - def test_cancelling_of_consolidated_sales_invoice(self): - "Check if cancelling consolidated Sales Invoice with submitted POS Closing Entry is blocked." + def test_cancelling_of_pos_closing_entry(self): test_user, pos_profile = init_user_and_profile() opening_entry = create_opening_entry(pos_profile, test_user.name) @@ -85,45 +83,13 @@ def test_cancelling_of_consolidated_sales_invoice(self): si_doc = frappe.get_doc("Sales Invoice", pos_inv1.consolidated_invoice) self.assertRaises(frappe.ValidationError, si_doc.cancel) - def test_cancelling_of_pos_closing_entry(self): - "Check impact of cancelling POS Closing Entry." - test_user, pos_profile = init_user_and_profile() - opening_entry = create_opening_entry(pos_profile, test_user.name) - - pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1) - pos_inv1.append('payments', { - 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3500 - }) - pos_inv1.submit() - - pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) - pos_inv2.append('payments', { - 'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 3200 - }) - pos_inv2.submit() - - pcv_doc = make_closing_entry_from_opening(opening_entry) - - for d in pcv_doc.payment_reconciliation: - if d.mode_of_payment == 'Cash': - d.closing_amount = 6700 - - pcv_doc.submit() - - pos_inv1.load_from_db() - si_name = pos_inv1.consolidated_invoice - pcv_doc.load_from_db() pcv_doc.cancel() + si_doc.load_from_db() pos_inv1.load_from_db() - - # After POS Closing Entry cancel, SI doc gets cancelled, unlinked and renamed - # There's no reference doc to fetch SI with new cancelled name - # which is why we are hardcoding suffix - si_docstatus = frappe.db.get_value("Sales Invoice", si_name+"-CANC-0", "docstatus") - self.assertEqual(si_docstatus, 2) + self.assertEqual(si_doc.docstatus, 2) self.assertEqual(pos_inv1.status, 'Paid') - self.assertIsNone(pos_inv1.consolidated_invoice) + def init_user_and_profile(**args): user = 'test@example.com' diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py index 985bca9d0de8..2b2d2b44004f 100644 --- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py @@ -51,7 +51,7 @@ def test_movement(self): reference_doctype = 'Purchase Receipt', reference_name = pr.name) self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2") - movement2 = create_asset_movement(purpose = 'Transfer', company = asset.company, + create_asset_movement(purpose = 'Transfer', company = asset.company, assets = [{ 'asset': asset.name , 'source_location': 'Test Location 2', 'target_location': 'Test Location'}], reference_doctype = 'Purchase Receipt', reference_name = pr.name) self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location") @@ -60,7 +60,7 @@ def test_movement(self): self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location") employee = make_employee("testassetmovemp@example.com", company="_Test Company") - movement3 = create_asset_movement(purpose = 'Issue', company = asset.company, + create_asset_movement(purpose = 'Issue', company = asset.company, assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'to_employee': employee}], reference_doctype = 'Purchase Receipt', reference_name = pr.name) From 08e4026456a698e4a41e1919795a03366e0ae2cb Mon Sep 17 00:00:00 2001 From: Marica Date: Thu, 12 Aug 2021 10:31:01 +0530 Subject: [PATCH 609/691] 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 0cc8ca48aace..d44685060c73 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 9e56ad413069..fc3d719a7809 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 54c31ed33c6112cb9040470dc40cbb78c489d418 Mon Sep 17 00:00:00 2001 From: Ganga Manoj Date: Thu, 12 Aug 2021 13:42:56 +0530 Subject: [PATCH 610/691] feat: depreciate asset after sale (#26543) --- .../doctype/sales_invoice/sales_invoice.py | 106 +++++++++++++++--- .../sales_invoice/test_sales_invoice.py | 25 +++++ erpnext/assets/doctype/asset/asset.py | 44 ++++++-- 3 files changed, 155 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index cecc1a18df4a..51548e9a5bd4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext import frappe.defaults -from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, get_link_to_form, formatdate +from frappe.utils import cint, flt, getdate, add_days, add_months, cstr, nowdate, get_link_to_form, formatdate from frappe import _, msgprint, throw from erpnext.accounts.party import get_party_account, get_due_date, get_party_details from frappe.model.mapper import get_mapped_doc @@ -13,7 +13,7 @@ from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data from erpnext.assets.doctype.asset.depreciation \ - import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain + import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain, post_depreciation_entries from erpnext.stock.doctype.batch.batch import set_batch_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no from erpnext.setup.doctype.company.company import update_company_current_month_sales @@ -920,27 +920,24 @@ def make_item_gl_entries(self, gl_entries): for item in self.get("items"): if flt(item.base_net_amount, item.precision("base_net_amount")): if item.is_fixed_asset: - if item.get('asset'): - asset = frappe.get_doc("Asset", item.asset) - else: - frappe.throw(_( - "Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name), - title=_("Missing Asset") - ) - if (len(asset.finance_books) > 1 and not item.finance_book - and asset.finance_books[0].finance_book): - frappe.throw(_("Select finance book for the item {0} at row {1}") - .format(item.item_code, item.idx)) + asset = self.get_asset(item) if self.is_return: fixed_asset_gl_entries = get_gl_entries_on_asset_regain(asset, item.base_net_amount, item.finance_book) asset.db_set("disposal_date", None) + + if asset.calculate_depreciation: + self.reset_depreciation_schedule(asset) + else: fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, item.base_net_amount, item.finance_book) asset.db_set("disposal_date", self.posting_date) + if asset.calculate_depreciation: + self.depreciate_asset(asset) + for gle in fixed_asset_gl_entries: gle["against"] = self.customer gl_entries.append(self.get_gl_dict(gle, item=item)) @@ -972,6 +969,89 @@ def make_item_gl_entries(self, gl_entries): erpnext.is_perpetual_inventory_enabled(self.company): gl_entries += super(SalesInvoice, self).get_gl_entries() + def get_asset(self, item): + if item.get('asset'): + asset = frappe.get_doc("Asset", item.asset) + else: + frappe.throw(_( + "Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name), + title=_("Missing Asset") + ) + + self.check_finance_books(item, asset) + return asset + + def check_finance_books(self, item, asset): + if (len(asset.finance_books) > 1 and not item.finance_book + and asset.finance_books[0].finance_book): + frappe.throw(_("Select finance book for the item {0} at row {1}") + .format(item.item_code, item.idx)) + + def depreciate_asset(self, asset): + asset.flags.ignore_validate_update_after_submit = True + asset.prepare_depreciation_data(self.posting_date) + asset.save() + + post_depreciation_entries(self.posting_date) + + def reset_depreciation_schedule(self, asset): + asset.flags.ignore_validate_update_after_submit = True + + # recreate original depreciation schedule of the asset + asset.prepare_depreciation_data() + + self.modify_depreciation_schedule_for_asset_repairs(asset) + asset.save() + + self.delete_depreciation_entry_made_after_sale(asset) + + def modify_depreciation_schedule_for_asset_repairs(self, asset): + asset_repairs = frappe.get_all( + 'Asset Repair', + filters = {'asset': asset.name}, + fields = ['name', 'increase_in_asset_life'] + ) + + for repair in asset_repairs: + if repair.increase_in_asset_life: + asset_repair = frappe.get_doc('Asset Repair', repair.name) + asset_repair.modify_depreciation_schedule() + asset.prepare_depreciation_data() + + def delete_depreciation_entry_made_after_sale(self, asset): + from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry + + posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice() + + row = -1 + finance_book = asset.get('schedules')[0].get('finance_book') + for schedule in asset.get('schedules'): + if schedule.finance_book != finance_book: + row = 0 + finance_book = schedule.finance_book + else: + row += 1 + + if schedule.schedule_date == posting_date_of_original_invoice: + if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice): + reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) + reverse_journal_entry.posting_date = nowdate() + reverse_journal_entry.submit() + + def get_posting_date_of_sales_invoice(self): + return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date') + + # if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone + def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_invoice): + for finance_book in asset.get('finance_books'): + if schedule.finance_book == finance_book.finance_book: + orginal_schedule_date = add_months(finance_book.depreciation_start_date, + row * cint(finance_book.frequency_of_depreciation)) + + if orginal_schedule_date == posting_date_of_original_invoice: + return True + return False + def set_asset_status(self, asset): if self.is_return: asset.set_status() diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index be20b18beada..f98275bcc1ea 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -12,6 +12,7 @@ from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import WarehouseMissingError from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data +from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError from frappe.model.naming import make_autoname @@ -2101,6 +2102,30 @@ def test_item_tax_net_range(self): sales_invoice.save() self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC") + def test_asset_depreciation_on_sale(self): + """ + Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on Sept 30. + """ + + create_asset_data() + asset = create_asset(item_code="Macbook Pro", calculate_depreciation=1, submit=1) + post_depreciation_entries(getdate("2021-09-30")) + + create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000, posting_date=getdate("2021-09-30")) + asset.load_from_db() + + expected_values = [ + ["2020-06-30", 1311.48, 1311.48], + ["2021-06-30", 20000.0, 21311.48], + ["2021-09-30", 3966.76, 25278.24] + ] + + for i, schedule in enumerate(asset.schedules): + self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_values[i][1], schedule.depreciation_amount) + self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) + self.assertTrue(schedule.journal_entry) + def get_sales_invoice_for_e_invoice(): si = make_sales_invoice_for_ewaybill() si.naming_series = 'INV-2020-.#####' diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 66f0bdcd5884..b7ca69333151 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -56,12 +56,12 @@ def validate_asset_and_reference(self): if self.is_existing_asset and self.purchase_invoice: frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name)) - def prepare_depreciation_data(self): + def prepare_depreciation_data(self, date_of_sale=None): if self.calculate_depreciation: self.value_after_depreciation = 0 self.set_depreciation_rate() - self.make_depreciation_schedule() - self.set_accumulated_depreciation() + self.make_depreciation_schedule(date_of_sale) + self.set_accumulated_depreciation(date_of_sale) else: self.finance_books = [] self.value_after_depreciation = (flt(self.gross_purchase_amount) - @@ -167,7 +167,7 @@ def set_depreciation_rate(self): d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True), d.precision("rate_of_depreciation")) - def make_depreciation_schedule(self): + def make_depreciation_schedule(self, date_of_sale): if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.schedules: self.schedules = [] @@ -212,6 +212,21 @@ def make_depreciation_schedule(self): # so monthly schedule date is calculated by removing 11 months from it monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1) + # if asset is being sold + if date_of_sale: + from_date = self.get_from_date(d.finance_book) + depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount, + from_date, date_of_sale) + + self.append("schedules", { + "schedule_date": date_of_sale, + "depreciation_amount": depreciation_amount, + "depreciation_method": d.depreciation_method, + "finance_book": d.finance_book, + "finance_book_id": d.idx + }) + break + # For first row if has_pro_rata and n==0: depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount, @@ -303,6 +318,21 @@ def clear_depreciation_schedule(self): break return start + def get_from_date(self, finance_book): + if not self.get('schedules'): + return self.available_for_use_date + + if len(self.finance_books) == 1: + return self.schedules[-1].schedule_date + + from_date = "" + for schedule in self.get('schedules'): + if schedule.finance_book == finance_book: + from_date = schedule.schedule_date + + if from_date: + return from_date + return self.available_for_use_date # if it returns True, depreciation_amount will not be equal for the first and last rows def check_is_pro_rata(self, row): @@ -357,7 +387,7 @@ def validate_asset_finance_books(self, row): frappe.throw(_("Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date") .format(row.idx)) - def set_accumulated_depreciation(self, ignore_booked_entry = False): + def set_accumulated_depreciation(self, date_of_sale=None, ignore_booked_entry = False): straight_line_idx = [d.idx for d in self.get("schedules") if d.depreciation_method == 'Straight Line'] finance_books = [] @@ -365,7 +395,7 @@ def set_accumulated_depreciation(self, ignore_booked_entry = False): if ignore_booked_entry and d.journal_entry: continue - if d.finance_book_id not in finance_books: + if int(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(int(d.finance_book_id)) @@ -374,7 +404,7 @@ def set_accumulated_depreciation(self, ignore_booked_entry = False): 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: + if straight_line_idx and i == max(straight_line_idx) - 1 and not date_of_sale: book = self.get('finance_books')[cint(d.finance_book_id) - 1] depreciation_amount += flt(value_after_depreciation - flt(book.expected_value_after_useful_life), d.precision("depreciation_amount")) From 88f13fef81b668547a9c66a68bb2fa9aea6973b2 Mon Sep 17 00:00:00 2001 From: Saqib Date: Thu, 12 Aug 2021 16:26:51 +0530 Subject: [PATCH 611/691] fix: ZeroDivisionError on creating e-invoice for credit note (#26915) --- 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 4276946ebb3b..fa7e88d3a15f 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 f4b2f4aaf7823cdae0ed344035fc8835ba2d9909 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 12 Aug 2021 17:11:38 +0530 Subject: [PATCH 612/691] 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 e65442dbff4e..2373512cca4b 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 b8658d003f0364bb195d641d3024859471e09334 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 12 Aug 2021 20:03:28 +0530 Subject: [PATCH 613/691] fix: from_warehouse getting set to None (#26920) --- erpnext/stock/doctype/stock_entry/stock_entry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 82933199d55c..7b31d2fdf2de 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1185,7 +1185,7 @@ def get_unconsumed_raw_materials(self): wo = frappe.get_doc("Work Order", self.work_order) wo_items = frappe.get_all('Work Order Item', filters={'parent': self.work_order}, - fields=["item_code", "required_qty", "consumed_qty", "transferred_qty"] + fields=["item_code", "source_warehouse", "required_qty", "consumed_qty", "transferred_qty"] ) work_order_qty = wo.material_transferred_for_manufacturing or wo.qty @@ -1205,7 +1205,7 @@ def get_unconsumed_raw_materials(self): if qty > 0: self.add_to_stock_entry_detail({ item.item_code: { - "from_warehouse": wo.wip_warehouse, + "from_warehouse": wo.wip_warehouse or item.source_warehouse, "to_warehouse": "", "qty": qty, "item_name": item.item_name, @@ -1857,4 +1857,4 @@ def get_supplied_items(purchase_order): supplied_item.total_supplied_qty = flt(supplied_item.supplied_qty) - flt(supplied_item.returned_qty) - return supplied_item_details \ No newline at end of file + return supplied_item_details From 1de4c01942e32eff83bce98805a803ea3c26f721 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 12 Aug 2021 23:06:34 +0530 Subject: [PATCH 614/691] fix: Deferred Revenue Section should be collapsible only if its not enabled (#26928) --- .../doctype/purchase_invoice_item/purchase_invoice_item.json | 3 ++- .../doctype/sales_invoice_item/sales_invoice_item.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) 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 b39022dd7587..528c9319032a 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -501,6 +501,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "enable_deferred_expense", "fieldname": "deferred_expense_section", "fieldtype": "Section Break", "label": "Deferred Expense" @@ -854,7 +855,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-06-16 19:43:51.099386", + "modified": "2021-08-12 20:14:45.506639", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", 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 6690bdafc346..dad29c0956e8 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -473,6 +473,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "enable_deferred_revenue", "fieldname": "deferred_revenue", "fieldtype": "Section Break", "label": "Deferred Revenue" @@ -825,7 +826,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-06-21 23:03:11.599901", + "modified": "2021-08-12 20:15:42.668399", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", From 587d2db6a9048c61414e00ab9d048be7efaa9db2 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Fri, 13 Aug 2021 10:59:06 +0530 Subject: [PATCH 615/691] fix: show proper currency symbol in taxes and charges table (#26827) Co-authored-by: Saqib Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> --- .../purchase_taxes_and_charges.json | 20 +++++++++---------- .../sales_taxes_and_charges.json | 20 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index 1fa68e0a8a8e..d86abade924d 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -22,7 +22,7 @@ "cost_center", "dimension_col_break", "section_break_9", - "currency", + "account_currency", "tax_amount", "tax_amount_after_discount_amount", "total", @@ -208,14 +208,6 @@ "fieldname": "dimension_col_break", "fieldtype": "Column Break" }, - { - "fetch_from": "account_head.account_currency", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Account Currency", - "options": "Currency", - "read_only": 1 - }, { "default": "0", "depends_on": "eval:['Purchase Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)", @@ -223,12 +215,20 @@ "fieldname": "included_in_paid_amount", "fieldtype": "Check", "label": "Considered In Paid Amount" + }, + { + "fetch_from": "account_head.account_currency", + "fieldname": "account_currency", + "fieldtype": "Link", + "label": "Account Currency", + "options": "Currency", + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-06-14 01:43:50.750455", + "modified": "2021-08-05 20:04:36.618240", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", 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 cfdb167bbca2..3a871bfceded 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 @@ -19,7 +19,7 @@ "section_break_8", "rate", "section_break_9", - "currency", + "account_currency", "tax_amount", "total", "tax_amount_after_discount_amount", @@ -186,14 +186,6 @@ "fieldname": "dimension_col_break", "fieldtype": "Column Break" }, - { - "fetch_from": "account_head.account_currency", - "fieldname": "currency", - "fieldtype": "Link", - "label": "Account Currency", - "options": "Currency", - "read_only": 1 - }, { "default": "0", "depends_on": "eval:['Sales Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)", @@ -210,13 +202,21 @@ "label": "Dont Recompute tax", "print_hide": 1, "read_only": 1 + }, + { + "fetch_from": "account_head.account_currency", + "fieldname": "account_currency", + "fieldtype": "Link", + "label": "Account Currency", + "options": "Currency", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-07-27 12:40:59.051803", + "modified": "2021-08-05 20:04:01.726867", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", From b32c2fa56190b9d745ac9e967e0927bf675bce5f Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Fri, 13 Aug 2021 12:19:16 +0530 Subject: [PATCH 616/691] fix: updated erpnext wspace json files (#26380) * fix: updated erpnext wspace json files * fix: updated wspace json files * fix: updated wspace json files * fix: removed padding code from wspace json files * fix: Updated restrict_to_domain in wspace json Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> --- .../workspace/accounting/accounting.json | 126 +++++++++++++++++- .../workspace/agriculture/agriculture.json | 32 ++++- erpnext/assets/workspace/assets/assets.json | 33 ++++- erpnext/buying/workspace/buying/buying.json | 65 ++++++++- erpnext/crm/workspace/crm/crm.json | 54 +++++++- .../workspace/education/education.json | 82 +++++++++++- .../erpnext_integrations.json | 45 ++++++- .../erpnext_integrations_settings.json | 39 +++++- .../workspace/healthcare/healthcare.json | 66 ++++++++- erpnext/hr/workspace/hr/hr.json | 104 ++++++++++++++- .../loan_management/loan_management.json | 40 +++++- .../manufacturing/manufacturing.json | 44 +++++- .../workspace/non_profit/non_profit.json | 39 +++++- .../payroll/workspace/payroll/payroll.json | 46 ++++++- .../projects/workspace/projects/projects.json | 35 ++++- .../workspace/quality/quality.json | 32 ++++- erpnext/selling/workspace/retail/retail.json | 27 +++- .../selling/workspace/selling/selling.json | 71 +++++++++- .../erpnext_settings/erpnext_settings.json | 25 ++-- erpnext/setup/workspace/home/home.json | 45 ++++++- erpnext/stock/workspace/stock/stock.json | 87 +++++++++++- .../support/workspace/support/support.json | 35 ++++- .../workspace/utilities/utilities.json | 24 +++- 23 files changed, 1092 insertions(+), 104 deletions(-) diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json index 821fa4d2af48..b5bd14d1370d 100644 --- a/erpnext/accounts/workspace/accounting/accounting.json +++ b/erpnext/accounts/workspace/accounting/accounting.json @@ -1,28 +1,32 @@ { - "category": "Modules", + "category": "", "charts": [ { "chart_name": "Profit and Loss", "label": "Profit and Loss" } ], + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Accounts\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Profit and Loss\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chart of Accounts\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Invoice\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Invoice\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Journal Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Payment Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Accounts Receivable\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"General Ledger\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Trial Balance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounting Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"General Ledger\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounts Receivable\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Accounts Payable\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Financial Statements\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Multi Currency\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Bank Statement\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Subscription Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Goods and Services Tax (GST India)\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Share Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Cost Center and Budgeting\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Opening and Closing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Taxes\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Profitability\", \"col\": 4}}]", "creation": "2020-03-02 15:41:59.515192", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "accounting", "idx": 0, "is_default": 0, - "is_standard": 1, + "is_standard": 0, "label": "Accounting", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Accounting Masters", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -31,6 +35,7 @@ "hidden": 0, "is_query_report": 0, "label": "Company", + "link_count": 0, "link_to": "Company", "link_type": "DocType", "onboard": 1, @@ -41,6 +46,7 @@ "hidden": 0, "is_query_report": 0, "label": "Chart of Accounts", + "link_count": 0, "link_to": "Account", "link_type": "DocType", "onboard": 1, @@ -51,6 +57,7 @@ "hidden": 0, "is_query_report": 0, "label": "Accounts Settings", + "link_count": 0, "link_to": "Accounts Settings", "link_type": "DocType", "onboard": 0, @@ -61,6 +68,7 @@ "hidden": 0, "is_query_report": 0, "label": "Fiscal Year", + "link_count": 0, "link_to": "Fiscal Year", "link_type": "DocType", "onboard": 0, @@ -71,6 +79,7 @@ "hidden": 0, "is_query_report": 0, "label": "Accounting Dimension", + "link_count": 0, "link_to": "Accounting Dimension", "link_type": "DocType", "onboard": 0, @@ -81,6 +90,7 @@ "hidden": 0, "is_query_report": 0, "label": "Finance Book", + "link_count": 0, "link_to": "Finance Book", "link_type": "DocType", "onboard": 0, @@ -91,6 +101,7 @@ "hidden": 0, "is_query_report": 0, "label": "Accounting Period", + "link_count": 0, "link_to": "Accounting Period", "link_type": "DocType", "onboard": 0, @@ -101,6 +112,7 @@ "hidden": 0, "is_query_report": 0, "label": "Payment Term", + "link_count": 0, "link_to": "Payment Term", "link_type": "DocType", "onboard": 0, @@ -110,6 +122,7 @@ "hidden": 0, "is_query_report": 0, "label": "General Ledger", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -118,6 +131,7 @@ "hidden": 0, "is_query_report": 0, "label": "Journal Entry", + "link_count": 0, "link_to": "Journal Entry", "link_type": "DocType", "onboard": 0, @@ -128,6 +142,7 @@ "hidden": 0, "is_query_report": 0, "label": "Journal Entry Template", + "link_count": 0, "link_to": "Journal Entry Template", "link_type": "DocType", "onboard": 0, @@ -138,6 +153,7 @@ "hidden": 0, "is_query_report": 1, "label": "General Ledger", + "link_count": 0, "link_to": "General Ledger", "link_type": "Report", "onboard": 0, @@ -148,6 +164,7 @@ "hidden": 0, "is_query_report": 1, "label": "Customer Ledger Summary", + "link_count": 0, "link_to": "Customer Ledger Summary", "link_type": "Report", "onboard": 0, @@ -158,6 +175,7 @@ "hidden": 0, "is_query_report": 1, "label": "Supplier Ledger Summary", + "link_count": 0, "link_to": "Supplier Ledger Summary", "link_type": "Report", "onboard": 0, @@ -167,6 +185,7 @@ "hidden": 0, "is_query_report": 0, "label": "Accounts Receivable", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -175,6 +194,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sales Invoice", + "link_count": 0, "link_to": "Sales Invoice", "link_type": "DocType", "onboard": 1, @@ -185,6 +205,7 @@ "hidden": 0, "is_query_report": 0, "label": "Customer", + "link_count": 0, "link_to": "Customer", "link_type": "DocType", "onboard": 1, @@ -195,6 +216,7 @@ "hidden": 0, "is_query_report": 0, "label": "Payment Entry", + "link_count": 0, "link_to": "Payment Entry", "link_type": "DocType", "onboard": 0, @@ -205,6 +227,7 @@ "hidden": 0, "is_query_report": 0, "label": "Payment Request", + "link_count": 0, "link_to": "Payment Request", "link_type": "DocType", "onboard": 0, @@ -215,6 +238,7 @@ "hidden": 0, "is_query_report": 1, "label": "Accounts Receivable", + "link_count": 0, "link_to": "Accounts Receivable", "link_type": "Report", "onboard": 0, @@ -225,6 +249,7 @@ "hidden": 0, "is_query_report": 1, "label": "Accounts Receivable Summary", + "link_count": 0, "link_to": "Accounts Receivable Summary", "link_type": "Report", "onboard": 0, @@ -235,6 +260,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Register", + "link_count": 0, "link_to": "Sales Register", "link_type": "Report", "onboard": 0, @@ -245,6 +271,7 @@ "hidden": 0, "is_query_report": 1, "label": "Item-wise Sales Register", + "link_count": 0, "link_to": "Item-wise Sales Register", "link_type": "Report", "onboard": 0, @@ -255,6 +282,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Order Analysis", + "link_count": 0, "link_to": "Sales Order Analysis", "link_type": "Report", "onboard": 0, @@ -265,6 +293,7 @@ "hidden": 0, "is_query_report": 1, "label": "Delivered Items To Be Billed", + "link_count": 0, "link_to": "Delivered Items To Be Billed", "link_type": "Report", "onboard": 0, @@ -274,6 +303,7 @@ "hidden": 0, "is_query_report": 0, "label": "Accounts Payable", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -282,6 +312,7 @@ "hidden": 0, "is_query_report": 0, "label": "Purchase Invoice", + "link_count": 0, "link_to": "Purchase Invoice", "link_type": "DocType", "onboard": 1, @@ -292,6 +323,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier", + "link_count": 0, "link_to": "Supplier", "link_type": "DocType", "onboard": 1, @@ -302,6 +334,7 @@ "hidden": 0, "is_query_report": 0, "label": "Payment Entry", + "link_count": 0, "link_to": "Payment Entry", "link_type": "DocType", "onboard": 0, @@ -312,6 +345,7 @@ "hidden": 0, "is_query_report": 1, "label": "Accounts Payable", + "link_count": 0, "link_to": "Accounts Payable", "link_type": "Report", "onboard": 0, @@ -322,6 +356,7 @@ "hidden": 0, "is_query_report": 1, "label": "Accounts Payable Summary", + "link_count": 0, "link_to": "Accounts Payable Summary", "link_type": "Report", "onboard": 0, @@ -332,6 +367,7 @@ "hidden": 0, "is_query_report": 1, "label": "Purchase Register", + "link_count": 0, "link_to": "Purchase Register", "link_type": "Report", "onboard": 0, @@ -342,6 +378,7 @@ "hidden": 0, "is_query_report": 1, "label": "Item-wise Purchase Register", + "link_count": 0, "link_to": "Item-wise Purchase Register", "link_type": "Report", "onboard": 0, @@ -352,6 +389,7 @@ "hidden": 0, "is_query_report": 1, "label": "Purchase Order Analysis", + "link_count": 0, "link_to": "Purchase Order Analysis", "link_type": "Report", "onboard": 0, @@ -362,6 +400,7 @@ "hidden": 0, "is_query_report": 1, "label": "Received Items To Be Billed", + "link_count": 0, "link_to": "Received Items To Be Billed", "link_type": "Report", "onboard": 0, @@ -371,6 +410,7 @@ "hidden": 0, "is_query_report": 0, "label": "Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -379,6 +419,7 @@ "hidden": 0, "is_query_report": 1, "label": "Trial Balance for Party", + "link_count": 0, "link_to": "Trial Balance for Party", "link_type": "Report", "onboard": 0, @@ -389,6 +430,7 @@ "hidden": 0, "is_query_report": 1, "label": "Payment Period Based On Invoice Date", + "link_count": 0, "link_to": "Payment Period Based On Invoice Date", "link_type": "Report", "onboard": 0, @@ -399,6 +441,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Partners Commission", + "link_count": 0, "link_to": "Sales Partners Commission", "link_type": "Report", "onboard": 0, @@ -409,6 +452,7 @@ "hidden": 0, "is_query_report": 1, "label": "Customer Credit Balance", + "link_count": 0, "link_to": "Customer Credit Balance", "link_type": "Report", "onboard": 0, @@ -419,6 +463,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Payment Summary", + "link_count": 0, "link_to": "Sales Payment Summary", "link_type": "Report", "onboard": 0, @@ -429,6 +474,7 @@ "hidden": 0, "is_query_report": 1, "label": "Address And Contacts", + "link_count": 0, "link_to": "Address And Contacts", "link_type": "Report", "onboard": 0, @@ -439,6 +485,7 @@ "hidden": 0, "is_query_report": 1, "label": "Tax Detail", + "link_count": 0, "link_to": "Tax Detail", "link_type": "Report", "onboard": 0, @@ -449,6 +496,7 @@ "hidden": 0, "is_query_report": 1, "label": "DATEV Export", + "link_count": 0, "link_to": "DATEV", "link_type": "Report", "onboard": 0, @@ -460,6 +508,7 @@ "hidden": 0, "is_query_report": 1, "label": "UAE VAT 201", + "link_count": 0, "link_to": "UAE VAT 201", "link_type": "Report", "onboard": 0, @@ -470,6 +519,7 @@ "hidden": 0, "is_query_report": 0, "label": "Financial Statements", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -478,6 +528,7 @@ "hidden": 0, "is_query_report": 1, "label": "Trial Balance", + "link_count": 0, "link_to": "Trial Balance", "link_type": "Report", "onboard": 0, @@ -488,6 +539,7 @@ "hidden": 0, "is_query_report": 1, "label": "Profit and Loss Statement", + "link_count": 0, "link_to": "Profit and Loss Statement", "link_type": "Report", "onboard": 0, @@ -498,6 +550,7 @@ "hidden": 0, "is_query_report": 1, "label": "Balance Sheet", + "link_count": 0, "link_to": "Balance Sheet", "link_type": "Report", "onboard": 0, @@ -508,6 +561,7 @@ "hidden": 0, "is_query_report": 1, "label": "Cash Flow", + "link_count": 0, "link_to": "Cash Flow", "link_type": "Report", "onboard": 0, @@ -518,6 +572,7 @@ "hidden": 0, "is_query_report": 1, "label": "Consolidated Financial Statement", + "link_count": 0, "link_to": "Consolidated Financial Statement", "link_type": "Report", "onboard": 0, @@ -527,6 +582,7 @@ "hidden": 0, "is_query_report": 0, "label": "Multi Currency", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -535,6 +591,7 @@ "hidden": 0, "is_query_report": 0, "label": "Currency", + "link_count": 0, "link_to": "Currency", "link_type": "DocType", "onboard": 0, @@ -545,6 +602,7 @@ "hidden": 0, "is_query_report": 0, "label": "Currency Exchange", + "link_count": 0, "link_to": "Currency Exchange", "link_type": "DocType", "onboard": 0, @@ -555,6 +613,7 @@ "hidden": 0, "is_query_report": 0, "label": "Exchange Rate Revaluation", + "link_count": 0, "link_to": "Exchange Rate Revaluation", "link_type": "DocType", "onboard": 0, @@ -564,6 +623,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -572,6 +632,7 @@ "hidden": 0, "is_query_report": 0, "label": "Payment Gateway Account", + "link_count": 0, "link_to": "Payment Gateway Account", "link_type": "DocType", "onboard": 0, @@ -582,6 +643,7 @@ "hidden": 0, "is_query_report": 0, "label": "Terms and Conditions Template", + "link_count": 0, "link_to": "Terms and Conditions", "link_type": "DocType", "onboard": 0, @@ -592,6 +654,7 @@ "hidden": 0, "is_query_report": 0, "label": "Mode of Payment", + "link_count": 0, "link_to": "Mode of Payment", "link_type": "DocType", "onboard": 0, @@ -601,6 +664,7 @@ "hidden": 0, "is_query_report": 0, "label": "Bank Statement", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -609,6 +673,7 @@ "hidden": 0, "is_query_report": 0, "label": "Bank", + "link_count": 0, "link_to": "Bank", "link_type": "DocType", "onboard": 0, @@ -619,6 +684,7 @@ "hidden": 0, "is_query_report": 0, "label": "Bank Account", + "link_count": 0, "link_to": "Bank Account", "link_type": "DocType", "onboard": 0, @@ -629,6 +695,7 @@ "hidden": 0, "is_query_report": 0, "label": "Bank Clearance", + "link_count": 0, "link_to": "Bank Clearance", "link_type": "DocType", "onboard": 0, @@ -639,6 +706,7 @@ "hidden": 0, "is_query_report": 0, "label": "Bank Reconciliation Tool", + "link_count": 0, "link_to": "Bank Reconciliation Tool", "link_type": "DocType", "onboard": 0, @@ -649,6 +717,7 @@ "hidden": 0, "is_query_report": 1, "label": "Bank Reconciliation Statement", + "link_count": 0, "link_to": "Bank Reconciliation Statement", "link_type": "Report", "onboard": 0, @@ -658,6 +727,7 @@ "hidden": 0, "is_query_report": 0, "label": "Subscription Management", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -666,6 +736,7 @@ "hidden": 0, "is_query_report": 0, "label": "Subscription Plan", + "link_count": 0, "link_to": "Subscription Plan", "link_type": "DocType", "onboard": 0, @@ -676,6 +747,7 @@ "hidden": 0, "is_query_report": 0, "label": "Subscription", + "link_count": 0, "link_to": "Subscription", "link_type": "DocType", "onboard": 0, @@ -686,6 +758,7 @@ "hidden": 0, "is_query_report": 0, "label": "Subscription Settings", + "link_count": 0, "link_to": "Subscription Settings", "link_type": "DocType", "onboard": 0, @@ -695,6 +768,7 @@ "hidden": 0, "is_query_report": 0, "label": "Goods and Services Tax (GST India)", + "link_count": 0, "onboard": 0, "only_for": "India", "type": "Card Break" @@ -704,6 +778,7 @@ "hidden": 0, "is_query_report": 0, "label": "GST Settings", + "link_count": 0, "link_to": "GST Settings", "link_type": "DocType", "onboard": 0, @@ -715,6 +790,7 @@ "hidden": 0, "is_query_report": 0, "label": "GST HSN Code", + "link_count": 0, "link_to": "GST HSN Code", "link_type": "DocType", "onboard": 0, @@ -726,6 +802,7 @@ "hidden": 0, "is_query_report": 1, "label": "GSTR-1", + "link_count": 0, "link_to": "GSTR-1", "link_type": "Report", "onboard": 0, @@ -737,6 +814,7 @@ "hidden": 0, "is_query_report": 1, "label": "GSTR-2", + "link_count": 0, "link_to": "GSTR-2", "link_type": "Report", "onboard": 0, @@ -748,6 +826,7 @@ "hidden": 0, "is_query_report": 0, "label": "GSTR 3B Report", + "link_count": 0, "link_to": "GSTR 3B Report", "link_type": "DocType", "onboard": 0, @@ -759,6 +838,7 @@ "hidden": 0, "is_query_report": 1, "label": "GST Sales Register", + "link_count": 0, "link_to": "GST Sales Register", "link_type": "Report", "onboard": 0, @@ -770,6 +850,7 @@ "hidden": 0, "is_query_report": 1, "label": "GST Purchase Register", + "link_count": 0, "link_to": "GST Purchase Register", "link_type": "Report", "onboard": 0, @@ -781,6 +862,7 @@ "hidden": 0, "is_query_report": 1, "label": "GST Itemised Sales Register", + "link_count": 0, "link_to": "GST Itemised Sales Register", "link_type": "Report", "onboard": 0, @@ -792,6 +874,7 @@ "hidden": 0, "is_query_report": 1, "label": "GST Itemised Purchase Register", + "link_count": 0, "link_to": "GST Itemised Purchase Register", "link_type": "Report", "onboard": 0, @@ -803,6 +886,7 @@ "hidden": 0, "is_query_report": 0, "label": "C-Form", + "link_count": 0, "link_to": "C-Form", "link_type": "DocType", "onboard": 0, @@ -814,6 +898,7 @@ "hidden": 0, "is_query_report": 0, "label": "Lower Deduction Certificate", + "link_count": 0, "link_to": "Lower Deduction Certificate", "link_type": "DocType", "onboard": 0, @@ -824,6 +909,7 @@ "hidden": 0, "is_query_report": 0, "label": "Share Management", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -832,6 +918,7 @@ "hidden": 0, "is_query_report": 0, "label": "Shareholder", + "link_count": 0, "link_to": "Shareholder", "link_type": "DocType", "onboard": 0, @@ -842,6 +929,7 @@ "hidden": 0, "is_query_report": 0, "label": "Share Transfer", + "link_count": 0, "link_to": "Share Transfer", "link_type": "DocType", "onboard": 0, @@ -852,6 +940,7 @@ "hidden": 0, "is_query_report": 1, "label": "Share Ledger", + "link_count": 0, "link_to": "Share Ledger", "link_type": "Report", "onboard": 0, @@ -862,6 +951,7 @@ "hidden": 0, "is_query_report": 1, "label": "Share Balance", + "link_count": 0, "link_to": "Share Balance", "link_type": "Report", "onboard": 0, @@ -871,6 +961,7 @@ "hidden": 0, "is_query_report": 0, "label": "Cost Center and Budgeting", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -879,6 +970,7 @@ "hidden": 0, "is_query_report": 0, "label": "Chart of Cost Centers", + "link_count": 0, "link_to": "Cost Center", "link_type": "DocType", "onboard": 0, @@ -889,6 +981,7 @@ "hidden": 0, "is_query_report": 0, "label": "Budget", + "link_count": 0, "link_to": "Budget", "link_type": "DocType", "onboard": 0, @@ -899,6 +992,7 @@ "hidden": 0, "is_query_report": 0, "label": "Accounting Dimension", + "link_count": 0, "link_to": "Accounting Dimension", "link_type": "DocType", "onboard": 0, @@ -909,6 +1003,7 @@ "hidden": 0, "is_query_report": 1, "label": "Budget Variance Report", + "link_count": 0, "link_to": "Budget Variance Report", "link_type": "Report", "onboard": 0, @@ -919,6 +1014,7 @@ "hidden": 0, "is_query_report": 0, "label": "Monthly Distribution", + "link_count": 0, "link_to": "Monthly Distribution", "link_type": "DocType", "onboard": 0, @@ -928,6 +1024,7 @@ "hidden": 0, "is_query_report": 0, "label": "Opening and Closing", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -936,6 +1033,7 @@ "hidden": 0, "is_query_report": 0, "label": "Opening Invoice Creation Tool", + "link_count": 0, "link_to": "Opening Invoice Creation Tool", "link_type": "DocType", "onboard": 0, @@ -946,6 +1044,7 @@ "hidden": 0, "is_query_report": 0, "label": "Chart of Accounts Importer", + "link_count": 0, "link_to": "Chart of Accounts Importer", "link_type": "DocType", "onboard": 0, @@ -956,6 +1055,7 @@ "hidden": 0, "is_query_report": 0, "label": "Period Closing Voucher", + "link_count": 0, "link_to": "Period Closing Voucher", "link_type": "DocType", "onboard": 0, @@ -965,6 +1065,7 @@ "hidden": 0, "is_query_report": 0, "label": "Taxes", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -973,6 +1074,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sales Taxes and Charges Template", + "link_count": 0, "link_to": "Sales Taxes and Charges Template", "link_type": "DocType", "onboard": 0, @@ -983,6 +1085,7 @@ "hidden": 0, "is_query_report": 0, "label": "Purchase Taxes and Charges Template", + "link_count": 0, "link_to": "Purchase Taxes and Charges Template", "link_type": "DocType", "onboard": 0, @@ -993,6 +1096,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Tax Template", + "link_count": 0, "link_to": "Item Tax Template", "link_type": "DocType", "onboard": 0, @@ -1003,6 +1107,7 @@ "hidden": 0, "is_query_report": 0, "label": "Tax Category", + "link_count": 0, "link_to": "Tax Category", "link_type": "DocType", "onboard": 0, @@ -1013,6 +1118,7 @@ "hidden": 0, "is_query_report": 0, "label": "Tax Rule", + "link_count": 0, "link_to": "Tax Rule", "link_type": "DocType", "onboard": 0, @@ -1023,6 +1129,7 @@ "hidden": 0, "is_query_report": 0, "label": "Tax Withholding Category", + "link_count": 0, "link_to": "Tax Withholding Category", "link_type": "DocType", "onboard": 0, @@ -1032,6 +1139,7 @@ "hidden": 0, "is_query_report": 0, "label": "Profitability", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -1040,6 +1148,7 @@ "hidden": 0, "is_query_report": 1, "label": "Gross Profit", + "link_count": 0, "link_to": "Gross Profit", "link_type": "Report", "onboard": 0, @@ -1050,6 +1159,7 @@ "hidden": 0, "is_query_report": 1, "label": "Profitability Analysis", + "link_count": 0, "link_to": "Profitability Analysis", "link_type": "Report", "onboard": 0, @@ -1060,6 +1170,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Invoice Trends", + "link_count": 0, "link_to": "Sales Invoice Trends", "link_type": "Report", "onboard": 0, @@ -1070,20 +1181,26 @@ "hidden": 0, "is_query_report": 1, "label": "Purchase Invoice Trends", + "link_count": 0, "link_to": "Purchase Invoice Trends", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2021-06-10 03:17:31.427945", + "modified": "2021-08-05 12:15:52.872470", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", "onboarding": "Accounts", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 2, "shortcuts": [ { "label": "Chart of Accounts", @@ -1130,5 +1247,6 @@ "link_to": "Accounts", "type": "Dashboard" } - ] + ], + "title": "Accounting" } \ No newline at end of file diff --git a/erpnext/agriculture/workspace/agriculture/agriculture.json b/erpnext/agriculture/workspace/agriculture/agriculture.json index 2cc252491d39..633777eeb706 100644 --- a/erpnext/agriculture/workspace/agriculture/agriculture.json +++ b/erpnext/agriculture/workspace/agriculture/agriculture.json @@ -1,22 +1,27 @@ { - "category": "Domains", + "category": "", "charts": [], + "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Crops & Lands\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Analytics\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Diseases & Fertilizers\", \"col\": 4}}]", "creation": "2020-03-02 17:23:34.339274", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "agriculture", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Agriculture", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Crops & Lands", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -25,6 +30,7 @@ "hidden": 0, "is_query_report": 0, "label": "Crop", + "link_count": 0, "link_to": "Crop", "link_type": "DocType", "onboard": 1, @@ -35,6 +41,7 @@ "hidden": 0, "is_query_report": 0, "label": "Crop Cycle", + "link_count": 0, "link_to": "Crop Cycle", "link_type": "DocType", "onboard": 1, @@ -45,6 +52,7 @@ "hidden": 0, "is_query_report": 0, "label": "Location", + "link_count": 0, "link_to": "Location", "link_type": "DocType", "onboard": 1, @@ -54,6 +62,7 @@ "hidden": 0, "is_query_report": 0, "label": "Analytics", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -62,6 +71,7 @@ "hidden": 0, "is_query_report": 0, "label": "Plant Analysis", + "link_count": 0, "link_to": "Plant Analysis", "link_type": "DocType", "onboard": 0, @@ -72,6 +82,7 @@ "hidden": 0, "is_query_report": 0, "label": "Soil Analysis", + "link_count": 0, "link_to": "Soil Analysis", "link_type": "DocType", "onboard": 0, @@ -82,6 +93,7 @@ "hidden": 0, "is_query_report": 0, "label": "Water Analysis", + "link_count": 0, "link_to": "Water Analysis", "link_type": "DocType", "onboard": 0, @@ -92,6 +104,7 @@ "hidden": 0, "is_query_report": 0, "label": "Soil Texture", + "link_count": 0, "link_to": "Soil Texture", "link_type": "DocType", "onboard": 0, @@ -102,6 +115,7 @@ "hidden": 0, "is_query_report": 0, "label": "Weather", + "link_count": 0, "link_to": "Weather", "link_type": "DocType", "onboard": 0, @@ -112,6 +126,7 @@ "hidden": 0, "is_query_report": 0, "label": "Agriculture Analysis Criteria", + "link_count": 0, "link_to": "Agriculture Analysis Criteria", "link_type": "DocType", "onboard": 0, @@ -121,6 +136,7 @@ "hidden": 0, "is_query_report": 0, "label": "Diseases & Fertilizers", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -129,6 +145,7 @@ "hidden": 0, "is_query_report": 0, "label": "Disease", + "link_count": 0, "link_to": "Disease", "link_type": "DocType", "onboard": 1, @@ -139,19 +156,26 @@ "hidden": 0, "is_query_report": 0, "label": "Fertilizer", + "link_count": 0, "link_to": "Fertilizer", "link_type": "DocType", "onboard": 1, "type": "Link" } ], - "modified": "2020-12-01 13:38:38.477493", + "modified": "2021-08-05 12:15:54.595197", "modified_by": "Administrator", "module": "Agriculture", "name": "Agriculture", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, "restrict_to_domain": "Agriculture", - "shortcuts": [] + "roles": [], + "sequence_id": 3, + "shortcuts": [], + "title": "Agriculture" } \ No newline at end of file diff --git a/erpnext/assets/workspace/assets/assets.json b/erpnext/assets/workspace/assets/assets.json index c40158175830..dfbf1a378e55 100644 --- a/erpnext/assets/workspace/assets/assets.json +++ b/erpnext/assets/workspace/assets/assets.json @@ -1,27 +1,32 @@ { - "category": "Modules", + "category": "", "charts": [ { "chart_name": "Asset Value Analytics", "label": "Asset Value Analytics" } ], + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Assets\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Asset Value Analytics\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Asset\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Asset Category\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Fixed Asset Register\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assets\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]", "creation": "2020-03-02 15:43:27.634865", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "assets", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Assets", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Assets", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -30,6 +35,7 @@ "hidden": 0, "is_query_report": 0, "label": "Asset", + "link_count": 0, "link_to": "Asset", "link_type": "DocType", "onboard": 1, @@ -40,6 +46,7 @@ "hidden": 0, "is_query_report": 0, "label": "Location", + "link_count": 0, "link_to": "Location", "link_type": "DocType", "onboard": 1, @@ -50,6 +57,7 @@ "hidden": 0, "is_query_report": 0, "label": "Asset Category", + "link_count": 0, "link_to": "Asset Category", "link_type": "DocType", "onboard": 1, @@ -60,6 +68,7 @@ "hidden": 0, "is_query_report": 0, "label": "Asset Movement", + "link_count": 0, "link_to": "Asset Movement", "link_type": "DocType", "onboard": 0, @@ -69,6 +78,7 @@ "hidden": 0, "is_query_report": 0, "label": "Maintenance", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -77,6 +87,7 @@ "hidden": 0, "is_query_report": 0, "label": "Asset Maintenance Team", + "link_count": 0, "link_to": "Asset Maintenance Team", "link_type": "DocType", "onboard": 1, @@ -87,6 +98,7 @@ "hidden": 0, "is_query_report": 0, "label": "Asset Maintenance", + "link_count": 0, "link_to": "Asset Maintenance", "link_type": "DocType", "onboard": 1, @@ -97,6 +109,7 @@ "hidden": 0, "is_query_report": 0, "label": "Asset Maintenance Log", + "link_count": 0, "link_to": "Asset Maintenance Log", "link_type": "DocType", "onboard": 0, @@ -107,6 +120,7 @@ "hidden": 0, "is_query_report": 0, "label": "Asset Value Adjustment", + "link_count": 0, "link_to": "Asset Value Adjustment", "link_type": "DocType", "onboard": 0, @@ -117,6 +131,7 @@ "hidden": 0, "is_query_report": 0, "label": "Asset Repair", + "link_count": 0, "link_to": "Asset Repair", "link_type": "DocType", "onboard": 0, @@ -126,6 +141,7 @@ "hidden": 0, "is_query_report": 0, "label": "Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -134,6 +150,7 @@ "hidden": 0, "is_query_report": 1, "label": "Asset Depreciation Ledger", + "link_count": 0, "link_to": "Asset Depreciation Ledger", "link_type": "Report", "onboard": 0, @@ -144,6 +161,7 @@ "hidden": 0, "is_query_report": 1, "label": "Asset Depreciations and Balances", + "link_count": 0, "link_to": "Asset Depreciations and Balances", "link_type": "Report", "onboard": 0, @@ -154,20 +172,26 @@ "hidden": 0, "is_query_report": 0, "label": "Asset Maintenance", + "link_count": 0, "link_to": "Asset Maintenance", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:37.977119", + "modified": "2021-08-05 12:15:54.839452", "modified_by": "Administrator", "module": "Assets", "name": "Assets", "onboarding": "Assets", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 4, "shortcuts": [ { "label": "Asset", @@ -189,5 +213,6 @@ "link_to": "Asset", "type": "Dashboard" } - ] + ], + "title": "Assets" } \ No newline at end of file diff --git a/erpnext/buying/workspace/buying/buying.json b/erpnext/buying/workspace/buying/buying.json index 6c9c0f3011bd..6c91e816954d 100644 --- a/erpnext/buying/workspace/buying/buying.json +++ b/erpnext/buying/workspace/buying/buying.json @@ -1,6 +1,6 @@ { "cards_label": "", - "category": "Modules", + "category": "", "charts": [ { "chart_name": "Purchase Order Trends", @@ -8,22 +8,27 @@ } ], "charts_label": "", + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Buying\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Purchase Order Trends\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Material Request\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Order Analysis\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Buying\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items & Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Supplier\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Supplier Scorecard\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Regional\", \"col\": 4}}]", "creation": "2020-01-28 11:50:26.195467", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "buying", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Buying", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Buying", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -32,6 +37,7 @@ "hidden": 0, "is_query_report": 0, "label": "Material Request", + "link_count": 0, "link_to": "Material Request", "link_type": "DocType", "onboard": 1, @@ -42,6 +48,7 @@ "hidden": 0, "is_query_report": 0, "label": "Purchase Order", + "link_count": 0, "link_to": "Purchase Order", "link_type": "DocType", "onboard": 1, @@ -52,6 +59,7 @@ "hidden": 0, "is_query_report": 0, "label": "Purchase Invoice", + "link_count": 0, "link_to": "Purchase Invoice", "link_type": "DocType", "onboard": 1, @@ -62,6 +70,7 @@ "hidden": 0, "is_query_report": 0, "label": "Request for Quotation", + "link_count": 0, "link_to": "Request for Quotation", "link_type": "DocType", "onboard": 1, @@ -72,6 +81,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier Quotation", + "link_count": 0, "link_to": "Supplier Quotation", "link_type": "DocType", "onboard": 0, @@ -81,6 +91,7 @@ "hidden": 0, "is_query_report": 0, "label": "Items & Pricing", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -89,6 +100,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item", + "link_count": 0, "link_to": "Item", "link_type": "DocType", "onboard": 1, @@ -99,6 +111,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Price", + "link_count": 0, "link_to": "Item Price", "link_type": "DocType", "onboard": 1, @@ -109,6 +122,7 @@ "hidden": 0, "is_query_report": 0, "label": "Price List", + "link_count": 0, "link_to": "Price List", "link_type": "DocType", "onboard": 1, @@ -119,6 +133,7 @@ "hidden": 0, "is_query_report": 0, "label": "Product Bundle", + "link_count": 0, "link_to": "Product Bundle", "link_type": "DocType", "onboard": 0, @@ -129,6 +144,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Group", + "link_count": 0, "link_to": "Item Group", "link_type": "DocType", "onboard": 0, @@ -139,6 +155,7 @@ "hidden": 0, "is_query_report": 0, "label": "Promotional Scheme", + "link_count": 0, "link_to": "Promotional Scheme", "link_type": "DocType", "onboard": 0, @@ -149,6 +166,7 @@ "hidden": 0, "is_query_report": 0, "label": "Pricing Rule", + "link_count": 0, "link_to": "Pricing Rule", "link_type": "DocType", "onboard": 0, @@ -158,6 +176,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -166,6 +185,7 @@ "hidden": 0, "is_query_report": 0, "label": "Buying Settings", + "link_count": 0, "link_to": "Buying Settings", "link_type": "DocType", "onboard": 0, @@ -176,6 +196,7 @@ "hidden": 0, "is_query_report": 0, "label": "Purchase Taxes and Charges Template", + "link_count": 0, "link_to": "Purchase Taxes and Charges Template", "link_type": "DocType", "onboard": 0, @@ -186,6 +207,7 @@ "hidden": 0, "is_query_report": 0, "label": "Terms and Conditions Template", + "link_count": 0, "link_to": "Terms and Conditions", "link_type": "DocType", "onboard": 0, @@ -195,6 +217,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -203,6 +226,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier", + "link_count": 0, "link_to": "Supplier", "link_type": "DocType", "onboard": 1, @@ -213,6 +237,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier Group", + "link_count": 0, "link_to": "Supplier Group", "link_type": "DocType", "onboard": 0, @@ -223,6 +248,7 @@ "hidden": 0, "is_query_report": 0, "label": "Contact", + "link_count": 0, "link_to": "Contact", "link_type": "DocType", "onboard": 0, @@ -233,6 +259,7 @@ "hidden": 0, "is_query_report": 0, "label": "Address", + "link_count": 0, "link_to": "Address", "link_type": "DocType", "onboard": 0, @@ -242,6 +269,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier Scorecard", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -250,6 +278,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier Scorecard", + "link_count": 0, "link_to": "Supplier Scorecard", "link_type": "DocType", "onboard": 0, @@ -260,6 +289,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier Scorecard Variable", + "link_count": 0, "link_to": "Supplier Scorecard Variable", "link_type": "DocType", "onboard": 0, @@ -270,6 +300,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier Scorecard Criteria", + "link_count": 0, "link_to": "Supplier Scorecard Criteria", "link_type": "DocType", "onboard": 0, @@ -280,6 +311,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier Scorecard Standing", + "link_count": 0, "link_to": "Supplier Scorecard Standing", "link_type": "DocType", "onboard": 0, @@ -289,6 +321,7 @@ "hidden": 0, "is_query_report": 0, "label": "Key Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -297,6 +330,7 @@ "hidden": 0, "is_query_report": 1, "label": "Purchase Analytics", + "link_count": 0, "link_to": "Purchase Analytics", "link_type": "Report", "onboard": 1, @@ -307,6 +341,7 @@ "hidden": 0, "is_query_report": 1, "label": "Purchase Order Analysis", + "link_count": 0, "link_to": "Purchase Order Analysis", "link_type": "Report", "onboard": 1, @@ -317,6 +352,7 @@ "hidden": 0, "is_query_report": 1, "label": "Supplier-Wise Sales Analytics", + "link_count": 0, "link_to": "Supplier-Wise Sales Analytics", "link_type": "Report", "onboard": 1, @@ -327,6 +363,7 @@ "hidden": 0, "is_query_report": 1, "label": "Items to Order and Receive", + "link_count": 0, "link_to": "Requested Items to Order and Receive", "link_type": "Report", "onboard": 1, @@ -337,6 +374,7 @@ "hidden": 0, "is_query_report": 1, "label": "Purchase Order Trends", + "link_count": 0, "link_to": "Purchase Order Trends", "link_type": "Report", "onboard": 1, @@ -347,6 +385,7 @@ "hidden": 0, "is_query_report": 1, "label": "Procurement Tracker", + "link_count": 0, "link_to": "Procurement Tracker", "link_type": "Report", "onboard": 1, @@ -356,6 +395,7 @@ "hidden": 0, "is_query_report": 0, "label": "Other Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -364,6 +404,7 @@ "hidden": 0, "is_query_report": 1, "label": "Items To Be Requested", + "link_count": 0, "link_to": "Items To Be Requested", "link_type": "Report", "onboard": 1, @@ -374,6 +415,7 @@ "hidden": 0, "is_query_report": 1, "label": "Item-wise Purchase History", + "link_count": 0, "link_to": "Item-wise Purchase History", "link_type": "Report", "onboard": 1, @@ -384,6 +426,7 @@ "hidden": 0, "is_query_report": 1, "label": "Purchase Receipt Trends", + "link_count": 0, "link_to": "Purchase Receipt Trends", "link_type": "Report", "onboard": 0, @@ -394,6 +437,7 @@ "hidden": 0, "is_query_report": 1, "label": "Purchase Invoice Trends", + "link_count": 0, "link_to": "Purchase Invoice Trends", "link_type": "Report", "onboard": 0, @@ -404,6 +448,7 @@ "hidden": 0, "is_query_report": 1, "label": "Subcontracted Raw Materials To Be Transferred", + "link_count": 0, "link_to": "Subcontracted Raw Materials To Be Transferred", "link_type": "Report", "onboard": 0, @@ -414,6 +459,7 @@ "hidden": 0, "is_query_report": 1, "label": "Subcontracted Item To Be Received", + "link_count": 0, "link_to": "Subcontracted Item To Be Received", "link_type": "Report", "onboard": 0, @@ -424,6 +470,7 @@ "hidden": 0, "is_query_report": 1, "label": "Supplier Quotation Comparison", + "link_count": 0, "link_to": "Supplier Quotation Comparison", "link_type": "Report", "onboard": 1, @@ -434,6 +481,7 @@ "hidden": 0, "is_query_report": 1, "label": "Material Requests for which Supplier Quotations are not created", + "link_count": 0, "link_to": "Material Requests for which Supplier Quotations are not created", "link_type": "Report", "onboard": 0, @@ -444,6 +492,7 @@ "hidden": 0, "is_query_report": 1, "label": "Supplier Addresses And Contacts", + "link_count": 0, "link_to": "Address And Contacts", "link_type": "Report", "onboard": 0, @@ -453,6 +502,7 @@ "hidden": 0, "is_query_report": 0, "label": "Regional", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -461,20 +511,26 @@ "hidden": 0, "is_query_report": 0, "label": "Import Supplier Invoice", + "link_count": 0, "link_to": "Import Supplier Invoice", "link_type": "DocType", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:38.615167", + "modified": "2021-08-05 12:15:56.218427", "modified_by": "Administrator", "module": "Buying", "name": "Buying", "onboarding": "Buying", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 6, "shortcuts": [ { "color": "Green", @@ -516,5 +572,6 @@ "type": "Dashboard" } ], - "shortcuts_label": "" + "shortcuts_label": "", + "title": "Buying" } \ No newline at end of file diff --git a/erpnext/crm/workspace/crm/crm.json b/erpnext/crm/workspace/crm/crm.json index b4fb7d8abe9c..c363395452b8 100644 --- a/erpnext/crm/workspace/crm/crm.json +++ b/erpnext/crm/workspace/crm/crm.json @@ -1,26 +1,31 @@ { - "category": "Modules", + "category": "", "charts": [ { "chart_name": "Territory Wise Sales" } ], + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"CRM\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Lead\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Opportunity\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Customer\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Sales Pipeline\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Campaign\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]", "creation": "2020-01-23 14:48:30.183272", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "crm", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "CRM", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Sales Pipeline", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -29,6 +34,7 @@ "hidden": 0, "is_query_report": 0, "label": "Lead", + "link_count": 0, "link_to": "Lead", "link_type": "DocType", "onboard": 1, @@ -39,6 +45,7 @@ "hidden": 0, "is_query_report": 0, "label": "Opportunity", + "link_count": 0, "link_to": "Opportunity", "link_type": "DocType", "onboard": 1, @@ -49,6 +56,7 @@ "hidden": 0, "is_query_report": 0, "label": "Customer", + "link_count": 0, "link_to": "Customer", "link_type": "DocType", "onboard": 1, @@ -59,6 +67,7 @@ "hidden": 0, "is_query_report": 0, "label": "Contact", + "link_count": 0, "link_to": "Contact", "link_type": "DocType", "onboard": 1, @@ -69,6 +78,7 @@ "hidden": 0, "is_query_report": 0, "label": "Communication", + "link_count": 0, "link_to": "Communication", "link_type": "DocType", "onboard": 0, @@ -79,6 +89,7 @@ "hidden": 0, "is_query_report": 0, "label": "Lead Source", + "link_count": 0, "link_to": "Lead Source", "link_type": "DocType", "onboard": 0, @@ -89,6 +100,7 @@ "hidden": 0, "is_query_report": 0, "label": "Contract", + "link_count": 0, "link_to": "Contract", "link_type": "DocType", "onboard": 0, @@ -99,6 +111,7 @@ "hidden": 0, "is_query_report": 0, "label": "Appointment", + "link_count": 0, "link_to": "Appointment", "link_type": "DocType", "onboard": 0, @@ -109,6 +122,7 @@ "hidden": 0, "is_query_report": 0, "label": "Newsletter", + "link_count": 0, "link_to": "Newsletter", "link_type": "DocType", "onboard": 0, @@ -118,6 +132,7 @@ "hidden": 0, "is_query_report": 0, "label": "Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -126,6 +141,7 @@ "hidden": 0, "is_query_report": 1, "label": "Lead Details", + "link_count": 0, "link_to": "Lead Details", "link_type": "Report", "onboard": 1, @@ -136,6 +152,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sales Funnel", + "link_count": 0, "link_to": "sales-funnel", "link_type": "Page", "onboard": 1, @@ -146,6 +163,7 @@ "hidden": 0, "is_query_report": 1, "label": "Prospects Engaged But Not Converted", + "link_count": 0, "link_to": "Prospects Engaged But Not Converted", "link_type": "Report", "onboard": 1, @@ -156,6 +174,7 @@ "hidden": 0, "is_query_report": 1, "label": "First Response Time for Opportunity", + "link_count": 0, "link_to": "First Response Time for Opportunity", "link_type": "Report", "onboard": 0, @@ -166,6 +185,7 @@ "hidden": 0, "is_query_report": 1, "label": "Inactive Customers", + "link_count": 0, "link_to": "Inactive Customers", "link_type": "Report", "onboard": 0, @@ -176,6 +196,7 @@ "hidden": 0, "is_query_report": 1, "label": "Campaign Efficiency", + "link_count": 0, "link_to": "Campaign Efficiency", "link_type": "Report", "onboard": 0, @@ -186,6 +207,7 @@ "hidden": 0, "is_query_report": 1, "label": "Lead Owner Efficiency", + "link_count": 0, "link_to": "Lead Owner Efficiency", "link_type": "Report", "onboard": 0, @@ -195,6 +217,7 @@ "hidden": 0, "is_query_report": 0, "label": "Maintenance", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -203,6 +226,7 @@ "hidden": 0, "is_query_report": 0, "label": "Maintenance Schedule", + "link_count": 0, "link_to": "Maintenance Schedule", "link_type": "DocType", "onboard": 1, @@ -213,6 +237,7 @@ "hidden": 0, "is_query_report": 0, "label": "Maintenance Visit", + "link_count": 0, "link_to": "Maintenance Visit", "link_type": "DocType", "onboard": 0, @@ -223,6 +248,7 @@ "hidden": 0, "is_query_report": 0, "label": "Warranty Claim", + "link_count": 0, "link_to": "Warranty Claim", "link_type": "DocType", "onboard": 0, @@ -232,6 +258,7 @@ "hidden": 0, "is_query_report": 0, "label": "Campaign", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -240,6 +267,7 @@ "hidden": 0, "is_query_report": 0, "label": "Campaign", + "link_count": 0, "link_to": "Campaign", "link_type": "DocType", "onboard": 0, @@ -250,6 +278,7 @@ "hidden": 0, "is_query_report": 0, "label": "Email Campaign", + "link_count": 0, "link_to": "Email Campaign", "link_type": "DocType", "onboard": 0, @@ -260,6 +289,7 @@ "hidden": 0, "is_query_report": 0, "label": "Social Media Post", + "link_count": 0, "link_to": "Social Media Post", "link_type": "DocType", "onboard": 0, @@ -269,6 +299,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -277,6 +308,7 @@ "hidden": 0, "is_query_report": 0, "label": "Customer Group", + "link_count": 0, "link_to": "Customer Group", "link_type": "DocType", "onboard": 1, @@ -287,6 +319,7 @@ "hidden": 0, "is_query_report": 0, "label": "Territory", + "link_count": 0, "link_to": "Territory", "link_type": "DocType", "onboard": 1, @@ -297,6 +330,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sales Person", + "link_count": 0, "link_to": "Sales Person", "link_type": "DocType", "onboard": 1, @@ -307,6 +341,7 @@ "hidden": 0, "is_query_report": 0, "label": "SMS Center", + "link_count": 0, "link_to": "SMS Center", "link_type": "DocType", "onboard": 0, @@ -317,6 +352,7 @@ "hidden": 0, "is_query_report": 0, "label": "SMS Log", + "link_count": 0, "link_to": "SMS Log", "link_type": "DocType", "onboard": 0, @@ -327,6 +363,7 @@ "hidden": 0, "is_query_report": 0, "label": "SMS Settings", + "link_count": 0, "link_to": "SMS Settings", "link_type": "DocType", "onboard": 0, @@ -337,6 +374,7 @@ "hidden": 0, "is_query_report": 0, "label": "Email Group", + "link_count": 0, "link_to": "Email Group", "link_type": "DocType", "onboard": 0, @@ -347,6 +385,7 @@ "hidden": 0, "is_query_report": 0, "label": "Twitter Settings", + "link_count": 0, "link_to": "Twitter Settings", "link_type": "DocType", "onboard": 0, @@ -357,20 +396,26 @@ "hidden": 0, "is_query_report": 0, "label": "LinkedIn Settings", + "link_count": 0, "link_to": "LinkedIn Settings", "link_type": "DocType", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:36.871352", + "modified": "2021-08-05 12:15:56.913091", "modified_by": "Administrator", "module": "CRM", "name": "CRM", "onboarding": "CRM", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 7, "shortcuts": [ { "color": "Blue", @@ -403,5 +448,6 @@ "link_to": "CRM", "type": "Dashboard" } - ] + ], + "title": "CRM" } \ No newline at end of file diff --git a/erpnext/education/workspace/education/education.json b/erpnext/education/workspace/education/education.json index bf7496146d94..c58ddd63cfe1 100644 --- a/erpnext/education/workspace/education/education.json +++ b/erpnext/education/workspace/education/education.json @@ -1,27 +1,32 @@ { - "category": "Domains", + "category": "", "charts": [ { "chart_name": "Program Enrollments", "label": "Program Enrollments" } ], + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Education\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Program Enrollments\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Instructor\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Program\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Course\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Fees\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student Monthly Attendance Sheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Course Scheduling Tool\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Student Attendance Tool\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Student and Instructor\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Content Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Admission\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Fees\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Schedule\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"LMS Activity\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assessment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Assessment Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]", "creation": "2020-03-02 17:22:57.066401", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "education", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Education", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Student and Instructor", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -30,6 +35,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student", + "link_count": 0, "link_to": "Student", "link_type": "DocType", "onboard": 1, @@ -40,6 +46,7 @@ "hidden": 0, "is_query_report": 0, "label": "Instructor", + "link_count": 0, "link_to": "Instructor", "link_type": "DocType", "onboard": 1, @@ -50,6 +57,7 @@ "hidden": 0, "is_query_report": 0, "label": "Guardian", + "link_count": 0, "link_to": "Guardian", "link_type": "DocType", "onboard": 0, @@ -60,6 +68,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Group", + "link_count": 0, "link_to": "Student Group", "link_type": "DocType", "onboard": 0, @@ -70,6 +79,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Log", + "link_count": 0, "link_to": "Student Log", "link_type": "DocType", "onboard": 0, @@ -79,6 +89,7 @@ "hidden": 0, "is_query_report": 0, "label": "Masters", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -87,6 +98,7 @@ "hidden": 0, "is_query_report": 0, "label": "Program", + "link_count": 0, "link_to": "Program", "link_type": "DocType", "onboard": 0, @@ -97,6 +109,7 @@ "hidden": 0, "is_query_report": 0, "label": "Course", + "link_count": 0, "link_to": "Course", "link_type": "DocType", "onboard": 1, @@ -107,6 +120,7 @@ "hidden": 0, "is_query_report": 0, "label": "Topic", + "link_count": 0, "link_to": "Topic", "link_type": "DocType", "onboard": 0, @@ -117,6 +131,7 @@ "hidden": 0, "is_query_report": 0, "label": "Room", + "link_count": 0, "link_to": "Room", "link_type": "DocType", "onboard": 1, @@ -126,6 +141,7 @@ "hidden": 0, "is_query_report": 0, "label": "Content Masters", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -134,6 +150,7 @@ "hidden": 0, "is_query_report": 0, "label": "Article", + "link_count": 0, "link_to": "Article", "link_type": "DocType", "onboard": 0, @@ -144,6 +161,7 @@ "hidden": 0, "is_query_report": 0, "label": "Video", + "link_count": 0, "link_to": "Video", "link_type": "DocType", "onboard": 0, @@ -154,6 +172,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quiz", + "link_count": 0, "link_to": "Quiz", "link_type": "DocType", "onboard": 0, @@ -163,6 +182,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -171,6 +191,7 @@ "hidden": 0, "is_query_report": 0, "label": "Education Settings", + "link_count": 0, "link_to": "Education Settings", "link_type": "DocType", "onboard": 0, @@ -181,6 +202,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Category", + "link_count": 0, "link_to": "Student Category", "link_type": "DocType", "onboard": 0, @@ -191,6 +213,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Batch Name", + "link_count": 0, "link_to": "Student Batch Name", "link_type": "DocType", "onboard": 0, @@ -201,6 +224,7 @@ "hidden": 0, "is_query_report": 0, "label": "Grading Scale", + "link_count": 0, "link_to": "Grading Scale", "link_type": "DocType", "onboard": 1, @@ -211,6 +235,7 @@ "hidden": 0, "is_query_report": 0, "label": "Academic Term", + "link_count": 0, "link_to": "Academic Term", "link_type": "DocType", "onboard": 0, @@ -221,6 +246,7 @@ "hidden": 0, "is_query_report": 0, "label": "Academic Year", + "link_count": 0, "link_to": "Academic Year", "link_type": "DocType", "onboard": 0, @@ -230,6 +256,7 @@ "hidden": 0, "is_query_report": 0, "label": "Admission", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -238,6 +265,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Applicant", + "link_count": 0, "link_to": "Student Applicant", "link_type": "DocType", "onboard": 0, @@ -248,6 +276,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Admission", + "link_count": 0, "link_to": "Student Admission", "link_type": "DocType", "onboard": 0, @@ -258,6 +287,7 @@ "hidden": 0, "is_query_report": 0, "label": "Program Enrollment", + "link_count": 0, "link_to": "Program Enrollment", "link_type": "DocType", "onboard": 0, @@ -268,6 +298,7 @@ "hidden": 0, "is_query_report": 0, "label": "Course Enrollment", + "link_count": 0, "link_to": "Course Enrollment", "link_type": "DocType", "onboard": 0, @@ -277,6 +308,7 @@ "hidden": 0, "is_query_report": 0, "label": "Fees", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -285,6 +317,7 @@ "hidden": 0, "is_query_report": 0, "label": "Fee Structure", + "link_count": 0, "link_to": "Fee Structure", "link_type": "DocType", "onboard": 0, @@ -295,6 +328,7 @@ "hidden": 0, "is_query_report": 0, "label": "Fee Category", + "link_count": 0, "link_to": "Fee Category", "link_type": "DocType", "onboard": 0, @@ -305,6 +339,7 @@ "hidden": 0, "is_query_report": 0, "label": "Fee Schedule", + "link_count": 0, "link_to": "Fee Schedule", "link_type": "DocType", "onboard": 0, @@ -315,6 +350,7 @@ "hidden": 0, "is_query_report": 0, "label": "Fees", + "link_count": 0, "link_to": "Fees", "link_type": "DocType", "onboard": 0, @@ -325,6 +361,7 @@ "hidden": 0, "is_query_report": 1, "label": "Student Fee Collection Report", + "link_count": 0, "link_to": "Student Fee Collection", "link_type": "Report", "onboard": 0, @@ -335,6 +372,7 @@ "hidden": 0, "is_query_report": 1, "label": "Program wise Fee Collection Report", + "link_count": 0, "link_to": "Program wise Fee Collection", "link_type": "Report", "onboard": 0, @@ -344,6 +382,7 @@ "hidden": 0, "is_query_report": 0, "label": "Schedule", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -352,6 +391,7 @@ "hidden": 0, "is_query_report": 0, "label": "Course Schedule", + "link_count": 0, "link_to": "Course Schedule", "link_type": "DocType", "onboard": 0, @@ -362,6 +402,7 @@ "hidden": 0, "is_query_report": 0, "label": "Course Scheduling Tool", + "link_count": 0, "link_to": "Course Scheduling Tool", "link_type": "DocType", "onboard": 0, @@ -371,6 +412,7 @@ "hidden": 0, "is_query_report": 0, "label": "Attendance", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -379,6 +421,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Attendance", + "link_count": 0, "link_to": "Student Attendance", "link_type": "DocType", "onboard": 0, @@ -389,6 +432,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Leave Application", + "link_count": 0, "link_to": "Student Leave Application", "link_type": "DocType", "onboard": 0, @@ -399,6 +443,7 @@ "hidden": 0, "is_query_report": 1, "label": "Student Monthly Attendance Sheet", + "link_count": 0, "link_to": "Student Monthly Attendance Sheet", "link_type": "Report", "onboard": 0, @@ -409,6 +454,7 @@ "hidden": 0, "is_query_report": 1, "label": "Absent Student Report", + "link_count": 0, "link_to": "Absent Student Report", "link_type": "Report", "onboard": 0, @@ -419,6 +465,7 @@ "hidden": 0, "is_query_report": 1, "label": "Student Batch-Wise Attendance", + "link_count": 0, "link_to": "Student Batch-Wise Attendance", "link_type": "Report", "onboard": 0, @@ -428,6 +475,7 @@ "hidden": 0, "is_query_report": 0, "label": "LMS Activity", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -436,6 +484,7 @@ "hidden": 0, "is_query_report": 0, "label": "Course Enrollment", + "link_count": 0, "link_to": "Course Enrollment", "link_type": "DocType", "onboard": 0, @@ -446,6 +495,7 @@ "hidden": 0, "is_query_report": 0, "label": "Course Activity", + "link_count": 0, "link_to": "Course Activity", "link_type": "DocType", "onboard": 0, @@ -456,6 +506,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quiz Activity", + "link_count": 0, "link_to": "Quiz Activity", "link_type": "DocType", "onboard": 0, @@ -465,6 +516,7 @@ "hidden": 0, "is_query_report": 0, "label": "Assessment", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -473,6 +525,7 @@ "hidden": 0, "is_query_report": 0, "label": "Assessment Plan", + "link_count": 0, "link_to": "Assessment Plan", "link_type": "DocType", "onboard": 0, @@ -483,6 +536,7 @@ "hidden": 0, "is_query_report": 0, "label": "Assessment Group", + "link_count": 0, "link_to": "Assessment Group", "link_type": "DocType", "onboard": 0, @@ -493,6 +547,7 @@ "hidden": 0, "is_query_report": 0, "label": "Assessment Result", + "link_count": 0, "link_to": "Assessment Result", "link_type": "DocType", "onboard": 0, @@ -503,6 +558,7 @@ "hidden": 0, "is_query_report": 0, "label": "Assessment Criteria", + "link_count": 0, "link_to": "Assessment Criteria", "link_type": "DocType", "onboard": 0, @@ -512,6 +568,7 @@ "hidden": 0, "is_query_report": 0, "label": "Assessment Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -520,6 +577,7 @@ "hidden": 0, "is_query_report": 1, "label": "Course wise Assessment Report", + "link_count": 0, "link_to": "Course wise Assessment Report", "link_type": "Report", "onboard": 0, @@ -530,6 +588,7 @@ "hidden": 0, "is_query_report": 1, "label": "Final Assessment Grades", + "link_count": 0, "link_to": "Final Assessment Grades", "link_type": "Report", "onboard": 0, @@ -540,6 +599,7 @@ "hidden": 0, "is_query_report": 1, "label": "Assessment Plan Status", + "link_count": 0, "link_to": "Assessment Plan Status", "link_type": "Report", "onboard": 0, @@ -550,6 +610,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Report Generation Tool", + "link_count": 0, "link_to": "Student Report Generation Tool", "link_type": "DocType", "onboard": 0, @@ -559,6 +620,7 @@ "hidden": 0, "is_query_report": 0, "label": "Tools", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -567,6 +629,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Attendance Tool", + "link_count": 0, "link_to": "Student Attendance Tool", "link_type": "DocType", "onboard": 0, @@ -577,6 +640,7 @@ "hidden": 0, "is_query_report": 0, "label": "Assessment Result Tool", + "link_count": 0, "link_to": "Assessment Result Tool", "link_type": "DocType", "onboard": 0, @@ -587,6 +651,7 @@ "hidden": 0, "is_query_report": 0, "label": "Student Group Creation Tool", + "link_count": 0, "link_to": "Student Group Creation Tool", "link_type": "DocType", "onboard": 0, @@ -597,6 +662,7 @@ "hidden": 0, "is_query_report": 0, "label": "Program Enrollment Tool", + "link_count": 0, "link_to": "Program Enrollment Tool", "link_type": "DocType", "onboard": 0, @@ -607,6 +673,7 @@ "hidden": 0, "is_query_report": 0, "label": "Course Scheduling Tool", + "link_count": 0, "link_to": "Course Scheduling Tool", "link_type": "DocType", "onboard": 0, @@ -616,6 +683,7 @@ "hidden": 0, "is_query_report": 0, "label": "Other Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -624,21 +692,26 @@ "hidden": 0, "is_query_report": 1, "label": "Student and Guardian Contact Details", + "link_count": 0, "link_to": "Student and Guardian Contact Details", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:37.448989", + "modified": "2021-08-05 12:15:57.929275", "modified_by": "Administrator", "module": "Education", "name": "Education", "onboarding": "Education", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, "restrict_to_domain": "Education", + "roles": [], + "sequence_id": 9, "shortcuts": [ { "color": "Grey", @@ -697,5 +770,6 @@ "link_to": "Education", "type": "Dashboard" } - ] + ], + "title": "Education" } \ No newline at end of file diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json index 24b8e48ed6f5..9f9204a78d8d 100644 --- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json @@ -1,22 +1,27 @@ { - "category": "Modules", + "category": "", "charts": [], + "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Marketplace\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Payments\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]", "creation": "2020-08-20 19:30:48.138801", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", - "extends": "Integrations", - "extends_another_page": 1, - "hide_custom": 1, + "extends": "", + "extends_another_page": 0, + "for_user": "", + "hide_custom": 0, + "icon": "integration", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "ERPNext Integrations", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Marketplace", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -25,6 +30,7 @@ "hidden": 0, "is_query_report": 0, "label": "Woocommerce Settings", + "link_count": 0, "link_to": "Woocommerce Settings", "link_type": "DocType", "onboard": 0, @@ -35,15 +41,28 @@ "hidden": 0, "is_query_report": 0, "label": "Amazon MWS Settings", + "link_count": 0, "link_to": "Amazon MWS Settings", "link_type": "DocType", "onboard": 0, "type": "Link" }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Shopify Settings", + "link_count": 0, + "link_to": "Shopify Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, { "hidden": 0, "is_query_report": 0, "label": "Payments", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -52,6 +71,7 @@ "hidden": 0, "is_query_report": 0, "label": "GoCardless Settings", + "link_count": 0, "link_to": "GoCardless Settings", "link_type": "DocType", "onboard": 0, @@ -62,6 +82,7 @@ "hidden": 0, "is_query_report": 0, "label": "M-Pesa Settings", + "link_count": 0, "link_to": "Mpesa Settings", "link_type": "DocType", "onboard": 0, @@ -71,6 +92,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -79,6 +101,7 @@ "hidden": 0, "is_query_report": 0, "label": "Plaid Settings", + "link_count": 0, "link_to": "Plaid Settings", "link_type": "DocType", "onboard": 0, @@ -89,18 +112,26 @@ "hidden": 0, "is_query_report": 0, "label": "Exotel Settings", + "link_count": 0, "link_to": "Exotel Settings", "link_type": "DocType", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:35.846528", + "modified": "2021-08-05 12:15:58.740246", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, - "shortcuts": [] + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 10, + "shortcuts": [], + "title": "ERPNext Integrations" } diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json index d656b3c4fee9..fd4afb85fdd2 100644 --- a/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json +++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations_settings/erpnext_integrations_settings.json @@ -1,22 +1,27 @@ { - "category": "Modules", + "category": "", "charts": [], + "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Integrations Settings\", \"col\": 4}}]", "creation": "2020-07-31 10:38:54.021237", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", - "extends": "Settings", - "extends_another_page": 1, + "extends": "", + "extends_another_page": 0, + "for_user": "", "hide_custom": 0, + "icon": "setting", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "ERPNext Integrations Settings", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Integrations Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -25,16 +30,29 @@ "hidden": 0, "is_query_report": 0, "label": "Woocommerce Settings", + "link_count": 0, "link_to": "Woocommerce Settings", "link_type": "DocType", "onboard": 0, "type": "Link" }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Shopify Settings", + "link_count": 0, + "link_to": "Shopify Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, { "dependencies": "", "hidden": 0, "is_query_report": 0, "label": "Amazon MWS Settings", + "link_count": 0, "link_to": "Amazon MWS Settings", "link_type": "DocType", "onboard": 0, @@ -45,6 +63,7 @@ "hidden": 0, "is_query_report": 0, "label": "Plaid Settings", + "link_count": 0, "link_to": "Plaid Settings", "link_type": "DocType", "onboard": 0, @@ -55,18 +74,26 @@ "hidden": 0, "is_query_report": 0, "label": "Exotel Settings", + "link_count": 0, "link_to": "Exotel Settings", "link_type": "DocType", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:34.732552", + "modified": "2021-08-05 12:15:58.951704", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations Settings", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, - "shortcuts": [] + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 11, + "shortcuts": [], + "title": "ERPNext Integrations Settings" } diff --git a/erpnext/healthcare/workspace/healthcare/healthcare.json b/erpnext/healthcare/workspace/healthcare/healthcare.json index b93dda2e879c..55132f3695ee 100644 --- a/erpnext/healthcare/workspace/healthcare/healthcare.json +++ b/erpnext/healthcare/workspace/healthcare/healthcare.json @@ -1,5 +1,5 @@ { - "category": "Domains", + "category": "", "charts": [ { "chart_name": "Patient Appointments", @@ -7,22 +7,27 @@ } ], "charts_label": "", + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Healthcare\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Patient Appointments\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient Appointment\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Service Unit\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Practitioner\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Patient History\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Masters\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Consultation Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Consultation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Laboratory Setup\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Laboratory\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Rehabilitation and Physiotherapy\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Records and History\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]", "creation": "2020-03-02 17:23:17.919682", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "healthcare", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Healthcare", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Masters", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -31,6 +36,7 @@ "hidden": 0, "is_query_report": 0, "label": "Patient", + "link_count": 0, "link_to": "Patient", "link_type": "DocType", "onboard": 1, @@ -41,6 +47,7 @@ "hidden": 0, "is_query_report": 0, "label": "Healthcare Practitioner", + "link_count": 0, "link_to": "Healthcare Practitioner", "link_type": "DocType", "onboard": 1, @@ -51,6 +58,7 @@ "hidden": 0, "is_query_report": 0, "label": "Practitioner Schedule", + "link_count": 0, "link_to": "Practitioner Schedule", "link_type": "DocType", "onboard": 1, @@ -61,6 +69,7 @@ "hidden": 0, "is_query_report": 0, "label": "Medical Department", + "link_count": 0, "link_to": "Medical Department", "link_type": "DocType", "onboard": 0, @@ -71,6 +80,7 @@ "hidden": 0, "is_query_report": 0, "label": "Healthcare Service Unit Type", + "link_count": 0, "link_to": "Healthcare Service Unit Type", "link_type": "DocType", "onboard": 0, @@ -81,6 +91,7 @@ "hidden": 0, "is_query_report": 0, "label": "Healthcare Service Unit", + "link_count": 0, "link_to": "Healthcare Service Unit", "link_type": "DocType", "onboard": 0, @@ -91,6 +102,7 @@ "hidden": 0, "is_query_report": 0, "label": "Medical Code Standard", + "link_count": 0, "link_to": "Medical Code Standard", "link_type": "DocType", "onboard": 0, @@ -101,6 +113,7 @@ "hidden": 0, "is_query_report": 0, "label": "Medical Code", + "link_count": 0, "link_to": "Medical Code", "link_type": "DocType", "onboard": 0, @@ -110,6 +123,7 @@ "hidden": 0, "is_query_report": 0, "label": "Consultation Setup", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -118,6 +132,7 @@ "hidden": 0, "is_query_report": 0, "label": "Appointment Type", + "link_count": 0, "link_to": "Appointment Type", "link_type": "DocType", "onboard": 0, @@ -128,6 +143,7 @@ "hidden": 0, "is_query_report": 0, "label": "Clinical Procedure Template", + "link_count": 0, "link_to": "Clinical Procedure Template", "link_type": "DocType", "onboard": 0, @@ -138,6 +154,7 @@ "hidden": 0, "is_query_report": 0, "label": "Prescription Dosage", + "link_count": 0, "link_to": "Prescription Dosage", "link_type": "DocType", "onboard": 0, @@ -148,6 +165,7 @@ "hidden": 0, "is_query_report": 0, "label": "Prescription Duration", + "link_count": 0, "link_to": "Prescription Duration", "link_type": "DocType", "onboard": 0, @@ -158,6 +176,7 @@ "hidden": 0, "is_query_report": 0, "label": "Antibiotic", + "link_count": 0, "link_to": "Antibiotic", "link_type": "DocType", "onboard": 0, @@ -167,6 +186,7 @@ "hidden": 0, "is_query_report": 0, "label": "Consultation", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -175,6 +195,7 @@ "hidden": 0, "is_query_report": 0, "label": "Patient Appointment", + "link_count": 0, "link_to": "Patient Appointment", "link_type": "DocType", "onboard": 0, @@ -185,6 +206,7 @@ "hidden": 0, "is_query_report": 0, "label": "Clinical Procedure", + "link_count": 0, "link_to": "Clinical Procedure", "link_type": "DocType", "onboard": 0, @@ -195,6 +217,7 @@ "hidden": 0, "is_query_report": 0, "label": "Patient Encounter", + "link_count": 0, "link_to": "Patient Encounter", "link_type": "DocType", "onboard": 0, @@ -205,6 +228,7 @@ "hidden": 0, "is_query_report": 0, "label": "Vital Signs", + "link_count": 0, "link_to": "Vital Signs", "link_type": "DocType", "onboard": 0, @@ -215,6 +239,7 @@ "hidden": 0, "is_query_report": 0, "label": "Complaint", + "link_count": 0, "link_to": "Complaint", "link_type": "DocType", "onboard": 0, @@ -225,6 +250,7 @@ "hidden": 0, "is_query_report": 0, "label": "Diagnosis", + "link_count": 0, "link_to": "Diagnosis", "link_type": "DocType", "onboard": 0, @@ -235,6 +261,7 @@ "hidden": 0, "is_query_report": 0, "label": "Fee Validity", + "link_count": 0, "link_to": "Fee Validity", "link_type": "DocType", "onboard": 0, @@ -244,6 +271,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -252,6 +280,7 @@ "hidden": 0, "is_query_report": 0, "label": "Healthcare Settings", + "link_count": 0, "link_to": "Healthcare Settings", "link_type": "DocType", "onboard": 1, @@ -261,6 +290,7 @@ "hidden": 0, "is_query_report": 0, "label": "Laboratory Setup", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -269,6 +299,7 @@ "hidden": 0, "is_query_report": 0, "label": "Lab Test Template", + "link_count": 0, "link_to": "Lab Test Template", "link_type": "DocType", "onboard": 0, @@ -279,6 +310,7 @@ "hidden": 0, "is_query_report": 0, "label": "Lab Test Sample", + "link_count": 0, "link_to": "Lab Test Sample", "link_type": "DocType", "onboard": 0, @@ -289,6 +321,7 @@ "hidden": 0, "is_query_report": 0, "label": "Lab Test UOM", + "link_count": 0, "link_to": "Lab Test UOM", "link_type": "DocType", "onboard": 0, @@ -299,6 +332,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sensitivity", + "link_count": 0, "link_to": "Sensitivity", "link_type": "DocType", "onboard": 0, @@ -308,6 +342,7 @@ "hidden": 0, "is_query_report": 0, "label": "Laboratory", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -316,6 +351,7 @@ "hidden": 0, "is_query_report": 0, "label": "Lab Test", + "link_count": 0, "link_to": "Lab Test", "link_type": "DocType", "onboard": 0, @@ -326,6 +362,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sample Collection", + "link_count": 0, "link_to": "Sample Collection", "link_type": "DocType", "onboard": 0, @@ -336,6 +373,7 @@ "hidden": 0, "is_query_report": 0, "label": "Dosage Form", + "link_count": 0, "link_to": "Dosage Form", "link_type": "DocType", "onboard": 0, @@ -345,6 +383,7 @@ "hidden": 0, "is_query_report": 0, "label": "Rehabilitation and Physiotherapy", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -353,6 +392,7 @@ "hidden": 0, "is_query_report": 0, "label": "Exercise Type", + "link_count": 0, "link_to": "Exercise Type", "link_type": "DocType", "onboard": 1, @@ -363,6 +403,7 @@ "hidden": 0, "is_query_report": 0, "label": "Therapy Type", + "link_count": 0, "link_to": "Therapy Type", "link_type": "DocType", "onboard": 1, @@ -373,6 +414,7 @@ "hidden": 0, "is_query_report": 0, "label": "Therapy Plan", + "link_count": 0, "link_to": "Therapy Plan", "link_type": "DocType", "onboard": 0, @@ -383,6 +425,7 @@ "hidden": 0, "is_query_report": 0, "label": "Therapy Session", + "link_count": 0, "link_to": "Therapy Session", "link_type": "DocType", "onboard": 0, @@ -393,6 +436,7 @@ "hidden": 0, "is_query_report": 0, "label": "Patient Assessment Template", + "link_count": 0, "link_to": "Patient Assessment Template", "link_type": "DocType", "onboard": 0, @@ -403,6 +447,7 @@ "hidden": 0, "is_query_report": 0, "label": "Patient Assessment", + "link_count": 0, "link_to": "Patient Assessment", "link_type": "DocType", "onboard": 0, @@ -412,6 +457,7 @@ "hidden": 0, "is_query_report": 0, "label": "Records and History", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -420,6 +466,7 @@ "hidden": 0, "is_query_report": 0, "label": "Patient History", + "link_count": 0, "link_to": "patient_history", "link_type": "Page", "onboard": 0, @@ -430,6 +477,7 @@ "hidden": 0, "is_query_report": 0, "label": "Patient Progress", + "link_count": 0, "link_to": "patient-progress", "link_type": "Page", "onboard": 0, @@ -440,6 +488,7 @@ "hidden": 0, "is_query_report": 0, "label": "Patient Medical Record", + "link_count": 0, "link_to": "Patient Medical Record", "link_type": "DocType", "onboard": 0, @@ -450,6 +499,7 @@ "hidden": 0, "is_query_report": 0, "label": "Inpatient Record", + "link_count": 0, "link_to": "Inpatient Record", "link_type": "DocType", "onboard": 0, @@ -459,6 +509,7 @@ "hidden": 0, "is_query_report": 0, "label": "Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -467,6 +518,7 @@ "hidden": 0, "is_query_report": 1, "label": "Patient Appointment Analytics", + "link_count": 0, "link_to": "Patient Appointment Analytics", "link_type": "Report", "onboard": 0, @@ -477,21 +529,26 @@ "hidden": 0, "is_query_report": 1, "label": "Lab Test Report", + "link_count": 0, "link_to": "Lab Test Report", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:34.841396", + "modified": "2021-08-05 12:15:59.434612", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare", "onboarding": "Healthcare", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, "restrict_to_domain": "Healthcare", + "roles": [], + "sequence_id": 13, "shortcuts": [ { "color": "Orange", @@ -532,5 +589,6 @@ "link_to": "Healthcare", "type": "Dashboard" } - ] + ], + "title": "Healthcare" } \ No newline at end of file diff --git a/erpnext/hr/workspace/hr/hr.json b/erpnext/hr/workspace/hr/hr.json index 4500ba4560ca..575fa7be6fa0 100644 --- a/erpnext/hr/workspace/hr/hr.json +++ b/erpnext/hr/workspace/hr/hr.json @@ -1,28 +1,32 @@ { - "category": "Modules", + "category": "", "charts": [ { "chart_name": "Outgoing Salary", "label": "Outgoing Salary" } ], + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Human Resource\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Outgoing Salary\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Employee\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Leave Application\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Job Applicant\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Monthly Attendance Sheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Employee\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Employee Lifecycle\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Shift Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Leaves\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Attendance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Expense Claims\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Fleet Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Recruitment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loans\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Training\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Performance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]", "creation": "2020-03-02 15:48:58.322521", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "hr", "idx": 0, "is_default": 0, - "is_standard": 1, + "is_standard": 0, "label": "HR", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Employee", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -31,6 +35,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee", + "link_count": 0, "link_to": "Employee", "link_type": "DocType", "onboard": 1, @@ -41,6 +46,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employment Type", + "link_count": 0, "link_to": "Employment Type", "link_type": "DocType", "onboard": 0, @@ -51,6 +57,7 @@ "hidden": 0, "is_query_report": 0, "label": "Branch", + "link_count": 0, "link_to": "Branch", "link_type": "DocType", "onboard": 0, @@ -61,6 +68,7 @@ "hidden": 0, "is_query_report": 0, "label": "Department", + "link_count": 0, "link_to": "Department", "link_type": "DocType", "onboard": 0, @@ -71,6 +79,7 @@ "hidden": 0, "is_query_report": 0, "label": "Designation", + "link_count": 0, "link_to": "Designation", "link_type": "DocType", "onboard": 0, @@ -81,6 +90,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Grade", + "link_count": 0, "link_to": "Employee Grade", "link_type": "DocType", "onboard": 0, @@ -91,6 +101,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Group", + "link_count": 0, "link_to": "Employee Group", "link_type": "DocType", "onboard": 0, @@ -101,6 +112,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Health Insurance", + "link_count": 0, "link_to": "Employee Health Insurance", "link_type": "DocType", "onboard": 0, @@ -110,6 +122,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Lifecycle", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -118,6 +131,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Onboarding", + "link_count": 0, "link_to": "Employee Onboarding", "link_type": "DocType", "onboard": 0, @@ -128,6 +142,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Skill Map", + "link_count": 0, "link_to": "Employee Skill Map", "link_type": "DocType", "onboard": 0, @@ -138,6 +153,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Promotion", + "link_count": 0, "link_to": "Employee Promotion", "link_type": "DocType", "onboard": 0, @@ -148,6 +164,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Transfer", + "link_count": 0, "link_to": "Employee Transfer", "link_type": "DocType", "onboard": 0, @@ -157,6 +174,7 @@ "hidden": 0, "is_query_report": 0, "label": "Grievance Type", + "link_count": 0, "link_to": "Grievance Type", "link_type": "DocType", "onboard": 0, @@ -166,6 +184,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Grievance", + "link_count": 0, "link_to": "Employee Grievance", "link_type": "DocType", "onboard": 0, @@ -176,6 +195,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Separation", + "link_count": 0, "link_to": "Employee Separation", "link_type": "DocType", "onboard": 0, @@ -186,6 +206,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Onboarding Template", + "link_count": 0, "link_to": "Employee Onboarding Template", "link_type": "DocType", "onboard": 0, @@ -196,6 +217,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Separation Template", + "link_count": 0, "link_to": "Employee Separation Template", "link_type": "DocType", "onboard": 0, @@ -205,6 +227,7 @@ "hidden": 0, "is_query_report": 0, "label": "Shift Management", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -213,6 +236,7 @@ "hidden": 0, "is_query_report": 0, "label": "Shift Type", + "link_count": 0, "link_to": "Shift Type", "link_type": "DocType", "onboard": 0, @@ -223,6 +247,7 @@ "hidden": 0, "is_query_report": 0, "label": "Shift Request", + "link_count": 0, "link_to": "Shift Request", "link_type": "DocType", "onboard": 0, @@ -233,6 +258,7 @@ "hidden": 0, "is_query_report": 0, "label": "Shift Assignment", + "link_count": 0, "link_to": "Shift Assignment", "link_type": "DocType", "onboard": 0, @@ -242,6 +268,7 @@ "hidden": 0, "is_query_report": 0, "label": "Leaves", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -250,6 +277,7 @@ "hidden": 0, "is_query_report": 0, "label": "Holiday List", + "link_count": 0, "link_to": "Holiday List", "link_type": "DocType", "onboard": 0, @@ -260,6 +288,7 @@ "hidden": 0, "is_query_report": 0, "label": "Leave Type", + "link_count": 0, "link_to": "Leave Type", "link_type": "DocType", "onboard": 0, @@ -270,6 +299,7 @@ "hidden": 0, "is_query_report": 0, "label": "Leave Period", + "link_count": 0, "link_to": "Leave Period", "link_type": "DocType", "onboard": 0, @@ -280,6 +310,7 @@ "hidden": 0, "is_query_report": 0, "label": "Leave Policy", + "link_count": 0, "link_to": "Leave Policy", "link_type": "DocType", "onboard": 0, @@ -290,6 +321,7 @@ "hidden": 0, "is_query_report": 0, "label": "Leave Policy Assignment", + "link_count": 0, "link_to": "Leave Policy Assignment", "link_type": "DocType", "onboard": 0, @@ -300,6 +332,7 @@ "hidden": 0, "is_query_report": 0, "label": "Leave Application", + "link_count": 0, "link_to": "Leave Application", "link_type": "DocType", "onboard": 0, @@ -310,6 +343,7 @@ "hidden": 0, "is_query_report": 0, "label": "Leave Allocation", + "link_count": 0, "link_to": "Leave Allocation", "link_type": "DocType", "onboard": 0, @@ -320,6 +354,7 @@ "hidden": 0, "is_query_report": 0, "label": "Leave Encashment", + "link_count": 0, "link_to": "Leave Encashment", "link_type": "DocType", "onboard": 0, @@ -330,6 +365,7 @@ "hidden": 0, "is_query_report": 0, "label": "Leave Block List", + "link_count": 0, "link_to": "Leave Block List", "link_type": "DocType", "onboard": 0, @@ -340,6 +376,7 @@ "hidden": 0, "is_query_report": 0, "label": "Compensatory Leave Request", + "link_count": 0, "link_to": "Compensatory Leave Request", "link_type": "DocType", "onboard": 0, @@ -349,6 +386,7 @@ "hidden": 0, "is_query_report": 0, "label": "Attendance", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -357,6 +395,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Attendance Tool", + "link_count": 0, "link_to": "Employee Attendance Tool", "link_type": "DocType", "onboard": 1, @@ -367,6 +406,7 @@ "hidden": 0, "is_query_report": 0, "label": "Attendance", + "link_count": 0, "link_to": "Attendance", "link_type": "DocType", "onboard": 1, @@ -377,6 +417,7 @@ "hidden": 0, "is_query_report": 0, "label": "Attendance Request", + "link_count": 0, "link_to": "Attendance Request", "link_type": "DocType", "onboard": 0, @@ -387,6 +428,7 @@ "hidden": 0, "is_query_report": 0, "label": "Upload Attendance", + "link_count": 0, "link_to": "Upload Attendance", "link_type": "DocType", "onboard": 0, @@ -397,6 +439,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Checkin", + "link_count": 0, "link_to": "Employee Checkin", "link_type": "DocType", "onboard": 0, @@ -406,6 +449,7 @@ "hidden": 0, "is_query_report": 0, "label": "Expense Claims", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -414,6 +458,7 @@ "hidden": 0, "is_query_report": 0, "label": "Expense Claim", + "link_count": 0, "link_to": "Expense Claim", "link_type": "DocType", "onboard": 0, @@ -424,6 +469,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Advance", + "link_count": 0, "link_to": "Employee Advance", "link_type": "DocType", "onboard": 0, @@ -433,6 +479,7 @@ "hidden": 0, "is_query_report": 0, "label": "Travel Request", + "link_count": 0, "link_to": "Travel Request", "link_type": "DocType", "onboard": 0, @@ -442,6 +489,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -450,6 +498,7 @@ "hidden": 0, "is_query_report": 0, "label": "HR Settings", + "link_count": 0, "link_to": "HR Settings", "link_type": "DocType", "onboard": 0, @@ -460,6 +509,7 @@ "hidden": 0, "is_query_report": 0, "label": "Daily Work Summary Group", + "link_count": 0, "link_to": "Daily Work Summary Group", "link_type": "DocType", "onboard": 0, @@ -470,6 +520,7 @@ "hidden": 0, "is_query_report": 0, "label": "Team Updates", + "link_count": 0, "link_to": "team-updates", "link_type": "Page", "onboard": 0, @@ -479,6 +530,7 @@ "hidden": 0, "is_query_report": 0, "label": "Fleet Management", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -486,6 +538,7 @@ "hidden": 0, "is_query_report": 0, "label": "Driver", + "link_count": 0, "link_to": "Driver", "link_type": "DocType", "onboard": 0, @@ -496,6 +549,7 @@ "hidden": 0, "is_query_report": 0, "label": "Vehicle", + "link_count": 0, "link_to": "Vehicle", "link_type": "DocType", "onboard": 0, @@ -506,6 +560,7 @@ "hidden": 0, "is_query_report": 0, "label": "Vehicle Log", + "link_count": 0, "link_to": "Vehicle Log", "link_type": "DocType", "onboard": 0, @@ -516,6 +571,7 @@ "hidden": 0, "is_query_report": 1, "label": "Vehicle Expenses", + "link_count": 0, "link_to": "Vehicle Expenses", "link_type": "Report", "onboard": 0, @@ -525,6 +581,7 @@ "hidden": 0, "is_query_report": 0, "label": "Recruitment", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -533,6 +590,7 @@ "hidden": 0, "is_query_report": 0, "label": "Job Opening", + "link_count": 0, "link_to": "Job Opening", "link_type": "DocType", "onboard": 1, @@ -542,6 +600,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Referral", + "link_count": 0, "link_to": "Employee Referral", "link_type": "DocType", "onboard": 0, @@ -552,6 +611,7 @@ "hidden": 0, "is_query_report": 0, "label": "Job Applicant", + "link_count": 0, "link_to": "Job Applicant", "link_type": "DocType", "onboard": 1, @@ -562,6 +622,7 @@ "hidden": 0, "is_query_report": 0, "label": "Job Offer", + "link_count": 0, "link_to": "Job Offer", "link_type": "DocType", "onboard": 1, @@ -572,6 +633,7 @@ "hidden": 0, "is_query_report": 0, "label": "Staffing Plan", + "link_count": 0, "link_to": "Staffing Plan", "link_type": "DocType", "onboard": 0, @@ -581,6 +643,7 @@ "hidden": 0, "is_query_report": 0, "label": "Appointment Letter", + "link_count": 0, "link_to": "Appointment Letter", "link_type": "DocType", "onboard": 0, @@ -590,6 +653,7 @@ "hidden": 0, "is_query_report": 0, "label": "Appointment Letter Template", + "link_count": 0, "link_to": "Appointment Letter Template", "link_type": "DocType", "onboard": 0, @@ -599,6 +663,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loans", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -607,6 +672,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Application", + "link_count": 0, "link_to": "Loan Application", "link_type": "DocType", "onboard": 0, @@ -617,6 +683,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan", + "link_count": 0, "link_to": "Loan", "link_type": "DocType", "onboard": 0, @@ -627,6 +694,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Type", + "link_count": 0, "link_to": "Loan Type", "link_type": "DocType", "onboard": 0, @@ -636,6 +704,7 @@ "hidden": 0, "is_query_report": 0, "label": "Training", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -644,6 +713,7 @@ "hidden": 0, "is_query_report": 0, "label": "Training Program", + "link_count": 0, "link_to": "Training Program", "link_type": "DocType", "onboard": 0, @@ -654,6 +724,7 @@ "hidden": 0, "is_query_report": 0, "label": "Training Event", + "link_count": 0, "link_to": "Training Event", "link_type": "DocType", "onboard": 0, @@ -664,6 +735,7 @@ "hidden": 0, "is_query_report": 0, "label": "Training Result", + "link_count": 0, "link_to": "Training Result", "link_type": "DocType", "onboard": 0, @@ -674,6 +746,7 @@ "hidden": 0, "is_query_report": 0, "label": "Training Feedback", + "link_count": 0, "link_to": "Training Feedback", "link_type": "DocType", "onboard": 0, @@ -683,6 +756,7 @@ "hidden": 0, "is_query_report": 0, "label": "Performance", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -691,6 +765,7 @@ "hidden": 0, "is_query_report": 0, "label": "Appraisal", + "link_count": 0, "link_to": "Appraisal", "link_type": "DocType", "onboard": 0, @@ -701,6 +776,7 @@ "hidden": 0, "is_query_report": 0, "label": "Appraisal Template", + "link_count": 0, "link_to": "Appraisal Template", "link_type": "DocType", "onboard": 0, @@ -711,6 +787,7 @@ "hidden": 0, "is_query_report": 0, "label": "Energy Point Rule", + "link_count": 0, "link_to": "Energy Point Rule", "link_type": "DocType", "onboard": 0, @@ -721,6 +798,7 @@ "hidden": 0, "is_query_report": 0, "label": "Energy Point Log", + "link_count": 0, "link_to": "Energy Point Log", "link_type": "DocType", "onboard": 0, @@ -730,6 +808,7 @@ "hidden": 0, "is_query_report": 0, "label": "Key Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -738,6 +817,7 @@ "hidden": 0, "is_query_report": 1, "label": "Monthly Attendance Sheet", + "link_count": 0, "link_to": "Monthly Attendance Sheet", "link_type": "Report", "onboard": 0, @@ -748,6 +828,7 @@ "hidden": 0, "is_query_report": 1, "label": "Recruitment Analytics", + "link_count": 0, "link_to": "Recruitment Analytics", "link_type": "Report", "onboard": 0, @@ -758,6 +839,7 @@ "hidden": 0, "is_query_report": 1, "label": "Employee Analytics", + "link_count": 0, "link_to": "Employee Analytics", "link_type": "Report", "onboard": 0, @@ -768,6 +850,7 @@ "hidden": 0, "is_query_report": 1, "label": "Employee Leave Balance", + "link_count": 0, "link_to": "Employee Leave Balance", "link_type": "Report", "onboard": 0, @@ -778,6 +861,7 @@ "hidden": 0, "is_query_report": 1, "label": "Employee Leave Balance Summary", + "link_count": 0, "link_to": "Employee Leave Balance Summary", "link_type": "Report", "onboard": 0, @@ -788,6 +872,7 @@ "hidden": 0, "is_query_report": 1, "label": "Employee Advance Summary", + "link_count": 0, "link_to": "Employee Advance Summary", "link_type": "Report", "onboard": 0, @@ -797,6 +882,7 @@ "hidden": 0, "is_query_report": 0, "label": "Other Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -805,6 +891,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Information", + "link_count": 0, "link_to": "Employee Information", "link_type": "Report", "onboard": 0, @@ -815,6 +902,7 @@ "hidden": 0, "is_query_report": 1, "label": "Employee Birthday", + "link_count": 0, "link_to": "Employee Birthday", "link_type": "Report", "onboard": 0, @@ -825,6 +913,7 @@ "hidden": 0, "is_query_report": 1, "label": "Employees Working on a Holiday", + "link_count": 0, "link_to": "Employees working on a holiday", "link_type": "Report", "onboard": 0, @@ -835,20 +924,26 @@ "hidden": 0, "is_query_report": 1, "label": "Daily Work Summary Replies", + "link_count": 0, "link_to": "Daily Work Summary Replies", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2021-05-13 17:19:40.524444", + "modified": "2021-08-05 12:15:59.842918", "modified_by": "Administrator", "module": "HR", "name": "HR", "onboarding": "Human Resource", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 14, "shortcuts": [ { "color": "Green", @@ -889,5 +984,6 @@ "stats_filter": "{\n \"status\": \"Open\"\n}", "type": "Dashboard" } - ] + ], + "title": "HR" } \ No newline at end of file diff --git a/erpnext/loan_management/workspace/loan_management/loan_management.json b/erpnext/loan_management/workspace/loan_management/loan_management.json index d0b67f7c64a5..ca528ec6bd9a 100644 --- a/erpnext/loan_management/workspace/loan_management/loan_management.json +++ b/erpnext/loan_management/workspace/loan_management/loan_management.json @@ -1,23 +1,27 @@ { - "category": "Modules", + "category": "", "charts": [], + "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Loan Application\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Loan\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Processes\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Disbursement and Repayment\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Security\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]", "creation": "2020-03-12 16:35:55.299820", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "loan", "idx": 0, "is_default": 0, - "is_standard": 1, + "is_standard": 0, "label": "Loans", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Loan", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -26,6 +30,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Type", + "link_count": 0, "link_to": "Loan Type", "link_type": "DocType", "onboard": 0, @@ -36,6 +41,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Application", + "link_count": 0, "link_to": "Loan Application", "link_type": "DocType", "onboard": 0, @@ -46,6 +52,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan", + "link_count": 0, "link_to": "Loan", "link_type": "DocType", "onboard": 0, @@ -55,6 +62,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Processes", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -63,6 +71,7 @@ "hidden": 0, "is_query_report": 0, "label": "Process Loan Security Shortfall", + "link_count": 0, "link_to": "Process Loan Security Shortfall", "link_type": "DocType", "onboard": 0, @@ -73,6 +82,7 @@ "hidden": 0, "is_query_report": 0, "label": "Process Loan Interest Accrual", + "link_count": 0, "link_to": "Process Loan Interest Accrual", "link_type": "DocType", "onboard": 0, @@ -82,6 +92,7 @@ "hidden": 0, "is_query_report": 0, "label": "Disbursement and Repayment", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -90,6 +101,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Disbursement", + "link_count": 0, "link_to": "Loan Disbursement", "link_type": "DocType", "onboard": 0, @@ -100,6 +112,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Repayment", + "link_count": 0, "link_to": "Loan Repayment", "link_type": "DocType", "onboard": 0, @@ -110,6 +123,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Write Off", + "link_count": 0, "link_to": "Loan Write Off", "link_type": "DocType", "onboard": 0, @@ -120,6 +134,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Interest Accrual", + "link_count": 0, "link_to": "Loan Interest Accrual", "link_type": "DocType", "onboard": 0, @@ -129,6 +144,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Security", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -137,6 +153,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Security Type", + "link_count": 0, "link_to": "Loan Security Type", "link_type": "DocType", "onboard": 0, @@ -147,6 +164,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Security Price", + "link_count": 0, "link_to": "Loan Security Price", "link_type": "DocType", "onboard": 0, @@ -157,6 +175,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Security", + "link_count": 0, "link_to": "Loan Security", "link_type": "DocType", "onboard": 0, @@ -167,6 +186,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Security Pledge", + "link_count": 0, "link_to": "Loan Security Pledge", "link_type": "DocType", "onboard": 0, @@ -177,6 +197,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Security Unpledge", + "link_count": 0, "link_to": "Loan Security Unpledge", "link_type": "DocType", "onboard": 0, @@ -187,6 +208,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Security Shortfall", + "link_count": 0, "link_to": "Loan Security Shortfall", "link_type": "DocType", "onboard": 0, @@ -196,6 +218,7 @@ "hidden": 0, "is_query_report": 0, "label": "Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -204,6 +227,7 @@ "hidden": 0, "is_query_report": 1, "label": "Loan Repayment and Closure", + "link_count": 0, "link_to": "Loan Repayment and Closure", "link_type": "Report", "onboard": 0, @@ -214,19 +238,26 @@ "hidden": 0, "is_query_report": 1, "label": "Loan Security Status", + "link_count": 0, "link_to": "Loan Security Status", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2021-05-25 17:31:53.586508", + "modified": "2021-08-05 12:18:13.350904", "modified_by": "Administrator", "module": "Loan Management", "name": "Loans", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 16, "shortcuts": [ { "color": "Green", @@ -247,5 +278,6 @@ "link_to": "Loan Dashboard", "type": "Dashboard" } - ] + ], + "title": "Loans" } \ No newline at end of file diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json index a355203e4d7f..84eabcd2bdb0 100644 --- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json @@ -1,26 +1,31 @@ { - "category": "Domains", + "category": "", "charts": [ { "chart_name": "Produced Quantity" } ], + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Manufacturing\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"BOM\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Work Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Production Plan\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Forecasting\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Work Order Summary\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"BOM Stock Report\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Production Planning Report\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Production\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Bill of Materials\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}]", "creation": "2020-03-02 17:11:37.032604", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "organization", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Manufacturing", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Production", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -29,6 +34,7 @@ "hidden": 0, "is_query_report": 0, "label": "Work Order", + "link_count": 0, "link_to": "Work Order", "link_type": "DocType", "onboard": 1, @@ -39,6 +45,7 @@ "hidden": 0, "is_query_report": 0, "label": "Production Plan", + "link_count": 0, "link_to": "Production Plan", "link_type": "DocType", "onboard": 1, @@ -49,6 +56,7 @@ "hidden": 0, "is_query_report": 0, "label": "Stock Entry", + "link_count": 0, "link_to": "Stock Entry", "link_type": "DocType", "onboard": 1, @@ -59,6 +67,7 @@ "hidden": 0, "is_query_report": 0, "label": "Job Card", + "link_count": 0, "link_to": "Job Card", "link_type": "DocType", "onboard": 0, @@ -69,6 +78,7 @@ "hidden": 0, "is_query_report": 0, "label": "Downtime Entry", + "link_count": 0, "link_to": "Downtime Entry", "link_type": "DocType", "onboard": 0, @@ -78,6 +88,7 @@ "hidden": 0, "is_query_report": 0, "label": "Bill of Materials", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -86,6 +97,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item", + "link_count": 0, "link_to": "Item", "link_type": "DocType", "onboard": 1, @@ -96,6 +108,7 @@ "hidden": 0, "is_query_report": 0, "label": "Bill of Materials", + "link_count": 0, "link_to": "BOM", "link_type": "DocType", "onboard": 1, @@ -106,6 +119,7 @@ "hidden": 0, "is_query_report": 0, "label": "Workstation", + "link_count": 0, "link_to": "Workstation", "link_type": "DocType", "onboard": 0, @@ -116,6 +130,7 @@ "hidden": 0, "is_query_report": 0, "label": "Operation", + "link_count": 0, "link_to": "Operation", "link_type": "DocType", "onboard": 0, @@ -126,6 +141,7 @@ "hidden": 0, "is_query_report": 0, "label": "Routing", + "link_count": 0, "link_to": "Routing", "link_type": "DocType", "onboard": 0, @@ -135,6 +151,7 @@ "hidden": 0, "is_query_report": 0, "label": "Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -143,6 +160,7 @@ "hidden": 0, "is_query_report": 1, "label": "Production Planning Report", + "link_count": 0, "link_to": "Production Planning Report", "link_type": "Report", "onboard": 0, @@ -153,6 +171,7 @@ "hidden": 0, "is_query_report": 1, "label": "Work Order Summary", + "link_count": 0, "link_to": "Work Order Summary", "link_type": "Report", "onboard": 0, @@ -163,6 +182,7 @@ "hidden": 0, "is_query_report": 1, "label": "Quality Inspection Summary", + "link_count": 0, "link_to": "Quality Inspection Summary", "link_type": "Report", "onboard": 0, @@ -173,6 +193,7 @@ "hidden": 0, "is_query_report": 1, "label": "Downtime Analysis", + "link_count": 0, "link_to": "Downtime Analysis", "link_type": "Report", "onboard": 0, @@ -183,6 +204,7 @@ "hidden": 0, "is_query_report": 1, "label": "Job Card Summary", + "link_count": 0, "link_to": "Job Card Summary", "link_type": "Report", "onboard": 0, @@ -193,6 +215,7 @@ "hidden": 0, "is_query_report": 1, "label": "BOM Search", + "link_count": 0, "link_to": "BOM Search", "link_type": "Report", "onboard": 0, @@ -203,6 +226,7 @@ "hidden": 0, "is_query_report": 1, "label": "BOM Stock Report", + "link_count": 0, "link_to": "BOM Stock Report", "link_type": "Report", "onboard": 0, @@ -213,6 +237,7 @@ "hidden": 0, "is_query_report": 1, "label": "Production Analytics", + "link_count": 0, "link_to": "Production Analytics", "link_type": "Report", "onboard": 0, @@ -223,6 +248,7 @@ "hidden": 0, "is_query_report": 1, "label": "BOM Operations Time", + "link_count": 0, "link_to": "BOM Operations Time", "link_type": "Report", "onboard": 0, @@ -232,6 +258,7 @@ "hidden": 0, "is_query_report": 0, "label": "Tools", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -240,6 +267,7 @@ "hidden": 0, "is_query_report": 0, "label": "BOM Update Tool", + "link_count": 0, "link_to": "BOM Update Tool", "link_type": "DocType", "onboard": 0, @@ -250,6 +278,7 @@ "hidden": 0, "is_query_report": 0, "label": "BOM Comparison Tool", + "link_count": 0, "link_to": "bom-comparison-tool", "link_type": "Page", "onboard": 0, @@ -259,6 +288,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -267,21 +297,26 @@ "hidden": 0, "is_query_report": 0, "label": "Manufacturing Settings", + "link_count": 0, "link_to": "Manufacturing Settings", "link_type": "DocType", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:39.365928", + "modified": "2021-08-05 12:16:00.825741", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", "onboarding": "Manufacturing", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, "restrict_to_domain": "Manufacturing", + "roles": [], + "sequence_id": 17, "shortcuts": [ { "color": "Green", @@ -346,5 +381,6 @@ "restrict_to_domain": "Manufacturing", "type": "Dashboard" } - ] + ], + "title": "Manufacturing" } \ No newline at end of file diff --git a/erpnext/non_profit/workspace/non_profit/non_profit.json b/erpnext/non_profit/workspace/non_profit/non_profit.json index 2557d77d8819..e6d4445945ec 100644 --- a/erpnext/non_profit/workspace/non_profit/non_profit.json +++ b/erpnext/non_profit/workspace/non_profit/non_profit.json @@ -1,23 +1,27 @@ { - "category": "Domains", + "category": "", "charts": [], + "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Member\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Non Profit Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Membership\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chapter\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Chapter Member\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loan Management\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Grant Application\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Membership\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Volunteer\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Chapter\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Donation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tax Exemption Certification (India)\", \"col\": 4}}]", "creation": "2020-03-02 17:23:47.811421", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "non-profit", "idx": 0, "is_default": 0, - "is_standard": 1, + "is_standard": 0, "label": "Non Profit", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Loan Management", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -26,6 +30,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Type", + "link_count": 0, "link_to": "Loan Type", "link_type": "DocType", "onboard": 0, @@ -36,6 +41,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan Application", + "link_count": 0, "link_to": "Loan Application", "link_type": "DocType", "onboard": 0, @@ -46,6 +52,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loan", + "link_count": 0, "link_to": "Loan", "link_type": "DocType", "onboard": 0, @@ -55,6 +62,7 @@ "hidden": 0, "is_query_report": 0, "label": "Grant Application", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -63,6 +71,7 @@ "hidden": 0, "is_query_report": 0, "label": "Grant Application", + "link_count": 0, "link_to": "Grant Application", "link_type": "DocType", "onboard": 0, @@ -72,6 +81,7 @@ "hidden": 0, "is_query_report": 0, "label": "Membership", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -80,6 +90,7 @@ "hidden": 0, "is_query_report": 0, "label": "Member", + "link_count": 0, "link_to": "Member", "link_type": "DocType", "onboard": 1, @@ -90,6 +101,7 @@ "hidden": 0, "is_query_report": 0, "label": "Membership", + "link_count": 0, "link_to": "Membership", "link_type": "DocType", "onboard": 1, @@ -100,6 +112,7 @@ "hidden": 0, "is_query_report": 0, "label": "Membership Type", + "link_count": 0, "link_to": "Membership Type", "link_type": "DocType", "onboard": 0, @@ -110,6 +123,7 @@ "hidden": 0, "is_query_report": 0, "label": "Membership Settings", + "link_count": 0, "link_to": "Non Profit Settings", "link_type": "DocType", "onboard": 0, @@ -119,6 +133,7 @@ "hidden": 0, "is_query_report": 0, "label": "Volunteer", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -127,6 +142,7 @@ "hidden": 0, "is_query_report": 0, "label": "Volunteer", + "link_count": 0, "link_to": "Volunteer", "link_type": "DocType", "onboard": 1, @@ -137,6 +153,7 @@ "hidden": 0, "is_query_report": 0, "label": "Volunteer Type", + "link_count": 0, "link_to": "Volunteer Type", "link_type": "DocType", "onboard": 0, @@ -146,6 +163,7 @@ "hidden": 0, "is_query_report": 0, "label": "Chapter", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -154,6 +172,7 @@ "hidden": 0, "is_query_report": 0, "label": "Chapter", + "link_count": 0, "link_to": "Chapter", "link_type": "DocType", "onboard": 1, @@ -163,6 +182,7 @@ "hidden": 0, "is_query_report": 0, "label": "Donation", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -171,6 +191,7 @@ "hidden": 0, "is_query_report": 0, "label": "Donor", + "link_count": 0, "link_to": "Donor", "link_type": "DocType", "onboard": 0, @@ -181,6 +202,7 @@ "hidden": 0, "is_query_report": 0, "label": "Donor Type", + "link_count": 0, "link_to": "Donor Type", "link_type": "DocType", "onboard": 0, @@ -190,6 +212,7 @@ "hidden": 0, "is_query_report": 0, "label": "Donation", + "link_count": 0, "link_to": "Donation", "link_type": "DocType", "onboard": 0, @@ -199,6 +222,7 @@ "hidden": 0, "is_query_report": 0, "label": "Tax Exemption Certification (India)", + "link_count": 0, "link_type": "DocType", "onboard": 0, "type": "Card Break" @@ -207,20 +231,26 @@ "hidden": 0, "is_query_report": 0, "label": "Tax Exemption 80G Certificate", + "link_count": 0, "link_to": "Tax Exemption 80G Certificate", "link_type": "DocType", "onboard": 0, "type": "Link" } ], - "modified": "2021-03-11 11:38:09.140655", + "modified": "2021-08-05 12:16:01.146206", "modified_by": "Administrator", "module": "Non Profit", "name": "Non Profit", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, "restrict_to_domain": "Non Profit", + "roles": [], + "sequence_id": 18, "shortcuts": [ { "label": "Member", @@ -247,5 +277,6 @@ "link_to": "Chapter Member", "type": "DocType" } - ] + ], + "title": "Non Profit" } \ No newline at end of file diff --git a/erpnext/payroll/workspace/payroll/payroll.json b/erpnext/payroll/workspace/payroll/payroll.json index 814973063da3..b55bdc771127 100644 --- a/erpnext/payroll/workspace/payroll/payroll.json +++ b/erpnext/payroll/workspace/payroll/payroll.json @@ -1,27 +1,32 @@ { - "category": "Modules", + "category": "", "charts": [ { "chart_name": "Outgoing Salary", "label": "Outgoing Salary" } ], + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Payroll\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Outgoing Salary\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Structure\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Payroll Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Slip\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Income Tax Slab\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Salary Register\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Payroll\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Taxation\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Compensations\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]", "creation": "2020-05-27 19:54:23.405607", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "money-coins-1", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Payroll", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Payroll", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -30,6 +35,7 @@ "hidden": 0, "is_query_report": 0, "label": "Salary Component", + "link_count": 0, "link_to": "Salary Component", "link_type": "DocType", "onboard": 1, @@ -40,6 +46,7 @@ "hidden": 0, "is_query_report": 0, "label": "Salary Structure", + "link_count": 0, "link_to": "Salary Structure", "link_type": "DocType", "onboard": 1, @@ -50,6 +57,7 @@ "hidden": 0, "is_query_report": 0, "label": "Salary Structure Assignment", + "link_count": 0, "link_to": "Salary Structure Assignment", "link_type": "DocType", "onboard": 1, @@ -60,6 +68,7 @@ "hidden": 0, "is_query_report": 0, "label": "Payroll Entry", + "link_count": 0, "link_to": "Payroll Entry", "link_type": "DocType", "onboard": 1, @@ -70,6 +79,7 @@ "hidden": 0, "is_query_report": 0, "label": "Salary Slip", + "link_count": 0, "link_to": "Salary Slip", "link_type": "DocType", "onboard": 1, @@ -79,6 +89,7 @@ "hidden": 0, "is_query_report": 0, "label": "Taxation", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -87,6 +98,7 @@ "hidden": 0, "is_query_report": 0, "label": "Payroll Period", + "link_count": 0, "link_to": "Payroll Period", "link_type": "DocType", "onboard": 1, @@ -97,6 +109,7 @@ "hidden": 0, "is_query_report": 0, "label": "Income Tax Slab", + "link_count": 0, "link_to": "Income Tax Slab", "link_type": "DocType", "onboard": 1, @@ -107,6 +120,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Other Income", + "link_count": 0, "link_to": "Employee Other Income", "link_type": "DocType", "onboard": 1, @@ -117,6 +131,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Declaration", + "link_count": 0, "link_to": "Employee Tax Exemption Declaration", "link_type": "DocType", "onboard": 1, @@ -127,6 +142,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Proof Submission", + "link_count": 0, "link_to": "Employee Tax Exemption Proof Submission", "link_type": "DocType", "onboard": 1, @@ -137,6 +153,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Category", + "link_count": 0, "link_to": "Employee Tax Exemption Category", "link_type": "DocType", "onboard": 0, @@ -147,6 +164,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Tax Exemption Sub Category", + "link_count": 0, "link_to": "Employee Tax Exemption Sub Category", "link_type": "DocType", "onboard": 0, @@ -156,6 +174,7 @@ "hidden": 0, "is_query_report": 0, "label": "Compensations", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -164,6 +183,7 @@ "hidden": 0, "is_query_report": 0, "label": "Additional Salary", + "link_count": 0, "link_to": "Additional Salary", "link_type": "DocType", "onboard": 1, @@ -174,6 +194,7 @@ "hidden": 0, "is_query_report": 0, "label": "Retention Bonus", + "link_count": 0, "link_to": "Retention Bonus", "link_type": "DocType", "onboard": 1, @@ -184,6 +205,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Incentive", + "link_count": 0, "link_to": "Employee Incentive", "link_type": "DocType", "onboard": 1, @@ -194,6 +216,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Benefit Application", + "link_count": 0, "link_to": "Employee Benefit Application", "link_type": "DocType", "onboard": 0, @@ -204,6 +227,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Benefit Claim", + "link_count": 0, "link_to": "Employee Benefit Claim", "link_type": "DocType", "onboard": 0, @@ -213,6 +237,7 @@ "hidden": 0, "is_query_report": 0, "label": "Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -221,6 +246,7 @@ "hidden": 0, "is_query_report": 1, "label": "Salary Register", + "link_count": 0, "link_to": "Salary Register", "link_type": "Report", "onboard": 0, @@ -231,6 +257,7 @@ "hidden": 0, "is_query_report": 1, "label": "Salary Payments Based On Payment Mode", + "link_count": 0, "link_to": "Salary Payments Based On Payment Mode", "link_type": "Report", "onboard": 0, @@ -241,6 +268,7 @@ "hidden": 0, "is_query_report": 1, "label": "Salary Payments via ECS", + "link_count": 0, "link_to": "Salary Payments via ECS", "link_type": "Report", "onboard": 0, @@ -251,6 +279,7 @@ "hidden": 0, "is_query_report": 1, "label": "Income Tax Deductions", + "link_count": 0, "link_to": "Income Tax Deductions", "link_type": "Report", "onboard": 0, @@ -261,6 +290,7 @@ "hidden": 0, "is_query_report": 1, "label": "Professional Tax Deductions", + "link_count": 0, "link_to": "Professional Tax Deductions", "link_type": "Report", "onboard": 0, @@ -271,6 +301,7 @@ "hidden": 0, "is_query_report": 1, "label": "Provident Fund Deductions", + "link_count": 0, "link_to": "Provident Fund Deductions", "link_type": "Report", "onboard": 0, @@ -281,20 +312,26 @@ "hidden": 0, "is_query_report": 1, "label": "Bank Remittance", + "link_count": 0, "link_to": "Bank Remittance", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:37.205628", + "modified": "2021-08-05 12:16:01.335324", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll", "onboarding": "Payroll", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 19, "shortcuts": [ { "label": "Salary Structure", @@ -329,5 +366,6 @@ "link_to": "Payroll", "type": "Dashboard" } - ] + ], + "title": "Payroll" } \ No newline at end of file diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json index c023a73ff4e2..065f1eda1f37 100644 --- a/erpnext/projects/workspace/projects/projects.json +++ b/erpnext/projects/workspace/projects/projects.json @@ -1,28 +1,32 @@ { - "category": "Modules", + "category": "", "charts": [ { "chart_name": "Project Summary", "label": "Open Projects" } ], + "content": "[{\"type\": \"chart\", \"data\": {\"chart_name\": \"Open Projects\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Task\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Project\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Timesheet\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Project Billing Summary\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Projects\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Time Tracking\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]", "creation": "2020-03-02 15:46:04.874669", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "project", "idx": 0, "is_default": 0, - "is_standard": 1, + "is_standard": 0, "label": "Projects", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Projects", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -31,6 +35,7 @@ "hidden": 0, "is_query_report": 0, "label": "Project", + "link_count": 0, "link_to": "Project", "link_type": "DocType", "onboard": 1, @@ -41,6 +46,7 @@ "hidden": 0, "is_query_report": 0, "label": "Task", + "link_count": 0, "link_to": "Task", "link_type": "DocType", "onboard": 1, @@ -51,6 +57,7 @@ "hidden": 0, "is_query_report": 0, "label": "Project Template", + "link_count": 0, "link_to": "Project Template", "link_type": "DocType", "onboard": 0, @@ -61,6 +68,7 @@ "hidden": 0, "is_query_report": 0, "label": "Project Type", + "link_count": 0, "link_to": "Project Type", "link_type": "DocType", "onboard": 0, @@ -71,6 +79,7 @@ "hidden": 0, "is_query_report": 0, "label": "Project Update", + "link_count": 0, "link_to": "Project Update", "link_type": "DocType", "onboard": 0, @@ -80,6 +89,7 @@ "hidden": 0, "is_query_report": 0, "label": "Time Tracking", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -88,6 +98,7 @@ "hidden": 0, "is_query_report": 0, "label": "Timesheet", + "link_count": 0, "link_to": "Timesheet", "link_type": "DocType", "onboard": 1, @@ -98,6 +109,7 @@ "hidden": 0, "is_query_report": 0, "label": "Activity Type", + "link_count": 0, "link_to": "Activity Type", "link_type": "DocType", "onboard": 1, @@ -108,6 +120,7 @@ "hidden": 0, "is_query_report": 0, "label": "Activity Cost", + "link_count": 0, "link_to": "Activity Cost", "link_type": "DocType", "onboard": 0, @@ -117,6 +130,7 @@ "hidden": 0, "is_query_report": 0, "label": "Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -125,6 +139,7 @@ "hidden": 0, "is_query_report": 1, "label": "Daily Timesheet Summary", + "link_count": 0, "link_to": "Daily Timesheet Summary", "link_type": "Report", "onboard": 1, @@ -135,6 +150,7 @@ "hidden": 0, "is_query_report": 1, "label": "Employee Hours Utilization", + "link_count": 0, "link_to": "Employee Hours Utilization Based On Timesheet", "link_type": "Report", "onboard": 0, @@ -145,6 +161,7 @@ "hidden": 0, "is_query_report": 1, "label": "Project Profitability", + "link_count": 0, "link_to": "Project Profitability", "link_type": "Report", "onboard": 0, @@ -155,6 +172,7 @@ "hidden": 0, "is_query_report": 1, "label": "Project wise Stock Tracking", + "link_count": 0, "link_to": "Project wise Stock Tracking", "link_type": "Report", "onboard": 0, @@ -165,6 +183,7 @@ "hidden": 0, "is_query_report": 1, "label": "Project Billing Summary", + "link_count": 0, "link_to": "Project Billing Summary", "link_type": "Report", "onboard": 0, @@ -175,19 +194,26 @@ "hidden": 0, "is_query_report": 1, "label": "Delayed Tasks Summary", + "link_count": 0, "link_to": "Delayed Tasks Summary", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2021-04-25 16:27:16.548780", + "modified": "2021-08-05 12:16:01.540145", "modified_by": "Administrator", "module": "Projects", "name": "Projects", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 20, "shortcuts": [ { "color": "Blue", @@ -220,5 +246,6 @@ "link_to": "Project", "type": "Dashboard" } - ] + ], + "title": "Projects" } \ No newline at end of file diff --git a/erpnext/quality_management/workspace/quality/quality.json b/erpnext/quality_management/workspace/quality/quality.json index e5fef4355052..4dc8129d8903 100644 --- a/erpnext/quality_management/workspace/quality/quality.json +++ b/erpnext/quality_management/workspace/quality/quality.json @@ -1,22 +1,27 @@ { - "category": "Modules", + "category": "", "charts": [], + "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Goal\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Procedure\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Inspection\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Review\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Quality Action\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Non Conformance\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Goal and Procedure\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Feedback\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Meeting\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Review and Action\", \"col\": 4}}]", "creation": "2020-03-02 15:49:28.632014", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "quality", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Quality", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Goal and Procedure", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -25,6 +30,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quality Goal", + "link_count": 0, "link_to": "Quality Goal", "link_type": "DocType", "onboard": 1, @@ -35,6 +41,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quality Procedure", + "link_count": 0, "link_to": "Quality Procedure", "link_type": "DocType", "onboard": 1, @@ -45,6 +52,7 @@ "hidden": 0, "is_query_report": 0, "label": "Tree of Procedures", + "link_count": 0, "link_to": "Quality Procedure", "link_type": "DocType", "onboard": 0, @@ -54,6 +62,7 @@ "hidden": 0, "is_query_report": 0, "label": "Feedback", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -62,6 +71,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quality Feedback", + "link_count": 0, "link_to": "Quality Feedback", "link_type": "DocType", "onboard": 1, @@ -72,6 +82,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quality Feedback Template", + "link_count": 0, "link_to": "Quality Feedback Template", "link_type": "DocType", "onboard": 0, @@ -81,6 +92,7 @@ "hidden": 0, "is_query_report": 0, "label": "Meeting", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -89,6 +101,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quality Meeting", + "link_count": 0, "link_to": "Quality Meeting", "link_type": "DocType", "onboard": 0, @@ -98,6 +111,7 @@ "hidden": 0, "is_query_report": 0, "label": "Review and Action", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -106,6 +120,7 @@ "hidden": 0, "is_query_report": 0, "label": "Non Conformance", + "link_count": 0, "link_to": "Non Conformance", "link_type": "DocType", "onboard": 0, @@ -116,6 +131,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quality Review", + "link_count": 0, "link_to": "Quality Review", "link_type": "DocType", "onboard": 0, @@ -126,19 +142,26 @@ "hidden": 0, "is_query_report": 0, "label": "Quality Action", + "link_count": 0, "link_to": "Quality Action", "link_type": "DocType", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:35.120213", + "modified": "2021-08-05 12:16:01.699912", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 21, "shortcuts": [ { "color": "Grey", @@ -186,5 +209,6 @@ "stats_filter": "{\"status\": \"Open\"}", "type": "DocType" } - ] + ], + "title": "Quality" } \ No newline at end of file diff --git a/erpnext/selling/workspace/retail/retail.json b/erpnext/selling/workspace/retail/retail.json index e20f8347c25e..9d2e6cabbc37 100644 --- a/erpnext/selling/workspace/retail/retail.json +++ b/erpnext/selling/workspace/retail/retail.json @@ -1,22 +1,27 @@ { - "category": "Domains", + "category": "", "charts": [], + "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Point Of Sale\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings & Configurations\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Loyalty Program\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Opening & Closing\", \"col\": 4}}]", "creation": "2020-03-02 17:18:32.505616", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "retail", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Retail", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Settings & Configurations", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -25,6 +30,7 @@ "hidden": 0, "is_query_report": 0, "label": "Point-of-Sale Profile", + "link_count": 0, "link_to": "POS Profile", "link_type": "DocType", "onboard": 1, @@ -35,6 +41,7 @@ "hidden": 0, "is_query_report": 0, "label": "POS Settings", + "link_count": 0, "link_to": "POS Settings", "link_type": "DocType", "onboard": 0, @@ -44,6 +51,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loyalty Program", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -52,6 +60,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loyalty Program", + "link_count": 0, "link_to": "Loyalty Program", "link_type": "DocType", "onboard": 0, @@ -62,6 +71,7 @@ "hidden": 0, "is_query_report": 0, "label": "Loyalty Point Entry", + "link_count": 0, "link_to": "Loyalty Point Entry", "link_type": "DocType", "onboard": 0, @@ -71,6 +81,7 @@ "hidden": 0, "is_query_report": 0, "label": "Opening & Closing", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -79,6 +90,7 @@ "hidden": 0, "is_query_report": 0, "label": "POS Opening Entry", + "link_count": 0, "link_to": "POS Opening Entry", "link_type": "DocType", "onboard": 0, @@ -89,20 +101,26 @@ "hidden": 0, "is_query_report": 0, "label": "POS Closing Entry", + "link_count": 0, "link_to": "POS Closing Entry", "link_type": "DocType", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:36.758038", + "modified": "2021-08-05 12:16:01.840988", "modified_by": "Administrator", "module": "Selling", "name": "Retail", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, "restrict_to_domain": "Retail", + "roles": [], + "sequence_id": 22, "shortcuts": [ { "doc_view": "", @@ -110,5 +128,6 @@ "link_to": "point-of-sale", "type": "Page" } - ] + ], + "title": "Retail" } \ No newline at end of file diff --git a/erpnext/selling/workspace/selling/selling.json b/erpnext/selling/workspace/selling/selling.json index 879034a0dfcc..345187f93c48 100644 --- a/erpnext/selling/workspace/selling/selling.json +++ b/erpnext/selling/workspace/selling/selling.json @@ -1,5 +1,5 @@ { - "category": "Modules", + "category": "", "charts": [ { "chart_name": "Sales Order Trends", @@ -7,22 +7,27 @@ } ], "charts_label": "Selling ", + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Selling\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": \"Sales Order Trends\", \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Quick Access\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Order\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Analytics\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Sales Order Analysis\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Selling\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items and Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}]", "creation": "2020-01-28 11:49:12.092882", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, - "hide_custom": 1, + "for_user": "", + "hide_custom": 0, "icon": "sell", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Selling", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Selling", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -31,6 +36,7 @@ "hidden": 0, "is_query_report": 0, "label": "Customer", + "link_count": 0, "link_to": "Customer", "link_type": "DocType", "onboard": 1, @@ -41,6 +47,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quotation", + "link_count": 0, "link_to": "Quotation", "link_type": "DocType", "onboard": 1, @@ -51,6 +58,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sales Order", + "link_count": 0, "link_to": "Sales Order", "link_type": "DocType", "onboard": 1, @@ -61,6 +69,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sales Invoice", + "link_count": 0, "link_to": "Sales Invoice", "link_type": "DocType", "onboard": 1, @@ -71,6 +80,7 @@ "hidden": 0, "is_query_report": 0, "label": "Blanket Order", + "link_count": 0, "link_to": "Blanket Order", "link_type": "DocType", "onboard": 1, @@ -81,6 +91,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sales Partner", + "link_count": 0, "link_to": "Sales Partner", "link_type": "DocType", "onboard": 0, @@ -91,6 +102,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sales Person", + "link_count": 0, "link_to": "Sales Person", "link_type": "DocType", "onboard": 0, @@ -100,6 +112,7 @@ "hidden": 0, "is_query_report": 0, "label": "Items and Pricing", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -108,6 +121,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item", + "link_count": 0, "link_to": "Item", "link_type": "DocType", "onboard": 1, @@ -118,6 +132,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Price", + "link_count": 0, "link_to": "Item Price", "link_type": "DocType", "onboard": 1, @@ -128,6 +143,7 @@ "hidden": 0, "is_query_report": 0, "label": "Price List", + "link_count": 0, "link_to": "Price List", "link_type": "DocType", "onboard": 1, @@ -138,6 +154,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Group", + "link_count": 0, "link_to": "Item Group", "link_type": "DocType", "onboard": 1, @@ -148,6 +165,7 @@ "hidden": 0, "is_query_report": 0, "label": "Product Bundle", + "link_count": 0, "link_to": "Product Bundle", "link_type": "DocType", "onboard": 0, @@ -158,6 +176,7 @@ "hidden": 0, "is_query_report": 0, "label": "Promotional Scheme", + "link_count": 0, "link_to": "Promotional Scheme", "link_type": "DocType", "onboard": 0, @@ -168,6 +187,7 @@ "hidden": 0, "is_query_report": 0, "label": "Pricing Rule", + "link_count": 0, "link_to": "Pricing Rule", "link_type": "DocType", "onboard": 0, @@ -178,6 +198,7 @@ "hidden": 0, "is_query_report": 0, "label": "Shipping Rule", + "link_count": 0, "link_to": "Shipping Rule", "link_type": "DocType", "onboard": 0, @@ -188,6 +209,7 @@ "hidden": 0, "is_query_report": 0, "label": "Coupon Code", + "link_count": 0, "link_to": "Coupon Code", "link_type": "DocType", "onboard": 0, @@ -197,6 +219,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -205,6 +228,7 @@ "hidden": 0, "is_query_report": 0, "label": "Selling Settings", + "link_count": 0, "link_to": "Selling Settings", "link_type": "DocType", "onboard": 0, @@ -215,6 +239,7 @@ "hidden": 0, "is_query_report": 0, "label": "Terms and Conditions Template", + "link_count": 0, "link_to": "Terms and Conditions", "link_type": "DocType", "onboard": 1, @@ -225,6 +250,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sales Taxes and Charges Template", + "link_count": 0, "link_to": "Sales Taxes and Charges Template", "link_type": "DocType", "onboard": 1, @@ -235,6 +261,7 @@ "hidden": 0, "is_query_report": 0, "label": "Lead Source", + "link_count": 0, "link_to": "Lead Source", "link_type": "DocType", "onboard": 0, @@ -245,6 +272,7 @@ "hidden": 0, "is_query_report": 0, "label": "Customer Group", + "link_count": 0, "link_to": "Customer Group", "link_type": "DocType", "onboard": 0, @@ -255,6 +283,7 @@ "hidden": 0, "is_query_report": 0, "label": "Contact", + "link_count": 0, "link_to": "Contact", "link_type": "DocType", "onboard": 0, @@ -265,6 +294,7 @@ "hidden": 0, "is_query_report": 0, "label": "Address", + "link_count": 0, "link_to": "Address", "link_type": "DocType", "onboard": 0, @@ -275,6 +305,7 @@ "hidden": 0, "is_query_report": 0, "label": "Territory", + "link_count": 0, "link_to": "Territory", "link_type": "DocType", "onboard": 0, @@ -285,6 +316,7 @@ "hidden": 0, "is_query_report": 0, "label": "Campaign", + "link_count": 0, "link_to": "Campaign", "link_type": "DocType", "onboard": 0, @@ -294,6 +326,7 @@ "hidden": 0, "is_query_report": 0, "label": "Key Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -302,6 +335,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Analytics", + "link_count": 0, "link_to": "Sales Analytics", "link_type": "Report", "onboard": 1, @@ -312,6 +346,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Order Analysis", + "link_count": 0, "link_to": "Sales Order Analysis", "link_type": "Report", "onboard": 1, @@ -322,6 +357,7 @@ "hidden": 0, "is_query_report": 0, "label": "Sales Funnel", + "link_count": 0, "link_to": "sales-funnel", "link_type": "Page", "onboard": 1, @@ -332,6 +368,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Order Trends", + "link_count": 0, "link_to": "Sales Order Trends", "link_type": "Report", "onboard": 0, @@ -342,6 +379,7 @@ "hidden": 0, "is_query_report": 1, "label": "Quotation Trends", + "link_count": 0, "link_to": "Quotation Trends", "link_type": "Report", "onboard": 0, @@ -352,6 +390,7 @@ "hidden": 0, "is_query_report": 1, "label": "Customer Acquisition and Loyalty", + "link_count": 0, "link_to": "Customer Acquisition and Loyalty", "link_type": "Report", "onboard": 0, @@ -362,6 +401,7 @@ "hidden": 0, "is_query_report": 1, "label": "Inactive Customers", + "link_count": 0, "link_to": "Inactive Customers", "link_type": "Report", "onboard": 0, @@ -372,6 +412,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Person-wise Transaction Summary", + "link_count": 0, "link_to": "Sales Person-wise Transaction Summary", "link_type": "Report", "onboard": 0, @@ -382,6 +423,7 @@ "hidden": 0, "is_query_report": 1, "label": "Item-wise Sales History", + "link_count": 0, "link_to": "Item-wise Sales History", "link_type": "Report", "onboard": 0, @@ -391,6 +433,7 @@ "hidden": 0, "is_query_report": 0, "label": "Other Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -399,6 +442,7 @@ "hidden": 0, "is_query_report": 1, "label": "Lead Details", + "link_count": 0, "link_to": "Lead Details", "link_type": "Report", "onboard": 0, @@ -409,6 +453,7 @@ "hidden": 0, "is_query_report": 1, "label": "Customer Addresses And Contacts", + "link_count": 0, "link_to": "Address And Contacts", "link_type": "Report", "onboard": 0, @@ -419,6 +464,7 @@ "hidden": 0, "is_query_report": 1, "label": "Available Stock for Packing Items", + "link_count": 0, "link_to": "Available Stock for Packing Items", "link_type": "Report", "onboard": 0, @@ -429,6 +475,7 @@ "hidden": 0, "is_query_report": 1, "label": "Pending SO Items For Purchase Request", + "link_count": 0, "link_to": "Pending SO Items For Purchase Request", "link_type": "Report", "onboard": 0, @@ -439,6 +486,7 @@ "hidden": 0, "is_query_report": 1, "label": "Delivery Note Trends", + "link_count": 0, "link_to": "Delivery Note Trends", "link_type": "Report", "onboard": 0, @@ -449,6 +497,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Invoice Trends", + "link_count": 0, "link_to": "Sales Invoice Trends", "link_type": "Report", "onboard": 0, @@ -459,6 +508,7 @@ "hidden": 0, "is_query_report": 1, "label": "Customer Credit Balance", + "link_count": 0, "link_to": "Customer Credit Balance", "link_type": "Report", "onboard": 0, @@ -469,6 +519,7 @@ "hidden": 0, "is_query_report": 1, "label": "Customers Without Any Sales Transactions", + "link_count": 0, "link_to": "Customers Without Any Sales Transactions", "link_type": "Report", "onboard": 0, @@ -479,6 +530,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Partners Commission", + "link_count": 0, "link_to": "Sales Partners Commission", "link_type": "Report", "onboard": 0, @@ -489,6 +541,7 @@ "hidden": 0, "is_query_report": 1, "label": "Territory Target Variance Based On Item Group", + "link_count": 0, "link_to": "Territory Target Variance Based On Item Group", "link_type": "Report", "onboard": 0, @@ -499,6 +552,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Person Target Variance Based On Item Group", + "link_count": 0, "link_to": "Sales Person Target Variance Based On Item Group", "link_type": "Report", "onboard": 0, @@ -509,20 +563,26 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Partner Target Variance Based On Item Group", + "link_count": 0, "link_to": "Sales Partner Target Variance based on Item Group", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:35.971277", + "modified": "2021-08-05 12:16:01.990702", "modified_by": "Administrator", "module": "Selling", "name": "Selling", "onboarding": "Selling", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 23, "shortcuts": [ { "color": "Grey", @@ -559,5 +619,6 @@ "type": "Dashboard" } ], - "shortcuts_label": "Quick Access" + "shortcuts_label": "Quick Access", + "title": "Selling" } \ No newline at end of file diff --git a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json index 6ca3d637da40..ef4b050ceb24 100644 --- a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json +++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json @@ -1,27 +1,35 @@ { - "category": "Modules", + "category": "", "charts": [], + "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Projects Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Accounts Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"HR Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Selling Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Buying Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Support Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Shopping Cart Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Portal Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Manufacturing Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Education Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Hotel Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Healthcare Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Domain Settings\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Products Settings\", \"col\": 4}}]", "creation": "2020-03-12 14:47:51.166455", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", - "extends": "Settings", - "extends_another_page": 1, + "extends": "", + "extends_another_page": 0, + "for_user": "", "hide_custom": 0, - "icon": "settings", + "icon": "setting", "idx": 0, "is_default": 0, - "is_standard": 1, + "is_standard": 0, "label": "ERPNext Settings", "links": [], - "modified": "2021-06-12 01:58:11.399566", + "modified": "2021-08-05 12:15:59.052327", "modified_by": "Administrator", "module": "Setup", "name": "ERPNext Settings", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 12, "shortcuts": [ { "icon": "project", @@ -118,5 +126,6 @@ "link_to": "Products Settings", "type": "DocType" } - ] -} + ], + "title": "ERPNext Settings" +} \ No newline at end of file diff --git a/erpnext/setup/workspace/home/home.json b/erpnext/setup/workspace/home/home.json index 1576d5a3993e..cc9569f64214 100644 --- a/erpnext/setup/workspace/home/home.json +++ b/erpnext/setup/workspace/home/home.json @@ -1,23 +1,27 @@ { - "category": "Modules", + "category": "", "charts": [], + "content": "[{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"level\":4,\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Supplier\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":4}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Leaderboard\",\"col\":4}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"level\":4,\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Stock\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Human Resources\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"CRM\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Data Import and Settings\",\"col\":4}}]", "creation": "2020-01-23 13:46:38.833076", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "getting-started", "idx": 0, "is_default": 0, - "is_standard": 1, + "is_standard": 0, "label": "Home", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Accounting", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -26,6 +30,7 @@ "hidden": 0, "is_query_report": 0, "label": "Chart of Accounts", + "link_count": 0, "link_to": "Account", "link_type": "DocType", "onboard": 1, @@ -36,6 +41,7 @@ "hidden": 0, "is_query_report": 0, "label": "Company", + "link_count": 0, "link_to": "Company", "link_type": "DocType", "onboard": 1, @@ -46,6 +52,7 @@ "hidden": 0, "is_query_report": 0, "label": "Customer", + "link_count": 0, "link_to": "Customer", "link_type": "DocType", "onboard": 1, @@ -56,6 +63,7 @@ "hidden": 0, "is_query_report": 0, "label": "Supplier", + "link_count": 0, "link_to": "Supplier", "link_type": "DocType", "onboard": 1, @@ -65,6 +73,7 @@ "hidden": 0, "is_query_report": 0, "label": "Stock", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -73,6 +82,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item", + "link_count": 0, "link_to": "Item", "link_type": "DocType", "onboard": 1, @@ -83,6 +93,7 @@ "hidden": 0, "is_query_report": 0, "label": "Warehouse", + "link_count": 0, "link_to": "Warehouse", "link_type": "DocType", "onboard": 1, @@ -93,6 +104,7 @@ "hidden": 0, "is_query_report": 0, "label": "Brand", + "link_count": 0, "link_to": "Brand", "link_type": "DocType", "onboard": 1, @@ -103,6 +115,7 @@ "hidden": 0, "is_query_report": 0, "label": "Unit of Measure (UOM)", + "link_count": 0, "link_to": "UOM", "link_type": "DocType", "onboard": 1, @@ -113,6 +126,7 @@ "hidden": 0, "is_query_report": 0, "label": "Stock Reconciliation", + "link_count": 0, "link_to": "Stock Reconciliation", "link_type": "DocType", "onboard": 1, @@ -122,6 +136,7 @@ "hidden": 0, "is_query_report": 0, "label": "Human Resources", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -130,6 +145,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee", + "link_count": 0, "link_to": "Employee", "link_type": "DocType", "onboard": 1, @@ -140,6 +156,7 @@ "hidden": 0, "is_query_report": 0, "label": "Employee Attendance Tool", + "link_count": 0, "link_to": "Employee Attendance Tool", "link_type": "DocType", "onboard": 1, @@ -150,6 +167,7 @@ "hidden": 0, "is_query_report": 0, "label": "Salary Structure", + "link_count": 0, "link_to": "Salary Structure", "link_type": "DocType", "onboard": 1, @@ -159,6 +177,7 @@ "hidden": 0, "is_query_report": 0, "label": "CRM", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -167,6 +186,7 @@ "hidden": 0, "is_query_report": 0, "label": "Lead", + "link_count": 0, "link_to": "Lead", "link_type": "DocType", "onboard": 1, @@ -177,6 +197,7 @@ "hidden": 0, "is_query_report": 0, "label": "Customer Group", + "link_count": 0, "link_to": "Customer Group", "link_type": "DocType", "onboard": 1, @@ -187,6 +208,7 @@ "hidden": 0, "is_query_report": 0, "label": "Territory", + "link_count": 0, "link_to": "Territory", "link_type": "DocType", "onboard": 1, @@ -196,6 +218,7 @@ "hidden": 0, "is_query_report": 0, "label": "Data Import and Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -204,6 +227,7 @@ "hidden": 0, "is_query_report": 0, "label": "Import Data", + "link_count": 0, "link_to": "Data Import", "link_type": "DocType", "onboard": 1, @@ -214,6 +238,7 @@ "hidden": 0, "is_query_report": 0, "label": "Opening Invoice Creation Tool", + "link_count": 0, "link_to": "Opening Invoice Creation Tool", "link_type": "DocType", "onboard": 1, @@ -224,6 +249,7 @@ "hidden": 0, "is_query_report": 0, "label": "Chart of Accounts Importer", + "link_count": 0, "link_to": "Chart of Accounts Importer", "link_type": "DocType", "onboard": 1, @@ -234,6 +260,7 @@ "hidden": 0, "is_query_report": 0, "label": "Letter Head", + "link_count": 0, "link_to": "Letter Head", "link_type": "DocType", "onboard": 1, @@ -244,19 +271,26 @@ "hidden": 0, "is_query_report": 0, "label": "Email Account", + "link_count": 0, "link_to": "Email Account", "link_type": "DocType", "onboard": 1, "type": "Link" } ], - "modified": "2021-04-19 15:48:44.089927", + "modified": "2021-08-10 15:33:20.704740", "modified_by": "Administrator", "module": "Setup", "name": "Home", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, - "pin_to_top": 1, + "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 1, "shortcuts": [ { "label": "Item", @@ -283,5 +317,6 @@ "link_to": "leaderboard", "type": "Page" } - ] + ], + "title": "Home" } \ No newline at end of file diff --git a/erpnext/stock/workspace/stock/stock.json b/erpnext/stock/workspace/stock/stock.json index 529ce8eb61e0..26d10ce70387 100644 --- a/erpnext/stock/workspace/stock/stock.json +++ b/erpnext/stock/workspace/stock/stock.json @@ -1,28 +1,32 @@ { "cards_label": "Masters & Reports", - "category": "Modules", + "category": "", "charts": [ { "chart_name": "Warehouse wise Stock Value" } ], + "content": "[{\"type\": \"onboarding\", \"data\": {\"onboarding_name\":\"Stock\", \"col\": 12}}, {\"type\": \"chart\", \"data\": {\"chart_name\": null, \"col\": 12}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Quick Access\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Item\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Material Request\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Entry\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Purchase Receipt\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Delivery Note\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Ledger\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Stock Balance\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Dashboard\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Masters & Reports\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Items and Pricing\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Stock Transactions\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Stock Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Serial No and Batch\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Tools\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Key Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Other Reports\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Incorrect Data Report\", \"col\": 4}}]", "creation": "2020-03-02 15:43:10.096528", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "stock", "idx": 0, "is_default": 0, - "is_standard": 1, + "is_standard": 0, "label": "Stock", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Items and Pricing", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -31,6 +35,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item", + "link_count": 0, "link_to": "Item", "link_type": "DocType", "onboard": 1, @@ -41,6 +46,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Group", + "link_count": 0, "link_to": "Item Group", "link_type": "DocType", "onboard": 1, @@ -51,6 +57,7 @@ "hidden": 0, "is_query_report": 0, "label": "Product Bundle", + "link_count": 0, "link_to": "Product Bundle", "link_type": "DocType", "onboard": 1, @@ -61,6 +68,7 @@ "hidden": 0, "is_query_report": 0, "label": "Price List", + "link_count": 0, "link_to": "Price List", "link_type": "DocType", "onboard": 0, @@ -71,6 +79,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Price", + "link_count": 0, "link_to": "Item Price", "link_type": "DocType", "onboard": 0, @@ -81,6 +90,7 @@ "hidden": 0, "is_query_report": 0, "label": "Shipping Rule", + "link_count": 0, "link_to": "Shipping Rule", "link_type": "DocType", "onboard": 0, @@ -91,6 +101,7 @@ "hidden": 0, "is_query_report": 0, "label": "Pricing Rule", + "link_count": 0, "link_to": "Pricing Rule", "link_type": "DocType", "onboard": 0, @@ -101,6 +112,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Alternative", + "link_count": 0, "link_to": "Item Alternative", "link_type": "DocType", "onboard": 0, @@ -111,6 +123,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Manufacturer", + "link_count": 0, "link_to": "Item Manufacturer", "link_type": "DocType", "onboard": 0, @@ -121,6 +134,7 @@ "hidden": 0, "is_query_report": 0, "label": "Customs Tariff Number", + "link_count": 0, "link_to": "Customs Tariff Number", "link_type": "DocType", "onboard": 0, @@ -130,6 +144,7 @@ "hidden": 0, "is_query_report": 0, "label": "Stock Transactions", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -138,6 +153,7 @@ "hidden": 0, "is_query_report": 0, "label": "Material Request", + "link_count": 0, "link_to": "Material Request", "link_type": "DocType", "onboard": 1, @@ -148,6 +164,7 @@ "hidden": 0, "is_query_report": 0, "label": "Stock Entry", + "link_count": 0, "link_to": "Stock Entry", "link_type": "DocType", "onboard": 1, @@ -158,6 +175,7 @@ "hidden": 0, "is_query_report": 0, "label": "Delivery Note", + "link_count": 0, "link_to": "Delivery Note", "link_type": "DocType", "onboard": 1, @@ -168,6 +186,7 @@ "hidden": 0, "is_query_report": 0, "label": "Purchase Receipt", + "link_count": 0, "link_to": "Purchase Receipt", "link_type": "DocType", "onboard": 1, @@ -178,6 +197,7 @@ "hidden": 0, "is_query_report": 0, "label": "Pick List", + "link_count": 0, "link_to": "Pick List", "link_type": "DocType", "onboard": 1, @@ -188,6 +208,7 @@ "hidden": 0, "is_query_report": 0, "label": "Delivery Trip", + "link_count": 0, "link_to": "Delivery Trip", "link_type": "DocType", "onboard": 0, @@ -197,6 +218,7 @@ "hidden": 0, "is_query_report": 0, "label": "Stock Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -205,6 +227,7 @@ "hidden": 0, "is_query_report": 1, "label": "Stock Ledger", + "link_count": 0, "link_to": "Stock Ledger", "link_type": "Report", "onboard": 1, @@ -215,6 +238,7 @@ "hidden": 0, "is_query_report": 1, "label": "Stock Balance", + "link_count": 0, "link_to": "Stock Balance", "link_type": "Report", "onboard": 1, @@ -225,6 +249,7 @@ "hidden": 0, "is_query_report": 1, "label": "Stock Projected Qty", + "link_count": 0, "link_to": "Stock Projected Qty", "link_type": "Report", "onboard": 1, @@ -235,6 +260,7 @@ "hidden": 0, "is_query_report": 0, "label": "Stock Summary", + "link_count": 0, "link_to": "stock-balance", "link_type": "Page", "onboard": 0, @@ -245,6 +271,7 @@ "hidden": 0, "is_query_report": 1, "label": "Stock Ageing", + "link_count": 0, "link_to": "Stock Ageing", "link_type": "Report", "onboard": 0, @@ -255,6 +282,7 @@ "hidden": 0, "is_query_report": 1, "label": "Item Price Stock", + "link_count": 0, "link_to": "Item Price Stock", "link_type": "Report", "onboard": 0, @@ -264,6 +292,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -272,6 +301,7 @@ "hidden": 0, "is_query_report": 0, "label": "Stock Settings", + "link_count": 0, "link_to": "Stock Settings", "link_type": "DocType", "onboard": 1, @@ -282,6 +312,7 @@ "hidden": 0, "is_query_report": 0, "label": "Warehouse", + "link_count": 0, "link_to": "Warehouse", "link_type": "DocType", "onboard": 1, @@ -292,6 +323,7 @@ "hidden": 0, "is_query_report": 0, "label": "Unit of Measure (UOM)", + "link_count": 0, "link_to": "UOM", "link_type": "DocType", "onboard": 1, @@ -302,6 +334,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Variant Settings", + "link_count": 0, "link_to": "Item Variant Settings", "link_type": "DocType", "onboard": 1, @@ -312,6 +345,7 @@ "hidden": 0, "is_query_report": 0, "label": "Brand", + "link_count": 0, "link_to": "Brand", "link_type": "DocType", "onboard": 1, @@ -322,6 +356,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item Attribute", + "link_count": 0, "link_to": "Item Attribute", "link_type": "DocType", "onboard": 0, @@ -332,6 +367,7 @@ "hidden": 0, "is_query_report": 0, "label": "UOM Conversion Factor", + "link_count": 0, "link_to": "UOM Conversion Factor", "link_type": "DocType", "onboard": 0, @@ -341,6 +377,7 @@ "hidden": 0, "is_query_report": 0, "label": "Serial No and Batch", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -349,6 +386,7 @@ "hidden": 0, "is_query_report": 0, "label": "Serial No", + "link_count": 0, "link_to": "Serial No", "link_type": "DocType", "onboard": 1, @@ -359,6 +397,7 @@ "hidden": 0, "is_query_report": 0, "label": "Batch", + "link_count": 0, "link_to": "Batch", "link_type": "DocType", "onboard": 1, @@ -369,6 +408,7 @@ "hidden": 0, "is_query_report": 0, "label": "Installation Note", + "link_count": 0, "link_to": "Installation Note", "link_type": "DocType", "onboard": 0, @@ -379,6 +419,7 @@ "hidden": 0, "is_query_report": 0, "label": "Serial No Service Contract Expiry", + "link_count": 0, "link_to": "Serial No Service Contract Expiry", "link_type": "Report", "onboard": 0, @@ -389,6 +430,7 @@ "hidden": 0, "is_query_report": 0, "label": "Serial No Status", + "link_count": 0, "link_to": "Serial No Status", "link_type": "Report", "onboard": 0, @@ -399,6 +441,7 @@ "hidden": 0, "is_query_report": 0, "label": "Serial No Warranty Expiry", + "link_count": 0, "link_to": "Serial No Warranty Expiry", "link_type": "Report", "onboard": 0, @@ -408,6 +451,7 @@ "hidden": 0, "is_query_report": 0, "label": "Tools", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -416,6 +460,7 @@ "hidden": 0, "is_query_report": 0, "label": "Stock Reconciliation", + "link_count": 0, "link_to": "Stock Reconciliation", "link_type": "DocType", "onboard": 1, @@ -426,6 +471,7 @@ "hidden": 0, "is_query_report": 0, "label": "Landed Cost Voucher", + "link_count": 0, "link_to": "Landed Cost Voucher", "link_type": "DocType", "onboard": 1, @@ -436,6 +482,7 @@ "hidden": 0, "is_query_report": 0, "label": "Packing Slip", + "link_count": 0, "link_to": "Packing Slip", "link_type": "DocType", "onboard": 1, @@ -446,6 +493,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quality Inspection", + "link_count": 0, "link_to": "Quality Inspection", "link_type": "DocType", "onboard": 0, @@ -456,6 +504,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quality Inspection Template", + "link_count": 0, "link_to": "Quality Inspection Template", "link_type": "DocType", "onboard": 0, @@ -466,6 +515,7 @@ "hidden": 0, "is_query_report": 0, "label": "Quick Stock Balance", + "link_count": 0, "link_to": "Quick Stock Balance", "link_type": "DocType", "onboard": 0, @@ -475,6 +525,7 @@ "hidden": 0, "is_query_report": 0, "label": "Key Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -483,6 +534,7 @@ "hidden": 0, "is_query_report": 0, "label": "Item-wise Price List Rate", + "link_count": 0, "link_to": "Item-wise Price List Rate", "link_type": "Report", "onboard": 1, @@ -493,6 +545,7 @@ "hidden": 0, "is_query_report": 1, "label": "Stock Analytics", + "link_count": 0, "link_to": "Stock Analytics", "link_type": "Report", "onboard": 1, @@ -503,6 +556,7 @@ "hidden": 0, "is_query_report": 1, "label": "Stock Qty vs Serial No Count", + "link_count": 0, "link_to": "Stock Qty vs Serial No Count", "link_type": "Report", "onboard": 1, @@ -513,6 +567,7 @@ "hidden": 0, "is_query_report": 1, "label": "Delivery Note Trends", + "link_count": 0, "link_to": "Delivery Note Trends", "link_type": "Report", "onboard": 0, @@ -523,6 +578,7 @@ "hidden": 0, "is_query_report": 1, "label": "Purchase Receipt Trends", + "link_count": 0, "link_to": "Purchase Receipt Trends", "link_type": "Report", "onboard": 0, @@ -533,6 +589,7 @@ "hidden": 0, "is_query_report": 1, "label": "Sales Order Analysis", + "link_count": 0, "link_to": "Sales Order Analysis", "link_type": "Report", "onboard": 0, @@ -543,6 +600,7 @@ "hidden": 0, "is_query_report": 1, "label": "Purchase Order Analysis", + "link_count": 0, "link_to": "Purchase Order Analysis", "link_type": "Report", "onboard": 0, @@ -553,6 +611,7 @@ "hidden": 0, "is_query_report": 1, "label": "Item Shortage Report", + "link_count": 0, "link_to": "Item Shortage Report", "link_type": "Report", "onboard": 0, @@ -563,6 +622,7 @@ "hidden": 0, "is_query_report": 1, "label": "Batch-Wise Balance History", + "link_count": 0, "link_to": "Batch-Wise Balance History", "link_type": "Report", "onboard": 0, @@ -572,6 +632,7 @@ "hidden": 0, "is_query_report": 0, "label": "Other Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -580,6 +641,7 @@ "hidden": 0, "is_query_report": 1, "label": "Requested Items To Be Transferred", + "link_count": 0, "link_to": "Requested Items To Be Transferred", "link_type": "Report", "onboard": 0, @@ -590,6 +652,7 @@ "hidden": 0, "is_query_report": 1, "label": "Batch Item Expiry Status", + "link_count": 0, "link_to": "Batch Item Expiry Status", "link_type": "Report", "onboard": 0, @@ -600,6 +663,7 @@ "hidden": 0, "is_query_report": 1, "label": "Item Prices", + "link_count": 0, "link_to": "Item Prices", "link_type": "Report", "onboard": 0, @@ -610,6 +674,7 @@ "hidden": 0, "is_query_report": 1, "label": "Itemwise Recommended Reorder Level", + "link_count": 0, "link_to": "Itemwise Recommended Reorder Level", "link_type": "Report", "onboard": 0, @@ -620,6 +685,7 @@ "hidden": 0, "is_query_report": 1, "label": "Item Variant Details", + "link_count": 0, "link_to": "Item Variant Details", "link_type": "Report", "onboard": 0, @@ -630,6 +696,7 @@ "hidden": 0, "is_query_report": 1, "label": "Subcontracted Raw Materials To Be Transferred", + "link_count": 0, "link_to": "Subcontracted Raw Materials To Be Transferred", "link_type": "Report", "onboard": 0, @@ -640,6 +707,7 @@ "hidden": 0, "is_query_report": 1, "label": "Subcontracted Item To Be Received", + "link_count": 0, "link_to": "Subcontracted Item To Be Received", "link_type": "Report", "onboard": 0, @@ -650,6 +718,7 @@ "hidden": 0, "is_query_report": 1, "label": "Stock and Account Value Comparison", + "link_count": 0, "link_to": "Stock and Account Value Comparison", "link_type": "Report", "onboard": 0, @@ -659,6 +728,7 @@ "hidden": 0, "is_query_report": 0, "label": "Incorrect Data Report", + "link_count": 0, "link_type": "DocType", "onboard": 0, "type": "Card Break" @@ -667,6 +737,7 @@ "hidden": 0, "is_query_report": 0, "label": "Incorrect Serial No Qty and Valuation", + "link_count": 0, "link_to": "Incorrect Serial No Valuation", "link_type": "Report", "onboard": 0, @@ -676,6 +747,7 @@ "hidden": 0, "is_query_report": 0, "label": "Incorrect Balance Qty After Transaction", + "link_count": 0, "link_to": "Incorrect Balance Qty After Transaction", "link_type": "Report", "onboard": 0, @@ -685,20 +757,26 @@ "hidden": 0, "is_query_report": 0, "label": "Stock and Account Value Comparison", + "link_count": 0, "link_to": "Stock and Account Value Comparison", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2021-05-13 13:10:24.914983", + "modified": "2021-08-05 12:16:02.361509", "modified_by": "Administrator", "module": "Stock", "name": "Stock", "onboarding": "Stock", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 24, "shortcuts": [ { "color": "Green", @@ -753,5 +831,6 @@ "type": "Dashboard" } ], - "shortcuts_label": "Quick Access" + "shortcuts_label": "Quick Access", + "title": "Stock" } \ No newline at end of file diff --git a/erpnext/support/workspace/support/support.json b/erpnext/support/workspace/support/support.json index 01a8676f05d4..4c5829d7a039 100644 --- a/erpnext/support/workspace/support/support.json +++ b/erpnext/support/workspace/support/support.json @@ -1,22 +1,27 @@ { - "category": "Modules", + "category": "", "charts": [], + "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Your Shortcuts\", \"level\": 4, \"col\": 12}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Issue\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Maintenance Visit\", \"col\": 4}}, {\"type\": \"shortcut\", \"data\": {\"shortcut_name\": \"Service Level Agreement\", \"col\": 4}}, {\"type\": \"spacer\", \"data\": {\"col\": 12}}, {\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Issues\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Maintenance\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Service Level Agreement\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Warranty\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Settings\", \"col\": 4}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Reports\", \"col\": 4}}]", "creation": "2020-03-02 15:48:23.224699", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "icon": "support", "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Support", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Issues", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -25,6 +30,7 @@ "hidden": 0, "is_query_report": 0, "label": "Issue", + "link_count": 0, "link_to": "Issue", "link_type": "DocType", "onboard": 1, @@ -35,6 +41,7 @@ "hidden": 0, "is_query_report": 0, "label": "Issue Type", + "link_count": 0, "link_to": "Issue Type", "link_type": "DocType", "onboard": 0, @@ -45,6 +52,7 @@ "hidden": 0, "is_query_report": 0, "label": "Issue Priority", + "link_count": 0, "link_to": "Issue Priority", "link_type": "DocType", "onboard": 0, @@ -54,6 +62,7 @@ "hidden": 0, "is_query_report": 0, "label": "Maintenance", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -62,6 +71,7 @@ "hidden": 0, "is_query_report": 0, "label": "Maintenance Schedule", + "link_count": 0, "link_to": "Maintenance Schedule", "link_type": "DocType", "onboard": 0, @@ -72,6 +82,7 @@ "hidden": 0, "is_query_report": 0, "label": "Maintenance Visit", + "link_count": 0, "link_to": "Maintenance Visit", "link_type": "DocType", "onboard": 0, @@ -81,6 +92,7 @@ "hidden": 0, "is_query_report": 0, "label": "Service Level Agreement", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -89,6 +101,7 @@ "hidden": 0, "is_query_report": 0, "label": "Service Level Agreement", + "link_count": 0, "link_to": "Service Level Agreement", "link_type": "DocType", "onboard": 0, @@ -98,6 +111,7 @@ "hidden": 0, "is_query_report": 0, "label": "Warranty", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -106,6 +120,7 @@ "hidden": 0, "is_query_report": 0, "label": "Warranty Claim", + "link_count": 0, "link_to": "Warranty Claim", "link_type": "DocType", "onboard": 0, @@ -116,6 +131,7 @@ "hidden": 0, "is_query_report": 0, "label": "Serial No", + "link_count": 0, "link_to": "Serial No", "link_type": "DocType", "onboard": 0, @@ -125,6 +141,7 @@ "hidden": 0, "is_query_report": 0, "label": "Settings", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -133,6 +150,7 @@ "hidden": 0, "is_query_report": 0, "label": "Support Settings", + "link_count": 0, "link_to": "Support Settings", "link_type": "DocType", "onboard": 0, @@ -142,6 +160,7 @@ "hidden": 0, "is_query_report": 0, "label": "Reports", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -150,19 +169,26 @@ "hidden": 0, "is_query_report": 1, "label": "First Response Time for Issues", + "link_count": 0, "link_to": "First Response Time for Issues", "link_type": "Report", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:37.073482", + "modified": "2021-08-05 12:16:02.699923", "modified_by": "Administrator", "module": "Support", "name": "Support", + "onboarding": "", "owner": "Administrator", + "parent_page": "", "pin_to_bottom": 0, "pin_to_top": 0, + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 25, "shortcuts": [ { "color": "Yellow", @@ -182,5 +208,6 @@ "link_to": "Service Level Agreement", "type": "DocType" } - ] + ], + "title": "Support" } \ No newline at end of file diff --git a/erpnext/utilities/workspace/utilities/utilities.json b/erpnext/utilities/workspace/utilities/utilities.json index 2f9250ee45c8..4ad4afb8f418 100644 --- a/erpnext/utilities/workspace/utilities/utilities.json +++ b/erpnext/utilities/workspace/utilities/utilities.json @@ -1,21 +1,26 @@ { - "category": "Modules", + "category": "", "charts": [], + "content": "[{\"type\": \"header\", \"data\": {\"text\": \"Reports & Masters\", \"level\": 4, \"col\": 12}}, {\"type\": \"card\", \"data\": {\"card_name\": \"Video\", \"col\": 4}}]", "creation": "2020-09-10 12:21:22.335307", "developer_mode_only": 0, "disable_user_customization": 0, "docstatus": 0, "doctype": "Workspace", + "extends": "", "extends_another_page": 0, + "for_user": "", "hide_custom": 0, "idx": 0, - "is_standard": 1, + "is_default": 0, + "is_standard": 0, "label": "Utilities", "links": [ { "hidden": 0, "is_query_report": 0, "label": "Video", + "link_count": 0, "onboard": 0, "type": "Card Break" }, @@ -24,6 +29,7 @@ "hidden": 0, "is_query_report": 0, "label": "Video", + "link_count": 0, "link_to": "Video", "link_type": "DocType", "onboard": 0, @@ -34,18 +40,26 @@ "hidden": 0, "is_query_report": 0, "label": "Video Settings", + "link_count": 0, "link_to": "Video Settings", "link_type": "DocType", "onboard": 0, "type": "Link" } ], - "modified": "2020-12-01 13:38:36.711884", + "modified": "2021-08-05 12:16:03.350804", "modified_by": "Administrator", "module": "Utilities", "name": "Utilities", + "onboarding": "", "owner": "user@erpnext.com", - "pin_to_bottom": 1, + "parent_page": "", + "pin_to_bottom": 0, "pin_to_top": 0, - "shortcuts": [] + "public": 1, + "restrict_to_domain": "", + "roles": [], + "sequence_id": 30, + "shortcuts": [], + "title": "Utilities" } \ No newline at end of file From 8a6b82b19629aa0d3d6df767cef8b548623bc520 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 13 Aug 2021 12:59:27 +0530 Subject: [PATCH 617/691] ci: ignore js files in unittests (#26934) * ci: ignore js files in unittests - Avoid running python unittests on PRs that ONLY change JS files. * ci: ignore md files in test workflows --- .github/workflows/patch.yml | 8 +++++++- .github/workflows/server-tests.yml | 6 ++++++ .github/workflows/ui-tests.yml | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/patch.yml b/.github/workflows/patch.yml index dc72987a41a6..72d4028ce6e1 100644 --- a/.github/workflows/patch.yml +++ b/.github/workflows/patch.yml @@ -1,6 +1,12 @@ name: Patch -on: [pull_request, workflow_dispatch] +on: + pull_request: + paths-ignore: + - '**.js' + - '**.md' + workflow_dispatch: + jobs: test: diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml index 606002e3cdf4..3a1ecd399c57 100644 --- a/.github/workflows/server-tests.yml +++ b/.github/workflows/server-tests.yml @@ -2,9 +2,15 @@ name: Server on: pull_request: + paths-ignore: + - '**.js' + - '**.md' workflow_dispatch: push: branches: [ develop ] + paths-ignore: + - '**.js' + - '**.md' jobs: test: diff --git a/.github/workflows/ui-tests.yml b/.github/workflows/ui-tests.yml index 9e29b6f1d2a9..3959268c7674 100644 --- a/.github/workflows/ui-tests.yml +++ b/.github/workflows/ui-tests.yml @@ -2,6 +2,8 @@ name: UI on: pull_request: + paths-ignore: + - '**.md' workflow_dispatch: jobs: From fe2a34f17197a2877356bcdf5d0bb6c46312ed33 Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 13 Aug 2021 15:37:45 +0530 Subject: [PATCH 618/691] fix: Copy previous balance dict object instead of assigning (#26942) - Due to plain assignment, dict mutation gave wrong monthly values --- 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 d44685060c73..fde934b13396 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 2e6899fbe439a6d194eedb3ef46adbdd9d2e4cfb Mon Sep 17 00:00:00 2001 From: Marica Date: Fri, 13 Aug 2021 15:37:45 +0530 Subject: [PATCH 619/691] 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 d44685060c73..fde934b13396 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 5999760a65db9b8d376126e4bf70feff07bc8b01 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 13 Aug 2021 17:11:53 +0530 Subject: [PATCH 620/691] 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 dbc42de583ec..2751b5509cc6 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 = class SalesInvoiceController extends e items_add(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() { super.set_dynamic_labels(); From f977c65e804054a7bbd8b6b3670859821ccd9d1d Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 13 Aug 2021 17:12:45 +0530 Subject: [PATCH 621/691] fix: Make enable_discount_accounting a class property --- .../doctype/purchase_invoice/purchase_invoice.py | 14 +++++++++----- .../doctype/sales_invoice/sales_invoice.py | 13 ++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a95f971e00ae..574a353cdf2c 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -523,8 +523,6 @@ def make_item_gl_entries(self, gl_entries): 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) @@ -615,7 +613,7 @@ def make_item_gl_entries(self, gl_entries): if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) if not item.is_fixed_asset: - dummy, amount = self.get_amount_and_base_amount(item, enable_discount_accounting) + dummy, amount = self.get_amount_and_base_amount(item, self.enable_discount_accounting) else: amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount")) @@ -857,10 +855,9 @@ def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value 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"): - amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting) + amount, base_amount = self.get_tax_amounts(tax, self.enable_discount_accounting) if tax.category in ("Total", "Valuation and Total") and flt(base_amount): account_currency = get_account_currency(tax.account_head) @@ -925,6 +922,13 @@ def make_tax_gl_entries(self, gl_entries): "remarks": self.remarks or "Accounting Entry for Stock" }, item=tax)) + @property + def enable_discount_accounting(self): + if not hasattr(self, "_enable_discount_accounting"): + self._enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + + return self._enable_discount_accounting + 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 1e1fe9001a08..0db426ad5ece 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -888,10 +888,8 @@ def make_customer_gl_entry(self, gl_entries): ) 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) + amount, base_amount = self.get_tax_amounts(tax, self.enable_discount_accounting) if flt(tax.base_tax_amount_after_discount_amount): account_currency = get_account_currency(tax.account_head) @@ -922,7 +920,6 @@ def make_internal_transfer_gl_entries(self, gl_entries): 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")): @@ -957,7 +954,7 @@ def make_item_gl_entries(self, gl_entries): 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, enable_discount_accounting) + amount, base_amount = self.get_amount_and_base_amount(item, self.enable_discount_accounting) account_currency = get_account_currency(income_account) gl_entries.append( @@ -1060,6 +1057,12 @@ def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_ if orginal_schedule_date == posting_date_of_original_invoice: return True return False + @property + def enable_discount_accounting(self): + if not hasattr(self, "_enable_discount_accounting"): + self._enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting')) + + return self._enable_discount_accounting def set_asset_status(self, asset): if self.is_return: From f9356ee64211e61bcb66af98b86b57e52240b085 Mon Sep 17 00:00:00 2001 From: GangaManoj Date: Fri, 13 Aug 2021 17:40:51 +0530 Subject: [PATCH 622/691] fix: Sider issues --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 4 ++-- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 574a353cdf2c..c3cb159038b5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -868,8 +868,8 @@ def make_tax_gl_entries(self, gl_entries): "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) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0db426ad5ece..3b8cba2a1e8a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1057,6 +1057,7 @@ def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_ if orginal_schedule_date == posting_date_of_original_invoice: return True return False + @property def enable_discount_accounting(self): if not hasattr(self, "_enable_discount_accounting"): From b5926a33b35878d0ee110ce32d5dd3cd4a737700 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 14 Aug 2021 10:48:33 +0530 Subject: [PATCH 623/691] fix: unknown attribute "string_type" (#26947) (#26950) (cherry picked from commit 6aed9e26acaf266f7260adab16656d554c6bb022) Co-authored-by: Ankush Menat --- erpnext/hr/doctype/attendance/attendance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index f79f0fe4180f..c1a7c8f88a5c 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -135,7 +135,7 @@ def mark_attendance(employee, attendance_date, status, shift=None, leave_type=No def mark_bulk_attendance(data): import json from pprint import pprint - if isinstance(data, frappe.string_types): + if isinstance(data, str): data = json.loads(data) data = frappe._dict(data) company = frappe.get_value('Employee', data.employee, 'company') 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 624/691] 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 8a1ec4992b0d..a66d6474e0d7 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 625/691] 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 89fb8d579253..fd4365ad8ff3 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 626/691] fix: expand all nodes not working when there are only 2 levels - added dom freeze while expanding all nodes and exporting --- .../organizational_chart.py | 19 ++++++++++--------- .../hierarchy_chart_desktop.js | 16 ++++++++++++---- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 1e03e3d06add..298319821724 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) - - while connections: - for entry in connections: - connections = frappe.get_list('Employee', filters=[ - ['reports_to', '=', entry.name] - ]) - num_connections += len(connections) + num_connections += len(nodes_to_expand) + + 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 fd4365ad8ff3..da050abc6e4c 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 627/691] 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 | 19 ++++++++++--------- .../hierarchy_chart_desktop.js | 17 +++++++++++++---- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/erpnext/hr/page/organizational_chart/organizational_chart.py b/erpnext/hr/page/organizational_chart/organizational_chart.py index 1e03e3d06add..298319821724 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) - - while connections: - for entry in connections: - connections = frappe.get_list('Employee', filters=[ - ['reports_to', '=', entry.name] - ]) - num_connections += len(connections) + num_connections += len(nodes_to_expand) + + 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 89fb8d579253..da050abc6e4c 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 3f07bb70215a8834d887f8f3fe2f48db99d59e65 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Aug 2021 13:18:39 +0530 Subject: [PATCH 628/691] 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 86356e302691..0fc50c5954a5 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -296,7 +296,7 @@ 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_export_type_for_gst #2021-08-16 erpnext.patches.v13_0.update_tds_check_field #3 erpnext.patches.v13_0.add_custom_field_for_south_africa #2 erpnext.patches.v13_0.shopify_deprecation_warning 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 478a2a6c8068..3e20212af6d3 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 b4f146ce57e7..2d6b9133900a 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 b58b1127f3776735484a41d4884387397e78533d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Aug 2021 13:18:39 +0530 Subject: [PATCH 629/691] 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 b891719b02d8..fec727d289c6 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 478a2a6c8068..3e20212af6d3 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 e9372f9b8fc2..37c714d21621 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 a9a24051c94dc07dae6b12b9099c1a4dc9c3e2cf Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Mon, 16 Aug 2021 14:48:53 +0530 Subject: [PATCH 630/691] feat: enable track changes for leave type (#26917) Co-authored-by: Jannat Patel <31363128+pateljannat@users.noreply.github.com> --- erpnext/hr/doctype/leave_type/leave_type.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index fc577ef1d3d1..8f2ae6eb15d8 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -214,7 +214,7 @@ "icon": "fa fa-flag", "idx": 1, "links": [], - "modified": "2021-03-02 11:22:33.776320", + "modified": "2021-08-12 16:10:36.464690", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", @@ -248,5 +248,6 @@ } ], "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file From 8410e4854ff90f68c2e5d41072aa0665d7482edb Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 16 Aug 2021 17:14:40 +0530 Subject: [PATCH 631/691] 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 f1b231b69012..9f0eee8aa5c4 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 632/691] 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 | 59 ++++++++++++------ .../doctype/training_event/training_event.py | 17 +++++- .../test_training_feedback.py | 61 ++++++++++++++++++- .../training_feedback/training_feedback.py | 40 +++++++++--- 4 files changed, 143 insertions(+), 34 deletions(-) diff --git a/erpnext/hr/doctype/training_event/test_training_event.py b/erpnext/hr/doctype/training_event/test_training_event.py index 313f90eba859..9b32136bfb6e 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 @@ 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") - - 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() + 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 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 5064f0330810..e2c30cb31452 100644 --- a/erpnext/hr/doctype/training_event/training_event.py +++ b/erpnext/hr/doctype/training_event/training_event.py @@ -14,10 +14,25 @@ def validate(self): 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 34559982a24c..c30a3ad34cdd 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 @@ import frappe import unittest +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): + 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}] -# test_records = frappe.get_test_records('Training Feedback') + def test_employee_validations_for_feedback(self): + training_event = create_training_event(self.attendees) + training_event.submit() -class TestTrainingFeedback(unittest.TestCase): - pass + 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 1a334507917b..0d32de793c44 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 - - if event_status: - frappe.db.set_value("Training Event", self.training_event, "event_status", event_status) + 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") + From 2a43fe1a22f993ed4eca779ce268064e56fe5246 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 16 Aug 2021 18:03:21 +0530 Subject: [PATCH 633/691] 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 9cc4663c394e..b4a4ba1bbddb 100644 --- a/.github/helper/documentation.py +++ b/.github/helper/documentation.py @@ -32,11 +32,15 @@ def docs_link_exists(body): 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 634/691] 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 f1b231b69012..9f0eee8aa5c4 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 635/691] 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 fb46bbb43312..5dac15f218b0 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 636/691] 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 000000000000..e52766673ce7 --- /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 637/691] 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 c90e01cfbd68..17d650568a4c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ 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 638/691] 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 5ff68cd682ce..ccae129ea0b5 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 639/691] 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 46904f7c5718..831b27085838 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -529,7 +529,7 @@ def set_unallocated_amount(self): 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 640/691] 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 b1273b704ff9..b67c7fceac9a 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 641/691] 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 943f7aaeb12b..b1f89b08d792 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 @@ def _update_percent_field(self, args, update_modified=True): 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 642/691] 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 eb9fa12b664d..083bde9a42c5 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 643/691] 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 b4a4ba1bbddb..91983d3eae5e 100644 --- a/.github/helper/documentation.py +++ b/.github/helper/documentation.py @@ -32,9 +32,9 @@ def docs_link_exists(body): 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 8b0f1935cfb4..6f199c87b6c0 100644 --- a/erpnext/patches/v13_0/shopify_deprecation_warning.py +++ b/erpnext/patches/v13_0/shopify_deprecation_warning.py @@ -4,10 +4,6 @@ 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 644/691] 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 5424ef3c6691..92f558fb3375 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1463,7 +1463,7 @@ def set_status(self, update=False, status=None, update_modified=True): 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 @@ def set_status(self, update=False, status=None, update_modified=True): 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 645/691] 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 d1302f5ae78b..801dadc7f17f 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 @@ def test_payment_entry_against_si_usd_to_inr(self): 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 646/691] 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 e3afaec2ad0c..9e126bade7bf 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 8c9c26691e52..08b8ef889002 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 c5a11cf3958e..d12b872dd794 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 6d7c3d31ae48..1467f27e66aa 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 927e818e0171..e45295d9fea3 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 f1de448bb98a..c52eeb2dfb91 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 73779d49aab4..339811782206 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 0544e0bc9e84..289e18484887 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 0c7cad6e9425..2e2a04826a32 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 ec1e0f202a28..3c90adb0a1c1 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 e99ce708f46f..4819e6d57ac1 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 3fc2bcf7d278..0a922306b46a 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 ceaf4a6ac719..d5fee6b5b837 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 b01239fe02d6..98a4435a50ea 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 ab56c6bc3cf2..f67e2450a296 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 7d8e675e9fe9..b23206b0bddb 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 b324f6ee6831..aed2e89ed79a 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 74db06727bf7..7479891595ea 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 647/691] 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 92f558fb3375..5fa622856bc1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -478,6 +478,9 @@ def set_pos_fields(self, for_validate=False): 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 @@ def set_pos_fields(self, for_validate=False): 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 648/691] 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 7a273c750491..8d65101b3ba4 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 649/691] 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 bd982cf488c3..5f5fdd5c830e 100644 --- a/erpnext/payroll/doctype/salary_slip/salary_slip.py +++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py @@ -680,7 +680,6 @@ def update_component_row(self, component_data, amount, component_type, additiona 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 650/691] 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 b67c7fceac9a..b649b8768bb6 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 083bde9a42c5..d9cffc231f48 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.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 @@ def validate_status_field(self): 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 7bc97d602270..1a5ff27d2a49 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 @@ def test_changing_of_variance_after_response(self): 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 651/691] 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 84f8c398bee8..739324f56201 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -13,6 +13,10 @@ 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 d9cffc231f48..472e96c059e3 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 652/691] 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 7f42cd92e3ca..409f4da8599c 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 update_from_return_docs(doctype): 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 653/691] 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 21c052a39127..4b4c8befa532 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 654/691] 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 db7c034596a6..f1b9235fe3a1 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 655/691] 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 b2be8608db2c..7dfe77db55f9 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 656/691] 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 4c79a5cb5f18..26eb5bb337c4 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1863,7 +1863,7 @@ def validate_quantity(child_item, d): 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 657/691] 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 7dfe77db55f9..b65b101051d3 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 2e794da84257..08065a204efb 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 909959323f7a..f38bd78c0d23 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 658/691] 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 1e05d1fb41df..30075f13d04f 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 e12eae9c1c19..d8a83e53dc03 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 335e8a15ab01..0c81d83ed8e8 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 65c5ff1ceaf6..2fa1d53c60c7 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 e657a9ae34b5..4f3ee7643ab1 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 74b7b5167636..9dd882a3119d 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 63b5dbbd3e61..739d8f6bc63e 100644 --- a/erpnext/accounts/doctype/accounting_period/accounting_period.py +++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py @@ -56,4 +56,4 @@ def bootstrap_doctypes_for_closing(self): 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 541901c9abf5..e44af3a9167e 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 55449132928e..62c97f24d5f6 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -37,7 +37,7 @@ def enable_payment_schedule_in_print(self): 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 @@ def toggle_discount_accounting_fields(self): 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 d47d6e58039f..3e38386481ca 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 19041a3f73dc..059e1d315884 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 41aae14362fd..99fa21c8f9ab 100644 --- a/erpnext/accounts/doctype/bank/bank.py +++ b/erpnext/accounts/doctype/bank/bank.py @@ -13,4 +13,4 @@ def onload(self): 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 a959cea98f2c..c7ea15229936 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 ba3f2face639..63cc46518ff3 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 ecc536733f22..59299f81e508 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 @@ 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 88e1055beb47..a0aac6ab170c 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.py @@ -25,6 +25,6 @@ def on_submit(self): 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 5246baa02b32..31cfb2da1da6 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 2ecc2b0cda33..bff41d5539bf 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 33ae45439e78..dc3b86747001 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 ee5098bea126..20e423a610ed 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 @@ 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 28d84b4442fc..b1ad2972beb3 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 @@ def validate_checked_options(self): 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 7ad1d3ab8319..081c6fa4718a 100644 --- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.py +++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.py @@ -33,4 +33,4 @@ def make_calculations(self): 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 6a430eb02bf1..d10c61858f14 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 8a5473f3a16c..981fec308cc9 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 788ac8be83fd..24cf3ea06891 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 fde41233c4d0..1d482c58f1a1 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 b5fc7e3b497f..7779ccefc202 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 55c119315e0d..92a816d25e99 100644 --- a/erpnext/accounts/doctype/coupon_code/coupon_code.py +++ b/erpnext/accounts/doctype/coupon_code/coupon_code.py @@ -17,7 +17,7 @@ def autoname(self): 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 5af12cde06d2..06987a8a4a5b 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 @@ def test_sales_order_with_coupon_code(self): 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 109737f72769..93dfcc14bdae 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.py @@ -7,4 +7,4 @@ 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 19a73ddfa48b..33c6ab080c95 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 7fc2e4b30602..67692ecc472d 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 b7b6020caa92..926a442f808c 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 f2b0a8c08a62..dbbcedcadfa3 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 @@ def check_journal_entry_condition(self): 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 502765812aea..cd8e204f4c8d 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 @@ def create_finance_book(self): 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 6523cd3cdb8a..6d35ca243976 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 a72023d8e127..4895efcd4cc2 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 75a69ac0cf33..c5f5520479f9 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 5f003e022a07..5835d462ae97 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 @@ def test_jv_against_stock_account(self): "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 cbb9fc4b0f92..1c19c1d2255a 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 7a06d3572a6a..103fa96d02d6 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 32473694c803..cea921e999e5 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 @@ def validate_pos_mode_of_payment(self): 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 bff642273251..ad8623fb4eee 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 a6794998159e..912bd9e331ae 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 a8c07d6bb9be..7eb5c4234d13 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 d76d90996289..9914b45dfc1f 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 831b27085838..d2dffde5cdc7 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -185,7 +185,7 @@ def set_missing_ref_details(self, force=False): 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 @@ def set_tax_withholding(self): 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 @@ def set_gain_or_loss(self, account_details=None): 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 e6d83b9f6832..2d76fe69ef98 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 14aa0736d40b..e8db2c3159dd 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 0c76343fa90a..34af79fcd101 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 9849d7672712..8c7f6f47dd34 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 fd213a47a1e5..3529c16a1c27 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 @@ 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 d12e474c5b16..aa373bc2fcc0 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 6b93f926cdf2..a4f335833ee5 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 5fdde07faa45..9ba57aef3008 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 d788d91855ec..acfe1fef2ee8 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 438951db627f..f83cb375fcf8 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -541,4 +541,4 @@ def set_missing_values(source, target): } }, 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 5eba62c0b311..ad6ff6f55532 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 @@ def test_multiple_payment_entries_against_sales_order(self): # 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 acd0144c2eac..feecf93484c7 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 84c8d09b1648..ea18adefa356 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 c705097ac66a..5c8cb4fbdc13 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 9cfb47876c8f..a6e3bd98e7ca 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 @@ def validate_posting_date(self): 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 @@ def make_gl_entries(self): 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 2f29372b01c3..f17a5c51a088 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 6418d7309033..264d4a68b009 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 cd08efc55fbe..2f8081b95ce8 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 08e072e20496..e50d437ba6a2 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 040a815fab3f..1b9659409c08 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 @@ def test_consolidated_invoice_item_taxes(self): 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 372e75649b3b..d23f348f04ee 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 0023a84a46e1..3318fefab149 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 @@ def validate_payment_method_account(self): 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 2e36391714be..c115be5ae94e 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 913f49829c13..d925dd9d86ea 100644 --- a/erpnext/accounts/doctype/pos_settings/pos_settings.py +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.py @@ -8,4 +8,4 @@ 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 3173db13af33..680370b6af0e 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 8155e7d799a2..8279b59cb410 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 0eac73236e90..5e7583a9745d 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 @@ def on_cancel(self): '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 e08a0e5cc2b2..03c269ac766a 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 @@ def test_creation_of_ledger_entry_on_submit(self): ["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 500952e38ad7..a12ea4033df5 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 890a1871bdde..e840c79cd759 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 28c4c61b9fa6..54fedb77387b 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 7354ef036c54..286f7cf6edd4 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 @@ 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) + 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 @@ def test_promotional_scheme(self): 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 3b91118402c9..6c74d2b438fb 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 173939df008d..b6467a3d5cac 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 914a2457d459..771b49ac6291 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 b470051b51d8..94b3b9ed3384 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 bfaa849200eb..d157837a7a54 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 @@ 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 a7489da316fe..5854ddee940c 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 @@ 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 11c220bf2dbb..db9793d77a66 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 c73f03b57bb2..10b05d0594f3 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 ada665a0ca9f..f01325d80bd2 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 1c47d3ab9fdd..21eb8ce6619c 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 f1069282edcb..3238ead43163 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 e12ac0385005..61d78e1fe4b2 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 f1cb22a49764..cf2d0fbedba0 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 651bf0aa4cea..45d9a14bffbc 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 b959cf961b83..0464e4509f6b 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 2697758d7a36..af484d7899c0 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 1ec517929ef0..28aeef4d5e11 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 @@ 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 8d1df5c19a34..b1de9d85fdb3 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 @@ 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 d825c6fd325c..522e282a170c 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 d02e70b5419f..8cd42f63a413 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 1cad4dfae3df..6317c9c8c0d1 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 4024b8155a44..3d4543fb0519 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 0201f762b372..63ea1bf35f41 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 ab1b77cd5f51..f3668b8b4065 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 dab59db70c37..db6ef117c225 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 @@ 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 c7325fb9f74a..6490ff3776e3 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 7c58e9865fd2..4f2cf487a4f6 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -630,5 +630,3 @@ def test_subscription_without_generate_invoice_past_due(self): 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 aaa32cfe7ef7..7d6f2aed100f 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 1ca442a45318..a341c2af6ac2 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 e4ebc6d12f99..581423181773 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 481ef285e720..1536a237dec4 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 2ba22ca43534..1c687e5cb15f 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 @@ def test_tax_withholding_category_checks(self): 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 e588ed6609eb..4ac657d1ae65 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 71c26e8c55a5..7643eca7635d 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 @@
                                                            4. Value Details
                                                            -
                                                            \ 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 0ca940f8bd54..c1c611ee3a30 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 283d505e3be0..ae07582704cb 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 043ac254ed3c..8696bffbfcb1 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 a53b593a72ab..efb2d00f0bf0 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 14ddf4a30fc2..f5c9449e85df 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 6abd6e5cf77e..b6c6689be0ba 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 9c6b0639c0a0..ea200720dff9 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 729eda9492f5..c08582b564cf 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 29c4f7d39412..1a32e2a8e061 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 2ff5b531c51c..cca676082381 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 657b3e8f2042..e94b30921f32 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 @@ def setup_ageing_columns(self): "{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 26bb44f4f7bb..7838385dc56b 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 dbee02297377..f0b6c6b20ac6 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 79b0a6f30ec7..95f724cc5804 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 8f028496cd5a..9bb6a14c6778 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 2ce5d50edf44..2dcea22f7e67 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 f547ca619bd6..718b6e2fcb65 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 9f0eee8aa5c4..443126e4655c 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 40ba20c4ac6d..d4ae54d4f389 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 a984bf46b505..a2c34c6ee267 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 1363b53746ad..6a8301a6f91f 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 515fd995e66a..9953d8fcaf58 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 4a551b801245..095f5eda66a6 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 1759fa3a48f9..5d8d49d6a654 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 40ba20c4ac6d..d4ae54d4f389 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 714e48d279bf..8e33af7ee8ef 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 08065a204efb..c9c22c246ed4 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 2e18ce11ddc5..51735056896a 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 7195c7e0b8b8..556f5ad4f793 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 6a42bb4fb65b..b7e112c0c9aa 100644 --- a/erpnext/accounts/report/pos_register/pos_register.py +++ b/erpnext/accounts/report/pos_register/pos_register.py @@ -10,7 +10,7 @@ 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 40ba20c4ac6d..d4ae54d4f389 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 a95cfacaeef7..feab96f26521 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 ad3783f0de18..ba236b9969d5 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 f34ea5716399..aaf76c429973 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 e9e9c9c4e69a..a5eced5f80b6 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 2d320f52cfa7..e3d43a7de1e7 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 068926b063db..44e20e83c50c 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 a51c4276301f..e4a3d3527fdd 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 85bbceab8275..2c9b01bbaa31 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 d2c23ee4e78f..fbd25b13bb5f 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 8645d55d0fe1..078b06519f18 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 33360e2b01ec..1fc0faab3a72 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 78c7e439d384..f034e7450ee1 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 eee620b7cc87..1250d676a06f 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 afd84fd9f66d..550824636b67 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 9a8f26fe90cb..8f37735c8122 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 138acbf85a35..40555634a2c1 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 c2e49174047d..b3079837c359 100644 --- a/erpnext/agriculture/doctype/crop/test_crop.py +++ b/erpnext/agriculture/doctype/crop/test_crop.py @@ -11,4 +11,4 @@ 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 464a3680baaa..87184daedc9f 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 c7707a546521..affa57046e55 100644 --- a/erpnext/agriculture/doctype/disease/disease.py +++ b/erpnext/agriculture/doctype/disease/disease.py @@ -17,4 +17,4 @@ def validate(self): 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 57d62c16c25a..33f60c4e152f 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 54788a2c8170..80861770b0d7 100644 --- a/erpnext/agriculture/doctype/disease/test_disease.py +++ b/erpnext/agriculture/doctype/disease/test_disease.py @@ -9,4 +9,4 @@ 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 9cb492aff1e3..c475f002981c 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 3a25b3f0a7a6..4c71d33fe808 100644 --- a/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py +++ b/erpnext/agriculture/doctype/fertilizer/test_fertilizer.py @@ -8,4 +8,4 @@ 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 2806cc6523ec..b65f93de0a0e 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 37835f8c7b16..234d0d4b011e 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 937c06ccadfb..16d105c9c58a 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 @@ def test_texture_selection(self): 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 d9f007cea13f..cb2691d45556 100644 --- a/erpnext/agriculture/doctype/water_analysis/water_analysis.py +++ b/erpnext/agriculture/doctype/water_analysis/water_analysis.py @@ -24,4 +24,4 @@ def validate(self): 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 ab91343d5d1b..75f07be5de2f 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 7f3c1de406a6..2c701796072a 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 922cc4a7b26d..da5778ea3d50 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 a5cf23803d20..62bb4be53aa6 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 02f39e0e7f44..4302cb2c5181 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 e23a71545244..605ce2e25032 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 46620d56e980..39032d637b5a 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -20,7 +20,7 @@ def validate_finance_books(self): 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 @@ def validate_account_currency(self): 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 @@ def validate_account_types(self): 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 39b79d6c5074..9f7ada65d82f 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 @@ def test_mandatory_fields(self): "accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC", "depreciation_expense_account": "_Test Depreciations - _TC" }) - + try: asset_category.insert() except frappe.DuplicateEntryError: @@ -44,4 +44,4 @@ def test_cwip_accounting(self): "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 70b8654509f7..52996e934753 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 a506deec93ee..e14f1d88dcbb 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 392fbdd2af7d..7610152039df 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 c5db90ad3707..bcdc3acf0ac8 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 06d8879091c3..2df7db974469 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 b2de250b168e..1771e27ddfe1 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -40,14 +40,14 @@ def validate_location(self): 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 @@ def validate_location(self): 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 @@ def validate_employee(self): 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 @@ def set_latest_location_in_asset(self): # 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 1cebfff66e51..18a56d33e6d9 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 d32fdf7054f2..746f582fdcd5 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -18,7 +18,7 @@ def validate(self): 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 @@ def decrease_asset_value(self): 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 @@ def get_gl_entries(self): 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 @@ def get_gl_entries(self): "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 @@ def get_gl_entries(self): "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 f36fd2f8dcb2..86376f400467 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 30bbb37851e1..5e727d007a92 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 @@ def test_total_repair_cost(self): 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 @@ def test_increase_in_asset_life(self): 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 03dc47b0bba7..a9dc9795ee37 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 b405afd1ddd0..3e105f6ca49a 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 1a6ef54a830a..75f42a9f7830 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 d1457b9b85a6..7d07397944b7 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 e496e9628d13..944bb61cfeb8 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 a0b1e073cc65..ca3bd90960cc 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 42d3995907f2..ef83f203e737 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 d668c76b6b9a..fa174ba8fa89 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 @@ def test_purchase_order_on_hold(self): 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 5d196874c980..012b0619cc9c 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 8c0c14431447..bc3d767f95df 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 4e73ab8ef4f4..83eb295010a7 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 1e54e50dda9d..a729dd9839fa 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 bf2dfeb37b7f..b605e76ddf4f 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 96775eb0075f..c258756b2a1b 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 39716ed560c0..ccc383fd74ec 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 8bdcd47e0286..b6e28b6c6740 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 6caffbda1f37..c85ca2fbafc9 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 @@ 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 1a76f0ee7dbe..00c93ed1ea38 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 @@ 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 a4ce84e1cf93..8ed6c9e2a6d7 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 6efbc7822521..751336dc4c6c 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 1fcfe75bb03d..75f85f86d1d2 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 2e1652de7334..f06c3f34c441 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 bd710e0df710..5f49a47e75ef 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 bf7c192c91cc..eaa4d0989d2d 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 3a2e5d6dcef7..4473ddea28e9 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 @@ def validate(self): '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 6a4c02c075c1..25e4e2a4dcfc 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 2d2b29cb9169..20fb43026aba 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 b151824ba688..0a51565b08e2 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 e37731eb5796..7ea3e6079cd8 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 5f5f54b79f58..b4cd852c32f4 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 3d2305e2853a..8e5cce5696b6 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 252824054927..a5f05ea52580 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 @@ def delete_test_scorecards(): "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 4eef4b4e03ee..3babfc8cab37 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 @@ def delete_test_scorecards(): "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 9938710e6e68..cc345e96bb83 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 post_process(source, target): }, 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 1ba5d06c5362..678855a457b6 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 37fdc5724f59..89a6459bbabc 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 fe6dde50489a..14b87105e66a 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 @@ def test_path_exists(self): "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 beeca091c8a3..99bcbe633cc7 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 44ab767c0a9b..c36083f2aff3 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 @@ def generate_expected_data(self): "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 89be62231b92..bda172769a90 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 83d25d80ba2a..90919dcc6a3c 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 1ed6cad6b460..095a44319d68 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 0c0d4f0531d3..9a45972837b8 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 d8de701bf6e7..cb304a1fdab7 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 68426abbb042..96cacb6f1b50 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 098214d741c9..015b31c2064e 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 @@

                                                            Analysis Chart

                                                            -

                                                            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 80e521a8bfa2..7a8d08dd22da 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 2b371915f32f..a5a3105a8479 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 a73cb0d62ec1..17928634e782 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 a991cf9881ef..2276c738fbe4 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 26eb5bb337c4..4c243d0cc466 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -842,7 +842,7 @@ def make_discount_gl_entries(self, gl_entries): 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 @@ def make_discount_gl_entries(self, gl_entries): 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 @@ def make_discount_gl_entries(self, gl_entries): "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 @@ def make_discount_gl_entries(self, gl_entries): "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 @@ def make_discount_gl_entries(self, gl_entries): 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 @@ def get_order_details(self): 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 @@ def linked_order_has_payment_terms(self, po_or_so, fieldname, doctype): 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 051481ff6038..8c361a2e561a 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 36ae11021649..969829f96519 100644 --- a/erpnext/controllers/subcontracting.py +++ b/erpnext/controllers/subcontracting.py @@ -390,4 +390,4 @@ def __validate_serial_no(self, row, key): 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 df73f09c493e..f7c6b6c79934 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 dc3ae8bf41a7..0c64eb8e822b 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 996885516307..7848de7a7279 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 26a2907c7cca..7d5609651a15 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 69fd86f7fb57..9281220eef4e 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 7f028cb31604..c0ce6badbf59 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -34,7 +34,7 @@ def validate(self): "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 69d8ca709268..3950d063f22f 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 d4886d35067c..d7bc46165fd9 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 e9a7a95fc7d4..632012b31d32 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 23ad98a28280..8ce482a3f9f2 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 68f0104fd6cd..b8c53f077ae6 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 04cd8a26cad7..52aa0b036ae4 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 0ce8b44e19bc..6fb0f975f462 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 0bc77a3f2a85..f29c2c64e146 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 ec498837f5ef..238884b5190b 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 0325de9b8d94..eeb8984513e5 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 f92070daf3f5..2f6d24224fb1 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 eeaaec2bce23..072a47611b7b 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 d79f8c8480fa..97c56f8c434f 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 1aa4afe1865b..858dcc4da81f 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 3a9d57d6075e..425b7a8fdd78 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 d5c2bfd2f02f..b1db7b57b17e 100644 --- a/erpnext/demo/domains.py +++ b/erpnext/demo/domains.py @@ -25,4 +25,4 @@ '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 fc31176e1e5d..883a6d88cf2a 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 8c7427ab2d17..9212d2ea7196 100644 --- a/erpnext/domains/agriculture.py +++ b/erpnext/domains/agriculture.py @@ -25,4 +25,4 @@ ], '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 bbaa6e55d99c..870624ab3b2d 100644 --- a/erpnext/domains/education.py +++ b/erpnext/domains/education.py @@ -26,4 +26,4 @@ ], '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 259ee9238e5f..b9ad49e772b2 100644 --- a/erpnext/domains/manufacturing.py +++ b/erpnext/domains/manufacturing.py @@ -21,4 +21,4 @@ ['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 b6772c53153d..7c4f6b1f9de9 100644 --- a/erpnext/domains/non_profit.py +++ b/erpnext/domains/non_profit.py @@ -21,4 +21,4 @@ '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 7a4ffc4993f5..892137207674 100644 --- a/erpnext/domains/services.py +++ b/erpnext/domains/services.py @@ -18,4 +18,4 @@ ['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 3aa0be157bca..fa7f2899dcb8 100644 --- a/erpnext/education/doctype/academic_term/academic_term.py +++ b/erpnext/education/doctype/academic_term/academic_term.py @@ -22,9 +22,9 @@ def validate(self): 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 871e0f328456..eb2f90742ce8 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 6d91e977c63c..383b65a7032d 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 0e8619849cda..20e25281ffc1 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 f27f7d14cf64..d3734df80366 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 ec2f49c5a1b8..51e9cf307d80 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 edfec26273b5..85b387f6217a 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 8ba367da76e5..b5cc5cbc7a6a 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 1ea37023b244..bfbf26cf6c1f 100644 --- a/erpnext/education/doctype/assessment_criteria/assessment_criteria.py +++ b/erpnext/education/doctype/assessment_criteria/assessment_criteria.py @@ -12,4 +12,4 @@ 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 db4a4cf5a8df..724c4dac4991 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 bcfcaf82e63c..ab27e6372395 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 2649d4b90c98..1a23606a61d4 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 e4676831a3b5..e0dfaa31fd72 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 a127fd4adf59..00e6309837df 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 726c0fcecd40..cf545c41afb7 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 5e6c29dcdf3b..8ac3faf6dde7 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 c35f607a99dd..b6d28817b5dd 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 6b873ecf97ae..7dfe0cf6c274 100644 --- a/erpnext/education/doctype/assessment_result/assessment_result.py +++ b/erpnext/education/doctype/assessment_result/assessment_result.py @@ -42,7 +42,3 @@ def validate_duplicate(self): "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 438379d08e45..2526076d308c 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 b7adfacb1a23..d4eb4b8ba66e 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 e5535d6085ac..adce57769dd1 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 @@ def test_grade(self): 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 649f420d41f4..a0d286ccbe97 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 @@ 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 0bbe33194a33..7ef5c688fb91 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 81e4a8c08d35..bd8d62c8d2a7 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 06efa54e7702..92f92ed9f3e7 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 8a570bdc5793..8de91b1c092b 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 88fddc2bb6d6..2b6860cb7f48 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 e7fc08a1d7f7..3aa1ea0c5b3d 100644 --- a/erpnext/education/doctype/course_activity/course_activity.py +++ b/erpnext/education/doctype/course_activity/course_activity.py @@ -16,4 +16,4 @@ def check_if_enrolled(self): 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 b9dd457b61cc..37972fe354c5 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 e22c7ce0bab1..874bf121f477 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 @@ def tearDown(self): 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 4275f6ef2a6f..366bbd8b0d46 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 5083ff6589d0..748728d104e9 100644 --- a/erpnext/education/doctype/course_schedule/course_schedule.py +++ b/erpnext/education/doctype/course_schedule/course_schedule.py @@ -14,11 +14,11 @@ def validate(self): 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 @@ def validate_date(self): """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 0866cd653576..22ce7e1ec24e 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 a901f1e467a0..5bb4de858468 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 d57f46ab98e8..7b0e4ab47c8c 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 658380ea4299..6c7e95c80daf 100644 --- a/erpnext/education/doctype/education_settings/education_settings.py +++ b/erpnext/education/doctype/education_settings/education_settings.py @@ -36,4 +36,4 @@ def validate(self): 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 0089957df405..97691a5b62a1 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 acfe40021930..4d7da21ea175 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 310c4105f47f..d9ab99f81803 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 781382b51be6..9755717ee941 100644 --- a/erpnext/education/doctype/fee_structure/fee_structure.py +++ b/erpnext/education/doctype/fee_structure/fee_structure.py @@ -11,13 +11,13 @@ 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 73e314f3512d..fdf7df7aa264 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 25d67d2d5f6b..7e8670490470 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 52e1c4beb5ae..ee8e1e382e93 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 6309d02c1553..0e7329716192 100644 --- a/erpnext/education/doctype/grading_scale/grading_scale.py +++ b/erpnext/education/doctype/grading_scale/grading_scale.py @@ -17,4 +17,4 @@ def validate(self): 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 e363545ff8db..fb56918fdb83 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 9bbfacd5802f..1ea6dc290bd0 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 24e80fa9378b..034b0aaf5dd7 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 a404fc56c547..c19c85947d67 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 98263b55a1f1..2d89351504b0 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 d24df5d61421..9d886b7b9e61 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 c5d249451f22..6c503e1bf1fd 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 dc347cf1b069..b9ca41ae3f9b 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 d7530365117e..204f2961e7fb 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 f9c65fbbfb3b..e92d063602d7 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 b282babd0fce..dd4aa576ac00 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 18d307cdf07d..c47f86668980 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 fec6422e75fd..497ee288aacd 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 @@ def tearDown(self): 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 a7deeab6f65e..fb3b50478c81 100644 --- a/erpnext/education/doctype/question/question.py +++ b/erpnext/education/doctype/question/question.py @@ -43,4 +43,4 @@ def get_answer(self): 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 01bcf7323601..320869be31e2 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 a774b88579aa..a128e1f34273 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 20cee6b2a615..1263b60ced22 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 99aac3393e67..7bcb97f709fa 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 fd23ae41ef17..aead556dc9f9 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 763f120f417a..c1bce24d153c 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 2e5263788f71..fcb2b5fb9303 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 99868d5f0207..529d65184a89 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 3a0bb0b2f23f..e01791a78ac2 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 b4cfdf16e0d5..7b41a7217481 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 211348201e3b..193b6d329778 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.py +++ b/erpnext/education/doctype/student_applicant/student_applicant.py @@ -49,7 +49,7 @@ def on_submit(self): 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 817a728f6963..c39d46ec63af 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 a69ad8a56461..fa679779858b 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 26244ab18451..03101e41e000 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 114358f32a11..daa36e75ce46 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 f025a1a53976..2bbecb911f61 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 9c41b8f3dc69..e405b8aed9d1 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 0d3e7ade152a..e89b76c8d553 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 c7da6f6b2461..3d30b090ba0c 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 028db9188128..972973fbadb4 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 cea0761ae8b0..b66d8397ba22 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 51e3b74a5cfe..39ee9cebd101 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 0260b808646e..3d4572abf700 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 ad7a6de7b3cb..d37445f7b98d 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 6673343be7eb..4c7e47bc38fe 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 d0d7afd701c3..c189e2763c81 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 dc8667ec0651..28ff7d618c17 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 @@ def create_student_groups(self): 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 34c10930b576..fa612ba27273 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 820e30118dc1..1fe4ea1dc352 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 @@ 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 fdcc14747976..0ff6d1a76ead 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 5af9f5d50f7d..6bbf17babfa3 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 fcdd42825f3a..9cae25774819 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 5775369e52e7..4c90c5f6ef2f 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 72772b7b32c2..a9e84e6e277d 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 Report Card") }}

                                                            -
                                                            +

                                                            {{ _("Student Attendance")}}


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

                                                            {{ _("Student Attendance")}}


                                                            -
                                                            +

                                                            {{ _("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 2002b0c8e3be..0c903c5a56a6 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 a5253e93294a..fb680d725b04 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 c145359129e3..c0ec0357cc65 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 ad04356201ee..9f1fcbc8162a 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 7793dcf39533..e2576a0c7106 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 104d3ec06f92..62c94557d7e8 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 699703c5792f..ffc5e984253b 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 a9925adee7a0..f5ea8047c6a1 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 a25a29f9e5fa..99ede8f31dec 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 @@ def fromstring(self, s): """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 6a846efad703..bff928c1c960 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 @@ def verify_credentials(self): 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 2c4d4bbdecf7..b74a7187f0c5 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 @@
                                                            {{ __("Balance Details") }}
                                                            {% 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 554c6b0eb0f4..d1adeeee0728 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 @@ def stk_push(self, business_shortcode=None, passcode=None, amount=None, callback 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 0499e88b5e71..139e2fb192b1 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 fdfaa1b0540f..de933578613f 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 b0e662d3f32e..d4cb6b982bbd 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 42d4b9b2b43c..73f5927df400 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 @@ def get_token_request(self, update_mode=False): "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 37bf28245058..3740d0498390 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 3ef069b5e205..eddcb3401f61 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 5482b9cc6953..af06b3451e04 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 bd072f40a192..45f261007f87 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 @@ def create_delete_custom_fields(self): 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 a35ca28e0a3c..108b4c0dd811 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 a5e162f8b5dd..caafc0821e11 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 dd6dc666d233..e494489d21ad 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 062da6e46542..eca7143e689d 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 861675acea34..99b7cb295a9c 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 03e96a4b3be5..81a3982c4bf8 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 1ef110dc6f46..ae6b39bb1815 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 f32b7cf9d8d5..58194f10a8c3 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 fb635c85788b..ae44a2b77b51 100644 --- a/erpnext/healthcare/doctype/exercise_type/exercise_type.py +++ b/erpnext/healthcare/doctype/exercise_type/exercise_type.py @@ -12,4 +12,3 @@ def autoname(self): 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 058bc9719295..5b9c17934fac 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 7e7fd8241198..6ae3e12d50e0 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 @@ def test_fee_validity(self): # 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 fc0b24122aeb..44c399856c8e 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 01cf4b0a494a..3ee3377b0041 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 7cb5a4814e88..ff9e21252a17 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 21776d2380aa..798976283b3c 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 5f2dc480a1b7..9dd4a2c73c03 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 3033a3e6ac9c..7bad20dffdcb 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 cc2141790f7a..2b3029efdeb4 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 887d58a2e044..63b00859d716 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 c1d9872a0192..f8ccc8a002ad 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 748c12c6896e..635d4beb8dfc 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 @@ def update_item_price(self): 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 fd2000369352..fbfa774c91cc 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 21f636997530..a5dad293e318 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 35c823d739c9..4bb3940ae0f4 100644 --- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py +++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py @@ -15,4 +15,3 @@ def validate(self): 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 be486c62d1eb..f1706557f45f 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 c20537ea81de..30064bd16540 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 2410b0ce845f..4b7599df2964 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 a04fb2b592a3..46bfb3db5d40 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 cd62dd390350..4ee65738ba31 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 b9077301bad3..28b60bdcc921 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 9c35dbb3ea54..9a4840acfeae 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 @@ def get_chart_data(self): 'datasets': [] }, "type": "line" - } \ No newline at end of file + } diff --git a/erpnext/healthcare/setup.py b/erpnext/healthcare/setup.py index bf4df7e4c88f..891272ddf813 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 7da3f1fb41cd..f09e54091924 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 8471aee4a031..6a2fc02574fd 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 7f7322cf4b6e..7bde292a2bc7 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 f77d43b3143a..259edb9c06d1 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 1a30ceac6d34..50612b923efd 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 9ca17e2e2262..fb1354c3f6b6 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 a6868ee2b1cf..11d9f3944d52 100644 --- a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py +++ b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.py @@ -7,4 +7,4 @@ 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 309427e30c29..392b370e6c3c 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 0403cad0683f..3eb64e085012 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 ca58e0c32027..b3c5704fa53d 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 @@ 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 456648969654..d9f6d2eb3eb0 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 8f30e8cc1611..b3e7fef02a03 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 cfdd6d3aefbc..2d3eb0001196 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 fab2ffc1a371..a847c8e21748 100644 --- a/erpnext/hr/doctype/branch/branch.py +++ b/erpnext/hr/doctype/branch/branch.py @@ -7,4 +7,4 @@ 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 c315385f1169..82a6ae103eea 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 5ba02b36b8a4..807698ba0a2e 100644 --- a/erpnext/hr/doctype/branch/test_branch.py +++ b/erpnext/hr/doctype/branch/test_branch.py @@ -4,4 +4,4 @@ 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 d2ceb8bd5276..15335171473f 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 52d864bc0e6e..5c7726de6a63 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 3a571f765354..e73779c97c67 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 2eeca26e3030..e4f6645ee420 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 efd864ad5934..a3f84aab5f02 100644 --- a/erpnext/hr/doctype/designation/designation.py +++ b/erpnext/hr/doctype/designation/designation.py @@ -7,4 +7,4 @@ 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 45c341719114..00adf8293f7a 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 3b300941a659..2778862a1c2f 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 5ca47560b106..f4280152c5c0 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 9ab091a1eb6f..7d6a70013d43 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 200dcd796664..3a414584804f 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 c88b2b8e49e9..100968bb7aa5 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 d25fb2247ede..c8d6644b2f8c 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 ab965d54e3bf..5ae8c6bd03b0 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 2827d4ba2899..48d4344df22f 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 60ea0f9895de..6c0cd4f963bf 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 9f12ef24e62d..7ba511f08d55 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 @@ def test_mark_attendance_and_link_log(self): 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 a1d449291c93..f0a76172b2c2 100644 --- a/erpnext/hr/doctype/employee_education/employee_education.py +++ b/erpnext/hr/doctype/employee_education/employee_education.py @@ -7,4 +7,4 @@ 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 c7166309f371..517ef57be85e 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 @@ 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 f2656e9a2b28..df6791041854 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 503b5ea44498..17055829efbe 100644 --- a/erpnext/hr/doctype/employee_grievance/employee_grievance.py +++ b/erpnext/hr/doctype/employee_grievance/employee_grievance.py @@ -12,4 +12,3 @@ def on_submit(self): 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 fc08e216099b..11672ca4e0e5 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 a615b20a5a2a..ed897ee10326 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 3a6bf8594b3c..26a61c407b25 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 d0f3d8d016a6..2f385a8113ee 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 @@ 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 55fe317b9eef..ca9b2987a6f9 100644 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py +++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.py @@ -57,4 +57,3 @@ def set_missing_values(source, target): }} }, 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 5f7756bcada1..0445270b9fff 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 837da5301627..ab0eb2f5dce4 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 0493306166f8..547a95e3bdf4 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 afa2a1ff1fcb..caca2961a1a4 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 7533ab635f5c..38dfc4d4c86f 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 a674f3902657..599f3262240d 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 f787d9c65683..d63501a9314c 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 39345f076637..75f985cec397 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 fb306b65d280..00aa6bb9bc46 100644 --- a/erpnext/hr/doctype/employment_type/employment_type.py +++ b/erpnext/hr/doctype/employment_type/employment_type.py @@ -7,4 +7,4 @@ 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 9835aabd481c..fd7c6a1ce339 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 e138136605c3..0297ffa01a3e 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 @@ 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 629341ff2a5a..3c4c672816c9 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 7de8f4fc13a2..fe9735070194 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 d0c43d3be47d..2529faec983e 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 96ea686706cb..c2bd1e9f9f1b 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 @@ def test_expense_claim_status(self): 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 8bfa1ade072f..5d48990c5ce0 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 @@ 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 2595506486d0..a637a5402130 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 @@ def validate_accounts(self): """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 62234e08a043..3c9ed35313d0 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 aabab0b0d35a..78a95b9b7410 100644 --- a/erpnext/hr/doctype/holiday/holiday.py +++ b/erpnext/hr/doctype/holiday/holiday.py @@ -7,4 +7,4 @@ 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 22e1de0c342a..05641c7dc26d 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 bfcafa9460ca..ce766143a62e 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 fd082fda09bc..ec99472d9bc2 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 ced98fb9a580..c99df269cc9b 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.py +++ b/erpnext/hr/doctype/hr_settings/hr_settings.py @@ -15,4 +15,3 @@ def set_naming_series(self): 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 c62515597cec..7658bc93539e 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 0594ba395ba8..14aeb03a87ea 100644 --- a/erpnext/hr/doctype/job_applicant/job_applicant.py +++ b/erpnext/hr/doctype/job_applicant/job_applicant.py @@ -50,4 +50,3 @@ def check_email_id_is_unique(self): 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 7f131151e180..ed97978a8ad0 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 b5391c8bf367..741a182addc0 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 c9d7d2bef793..5339b9c3d67c 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 b3e1dc8d87b4..edb21321fcc8 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 c0890b4f57c4..31ef33ef2ce5 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 c015101600a1..69bf49bef77e 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 b9e6c0a8b2da..cc2f027e85b8 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 e9e129cdd247..d94764104d08 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 7456aebb4579..7a063d92eac6 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 0ef78f2f8832..d5364fc8b2e3 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 0286f300646d..31faadb10792 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 c1d6a6665b67..c45717f5870f 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 209302e8f303..14ca41bebcac 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 @@

                                                            Details:

                                                            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 6d7b6a705885..0866b0b6d2a4 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 2aa54984ec5e..45aa4915bc66 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 453787865c7f..b39601b490db 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 be06b768bf72..8e5a09e01ec2 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 @@ 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 f4028f54eba3..54978a1e83af 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 @@ 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 b60e225a7279..4a450807ccfc 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 2b5cec1c1e16..9d3732771759 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 912bd8ad92f8..d136210a0432 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 cf1303618101..33a6243e609a 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 1572de3cb72a..7c2c9632d85e 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 b5857bcd8fe8..cbb34371fc99 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 ff7f0422e030..474f3a77ad0b 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 fc868ea15a65..af7567b5bc75 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 4bb0535cf8c9..a2f7f5866b7d 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 8fe4b8f8efa0..8b954c46a10e 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 9a14e3588d0e..0089804f5129 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 @@ def test_allow_to_grant_all_leave_after_cancellation_of_every_leave_allocation(s 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 5cae9a8809c2..c8944fcb9e23 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 d939a248102b..db910cde5123 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 7fef2975c8a3..048dddd3ef94 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 bb692e1402ea..5d2360f10fa9 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 4c3c1ed579ef..07d92fe61d6a 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 @@ def test_overlapping_for_fixed_period_shift(self): "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 6461f07552b1..2731da125a85 100644 --- a/erpnext/hr/doctype/shift_request/shift_request.py +++ b/erpnext/hr/doctype/shift_request/shift_request.py @@ -94,4 +94,4 @@ def throw_overlap_error(self, d): 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 e3bf5df94909..f70b61a20a6a 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 3525540cdfd9..60b7676e251f 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 35a303f0fb2a..8e89d53c8e0b 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 628255b11f50..1c6218e9a70b 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 9b32136bfb6e..6a275b330c05 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 8ff4fecd6ee3..08031a1963e3 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 d5f6e5f573e3..642e6a1fd754 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 1c1645c766f9..19afd8dd6e1c 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 9daa51f92759..5c825aea7fb1 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 c30a3ad34cdd..4c0c18029d03 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 0dea098a67eb..5e875c1b434a 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 0d32de793c44..3d4b9b3ea963 100644 --- a/erpnext/hr/doctype/training_feedback/training_feedback.py +++ b/erpnext/hr/doctype/training_feedback/training_feedback.py @@ -42,4 +42,3 @@ def on_cancel(self): 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 7d85cab59dc9..a4ccf5406364 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 441a71bba77a..0fc18a802982 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 62ac383ab786..5cdbcad80584 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 2ebf8962ee27..3f3975083578 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 761c70182b29..628c8972cec4 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 ed52c4e12220..ed02120cca39 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 6f3a0dc40eb2..14fe9a02da29 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 fd8fef9e82c8..b49662a6eb93 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 374038ac2022..50f6d07a470c 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 08f2c94ad448..81162a4c9a81 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 298319821724..4423d29e4029 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 a6cf935985b3..58cdc4b7e1dc 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 d60582e1a188..87daafcaaeea 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 aa8eea5d74e1..d8691b4d0259 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 528ae4cea638..8de4af5d4fca 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 8f3938892690..fe77b6abc963 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 60b69b409a09..bbe4a8d179ec 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 9620f520002a..51dc7ff85b81 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 879acd18ef49..2d0aa0f36d35 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 992b18d37a6e..a1026ce055b3 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 699703c5792f..ffc5e984253b 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 cf75cc8e41a0..58179416b1ad 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 6bb04401bed0..6ce2a54b1900 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 7a8190f74507..711a7829baf3 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 314f58dd15ee..122d72360515 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 bf3f58b83ef2..3975adf44317 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 f341e81065f7..f113c10ef71e 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 7978350adf86..d75213ce78d3 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 b8b1a40b5fd0..57aec2e5c9c0 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 878b3fd051e3..3eec5660ac11 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 11c932ff1c19..48ca392edf75 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 32d81afed5d3..9fc1fda53f41 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 8233b7b297a1..cd7694b7b177 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 ac33589b5497..17de8c1da4d3 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 b24dc2f7c285..4f936dd7c117 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 208cb19c88e4..50ef930dbbe6 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 @@ def validate_accounts(self): 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 58c668948c25..95d97fdf9b03 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 54a3f2cbb1fd..676df701cc3f 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 @@ def make_gl_entries(self, cancel=0): ) 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 11333dc2aaf2..8c67c0affeec 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 243a7a3ba664..e104c6646b07 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 dc9bd81a1ddc..e67e4d4738f1 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 50b68da30e3a..43980ffef48e 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 0ccd149e5fbf..f2cbbb469f08 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 2a74a1eb8584..a505e72c4d9c 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 c6f6b990cc56..659107708812 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 887a86a46c5d..34bbe5a45034 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 546a68f268b8..d1a8c8de27c7 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 d6e42f3ee1ca..97289032d70f 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -52,15 +52,15 @@ def validate_end_date_visits(self): 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 @@ def validate(self): 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 @@ def get_pending_data(self, data_type, s_date=None, item_name=None): 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 update_status_and_detail(source, target, parent): 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 09981bad05fe..c733dd0c92c6 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 @@ def test_events_should_be_created_and_deleted(self): 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 @@ def test_make_schedule(self): #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 7fffc942a039..d63c7003870b 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py @@ -28,11 +28,11 @@ def validate_maintenance_date(self): 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 f19a1b08681b..d3bb33e86e0b 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 d7556add8055..1aedb1e590f0 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -76,4 +76,4 @@ def update_item(source, target, source_parent): "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 3171defdaeae..9a0a72fb475c 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 6088e46265b1..e614a7ebaa17 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 60fb377f476c..6e2599e41bc6 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 5044a2844441..98a9198b79bc 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 cc5a3f8cb1a6..39ccbddbea2d 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 @@ 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 e7cdea290b86..220c73e1493c 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.py +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.py @@ -7,4 +7,4 @@ 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 ee3f877da3cd..e3501eb9cf6c 100644 --- a/erpnext/manufacturing/doctype/bom_operation/bom_operation.py +++ b/erpnext/manufacturing/doctype/bom_operation/bom_operation.py @@ -7,4 +7,4 @@ 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 e4b8a2028825..bf5fe2e18de2 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 81860c9fbcf2..91eb4a0fa90a 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 ed851ebc83b0..8017209e7de0 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 b6a6c33d37fa..8fa0b27fcb82 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 @@ def test_job_card_with_different_work_station(self): 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 668e981d188e..a0122a473858 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 e88164f91787..149fe3e22b82 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 102b6780e5ff..2936e33b118a 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 00672317018f..8e7e72372637 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 ca597f63278f..52a56af7bce0 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 8b570422ddc6..37cf5a49dc98 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 @@ 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 ef7f79e8d2c4..99c7273a6404 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 @@ 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 ab309cc9d504..50a3fe62da54 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 9aa0715e7ff9..403d46d8d42d 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 a4bf93edef14..95bdd291ee0b 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 d18f028fc6ee..9aa53b5e3c37 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 ba8e30cba077..d8d25fc6f832 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 e7d92658f7d9..8778d9ba557a 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 119a4fc62923..2ae8848cc034 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 @@

                                                            {%= filters.warehouse %}
                                                            {% } %} - \ 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 9f81e7d26a17..b4db98c3d7e3 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 093309a005be..74c794b5dd06 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 fc27d355984f..9a6c764c6094 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 @@ def get_summary_data(self): "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 b1bff3500c6d..a8939051523f 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 79af8a1e39bb..42c9d97cb5ef 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 6192632bda6f..a12ac7f9d915 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 97553e699d88..599a738f6f6d 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 612dad0bf51d..d0766f9abe51 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 c4b899913b9f..a1b25f2d4e2a 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 @@ class ChapterMember(Document): pass - - diff --git a/erpnext/non_profit/doctype/donation/donation.py b/erpnext/non_profit/doctype/donation/donation.py index 4fd1a30ab9e1..9aa7e13433c2 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 7e25c8d2173d..3da89423d37f 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 bbe9bf5228d6..b206f54523eb 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 fb70e59575b4..ab6a197ed51e 100644 --- a/erpnext/non_profit/doctype/donor/donor.py +++ b/erpnext/non_profit/doctype/donor/donor.py @@ -15,4 +15,3 @@ def validate(self): 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 f0123b2e4942..b810fd027af6 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 6b8f1b1deb63..e58ec0f5eea3 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 0f5a9bed826e..5ad2088fc317 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 022829bd3a60..c712b99c3b88 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 @@ def validate(self): 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 a84cc2cdb534..50c93516adc1 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 @@ def get_webhook_secret(self, endpoint="Membership"): 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 7da3f1fb41cd..f09e54091924 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 7666ef6b6165..186722a8bf09 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 102b6da87575..daa258e88255 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 2e3095153a59..f832936b10ac 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 48fa22204d17..a9dd31031007 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 f722be26b41a..0f1e49662310 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 eecf75568a49..a613bd88497e 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 d956052f1a60..a45f39d43404 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 5a30c780f8cd..0243dfb38ed7 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 ed47d4293f71..a8841ef3a441 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 ac2ae1511aef..d06c6889ff72 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 462f830c1835..0a1a36007e57 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 @@ 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 a908c16715ad..d3ea7a3c1c05 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 591c521efbe5..029f75a22588 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 @@ 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 9925b70a9630..4247c788e33f 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 80476c8a74c8..8d187965011d 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 ee709ac2d49d..dfcf5ab28867 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 a307e8c36594..8c92b5180d97 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 c7c763554009..6da70b4ce386 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 edab34cc58aa..ef703d0ea71f 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 b997ba2db22a..dd5cb639b1b1 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 b85ab66f1486..9f231edea730 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 fad0cf7a45e1..923b23048dfc 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 @@ 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 882ec84e6443..0e6036b07400 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 e605071a2979..06fc6151675b 100644 --- a/erpnext/patches/v11_0/rename_health_insurance.py +++ b/erpnext/patches/v11_0/rename_health_insurance.py @@ -6,4 +6,4 @@ 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 077829f48184..fbf925d955cc 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 @@ 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 4f6844000251..d5ca4cc57491 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 4353ef80e24d..8f8a545c4101 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 2498888273de..d8ce31f30768 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 7bd8577f9c50..2f90f14db3ec 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 0f7fad7e4976..4e72917547b2 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 efa04fd2cec7..dabaeffc94a3 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 1b58c97ea4d5..799e91a3e226 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 f2eeadac600d..37a616c7021f 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 a8d3fab4812d..977d84fefe86 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 b2f407b18ef0..2b3820371091 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 457f51166704..2892674d374f 100644 --- a/erpnext/patches/v11_1/delete_bom_browser.py +++ b/erpnext/patches/v11_1/delete_bom_browser.py @@ -5,4 +5,4 @@ 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 6e708df48d83..b706e5c1ffb9 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 5b1251c31cf6..fc3ec74083af 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 a0f2536f7d89..4c4b14fd4e70 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 222b9a0b170d..8e8725c8af6e 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 @@ 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 d41cff523d57..ec01fbb642e6 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 019eefd68f43..49a9a1772468 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 544bc5e69114..354e636c9b0c 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 347dec1f74d6..8c360ad9353c 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 5ccdec6d2622..074b904002c3 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 b6bd5fa311c2..c2ed6c288fed 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 484f81a7acae..855d21dd9922 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 4d649dd0f0c9..6fe578dbd95a 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 387e88588d99..2dfd30714c8c 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 bf8f566d32ac..c1c11e260069 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 bb4b0380f881..cf1ed3676bfe 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 87d98f1a5631..f29b71437e8c 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 5bb6e3fb339f..a0b1f87d61b0 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 1208222504c3..c90819238c8c 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 485dd314a16e..d765b93d2186 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 af9bf74f30eb..2e42368b1522 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 657decfed23c..f171542df168 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 88233b4cf7fe..93d2576bb6df 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 @@ 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 7feaffdf4088..23a8f24d780a 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 0078a53cd694..a6230f427718 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 a0c9fcf4cbe3..b9ee81df50e7 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 592726754301..163855729df8 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 c8708d801341..ac7e82d2d0d2 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 548c1a471750..97badf355d98 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 8267df95e116..46794bebe706 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 d1446b3227dc..be884f94d15e 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 7859606e5cb1..4fcffb702a43 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 24286dcebf98..6b1b601db195 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 ffb4e937b189..9a08ad452137 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 @@ 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 4230cb88f4d7..7489ea30a09f 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 d0dc356bd0e6..c71b91c92569 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 0577f81234c2..9d8626b85271 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 aa2fff4ca72a..20b096331edf 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 09fc4c1b04ed..f88a22f6c9d6 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 @@ 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 13e935b2d39e..c52f380d8c26 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 5ee75be49905..b5d7e3dcb9e7 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 @@ 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 8ba0d79a8316..4415cfeaba99 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 4d4fc7c46290..13110dfe03f5 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 241e4b9b5e15..a290e31cf240 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 04b54a6cf617..038bd6d21ae6 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 @@ 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 a996a69b3d90..a27c7b24a8cd 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 55bbdee7edf2..cc093953bf4f 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 a6011c4dace9..8fdc73b8ff16 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 70ca6b222e97..a5c8f7524a7f 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 fafbec6a9a7d..84645a386390 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 a5490ef20d57..66696bee5414 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 07026732fd48..6c11cb415f96 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 bae1e28deb94..babaebeaefc5 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 64a9080a8fe2..87643a235451 100644 --- a/erpnext/patches/v12_0/set_quotation_status.py +++ b/erpnext/patches/v12_0/set_quotation_status.py @@ -4,4 +4,4 @@ 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 63ca540a8e24..1cc37caba42b 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 @@ 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 2474bc3b82cb..82b14fc9d604 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 847d92894bea..17fdcd9395ad 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 6005ab707263..3474a34af4be 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 91931eeb3bc5..f45166496100 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 309ae4c2ab79..8a871718133d 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 db71a735def5..c45f6221f932 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 c0230c439548..9e5f599d2c8a 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 963edad150e3..1a54216b8850 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 d06c5713d23d..d0b04433979c 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 0b2e82750b28..4bbec44aa429 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 f7496999b334..e15894df890f 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 e4dcecd9bdbc..6ebaf48e0e83 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 f5e7b947c23d..09f070742992 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 7450e9cd8c09..8dbfa1866d36 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 b5a20aa6fd9d..24914fd13bc6 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 e2c7fd268aa4..cdc5a1eabb54 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 5ed9040f1eda..a7b66f0d2bb9 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 605a29e4778e..1e4f383dda69 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 c92d52dcec01..ebae3ad7157b 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 289b6a761e34..341955aa35f3 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 585e54062656..08d4876c0d1f 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 90dc0e2e18b1..9a354537f7df 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 59b2e49b26ea..6ad3402ba022 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 8bdc07ee5b80..c17aad06c7f6 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 0f44865808a5..671c012c8a0c 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 1d6eebe0398f..50a4a0efcbed 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 94a9fa85a8ed..8d6340d44ef1 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 8980fd00392b..76b8041cd946 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 @@ 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 61dc824dd4ce..f20c73ae1026 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 11e1e9b3b94a..dca43b4193da 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 021bb72cae6c..c4ad1b7ff4fb 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 ee7734053c1d..d2228c3bf319 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 a9d7883d40ab..73361f00262a 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 833ae2a48fbb..24d9196d29f6 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 fde8f864703d..15aeb76e53f2 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 48325fc2d438..4ef04ad9b1b1 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 3fa09a7baaa5..f60567b6b218 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 390e217cadaa..d8bcd7f07752 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 @@ 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 7cb264830ab3..bc1fc98e4da3 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 66857c4e659b..13ec41ec55ef 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 edca2383930f..7f75946af9fb 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 1c576db1c7e6..7968e74f50f8 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 @@ 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 18cad8d86c03..3db183fb2ab6 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 c3b49eb4fe56..f6104d1579f2 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 ecc7822e1d74..c8c160fae715 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 d927524a3c02..83581dd41447 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 0bdcc9c0e883..7b93ce357680 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 adfa20e368a8..50f233deef49 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 eae5ff60b90b..dc9ed18eade6 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 a7d82077b760..bcc09527a299 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 3e20212af6d3..ef70b55d94c0 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 d4e65c6f2f2d..733b3a960cfa 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 8cc27d217fe9..b41b74205c72 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 792118fbee2f..ccdc334f3065 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 409f4da8599c..e642547ef82e 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 update_from_return_docs(doctype): 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 871ebf17c4ea..d25e9c805b79 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 28e650e9cedf..d9c3e453d478 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 3d149586a046..341b0e8e2e25 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 93b7f8e59a40..a36c84ea6e21 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 340bf4947b6b..7d344f9cd7ea 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 ccb2e0ec74aa..55f5f8201fbe 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 e8b017d86445..c214990693c8 100644 --- a/erpnext/patches/v8_1/setup_gst_india.py +++ b/erpnext/patches/v8_1/setup_gst_india.py @@ -50,4 +50,4 @@ def send_gst_update_email(): 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 4d47f25fcf38..2a9c56179e7e 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 @@ def test_recurring_additional_salary(self): 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 5ebe514ac052..c7fbb06b1000 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 a8dd7e4d6dd9..d3f24c937805 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 @@ def validate(self): category_max_amount = frappe.db.get_value("Employee Tax Exemption Category", self.exemption_category, "max_amount") if flt(self.max_amount) > flt(category_max_amount): frappe.throw(_("Max Exemption Amount cannot be greater than maximum exemption amount {0} of Tax Exemption Category {1}") - .format(category_max_amount, self.exemption_category)) \ No newline at end of file + .format(category_max_amount, self.exemption_category)) diff --git a/erpnext/payroll/doctype/gratuity/gratuity.js b/erpnext/payroll/doctype/gratuity/gratuity.js index 565d2c49f940..377f3c649167 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 1acd6e342fdb..8cb804db6fa3 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 5b2489f22cdd..483e346a32d4 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 ee6c5df73713..014a121c96ac 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 0d70163495a6..0f27315cfbf8 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 7af507d119c2..0346a7cc594f 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 4e9c7c9e7cc8..e33299559cc9 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 06a1c7d72dff..a47eba1887df 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 d07a1ab551a9..0f6cc378513e 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 @@

                                                            Examples for Conditions and formula

                                                            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 58c445f8a961..6dfb3a57d5df 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 547f2b81be7e..0159e3530fb0 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 a0c3013061de..5fb3ce2a98e4 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 @@ def validate_dates(self): 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 7a395572613e..05c2c1bec277 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 6482ed34516c..8b75b4facea1 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 4bbb7f6a1bec..6ecf2b1960cf 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 166d982c9c6c..9b829541692c 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 4e4d4774abfa..54ea7c62df46 100644 --- a/erpnext/portal/doctype/homepage/homepage.py +++ b/erpnext/portal/doctype/homepage/homepage.py @@ -23,4 +23,3 @@ def setup_items(self): 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 8aa073402ac3..ec7c83aa397d 100644 --- a/erpnext/portal/product_configurator/test_product_configurator.py +++ b/erpnext/portal/product_configurator/test_product_configurator.py @@ -139,4 +139,4 @@ def test_products_in_multiple_item_groups(self): # 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 ba10153e5cdb..2d22caad8e2a 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 862a70717abd..99226ea581c9 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 67d76eb1eee0..5f35f299b36f 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 @@ def test_duplication(self): 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 8b610c29561f..50e18ef4de94 100644 --- a/erpnext/projects/doctype/activity_type/activity_type.py +++ b/erpnext/projects/doctype/activity_type/activity_type.py @@ -5,4 +5,4 @@ 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 3ea28dfbe2c9..dcb01018de0e 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 @@ 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 f5bfbb7ca1f0..1f299e30833e 100644 --- a/erpnext/projects/doctype/project/project_dashboard.html +++ b/erpnext/projects/doctype/project/project_dashboard.html @@ -23,4 +23,4 @@
                                                            {{ __("Total hours: {0}", [flt(sum, 2) ]) }} -{% 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 aace40240c4f..2426fd2af894 100644 --- a/erpnext/projects/doctype/project_template/project_template.py +++ b/erpnext/projects/doctype/project_template/project_template.py @@ -22,7 +22,7 @@ def validate_dependencies(self): 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 95663cdcbbbc..d546fd09a30b 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 a1f941fe148c..e3dda5eccc5d 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 f46876eda232..36137ca0186c 100644 --- a/erpnext/projects/doctype/project_type/project_type.py +++ b/erpnext/projects/doctype/project_type/project_type.py @@ -10,4 +10,4 @@ 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 faa4bf1f9b62..2e1ec746ed63 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 d5d09194446e..2edd2f85a310 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 d1d872f28a4e..9ebfcdd180c5 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 3a38415e6c63..1e055629ba8b 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 84c7b8118b81..1655b76b9882 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 a0042eb7d1a2..5f569d6bcd4b 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 14f016a7653a..80967ede1ce3 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 acff97a2269f..088d98c4d5fb 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 1b200f855dba..b59fdc96fe80 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 5efde41b5b35..a22ed7b83386 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 682fb2e09dca..3dcae5b1b53d 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 dbeedb4be926..78291b2d7818 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 @@ def test_delayed_tasks_summary(self): ] 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 @@ def test_delayed_tasks_summary(self): 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 cd5ad7803a5e..17c92c234d5e 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 0e5a59756e33..969fc556e8d5 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 @@ def get_expected_data_for_test_employees(self): '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 cd5ad7803a5e..17c92c234d5e 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 9139d84facc0..0a52f7bf904c 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 699703c5792f..ffc5e984253b 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 e97f36d04b4c..e5a94048be1b 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 a3ac3bb2ce25..6bc6b2c2db14 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 a3ac3bb2ce25..6bc6b2c2db14 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 3d12d9cb86e9..90714e9491d3 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 b591010ec861..f7d1ab39fcff 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 3cf259216a70..b64c73a43c79 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 519cfcac72b2..9f7f29ad72b6 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 b2f7afe53f3a..0d79b10c041b 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 26ab6d92b9d4..02703139dd98 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 da050abc6e4c..23ec2fdb8491 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 bd7946a1e139..b1b78c051740 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 5e4e439f3d08..aa83bb0e4655 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 f0fe001973cb..d0e83f3b1cd2 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 63ae7e99bbd5..8380b2b2c0ba 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 96fa0aae4e56..ecba4b1e5a8c 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 c0903c64c373..3c9b800f4a0a 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 6e6a7cb5989d..4912d6849912 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 4352f7025b7e..26f1e19da5a8 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 41d9bb9ed4c7..91dd3d434c77 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 26be997d482a..0e5c0d3720e8 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 6f5d67c74621..38e1eb5156cb 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 dfe2c88ea82d..a343c3402afd 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 6a5f36da77c0..e5e78690622a 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 58fb26c0e4c2..86a15f490727 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 fb94df85ed8f..4cb6ee03c0cc 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 96e181788e37..bb23f1512b9d 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 a66d6474e0d7..57d5e8414ae7 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 5962859be5aa..490a7c4af73e 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 f4325c03f5b9..9ea841603421 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 e216a7539c88..b44f2a203440 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 d6fa5051ee6d..02401ba689d0 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 @@ 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 24b97ca3a091..98d665f3910b 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 @@ 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 5a8ec73cfe17..d3e96cf2d94d 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 @@ def validate(self): 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 b3eed1038368..afed14b6ad0e 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 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 f3fe986d5391..3e616b75ceb2 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 @@ 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 f61d6e581d79..0e135b50212b 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 f8de22958bda..9e453ebfc2eb 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 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 ff85c84dc9df..5fd1b30eb452 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 754bccb06e05..6bf4c179c6bb 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 @@ 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 ac876229ecb1..fd2b6a4eaa0d 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 53f4e6c70feb..117db0012ba8 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 eeb4cf617c30..2851fcc5969a 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 36bdf26acf55..4fa7734bc685 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 67371bfc5c65..0e6b7034101d 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 e3a8b073f0f6..34cc890e2197 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 e2eb31b55a3f..b0be783de56f 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 a7d92da8ace0..161ecd01ef1c 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 @@ def test_review_creation(self): 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 9f318de34512..1b4087d77bad 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 7fa4c32612aa..25c9c9d32e6e 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 c24ad886ea1a..4f6b3eca7a63 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 7ff4de48639c..347fdfe61b66 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 86cd4d1545d6..4791dc26753d 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 3b6a45a3b42a..f3fc60fdb636 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 @@
                                                            5.    {{__("Values of exempt, nil rated and non-GST inward supplies" 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 c2d6edfc773c..5918ec8b3160 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 ad60db05595e..656c3296e582 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 @@ def are_dates_overlapping(self,duplicate_certificate): 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 c478b0f32280..41b42036687f 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 63f9a777bb57..be3d7a3e5427 100644 --- a/erpnext/regional/germany/utils/datev/datev_constants.py +++ b/erpnext/regional/germany/utils/datev/datev_constants.py @@ -455,7 +455,7 @@ "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 8ad30fa91065..348f0c6feeda 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 fa7e88d3a15f..765b51f435fe 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 d3b7ea3b1a01..5f6dcdeb9227 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 a152797a5d47..949733e0ad89 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 ef1d5822ba14..4932f660ca56 100644 --- a/erpnext/regional/italy/__init__.py +++ b/erpnext/regional/italy/__init__.py @@ -76,4 +76,4 @@ '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 a5ca7eee5d4c..86aed2ef8145 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 47acf291a399..66ffceae5391 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 67297f757cae..d7e3ac9a5d3c 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 5b9896be2a1c..4f777fcf7e3f 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 2b4359a74935..bbcd355d13b9 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 7274e0accea7..12e9676b4ba6 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 59389ce32697..1adddbdae579 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 496060139463..bddc32096f14 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 4e57ff7ea379..f67d622fdf83 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 29c7dbf43c68..bb75238b8c0b 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 acde68a942b2..54808e59e1a6 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 b4dc28d177d1..a91a30796bc0 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 597072c53a1d..82423f005cce 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 d9b9968d90c8..7328f3f218e2 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 @@

                                                            {%= __("VAT on Expenses and All Other {% } %} - \ 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 292605ef13d4..17aca17afdab 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 @@ def get_consolidated_data(self, doctype): 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 4657ff833ddf..8a75987c3d7e 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 ebf3b2bee1f5..2396aab91f52 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 ec62ba22b4db..adce5c733523 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 26de5d04aaa2..8fe4e7b84d50 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 83020b6cca82..952c46769b78 100644 --- a/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py +++ b/erpnext/restaurant/doctype/restaurant_menu/restaurant_menu.py @@ -57,5 +57,3 @@ def get_price_list(self): price_list.save() return price_list - - diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 284946626737..2f06e988804e 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 edb83838b61a..cad9a27ace64 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 3878a791db65..3680906057fa 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 65b17e976a65..7a30d6524a0c 100644 --- a/erpnext/selling/doctype/industry_type/industry_type.py +++ b/erpnext/selling/doctype/industry_type/industry_type.py @@ -7,4 +7,4 @@ 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 1246a241c7e2..ebc6366155e2 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 @@ 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 681b8171e2c2..7e1205231bbb 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 @@ 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 ba5ba0dc3bae..0dc90ec21146 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 f1ac951ef94f..d1bb788937be 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 aeb5d1b9eb79..b59bb0510e84 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 e7349e3201cb..f5172fbae2e8 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 5b4224dfe9ad..0d340997ad95 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 50b8a8396d7c..84be56f46058 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 ac7ed65ec027..5e21f817573d 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 05a760de2730..2a71c27009f8 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 a0a21eef5a31..d685fbff82b0 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 @@ def test_so_optional_blanket_order(self): 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 @@ def test_payment_terms_are_fetched_when_creating_sales_invoice(self): 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 74268685079f..9eebfdaf21a8 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 8e0538511afc..be76c49f8452 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 27f303d43b1d..62afef3e1704 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 1832108399f9..28bea254d68b 100644 --- a/erpnext/selling/doctype/sales_team/sales_team.py +++ b/erpnext/selling/doctype/sales_team/sales_team.py @@ -7,4 +7,4 @@ 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 95a4243fb456..d8d30515f8f4 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 d142d16248f8..87846a84d301 100644 --- a/erpnext/selling/doctype/sms_center/sms_center.py +++ b/erpnext/selling/doctype/sms_center/sms_center.py @@ -83,4 +83,3 @@ def send_sms(self): 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 8d1f112dc280..03c46bb2ae0c 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 c827368dbf53..e61a634aaee4 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 a4a4b0e0ed2b..9d8338e5fed0 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 6a4d3d5214d7..d899c5c19b46 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 e41011fba2cc..87ed5a8ea21d 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 53560285b3f4..f07293d8ec2b 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 0c8490961165..9917d72af86d 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 a8e2fad37346..2b8443627d5a 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 5166cc808e94..ea9bbab0c729 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 @@ 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 b236151bad9c..e269f02d0ce6 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 263391a7f72d..9f3d255e6626 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 f515baf31bc0..22bf3fc94fd6 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 94f5aa488dce..8dbccc497b7b 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 b8b09cbc53f6..827839f8b756 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 3878a791db65..3680906057fa 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 12839d18aea6..a8d1cf8ff2da 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 265d2fe577e6..25ed86ef1dd9 100644 --- a/erpnext/setup/doctype/brand/test_brand.py +++ b/erpnext/setup/doctype/brand/test_brand.py @@ -4,4 +4,4 @@ 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 d05541b63447..8f83d3cd73a9 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 95cbf5150cdb..54c67538aef2 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -108,7 +108,7 @@ def on_update(self): 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 9b483dd55e55..2d760284e5a6 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 19b276c77db3..160481cc9524 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 e1c803a038be..1b7fd4fd5c82 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 8c0b609775a0..b568494c84a7 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 bf6e5405b420..a4c1e2e7deac 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 68e1ccb63563..c06669b16b40 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 b52c79c497ba..d50e9c8835c6 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 ec1af7a67653..ec90b376cdce 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 @@ 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 2e415af28287..c2c2710b0255 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 95afe974b29c..5451ee1daffe 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 4ee4b0ff166f..666301a643e7 100644 --- a/erpnext/setup/doctype/email_digest/templates/default.html +++ b/erpnext/setup/doctype/email_digest/templates/default.html @@ -180,8 +180,8 @@

                                                            {{ _("Open Notifications") }}


                                                            {% endif %} - - + + {% if purchase_orders_items_overdue_list %}

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

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

                                                            {{ _("Purchase Order Items no


                                                            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 57afe02d7967..b2628f4f4f83 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 3878a791db65..3680906057fa 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 00dc0f3d9191..3d5cd2d6f9e7 100644 --- a/erpnext/setup/doctype/print_heading/print_heading.py +++ b/erpnext/setup/doctype/print_heading/print_heading.py @@ -7,4 +7,4 @@ 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 59455d2b1dd5..b2be2e375e44 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 @@ 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 3878a791db65..3680906057fa 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 2cc6235b946e..42c5a5a54fb2 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 @@ 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 ff4c7885bc7b..f6b30b649b50 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 @@ 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 3d0b2ff7f8de..662008ec8db7 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 bcdfac926c4f..00056fde8699 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 0788e2e167a2..728793eb25f0 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 d2e2597cb473..633be45d20b5 100644 --- a/erpnext/setup/doctype/target_detail/target_detail.py +++ b/erpnext/setup/doctype/target_detail/target_detail.py @@ -7,4 +7,4 @@ 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 3878a791db65..3680906057fa 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 372cc6d3e3e2..5b00ccbdbb3b 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 ceec47ae8c63..3caf814c90b8 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 05e8f666cfbe..7eefe77495c8 100644 --- a/erpnext/setup/doctype/territory/territory.py +++ b/erpnext/setup/doctype/territory/territory.py @@ -24,4 +24,4 @@ def on_update(self): 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 edd11dfa69b5..dadeeef09e9b 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 bbe68369ffd6..933a8c3bed85 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 @@ def test_doctypes_contain_company_field(self): 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 @@ def test_deletion_is_successful(self): '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 20caa15ee415..6a50ef8bbd90 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 d7175ddac430..c238f18abad7 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 3878a791db65..3680906057fa 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 f7f86d675097..404b84b11342 100644 --- a/erpnext/setup/doctype/uom/uom.py +++ b/erpnext/setup/doctype/uom/uom.py @@ -7,4 +7,4 @@ 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 9ac7df2c667a..e416b509b98f 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 @@ 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 c11a3885c9b8..c6d9f0851b12 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 bacada9f5cce..faa25dfbaa2c 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 c069b90e9868..2a497225fbc2 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 @@ def validate_exchange_rates_exist(self): [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 75899e121a59..008751e20889 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 @@ def test_tax_rule_validation(self): 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 29617a874857..6c9e531a4d1a 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 63e9fe1b31bb..9f674dcebf87 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 3241234af5f8..0e1466fd1faa 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 1b3953435e48..1e3d0d069a1b 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 890ae502c825..fe061d5f5f51 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 @@

                                                            {{ title }}

                                                            \ No newline at end of file + diff --git a/erpnext/startup/filters.py b/erpnext/startup/filters.py index ec07329dedfa..98210165dfa4 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 8819a55c0abc..a89435d48663 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 1e18969e63e9..99698ba69a9b 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 ab573e566ac1..70b030e48f21 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 a4137547f7ee..2b9d46e4ab0d 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 374a34ea7ca6..2258532c6feb 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 af7f50ff91ef..2d2150b8acd8 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 47684d5c6eca..9db5db865f59 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 5e1ff9800094..e853858b60b5 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 3f6e8d150351..76f798942906 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 756825e826d2..91e7c006eef1 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 @@ def test_payment_terms_are_fetched_when_creating_sales_invoice(self): 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 21eb35ce378d..9f1375f563c3 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 50305957892d..8bd381a2ed30 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 @@ 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 9ec28d898147..f76bb87efede 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 9c062bc34cad..d12334e355aa 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 @@

                                                            Details:

                                                            {{ vehicle }} - \ No newline at end of file + diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 614c53abb573..422fe3e4b7a9 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 77ae51fa3418..cceb1ec895f1 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 db123090aaeb..5c42f3b12473 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 2b999819cbbc..f81fc1d8743a 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 5e3524e5b6d3..7f7e72d5c0f7 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 61e53d24a469..07af176a9443 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 @@ def test_numeric_item_attribute(self): 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 a9183ce58661..3e4e85004671 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 @@ 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 c27d1be78928..939abf8d3242 100644 --- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py +++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.py @@ -65,4 +65,4 @@ def manage_default_item_manufacturer(self, delete=False): @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 92aefc8d9e9c..785737b267f6 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 @@ 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 0f9c593d36a0..5cdaa229565b 100644 --- a/erpnext/stock/doctype/item_reorder/item_reorder.py +++ b/erpnext/stock/doctype/item_reorder/item_reorder.py @@ -9,4 +9,4 @@ 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 1a07f03ec587..5dda535f8105 100644 --- a/erpnext/stock/doctype/item_supplier/item_supplier.py +++ b/erpnext/stock/doctype/item_supplier/item_supplier.py @@ -7,4 +7,4 @@ 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 1fe2f4546811..7c9e81157587 100644 --- a/erpnext/stock/doctype/item_tax/item_tax.py +++ b/erpnext/stock/doctype/item_tax/item_tax.py @@ -7,4 +7,4 @@ 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 6d0dbad2a5e4..e3041cf3eef6 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 @@ 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 0521a7ad1cb4..493e8b239a46 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 @@ 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 f7ccb9b6e295..38f4eafc3aa8 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 @@ 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 e4458207dbbc..0dc396aefac3 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 @@ 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 f3e5e5db2509..e1e4faf6825d 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 bf26cd117f82..a2cd03b6495f 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 d8b39fe5aafa..6fb55ae02ace 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 91b47bac4d97..137079b9838f 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 050e0f0d1c93..b03a8543c6f7 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 d6f9b6614145..7c62c2e63a15 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 16f007f6a206..e0066e65d2ce 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 694ab384bf65..b0a855961f9f 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 @@ 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 ee218f2f6852..730fd7a829cf 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 6e007df5e6bc..7c321c450a60 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 61b069442f8a..6832954a8115 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 c362b5a765c1..9291498e8635 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 33713faf696f..10abde17eb28 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 5979c861294e..2c287c9033c4 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 @@ # 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 7fbce7d59d23..3896c0e59ea7 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 b4f1201f36cd..2d982cc1bb38 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 0f50bcd6ea8f..315e723fabc4 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 86f7dc3e0841..0590ae1abeb9 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 f7565fd505c6..d08dc3e8b762 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 65188a22c6eb..b10fa310d662 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 @@ 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 01d2031b3a4b..971b3c298251 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 b9a58cf43e45..0eccce3a58e7 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 @@ def test_serial_no_sanitation(self): 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 ce2906ecbe9f..13a17a259138 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 52b052c81f3f..ae6a3c154e80 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 9c3e22f0231b..db2f11617435 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 3cf4861ccb88..a87a7fb7fd82 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 aac09c30cd5b..cae318d8f2c8 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 828738eb6ca3..ef0286fe1b96 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 ffd06642bf04..54e1ac81211d 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 cdeb4ab04a77..fac0b4b89229 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 e8b2973c457a..9f8530727092 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 699634df6d26..20f119ad6175 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 770f886d0438..8243426032d7 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 f9e062f85168..a5623fded23d 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 @@ 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 80001d63fd45..666d2c7144f4 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 c192582531ae..94b006c8944d 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 67fe20bd3799..fdead2056702 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 @@ 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 8ea280cc59e4..850da1ee45f9 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 e3981c913e19..6e429a225524 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 1f172504a7fb..9243e1ed84fb 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 407d7d1ccd5f..e9e14c724668 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 f3f61963a888..ff8a69fb0337 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 90112c78a839..de7e38e7d3e0 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 acaf180a903c..7ac5e6403022 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 7354eee41308..29689b1a912d 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 9e5e63e37e2e..da593a40d685 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 4fc4027200dd..61306662c0d7 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 @@ def get_columns(self): "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 79dc5d88219e..d9151606884d 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 @@ def get_columns(self): "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 ade004cde42f..8a04565c197e 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 446d3049b719..77fd2ff24472 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 cf174c936829..00125e71a9a9 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 e54cf4c66c7b..b3b7594ffd72 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 a7243878eb82..c8f60a15d649 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 0bbc61b9dbfe..7af1dab6a0be 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 086d833bbc47..c67eed7e9263 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 c0535bf0efad..173aad6d5a92 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 d16485e8cc62..695efacb6946 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 8227f1548c15..0d96ea6aa8ec 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 c3339fd341e5..cc3aa3522d8f 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 8495142ba5bb..b22788f7a29c 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 fde934b13396..d62abed91f35 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 bfc4471b9afc..7e0c0e8ab354 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 808d27917092..7956f2e8648e 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 78e95df9898c..fa19eeba58b0 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 cdc9895917c5..5b0064707569 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 264642856dab..90648f1b2499 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 9ac1efa268d0..d4daacd4ea46 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 739324f56201..4146e488629d 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -182,7 +182,7 @@ def test_first_response_time_case5(self): # 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 @@ def test_first_response_time_case8(self): 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 @@ def test_first_response_time_case12(self): 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 @@ def test_first_response_time_case29(self): """ 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 7c8925ebc301..514b6cc26ba8 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.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 a7b55f8a74c6..618c93ea9d80 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 472e96c059e3..8c1c1ef0de00 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 f2bd68139655..7e7a405d6e73 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 1a5ff27d2a49..a81516ec1162 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 922da2b33de5..69bf2730d35f 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 3fdb10ddf38c..54fce0b35928 100644 --- a/erpnext/support/report/issue_analytics/issue_analytics.py +++ b/erpnext/support/report/issue_analytics/issue_analytics.py @@ -218,4 +218,4 @@ def get_chart_data(self): '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 77483198ecc9..a9d961a4592b 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 @@ def setUpClass(self): 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 bba25b8bed68..7c4af39f1040 100644 --- a/erpnext/support/report/issue_summary/issue_summary.py +++ b/erpnext/support/report/issue_summary/issue_summary.py @@ -362,4 +362,3 @@ def get_report_summary(self): 'datatype': 'Int', } ] - diff --git a/erpnext/support/web_form/issues/issues.js b/erpnext/support/web_form/issues/issues.js index 699703c5792f..ffc5e984253b 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 c00dfa905667..6f8e41169567 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 1bcc84613232..b80acdb37609 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 12cdf1ec6000..1f57b4969c04 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 8b60830db62f..5ccc61016651 100644 --- a/erpnext/templates/emails/daily_project_summary.html +++ b/erpnext/templates/emails/daily_project_summary.html @@ -43,4 +43,4 @@

                                                            {{ title }}

                                                            -{% 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 a22e09cb8dea..1764e8f70387 100644 --- a/erpnext/templates/emails/daily_work_summary.html +++ b/erpnext/templates/emails/daily_work_summary.html @@ -52,4 +52,4 @@

                                                            {{ title }}

                                                            -{% 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 812939a55383..3283987fab05 100644 --- a/erpnext/templates/emails/request_for_quotation.html +++ b/erpnext/templates/emails/request_for_quotation.html @@ -21,4 +21,4 @@

                                                            {{_("Request for Quotation")}}

                                                            -{% 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 51c232d8e879..8a2414a3c929 100644 --- a/erpnext/templates/emails/training_event.html +++ b/erpnext/templates/emails/training_event.html @@ -11,7 +11,7 @@

                                                            {{_("Details")}}

                                                            {{_("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 e7db3a368df0..4724b6811964 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 469a45fd7d43..d4dfa8e591af 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 9050cc388ae3..b5f18ba66d1e 100644 --- a/erpnext/templates/generators/item_group.html +++ b/erpnext/templates/generators/item_group.html @@ -159,4 +159,4 @@

                                                            {{ title }}

                                                            }); }); -{% 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 c562db3c25a4..135fb3643d33 100644 --- a/erpnext/templates/generators/job_opening.html +++ b/erpnext/templates/generators/job_opening.html @@ -14,17 +14,17 @@

                                                            {{ job_title }}

                                                            {{ 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 8b153448eeaf..8cc58a0a1f26 100644 --- a/erpnext/templates/generators/student_admission.html +++ b/erpnext/templates/generators/student_admission.html @@ -14,7 +14,7 @@

                                                            {{ title }}

                                                            {%- 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 2334ea2955d2..646210e65f1a 100644 --- a/erpnext/templates/includes/cart/address_picker_card.html +++ b/erpnext/templates/includes/cart/address_picker_card.html @@ -9,4 +9,4 @@

                                                            {{ address.title }}

                                                            {{ _('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 72cc5f51423e..66a50ecc9f3a 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 b2ba4312d6c2..5d107fc0d069 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 c80dca4bcc95..334b5ea200a6 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 c2f13539cdf3..5652bb1ddddd 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 c44bfb538487..be0d47f37154 100644 --- a/erpnext/templates/includes/macros.html +++ b/erpnext/templates/includes/macros.html @@ -120,4 +120,4 @@
                                                            {{ card.title }}
                                                            {% 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 133d99e5eb98..291220629c9e 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 da4fb8c0460d..7b3c9a413182 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 5a717fc6699e..5d9fc50385ef 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 6f53bae2e80d..d7466873ddaf 100644 --- a/erpnext/templates/includes/projects/project_search_box.html +++ b/erpnext/templates/includes/projects/project_search_box.html @@ -27,4 +27,4 @@

                                                            }); $(".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 107df51dd8b3..d36ee6e23bb0 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 3401bd39371b..38d46b7fe00f 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 521c583cb606..6d84fb86a785 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 42e7f3e70b7e..6592f7a2e5ca 100644 --- a/erpnext/templates/pages/courses.html +++ b/erpnext/templates/pages/courses.html @@ -8,4 +8,4 @@

                                                            About

                                                            {{ intro }}

                                                            -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/templates/pages/courses.py b/erpnext/templates/pages/courses.py index c80d8e7d2294..92c38f6fcae9 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 cf5476635bd3..785d8059ba01 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 2ef9c1053473..9a61eabaf8cd 100644 --- a/erpnext/templates/pages/home.html +++ b/erpnext/templates/pages/home.html @@ -72,4 +72,4 @@
                                                            {{ blog.title }}
                                                            {{ 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 7193d755a1ef..6072db49ea9d 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 96a0f42a05c2..bdef79cfbedf 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 6ba154a06c79..d961c6344af3 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 cfaa1a15cfc3..0b72e9f8b607 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 0c2772e4d822..151d029ee470 100644 --- a/erpnext/templates/pages/material_request_info.html +++ b/erpnext/templates/pages/material_request_info.html @@ -71,4 +71,4 @@

                                                            {{ doc.name }}

                                                            {% 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 28e541a5d927..e29860ddd67e 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 89a7d2aace89..4923efc4e8c1 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 bc4242f91969..fd7658b3b1e1 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 34985d94ea7c..816a25963f5a 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 d0d72f073a9f..9ab76deff73b 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 262167fc0b9f..bd6bcea7ca0a 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 6cd6a7e51af7..fe4d304a398c 100644 --- a/erpnext/templates/pages/task_info.html +++ b/erpnext/templates/pages/task_info.html @@ -147,4 +147,4 @@

                                                            {{ __("Comments") }}

                                                            }); -{% 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 b832b88048bc..260e2788cd0d 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 22ea3e45d380..be13826444c9 100644 --- a/erpnext/templates/pages/timelog_info.html +++ b/erpnext/templates/pages/timelog_info.html @@ -45,4 +45,4 @@

                                                            {{ doc.name }}

                                                            -{% 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 7a3361c2ef56..ee86483fa299 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 8e68f1cc6388..aaa949192cac 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 282fc6454b75..5b3f45a1af78 100644 --- a/erpnext/tests/test_regional.py +++ b/erpnext/tests/test_regional.py @@ -14,4 +14,4 @@ def test_regional_overrides(self): 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 8b0ce0957d4c..f55137bc9cf8 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 aeb8d2a11676..ccff785ec948 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 fc3aa298242b..902fd64d686d 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 50c4b255ce12..0f9f2f886de3 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 b2e74da9215a..485b0b3383f9 100644 --- a/erpnext/utilities/bot.py +++ b/erpnext/utilities/bot.py @@ -36,4 +36,4 @@ def get_reply(self): 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 0f8a7a385c14..5e3ac1a4c92d 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 8273a4a781fd..6f78f6ee127a 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 36fb54f0150c..db021b473a45 100644 --- a/erpnext/utilities/doctype/video_settings/video_settings.py +++ b/erpnext/utilities/doctype/video_settings/video_settings.py @@ -19,4 +19,4 @@ def validate_youtube_api_key(self): 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 fb58a5d58679..384d84194bb6 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 3516a35097a4..29a489ddcc74 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 699703c5792f..ffc5e984253b 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 92c76ad8790b..7c18ecc41fee 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 20fc9a4878c9..a7e994c1e3f9 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 e1986b44154b..91989a9ef48c 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 6c49fde739eb..277610876f78 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 f242f43ad54e..207175f89dc2 100644 --- a/erpnext/www/book_appointment/index.html +++ b/erpnext/www/book_appointment/index.html @@ -63,4 +63,4 @@

                                                            Add details

                                                            -{% 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 ebb65b1f24ea..9bcd3d202e23 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 d4478ae34a83..bd766c0ea8a3 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 0c0484536259..05cbb16d3cb8 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 0d70ed5cefd9..c07b9402b104 100644 --- a/erpnext/www/lms/course.html +++ b/erpnext/www/lms/course.html @@ -103,4 +103,4 @@
                                                            {{ topic.topic_name }}
                                                            -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/index.py b/erpnext/www/lms/index.py index 26f59a2395e2..c14b94326b56 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 dc8fc5c72c77..3cbdec61aa08 100644 --- a/erpnext/www/lms/macros/card.html +++ b/erpnext/www/lms/macros/card.html @@ -31,4 +31,4 @@
                                                            {{ program.program_name }}
                                                            -{% 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 94f239eb8edb..e72bfc8175b4 100644 --- a/erpnext/www/lms/macros/hero.html +++ b/erpnext/www/lms/macros/hero.html @@ -52,4 +52,4 @@

                                                            {{ title }}

                                                            } {% endblock %} -{% endmacro %} \ No newline at end of file +{% endmacro %} diff --git a/erpnext/www/lms/profile.py b/erpnext/www/lms/profile.py index 4788ea6e70b7..7e338e38f13c 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 7ad618630a41..30528c667dd3 100644 --- a/erpnext/www/lms/program.html +++ b/erpnext/www/lms/program.html @@ -84,4 +84,4 @@
                                                            {{ course.course_name }}
                                                            -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/program.py b/erpnext/www/lms/program.py index 104d3fa315ad..a4f588ccf182 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 cd24616cd454..dc69599112ae 100644 --- a/erpnext/www/lms/topic.html +++ b/erpnext/www/lms/topic.html @@ -55,4 +55,4 @@
                                                            {{ content.content.name }}
                                                            -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/erpnext/www/lms/topic.py b/erpnext/www/lms/topic.py index 8abbc72e918d..993828090ce0 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 12b4c2c08197..3c19198cc161 100644 --- a/erpnext/www/support/index.html +++ b/erpnext/www/support/index.html @@ -96,6 +96,6 @@
                                                            {{ item['category'].name }}
                                                            .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 5d267430c160..70090c7805d9 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 659/691] 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 e7fa354a2e91..566323a9e97d 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 660/691] 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 566323a9e97d..068672a790f2 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 661/691] 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 068672a790f2..566323a9e97d 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -15,4 +15,4 @@ 5f473611bd6ed57703716244a054d3fb5ba9cd23 # Whitespace fix throughout codebase -0ef5247e2f6e9562fe837394243b71642d1d98d6 +4551d7d6029b6f587f6c99d4f8df5519241c6a86 From cf8d2d97dddb02e23fa338d9c15269528187c02b Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 19 Aug 2021 15:27:30 +0530 Subject: [PATCH 662/691] fix: Incorrect mandatory error message for warehouse --- erpnext/stock/doctype/stock_entry/stock_entry.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 7b31d2fdf2de..90a33d3617eb 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -317,9 +317,6 @@ def validate_warehouse(self): d.s_warehouse = self.from_warehouse d.t_warehouse = self.to_warehouse - if not (d.s_warehouse or d.t_warehouse): - frappe.throw(_("Atleast one warehouse is mandatory")) - if self.purpose in source_mandatory and not d.s_warehouse: if self.from_warehouse: d.s_warehouse = self.from_warehouse @@ -332,6 +329,7 @@ def validate_warehouse(self): else: frappe.throw(_("Target warehouse is mandatory for row {0}").format(d.idx)) + if self.purpose == "Manufacture": if validate_for_manufacture: if d.is_finished_item or d.is_scrap_item: @@ -346,6 +344,9 @@ def validate_warehouse(self): if cstr(d.s_warehouse) == cstr(d.t_warehouse) and not self.purpose == "Material Transfer for Manufacture": frappe.throw(_("Source and target warehouse cannot be same for row {0}").format(d.idx)) + if not (d.s_warehouse or d.t_warehouse): + frappe.throw(_("Atleast one warehouse is mandatory")) + def validate_work_order(self): if self.purpose in ("Manufacture", "Material Transfer for Manufacture", "Material Consumption for Manufacture"): # check if work order is entered From 993b0532f8b9240c8cc5ac8fb56a202a783fab4f Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 19 Aug 2021 15:43:34 +0530 Subject: [PATCH 663/691] Merge pull request #27026 from ankush/eq_assign fix: equality check instead of assignment [skip ci] --- erpnext/shopping_cart/cart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 56afe95efd07..e9f4bd57a6ae 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -308,7 +308,7 @@ def update_party(fullname, company_name=None, mobile_no=None, phone=None): party = get_party() party.customer_name = company_name or fullname - party.customer_type == "Company" if company_name else "Individual" + party.customer_type = "Company" if company_name else "Individual" contact_name = frappe.db.get_value("Contact", {"email_id": frappe.session.user}) contact = frappe.get_doc("Contact", contact_name) From 09f34e558eb695d451c393a6b76c7517b5f283c6 Mon Sep 17 00:00:00 2001 From: Alan <2.alan.tom@gmail.com> Date: Thu, 19 Aug 2021 15:51:36 +0530 Subject: [PATCH 664/691] fix: set production plan to completed even on over production (#27027) --- .../manufacturing/doctype/production_plan/production_plan.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 8c27d6ccc964..7e7dc044e4e0 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -297,7 +297,7 @@ def set_status(self, close=None): if self.total_produced_qty > 0: self.status = "In Process" - if self.total_produced_qty == self.total_planned_qty: + if self.total_produced_qty >= self.total_planned_qty: self.status = "Completed" if self.status != 'Completed': From 6541453c1697571bbc8fd50d0598cc20dfef2c34 Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 19 Aug 2021 15:53:55 +0530 Subject: [PATCH 665/691] fix(ux): removed rate from grid view --- .../promotional_scheme_price_discount.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json index 795fb1c6f46b..a70d5c9d4305 100644 --- a/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json +++ b/erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json @@ -106,7 +106,6 @@ "depends_on": "eval:doc.rate_or_discount==\"Rate\"", "fieldname": "rate", "fieldtype": "Currency", - "in_list_view": 1, "label": "Rate" }, { @@ -170,7 +169,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2021-03-07 11:56:23.424137", + "modified": "2021-08-19 15:49:29.598727", "modified_by": "Administrator", "module": "Accounts", "name": "Promotional Scheme Price Discount", From c60d5523bca0a0631555a6234a485cd7a1e3c245 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Thu, 19 Aug 2021 16:23:18 +0530 Subject: [PATCH 666/691] fix: add child item groups into the filters (#26997) * fix: add child item groups into the filters * fix: appending values to proper variable * fix: refactor the loop --- .../item_group_wise_sales_target_variance.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py index 89cfa16abe0c..24ca666f6b13 100644 --- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py +++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py @@ -44,6 +44,18 @@ def get_data(filters, period_list, partner_doctype): if d.item_group not in item_groups: item_groups.append(d.item_group) + if item_groups: + child_items = [] + for item_group in item_groups: + if frappe.db.get_value("Item Group", {"name":item_group}, "is_group"): + for child_item_group in frappe.get_all("Item Group", {"parent_item_group":item_group}): + if child_item_group['name'] not in child_items: + child_items.append(child_item_group['name']) + + for item in child_items: + if item not in item_groups: + item_groups.append(item) + date_field = ("transaction_date" if filters.get('doctype') == "Sales Order" else "posting_date") From 9225f02599c87ab9ee720e84877e5a3d7badadc2 Mon Sep 17 00:00:00 2001 From: Alan <2.alan.tom@gmail.com> Date: Thu, 19 Aug 2021 18:49:20 +0530 Subject: [PATCH 667/691] fix: pass planned start date to created work order (#27031) * fix: pass planned start date to created workorder * test: production plan to work order start date Co-authored-by: Ankush Menat --- .../doctype/production_plan/production_plan.py | 1 + .../production_plan/test_production_plan.py | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 7e7dc044e4e0..b4c663507ce1 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -346,6 +346,7 @@ def get_production_items(self): "production_plan" : self.name, "production_plan_item" : d.name, "product_bundle_item" : d.product_bundle_item, + "planned_start_date" : d.planned_start_date, "make_work_order_for_sub_assembly_items": d.get("make_work_order_for_sub_assembly_items", 0) } diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index af8de8ee0e36..a5b9ff845fc5 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -5,7 +5,7 @@ import frappe import unittest -from frappe.utils import nowdate, now_datetime, flt +from frappe.utils import nowdate, now_datetime, flt, add_to_date from erpnext.stock.doctype.item.test_item import create_item from erpnext.manufacturing.doctype.production_plan.production_plan import get_sales_orders from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation @@ -61,6 +61,21 @@ def test_production_plan(self): pln = frappe.get_doc('Production Plan', pln.name) pln.cancel() + def test_production_plan_start_date(self): + planned_date = add_to_date(date=None, days=3) + plan = create_production_plan(item_code='Test Production Item 1', planned_start_date=planned_date) + plan.make_work_order() + + work_orders = frappe.get_all('Work Order', fields = ['name', 'planned_start_date'], + filters = {'production_plan': plan.name}) + + self.assertEqual(work_orders[0].planned_start_date, planned_date) + + for wo in work_orders: + frappe.delete_doc('Work Order', wo.name) + + frappe.get_doc('Production Plan', plan.name).cancel() + def test_production_plan_for_existing_ordered_qty(self): sr1 = create_stock_reconciliation(item_code="Raw Material Item 1", target="_Test Warehouse - _TC", qty=1, rate=110) From 45617ae22fbcb2c32343acc0324151e24a51e48b Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 19 Aug 2021 17:40:00 +0530 Subject: [PATCH 668/691] fix: Shopping cart Exchange rate validation - Use `get_exchange_rate` to check for price list exchange rate in cart settings - Move cart exchange rate validation for Price List from hooks to doc event - Call cart exchange rate validation on PL update only if PL is in cart and currency is changed --- erpnext/hooks.py | 2 +- .../shopping_cart_settings.py | 57 ++++++++----------- .../test_shopping_cart_settings.py | 4 +- .../stock/doctype/price_list/price_list.py | 14 +++++ 4 files changed, 40 insertions(+), 37 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 8f7c7db208ea..1aaf4ccd33f0 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -251,7 +251,7 @@ "erpnext.support.doctype.issue.issue.set_first_response_time" ] }, - ("Sales Taxes and Charges Template", 'Price List'): { + "Sales Taxes and Charges Template": { "on_update": "erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings.validate_cart_settings" }, "Website Settings": { 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 2a497225fbc2..efed1968a147 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 @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _, msgprint -from frappe.utils import comma_and +from frappe.utils import flt from frappe.model.document import Document from frappe.utils import get_datetime, get_datetime_str, now_datetime @@ -18,46 +18,35 @@ def onload(self): def validate(self): if self.enabled: - self.validate_exchange_rates_exist() + self.validate_price_list_exchange_rate() - def validate_exchange_rates_exist(self): - """check if exchange rates exist for all Price List currencies (to company's currency)""" - company_currency = frappe.get_cached_value('Company', self.company, "default_currency") - if not company_currency: - msgprint(_("Please specify currency in Company") + ": " + self.company, - raise_exception=ShoppingCartSetupError) - - price_list_currency_map = frappe.db.get_values("Price List", - [self.price_list], "currency") + def validate_price_list_exchange_rate(self): + "Check if exchange rate exists for Price List currency (to Company's currency)." + from erpnext.setup.utils import get_exchange_rate - price_list_currency_map = dict(price_list_currency_map) + if not self.enabled or not self.company or not self.price_list: + return # this function is also called from hooks, check values again - # check if all price lists have a currency - for price_list, currency in price_list_currency_map.items(): - if not currency: - frappe.throw(_("Currency is required for Price List {0}").format(price_list)) + company_currency = frappe.get_cached_value("Company", self.company, "default_currency") + price_list_currency = frappe.db.get_value("Price List", self.price_list, "currency") - expected_to_exist = [currency + "-" + company_currency - for currency in price_list_currency_map.values() - if currency != company_currency] + if not company_currency: + msg = f"Please specify currency in Company {self.company}" + frappe.throw(_(msg), title=_("Missing Currency"), exc=ShoppingCartSetupError) - # manqala 20/09/2016: set up selection parameters for query from tabCurrency Exchange - from_currency = [currency for currency in price_list_currency_map.values() if currency != company_currency] - to_currency = company_currency - # manqala end + if not price_list_currency: + msg = f"Please specify currency in Price List {frappe.bold(self.price_list)}" + frappe.throw(_(msg), title=_("Missing Currency"), exc=ShoppingCartSetupError) - if expected_to_exist: - # manqala 20/09/2016: modify query so that it uses date in the selection from Currency Exchange. - # exchange rates defined with date less than the date on which this document is being saved will be selected - exists = frappe.db.sql_list("""select CONCAT(from_currency,'-',to_currency) from `tabCurrency Exchange` - where from_currency in (%s) and to_currency = "%s" and date <= curdate()""" % (", ".join(["%s"]*len(from_currency)), to_currency), tuple(from_currency)) - # manqala end + if price_list_currency != company_currency: + from_currency, to_currency = price_list_currency, company_currency - missing = list(set(expected_to_exist).difference(exists)) + # Get exchange rate checks Currency Exchange Records too + exchange_rate = get_exchange_rate(from_currency, to_currency, args="for_selling") - if missing: - msgprint(_("Missing Currency Exchange Rates for {0}").format(comma_and(missing)), - raise_exception=ShoppingCartSetupError) + if not flt(exchange_rate): + msg = f"Missing Currency Exchange Rates for {from_currency}-{to_currency}" + frappe.throw(_(msg), title=_("Missing"), exc=ShoppingCartSetupError) def validate_tax_rule(self): if not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart" : 1}, "name"): @@ -71,7 +60,7 @@ def get_tax_master(self, billing_territory): def get_shipping_rules(self, shipping_territory): return self.get_name_from_territory(shipping_territory, "shipping_rules", "shipping_rule") -def validate_cart_settings(doc, method): +def validate_cart_settings(doc=None, method=None): frappe.get_doc("Shopping Cart Settings", "Shopping Cart Settings").run_method("validate") def get_shopping_cart_settings(): 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 008751e20889..18a492b2c049 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 @@ -21,12 +21,12 @@ def test_exchange_rate_exists(self): cart_settings = self.get_cart_settings() cart_settings.price_list = "_Test Price List Rest of the World" - self.assertRaises(ShoppingCartSetupError, cart_settings.validate_exchange_rates_exist) + self.assertRaises(ShoppingCartSetupError, cart_settings.validate_price_list_exchange_rate) from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records as \ currency_exchange_records frappe.get_doc(currency_exchange_records[0]).insert() - cart_settings.validate_exchange_rates_exist() + cart_settings.validate_price_list_exchange_rate() def test_tax_rule_validation(self): frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0") diff --git a/erpnext/stock/doctype/price_list/price_list.py b/erpnext/stock/doctype/price_list/price_list.py index 10abde17eb28..002d3d898eb5 100644 --- a/erpnext/stock/doctype/price_list/price_list.py +++ b/erpnext/stock/doctype/price_list/price_list.py @@ -13,6 +13,9 @@ def validate(self): if not cint(self.buying) and not cint(self.selling): throw(_("Price List must be applicable for Buying or Selling")) + if not self.is_new(): + self.check_impact_on_shopping_cart() + def on_update(self): self.set_default_if_missing() self.update_item_price() @@ -32,6 +35,17 @@ def update_item_price(self): buying=%s, selling=%s, modified=NOW() where price_list=%s""", (self.currency, cint(self.buying), cint(self.selling), self.name)) + def check_impact_on_shopping_cart(self): + "Check if Price List currency change impacts Shopping Cart." + from erpnext.shopping_cart.doctype.shopping_cart_settings.shopping_cart_settings import validate_cart_settings + + doc_before_save = self.get_doc_before_save() + currency_changed = self.currency != doc_before_save.currency + affects_cart = self.name == frappe.get_cached_value("Shopping Cart Settings", None, "price_list") + + if currency_changed and affects_cart: + validate_cart_settings() + def on_trash(self): self.delete_price_list_details_key() From f13315809e2bd56edb62955c7aa7b0b4bc4998bb Mon Sep 17 00:00:00 2001 From: Noah Jacob Date: Thu, 19 Aug 2021 20:28:30 +0530 Subject: [PATCH 669/691] refactor: renamed varint_item_code to variant_item_code (#27025) --- erpnext/manufacturing/doctype/bom/bom.js | 6 +++--- erpnext/manufacturing/doctype/work_order/work_order.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 3f50b41be1b2..e72c8eb40884 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -235,7 +235,7 @@ frappe.ui.form.on("BOM", { reqd: 1, }, { - fieldname: "varint_item_code", + fieldname: "variant_item_code", options: "Item", label: __("Variant Item"), fieldtype: "Link", @@ -287,7 +287,7 @@ frappe.ui.form.on("BOM", { let variant_items = data.items || []; variant_items.forEach(d => { - if (!d.varint_item_code) { + if (!d.variant_item_code) { frappe.throw(__("Select variant item code for the template item {0}", [d.item_code])); } }) @@ -299,7 +299,7 @@ frappe.ui.form.on("BOM", { has_template_rm.forEach(d => { dialog.fields_dict.items.df.data.push({ "item_code": d.item_code, - "varint_item_code": "", + "variant_item_code": "", "qty": d.qty, "source_warehouse": d.source_warehouse, "operation": d.operation diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 282b5d0afe4d..69a4b95c9ac4 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -838,7 +838,7 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): for item in variant_items: args = frappe._dict({ - "item_code": item.get("varint_item_code"), + "item_code": item.get("variant_item_code"), "required_qty": item.get("qty"), "qty": item.get("qty"), # for bom "source_warehouse": item.get("source_warehouse"), @@ -859,7 +859,7 @@ def add_variant_item(variant_items, wo_doc, bom_no, table_name="items"): }, bom_doc) if not args.source_warehouse: - args["source_warehouse"] = get_item_defaults(item.get("varint_item_code"), + args["source_warehouse"] = get_item_defaults(item.get("variant_item_code"), wo_doc.company).default_warehouse args["amount"] = flt(args.get("required_qty")) * flt(args.get("rate")) From 8046d15ef0edf2d9abc708133a39830b9a45b843 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 19 Aug 2021 21:10:30 +0530 Subject: [PATCH 670/691] chore: Comment out obsolete test - Modifying this test means considering extreme edge cases, which seems pointless now --- .../test_shopping_cart_settings.py | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) 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 18a492b2c049..9965e1af672f 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 @@ -16,17 +16,25 @@ def get_cart_settings(self): return frappe.get_doc({"doctype": "Shopping Cart Settings", "company": "_Test Company"}) - def test_exchange_rate_exists(self): - frappe.db.sql("""delete from `tabCurrency Exchange`""") - - cart_settings = self.get_cart_settings() - cart_settings.price_list = "_Test Price List Rest of the World" - self.assertRaises(ShoppingCartSetupError, cart_settings.validate_price_list_exchange_rate) - - from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records as \ - currency_exchange_records - frappe.get_doc(currency_exchange_records[0]).insert() - cart_settings.validate_price_list_exchange_rate() + # NOTE: Exchangrate API has all enabled currencies that ERPNext supports. + # We aren't checking just currency exchange record anymore + # while validating price list currency exchange rate to that of company. + # The API is being used to fetch the rate which again almost always + # gives back a valid value (for valid currencies). + # This makes the test obsolete. + # Commenting because im not sure if there's a better test we can write + + # def test_exchange_rate_exists(self): + # frappe.db.sql("""delete from `tabCurrency Exchange`""") + + # cart_settings = self.get_cart_settings() + # cart_settings.price_list = "_Test Price List Rest of the World" + # self.assertRaises(ShoppingCartSetupError, cart_settings.validate_price_list_exchange_rate) + + # from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records as \ + # currency_exchange_records + # frappe.get_doc(currency_exchange_records[0]).insert() + # cart_settings.validate_price_list_exchange_rate() def test_tax_rule_validation(self): frappe.db.sql("update `tabTax Rule` set use_for_shopping_cart = 0") From 153fe1cdb4a0029d7327c2835007a96395479173 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 20 Aug 2021 11:09:51 +0530 Subject: [PATCH 671/691] refactor: scan barcode field scanning (#26990) --- .../doctype/pos_invoice/pos_invoice.json | 5 ++-- .../purchase_invoice/purchase_invoice.json | 5 ++-- .../doctype/sales_invoice/sales_invoice.json | 3 ++- .../purchase_order/purchase_order.json | 3 ++- erpnext/public/js/controllers/transaction.js | 24 ------------------- .../doctype/sales_order/sales_order.json | 5 ++-- .../doctype/delivery_note/delivery_note.json | 5 ++-- .../material_request/material_request.json | 7 +++--- .../purchase_receipt/purchase_receipt.json | 5 ++-- .../doctype/stock_entry/stock_entry.json | 3 ++- 10 files changed, 24 insertions(+), 41 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 33c3e0432bc6..fcccb39b70c3 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -595,7 +595,8 @@ { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "options": "Barcode" }, { "allow_bulk_edit": 1, @@ -1553,7 +1554,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2021-07-29 13:37:20.636171", + "modified": "2021-08-17 20:13:44.255437", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 7025dd98db38..7822f747f641 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -668,8 +668,7 @@ "fieldname": "scan_barcode", "fieldtype": "Data", "label": "Scan Barcode", - "show_days": 1, - "show_seconds": 1 + "options": "Barcode" }, { "allow_bulk_edit": 1, @@ -1715,7 +1714,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2021-08-07 17:53:14.351439", + "modified": "2021-08-17 20:16:12.737743", "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 b65b101051d3..e317443b91ab 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -692,6 +692,7 @@ { "fieldname": "scan_barcode", "fieldtype": "Data", + "options": "Barcode", "hide_days": 1, "hide_seconds": 1, "label": "Scan Barcode" @@ -2013,7 +2014,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-08-17 19:00:32.230701", + "modified": "2021-08-17 20:16:12.737743", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index bb0ad60cabc0..a55a0b7f9fc2 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -565,6 +565,7 @@ "fieldname": "scan_barcode", "fieldtype": "Data", "label": "Scan Barcode", + "options": "Barcode", "show_days": 1, "show_seconds": 1 }, @@ -1378,7 +1379,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2021-05-30 15:17:53.663648", + "modified": "2021-08-17 20:16:12.737743", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 3c6c34754047..9375e358a970 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -342,30 +342,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.set_dynamic_labels(); this.setup_sms(); this.setup_quality_inspection(); - let scan_barcode_field = this.frm.get_field('scan_barcode'); - if (scan_barcode_field && scan_barcode_field.get_value()) { - scan_barcode_field.set_value(""); - scan_barcode_field.set_new_description(""); - - if (frappe.is_mobile()) { - if (scan_barcode_field.$input_wrapper.find('.input-group').length) return; - - let $input_group = $('
                                                            '); - scan_barcode_field.$input_wrapper.find('.control-input').append($input_group); - $input_group.append(scan_barcode_field.$input); - $(` - - `) - .on('click', '.btn', () => { - frappe.barcode.scan_barcode().then(barcode => { - scan_barcode_field.set_value(barcode); - }); - }) - .appendTo($input_group); - } - } } scan_barcode() { diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index d31db820abc5..38ea5c81d495 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -571,7 +571,8 @@ "fieldtype": "Data", "hide_days": 1, "hide_seconds": 1, - "label": "Scan Barcode" + "label": "Scan Barcode", + "options": "Barcode" }, { "allow_bulk_edit": 1, @@ -1510,7 +1511,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2021-07-08 21:37:44.177493", + "modified": "2021-08-17 20:15:26.531553", "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 dbfeb4a10b7e..958189614fd8 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -515,7 +515,8 @@ { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "options": "Barcode" }, { "allow_bulk_edit": 1, @@ -1305,7 +1306,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2021-07-08 21:37:20.802652", + "modified": "2021-08-17 20:15:50.574966", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json index 4e2d9e617044..cb46a6c3681c 100644 --- a/erpnext/stock/doctype/material_request/material_request.json +++ b/erpnext/stock/doctype/material_request/material_request.json @@ -133,7 +133,8 @@ { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "options": "Barcode" }, { "allow_bulk_edit": 1, @@ -181,7 +182,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nPartially Received\nOrdered\nIssued\nTransferred\nReceived", + "options": "\nDraft\nSubmitted\nStopped\nCancelled\nPending\nPartially Ordered\nOrdered\nIssued\nTransferred\nReceived", "print_hide": 1, "print_width": "100px", "read_only": 1, @@ -314,7 +315,7 @@ "idx": 70, "is_submittable": 1, "links": [], - "modified": "2021-03-31 23:52:55.392512", + "modified": "2021-08-17 20:16:12.737743", "modified_by": "Administrator", "module": "Stock", "name": "Material Request", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 44fb736304f8..1a597343c0a0 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1098,7 +1098,8 @@ { "fieldname": "scan_barcode", "fieldtype": "Data", - "label": "Scan Barcode" + "label": "Scan Barcode", + "options": "Barcode" }, { "fieldname": "billing_address", @@ -1148,7 +1149,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2021-05-25 00:15:12.239017", + "modified": "2021-08-17 20:16:40.849885", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 523d332b8f47..e6ce3c851ff5 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -355,6 +355,7 @@ }, { "fieldname": "scan_barcode", + "options": "Barcode", "fieldtype": "Data", "label": "Scan Barcode" }, @@ -629,7 +630,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-05-26 17:07:58.015737", + "modified": "2021-08-17 20:16:12.737743", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", From c335962827e4927f7ada084e9ba4ab2db15e3eb6 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 20 Aug 2021 11:10:46 +0530 Subject: [PATCH 672/691] refactor!: drop e-invoicing integration from erpnext (#26940) --- .../doctype/sales_invoice/regional/india.js | 2 - .../sales_invoice/regional/india_list.js | 135 --- .../doctype/sales_invoice/sales_invoice.py | 2 - .../sales_invoice/test_sales_invoice.py | 48 - .../gst_e_invoice/gst_e_invoice.html | 162 --- .../gst_e_invoice/gst_e_invoice.json | 24 - erpnext/controllers/accounts_controller.py | 9 - erpnext/hooks.py | 1 - erpnext/patches.txt | 9 +- .../add_company_link_to_einvoice_settings.py | 16 - .../v12_0/add_einvoice_status_field.py | 69 -- ...add_einvoice_summary_report_permissions.py | 18 - .../v12_0/add_ewaybill_validity_field.py | 16 - .../patches/v12_0/setup_einvoice_fields.py | 56 - .../show_einvoice_irn_cancelled_field.py | 12 - .../v13_0/einvoicing_deprecation_warning.py | 9 + .../v14_0}/__init__.py | 0 .../v14_0/delete_einvoicing_doctypes.py | 9 + .../doctype/e_invoice_request_log/__init__.py | 0 .../e_invoice_request_log.js | 8 - .../e_invoice_request_log.json | 102 -- .../e_invoice_request_log.py | 10 - .../test_e_invoice_request_log.py | 10 - .../doctype/e_invoice_settings/__init__.py | 0 .../e_invoice_settings/e_invoice_settings.js | 11 - .../e_invoice_settings.json | 73 -- .../e_invoice_settings/e_invoice_settings.py | 13 - .../test_e_invoice_settings.py | 10 - .../doctype/e_invoice_user/__init__.py | 0 .../e_invoice_user/e_invoice_user.json | 57 -- .../doctype/e_invoice_user/e_invoice_user.py | 10 - erpnext/regional/india/e_invoice/__init__.py | 0 .../india/e_invoice/einv_item_template.json | 31 - .../india/e_invoice/einv_template.json | 110 -- .../india/e_invoice/einv_validation.json | 957 ------------------ erpnext/regional/india/e_invoice/einvoice.js | 292 ------ erpnext/regional/india/setup.py | 50 +- .../report/e_invoice_summary/__init__.py | 0 .../e_invoice_summary/e_invoice_summary.js | 55 - .../e_invoice_summary/e_invoice_summary.json | 28 - .../e_invoice_summary/e_invoice_summary.py | 106 -- 41 files changed, 24 insertions(+), 2506 deletions(-) delete mode 100644 erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html delete mode 100644 erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.json delete mode 100644 erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py delete mode 100644 erpnext/patches/v12_0/add_einvoice_status_field.py delete mode 100644 erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py delete mode 100644 erpnext/patches/v12_0/add_ewaybill_validity_field.py delete mode 100644 erpnext/patches/v12_0/setup_einvoice_fields.py delete mode 100644 erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py create mode 100644 erpnext/patches/v13_0/einvoicing_deprecation_warning.py rename erpnext/{accounts/print_format/gst_e_invoice => patches/v14_0}/__init__.py (100%) create mode 100644 erpnext/patches/v14_0/delete_einvoicing_doctypes.py delete mode 100644 erpnext/regional/doctype/e_invoice_request_log/__init__.py delete mode 100644 erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.js delete mode 100644 erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json delete mode 100644 erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.py delete mode 100644 erpnext/regional/doctype/e_invoice_request_log/test_e_invoice_request_log.py delete mode 100644 erpnext/regional/doctype/e_invoice_settings/__init__.py delete mode 100644 erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js delete mode 100644 erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json delete mode 100644 erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py delete mode 100644 erpnext/regional/doctype/e_invoice_settings/test_e_invoice_settings.py delete mode 100644 erpnext/regional/doctype/e_invoice_user/__init__.py delete mode 100644 erpnext/regional/doctype/e_invoice_user/e_invoice_user.json delete mode 100644 erpnext/regional/doctype/e_invoice_user/e_invoice_user.py delete mode 100644 erpnext/regional/india/e_invoice/__init__.py delete mode 100644 erpnext/regional/india/e_invoice/einv_item_template.json delete mode 100644 erpnext/regional/india/e_invoice/einv_template.json delete mode 100644 erpnext/regional/india/e_invoice/einv_validation.json delete mode 100644 erpnext/regional/india/e_invoice/einvoice.js delete mode 100644 erpnext/regional/report/e_invoice_summary/__init__.py delete mode 100644 erpnext/regional/report/e_invoice_summary/e_invoice_summary.js delete mode 100644 erpnext/regional/report/e_invoice_summary/e_invoice_summary.json delete mode 100644 erpnext/regional/report/e_invoice_summary/e_invoice_summary.py diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india.js b/erpnext/accounts/doctype/sales_invoice/regional/india.js index f54bce8aac7b..6336db16ebca 100644 --- a/erpnext/accounts/doctype/sales_invoice/regional/india.js +++ b/erpnext/accounts/doctype/sales_invoice/regional/india.js @@ -1,8 +1,6 @@ {% include "erpnext/regional/india/taxes.js" %} -{% include "erpnext/regional/india/e_invoice/einvoice.js" %} erpnext.setup_auto_gst_taxation('Sales Invoice'); -erpnext.setup_einvoice_actions('Sales Invoice') frappe.ui.form.on("Sales Invoice", { setup: function(frm) { diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js index f01325d80bd2..d9d6634c3977 100644 --- a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js +++ b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js @@ -36,139 +36,4 @@ frappe.listview_settings['Sales Invoice'].onload = function (list_view) { }; list_view.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false); - - const generate_irns = () => { - const docnames = list_view.get_checked_items(true); - if (docnames && docnames.length) { - frappe.call({ - method: 'erpnext.regional.india.e_invoice.utils.generate_einvoices', - args: { docnames }, - freeze: true, - freeze_message: __('Generating E-Invoices...') - }); - } else { - frappe.msgprint({ - message: __('Please select at least one sales invoice to generate IRN'), - title: __('No Invoice Selected'), - indicator: 'red' - }); - } - }; - - const cancel_irns = () => { - const docnames = list_view.get_checked_items(true); - - const fields = [ - { - "label": "Reason", - "fieldname": "reason", - "fieldtype": "Select", - "reqd": 1, - "default": "1-Duplicate", - "options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"] - }, - { - "label": "Remark", - "fieldname": "remark", - "fieldtype": "Data", - "reqd": 1 - } - ]; - - const d = new frappe.ui.Dialog({ - title: __("Cancel IRN"), - fields: fields, - primary_action: function() { - const data = d.get_values(); - frappe.call({ - method: 'erpnext.regional.india.e_invoice.utils.cancel_irns', - args: { - doctype: list_view.doctype, - docnames, - reason: data.reason.split('-')[0], - remark: data.remark - }, - freeze: true, - freeze_message: __('Cancelling E-Invoices...'), - }); - d.hide(); - }, - primary_action_label: __('Submit') - }); - d.show(); - }; - - let einvoicing_enabled = false; - frappe.db.get_single_value("E Invoice Settings", "enable").then(enabled => { - einvoicing_enabled = enabled; - }); - - list_view.$result.on("change", "input[type=checkbox]", () => { - if (einvoicing_enabled) { - const docnames = list_view.get_checked_items(true); - // show/hide e-invoicing actions when no sales invoices are checked - if (docnames && docnames.length) { - // prevent adding actions twice if e-invoicing action group already exists - if (list_view.page.get_inner_group_button(__('E-Invoicing')).length == 0) { - list_view.page.add_inner_button(__('Generate IRNs'), generate_irns, __('E-Invoicing')); - list_view.page.add_inner_button(__('Cancel IRNs'), cancel_irns, __('E-Invoicing')); - } - } else { - list_view.page.remove_inner_button(__('Generate IRNs'), __('E-Invoicing')); - list_view.page.remove_inner_button(__('Cancel IRNs'), __('E-Invoicing')); - } - } - }); - - 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]), - title: __('Bulk E-Invoice Generation Complete'), - indicator: 'orange' - }); - } - - if (failures && failures.length && user == frappe.session.user) { - let message = ` - Failed to generate IRNs for following ${failures.length} sales invoices: -
                                                              - ${failures.map(d => `
                                                            • ${d.docname}
                                                            • `).join('')} -
                                                            - `; - frappe.msgprint({ - message: message, - title: __('Bulk E-Invoice Generation Complete'), - indicator: 'orange' - }); - } - }); - - frappe.realtime.on("bulk_einvoice_cancellation_complete", (data) => { - const { failures, user, invoices } = data; - - if (invoices.length != failures.length) { - frappe.msgprint({ - message: __('{0} e-invoices cancelled successfully', [invoices.length]), - title: __('Bulk E-Invoice Cancellation Complete'), - indicator: 'orange' - }); - } - - if (failures && failures.length && user == frappe.session.user) { - let message = ` - Failed to cancel IRNs for following ${failures.length} sales invoices: -
                                                              - ${failures.map(d => `
                                                            • ${d.docname}
                                                            • `).join('')} -
                                                            - `; - frappe.msgprint({ - message: message, - title: __('Bulk E-Invoice Cancellation Complete'), - indicator: 'orange' - }); - } - }); }; diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 5fa622856bc1..1cf0df00dbf1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -285,8 +285,6 @@ def check_if_consolidated_invoice(self): def before_cancel(self): self.check_if_consolidated_invoice() - - super(SalesInvoice, self).before_cancel() self.update_time_sheet(None) def on_cancel(self): diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 4d1e0c3e062e..984a65224898 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2027,54 +2027,6 @@ def test_eway_bill_json(self): self.assertEqual(data['billLists'][0]['actualFromStateCode'],7) self.assertEqual(data['billLists'][0]['fromStateCode'],27) - def test_einvoice_submission_without_irn(self): - # init - einvoice_settings = frappe.get_doc('E Invoice Settings') - einvoice_settings.enable = 1 - einvoice_settings.applicable_from = nowdate() - einvoice_settings.append('credentials', { - 'company': '_Test Company', - 'gstin': '27AAECE4835E1ZR', - 'username': 'test', - 'password': 'test' - }) - einvoice_settings.save() - - country = frappe.flags.country - frappe.flags.country = 'India' - - si = make_sales_invoice_for_ewaybill() - self.assertRaises(frappe.ValidationError, si.submit) - - si.irn = 'test_irn' - si.submit() - - # reset - einvoice_settings = frappe.get_doc('E Invoice Settings') - einvoice_settings.enable = 0 - frappe.flags.country = country - - def test_einvoice_json(self): - from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals - - si = get_sales_invoice_for_e_invoice() - si.discount_amount = 100 - si.save() - - einvoice = make_einvoice(si) - self.assertTrue(einvoice['EwbDtls']) - validate_totals(einvoice) - - si.apply_discount_on = 'Net Total' - si.save() - einvoice = make_einvoice(si) - validate_totals(einvoice) - - [d.set('included_in_print_rate', 1) for d in si.taxes] - si.save() - einvoice = make_einvoice(si) - validate_totals(einvoice) - def test_item_tax_net_range(self): item = create_item("T Shirt") 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 deleted file mode 100644 index 7643eca7635d..000000000000 --- a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html +++ /dev/null @@ -1,162 +0,0 @@ -{%- from "templates/print_formats/standard_macros.html" import add_header, render_field, print_value -%} -{%- set einvoice = json.loads(doc.signed_einvoice) -%} - -
                                                            -
                                                            - {% if letter_head and not no_letterhead %} -
                                                            {{ letter_head }}
                                                            - {% endif %} - -
                                                            - {% if print_settings.repeat_header_footer %} - - {% endif %} -
                                                            1. Transaction Details
                                                            -
                                                            -
                                                            -
                                                            -
                                                            -
                                                            {{ einvoice.Irn }}
                                                            -
                                                            -
                                                            -
                                                            -
                                                            {{ einvoice.AckNo }}
                                                            -
                                                            -
                                                            -
                                                            -
                                                            {{ frappe.utils.format_datetime(einvoice.AckDt, "dd/MM/yyyy hh:mm:ss") }}
                                                            -
                                                            -
                                                            -
                                                            -
                                                            {{ einvoice.TranDtls.SupTyp }}
                                                            -
                                                            -
                                                            -
                                                            -
                                                            {{ einvoice.DocDtls.Typ }}
                                                            -
                                                            -
                                                            -
                                                            -
                                                            {{ einvoice.DocDtls.No }}
                                                            -
                                                            -
                                                            -
                                                            - -
                                                            -
                                                            -
                                                            2. Party Details
                                                            -
                                                            - {%- set seller = einvoice.SellerDtls -%} -
                                                            -
                                                            Seller
                                                            -

                                                            {{ seller.Gstin }}

                                                            -

                                                            {{ seller.LglNm }}

                                                            -

                                                            {{ seller.Addr1 }}

                                                            - {%- if seller.Addr2 -%}

                                                            {{ seller.Addr2 }}

                                                            {% endif %} -

                                                            {{ seller.Loc }}

                                                            -

                                                            {{ frappe.db.get_value("Address", doc.company_address, "gst_state") }} - {{ seller.Pin }}

                                                            - - {%- if einvoice.ShipDtls -%} - {%- set shipping = einvoice.ShipDtls -%} -
                                                            Shipping
                                                            -

                                                            {{ shipping.Gstin }}

                                                            -

                                                            {{ shipping.LglNm }}

                                                            -

                                                            {{ shipping.Addr1 }}

                                                            - {%- if shipping.Addr2 -%}

                                                            {{ shipping.Addr2 }}

                                                            {% endif %} -

                                                            {{ shipping.Loc }}

                                                            -

                                                            {{ frappe.db.get_value("Address", doc.shipping_address_name, "gst_state") }} - {{ shipping.Pin }}

                                                            - {% endif %} -
                                                            - {%- set buyer = einvoice.BuyerDtls -%} -
                                                            -
                                                            Buyer
                                                            -

                                                            {{ buyer.Gstin }}

                                                            -

                                                            {{ buyer.LglNm }}

                                                            -

                                                            {{ buyer.Addr1 }}

                                                            - {%- if buyer.Addr2 -%}

                                                            {{ buyer.Addr2 }}

                                                            {% endif %} -

                                                            {{ buyer.Loc }}

                                                            -

                                                            {{ frappe.db.get_value("Address", doc.customer_address, "gst_state") }} - {{ buyer.Pin }}

                                                            -
                                                            -
                                                            -
                                                            -
                                                            3. Item Details
                                                            - - - - - - - - - - - - - - - - - - {% for item in einvoice.ItemList %} - - - - - - - - - - - - - - {% endfor %} - -
                                                            Sr. No.ItemHSN CodeQtyUOMRateDiscountTaxable AmountTax RateOther ChargesTotal
                                                            {{ item.SlNo }}{{ item.PrdDesc }}{{ item.HsnCd }}{{ item.Qty }}{{ item.Unit }}{{ frappe.utils.fmt_money(item.UnitPrice, None, "INR") }}{{ frappe.utils.fmt_money(item.Discount, None, "INR") }}{{ frappe.utils.fmt_money(item.AssAmt, None, "INR") }}{{ item.GstRt + item.CesRt }} %{{ frappe.utils.fmt_money(0, None, "INR") }}{{ frappe.utils.fmt_money(item.TotItemVal, None, "INR") }}
                                                            -
                                                            -
                                                            -
                                                            4. Value Details
                                                            - - - - - - - - - - - - - - - - - {%- set value_details = einvoice.ValDtls -%} - - - - - - - - - - - - - -
                                                            Taxable AmountCGSTSGSTIGSTCESSState CESSDiscountOther ChargesRound OffTotal Value
                                                            {{ frappe.utils.fmt_money(value_details.AssVal, None, "INR") }}{{ frappe.utils.fmt_money(value_details.CgstVal, None, "INR") }}{{ frappe.utils.fmt_money(value_details.SgstVal, None, "INR") }}{{ frappe.utils.fmt_money(value_details.IgstVal, None, "INR") }}{{ frappe.utils.fmt_money(value_details.CesVal, None, "INR") }}{{ frappe.utils.fmt_money(0, None, "INR") }}{{ frappe.utils.fmt_money(value_details.Discount, None, "INR") }}{{ frappe.utils.fmt_money(value_details.OthChrg, None, "INR") }}{{ frappe.utils.fmt_money(value_details.RndOffAmt, None, "INR") }}{{ frappe.utils.fmt_money(value_details.TotInvVal, None, "INR") }}
                                                            -
                                                            -
                                                            diff --git a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.json b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.json deleted file mode 100644 index 1001199a0921..000000000000 --- a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "align_labels_right": 1, - "creation": "2020-10-10 18:01:21.032914", - "custom_format": 0, - "default_print_language": "en-US", - "disabled": 1, - "doc_type": "Sales Invoice", - "docstatus": 0, - "doctype": "Print Format", - "font": "Default", - "html": "", - "idx": 0, - "line_breaks": 1, - "modified": "2020-10-23 19:54:40.634936", - "modified_by": "Administrator", - "module": "Accounts", - "name": "GST E-Invoice", - "owner": "Administrator", - "print_format_builder": 0, - "print_format_type": "Jinja", - "raw_printing": 0, - "show_section_headings": 1, - "standard": "Yes" -} \ No newline at end of file diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 4c243d0cc466..8addbeb98f8b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -135,14 +135,9 @@ def validate(self): validate_regional(self) - validate_einvoice_fields(self) - if self.doctype != 'Material Request': apply_pricing_rule_on_transaction(self) - def before_cancel(self): - validate_einvoice_fields(self) - def on_trash(self): # delete sl and gl entries on deletion of transaction if frappe.db.get_single_value('Accounts Settings', 'delete_linked_ledger_entries'): @@ -1975,7 +1970,3 @@ def validate_quantity(child_item, d): @erpnext.allow_regional def validate_regional(doc): pass - -@erpnext.allow_regional -def validate_einvoice_fields(doc): - pass diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 1aaf4ccd33f0..73831bf9af09 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -436,7 +436,6 @@ 'erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts': 'erpnext.regional.india.utils.get_regional_round_off_accounts', '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.stock.doctype.item.item.set_item_tax_from_hsn_code': 'erpnext.regional.india.utils.set_item_tax_from_hsn_code' }, diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 776b41dcc2b1..b86c236a7fb3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -214,7 +214,6 @@ erpnext.patches.v13_0.delete_old_sales_reports execute:frappe.delete_doc_if_exists("DocType", "Bank Reconciliation") erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #22-06-2020 erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020 -execute:frappe.reload_doc("regional", "doctype", "e_invoice_settings") erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020 erpnext.patches.v13_0.loyalty_points_entry_for_pos_invoice #22-07-2020 erpnext.patches.v12_0.add_taxjar_integration_field @@ -237,7 +236,6 @@ erpnext.patches.v13_0.set_app_name erpnext.patches.v13_0.print_uom_after_quantity_patch erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail -erpnext.patches.v12_0.setup_einvoice_fields #2020-12-02 erpnext.patches.v13_0.updates_for_multi_currency_payroll erpnext.patches.v13_0.update_reason_for_resignation_in_employee execute:frappe.delete_doc("Report", "Quoted Item Comparison") @@ -259,14 +257,11 @@ erpnext.patches.v12_0.add_state_code_for_ladakh erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes erpnext.patches.v12_0.update_vehicle_no_reqd_condition -erpnext.patches.v12_0.add_einvoice_status_field #2021-03-17 -erpnext.patches.v12_0.add_einvoice_summary_report_permissions erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae erpnext.patches.v13_0.setup_uae_vat_fields execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext') -erpnext.patches.v12_0.add_company_link_to_einvoice_settings erpnext.patches.v13_0.rename_discharge_date_in_ip_record erpnext.patches.v12_0.create_taxable_value_field erpnext.patches.v12_0.add_gst_category_in_delivery_note @@ -277,7 +272,6 @@ erpnext.patches.v12_0.add_document_type_field_for_italy_einvoicing erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021 erpnext.patches.v13_0.update_shipment_status erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting -erpnext.patches.v12_0.add_ewaybill_validity_field erpnext.patches.v13_0.germany_make_custom_fields erpnext.patches.v13_0.germany_fill_debtor_creditor_number erpnext.patches.v13_0.set_pos_closing_as_failed @@ -294,10 +288,11 @@ 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 #2021-08-16 erpnext.patches.v13_0.update_tds_check_field #3 erpnext.patches.v13_0.add_custom_field_for_south_africa #2 erpnext.patches.v13_0.update_recipient_email_digest erpnext.patches.v13_0.shopify_deprecation_warning +erpnext.patches.v13_0.einvoicing_deprecation_warning +erpnext.patches.v14_0.delete_einvoicing_doctypes 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 deleted file mode 100644 index c2ed6c288fed..000000000000 --- a/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company or not frappe.db.count('E Invoice User'): - return - - frappe.reload_doc("regional", "doctype", "e_invoice_user") - for creds in frappe.db.get_all('E Invoice User', fields=['name', 'gstin']): - company_name = frappe.db.sql(""" - select dl.link_name from `tabAddress` a, `tabDynamic Link` dl - 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]) diff --git a/erpnext/patches/v12_0/add_einvoice_status_field.py b/erpnext/patches/v12_0/add_einvoice_status_field.py deleted file mode 100644 index 2dfd30714c8c..000000000000 --- a/erpnext/patches/v12_0/add_einvoice_status_field.py +++ /dev/null @@ -1,69 +0,0 @@ -from __future__ import unicode_literals -import json -import frappe -from frappe.custom.doctype.custom_field.custom_field import create_custom_fields - -def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company: - return - - # move hidden einvoice fields to a different section - custom_fields = { - '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', - 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', - no_copy=1, print_hide=1, read_only=1), - - dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice', - no_copy=1, print_hide=1, read_only=1), - - dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code', - no_copy=1, print_hide=1, read_only=1), - - dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image', - options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1), - - dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON', - hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1) - ] - } - create_custom_fields(custom_fields, update=True) - - if frappe.db.exists('E Invoice Settings') and frappe.db.get_single_value('E Invoice Settings', 'enable'): - frappe.db.sql(''' - UPDATE `tabSales Invoice` SET einvoice_status = 'Pending' - WHERE - posting_date >= '2021-04-01' - AND ifnull(irn, '') = '' - AND ifnull(`billing_address_gstin`, '') != ifnull(`company_gstin`, '') - AND ifnull(gst_category, '') in ('Registered Regular', 'SEZ', 'Overseas', 'Deemed Export') - ''') - - # set appropriate statuses - frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Generated' - WHERE ifnull(irn, '') != '' AND ifnull(irn_cancelled, 0) = 0''') - - frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Cancelled' - WHERE ifnull(irn_cancelled, 0) = 1''') - - # set correct acknowledgement in e-invoices - einvoices = frappe.get_all('Sales Invoice', {'irn': ['is', 'set']}, ['name', 'signed_einvoice']) - - if einvoices: - for inv in einvoices: - signed_einvoice = inv.get('signed_einvoice') - 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) diff --git a/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py b/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py deleted file mode 100644 index c1c11e260069..000000000000 --- a/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import unicode_literals -import frappe - -def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company: - return - - if frappe.db.exists('Report', 'E-Invoice Summary') and \ - not frappe.db.get_value('Custom Role', dict(report='E-Invoice Summary')): - frappe.get_doc(dict( - doctype='Custom Role', - report='E-Invoice Summary', - roles= [ - dict(role='Accounts User'), - dict(role='Accounts Manager') - ] - )).insert() diff --git a/erpnext/patches/v12_0/add_ewaybill_validity_field.py b/erpnext/patches/v12_0/add_ewaybill_validity_field.py deleted file mode 100644 index f29b71437e8c..000000000000 --- a/erpnext/patches/v12_0/add_ewaybill_validity_field.py +++ /dev/null @@ -1,16 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.custom.doctype.custom_field.custom_field import create_custom_fields - -def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company: - return - - custom_fields = { - 'Sales Invoice': [ - 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') - ] - } - create_custom_fields(custom_fields, update=True) diff --git a/erpnext/patches/v12_0/setup_einvoice_fields.py b/erpnext/patches/v12_0/setup_einvoice_fields.py deleted file mode 100644 index 82b14fc9d604..000000000000 --- a/erpnext/patches/v12_0/setup_einvoice_fields.py +++ /dev/null @@ -1,56 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe.custom.doctype.custom_field.custom_field import create_custom_fields -from erpnext.regional.india.setup import add_permissions, add_print_formats - -def execute(): - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company: - return - - frappe.reload_doc("custom", "doctype", "custom_field") - frappe.reload_doc("regional", "doctype", "e_invoice_settings") - custom_fields = { - '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, - depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'), - - dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1, - depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'), - - dict(fieldname='signed_einvoice', fieldtype='Code', options='JSON', hidden=1, no_copy=1, print_hide=1, read_only=1), - - dict(fieldname='signed_qr_code', fieldtype='Code', options='JSON', hidden=1, no_copy=1, print_hide=1, read_only=1), - - dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, no_copy=1, print_hide=1, read_only=1) - ] - } - create_custom_fields(custom_fields, update=True) - add_permissions() - add_print_formats() - - einvoice_cond = 'in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category)' - t = { - 'mode_of_transport': [{'default': None}], - 'distance': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.transporter'}], - 'gst_vehicle_type': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.mode_of_transport == "Road"'}], - 'lr_date': [{'mandatory_depends_on': f'eval:{einvoice_cond} && in_list(["Air", "Ship", "Rail"], doc.mode_of_transport)'}], - 'lr_no': [{'mandatory_depends_on': f'eval:{einvoice_cond} && in_list(["Air", "Ship", "Rail"], doc.mode_of_transport)'}], - 'vehicle_no': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.mode_of_transport == "Road"'}], - 'ewaybill': [ - {'read_only_depends_on': 'eval:doc.irn && doc.ewaybill'}, - {'depends_on': 'eval:((doc.docstatus === 1 || doc.ewaybill) && doc.eway_bill_cancelled === 0)'} - ] - } - - for field, conditions in t.items(): - for c in conditions: - [(prop, value)] = c.items() - frappe.db.set_value('Custom Field', { 'fieldname': field }, prop, value) diff --git a/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py b/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py deleted file mode 100644 index 2319c17b34c6..000000000000 --- a/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py +++ /dev/null @@ -1,12 +0,0 @@ -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/patches/v13_0/einvoicing_deprecation_warning.py b/erpnext/patches/v13_0/einvoicing_deprecation_warning.py new file mode 100644 index 000000000000..e123a55f5ab0 --- /dev/null +++ b/erpnext/patches/v13_0/einvoicing_deprecation_warning.py @@ -0,0 +1,9 @@ +import click + + +def execute(): + click.secho( + "Indian E-Invoicing 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/erpnext_gst_compliance", + fg="yellow", + ) diff --git a/erpnext/accounts/print_format/gst_e_invoice/__init__.py b/erpnext/patches/v14_0/__init__.py similarity index 100% rename from erpnext/accounts/print_format/gst_e_invoice/__init__.py rename to erpnext/patches/v14_0/__init__.py diff --git a/erpnext/patches/v14_0/delete_einvoicing_doctypes.py b/erpnext/patches/v14_0/delete_einvoicing_doctypes.py new file mode 100644 index 000000000000..b77d2440eb14 --- /dev/null +++ b/erpnext/patches/v14_0/delete_einvoicing_doctypes.py @@ -0,0 +1,9 @@ +import frappe + +def execute(): + frappe.delete_doc('DocType', 'E Invoice Settings', ignore_missing=True) + frappe.delete_doc('DocType', 'E Invoice User', ignore_missing=True) + frappe.delete_doc('Report', 'E-Invoice Summary', ignore_missing=True) + frappe.delete_doc('Print Format', 'GST E-Invoice', ignore_missing=True) + frappe.delete_doc('Custom Field', 'Sales Invoice-eway_bill_cancelled', ignore_missing=True) + frappe.delete_doc('Custom Field', 'Sales Invoice-irn_cancelled', ignore_missing=True) \ No newline at end of file diff --git a/erpnext/regional/doctype/e_invoice_request_log/__init__.py b/erpnext/regional/doctype/e_invoice_request_log/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.js b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.js deleted file mode 100644 index 7b7ba964e5ef..000000000000 --- a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.js +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('E Invoice Request Log', { - // refresh: function(frm) { - - // } -}); diff --git a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json deleted file mode 100644 index 3034370feac2..000000000000 --- a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "actions": [], - "autoname": "EINV-REQ-.#####", - "creation": "2020-12-08 12:54:08.175992", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "user", - "url", - "headers", - "response", - "column_break_7", - "timestamp", - "reference_invoice", - "data" - ], - "fields": [ - { - "fieldname": "user", - "fieldtype": "Link", - "label": "User", - "options": "User" - }, - { - "fieldname": "reference_invoice", - "fieldtype": "Data", - "label": "Reference Invoice" - }, - { - "fieldname": "headers", - "fieldtype": "Code", - "label": "Headers", - "options": "JSON" - }, - { - "fieldname": "data", - "fieldtype": "Code", - "label": "Data", - "options": "JSON" - }, - { - "default": "Now", - "fieldname": "timestamp", - "fieldtype": "Datetime", - "label": "Timestamp" - }, - { - "fieldname": "response", - "fieldtype": "Code", - "label": "Response", - "options": "JSON" - }, - { - "fieldname": "url", - "fieldtype": "Data", - "label": "URL" - }, - { - "fieldname": "column_break_7", - "fieldtype": "Column Break" - } - ], - "index_web_pages_for_search": 1, - "links": [], - "modified": "2021-01-13 12:06:57.253111", - "modified_by": "Administrator", - "module": "Regional", - "name": "E Invoice Request Log", - "owner": "Administrator", - "permissions": [ - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1 - }, - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "share": 1 - }, - { - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "share": 1 - } - ], - "sort_field": "modified", - "sort_order": "DESC" -} \ No newline at end of file diff --git a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.py b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.py deleted file mode 100644 index 9150bdd92607..000000000000 --- a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 EInvoiceRequestLog(Document): - pass diff --git a/erpnext/regional/doctype/e_invoice_request_log/test_e_invoice_request_log.py b/erpnext/regional/doctype/e_invoice_request_log/test_e_invoice_request_log.py deleted file mode 100644 index c84e9a249bdc..000000000000 --- a/erpnext/regional/doctype/e_invoice_request_log/test_e_invoice_request_log.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -# import frappe -import unittest - -class TestEInvoiceRequestLog(unittest.TestCase): - pass diff --git a/erpnext/regional/doctype/e_invoice_settings/__init__.py b/erpnext/regional/doctype/e_invoice_settings/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js deleted file mode 100644 index 54e488610df8..000000000000 --- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('E Invoice Settings', { - refresh(frm) { - 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`]) - ); - } -}); diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json deleted file mode 100644 index 68ed3391d04d..000000000000 --- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json +++ /dev/null @@ -1,73 +0,0 @@ -{ - "actions": [], - "creation": "2020-09-24 16:23:16.235722", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "enable", - "section_break_2", - "sandbox_mode", - "applicable_from", - "credentials", - "auth_token", - "token_expiry" - ], - "fields": [ - { - "default": "0", - "fieldname": "enable", - "fieldtype": "Check", - "label": "Enable" - }, - { - "depends_on": "enable", - "fieldname": "section_break_2", - "fieldtype": "Section Break" - }, - { - "fieldname": "auth_token", - "fieldtype": "Data", - "hidden": 1, - "read_only": 1 - }, - { - "fieldname": "token_expiry", - "fieldtype": "Datetime", - "hidden": 1, - "read_only": 1 - }, - { - "fieldname": "credentials", - "fieldtype": "Table", - "label": "Credentials", - "mandatory_depends_on": "enable", - "options": "E Invoice User" - }, - { - "default": "0", - "fieldname": "sandbox_mode", - "fieldtype": "Check", - "label": "Sandbox Mode" - }, - { - "fieldname": "applicable_from", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Applicable From", - "reqd": 1 - } - ], - "index_web_pages_for_search": 1, - "issingle": 1, - "links": [], - "modified": "2021-03-30 12:26:25.538294", - "modified_by": "Administrator", - "module": "Regional", - "name": "E Invoice Settings", - "owner": "Administrator", - "permissions": [], - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py deleted file mode 100644 index 4f6b3eca7a63..000000000000 --- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- 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 import _ -from frappe.model.document import Document - -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/e_invoice_settings/test_e_invoice_settings.py b/erpnext/regional/doctype/e_invoice_settings/test_e_invoice_settings.py deleted file mode 100644 index a11ce63ee6c3..000000000000 --- a/erpnext/regional/doctype/e_invoice_settings/test_e_invoice_settings.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -# import frappe -import unittest - -class TestEInvoiceSettings(unittest.TestCase): - pass diff --git a/erpnext/regional/doctype/e_invoice_user/__init__.py b/erpnext/regional/doctype/e_invoice_user/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json deleted file mode 100644 index a65b1ca7ca87..000000000000 --- a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "actions": [], - "creation": "2020-12-22 15:02:46.229474", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "company", - "gstin", - "username", - "password" - ], - "fields": [ - { - "fieldname": "gstin", - "fieldtype": "Data", - "in_list_view": 1, - "label": "GSTIN", - "reqd": 1 - }, - { - "fieldname": "username", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Username", - "reqd": 1 - }, - { - "fieldname": "password", - "fieldtype": "Password", - "in_list_view": 1, - "label": "Password", - "reqd": 1 - }, - { - "fieldname": "company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Company", - "options": "Company", - "reqd": 1 - } - ], - "index_web_pages_for_search": 1, - "istable": 1, - "links": [], - "modified": "2021-03-22 12:16:56.365616", - "modified_by": "Administrator", - "module": "Regional", - "name": "E Invoice User", - "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/regional/doctype/e_invoice_user/e_invoice_user.py b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py deleted file mode 100644 index 056c54f069d6..000000000000 --- a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- 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 EInvoiceUser(Document): - pass diff --git a/erpnext/regional/india/e_invoice/__init__.py b/erpnext/regional/india/e_invoice/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/erpnext/regional/india/e_invoice/einv_item_template.json b/erpnext/regional/india/e_invoice/einv_item_template.json deleted file mode 100644 index 78e56518dffc..000000000000 --- a/erpnext/regional/india/e_invoice/einv_item_template.json +++ /dev/null @@ -1,31 +0,0 @@ -{{ - "SlNo": "{item.sr_no}", - "PrdDesc": "{item.description}", - "IsServc": "{item.is_service_item}", - "HsnCd": "{item.gst_hsn_code}", - "Barcde": "{item.barcode}", - "Unit": "{item.uom}", - "Qty": "{item.qty}", - "FreeQty": "{item.free_qty}", - "UnitPrice": "{item.unit_rate}", - "TotAmt": "{item.gross_amount}", - "Discount": "{item.discount_amount}", - "AssAmt": "{item.taxable_value}", - "PrdSlNo": "{item.serial_no}", - "GstRt": "{item.tax_rate}", - "IgstAmt": "{item.igst_amount}", - "CgstAmt": "{item.cgst_amount}", - "SgstAmt": "{item.sgst_amount}", - "CesRt": "{item.cess_rate}", - "CesAmt": "{item.cess_amount}", - "CesNonAdvlAmt": "{item.cess_nadv_amount}", - "StateCesRt": "{item.state_cess_rate}", - "StateCesAmt": "{item.state_cess_amount}", - "StateCesNonAdvlAmt": "{item.state_cess_nadv_amount}", - "OthChrg": "{item.other_charges}", - "TotItemVal": "{item.total_value}", - "BchDtls": {{ - "Nm": "{item.batch_no}", - "ExpDt": "{item.batch_expiry_date}" - }} -}} \ No newline at end of file diff --git a/erpnext/regional/india/e_invoice/einv_template.json b/erpnext/regional/india/e_invoice/einv_template.json deleted file mode 100644 index 60f490d61669..000000000000 --- a/erpnext/regional/india/e_invoice/einv_template.json +++ /dev/null @@ -1,110 +0,0 @@ -{{ - "Version": "1.1", - "TranDtls": {{ - "TaxSch": "{transaction_details.tax_scheme}", - "SupTyp": "{transaction_details.supply_type}", - "RegRev": "{transaction_details.reverse_charge}", - "EcmGstin": "{transaction_details.ecom_gstin}", - "IgstOnIntra": "{transaction_details.igst_on_intra}" - }}, - "DocDtls": {{ - "Typ": "{doc_details.invoice_type}", - "No": "{doc_details.invoice_name}", - "Dt": "{doc_details.invoice_date}" - }}, - "SellerDtls": {{ - "Gstin": "{seller_details.gstin}", - "LglNm": "{seller_details.legal_name}", - "TrdNm": "{seller_details.trade_name}", - "Loc": "{seller_details.location}", - "Pin": "{seller_details.pincode}", - "Stcd": "{seller_details.state_code}", - "Addr1": "{seller_details.address_line1}", - "Addr2": "{seller_details.address_line2}", - "Ph": "{seller_details.phone}", - "Em": "{seller_details.email}" - }}, - "BuyerDtls": {{ - "Gstin": "{buyer_details.gstin}", - "LglNm": "{buyer_details.legal_name}", - "TrdNm": "{buyer_details.trade_name}", - "Addr1": "{buyer_details.address_line1}", - "Addr2": "{buyer_details.address_line2}", - "Loc": "{buyer_details.location}", - "Pin": "{buyer_details.pincode}", - "Stcd": "{buyer_details.state_code}", - "Ph": "{buyer_details.phone}", - "Em": "{buyer_details.email}", - "Pos": "{buyer_details.place_of_supply}" - }}, - "DispDtls": {{ - "Nm": "{dispatch_details.company_name}", - "Addr1": "{dispatch_details.address_line1}", - "Addr2": "{dispatch_details.address_line2}", - "Loc": "{dispatch_details.location}", - "Pin": "{dispatch_details.pincode}", - "Stcd": "{dispatch_details.state_code}" - }}, - "ShipDtls": {{ - "Gstin": "{shipping_details.gstin}", - "LglNm": "{shipping_details.legal_name}", - "TrdNm": "{shipping_details.trader_name}", - "Addr1": "{shipping_details.address_line1}", - "Addr2": "{shipping_details.address_line2}", - "Loc": "{shipping_details.location}", - "Pin": "{shipping_details.pincode}", - "Stcd": "{shipping_details.state_code}" - }}, - "ItemList": [ - {item_list} - ], - "ValDtls": {{ - "AssVal": "{invoice_value_details.base_total}", - "CgstVal": "{invoice_value_details.total_cgst_amt}", - "SgstVal": "{invoice_value_details.total_sgst_amt}", - "IgstVal": "{invoice_value_details.total_igst_amt}", - "CesVal": "{invoice_value_details.total_cess_amt}", - "Discount": "{invoice_value_details.invoice_discount_amt}", - "RndOffAmt": "{invoice_value_details.round_off}", - "OthChrg": "{invoice_value_details.total_other_charges}", - "TotInvVal": "{invoice_value_details.base_grand_total}", - "TotInvValFc": "{invoice_value_details.grand_total}" - }}, - "PayDtls": {{ - "Nm": "{payment_details.payee_name}", - "AccDet": "{payment_details.account_no}", - "Mode": "{payment_details.mode_of_payment}", - "FinInsBr": "{payment_details.ifsc_code}", - "PayTerm": "{payment_details.terms}", - "PaidAmt": "{payment_details.paid_amount}", - "PaymtDue": "{payment_details.outstanding_amount}" - }}, - "RefDtls": {{ - "DocPerdDtls": {{ - "InvStDt": "{period_details.start_date}", - "InvEndDt": "{period_details.end_date}" - }}, - "PrecDocDtls": [{{ - "InvNo": "{prev_doc_details.invoice_name}", - "InvDt": "{prev_doc_details.invoice_date}" - }}] - }}, - "ExpDtls": {{ - "ShipBNo": "{export_details.bill_no}", - "ShipBDt": "{export_details.bill_date}", - "Port": "{export_details.port}", - "ForCur": "{export_details.foreign_curr_code}", - "CntCode": "{export_details.country_code}", - "ExpDuty": "{export_details.export_duty}" - }}, - "EwbDtls": {{ - "TransId": "{eway_bill_details.gstin}", - "TransName": "{eway_bill_details.name}", - "TransMode": "{eway_bill_details.mode_of_transport}", - "Distance": "{eway_bill_details.distance}", - "TransDocNo": "{eway_bill_details.document_name}", - "TransDocDt": "{eway_bill_details.document_date}", - "VehNo": "{eway_bill_details.vehicle_no}", - "VehType": "{eway_bill_details.vehicle_type}" - }} -}} \ No newline at end of file diff --git a/erpnext/regional/india/e_invoice/einv_validation.json b/erpnext/regional/india/e_invoice/einv_validation.json deleted file mode 100644 index f4a3542a60e2..000000000000 --- a/erpnext/regional/india/e_invoice/einv_validation.json +++ /dev/null @@ -1,957 +0,0 @@ -{ - "Version": { - "type": "string", - "minLength": 1, - "maxLength": 6, - "description": "Version of the schema" - }, - "Irn": { - "type": "string", - "minLength": 64, - "maxLength": 64, - "description": "Invoice Reference Number" - }, - "TranDtls": { - "type": "object", - "properties": { - "TaxSch": { - "type": "string", - "minLength": 3, - "maxLength": 10, - "enum": ["GST"], - "description": "GST- Goods and Services Tax Scheme" - }, - "SupTyp": { - "type": "string", - "minLength": 3, - "maxLength": 10, - "enum": ["B2B", "SEZWP", "SEZWOP", "EXPWP", "EXPWOP", "DEXP"], - "description": "Type of Supply: B2B-Business to Business, SEZWP - SEZ with payment, SEZWOP - SEZ without payment, EXPWP - Export with Payment, EXPWOP - Export without payment,DEXP - Deemed Export" - }, - "RegRev": { - "type": "string", - "minLength": 1, - "maxLength": 1, - "enum": ["Y", "N"], - "description": "Y- whether the tax liability is payable under reverse charge" - }, - "EcmGstin": { - "type": "string", - "minLength": 15, - "maxLength": 15, - "pattern": "([0-9]{2}[0-9A-Z]{13})", - "description": "E-Commerce GSTIN", - "validationMsg": "E-Commerce GSTIN is invalid" - }, - "IgstOnIntra": { - "type": "string", - "minLength": 1, - "maxLength": 1, - "enum": ["Y", "N"], - "description": "Y- indicates the supply is intra state but chargeable to IGST" - } - }, - "required": ["TaxSch", "SupTyp"] - }, - "DocDtls": { - "type": "object", - "properties": { - "Typ": { - "type": "string", - "minLength": 3, - "maxLength": 3, - "enum": ["INV", "CRN", "DBN"], - "description": "Document Type" - }, - "No": { - "type": "string", - "minLength": 1, - "maxLength": 16, - "pattern": "^([A-Z1-9]{1}[A-Z0-9/-]{0,15})$", - "description": "Document Number", - "validationMsg": "Document Number should not be starting with 0, / and -" - }, - "Dt": { - "type": "string", - "minLength": 10, - "maxLength": 10, - "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", - "description": "Document Date" - } - }, - "required": ["Typ", "No", "Dt"] - }, - "SellerDtls": { - "type": "object", - "properties": { - "Gstin": { - "type": "string", - "minLength": 15, - "maxLength": 15, - "pattern": "([0-9]{2}[0-9A-Z]{13})", - "description": "Supplier GSTIN", - "validationMsg": "Company GSTIN is invalid" - }, - "LglNm": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Legal Name" - }, - "TrdNm": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Tradename" - }, - "Addr1": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Address Line 1" - }, - "Addr2": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Address Line 2" - }, - "Loc": { - "type": "string", - "minLength": 3, - "maxLength": 50, - "description": "Location" - }, - "Pin": { - "type": "number", - "minimum": 100000, - "maximum": 999999, - "description": "Pincode" - }, - "Stcd": { - "type": "string", - "minLength": 1, - "maxLength": 2, - "description": "Supplier State Code" - }, - "Ph": { - "type": "string", - "minLength": 6, - "maxLength": 12, - "description": "Phone" - }, - "Em": { - "type": "string", - "minLength": 6, - "maxLength": 100, - "description": "Email-Id" - } - }, - "required": ["Gstin", "LglNm", "Addr1", "Loc", "Pin", "Stcd"] - }, - "BuyerDtls": { - "type": "object", - "properties": { - "Gstin": { - "type": "string", - "minLength": 3, - "maxLength": 15, - "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$", - "description": "Buyer GSTIN", - "validationMsg": "Customer GSTIN is invalid" - }, - "LglNm": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Legal Name" - }, - "TrdNm": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Trade Name" - }, - "Pos": { - "type": "string", - "minLength": 1, - "maxLength": 2, - "description": "Place of Supply State code" - }, - "Addr1": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Address Line 1" - }, - "Addr2": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Address Line 2" - }, - "Loc": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Location" - }, - "Pin": { - "type": "number", - "minimum": 100000, - "maximum": 999999, - "description": "Pincode" - }, - "Stcd": { - "type": "string", - "minLength": 1, - "maxLength": 2, - "description": "Buyer State Code" - }, - "Ph": { - "type": "string", - "minLength": 6, - "maxLength": 12, - "description": "Phone" - }, - "Em": { - "type": "string", - "minLength": 6, - "maxLength": 100, - "description": "Email-Id" - } - }, - "required": ["Gstin", "LglNm", "Pos", "Addr1", "Loc", "Stcd"] - }, - "DispDtls": { - "type": "object", - "properties": { - "Nm": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Dispatch Address Name" - }, - "Addr1": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Address Line 1" - }, - "Addr2": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Address Line 2" - }, - "Loc": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Location" - }, - "Pin": { - "type": "number", - "minimum": 100000, - "maximum": 999999, - "description": "Pincode" - }, - "Stcd": { - "type": "string", - "minLength": 1, - "maxLength": 2, - "description": "State Code" - } - }, - "required": ["Nm", "Addr1", "Loc", "Pin", "Stcd"] - }, - "ShipDtls": { - "type": "object", - "properties": { - "Gstin": { - "type": "string", - "maxLength": 15, - "minLength": 3, - "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$", - "description": "Shipping Address GSTIN", - "validationMsg": "Shipping Address GSTIN is invalid" - }, - "LglNm": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Legal Name" - }, - "TrdNm": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Trade Name" - }, - "Addr1": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Address Line 1" - }, - "Addr2": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Address Line 2" - }, - "Loc": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Location" - }, - "Pin": { - "type": "number", - "minimum": 100000, - "maximum": 999999, - "description": "Pincode" - }, - "Stcd": { - "type": "string", - "minLength": 1, - "maxLength": 2, - "description": "State Code" - } - }, - "required": ["LglNm", "Addr1", "Loc", "Pin", "Stcd"] - }, - "ItemList": { - "type": "Array", - "properties": { - "SlNo": { - "type": "string", - "minLength": 1, - "maxLength": 6, - "description": "Serial No. of Item" - }, - "PrdDesc": { - "type": "string", - "minLength": 3, - "maxLength": 300, - "description": "Item Name" - }, - "IsServc": { - "type": "string", - "minLength": 1, - "maxLength": 1, - "enum": ["Y", "N"], - "description": "Is Service Item" - }, - "HsnCd": { - "type": "string", - "minLength": 4, - "maxLength": 8, - "description": "HSN Code" - }, - "Barcde": { - "type": "string", - "minLength": 3, - "maxLength": 30, - "description": "Barcode" - }, - "Qty": { - "type": "number", - "minimum": 0, - "maximum": 9999999999.999, - "description": "Quantity" - }, - "FreeQty": { - "type": "number", - "minimum": 0, - "maximum": 9999999999.999, - "description": "Free Quantity" - }, - "Unit": { - "type": "string", - "minLength": 3, - "maxLength": 8, - "description": "UOM" - }, - "UnitPrice": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.999, - "description": "Rate" - }, - "TotAmt": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "Gross Amount" - }, - "Discount": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "Discount" - }, - "PreTaxVal": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "Pre tax value" - }, - "AssAmt": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "Taxable Value" - }, - "GstRt": { - "type": "number", - "minimum": 0, - "maximum": 999.999, - "description": "GST Rate" - }, - "IgstAmt": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "IGST Amount" - }, - "CgstAmt": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "CGST Amount" - }, - "SgstAmt": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "SGST Amount" - }, - "CesRt": { - "type": "number", - "minimum": 0, - "maximum": 999.999, - "description": "Cess Rate" - }, - "CesAmt": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "Cess Amount (Advalorem)" - }, - "CesNonAdvlAmt": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "Cess Amount (Non-Advalorem)" - }, - "StateCesRt": { - "type": "number", - "minimum": 0, - "maximum": 999.999, - "description": "State CESS Rate" - }, - "StateCesAmt": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "State CESS Amount" - }, - "StateCesNonAdvlAmt": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "State CESS Amount (Non Advalorem)" - }, - "OthChrg": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "Other Charges" - }, - "TotItemVal": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "Total Item Value" - }, - "OrdLineRef": { - "type": "string", - "minLength": 1, - "maxLength": 50, - "description": "Order line reference" - }, - "OrgCntry": { - "type": "string", - "minLength": 2, - "maxLength": 2, - "description": "Origin Country" - }, - "PrdSlNo": { - "type": "string", - "minLength": 1, - "maxLength": 20, - "description": "Serial number" - }, - "BchDtls": { - "type": "object", - "properties": { - "Nm": { - "type": "string", - "minLength": 3, - "maxLength": 20, - "description": "Batch number" - }, - "ExpDt": { - "type": "string", - "maxLength": 10, - "minLength": 10, - "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", - "description": "Batch Expiry Date" - }, - "WrDt": { - "type": "string", - "maxLength": 10, - "minLength": 10, - "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", - "description": "Warranty Date" - } - }, - "required": ["Nm"] - }, - "AttribDtls": { - "type": "Array", - "Attribute": { - "type": "object", - "properties": { - "Nm": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Attribute name of the item" - }, - "Val": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Attribute value of the item" - } - } - } - } - }, - "required": [ - "SlNo", - "IsServc", - "HsnCd", - "UnitPrice", - "TotAmt", - "AssAmt", - "GstRt", - "TotItemVal" - ] - }, - "ValDtls": { - "type": "object", - "properties": { - "AssVal": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Total Assessable value of all items" - }, - "CgstVal": { - "type": "number", - "maximum": 99999999999999.99, - "minimum": 0, - "description": "Total CGST value of all items" - }, - "SgstVal": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Total SGST value of all items" - }, - "IgstVal": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Total IGST value of all items" - }, - "CesVal": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Total CESS value of all items" - }, - "StCesVal": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Total State CESS value of all items" - }, - "Discount": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Invoice Discount" - }, - "OthChrg": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Other Charges" - }, - "RndOffAmt": { - "type": "number", - "minimum": -99.99, - "maximum": 99.99, - "description": "Rounded off Amount" - }, - "TotInvVal": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Final Invoice Value " - }, - "TotInvValFc": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Final Invoice value in Foreign Currency" - } - }, - "required": ["AssVal", "TotInvVal"] - }, - "PayDtls": { - "type": "object", - "properties": { - "Nm": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Payee Name" - }, - "AccDet": { - "type": "string", - "minLength": 1, - "maxLength": 18, - "description": "Bank Account Number of Payee" - }, - "Mode": { - "type": "string", - "minLength": 1, - "maxLength": 18, - "description": "Mode of Payment" - }, - "FinInsBr": { - "type": "string", - "minLength": 1, - "maxLength": 11, - "description": "Branch or IFSC code" - }, - "PayTerm": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Terms of Payment" - }, - "PayInstr": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Payment Instruction" - }, - "CrTrn": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Credit Transfer" - }, - "DirDr": { - "type": "string", - "minLength": 1, - "maxLength": 100, - "description": "Direct Debit" - }, - "CrDay": { - "type": "number", - "minimum": 0, - "maximum": 9999, - "description": "Credit Days" - }, - "PaidAmt": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Advance Amount" - }, - "PaymtDue": { - "type": "number", - "minimum": 0, - "maximum": 99999999999999.99, - "description": "Outstanding Amount" - } - } - }, - "RefDtls": { - "type": "object", - "properties": { - "InvRm": { - "type": "string", - "maxLength": 100, - "minLength": 3, - "pattern": "^[0-9A-Za-z/-]{3,100}$", - "description": "Remarks/Note" - }, - "DocPerdDtls": { - "type": "object", - "properties": { - "InvStDt": { - "type": "string", - "maxLength": 10, - "minLength": 10, - "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", - "description": "Invoice Period Start Date" - }, - "InvEndDt": { - "type": "string", - "maxLength": 10, - "minLength": 10, - "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", - "description": "Invoice Period End Date" - } - }, - "required": ["InvStDt ", "InvEndDt "] - }, - "PrecDocDtls": { - "type": "object", - "properties": { - "InvNo": { - "type": "string", - "minLength": 1, - "maxLength": 16, - "pattern": "^[1-9A-Z]{1}[0-9A-Z/-]{1,15}$", - "description": "Reference of Original Invoice" - }, - "InvDt": { - "type": "string", - "maxLength": 10, - "minLength": 10, - "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", - "description": "Date of Orginal Invoice" - }, - "OthRefNo": { - "type": "string", - "minLength": 1, - "maxLength": 20, - "description": "Other Reference" - } - } - }, - "required": ["InvNo", "InvDt"], - "ContrDtls": { - "type": "object", - "properties": { - "RecAdvRefr": { - "type": "string", - "minLength": 1, - "maxLength": 20, - "pattern": "^([0-9A-Za-z/-]){1,20}$", - "description": "Receipt Advice No." - }, - "RecAdvDt": { - "type": "string", - "minLength": 10, - "maxLength": 10, - "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", - "description": "Date of receipt advice" - }, - "TendRefr": { - "type": "string", - "minLength": 1, - "maxLength": 20, - "pattern": "^([0-9A-Za-z/-]){1,20}$", - "description": "Lot/Batch Reference No." - }, - "ContrRefr": { - "type": "string", - "minLength": 1, - "maxLength": 20, - "pattern": "^([0-9A-Za-z/-]){1,20}$", - "description": "Contract Reference Number" - }, - "ExtRefr": { - "type": "string", - "minLength": 1, - "maxLength": 20, - "pattern": "^([0-9A-Za-z/-]){1,20}$", - "description": "Any other reference" - }, - "ProjRefr": { - "type": "string", - "minLength": 1, - "maxLength": 20, - "pattern": "^([0-9A-Za-z/-]){1,20}$", - "description": "Project Reference Number" - }, - "PORefr": { - "type": "string", - "minLength": 1, - "maxLength": 16, - "pattern": "^([0-9A-Za-z/-]){1,16}$", - "description": "PO Reference Number" - }, - "PORefDt": { - "type": "string", - "minLength": 10, - "maxLength": 10, - "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", - "description": "PO Reference date" - } - } - } - } - }, - "AddlDocDtls": { - "type": "Array", - "properties": { - "Url": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Supporting document URL" - }, - "Docs": { - "type": "string", - "minLength": 3, - "maxLength": 1000, - "description": "Supporting document in Base64 Format" - }, - "Info": { - "type": "string", - "minLength": 3, - "maxLength": 1000, - "description": "Any additional information" - } - } - }, - - "ExpDtls": { - "type": "object", - "properties": { - "ShipBNo": { - "type": "string", - "minLength": 1, - "maxLength": 20, - "description": "Shipping Bill No." - }, - "ShipBDt": { - "type": "string", - "minLength": 10, - "maxLength": 10, - "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", - "description": "Shipping Bill Date" - }, - "Port": { - "type": "string", - "minLength": 2, - "maxLength": 10, - "pattern": "^[0-9A-Za-z]{2,10}$", - "description": "Port Code. Refer the master" - }, - "RefClm": { - "type": "string", - "minLength": 1, - "maxLength": 1, - "description": "Claiming Refund. Y/N" - }, - "ForCur": { - "type": "string", - "minLength": 3, - "maxLength": 16, - "description": "Additional Currency Code. Refer the master" - }, - "CntCode": { - "type": "string", - "minLength": 2, - "maxLength": 2, - "description": "Country Code. Refer the master" - }, - "ExpDuty": { - "type": "number", - "minimum": 0, - "maximum": 999999999999.99, - "description": "Export Duty" - } - } - }, - "EwbDtls": { - "type": "object", - "properties": { - "TransId": { - "type": "string", - "minLength": 15, - "maxLength": 15, - "description": "Transporter GSTIN" - }, - "TransName": { - "type": "string", - "minLength": 3, - "maxLength": 100, - "description": "Transporter Name" - }, - "TransMode": { - "type": "string", - "maxLength": 1, - "minLength": 1, - "enum": ["1", "2", "3", "4"], - "description": "Mode of Transport" - }, - "Distance": { - "type": "number", - "minimum": 1, - "maximum": 9999, - "description": "Distance" - }, - "TransDocNo": { - "type": "string", - "minLength": 1, - "maxLength": 15, - "pattern": "^([0-9A-Z/-]){1,15}$", - "description": "Tranport Document Number", - "validationMsg": "Transport Receipt No is invalid" - }, - "TransDocDt": { - "type": "string", - "minLength": 10, - "maxLength": 10, - "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]", - "description": "Transport Document Date" - }, - "VehNo": { - "type": "string", - "minLength": 4, - "maxLength": 20, - "description": "Vehicle Number" - }, - "VehType": { - "type": "string", - "minLength": 1, - "maxLength": 1, - "enum": ["O", "R"], - "description": "Vehicle Type" - } - }, - "required": ["Distance"] - }, - "required": [ - "Version", - "TranDtls", - "DocDtls", - "SellerDtls", - "BuyerDtls", - "ItemList", - "ValDtls" - ] -} diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js deleted file mode 100644 index 348f0c6feeda..000000000000 --- a/erpnext/regional/india/e_invoice/einvoice.js +++ /dev/null @@ -1,292 +0,0 @@ -erpnext.setup_einvoice_actions = (doctype) => { - frappe.ui.form.on(doctype, { - async refresh(frm) { - if (frm.doc.docstatus == 2) return; - - const res = await frappe.call({ - method: 'erpnext.regional.india.e_invoice.utils.validate_eligibility', - args: { doc: frm.doc } - }); - const invoice_eligible = res.message; - - if (!invoice_eligible) return; - - const { doctype, irn, irn_cancelled, ewaybill, eway_bill_cancelled, name, __unsaved } = frm.doc; - - const add_custom_button = (label, action) => { - if (!frm.custom_buttons[label]) { - frm.add_custom_button(label, action, __('E Invoicing')); - } - }; - - if (!irn && !__unsaved) { - const action = () => { - if (frm.doc.__unsaved) { - frappe.throw(__('Please save the document to generate IRN.')); - } - frappe.call({ - method: 'erpnext.regional.india.e_invoice.utils.get_einvoice', - args: { doctype, docname: name }, - freeze: true, - callback: (res) => { - const einvoice = res.message; - show_einvoice_preview(frm, einvoice); - } - }); - }; - - add_custom_button(__("Generate IRN"), action); - } - - if (irn && !irn_cancelled && !ewaybill) { - const fields = [ - { - "label": "Reason", - "fieldname": "reason", - "fieldtype": "Select", - "reqd": 1, - "default": "1-Duplicate", - "options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"] - }, - { - "label": "Remark", - "fieldname": "remark", - "fieldtype": "Data", - "reqd": 1 - } - ]; - const action = () => { - const d = new frappe.ui.Dialog({ - title: __("Cancel IRN"), - fields: fields, - primary_action: function() { - const data = d.get_values(); - frappe.call({ - method: 'erpnext.regional.india.e_invoice.utils.cancel_irn', - args: { - doctype, - docname: name, - irn: irn, - reason: data.reason.split('-')[0], - remark: data.remark - }, - freeze: true, - callback: () => frm.reload_doc() || d.hide(), - error: () => d.hide() - }); - }, - primary_action_label: __('Submit') - }); - d.show(); - }; - add_custom_button(__("Cancel IRN"), action); - } - - if (irn && !irn_cancelled && !ewaybill) { - const action = () => { - const d = new frappe.ui.Dialog({ - title: __('Generate E-Way Bill'), - size: "large", - fields: get_ewaybill_fields(frm), - primary_action: function() { - const data = d.get_values(); - frappe.call({ - method: 'erpnext.regional.india.e_invoice.utils.generate_eway_bill', - args: { - doctype, - docname: name, - irn, - ...data - }, - freeze: true, - callback: () => frm.reload_doc() || d.hide(), - error: () => d.hide() - }); - }, - primary_action_label: __('Submit') - }); - d.show(); - }; - - add_custom_button(__("Generate E-Way Bill"), action); - } - - if (irn && ewaybill && !irn_cancelled && !eway_bill_cancelled) { - const action = () => { - let message = __('Cancellation of e-way bill is currently not supported.') + ' '; - message += '

                                                            '; - message += __('You must first use the portal to cancel the e-way bill and then update the cancelled status in the ERPNext system.'); - - const dialog = frappe.msgprint({ - title: __('Update E-Way Bill Cancelled Status?'), - message: message, - indicator: 'orange', - primary_action: { - action: function() { - frappe.call({ - method: 'erpnext.regional.india.e_invoice.utils.cancel_eway_bill', - args: { doctype, docname: name }, - freeze: true, - callback: () => frm.reload_doc() || dialog.hide() - }); - } - }, - primary_action_label: __('Yes') - }); - }; - add_custom_button(__("Cancel E-Way Bill"), action); - } - } - }); -}; - -const get_ewaybill_fields = (frm) => { - return [ - { - 'fieldname': 'transporter', - 'label': 'Transporter', - 'fieldtype': 'Link', - 'options': 'Supplier', - 'default': frm.doc.transporter - }, - { - 'fieldname': 'gst_transporter_id', - 'label': 'GST Transporter ID', - 'fieldtype': 'Data', - 'fetch_from': 'transporter.gst_transporter_id', - 'default': frm.doc.gst_transporter_id - }, - { - 'fieldname': 'driver', - 'label': 'Driver', - 'fieldtype': 'Link', - 'options': 'Driver', - 'default': frm.doc.driver - }, - { - 'fieldname': 'lr_no', - 'label': 'Transport Receipt No', - 'fieldtype': 'Data', - 'default': frm.doc.lr_no - }, - { - 'fieldname': 'vehicle_no', - 'label': 'Vehicle No', - 'fieldtype': 'Data', - 'default': frm.doc.vehicle_no - }, - { - 'fieldname': 'distance', - 'label': 'Distance (in km)', - 'fieldtype': 'Float', - 'default': frm.doc.distance - }, - { - 'fieldname': 'transporter_col_break', - 'fieldtype': 'Column Break', - }, - { - 'fieldname': 'transporter_name', - 'label': 'Transporter Name', - 'fieldtype': 'Data', - 'fetch_from': 'transporter.name', - 'read_only': 1, - 'default': frm.doc.transporter_name - }, - { - 'fieldname': 'mode_of_transport', - 'label': 'Mode of Transport', - 'fieldtype': 'Select', - 'options': `\nRoad\nAir\nRail\nShip`, - 'default': frm.doc.mode_of_transport - }, - { - 'fieldname': 'driver_name', - 'label': 'Driver Name', - 'fieldtype': 'Data', - 'fetch_from': 'driver.full_name', - 'read_only': 1, - 'default': frm.doc.driver_name - }, - { - 'fieldname': 'lr_date', - 'label': 'Transport Receipt Date', - 'fieldtype': 'Date', - 'default': frm.doc.lr_date - }, - { - 'fieldname': 'gst_vehicle_type', - 'label': 'GST Vehicle Type', - 'fieldtype': 'Select', - 'options': `Regular\nOver Dimensional Cargo (ODC)`, - 'depends_on': 'eval:(doc.mode_of_transport === "Road")', - 'default': frm.doc.gst_vehicle_type - } - ]; -}; - -const request_irn_generation = (frm) => { - frappe.call({ - method: 'erpnext.regional.india.e_invoice.utils.generate_irn', - args: { doctype: frm.doc.doctype, docname: frm.doc.name }, - freeze: true, - callback: () => frm.reload_doc() - }); -}; - -const get_preview_dialog = (frm, action) => { - const dialog = new frappe.ui.Dialog({ - title: __("Preview"), - size: "large", - fields: [ - { - "label": "Preview", - "fieldname": "preview_html", - "fieldtype": "HTML" - } - ], - primary_action: () => action(frm) || dialog.hide(), - primary_action_label: __('Generate IRN') - }); - return dialog; -}; - -const show_einvoice_preview = (frm, einvoice) => { - const preview_dialog = get_preview_dialog(frm, request_irn_generation); - - // initialize e-invoice fields - einvoice["Irn"] = einvoice["AckNo"] = ''; einvoice["AckDt"] = frappe.datetime.nowdate(); - frm.doc.signed_einvoice = JSON.stringify(einvoice); - - // initialize preview wrapper - const $preview_wrapper = preview_dialog.get_field("preview_html").$wrapper; - $preview_wrapper.html( - `
                                                            - -
                                                            -
                                                            ` - ); - - frappe.call({ - method: "frappe.www.printview.get_html_and_style", - args: { - doc: frm.doc, - print_format: "GST E-Invoice", - no_letterhead: 1 - }, - callback: function (r) { - if (!r.exc) { - $preview_wrapper.find(".print-format").html(r.message.html); - const style = ` - .print-format { box-shadow: 0px 0px 5px rgba(0,0,0,0.2); padding: 0.30in; min-height: 80vh; } - .print-preview { min-height: 0px; } - .modal-dialog { width: 720px; }`; - - frappe.dom.set_style(style, "custom-print-style"); - preview_dialog.show(); - } - } - }); -}; diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 2d6b9133900a..a6ab6aba7747 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -61,7 +61,7 @@ def create_hsn_codes(data, code_field): def add_custom_roles_for_reports(): for report_name in ('GST Sales Register', 'GST Purchase Register', - 'GST Itemised Sales Register', 'GST Itemised Purchase Register', 'Eway Bill', 'E-Invoice Summary'): + 'GST Itemised Sales Register', 'GST Itemised Purchase Register', 'Eway Bill'): if not frappe.db.get_value('Custom Role', dict(report=report_name)): frappe.get_doc(dict( @@ -100,7 +100,7 @@ def add_custom_roles_for_reports(): )).insert() def add_permissions(): - for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate', 'E Invoice Settings'): + for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'): add_permission(doctype, 'All', 0) for role in ('Accounts Manager', 'Accounts User', 'System Manager'): add_permission(doctype, role, 0) @@ -116,11 +116,9 @@ def add_permissions(): def add_print_formats(): frappe.reload_doc("regional", "print_format", "gst_tax_invoice") frappe.reload_doc("accounts", "print_format", "gst_pos_invoice") - frappe.reload_doc("accounts", "print_format", "GST E-Invoice") frappe.db.set_value("Print Format", "GST POS Invoice", "disabled", 0) frappe.db.set_value("Print Format", "GST Tax Invoice", "disabled", 0) - frappe.db.set_value("Print Format", "GST E-Invoice", "disabled", 0) def make_property_setters(patch=False): # GST rules do not allow for an invoice no. bigger than 16 characters @@ -445,53 +443,13 @@ def make_custom_fields(update=True): 'fieldname': 'ewaybill', 'label': 'E-Way Bill No.', 'fieldtype': 'Data', - 'depends_on': 'eval:((doc.docstatus === 1 || doc.ewaybill) && doc.eway_bill_cancelled === 0)', + 'depends_on': 'eval:(doc.docstatus === 1)', 'allow_on_submit': 1, 'insert_after': 'tax_id', 'translatable': 0 } ] - si_einvoice_fields = [ - 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='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1, - 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'), - - dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1, - depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'), - - 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', - 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', - no_copy=1, print_hide=1, read_only=1), - - dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice', - no_copy=1, print_hide=1, read_only=1), - - dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code', - no_copy=1, print_hide=1, read_only=1), - - dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image', - options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1), - - dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON', - hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1) - ] - custom_fields = { 'Address': [ dict(fieldname='gstin', label='Party GSTIN', fieldtype='Data', @@ -504,7 +462,7 @@ def make_custom_fields(update=True): 'Purchase Invoice': purchase_invoice_gst_category + invoice_gst_fields + purchase_invoice_itc_fields + purchase_invoice_gst_fields, 'Purchase Order': purchase_invoice_gst_fields, 'Purchase Receipt': purchase_invoice_gst_fields, - 'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields + si_einvoice_fields, + 'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields, 'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields + delivery_note_gst_category, 'Journal Entry': journal_entry_fields, 'Sales Order': sales_invoice_gst_fields, diff --git a/erpnext/regional/report/e_invoice_summary/__init__.py b/erpnext/regional/report/e_invoice_summary/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js deleted file mode 100644 index 4713217d83c5..000000000000 --- a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt -/* eslint-disable */ - -frappe.query_reports["E-Invoice Summary"] = { - "filters": [ - { - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "fieldname": "company", - "label": __("Company"), - "default": frappe.defaults.get_user_default("Company"), - }, - { - "fieldtype": "Link", - "options": "Customer", - "fieldname": "customer", - "label": __("Customer") - }, - { - "fieldtype": "Date", - "reqd": 1, - "fieldname": "from_date", - "label": __("From Date"), - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - }, - { - "fieldtype": "Date", - "reqd": 1, - "fieldname": "to_date", - "label": __("To Date"), - "default": frappe.datetime.get_today(), - }, - { - "fieldtype": "Select", - "fieldname": "status", - "label": __("Status"), - "options": "\nPending\nGenerated\nCancelled\nFailed" - } - ], - - "formatter": function (value, row, column, data, default_formatter) { - value = default_formatter(value, row, column, data); - - if (column.fieldname == "einvoice_status" && value) { - if (value == 'Pending') value = `${value}`; - else if (value == 'Generated') value = `${value}`; - else if (value == 'Cancelled') value = `${value}`; - else if (value == 'Failed') value = `${value}`; - } - - return value; - } -}; diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json deleted file mode 100644 index d0000ad50dfc..000000000000 --- a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "add_total_row": 0, - "columns": [], - "creation": "2021-03-12 11:23:37.312294", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "filters": [], - "idx": 0, - "is_standard": "Yes", - "json": "{}", - "letter_head": "Logo", - "modified": "2021-03-13 12:36:48.689413", - "modified_by": "Administrator", - "module": "Regional", - "name": "E-Invoice Summary", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Sales Invoice", - "report_name": "E-Invoice Summary", - "report_type": "Script Report", - "roles": [ - { - "role": "Administrator" - } - ] -} \ No newline at end of file diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py deleted file mode 100644 index 66ffceae5391..000000000000 --- a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe import _ - -def execute(filters=None): - validate_filters(filters) - - columns = get_columns() - data = get_data(filters) - - return columns, data - -def validate_filters(filters={}): - filters = frappe._dict(filters) - - if not filters.company: - frappe.throw(_('{} is mandatory for generating E-Invoice Summary Report').format(_('Company')), title=_('Invalid Filter')) - if filters.company: - # validate if company has e-invoicing enabled - pass - if not filters.from_date or not filters.to_date: - frappe.throw(_('From Date & To Date is mandatory for generating E-Invoice Summary Report'), title=_('Invalid Filter')) - if filters.from_date > filters.to_date: - frappe.throw(_('From Date must be before To Date'), title=_('Invalid Filter')) - -def get_data(filters={}): - query_filters = { - 'posting_date': ['between', [filters.from_date, filters.to_date]], - 'einvoice_status': ['is', 'set'], - 'company': filters.company - } - if filters.customer: - query_filters['customer'] = filters.customer - if filters.status: - query_filters['einvoice_status'] = filters.status - - data = frappe.get_all( - 'Sales Invoice', - filters=query_filters, - fields=[d.get('fieldname') for d in get_columns()] - ) - - return data - -def get_columns(): - return [ - { - "fieldtype": "Date", - "fieldname": "posting_date", - "label": _("Posting Date"), - "width": 0 - }, - { - "fieldtype": "Link", - "fieldname": "name", - "label": _("Sales Invoice"), - "options": "Sales Invoice", - "width": 140 - }, - { - "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.", - "width": 145 - }, - { - "fieldtype": "Data", - "fieldname": "ack_date", - "label": "Ack. Date", - "width": 165 - }, - { - "fieldtype": "Data", - "fieldname": "irn", - "label": _("IRN No."), - "width": 250 - }, - { - "fieldtype": "Currency", - "options": "Company:company:default_currency", - "fieldname": "base_grand_total", - "label": _("Grand Total"), - "width": 120 - } - ] From 2b2572b9b997eaeaae86e23f8b74803db85daf5a Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 20 Aug 2021 14:40:12 +0530 Subject: [PATCH 673/691] fix: Cascade deletion for Company (#26923) * fix: Cascade deletion for Company --- erpnext/hooks.py | 3 +++ erpnext/regional/india/utils.py | 17 +++++++++++++++++ erpnext/setup/doctype/company/company.py | 4 ++++ 3 files changed, 24 insertions(+) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 73831bf9af09..74977cd8bcad 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -308,6 +308,9 @@ }, ('Quotation', 'Sales Order', 'Sales Invoice'): { 'validate': ["erpnext.erpnext_integrations.taxjar_integration.set_sales_tax"] + }, + "Company": { + "on_trash": "erpnext.regional.india.utils.delete_gst_settings_for_company" } } diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 949733e0ad89..4e4dcf85859b 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -871,3 +871,20 @@ def set_item_tax_from_hsn_code(item): 'tax_category': tax.tax_category, 'valid_from': tax.valid_from }) + +def delete_gst_settings_for_company(doc, method): + if doc.country != 'India': + return + + gst_settings = frappe.get_doc("GST Settings") + records_to_delete = [] + + for d in reversed(gst_settings.get('gst_accounts')): + if d.company == doc.name: + records_to_delete.append(d) + + for d in records_to_delete: + gst_settings.remove(d) + + gst_settings.save() + diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 54c67538aef2..45d5ce0c1c7b 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -393,6 +393,10 @@ def on_trash(self): frappe.db.sql("delete from `tabPurchase Taxes and Charges Template` where company=%s", self.name) frappe.db.sql("delete from `tabItem Tax Template` where company=%s", self.name) + # delete Process Deferred Accounts if no GL Entry found + if not frappe.db.get_value('GL Entry', {'company': self.name}): + frappe.db.sql("delete from `tabProcess Deferred Accounting` where company=%s", self.name) + @frappe.whitelist() def enqueue_replace_abbr(company, old, new): kwargs = dict(queue="long", company=company, old=old, new=new) From 62c590261c5c7cc96d1cad29bc44615229a0f83a Mon Sep 17 00:00:00 2001 From: Alan <2.alan.tom@gmail.com> Date: Fri, 20 Aug 2021 18:21:09 +0530 Subject: [PATCH 674/691] refactor: rectify typo (#27057) [skip ci] --- erpnext/manufacturing/doctype/work_order/work_order.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 69a4b95c9ac4..5fe9fec2af1d 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -602,7 +602,7 @@ def update_required_items(self): if self.docstatus==1: # calculate transferred qty based on submitted stock entries - self.update_transaferred_qty_for_required_items() + self.update_transferred_qty_for_required_items() # update in bin self.update_reserved_qty_for_production() @@ -671,7 +671,7 @@ def set_required_items(self, reset_only_qty=False): self.set_available_qty() - def update_transaferred_qty_for_required_items(self): + def update_transferred_qty_for_required_items(self): '''update transferred qty from submitted stock entries for that item against the work order''' From ec258551bf48c93ce7190397ec34adee6eee66e6 Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 20 Aug 2021 19:09:31 +0530 Subject: [PATCH 675/691] fix: flaky test for SLA (#27051) --- .../test_service_level_agreement.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 a81516ec1162..d9c671e9a212 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 @@ -267,11 +267,15 @@ def test_service_level_agreement_filters(self): ) creation = datetime.datetime(2019, 3, 4, 12, 0) lead = make_lead(creation=creation, index=4) - self.assertFalse(lead.service_level_agreement) + applied_sla = frappe.db.get_value('Lead', lead.name, 'service_level_agreement') + self.assertFalse(applied_sla) + source = frappe.get_doc(doctype='Lead Source', source_name='Test Source') + source.insert(ignore_if_duplicate=True) lead.source = "Test Source" lead.save() - self.assertEqual(lead.service_level_agreement, lead_sla.name) + applied_sla = frappe.db.get_value('Lead', lead.name, 'service_level_agreement') + self.assertEqual(applied_sla, lead_sla.name) def tearDown(self): for d in frappe.get_all("Service Level Agreement"): From 57e326e7d0ad1a1053ac2d987aacd540353ef47d Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sat, 21 Aug 2021 17:59:11 +0530 Subject: [PATCH 676/691] fix: Consolidated balance sheet showing incorrect values (#26975) --- .../consolidated_financial_statement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 56a67bb0989d..fc4212733a39 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -210,10 +210,10 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i company_currency = get_company_currency(filters) if filters.filter_based_on == 'Fiscal Year': - start_date = fiscal_year.year_start_date + start_date = fiscal_year.year_start_date if filters.report != 'Balance Sheet' else None end_date = fiscal_year.year_end_date else: - start_date = filters.period_start_date + start_date = filters.period_start_date if filters.report != 'Balance Sheet' else None end_date = filters.period_end_date gl_entries_by_account = {} From 496bff5136c7a72c3346361af7b7e41e787cff1b Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sun, 22 Aug 2021 18:10:51 +0530 Subject: [PATCH 677/691] feat: Column for total amount due in Accounts Receivable/Payable Summary (#27069) --- .../report/accounts_receivable/accounts_receivable.py | 2 ++ .../accounts_receivable_summary.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index b54646fd27af..cedfc0f58b83 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -535,6 +535,8 @@ def set_ageing(self, row): if getdate(entry_date) > getdate(self.filters.report_date): row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 + row.total_due = row.range1 + row.range2 + row.range3 + row.range4 + row.range5 + def get_ageing_data(self, entry_date, row): # [0-30, 30-60, 60-90, 90-120, 120-above] row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 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 e94b30921f32..4bfb022c4ee8 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -82,6 +82,7 @@ def init_party_total(self, row): "range3": 0.0, "range4": 0.0, "range5": 0.0, + "total_due": 0.0, "sales_person": [] })) @@ -135,3 +136,6 @@ def setup_ageing_columns(self): "{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)) + + # Add column for total due amount + self.add_column(label="Total Amount Due", fieldname='total_due') From 7d627df4dbe15b3db16da031945ab98ccaae71ea Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 23 Aug 2021 11:05:07 +0530 Subject: [PATCH 678/691] fix: pos return payment mode issue (#26872) --- erpnext/controllers/taxes_and_totals.py | 9 ++++----- erpnext/public/js/controllers/taxes_and_totals.js | 2 -- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 05edb2530c22..7c6d3552f123 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -595,7 +595,8 @@ def calculate_outstanding_amount(self): self.doc.precision("outstanding_amount")) if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'): - self.update_paid_amount_for_return(total_amount_to_pay) + self.set_total_amount_to_default_mop(total_amount_to_pay) + self.calculate_paid_amount() def calculate_paid_amount(self): @@ -675,7 +676,7 @@ def calculate_margin(self, item): def set_item_wise_tax_breakup(self): self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc) - def update_paid_amount_for_return(self, total_amount_to_pay): + def set_total_amount_to_default_mop(self, total_amount_to_pay): default_mode_of_payment = frappe.db.get_value('POS Payment Method', {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1) @@ -685,9 +686,7 @@ def update_paid_amount_for_return(self, total_amount_to_pay): 'mode_of_payment': default_mode_of_payment.mode_of_payment, 'amount': total_amount_to_pay, 'default': 1 - }) - - self.calculate_paid_amount() + }) def get_itemised_tax_breakup_html(doc): if not doc.taxes: diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index e8f31225ba61..702064fe5565 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -754,8 +754,6 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } }); this.frm.refresh_fields(); - - this.calculate_paid_amount(); } set_default_payment(total_amount_to_pay, update_paid_amount) { From 8b2fe9e793e226339bbcec554d8c57a09f87b5c5 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 23 Aug 2021 11:17:31 +0530 Subject: [PATCH 679/691] fix: eway bill version changed to 1.0.0421 (#27044) --- erpnext/regional/india/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 4e4dcf85859b..ce5aa10902ee 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -475,7 +475,7 @@ def get_ewb_data(dt, dn): ewaybills.append(data) data = { - 'version': '1.0.1118', + 'version': '1.0.0421', 'billLists': ewaybills } From 098d349bf4181100111e2b01025ce7d939b96392 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 23 Aug 2021 13:36:13 +0530 Subject: [PATCH 680/691] fix: stock ledger report not working if include uom selected in filter --- erpnext/stock/report/stock_ledger/stock_ledger.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index 8909f217f499..b6923e97c4f1 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -23,6 +23,7 @@ def execute(filters=None): conversion_factors = [] if opening_row: data.append(opening_row) + conversion_factors.append(0) actual_qty = stock_value = 0 From e1f070437ae5869ac43896730cccb4b4c904e7de Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 23 Aug 2021 14:27:55 +0530 Subject: [PATCH 681/691] fix: selected batch no changed on updation of qty --- erpnext/selling/sales_common.js | 4 ++++ erpnext/stock/get_item_details.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 22bf3fc94fd6..2de57c87f186 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -394,6 +394,10 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran } _set_batch_number(doc) { + if (doc.batch_no) { + return + } + let args = {'item_code': doc.item_code, 'warehouse': doc.warehouse, 'qty': flt(doc.qty) * flt(doc.conversion_factor)}; if (doc.has_serial_no && doc.serial_no) { args['serial_no'] = doc.serial_no diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index a0fbcecc5de3..c72073c6143b 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -278,6 +278,10 @@ def get_basic_details(args, item, overwrite_warehouse=True): else: args.uom = item.stock_uom + if (args.get("batch_no") and + item.name != frappe.get_cached_value('Batch', args.get("batch_no"), 'item')): + args['batch_no'] = '' + out = frappe._dict({ "item_code": item.name, "item_name": item.item_name, From 47b63a627d7d8b6d32cc5ff5713af9eb973001d3 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 23 Aug 2021 15:38:26 +0530 Subject: [PATCH 682/691] fix: Eway bill test update to check ver 1.0.0421 (#27083) --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 984a65224898..01a6b0273228 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2014,7 +2014,7 @@ def test_eway_bill_json(self): data = get_ewb_data("Sales Invoice", [si.name]) - self.assertEqual(data['version'], '1.0.1118') + self.assertEqual(data['version'], '1.0.0421') self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR') self.assertEqual(data['billLists'][0]['fromTrdName'], '_Test Company') self.assertEqual(data['billLists'][0]['toTrdName'], '_Test Customer') From 49e0a4f28714b468ba5edad296b30f22991210f3 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 24 Aug 2021 08:33:57 +0530 Subject: [PATCH 683/691] Revert "fix: Salary component account filter (#26604)" This reverts commit ae9d1d9617015b6e2714a9fcd027a2957053d99a. --- .../doctype/salary_component/salary_component.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/erpnext/payroll/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js index e9e6f81862c1..dbf75140ac14 100644 --- a/erpnext/payroll/doctype/salary_component/salary_component.js +++ b/erpnext/payroll/doctype/salary_component/salary_component.js @@ -4,18 +4,11 @@ frappe.ui.form.on('Salary Component', { setup: function(frm) { frm.set_query("account", "accounts", function(doc, cdt, cdn) { - let d = frappe.get_doc(cdt, cdn); - - let root_type = "Liability"; - if (frm.doc.type == "Deduction") { - root_type = "Expense"; - } - + var d = locals[cdt][cdn]; return { filters: { "is_group": 0, - "company": d.company, - "root_type": root_type + "company": d.company } }; }); From 332ac105b5ffec04009539eba94b153a8b93c0c4 Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Tue, 24 Aug 2021 12:07:38 +0530 Subject: [PATCH 684/691] refactor: use `read_only_depends_on` instead of code (#27008) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.js | 6 +----- erpnext/accounts/doctype/pos_invoice/pos_invoice.json | 5 +++-- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 8 +------- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 5 +++-- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index 181e9f8ec090..e3175464813d 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -111,16 +111,12 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex } write_off_outstanding_amount_automatically() { - if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { + if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) { frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); // this will make outstanding amount 0 this.frm.set_value("write_off_amount", flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount")) ); - this.frm.toggle_enable("write_off_amount", false); - - } else { - this.frm.toggle_enable("write_off_amount", true); } this.calculate_outstanding_amount(false); diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index fcccb39b70c3..3e22b9e00a8c 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -1183,7 +1183,8 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "read_only_depends_on": "eval: doc.write_off_outstanding_amount_automatically" }, { "fieldname": "base_write_off_amount", @@ -1554,7 +1555,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2021-08-17 20:13:44.255437", + "modified": "2021-08-18 16:13:52.080543", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 8d65101b3ba4..2071827d9942 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -324,16 +324,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e } write_off_outstanding_amount_automatically() { - if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { + if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) { frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); // this will make outstanding amount 0 this.frm.set_value("write_off_amount", flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount")) ); - this.frm.toggle_enable("write_off_amount", false); - - } else { - this.frm.toggle_enable("write_off_amount", true); } this.calculate_outstanding_amount(false); @@ -787,8 +783,6 @@ frappe.ui.form.on('Sales Invoice', { if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']); else hide_field(['c_form_applicable', 'c_form_no']); - frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically)); - frm.refresh_fields(); }, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index e317443b91ab..5023c9c61a3e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1444,7 +1444,8 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "read_only_depends_on": "eval:doc.write_off_outstanding_amount_automatically" }, { "fieldname": "base_write_off_amount", @@ -2014,7 +2015,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-08-17 20:16:12.737743", + "modified": "2021-08-18 16:07:45.122570", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From 0dff0beabaa937e397eae2f930bae337cfe8af82 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 24 Aug 2021 12:16:46 +0530 Subject: [PATCH 685/691] fix: stock analytics report date range issues and add company filter (#27014) * test: tests for correct get_period_date_ranges * fix: stock analytics report date range issues - Upon selecting second half of month with Monthly filter, data from that period was missing. - Solution: "round down" the date as per expected frequency. * chore: drop py2 and fix misleading docstring * test: fix test to avoid FY clash * feat: add company filter in stock analytics report [skip ci] Co-authored-by: Marica --- .../report/stock_analytics/stock_analytics.js | 16 +++++++- .../report/stock_analytics/stock_analytics.py | 37 ++++++++++++++++--- .../stock_analytics/test_stock_analytics.py | 35 ++++++++++++++++++ 3 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 erpnext/stock/report/stock_analytics/test_stock_analytics.py diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.js b/erpnext/stock/report/stock_analytics/stock_analytics.js index 6b384e286114..78afe6d2642e 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.js +++ b/erpnext/stock/report/stock_analytics/stock_analytics.js @@ -36,12 +36,26 @@ frappe.query_reports["Stock Analytics"] = { options:"Brand", default: "", }, + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, { fieldname: "warehouse", label: __("Warehouse"), fieldtype: "Link", - options:"Warehouse", + options: "Warehouse", default: "", + get_query: function() { + const company = frappe.query_report.get_filter_value('company'); + return { + filters: { 'company': company } + } + } }, { fieldname: "from_date", diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index d62abed91f35..a1e1e7fce7c4 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -1,14 +1,15 @@ # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import datetime -from __future__ import unicode_literals import frappe from frappe import _, scrub -from frappe.utils import getdate, flt +from frappe.utils import getdate, get_quarter_start, get_first_day_of_week +from frappe.utils import get_first_day as get_first_day_of_month + from erpnext.stock.report.stock_balance.stock_balance import (get_items, get_stock_ledger_entries, get_item_details) from erpnext.accounts.utils import get_fiscal_year from erpnext.stock.utils import is_reposting_item_valuation_in_progress -from six import iteritems def execute(filters=None): is_reposting_item_valuation_in_progress() @@ -71,7 +72,8 @@ def get_columns(filters): def get_period_date_ranges(filters): from dateutil.relativedelta import relativedelta - from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) + from_date = round_down_to_nearest_frequency(filters.from_date, filters.range) + to_date = getdate(filters.to_date) increment = { "Monthly": 1, @@ -97,6 +99,31 @@ def get_period_date_ranges(filters): return periodic_daterange + +def round_down_to_nearest_frequency(date: str, frequency: str) -> datetime.datetime: + """Rounds down the date to nearest frequency unit. + example: + + >>> round_down_to_nearest_frequency("2021-02-21", "Monthly") + datetime.datetime(2021, 2, 1) + + >>> round_down_to_nearest_frequency("2021-08-21", "Yearly") + datetime.datetime(2021, 1, 1) + """ + + def _get_first_day_of_fiscal_year(date): + fiscal_year = get_fiscal_year(date) + return fiscal_year and fiscal_year[1] or date + + round_down_function = { + "Monthly": get_first_day_of_month, + "Quarterly": get_quarter_start, + "Weekly": get_first_day_of_week, + "Yearly": _get_first_day_of_fiscal_year, + }.get(frequency, getdate) + return round_down_function(date) + + def get_period(posting_date, filters): months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] @@ -177,7 +204,7 @@ def get_data(filters): periodic_data = get_periodic_data(sle, filters) ranges = get_period_date_ranges(filters) - for dummy, item_data in iteritems(item_details): + for dummy, item_data in item_details.items(): row = { "name": item_data.name, "item_name": item_data.item_name, diff --git a/erpnext/stock/report/stock_analytics/test_stock_analytics.py b/erpnext/stock/report/stock_analytics/test_stock_analytics.py new file mode 100644 index 000000000000..00e268b4e0e3 --- /dev/null +++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py @@ -0,0 +1,35 @@ +import datetime +import unittest + +from frappe import _dict +from erpnext.accounts.utils import get_fiscal_year + +from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges + + +class TestStockAnalyticsReport(unittest.TestCase): + def test_get_period_date_ranges(self): + + filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06") + + ranges = get_period_date_ranges(filters) + + expected_ranges = [ + [datetime.date(2020, 12, 1), datetime.date(2020, 12, 31)], + [datetime.date(2021, 1, 1), datetime.date(2021, 1, 31)], + [datetime.date(2021, 2, 1), datetime.date(2021, 2, 6)], + ] + + self.assertEqual(ranges, expected_ranges) + + def test_get_period_date_ranges_yearly(self): + + filters = _dict(range="Yearly", from_date="2021-01-28", to_date="2021-02-06") + + ranges = get_period_date_ranges(filters) + first_date = get_fiscal_year("2021-01-28")[1] + expected_ranges = [ + [first_date, datetime.date(2021, 2, 6)], + ] + + self.assertEqual(ranges, expected_ranges) From 6de7b8ea93e3ffe621976ebf3b613406a379216d Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 24 Aug 2021 12:18:40 +0530 Subject: [PATCH 686/691] fix: discard empty rows from update items (#27021) --- erpnext/controllers/accounts_controller.py | 5 +++++ erpnext/public/js/utils.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 8addbeb98f8b..01d354df81f9 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1836,6 +1836,11 @@ def validate_quantity(child_item, d): for d in data: new_child_flag = False + + if not d.get("item_code"): + # ignore empty rows + continue + if not d.get("docname"): new_child_flag = True check_doc_permissions(parent, 'create') diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index f1b9235fe3a1..f240b8ca91fe 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -563,7 +563,7 @@ erpnext.utils.update_child_items = function(opts) { }, ], primary_action: function() { - const trans_items = this.get_values()["trans_items"]; + const trans_items = this.get_values()["trans_items"].filter((item) => !!item.item_code); frappe.call({ method: 'erpnext.controllers.accounts_controller.update_child_qty_rate', freeze: true, From c09d8a28098ca2963122373ae18f0c8c1954fcf3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 24 Aug 2021 12:20:34 +0530 Subject: [PATCH 687/691] fix(ux): keep stock entry title & purpose in sync (#27043) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/stock_entry/stock_entry.json | 4 +--- erpnext/stock/doctype/stock_entry/stock_entry.py | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index e6ce3c851ff5..2f377788961d 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -84,8 +84,6 @@ "oldfieldtype": "Section Break" }, { - "allow_on_submit": 1, - "default": "{purpose}", "fieldname": "title", "fieldtype": "Data", "hidden": 1, @@ -630,7 +628,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-08-17 20:16:12.737743", + "modified": "2021-08-20 19:19:31.514846", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 90a33d3617eb..0b4592c12f9e 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -58,6 +58,7 @@ def validate(self): self.validate_posting_time() self.validate_purpose() + self.set_title() self.validate_item() self.validate_customer_provided_item() self.validate_qty() @@ -1608,6 +1609,14 @@ def get_available_serial_nos(self, stock_entries): return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos))) + def set_title(self): + if frappe.flags.in_import and self.title: + # Allow updating title during data import/update + return + + self.title = self.purpose + + @frappe.whitelist() def move_sample_to_retention_warehouse(company, items): if isinstance(items, string_types): From 1e05c9467bc5fa9315a67c7f8ed9ff5b17baa3e6 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 24 Aug 2021 14:21:14 +0530 Subject: [PATCH 688/691] fix: Use remove_all from file_manager --- erpnext/regional/italy/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py index ba1aeafc3e95..024f82056868 100644 --- a/erpnext/regional/italy/utils.py +++ b/erpnext/regional/italy/utils.py @@ -6,7 +6,7 @@ from frappe.utils import flt, cstr from erpnext.controllers.taxes_and_totals import get_itemised_tax from frappe import _ -from frappe.core.doctype.file.file import remove_file +from frappe.utils.file_manager import remove_file from six import string_types from frappe.desk.form.load import get_attachments from erpnext.regional.italy import state_codes From 6cf9254ee5d6599f3dfe1b696dbb51edc33b965c Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 24 Aug 2021 15:26:44 +0530 Subject: [PATCH 689/691] fix: invalid imports (#27101) --- erpnext/regional/italy/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py index ba1aeafc3e95..56f609eb234e 100644 --- a/erpnext/regional/italy/utils.py +++ b/erpnext/regional/italy/utils.py @@ -6,9 +6,8 @@ from frappe.utils import flt, cstr from erpnext.controllers.taxes_and_totals import get_itemised_tax from frappe import _ -from frappe.core.doctype.file.file import remove_file +from frappe.utils.file_manager import remove_file from six import string_types -from frappe.desk.form.load import get_attachments from erpnext.regional.italy import state_codes From 9e82c24b277d09b61dc94f345b173603bee97a02 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 24 Aug 2021 15:37:00 +0530 Subject: [PATCH 690/691] fix: pos closing entry cancellation test (#27099) * fix: pos closing entry cancellation test * fix: invalid imports * fix: sider issue --- .../pos_closing_entry/test_pos_closing_entry.py | 10 ++++++++-- erpnext/accounts/doctype/pos_invoice/pos_invoice.js | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index b596c0cf25a1..5b18ebb40dd7 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -85,9 +85,15 @@ def test_cancelling_of_pos_closing_entry(self): pcv_doc.load_from_db() pcv_doc.cancel() - si_doc.load_from_db() + + cancelled_invoice = frappe.db.get_value( + 'POS Invoice Merge Log', {'pos_closing_entry': pcv_doc.name}, + 'consolidated_invoice' + ) + docstatus = frappe.db.get_value("Sales Invoice", cancelled_invoice, 'docstatus') + self.assertEqual(docstatus, 2) + pos_inv1.load_from_db() - self.assertEqual(si_doc.docstatus, 2) self.assertEqual(pos_inv1.status, 'Paid') diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index e3175464813d..15c292211c0f 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -16,7 +16,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex onload(doc) { super.onload(); - this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log']; + this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log', 'POS Closing Entry']; if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') { this.frm.script_manager.trigger("is_pos"); this.frm.refresh_fields(); From d4d5a4221a00771864d3b27eeb9a4f9580614c04 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 24 Aug 2021 16:22:46 +0530 Subject: [PATCH 691/691] feat: coupon code discount in pos invoice (#27004) --- .../doctype/pos_invoice/pos_invoice.json | 9 +++++++++ .../doctype/pos_invoice/pos_invoice.py | 11 +++++++++++ .../doctype/pricing_rule/pricing_rule.py | 9 ++++++++- erpnext/public/js/controllers/transaction.js | 19 +++++++++++++------ erpnext/public/scss/point-of-sale.scss | 2 ++ 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 3e22b9e00a8c..b8195374002b 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -99,6 +99,7 @@ "loyalty_redemption_account", "loyalty_redemption_cost_center", "section_break_49", + "coupon_code", "apply_discount_on", "base_discount_amount", "column_break_51", @@ -1550,6 +1551,14 @@ "no_copy": 1, "options": "Sales Invoice", "read_only": 1 + }, + { + "depends_on": "coupon_code", + "fieldname": "coupon_code", + "fieldtype": "Link", + "label": "Coupon Code", + "options": "Coupon Code", + "print_hide": 1 } ], "icon": "fa fa-file-text", diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 8ec4ef224cda..759cad53d4ac 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -44,6 +44,9 @@ def validate(self): self.validate_pos() self.validate_payment_amount() self.validate_loyalty_transaction() + if self.coupon_code: + from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code + validate_coupon_code(self.coupon_code) def on_submit(self): # create the loyalty point ledger entry if the customer is enrolled in any loyalty program @@ -58,6 +61,10 @@ def on_submit(self): self.check_phone_payments() self.set_status(update=True) + if self.coupon_code: + from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count + update_coupon_code_count(self.coupon_code,'used') + def before_cancel(self): if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1: pos_closing_entry = frappe.get_all( @@ -84,6 +91,10 @@ def on_cancel(self): against_psi_doc.delete_loyalty_point_entry() against_psi_doc.make_loyalty_point_entry() + if self.coupon_code: + from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count + update_coupon_code_count(self.coupon_code,'cancelled') + def check_phone_payments(self): for pay in self.payments: if pay.type == "Phone" and pay.amount >= 0: diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 556f49d34c02..4903c50e17b3 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -198,12 +198,19 @@ def apply_pricing_rule(args, doc=None): set_serial_nos_based_on_fifo = frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo") + item_code_list = tuple(item.get('item_code') for item in item_list) + query_items = frappe.get_all('Item', fields=['item_code','has_serial_no'], filters=[['item_code','in',item_code_list]],as_list=1) + serialized_items = dict() + for item_code, val in query_items: + serialized_items.setdefault(item_code, val) + for item in item_list: args_copy = copy.deepcopy(args) args_copy.update(item) data = get_pricing_rule_for_item(args_copy, item.get('price_list_rate'), doc=doc) out.append(data) - if not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'): + + if serialized_items.get(item.get('item_code')) and not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'): out[0].update(get_serial_no_for_item(args_copy)) return out diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 9375e358a970..2538852bfa55 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2242,12 +2242,19 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe coupon_code() { var me = this; - frappe.run_serially([ - () => this.frm.doc.ignore_pricing_rule=1, - () => me.ignore_pricing_rule(), - () => this.frm.doc.ignore_pricing_rule=0, - () => me.apply_pricing_rule() - ]); + if (this.frm.doc.coupon_code) { + frappe.run_serially([ + () => this.frm.doc.ignore_pricing_rule=1, + () => me.ignore_pricing_rule(), + () => this.frm.doc.ignore_pricing_rule=0, + () => me.apply_pricing_rule() + ]); + } else { + frappe.run_serially([ + () => this.frm.doc.ignore_pricing_rule=1, + () => me.ignore_pricing_rule() + ]); + } } }; diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index c77b2ce3dfa1..1677e9b3de44 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -860,6 +860,8 @@ .invoice-fields { overflow-y: scroll; + height: 100%; + padding-right: var(--padding-sm); } }