diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index dc4c69d9e8..b70c6d2b27 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -121,7 +121,7 @@ frappe.treeview_settings["Account"] = { }, onrender: function(node) { if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){ - var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr"; + var dr_or_cr = in_list(["Liability", "Income", "Equity"], node.data.root_type) ? "Cr" : "Dr"; if (node.data && node.data.balance!==undefined) { $('' + (node.data.balance_in_account_currency ? diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index fcbd10f606..dd20632a65 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -9,6 +9,26 @@ frappe.ui.form.on('Accounting Dimension', { frappe.set_route("List", frm.doc.document_type); }); } + + let button = frm.doc.disabled ? "Enable" : "Disable"; + + frm.add_custom_button(__(button), function() { + + frm.set_value('disabled', 1 - frm.doc.disabled); + + frappe.call({ + method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension", + args: { + doc: frm.doc + }, + freeze: true, + callback: function(r) { + let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled"; + frm.save(); + frappe.show_alert({message:__(message), indicator:'green'}); + } + }); + }); }, document_type: function(frm) { @@ -21,13 +41,4 @@ frappe.ui.form.on('Accounting Dimension', { } }); }, - - disabled: function(frm) { - frappe.call({ - method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension", - args: { - doc: frm.doc - } - }); - } }); diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json index 1e2bb925a1..57543a0ef4 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.json @@ -38,7 +38,8 @@ "default": "0", "fieldname": "disabled", "fieldtype": "Check", - "label": "Disable" + "label": "Disable", + "read_only": 1 }, { "default": "0", @@ -53,7 +54,7 @@ "label": "Mandatory For Profit and Loss Account" } ], - "modified": "2019-05-27 18:18:17.792726", + "modified": "2019-07-07 18:56:19.517450", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting Dimension", diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 91d03be493..15ace7239e 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -121,11 +121,11 @@ def delete_accounting_dimension(doc): @frappe.whitelist() def disable_dimension(doc): if frappe.flags.in_test: - frappe.enqueue(start_dimension_disabling, doc=doc) + toggle_disabling(doc=doc) else: - start_dimension_disabling(doc=doc) + frappe.enqueue(toggle_disabling, doc=doc) -def start_dimension_disabling(doc): +def toggle_disabling(doc): doc = json.loads(doc) if doc.get('disabled'): diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index c2372bd111..4ff4212920 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -10,6 +10,7 @@ "acc_frozen_upto", "frozen_accounts_modifier", "determine_address_tax_category_from", + "over_billing_allowance", "column_break_4", "credit_controller", "check_supplier_invoice_uniqueness", @@ -168,12 +169,18 @@ "fieldname": "automatically_fetch_payment_terms", "fieldtype": "Check", "label": "Automatically Fetch Payment Terms" + }, + { + "description": "Percentage you are allowed to bill more against the amount ordered. For example: If the order value is $100 for an item and tolerance is set as 10% then you are allowed to bill for $110.", + "fieldname": "over_billing_allowance", + "fieldtype": "Currency", + "label": "Over Billing Allowance (%)" } ], "icon": "icon-cog", "idx": 1, "issingle": 1, - "modified": "2019-04-28 18:20:55.789946", + "modified": "2019-07-04 18:20:55.789946", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", @@ -200,4 +207,4 @@ "quick_entry": 1, "sort_order": "ASC", "track_changes": 1 - } \ No newline at end of file + } diff --git a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py index 28807c4118..90cdf834c5 100644 --- a/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/doctype/bank_reconciliation/bank_reconciliation.py @@ -103,7 +103,7 @@ class BankReconciliation(Document): for d in self.get('payment_entries'): if d.clearance_date: if not d.payment_document: - frappe.throw(_("Row #{0}: Payment document is required to complete the trasaction")) + frappe.throw(_("Row #{0}: Payment document is required to complete the transaction")) if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date): frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}") @@ -113,10 +113,8 @@ class BankReconciliation(Document): if not d.clearance_date: d.clearance_date = None - frappe.db.set_value(d.payment_document, d.payment_entry, "clearance_date", d.clearance_date) - frappe.db.sql("""update `tab{0}` set clearance_date = %s, modified = %s - where name=%s""".format(d.payment_document), - (d.clearance_date, nowdate(), d.payment_entry)) + payment_entry = frappe.get_doc(d.payment_document, d.payment_entry) + payment_entry.db_set('clearance_date', d.clearance_date) clearance_date_updated = True diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js index aea6080288..40a97ae295 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js @@ -78,7 +78,6 @@ var validate_csv_data = function(frm) { var create_import_button = function(frm) { frm.page.set_primary_action(__("Start Import"), function () { - setup_progress_bar(frm); frappe.call({ method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa", args: { @@ -86,11 +85,11 @@ var create_import_button = function(frm) { company: frm.doc.company }, freeze: true, + freeze_message: __("Creating Accounts..."), callback: function(r) { if(!r.exc) { clearInterval(frm.page["interval"]); frm.page.set_indicator(__('Import Successfull'), 'blue'); - frappe.hide_progress(); create_reset_button(frm); } } @@ -126,13 +125,3 @@ var generate_tree_preview = function(frm) { } }); }; - -var setup_progress_bar = function(frm) { - frm.page["seconds_elapsed"] = 0; - frm.page["execution_time"] = (frm.page["total_accounts"] > 100) ? 100 : frm.page["total_accounts"]; - - frm.page["interval"] = setInterval(function() { - frm.page["seconds_elapsed"] += 1; - frappe.show_progress(__('Creating Accounts'), frm.page["seconds_elapsed"], frm.page["execution_time"]); - }, 250); -}; \ No newline at end of file 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 76efa486f8..4683c7ae46 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 @@ -33,6 +33,9 @@ def import_coa(file_name, company): def generate_data_from_csv(file_name, as_dict=False): ''' read csv file and return the generated nested tree ''' + if not file_name.endswith('.csv'): + frappe.throw("Only CSV files can be used to for importing data. Please check the file format you are trying to upload") + file_doc = frappe.get_doc('File', {"file_url": file_name}) file_path = file_doc.get_full_path() @@ -96,15 +99,27 @@ def build_forest(data): return [child] + return_parent(data, parent_account) charts_map, paths = {}, [] + + line_no = 3 + error_messages = [] + for i in data: account_name, _, account_number, is_group, account_type, root_type = i + + if not account_name: + error_messages.append("Row {0}: Please enter Account Name".format(line_no)) + charts_map[account_name] = {} - if is_group: charts_map[account_name]["is_group"] = is_group + if is_group == 1: charts_map[account_name]["is_group"] = is_group if account_type: charts_map[account_name]["account_type"] = account_type if root_type: charts_map[account_name]["root_type"] = root_type if account_number: charts_map[account_name]["account_number"] = account_number path = return_parent(data, account_name)[::-1] paths.append(path) # List of path is created + line_no += 1 + + if error_messages: + frappe.throw("
".join(error_messages)) out = {} for path in paths: @@ -150,22 +165,27 @@ def validate_root(accounts): if len(roots) < 4: return _("Number of root accounts cannot be less than 4") + error_messages = [] + for account in roots: - if not account.get("root_type"): - return _("Please enter Root Type for - {0}").format(account.get("account_name")) - elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity"): - return _('Root Type for "{0}" must be one of the Asset, Liability, Income, Expense and Equity').format(account.get("account_name")) + if not account.get("root_type") and account.get("account_name"): + error_messages.append("Please enter Root Type for account- {0}".format(account.get("account_name"))) + elif account.get("root_type") not in ("Asset", "Liability", "Expense", "Income", "Equity") and account.get("account_name"): + error_messages.append("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity".format(account.get("account_name"))) + + if error_messages: + return "
".join(error_messages) def validate_account_types(accounts): account_types_for_ledger = ["Cost of Goods Sold", "Depreciation", "Fixed Asset", "Payable", "Receivable", "Stock Adjustment"] - account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group']] + account_types = [accounts[d]["account_type"] for d in accounts if not accounts[d]['is_group'] == 1] missing = list(set(account_types_for_ledger) - set(account_types)) if missing: return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing)) account_types_for_group = ["Bank", "Cash", "Stock"] - account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group']] + account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group'] not in ('', 1)] missing = list(set(account_types_for_group) - set(account_groups)) if missing: diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json index 0d04b19fd1..8d7ed74eb9 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json @@ -1,177 +1,64 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, "creation": "2019-03-07 12:07:09.416101", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "sales_invoice", + "customer", + "column_break_3", + "posting_date", + "outstanding_amount" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sales_invoice", "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": "Invoice", - "length": 0, - "no_copy": 0, "options": "Sales Invoice", - "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_from": "sales_invoice.customer", "fieldname": "customer", "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": "Customer", - "length": 0, - "no_copy": 0, "options": "Customer", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "sales_invoice.posting_date", "fieldname": "posting_date", "fieldtype": "Date", - "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": "Date", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "sales_invoice.grand_total", "fieldname": "outstanding_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Outstanding Amount", - "length": 0, - "no_copy": 0, "options": "Company:company:default_currency", - "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 + "read_only": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, "istable": 1, - "max_attachments": 0, - "modified": "2019-03-07 16:38:03.622666", + "modified": "2019-05-30 19:27:29.436153", "modified_by": "Administrator", "module": "Accounts", "name": "Discounted Invoice", - "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 + "track_changes": 1 } \ No newline at end of file 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 dad75b4ba1..0d5456ece6 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js @@ -21,9 +21,29 @@ frappe.ui.form.on('Exchange Rate Revaluation', { refresh: function(frm) { if(frm.doc.docstatus==1) { - frm.add_custom_button(__('Create Journal Entry'), function() { - return frm.events.make_jv(frm); - }); + frappe.db.get_value("Journal Entry Account", { + 'reference_type': 'Exchange Rate Revaluation', + 'reference_name': frm.doc.name, + 'docstatus': 1 + }, "sum(debit) as sum", (r) =>{ + let total_amt = 0; + frm.doc.accounts.forEach(d=> { + total_amt = total_amt + d['new_balance_in_base_currency']; + }); + if(total_amt === r.sum) { + frm.add_custom_button(__("Journal Entry"), function(){ + frappe.route_options = { + 'reference_type': 'Exchange Rate Revaluation', + 'reference_name': frm.doc.name + }; + frappe.set_route("List", "Journal Entry"); + }, __("View")); + } else { + frm.add_custom_button(__('Create Journal Entry'), function() { + return frm.events.make_jv(frm); + }); + } + }, 'Journal Entry'); } }, diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index 333d39aae6..a232a953f3 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -848,6 +848,39 @@ "set_only_once": 0, "translatable": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fetch_if_empty": 0, + "fieldname": "due_date", + "fieldtype": "Date", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Due Date", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 } ], "has_web_view": 0, @@ -861,7 +894,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-01-07 07:05:00.366399", + "modified": "2019-05-01 07:05:00.366399", "modified_by": "Administrator", "module": "Accounts", "name": "GL Entry", diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json index 8927ca708d..3bfe259432 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json @@ -1,744 +1,177 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, "autoname": "ACC-INV-DISC-.YYYY.-.#####", - "beta": 0, "creation": "2019-03-07 12:01:56.296952", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "posting_date", + "loan_start_date", + "loan_period", + "loan_end_date", + "column_break_3", + "status", + "company", + "section_break_5", + "invoices", + "section_break_7", + "total_amount", + "column_break_9", + "bank_charges", + "section_break_6", + "short_term_loan", + "bank_account", + "bank_charges_account", + "column_break_15", + "accounts_receivable_credit", + "accounts_receivable_discounted", + "accounts_receivable_unpaid", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "Today", "fieldname": "posting_date", "fieldtype": "Date", - "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": "Posting Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "loan_start_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Loan Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Loan Start Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "loan_period", "fieldtype": "Int", - "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": "Loan Period", - "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": "Loan Period (Days)" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "loan_end_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Loan End Date", - "length": 0, "no_copy": 1, - "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 + "read_only": 1 }, { - "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 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "status", "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": "Status", - "length": 0, "no_copy": 1, "options": "Draft\nSanctioned\nDisbursed\nSettled\nCancelled", - "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 + "read_only": 1 }, { - "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": 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, "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "invoices", "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": "Invoices", - "length": 0, - "no_copy": 0, "options": "Discounted Invoice", - "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, "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, - "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, "fieldname": "total_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Amount", - "length": 0, - "no_copy": 0, "options": "Company:company:default_currency", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_9", - "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, "fieldname": "bank_charges", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Bank Charges", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_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": "Company:company:default_currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "short_term_loan", "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": "Short Term Loan 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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "bank_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": "Bank 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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "bank_charges_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": "Bank Charges 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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_15", - "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, "fieldname": "accounts_receivable_credit", "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": "Accounts Receivable Credit 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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "accounts_receivable_discounted", "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": "Accounts Receivable Discounted 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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "accounts_receivable_unpaid", "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": "Accounts Receivable Unpaid 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 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "amended_from", "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": "Amended From", - "length": 0, "no_copy": 1, "options": "Invoice Discounting", - "permlevel": 0, "print_hide": 1, - "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 + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-08 14:24:31.222027", + "modified": "2019-05-30 19:08:21.199759", "modified_by": "Administrator", "module": "Accounts", "name": "Invoice Discounting", - "name_case": "", "owner": "Administrator", "permissions": [ { @@ -748,26 +181,17 @@ "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, "submit": 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 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index c8756af7d7..29475d5644 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -28,18 +28,39 @@ class InvoiceDiscounting(AccountsController): self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices]) def on_submit(self): + self.update_sales_invoice() self.make_gl_entries() def on_cancel(self): self.set_status() + self.update_sales_invoice() self.make_gl_entries() - def set_status(self): - self.status = "Draft" - if self.docstatus == 1: - self.status = "Sanctioned" - elif self.docstatus == 2: - self.status = "Cancelled" + def set_status(self, status=None): + if status: + self.status = status + self.db_set("status", status) + for d in self.invoices: + frappe.get_doc("Sales Invoice", d.sales_invoice).set_status(update=True, update_modified=False) + else: + self.status = "Draft" + if self.docstatus == 1: + self.status = "Sanctioned" + elif self.docstatus == 2: + self.status = "Cancelled" + + def update_sales_invoice(self): + for d in self.invoices: + if self.docstatus == 1: + is_discounted = 1 + else: + discounted_invoice = frappe.db.exists({ + "doctype": "Discounted Invoice", + "sales_invoice": d.sales_invoice, + "docstatus": 1 + }) + is_discounted = 1 if discounted_invoice else 0 + frappe.db.set_value("Sales Invoice", d.sales_invoice, "is_discounted", is_discounted) def make_gl_entries(self): company_currency = frappe.get_cached_value('Company', self.company, "default_currency") diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 3132c937dd..eb75d0d284 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -105,24 +105,28 @@ class JournalEntry(AccountsController): invoice_discounting_list = list(set([d.reference_name for d in self.accounts if d.reference_type=="Invoice Discounting"])) for inv_disc in invoice_discounting_list: - short_term_loan_account, id_status = frappe.db.get_value("Invoice Discounting", inv_disc, ["short_term_loan", "status"]) + inv_disc_doc = frappe.get_doc("Invoice Discounting", inv_disc) + status = None for d in self.accounts: - if d.account == short_term_loan_account and d.reference_name == inv_disc: + if d.account == inv_disc_doc.short_term_loan and d.reference_name == inv_disc: if self.docstatus == 1: if d.credit > 0: - _validate_invoice_discounting_status(inv_disc, id_status, "Sanctioned", d.idx) + _validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Sanctioned", d.idx) status = "Disbursed" elif d.debit > 0: - _validate_invoice_discounting_status(inv_disc, id_status, "Disbursed", d.idx) + _validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Disbursed", d.idx) status = "Settled" else: if d.credit > 0: - _validate_invoice_discounting_status(inv_disc, id_status, "Disbursed", d.idx) + _validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Disbursed", d.idx) status = "Sanctioned" elif d.debit > 0: - _validate_invoice_discounting_status(inv_disc, id_status, "Settled", d.idx) + _validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Settled", d.idx) status = "Disbursed" - frappe.db.set_value("Invoice Discounting", inv_disc, "status", status) + break + if status: + inv_disc_doc.set_status(status=status) + def unlink_advance_entry_reference(self): for d in self.get("accounts"): @@ -498,6 +502,7 @@ class JournalEntry(AccountsController): self.get_gl_dict({ "account": d.account, "party_type": d.party_type, + "due_date": self.due_date, "party": d.party, "against": d.against_account, "debit": flt(d.debit, d.precision("debit")), diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 2c382c58f6..f17b2cbeda 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -302,7 +302,7 @@ frappe.ui.form.on('Payment Entry', { }, () => frm.set_value("party_balance", r.message.party_balance), () => frm.set_value("party_name", r.message.party_name), - () => frm.events.get_outstanding_documents(frm), + () => frm.clear_table("references"), () => frm.events.hide_unhide_fields(frm), () => frm.events.set_dynamic_labels(frm), () => { @@ -323,9 +323,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_account_currency_and_balance(frm, frm.doc.paid_from, "paid_from_account_currency", "paid_from_account_balance", function(frm) { - if (frm.doc.payment_type == "Receive") { - frm.events.get_outstanding_documents(frm); - } else if (frm.doc.payment_type == "Pay") { + if (frm.doc.payment_type == "Pay") { frm.events.paid_amount(frm); } } @@ -337,9 +335,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_account_currency_and_balance(frm, frm.doc.paid_to, "paid_to_account_currency", "paid_to_account_balance", function(frm) { - if(frm.doc.payment_type == "Pay") { - frm.events.get_outstanding_documents(frm); - } else if (frm.doc.payment_type == "Receive") { + if (frm.doc.payment_type == "Receive") { if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { if(frm.doc.source_exchange_rate) { frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); @@ -533,26 +529,87 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_unallocated_amount(frm); }, - get_outstanding_documents: function(frm) { + get_outstanding_invoice: function(frm) { + const today = frappe.datetime.get_today(); + const fields = [ + {fieldtype:"Section Break", label: __("Posting Date")}, + {fieldtype:"Date", label: __("From Date"), + fieldname:"from_posting_date", default:frappe.datetime.add_days(today, -30)}, + {fieldtype:"Column Break"}, + {fieldtype:"Date", label: __("To Date"), fieldname:"to_posting_date", default:today}, + {fieldtype:"Section Break", label: __("Due Date")}, + {fieldtype:"Date", label: __("From Date"), fieldname:"from_due_date"}, + {fieldtype:"Column Break"}, + {fieldtype:"Date", label: __("To Date"), fieldname:"to_due_date"}, + {fieldtype:"Section Break", label: __("Outstanding Amount")}, + {fieldtype:"Float", label: __("Greater Than Amount"), + fieldname:"outstanding_amt_greater_than", default: 0}, + {fieldtype:"Column Break"}, + {fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"}, + {fieldtype:"Section Break"}, + {fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1}, + ]; + + frappe.prompt(fields, function(filters){ + frappe.flags.allocate_payment_amount = true; + frm.events.validate_filters_data(frm, filters); + frm.events.get_outstanding_documents(frm, filters); + }, __("Filters"), __("Get Outstanding Invoices")); + }, + + validate_filters_data: function(frm, filters) { + const fields = { + 'Posting Date': ['from_posting_date', 'to_posting_date'], + 'Due Date': ['from_posting_date', 'to_posting_date'], + 'Advance Amount': ['from_posting_date', 'to_posting_date'], + }; + + for (let key in fields) { + let from_field = fields[key][0]; + let to_field = fields[key][1]; + + if (filters[from_field] && !filters[to_field]) { + frappe.throw(__("Error: {0} is mandatory field", + [to_field.replace(/_/g, " ")] + )); + } else if (filters[from_field] && filters[from_field] > filters[to_field]) { + frappe.throw(__("{0}: {1} must be less than {2}", + [key, from_field.replace(/_/g, " "), to_field.replace(/_/g, " ")] + )); + } + } + }, + + get_outstanding_documents: function(frm, filters) { frm.clear_table("references"); - if(!frm.doc.party) return; + if(!frm.doc.party) { + return; + } frm.events.check_mandatory_to_fetch(frm); var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; + var args = { + "posting_date": frm.doc.posting_date, + "company": frm.doc.company, + "party_type": frm.doc.party_type, + "payment_type": frm.doc.payment_type, + "party": frm.doc.party, + "party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to, + "cost_center": frm.doc.cost_center + } + + for (let key in filters) { + args[key] = filters[key]; + } + + frappe.flags.allocate_payment_amount = filters['allocate_payment_amount']; + return frappe.call({ method: 'erpnext.accounts.doctype.payment_entry.payment_entry.get_outstanding_reference_documents', args: { - args: { - "posting_date": frm.doc.posting_date, - "company": frm.doc.company, - "party_type": frm.doc.party_type, - "payment_type": frm.doc.payment_type, - "party": frm.doc.party, - "party_account": frm.doc.payment_type=="Receive" ? frm.doc.paid_from : frm.doc.paid_to, - "cost_center": frm.doc.cost_center - } + args:args }, callback: function(r, rt) { if(r.message) { @@ -608,25 +665,11 @@ frappe.ui.form.on('Payment Entry', { frm.events.allocate_party_amount_against_ref_docs(frm, (frm.doc.payment_type=="Receive" ? frm.doc.paid_amount : frm.doc.received_amount)); + } }); }, - allocate_payment_amount: function(frm) { - if(frm.doc.payment_type == 'Internal Transfer'){ - return - } - - if(frm.doc.references.length == 0){ - frm.events.get_outstanding_documents(frm); - } - if(frm.doc.payment_type == 'Internal Transfer') { - frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.paid_amount); - } else { - frm.events.allocate_party_amount_against_ref_docs(frm, frm.doc.received_amount); - } - }, - allocate_party_amount_against_ref_docs: function(frm, paid_amount) { var total_positive_outstanding_including_order = 0; var total_negative_outstanding = 0; @@ -677,7 +720,7 @@ frappe.ui.form.on('Payment Entry', { $.each(frm.doc.references || [], function(i, row) { row.allocated_amount = 0 //If allocate payment amount checkbox is unchecked, set zero to allocate amount - if(frm.doc.allocate_payment_amount){ + if(frappe.flags.allocate_payment_amount){ if(row.outstanding_amount > 0 && allocated_positive_outstanding > 0) { if(row.outstanding_amount >= allocated_positive_outstanding) { row.allocated_amount = allocated_positive_outstanding; @@ -958,7 +1001,7 @@ frappe.ui.form.on('Payment Entry', { }, () => { if(frm.doc.payment_type != "Internal") { - frm.events.get_outstanding_documents(frm); + frm.clear_table("references"); } } ]); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index fcaa570331..a85eccd30a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -40,7 +40,7 @@ "target_exchange_rate", "base_received_amount", "section_break_14", - "allocate_payment_amount", + "get_outstanding_invoice", "references", "section_break_34", "total_allocated_amount", @@ -325,19 +325,15 @@ "reqd": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "references", "depends_on": "eval:(doc.party && doc.paid_from && doc.paid_to && doc.paid_amount && doc.received_amount)", "fieldname": "section_break_14", "fieldtype": "Section Break", "label": "Reference" }, { - "default": "1", - "depends_on": "eval:in_list(['Pay', 'Receive'], doc.payment_type)", - "fieldname": "allocate_payment_amount", - "fieldtype": "Check", - "label": "Allocate Payment Amount" + "fieldname": "get_outstanding_invoice", + "fieldtype": "Button", + "label": "Get Outstanding Invoice" }, { "fieldname": "references", @@ -570,7 +566,7 @@ } ], "is_submittable": 1, - "modified": "2019-05-25 22:02:40.575822", + "modified": "2019-05-27 15:53:21.108857", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index ea76126d8c..699f04675f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -574,8 +574,8 @@ def get_outstanding_reference_documents(args): # Get negative outstanding sales /purchase invoices negative_outstanding_invoices = [] if args.get("party_type") not in ["Student", "Employee"] and not args.get("voucher_no"): - negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), - args.get("party"), args.get("party_account"), party_account_currency, company_currency) + negative_outstanding_invoices = get_negative_outstanding_invoices(args.get("party_type"), args.get("party"), + args.get("party_account"), args.get("company"), party_account_currency, company_currency) # Get positive outstanding sales /purchase invoices/ Fees condition = "" @@ -585,10 +585,23 @@ def get_outstanding_reference_documents(args): # Add cost center condition if args.get("cost_center") and get_allow_cost_center_in_entry_of_bs_account(): - condition += " and cost_center='%s'" % args.get("cost_center") + condition += " and cost_center='%s'" % args.get("cost_center") + + date_fields_dict = { + 'posting_date': ['from_posting_date', 'to_posting_date'], + 'due_date': ['from_due_date', 'to_due_date'] + } + + for fieldname, date_fields in date_fields_dict.items(): + if args.get(date_fields[0]) and args.get(date_fields[1]): + condition += " and {0} between '{1}' and '{2}'".format(fieldname, + args.get(date_fields[0]), args.get(date_fields[1])) + + if args.get("company"): + condition += " and company = {0}".format(frappe.db.escape(args.get("company"))) outstanding_invoices = get_outstanding_invoices(args.get("party_type"), args.get("party"), - args.get("party_account"), condition=condition) + args.get("party_account"), filters=args, condition=condition, limit=100) for d in outstanding_invoices: d["exchange_rate"] = 1 @@ -606,12 +619,19 @@ def get_outstanding_reference_documents(args): orders_to_be_billed = [] if (args.get("party_type") != "Student"): orders_to_be_billed = get_orders_to_be_billed(args.get("posting_date"),args.get("party_type"), - args.get("party"), party_account_currency, company_currency) + args.get("party"), args.get("company"), party_account_currency, company_currency, filters=args) - return negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed + data = negative_outstanding_invoices + outstanding_invoices + orders_to_be_billed + + if not data: + frappe.msgprint(_("No outstanding invoices found for the {0} {1}.") + .format(args.get("party_type").lower(), args.get("party"))) + + return data -def get_orders_to_be_billed(posting_date, party_type, party, party_account_currency, company_currency, cost_center=None): +def get_orders_to_be_billed(posting_date, party_type, party, + company, party_account_currency, company_currency, cost_center=None, filters=None): if party_type == "Customer": voucher_type = 'Sales Order' elif party_type == "Supplier": @@ -641,6 +661,7 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre where {party_type} = %s and docstatus = 1 + and company = %s and ifnull(status, "") != "Closed" and {ref_field} > advance_paid and abs(100 - per_billed) > 0.01 @@ -652,10 +673,14 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre "voucher_type": voucher_type, "party_type": scrub(party_type), "condition": condition - }), party, as_dict=True) + }), (party, company), as_dict=True) order_list = [] for d in orders: + if not (d.outstanding_amount >= filters.get("outstanding_amt_greater_than") + and d.outstanding_amount <= filters.get("outstanding_amt_less_than")): + continue + d["voucher_type"] = voucher_type # This assumes that the exchange rate required is the one in the SO d["exchange_rate"] = get_exchange_rate(party_account_currency, company_currency, posting_date) @@ -663,7 +688,8 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre return order_list -def get_negative_outstanding_invoices(party_type, party, party_account, party_account_currency, company_currency, cost_center=None): +def get_negative_outstanding_invoices(party_type, party, party_account, + company, party_account_currency, company_currency, cost_center=None): voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice" supplier_condition = "" if voucher_type == "Purchase Invoice": @@ -684,7 +710,8 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac from `tab{voucher_type}` where - {party_type} = %s and {party_account} = %s and docstatus = 1 and outstanding_amount < 0 + {party_type} = %s and {party_account} = %s and docstatus = 1 and + company = %s and outstanding_amount < 0 {supplier_condition} order by posting_date, name @@ -696,7 +723,7 @@ def get_negative_outstanding_invoices(party_type, party, party_account, party_ac "party_type": scrub(party_type), "party_account": "debit_to" if party_type == "Customer" else "credit_to", "cost_center": cost_center - }), (party, party_account), as_dict=True) + }), (party, party_account, company), as_dict=True) @frappe.whitelist() @@ -924,7 +951,6 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= pe.paid_to_account_currency = party_account_currency if payment_type=="Pay" else bank.account_currency pe.paid_amount = paid_amount pe.received_amount = received_amount - pe.allocate_payment_amount = 1 pe.letter_head = doc.get("letter_head") if pe.party_type in ["Customer", "Supplier"]: diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index 10f127a53f..5e94118d60 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -8,6 +8,10 @@ frappe.ui.form.on("POS Profile", "onload", function(frm) { return { filters: { selling: 1 } }; }); + frm.set_query("tc_name", function() { + return { filters: { selling: 1 } }; + }); + erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index a6f6acea66..219d9899d2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -337,7 +337,8 @@ class PurchaseInvoice(BuyingController): if not self.is_return: self.update_against_document_in_jv() self.update_billing_status_for_zero_amount_refdoc("Purchase Order") - self.update_billing_status_in_pr() + + self.update_billing_status_in_pr() # Updating stock ledger should always be called after updating prevdoc status, # because updating ordered qty in bin depends upon updated ordered qty in PO @@ -416,6 +417,7 @@ class PurchaseInvoice(BuyingController): "account": self.credit_to, "party_type": "Supplier", "party": self.supplier, + "due_date": self.due_date, "against": self.against_expense_account, "credit": grand_total_in_company_currency, "credit_in_account_currency": grand_total_in_company_currency \ @@ -484,9 +486,13 @@ class PurchaseInvoice(BuyingController): "credit": flt(item.rm_supp_cost) }, warehouse_account[self.supplier_warehouse]["account_currency"], item=item)) elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()): + + expense_account = (item.expense_account + if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) + gl_entries.append( self.get_gl_dict({ - "account": item.expense_account if not item.enable_deferred_expense else item.deferred_expense_account, + "account": expense_account, "against": self.supplier, "debit": flt(item.base_net_amount, item.precision("base_net_amount")), "debit_in_account_currency": (flt(item.base_net_amount, @@ -769,7 +775,8 @@ class PurchaseInvoice(BuyingController): if not self.is_return: self.update_billing_status_for_zero_amount_refdoc("Purchase Order") - self.update_billing_status_in_pr() + + self.update_billing_status_in_pr() # Updating stock ledger should always be called after updating prevdoc status, # because updating ordered qty in bin depends upon updated ordered qty in PO diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 07494a27d6..1fe6895601 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -83,10 +83,14 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte } } - if (doc.outstanding_amount>0 && !cint(doc.is_return)) { + if (doc.outstanding_amount>0) { cur_frm.add_custom_button(__('Payment Request'), function() { me.make_payment_request(); }, __('Create')); + + cur_frm.add_custom_button(__('Invoice Discounting'), function() { + cur_frm.events.create_invoice_discounting(cur_frm); + }, __('Create')); } if (doc.docstatus === 1) { @@ -187,9 +191,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte method: "erpnext.selling.doctype.quotation.quotation.make_sales_invoice", source_doctype: "Quotation", target: me.frm, - setters: { - customer: me.frm.doc.customer || undefined, - }, + setters: [{ + fieldtype: 'Link', + label: __('Customer'), + options: 'Customer', + fieldname: 'party_name', + default: me.frm.doc.customer, + }], get_query_filters: { docstatus: 1, status: ["!=", "Lost"], @@ -804,6 +812,13 @@ frappe.ui.form.on('Sales Invoice', { frm.set_df_property("patient_name", "hidden", 1); frm.set_df_property("ref_practitioner", "hidden", 1); } + }, + + create_invoice_discounting: function(frm) { + frappe.model.open_mapped_doc({ + method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.create_invoice_discounting", + frm: frm + }); } }) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 4aec1c82c5..1f578366d9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -155,6 +155,7 @@ "inter_company_invoice_reference", "customer_group", "campaign", + "is_discounted", "col_break23", "status", "source", @@ -1324,6 +1325,13 @@ "options": "Campaign", "print_hide": 1 }, + { + "fieldname": "is_discounted", + "fieldtype": "Check", + "label": "Is Discounted", + "no_copy": 1, + "read_only": 1 + }, { "fieldname": "col_break23", "fieldtype": "Column Break", @@ -1336,7 +1344,7 @@ "in_standard_filter": 1, "label": "Status", "no_copy": 1, - "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nOverdue\nCancelled", + "options": "\nDraft\nReturn\nCredit Note Issued\nSubmitted\nPaid\nUnpaid\nUnpaid and Discounted\nOverdue and Discounted\nOverdue\nOverdue\nCancelled", "print_hide": 1, "read_only": 1 }, @@ -1558,7 +1566,7 @@ "icon": "fa fa-file-text", "idx": 181, "is_submittable": 1, - "modified": "2019-05-25 22:05:03.474745", + "modified": "2019-07-04 22:05:03.474745", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", @@ -1612,4 +1620,4 @@ "title_field": "title", "track_changes": 1, "track_seen": 1 - } \ 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 b725c73c8b..88ad6b37db 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -734,6 +734,7 @@ class SalesInvoice(SellingController): "account": self.debit_to, "party_type": "Customer", "party": self.customer, + "due_date": self.due_date, "against": self.against_income_account, "debit": grand_total_in_company_currency, "debit_in_account_currency": grand_total_in_company_currency \ @@ -783,10 +784,13 @@ class SalesInvoice(SellingController): asset.db_set("disposal_date", self.posting_date) asset.set_status("Sold" if self.docstatus==1 else None) else: - account_currency = get_account_currency(item.income_account) + 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": item.income_account if not item.enable_deferred_revenue else item.deferred_revenue_account, + "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")) @@ -1173,6 +1177,56 @@ class SalesInvoice(SellingController): self.set_missing_values(for_validate = True) + def get_discounting_status(self): + status = None + if self.is_discounted: + invoice_discounting_list = frappe.db.sql(""" + select status + from `tabInvoice Discounting` id, `tabDiscounted Invoice` d + where + id.name = d.parent + and d.sales_invoice=%s + and id.docstatus=1 + and status in ('Disbursed', 'Settled') + """, self.name) + for d in invoice_discounting_list: + status = d[0] + if status == "Disbursed": + break + return status + + def set_status(self, update=False, status=None, update_modified=True): + if self.is_new(): + if self.get('amended_from'): + self.status = 'Draft' + return + + if not status: + if self.docstatus == 2: + status = "Cancelled" + elif self.docstatus == 1: + if flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + self.status = "Overdue and Discounted" + elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()): + self.status = "Overdue" + elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + self.status = "Unpaid and Discounted" + elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()): + self.status = "Unpaid" + elif flt(self.outstanding_amount) < 0 and self.is_return==0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): + self.status = "Credit Note Issued" + elif self.is_return == 1: + self.status = "Return" + elif flt(self.outstanding_amount)<=0: + self.status = "Paid" + else: + self.status = "Submitted" + else: + self.status = "Draft" + + if update: + self.db_set('status', self.status, update_modified = update_modified) + def validate_inter_company_party(doctype, party, company, inter_company_reference): if not party: return @@ -1427,4 +1481,18 @@ def get_loyalty_programs(customer): frappe.db.set(customer, 'loyalty_program', lp_details[0]) return [] else: - return lp_details \ No newline at end of file + return lp_details + +@frappe.whitelist() +def create_invoice_discounting(source_name, target_doc=None): + invoice = frappe.get_doc("Sales Invoice", source_name) + invoice_discounting = frappe.new_doc("Invoice Discounting") + invoice_discounting.company = invoice.company + invoice_discounting.append("invoices", { + "sales_invoice": source_name, + "customer": invoice.customer, + "posting_date": invoice.posting_date, + "outstanding_amount": invoice.outstanding_amount + }) + + return invoice_discounting \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js index 52d292430a..05d49df711 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js @@ -6,17 +6,18 @@ frappe.listview_settings['Sales Invoice'] = { add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company", "currency", "is_return"], get_indicator: function(doc) { - if(flt(doc.outstanding_amount) < 0) { - return [__("Credit Note Issued"), "darkgrey", "outstanding_amount,<,0"] - } else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) { - return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"] - } else if (flt(doc.outstanding_amount) > 0 && doc.due_date < frappe.datetime.get_today()) { - return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"] - } else if(cint(doc.is_return)) { - return [__("Return"), "darkgrey", "is_return,=,Yes"]; - } else if(flt(doc.outstanding_amount)==0) { - return [__("Paid"), "green", "outstanding_amount,=,0"] - } + var status_color = { + "Draft": "grey", + "Unpaid": "orange", + "Paid": "green", + "Return": "darkgrey", + "Credit Note Issued": "darkgrey", + "Unpaid and Discounted": "orange", + "Overdue and Discounted": "red", + "Overdue": "red" + + }; + return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; }, right_column: "grand_total" }; diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 70f193e787..f6a561f04f 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -108,3 +108,14 @@ frappe.query_reports["Accounts Payable"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Payable"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); 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 06499adeea..ec4f0c983f 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -92,3 +92,14 @@ frappe.query_reports["Accounts Payable Summary"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Payable Summary"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 3661afe797..2a45454bac 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -172,3 +172,14 @@ frappe.query_reports["Accounts Receivable"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Receivable"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 29737484c7..0cda2c15dd 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _, scrub from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions class ReceivablePayableReport(object): def __init__(self, filters=None): @@ -553,6 +554,14 @@ class ReceivablePayableReport(object): conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts))) values += accounts + accounting_dimensions = get_accounting_dimensions() + + if accounting_dimensions: + for dimension in accounting_dimensions: + if self.filters.get(dimension): + conditions.append("{0} = %s".format(dimension)) + values.append(self.filters.get(dimension)) + return " and ".join(conditions), values def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher): diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index f9162adabd..a7c0787fcd 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -116,3 +116,14 @@ frappe.query_reports["Accounts Receivable Summary"] = { }); } } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Accounts Receivable Summary"].filters.splice(9, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); 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 b2072f06f1..f2a33a83ee 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -63,9 +63,7 @@ frappe.query_reports["Budget Variance Report"] = { ] } -let dimension_filters = erpnext.get_dimension_filters(); - -dimension_filters.then((dimensions) => { +erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]); }); diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 0422111093..03940f4b24 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -8,17 +8,19 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { // The last item in the array is the definition for Presentation Currency // filter. It won't be used in cash flow for now so we pop it. Please take // of this if you are working here. - frappe.query_reports["Cash Flow"]["filters"].pop(); - frappe.query_reports["Cash Flow"]["filters"].push({ - "fieldname": "accumulated_values", - "label": __("Accumulated Values"), - "fieldtype": "Check" - }); + frappe.query_reports["Cash Flow"]["filters"].splice(5, 1); - frappe.query_reports["Cash Flow"]["filters"].push({ - "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), - "fieldtype": "Check" - }); + frappe.query_reports["Cash Flow"]["filters"].push( + { + "fieldname": "accumulated_values", + "label": __("Accumulated Values"), + "fieldtype": "Check" + }, + { + "fieldname": "include_default_book_entries", + "label": __("Include Default Book Entries"), + "fieldtype": "Check" + } + ); }); \ No newline at end of file diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 75d99e75de..cd3d8dc125 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -69,7 +69,9 @@ def execute(filters=None): add_total_row_account(data, data, _("Net Change in Cash"), period_list, company_currency) columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company) - return columns, data + chart = get_chart_data(columns, data) + + return columns, data, None, chart def get_cash_flow_accounts(): operation_accounts = { @@ -171,4 +173,21 @@ def add_total_row_account(out, data, label, period_list, currency, consolidated total_row["total"] += row["total"] out.append(total_row) - out.append({}) \ No newline at end of file + out.append({}) + +def get_chart_data(columns, data): + labels = [d.get("label") for d in columns[2:]] + datasets = [{'name':account.get('account').replace("'", ""), 'values': [account.get('total')]} for account in data if account.get('parent_account') == None and account.get('currency')] + datasets = datasets[:-1] + + chart = { + "data": { + 'labels': labels, + 'datasets': datasets + }, + "type": "bar" + } + + chart["fieldtype"] = "Currency" + + return chart diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 32af644021..ea82575b80 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -159,9 +159,7 @@ frappe.query_reports["General Ledger"] = { ] } -let dimension_filters = erpnext.get_dimension_filters(); - -dimension_filters.then((dimensions) => { +erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.query_reports["General Ledger"].filters.splice(15, 0 ,{ "fieldname": dimension["fieldname"], diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index f7c0250732..86fd1088f5 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -11,6 +11,7 @@ from erpnext.accounts.utils import get_account_currency from erpnext.accounts.report.financial_statements import get_cost_centers_with_children from six import iteritems from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +from collections import OrderedDict def execute(filters=None): if not filters: @@ -274,7 +275,7 @@ def group_by_field(group_by): return 'voucher_no' def initialize_gle_map(gl_entries, filters): - gle_map = frappe._dict() + gle_map = OrderedDict() group_by = group_by_field(filters.get('group_by')) for gle in gl_entries: diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index 48d7361fe0..ac11868cab 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -93,4 +93,6 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss): else: chart["type"] = "line" + chart["fieldtype"] = "Currency" + return chart \ No newline at end of file diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 80b50b92c3..d6864b54f7 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -16,7 +16,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "fieldname": "based_on", "label": __("Based On"), "fieldtype": "Select", - "options": "Cost Center\nProject", + "options": ["Cost Center", "Project"], "default": "Cost Center", "reqd": 1 }, @@ -104,5 +104,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "parent_field": "parent_account", "initial_depth": 3 } -}); + erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Profitability Analysis"].filters[1].options.push(dimension["document_type"]); + }); + }); +}); diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py index a0d8c5f0e4..6e9b31f2f6 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.py +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.py @@ -24,8 +24,17 @@ def get_accounts_data(based_on, company): if based_on == 'cost_center': return frappe.db.sql("""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt from `tabCost Center` where company=%s order by name""", company, as_dict=True) - else: + elif based_on == 'project': return frappe.get_all('Project', fields = ["name"], filters = {'company': company}, order_by = 'name') + else: + filters = {} + doctype = frappe.unscrub(based_on) + has_company = frappe.db.has_column(doctype, 'company') + + if has_company: + filters.update({'company': company}) + + return frappe.get_all(doctype, fields = ["name"], filters = filters, order_by = 'name') def get_data(accounts, filters, based_on): if not accounts: @@ -42,7 +51,7 @@ def get_data(accounts, filters, based_on): accumulate_values_into_parents(accounts, accounts_by_name) data = prepare_data(accounts, filters, total_row, parent_children_map, based_on) - data = filter_out_zero_value_rows(data, parent_children_map, + data = filter_out_zero_value_rows(data, parent_children_map, show_zero_values=filters.get("show_zero_values")) return data @@ -112,14 +121,14 @@ def prepare_data(accounts, filters, total_row, parent_children_map, based_on): for key in value_fields: row[key] = flt(d.get(key, 0.0), 3) - + if abs(row[key]) >= 0.005: # ignore zero values has_value = True row["has_value"] = has_value data.append(row) - + data.extend([{},total_row]) return data @@ -174,7 +183,7 @@ def set_gl_entries_by_account(company, from_date, to_date, based_on, gl_entries_ if from_date: additional_conditions.append("and posting_date >= %(from_date)s") - gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit, + gl_entries = frappe.db.sql("""select posting_date, {based_on} as based_on, debit, credit, is_opening, (select root_type from `tabAccount` where name = account) as type from `tabGL Entry` where company=%(company)s {additional_conditions} diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 0b48882ca9..442aa1262e 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -67,3 +67,14 @@ frappe.query_reports["Sales Register"] = { } ] } + +erpnext.dimension_filters.then((dimensions) => { + dimensions.forEach((dimension) => { + frappe.query_reports["Sales Register"].filters.splice(7, 0 ,{ + "fieldname": dimension["fieldname"], + "label": __(dimension["label"]), + "fieldtype": "Link", + "options": dimension["document_type"] + }); + }); +}); diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index de60995ca2..d08056f6f9 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe from frappe.utils import flt from frappe import msgprint, _ +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions def execute(filters=None): return _execute(filters) @@ -163,6 +164,16 @@ def get_conditions(filters): where parent=`tabSales Invoice`.name and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s)""" + accounting_dimensions = get_accounting_dimensions() + + if accounting_dimensions: + for dimension in accounting_dimensions: + if filters.get(dimension): + conditions += """ and exists(select name from `tabSales Invoice Item` + where parent=`tabSales Invoice`.name + and ifnull(`tabSales Invoice Item`.{0}, '') = %({0})s)""".format(dimension) + + return conditions def get_invoices(filters, additional_query_columns): diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 8bc72807b3..73d2ab3898 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -96,9 +96,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { } }); -let dimension_filters = erpnext.get_dimension_filters(); - -dimension_filters.then((dimensions) => { +erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{ "fieldname": dimension["fieldname"], diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 7a1f6c57c2..e75dd957db 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -628,7 +628,7 @@ def get_held_invoices(party_type, party): return held_invoices -def get_outstanding_invoices(party_type, party, account, condition=None): +def get_outstanding_invoices(party_type, party, account, condition=None, filters=None): outstanding_invoices = [] precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2 @@ -644,7 +644,8 @@ def get_outstanding_invoices(party_type, party, account, condition=None): invoice_list = frappe.db.sql(""" select - voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount + voucher_no, voucher_type, posting_date, due_date, + ifnull(sum({dr_or_cr}), 0) as invoice_amount from `tabGL Entry` where @@ -677,7 +678,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None): """.format(payment_dr_or_cr=payment_dr_or_cr), { "party_type": party_type, "party": party, - "account": account, + "account": account }, as_dict=True) pe_map = frappe._dict() @@ -688,10 +689,12 @@ def get_outstanding_invoices(party_type, party, account, condition=None): payment_amount = pe_map.get((d.voucher_type, d.voucher_no), 0) outstanding_amount = flt(d.invoice_amount - payment_amount, precision) if outstanding_amount > 0.5 / (10**precision): - if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices: - due_date = frappe.db.get_value( - d.voucher_type, d.voucher_no, "posting_date" if party_type == "Employee" else "due_date") + if (filters.get("outstanding_amt_greater_than") and + not (outstanding_amount >= filters.get("outstanding_amt_greater_than") and + outstanding_amount <= filters.get("outstanding_amt_less_than"))): + continue + if not d.voucher_type == "Purchase Invoice" or d.voucher_no not in held_invoices: outstanding_invoices.append( frappe._dict({ 'voucher_no': d.voucher_no, @@ -700,7 +703,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None): 'invoice_amount': flt(d.invoice_amount), 'payment_amount': payment_amount, 'outstanding_amount': outstanding_amount, - 'due_date': due_date + 'due_date': d.due_date }) ) @@ -731,6 +734,7 @@ def get_children(doctype, parent, company, is_root=False): parent_fieldname = 'parent_' + doctype.lower().replace(' ', '_') fields = [ 'name as value', + 'root_type', 'is_group as expandable' ] filters = [['docstatus', '<', 2]] @@ -738,7 +742,7 @@ def get_children(doctype, parent, company, is_root=False): filters.append(['ifnull(`{0}`,"")'.format(parent_fieldname), '=', '' if is_root else parent]) if is_root: - fields += ['root_type', 'report_type', 'account_currency'] if doctype == 'Account' else [] + fields += ['report_type', 'account_currency'] if doctype == 'Account' else [] filters.append(['company', '=', company]) else: diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index dead1f030c..5dce349782 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -66,10 +66,11 @@ "net_total", "total_net_weight", "taxes_section", - "taxes_and_charges", + "tax_category", "column_break_50", "shipping_rule", "section_break_52", + "taxes_and_charges", "taxes", "sec_tax_breakup", "other_charges_calculation", @@ -569,7 +570,7 @@ { "fieldname": "taxes_and_charges", "fieldtype": "Link", - "label": "Taxes and Charges", + "label": "Purchase Taxes and Charges Template", "oldfieldname": "purchase_other_charges", "oldfieldtype": "Link", "options": "Purchase Taxes and Charges Template", @@ -1032,12 +1033,18 @@ "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", "label": "Update Auto Repeat Reference" + }, + { + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, - "modified": "2019-06-24 21:22:05.483429", + "modified": "2019-07-11 18:25:49.509343", "modified_by": "Administrator", "module": "Buying", "name": "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 be68594d29..ff0b65b7be 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import unittest import frappe +import json import frappe.defaults from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from frappe.utils import flt, add_days, nowdate, getdate @@ -15,7 +16,7 @@ from erpnext.stock.doctype.material_request.test_material_request import make_ma from erpnext.stock.doctype.material_request.material_request import make_purchase_order from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.controllers.accounts_controller import update_child_qty_rate -import json +from erpnext.controllers.status_updater import OverAllowanceError class TestPurchaseOrder(unittest.TestCase): def test_make_purchase_receipt(self): @@ -41,7 +42,7 @@ class TestPurchaseOrder(unittest.TestCase): po.load_from_db() self.assertEqual(po.get("items")[0].received_qty, 4) - frappe.db.set_value('Item', '_Test Item', 'tolerance', 50) + frappe.db.set_value('Item', '_Test Item', 'over_delivery_receipt_allowance', 50) pr = create_pr_against_po(po.name, received_qty=8) self.assertEqual(get_ordered_qty(), existing_ordered_qty) @@ -57,12 +58,12 @@ class TestPurchaseOrder(unittest.TestCase): def test_ordered_qty_against_pi_with_update_stock(self): existing_ordered_qty = get_ordered_qty() - po = create_purchase_order() self.assertEqual(get_ordered_qty(), existing_ordered_qty + 10) - frappe.db.set_value('Item', '_Test Item', 'tolerance', 50) + frappe.db.set_value('Item', '_Test Item', 'over_delivery_receipt_allowance', 50) + frappe.db.set_value('Item', '_Test Item', 'over_billing_allowance', 20) pi = make_pi_from_po(po.name) pi.update_stock = 1 @@ -81,6 +82,11 @@ class TestPurchaseOrder(unittest.TestCase): po.load_from_db() self.assertEqual(po.get("items")[0].received_qty, 0) + frappe.db.set_value('Item', '_Test Item', 'over_delivery_receipt_allowance', 0) + frappe.db.set_value('Item', '_Test Item', 'over_billing_allowance', 0) + frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0) + + def test_update_child_qty_rate(self): mr = make_material_request(qty=10) po = make_purchase_order(mr.name) diff --git a/erpnext/config/accounting.py b/erpnext/config/accounting.py index ce1384aae7..ab75f211c0 100644 --- a/erpnext/config/accounting.py +++ b/erpnext/config/accounting.py @@ -6,11 +6,12 @@ import frappe def get_data(): config = [ { - "label": _("Masters and Accounts"), + "label": _("Accounts Receivable"), "items": [ { "type": "doctype", - "name": "Item", + "name": "Sales Invoice", + "description": _("Bills raised to Customers."), "onboard": 1, }, { @@ -19,12 +20,115 @@ def get_data(): "description": _("Customer database."), "onboard": 1, }, + { + "type": "doctype", + "name": "Payment Entry", + "description": _("Bank/Cash transactions against party or for internal transfer") + }, + { + "type": "doctype", + "name": "Payment Request", + "description": _("Payment Request"), + }, + { + "type": "report", + "name": "Accounts Receivable", + "doctype": "Sales Invoice", + "is_query_report": True + }, + { + "type": "report", + "name": "Accounts Receivable Summary", + "doctype": "Sales Invoice", + "is_query_report": True + }, + { + "type": "report", + "name": "Sales Register", + "doctype": "Sales Invoice", + "is_query_report": True + }, + { + "type": "report", + "name": "Item-wise Sales Register", + "is_query_report": True, + "doctype": "Sales Invoice" + }, + { + "type": "report", + "name": "Ordered Items To Be Billed", + "is_query_report": True, + "doctype": "Sales Invoice" + }, + { + "type": "report", + "name": "Delivered Items To Be Billed", + "is_query_report": True, + "doctype": "Sales Invoice" + }, + ] + }, + { + "label": _("Accounts Payable"), + "items": [ + { + "type": "doctype", + "name": "Purchase Invoice", + "description": _("Bills raised by Suppliers."), + "onboard": 1 + }, { "type": "doctype", "name": "Supplier", "description": _("Supplier database."), "onboard": 1, }, + { + "type": "doctype", + "name": "Payment Entry", + "description": _("Bank/Cash transactions against party or for internal transfer") + }, + { + "type": "report", + "name": "Accounts Payable", + "doctype": "Purchase Invoice", + "is_query_report": True + }, + { + "type": "report", + "name": "Accounts Payable Summary", + "doctype": "Purchase Invoice", + "is_query_report": True + }, + { + "type": "report", + "name": "Purchase Register", + "doctype": "Purchase Invoice", + "is_query_report": True + }, + { + "type": "report", + "name": "Item-wise Purchase Register", + "is_query_report": True, + "doctype": "Purchase Invoice" + }, + { + "type": "report", + "name": "Purchase Order Items To Be Billed", + "is_query_report": True, + "doctype": "Purchase Invoice" + }, + { + "type": "report", + "name": "Received Items To Be Billed", + "is_query_report": True, + "doctype": "Purchase Invoice" + }, + ] + }, + { + "label": _("Accounting Masters"), + "items": [ { "type": "doctype", "name": "Company", @@ -40,201 +144,31 @@ def get_data(): "description": _("Tree of financial accounts."), "onboard": 1, }, - ] - }, - { - "label": _("Billing"), - "items": [ - { - "type": "doctype", - "name": "Sales Invoice", - "description": _("Bills raised to Customers."), - "onboard": 1, - }, - { - "type": "doctype", - "name": "Purchase Invoice", - "description": _("Bills raised by Suppliers."), - "onboard": 1 - }, - { - "type": "doctype", - "name": "Payment Request", - "description": _("Payment Request"), - }, - { - "type": "doctype", - "name": "Payment Term", - "description": _("Payment Terms based on conditions") - }, - - # Reports - { - "type": "report", - "name": "Ordered Items To Be Billed", - "is_query_report": True, - "reference_doctype": "Sales Invoice" - }, - { - "type": "report", - "name": "Delivered Items To Be Billed", - "is_query_report": True, - "reference_doctype": "Sales Invoice" - }, - { - "type": "report", - "name": "Purchase Order Items To Be Billed", - "is_query_report": True, - "reference_doctype": "Purchase Invoice" - }, - { - "type": "report", - "name": "Received Items To Be Billed", - "is_query_report": True, - "reference_doctype": "Purchase Invoice" - }, - ] - - }, - { - "label": _("Settings"), - "icon": "fa fa-cog", - "items": [ { "type": "doctype", "name": "Accounts Settings", - "description": _("Default settings for accounting transactions.") }, { "type": "doctype", "name": "Fiscal Year", "description": _("Financial / accounting year.") }, - { - "type": "doctype", - "name": "Currency", - "description": _("Enable / disable currencies.") - }, - { - "type": "doctype", - "name": "Currency Exchange", - "description": _("Currency exchange rate master.") - }, - { - "type": "doctype", - "name": "Exchange Rate Revaluation", - "description": _("Exchange Rate Revaluation master.") - }, - { - "type": "doctype", - "name": "Payment Gateway Account", - "description": _("Setup Gateway accounts.") - }, - { - "type": "doctype", - "name": "Terms and Conditions", - "label": _("Terms and Conditions Template"), - "description": _("Template of terms or contract.") - }, - { - "type": "doctype", - "name": "Mode of Payment", - "description": _("e.g. Bank, Cash, Credit Card") - }, - { - "type": "doctype", - "name": "Auto Repeat", - "label": _("Auto Repeat"), - "description": _("To make recurring documents") - }, - { - "type": "doctype", - "name": "C-Form", - "description": _("C-Form records"), - "country": "India" - }, - { - "type": "doctype", - "name": "Cheque Print Template", - "description": _("Setup cheque dimensions for printing") - }, { "type": "doctype", "name": "Accounting Dimension", - "description": _("Setup custom dimensions for accounting") }, { "type": "doctype", - "name": "Opening Invoice Creation Tool", - "description": _("Create Opening Sales and Purchase Invoices") - } - ] - }, - { - "label": _("Accounting Entries"), - "items": [ - { - "type": "doctype", - "name": "Payment Entry", - "description": _("Bank/Cash transactions against party or for internal transfer") + "name": "Finance Book", }, { "type": "doctype", - "name": "Journal Entry", - "description": _("Accounting journal entries.") - } - ] - }, - { - "label": _("Financial Statements"), - "items": [ - { - "type": "report", - "name": "General Ledger", - "doctype": "GL Entry", - "is_query_report": True, + "name": "Accounting Period", }, { - "type": "report", - "name": "Accounts Receivable", - "doctype": "Sales Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Accounts Payable", - "doctype": "Purchase Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Trial Balance", - "doctype": "GL Entry", - "is_query_report": True, - }, - { - "type": "report", - "name": "Balance Sheet", - "doctype": "GL Entry", - "is_query_report": True - }, - { - "type": "report", - "name": "Cash Flow", - "doctype": "GL Entry", - "is_query_report": True - }, - { - "type": "report", - "name": "Profit and Loss Statement", - "doctype": "GL Entry", - "is_query_report": True - }, - { - "type": "report", - "name": "Consolidated Financial Statement", - "doctype": "GL Entry", - "is_query_report": True + "type": "doctype", + "name": "Payment Term", + "description": _("Payment Terms based on conditions") }, ] }, @@ -243,43 +177,10 @@ def get_data(): "items": [ { "type": "doctype", - "label": _("Bank"), - "name": "Bank", + "label": _("Match Payments with Invoices"), + "name": "Payment Reconciliation", + "description": _("Match non-linked Invoices and Payments.") }, - { - "type": "page", - "label": _("Reconcile payments and bank transactions"), - "name": "bank-reconciliation", - "description": _("Link bank transactions with payments.") - }, - { - "type": "doctype", - "label": _("Bank Account"), - "name": "Bank Account", - }, - { - "type": "doctype", - "label": _("Invoice Discounting"), - "name": "Invoice Discounting", - }, - { - "type": "doctype", - "label": _("Bank Statement Transaction Entry List"), - "name": "Bank Statement Transaction Entry", - "route": "#List/Bank Statement Transaction Entry", - }, - { - "type": "doctype", - "label": _("Bank Statement Transaction Entry Report"), - "name": "Bank Statement Transaction Entry", - "route": "#Report/Bank Statement Transaction Entry", - }, - { - "type": "doctype", - "label": _("Bank Statement Settings"), - "name": "Bank Statement Settings", - }, - { "type": "doctype", "label": _("Update Bank Transaction Dates"), @@ -288,9 +189,8 @@ def get_data(): }, { "type": "doctype", - "label": _("Match Payments with Invoices"), - "name": "Payment Reconciliation", - "description": _("Match non-linked Invoices and Payments.") + "label": _("Invoice Discounting"), + "name": "Invoice Discounting", }, { "type": "report", @@ -306,8 +206,75 @@ def get_data(): }, { "type": "doctype", - "name": "Bank Guarantee", - "doctype": "Bank Guarantee" + "name": "Bank Guarantee" + }, + { + "type": "doctype", + "name": "Cheque Print Template", + "description": _("Setup cheque dimensions for printing") + }, + ] + }, + { + "label": _("General Ledger"), + "items": [ + { + "type": "doctype", + "name": "Journal Entry", + "description": _("Accounting journal entries.") + }, + { + "type": "report", + "name": "General Ledger", + "doctype": "GL Entry", + "is_query_report": True, + }, + { + "type": "report", + "name": "Customer Ledger Summary", + "doctype": "Sales Invoice", + "is_query_report": True, + }, + { + "type": "report", + "name": "Supplier Ledger Summary", + "doctype": "Sales Invoice", + "is_query_report": True, + } + ] + }, + { + "label": _("Taxes"), + "items": [ + { + "type": "doctype", + "name": "Sales Taxes and Charges Template", + "description": _("Tax template for selling transactions.") + }, + { + "type": "doctype", + "name": "Purchase Taxes and Charges Template", + "description": _("Tax template for buying transactions.") + }, + { + "type": "doctype", + "name": "Item Tax Template", + "description": _("Tax template for item tax rates.") + }, + { + "type": "doctype", + "name": "Tax Category", + "description": _("Tax Category for overriding tax rates.") + }, + { + "type": "doctype", + "name": "Tax Rule", + "description": _("Tax Rule for transactions.") + }, + { + "type": "doctype", + "name": "Tax Withholding Category", + "description": _("Tax Withholding rates to be applied on transactions.") }, ] }, @@ -327,6 +294,10 @@ def get_data(): "name": "Budget", "description": _("Define budget for a financial year.") }, + { + "type": "doctype", + "name": "Accounting Dimension", + }, { "type": "report", "name": "Budget Variance Report", @@ -338,51 +309,106 @@ def get_data(): "name": "Monthly Distribution", "description": _("Seasonality for setting budgets, targets etc.") }, + ] + }, + { + "label": _("Financial Statements"), + "items": [ + { + "type": "report", + "name": "Trial Balance", + "doctype": "GL Entry", + "is_query_report": True, + }, + { + "type": "report", + "name": "Profit and Loss Statement", + "doctype": "GL Entry", + "is_query_report": True + }, + { + "type": "report", + "name": "Balance Sheet", + "doctype": "GL Entry", + "is_query_report": True + }, + { + "type": "report", + "name": "Cash Flow", + "doctype": "GL Entry", + "is_query_report": True + }, + { + "type": "report", + "name": "Consolidated Financial Statement", + "doctype": "GL Entry", + "is_query_report": True + }, + ] + }, + { + "label": _("Opening and Closing"), + "items": [ + { + "type": "doctype", + "name": "Opening Invoice Creation Tool", + }, + { + "type": "doctype", + "name": "Chart of Accounts Importer", + }, { "type": "doctype", "name": "Period Closing Voucher", "description": _("Close Balance Sheet and book Profit or Loss.") }, ] + }, { - "label": _("Taxes"), + "label": _("Multi Currency"), "items": [ { "type": "doctype", - "name": "Tax Category", - "description": _("Tax Category for overriding tax rates.") + "name": "Currency", + "description": _("Enable / disable currencies.") }, { "type": "doctype", - "name": "Sales Taxes and Charges Template", - "description": _("Tax template for selling transactions.") + "name": "Currency Exchange", + "description": _("Currency exchange rate master.") }, { "type": "doctype", - "name": "Purchase Taxes and Charges Template", - "description": _("Tax template for buying transactions.") + "name": "Exchange Rate Revaluation", + "description": _("Exchange Rate Revaluation master.") + }, + ] + }, + { + "label": _("Settings"), + "icon": "fa fa-cog", + "items": [ + { + "type": "doctype", + "name": "Payment Gateway Account", + "description": _("Setup Gateway accounts.") }, { "type": "doctype", - "name": "Item Tax Template", - "description": _("Tax template for item tax rates.") + "name": "Terms and Conditions", + "label": _("Terms and Conditions Template"), + "description": _("Template of terms or contract.") }, { "type": "doctype", - "name": "Tax Rule", - "description": _("Tax Rule for transactions.") - }, - { - "type": "doctype", - "name": "Tax Withholding Category", - "description": _("Tax Withholding rates to be applied on transactions.") + "name": "Mode of Payment", + "description": _("e.g. Bank, Cash, Credit Card") }, ] }, { "label": _("Subscription Management"), - "icon": "fa fa-microchip ", "items": [ { "type": "doctype", @@ -403,7 +429,31 @@ def get_data(): ] }, { - "label": _("Key Reports"), + "label": _("Bank Statement"), + "items": [ + { + "type": "doctype", + "label": _("Bank"), + "name": "Bank", + }, + { + "type": "doctype", + "label": _("Bank Account"), + "name": "Bank Account", + }, + { + "type": "doctype", + "name": "Bank Statement Transaction Entry", + }, + { + "type": "doctype", + "label": _("Bank Statement Settings"), + "name": "Bank Statement Settings", + }, + ] + }, + { + "label": _("Profitability"), "items": [ { "type": "report", @@ -413,21 +463,9 @@ def get_data(): }, { "type": "report", - "name": "Sales Register", - "doctype": "Sales Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Purchase Register", - "doctype": "Purchase Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Purchase Invoice Trends", + "name": "Profitability Analysis", + "doctype": "GL Entry", "is_query_report": True, - "doctype": "Purchase Invoice" }, { "type": "report", @@ -437,38 +475,14 @@ def get_data(): }, { "type": "report", - "name": "Item-wise Sales Register", - "is_query_report": True, - "doctype": "Sales Invoice" - }, - { - "type": "report", - "name": "Item-wise Purchase Register", + "name": "Purchase Invoice Trends", "is_query_report": True, "doctype": "Purchase Invoice" }, - { - "type": "report", - "name": "Profitability Analysis", - "doctype": "GL Entry", - "is_query_report": True, - }, - { - "type": "report", - "name": "Customer Ledger Summary", - "doctype": "Sales Invoice", - "is_query_report": True, - }, - { - "type": "report", - "name": "Supplier Ledger Summary", - "doctype": "Sales Invoice", - "is_query_report": True, - } ] }, { - "label": _("Other Reports"), + "label": _("Reports"), "icon": "fa fa-table", "items": [ { @@ -489,18 +503,6 @@ def get_data(): "is_query_report": True, "doctype": "Sales Invoice" }, - { - "type": "report", - "name": "Accounts Receivable Summary", - "doctype": "Sales Invoice", - "is_query_report": True - }, - { - "type": "report", - "name": "Accounts Payable Summary", - "doctype": "Purchase Invoice", - "is_query_report": True - }, { "type": "report", "is_query_report": True, @@ -549,27 +551,7 @@ def get_data(): } ] }, - { - "label": _("Help"), - "icon": "fa fa-facetime-video", - "items": [ - { - "type": "help", - "label": _("Chart of Accounts"), - "youtube_id": "DyR-DST-PyA" - }, - { - "type": "help", - "label": _("Opening Accounting Balance"), - "youtube_id": "kdgM20Q-q68" - }, - { - "type": "help", - "label": _("Setting up Taxes"), - "youtube_id": "nQ1zZdPgdaQ" - } - ] - } + ] gst = { @@ -617,6 +599,12 @@ def get_data(): "name": "GST Itemised Purchase Register", "is_query_report": True }, + { + "type": "doctype", + "name": "C-Form", + "description": _("C-Form records"), + "country": "India" + }, ] } @@ -624,6 +612,6 @@ def get_data(): countries = frappe.get_all("Company", fields="country") countries = [country["country"] for country in countries] if "India" in countries: - config.insert(7, gst) + config.insert(9, gst) domains = frappe.get_active_domains() return config diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index ba3492330e..e0f4be9233 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -228,29 +228,5 @@ def get_data(): } ] }, - { - "label": _("Help"), - "items": [ - { - "type": "help", - "label": _("Customer and Supplier"), - "youtube_id": "anoGi_RpQ20" - }, - { - "type": "help", - "label": _("Material Request to Purchase Order"), - "youtube_id": "4TN9kPyfIqM" - }, - { - "type": "help", - "label": _("Purchase Order to Payment"), - "youtube_id": "EK65tLdVUDk" - }, - { - "type": "help", - "label": _("Managing Subcontracting"), - "youtube_id": "ThiMCC2DtKo" - }, - ] - }, + ] diff --git a/erpnext/config/healthcare.py b/erpnext/config/healthcare.py index 1311111618..756d22e416 100644 --- a/erpnext/config/healthcare.py +++ b/erpnext/config/healthcare.py @@ -26,8 +26,8 @@ def get_data(): }, { "type": "page", - "name": "medical_record", - "label": _("Patient Medical Record"), + "name": "patient_history", + "label": _("Patient History"), }, { "type": "page", diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py index 21e6a505a8..261edaf942 100644 --- a/erpnext/config/hr.py +++ b/erpnext/config/hr.py @@ -4,127 +4,13 @@ from frappe import _ def get_data(): return [ { - "label": _("Employee and Attendance"), + "label": _("Employee"), "items": [ { "type": "doctype", "name": "Employee", "onboard": 1, }, - { - "type": "doctype", - "name": "Employee Attendance Tool", - "hide_count": True, - "onboard": 1, - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Group", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Attendance", - "onboard": 1, - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Attendance Request", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Upload Attendance", - "hide_count": True, - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Checkin", - "hide_count": True, - "onboard": 1, - "dependencies": ["Employee"] - }, - ] - }, - { - "label": _("Payroll"), - "items": [ - { - "type": "doctype", - "name": "Salary Structure", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Salary Structure Assignment", - "onboard": 1, - "dependencies": ["Salary Structure", "Employee"], - }, - { - "type": "doctype", - "name": "Salary Slip", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Payroll Entry", - "onboard": 1, - }, - { - "type": "doctype", - "name": "Employee Benefit Application", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Benefit Claim", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Additional Salary", - }, - { - "type": "doctype", - "name": "Employee Tax Exemption Declaration", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Tax Exemption Proof Submission", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Incentive", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Retention Bonus", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Payroll Period", - }, - { - "type": "doctype", - "name": "Salary Component", - }, - ] - }, - { - "label": _("Settings"), - "icon": "fa fa-cog", - "items": [ - { - "type": "doctype", - "name": "HR Settings", - }, { "type": "doctype", "name": "Employment Type", @@ -147,19 +33,56 @@ def get_data(): }, { "type": "doctype", - "name": "Daily Work Summary Group" + "name": "Employee Group", + "dependencies": ["Employee"] }, { "type": "doctype", "name": "Employee Health Insurance" }, - { - "type": "doctype", - "name": "Staffing Plan", - } ] }, - + { + "label": _("Attendance"), + "items": [ + { + "type": "doctype", + "name": "Employee Attendance Tool", + "hide_count": True, + "onboard": 1, + "dependencies": ["Employee"] + }, + { + "type": "doctype", + "name": "Attendance", + "onboard": 1, + "dependencies": ["Employee"] + }, + { + "type": "doctype", + "name": "Attendance Request", + "dependencies": ["Employee"] + }, + { + "type": "doctype", + "name": "Upload Attendance", + "hide_count": True, + "dependencies": ["Employee"] + }, + { + "type": "doctype", + "name": "Employee Checkin", + "hide_count": True, + "dependencies": ["Employee"] + }, + { + "type": "report", + "is_query_report": True, + "name": "Monthly Attendance Sheet", + "doctype": "Attendance" + }, + ] + }, { "label": _("Leaves"), "items": [ @@ -175,13 +98,8 @@ def get_data(): }, { "type": "doctype", - "name": "Compensatory Leave Request", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Leave Encashment", - "dependencies": ["Employee"] + "name": "Leave Policy", + "dependencies": ["Leave Type"] }, { "type": "doctype", @@ -194,37 +112,167 @@ def get_data(): }, { "type": "doctype", - "name": "Leave Policy", - "dependencies": ["Leave Type"] + "name": "Holiday List", }, { "type": "doctype", - "name": "Holiday List", + "name": "Compensatory Leave Request", + "dependencies": ["Employee"] + }, + { + "type": "doctype", + "name": "Leave Encashment", + "dependencies": ["Employee"] }, { "type": "doctype", "name": "Leave Block List", }, + { + "type": "report", + "is_query_report": True, + "name": "Employee Leave Balance", + "doctype": "Leave Application" + }, ] }, { - "label": _("Recruitment and Training"), + "label": _("Payroll"), "items": [ { "type": "doctype", - "name": "Job Applicant", + "name": "Salary Structure", "onboard": 1, }, + { + "type": "doctype", + "name": "Salary Structure Assignment", + "onboard": 1, + "dependencies": ["Salary Structure", "Employee"], + }, + { + "type": "doctype", + "name": "Payroll Entry", + "onboard": 1, + }, + { + "type": "doctype", + "name": "Salary Slip", + "onboard": 1, + }, + { + "type": "doctype", + "name": "Salary Component", + }, + { + "type": "doctype", + "name": "Additional Salary", + }, + { + "type": "doctype", + "name": "Retention Bonus", + "dependencies": ["Employee"] + }, + { + "type": "doctype", + "name": "Employee Incentive", + "dependencies": ["Employee"] + }, + { + "type": "report", + "is_query_report": True, + "name": "Salary Register", + "doctype": "Salary Slip" + }, + ] + }, + { + "label": _("Employee Tax and Benefits"), + "items": [ + { + "type": "doctype", + "name": "Employee Tax Exemption Declaration", + "dependencies": ["Employee"] + }, + { + "type": "doctype", + "name": "Employee Tax Exemption Proof Submission", + "dependencies": ["Employee"] + }, + { + "type": "doctype", + "name": "Employee Benefit Application", + "dependencies": ["Employee"] + }, + { + "type": "doctype", + "name": "Employee Benefit Claim", + "dependencies": ["Employee"] + }, + ] + }, + { + "label": _("Employee Lifecycle"), + "items": [ + { + "type": "doctype", + "name": "Employee Onboarding", + "dependencies": ["Job Applicant"], + }, + { + "type": "doctype", + "name": "Employee Promotion", + "dependencies": ["Employee"], + }, + { + "type": "doctype", + "name": "Employee Transfer", + "dependencies": ["Employee"], + }, + { + "type": "doctype", + "name": "Employee Separation", + "dependencies": ["Employee"], + }, + { + "type": "doctype", + "name": "Employee Onboarding Template", + "dependencies": ["Employee"] + }, + { + "type": "doctype", + "name": "Employee Separation Template", + "dependencies": ["Employee"] + }, + ] + }, + { + "label": _("Recruitment"), + "items": [ { "type": "doctype", "name": "Job Opening", "onboard": 1, }, + { + "type": "doctype", + "name": "Job Applicant", + "onboard": 1, + }, { "type": "doctype", "name": "Job Offer", "onboard": 1, }, + { + "type": "doctype", + "name": "Staffing Plan", + }, + ] + }, + { + "label": _("Training"), + "items": [ { "type": "doctype", "name": "Training Program" @@ -244,42 +292,7 @@ def get_data(): ] }, { - "label": _("Employee Lifecycle"), - "items": [ - { - "type": "doctype", - "name": "Employee Transfer", - "dependencies": ["Employee"], - }, - { - "type": "doctype", - "name": "Employee Promotion", - "dependencies": ["Employee"], - }, - { - "type": "doctype", - "name": "Employee Separation", - "dependencies": ["Employee"], - }, - { - "type": "doctype", - "name": "Employee Onboarding", - "dependencies": ["Job Applicant"], - }, - { - "type": "doctype", - "name": "Employee Separation Template", - "dependencies": ["Employee"] - }, - { - "type": "doctype", - "name": "Employee Onboarding Template", - "dependencies": ["Employee"] - } - ] - }, - { - "label": _("Appraisals, Expense Claims and Loans"), + "label": _("Performance"), "items": [ { "type": "doctype", @@ -290,15 +303,24 @@ def get_data(): "name": "Appraisal Template", }, { - "type": "page", - "name": "team-updates", - "label": _("Team Updates") + "type": "doctype", + "name": "Energy Point Rule", }, { "type": "doctype", - "name": "Employee Advance", - "dependencies": ["Employee"] + "name": "Energy Point Log", }, + { + "type": "link", + "doctype": "Energy Point Log", + "label": _("Energy Point Leaderboard"), + "route": "#social/users" + }, + ] + }, + { + "label": _("Expense Claims"), + "items": [ { "type": "doctype", "name": "Expense Claim", @@ -306,8 +328,14 @@ def get_data(): }, { "type": "doctype", - "name": "Loan Type", + "name": "Employee Advance", + "dependencies": ["Employee"] }, + ] + }, + { + "label": _("Loans"), + "items": [ { "type": "doctype", "name": "Loan Application", @@ -316,19 +344,72 @@ def get_data(): { "type": "doctype", "name": "Loan" - } + }, + { + "type": "doctype", + "name": "Loan Type", + }, + ] + }, + { + "label": _("Shift Management"), + "items": [ + { + "type": "doctype", + "name": "Shift Type", + }, + { + "type": "doctype", + "name": "Shift Request", + }, + { + "type": "doctype", + "name": "Shift Assignment", + }, + ] + }, + { + "label": _("Fleet Management"), + "items": [ + { + "type": "doctype", + "name": "Vehicle" + }, + { + "type": "doctype", + "name": "Vehicle Log" + }, + { + "type": "report", + "is_query_report": True, + "name": "Vehicle Expenses", + "doctype": "Vehicle" + }, + ] + }, + { + "label": _("Settings"), + "icon": "fa fa-cog", + "items": [ + { + "type": "doctype", + "name": "HR Settings", + }, + { + "type": "doctype", + "name": "Daily Work Summary Group" + }, + { + "type": "page", + "name": "team-updates", + "label": _("Team Updates") + }, ] }, { "label": _("Reports"), "icon": "fa fa-list", "items": [ - { - "type": "report", - "is_query_report": True, - "name": "Employee Leave Balance", - "doctype": "Leave Application" - }, { "type": "report", "is_query_report": True, @@ -341,29 +422,6 @@ def get_data(): "name": "Employees working on a holiday", "doctype": "Employee" }, - { - "type": "report", - "name": "Employee Information", - "doctype": "Employee" - }, - { - "type": "report", - "is_query_report": True, - "name": "Salary Register", - "doctype": "Salary Slip" - }, - { - "type": "report", - "is_query_report": True, - "name": "Monthly Attendance Sheet", - "doctype": "Attendance" - }, - { - "type": "report", - "is_query_report": True, - "name": "Vehicle Expenses", - "doctype": "Vehicle" - }, { "type": "report", "is_query_report": True, @@ -372,50 +430,4 @@ def get_data(): }, ] }, - { - "label": _("Shifts and Fleet Management"), - "items": [ - { - "type": "doctype", - "name": "Shift Type", - }, - { - "type": "doctype", - "name": "Shift Request", - }, - { - "type": "doctype", - "name": "Shift Assignment", - }, - { - "type": "doctype", - "name": "Vehicle" - }, - { - "type": "doctype", - "name": "Vehicle Log" - }, - ] - }, - # { - # "label": _("Help"), - # "icon": "fa fa-facetime-video", - # "items": [ - # { - # "type": "help", - # "label": _("Setting up Employees"), - # "youtube_id": "USfIUdZlUhw" - # }, - # { - # "type": "help", - # "label": _("Leave Management"), - # "youtube_id": "fc0p_AXebc8" - # }, - # { - # "type": "help", - # "label": _("Expense Claims"), - # "youtube_id": "5SZHJF--ZFY" - # } - # ] - # }, ] diff --git a/erpnext/config/projects.py b/erpnext/config/projects.py index 5a907fff48..47700d10b2 100644 --- a/erpnext/config/projects.py +++ b/erpnext/config/projects.py @@ -88,17 +88,14 @@ def get_data(): "doctype": "Project", "dependencies": ["Project"], }, - ] - }, - { - "label": _("Help"), - "icon": "fa fa-facetime-video", - "items": [ { - "type": "help", - "label": _("Managing Projects"), - "youtube_id": "egxIGwtoKI4" + "type": "report", + "is_query_report": True, + "name": "Project Billing Summary", + "doctype": "Project", + "dependencies": ["Project"], }, ] }, + ] diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py index f18aadb9ef..844710d47c 100644 --- a/erpnext/config/selling.py +++ b/erpnext/config/selling.py @@ -318,41 +318,5 @@ def get_data(): } ] }, - { - "label": _("SMS"), - "icon": "fa fa-wrench", - "items": [ - { - "type": "doctype", - "name": "SMS Center", - "description":_("Send mass SMS to your contacts"), - }, - { - "type": "doctype", - "name": "SMS Log", - "description":_("Logs for maintaining sms delivery status"), - }, - - ] - }, - { - "label": _("Help"), - "items": [ - { - "type": "help", - "label": _("Customer and Supplier"), - "youtube_id": "anoGi_RpQ20" - }, - { - "type": "help", - "label": _("Sales Order to Payment"), - "youtube_id": "1eP90MWoDQM" - }, - { - "type": "help", - "label": _("Point-of-Sale"), - "youtube_id": "4WkelWkbP_c" - }, - ] - }, + ] diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py index 84aa8474d3..7d66df2360 100644 --- a/erpnext/config/stock.py +++ b/erpnext/config/stock.py @@ -281,9 +281,9 @@ def get_data(): }, { "type": "report", + "is_query_report": True, "name": "Item Shortage Report", - "route": "#Report/Bin/Item Shortage Report", - "doctype": "Purchase Receipt" + "doctype": "Bin" }, { "type": "report", @@ -329,45 +329,5 @@ def get_data(): } ] }, - { - "label": _("Help"), - "icon": "fa fa-facetime-video", - "items": [ - { - "type": "help", - "label": _("Items and Pricing"), - "youtube_id": "qXaEwld4_Ps" - }, - { - "type": "help", - "label": _("Item Variants"), - "youtube_id": "OGBETlCzU5o" - }, - { - "type": "help", - "label": _("Opening Stock Balance"), - "youtube_id": "0yPgrtfeCTs" - }, - { - "type": "help", - "label": _("Making Stock Entries"), - "youtube_id": "Njt107hlY3I" - }, - { - "type": "help", - "label": _("Serialized Inventory"), - "youtube_id": "gvOVlEwFDAk" - }, - { - "type": "help", - "label": _("Batch Inventory"), - "youtube_id": "J0QKl7ABPKM" - }, - { - "type": "help", - "label": _("Managing Subcontracting"), - "youtube_id": "ThiMCC2DtKo" - }, - ] - } + ] diff --git a/erpnext/config/support.py b/erpnext/config/support.py index 0301bb3e19..151c4f743e 100644 --- a/erpnext/config/support.py +++ b/erpnext/config/support.py @@ -21,13 +21,7 @@ def get_data(): "type": "doctype", "name": "Issue Priority", "description": _("Issue Priority."), - }, - { - "type": "doctype", - "name": "Communication", - "description": _("Communication log."), - "onboard": 1, - }, + } ] }, { @@ -97,4 +91,15 @@ def get_data(): }, ] }, + { + "label": _("Settings"), + "icon": "fa fa-list", + "items": [ + { + "type": "doctype", + "name": "Support Settings", + "label": _("Support Settings"), + }, + ] + }, ] \ No newline at end of file diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index f5ecaeb286..47f56cbfb1 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -475,21 +475,20 @@ class AccountsController(TransactionBase): order_doctype = "Purchase Order" order_list = list(set([d.get(order_field) - for d in self.get("items") if d.get(order_field)])) + for d in self.get("items") if d.get(order_field)])) journal_entries = get_advance_journal_entries(party_type, party, party_account, - amount_field, order_doctype, order_list, include_unallocated) + amount_field, order_doctype, order_list, include_unallocated) payment_entries = get_advance_payment_entries(party_type, party, party_account, - order_doctype, order_list, include_unallocated) + order_doctype, order_list, include_unallocated) res = journal_entries + payment_entries return res def is_inclusive_tax(self): - is_inclusive = cint(frappe.db.get_single_value("Accounts Settings", - "show_inclusive_tax_in_print")) + is_inclusive = cint(frappe.db.get_single_value("Accounts Settings", "show_inclusive_tax_in_print")) if is_inclusive: is_inclusive = 0 @@ -501,7 +500,7 @@ class AccountsController(TransactionBase): def validate_advance_entries(self): order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order" order_list = list(set([d.get(order_field) - for d in self.get("items") if d.get(order_field)])) + for d in self.get("items") if d.get(order_field)])) if not order_list: return @@ -513,7 +512,7 @@ class AccountsController(TransactionBase): if not advance_entries_against_si or d.reference_name not in advance_entries_against_si: frappe.msgprint(_( "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)) + .format(d.reference_name, d.against_order)) def update_against_document_in_jv(self): """ @@ -551,9 +550,9 @@ class AccountsController(TransactionBase): 'unadjusted_amount': flt(d.advance_amount), 'allocated_amount': flt(d.allocated_amount), 'exchange_rate': (self.conversion_rate - if self.party_account_currency != self.company_currency else 1), + 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), + if self.party_account_currency == self.company_currency else self.grand_total), 'outstanding_amount': self.outstanding_amount }) lst.append(args) @@ -576,36 +575,37 @@ class AccountsController(TransactionBase): unlink_ref_doc_from_payment_entries(self) def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield): - from erpnext.controllers.status_updater import get_tolerance_for - item_tolerance = {} - global_tolerance = None + from erpnext.controllers.status_updater import get_allowance_for + item_allowance = {} + global_qty_allowance, global_amount_allowance = None, None for item in self.get("items"): if item.get(item_ref_dn): ref_amt = flt(frappe.db.get_value(ref_dt + " Item", - item.get(item_ref_dn), based_on), self.precision(based_on, item)) + item.get(item_ref_dn), based_on), self.precision(based_on, item)) if not ref_amt: frappe.msgprint( - _("Warning: System will not check overbilling since amount for Item {0} in {1} is zero").format( - item.item_code, ref_dt)) + _("Warning: System will not check overbilling since amount for Item {0} in {1} is zero") + .format(item.item_code, ref_dt)) else: - already_billed = frappe.db.sql("""select sum(%s) from `tab%s` - where %s=%s and docstatus=1 and parent != %s""" % - (based_on, self.doctype + " Item", item_ref_dn, '%s', '%s'), - (item.get(item_ref_dn), self.name))[0][0] + already_billed = frappe.db.sql(""" + select sum(%s) + from `tab%s` + where %s=%s and docstatus=1 and parent != %s + """ % (based_on, self.doctype + " Item", item_ref_dn, '%s', '%s'), + (item.get(item_ref_dn), self.name))[0][0] total_billed_amt = flt(flt(already_billed) + flt(item.get(based_on)), - self.precision(based_on, item)) + self.precision(based_on, item)) - tolerance, item_tolerance, global_tolerance = get_tolerance_for(item.item_code, - item_tolerance, global_tolerance) + allowance, item_allowance, global_qty_allowance, global_amount_allowance = \ + get_allowance_for(item.item_code, item_allowance, global_qty_allowance, global_amount_allowance, "amount") - max_allowed_amt = flt(ref_amt * (100 + tolerance) / 100) + max_allowed_amt = flt(ref_amt * (100 + allowance) / 100) if total_billed_amt - max_allowed_amt > 0.01: - frappe.throw(_( - "Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set in Stock Settings").format( - item.item_code, item.idx, max_allowed_amt)) + frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set in Stock Settings") + .format(item.item_code, item.idx, max_allowed_amt)) def get_company_default(self, fieldname): from erpnext.accounts.utils import get_company_default @@ -615,9 +615,10 @@ class AccountsController(TransactionBase): stock_items = [] item_codes = list(set(item.item_code for item in self.get("items"))) if item_codes: - stock_items = [r[0] for r in frappe.db.sql("""select name - from `tabItem` where name in (%s) and is_stock_item=1""" % \ - (", ".join((["%s"] * len(item_codes))),), item_codes)] + stock_items = [r[0] for r in frappe.db.sql(""" + select name from `tabItem` + where name in (%s) and is_stock_item=1 + """ % (", ".join((["%s"] * len(item_codes))),), item_codes)] return stock_items diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index d74bc0ea18..47c9f0a4ce 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -206,10 +206,11 @@ def bom(doctype, txt, searchfield, start, page_len, filters): if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), idx desc, name limit %(start)s, %(page_len)s """.format( - fcond=get_filters_cond(doctype, filters, conditions), + fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'), mcond=get_match_cond(doctype), - key=searchfield), { - 'txt': '%' + txt + '%', + key=frappe.db.escape(searchfield)), + { + 'txt': "%"+frappe.db.escape(txt)+"%", '_txt': txt.replace("%", ""), 'start': start or 0, 'page_len': page_len or 20 diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 8cf11f785b..2fddcdf24c 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -75,7 +75,7 @@ def validate_returned_items(doc): items_returned = False for d in doc.get("items"): - if flt(d.qty) < 0 or d.get('received_qty') < 0: + if d.item_code and (flt(d.qty) < 0 or d.get('received_qty') < 0): if d.item_code not in valid_items: frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}") .format(d.idx, d.item_code, doc.doctype, doc.return_against)) @@ -107,6 +107,9 @@ def validate_returned_items(doc): items_returned = True + elif d.item_name: + items_returned = True + if not items_returned: frappe.throw(_("Atleast one item should be entered with negative quantity in return document")) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index f70870b3ad..2da0f0381b 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -7,6 +7,8 @@ from frappe.utils import flt, comma_or, nowdate, getdate from frappe import _ from frappe.model.document import Document +class OverAllowanceError(frappe.ValidationError): pass + def validate_status(status, options): if status not in options: frappe.throw(_("Status must be one of {0}").format(comma_or(options))) @@ -43,16 +45,6 @@ status_map = { ["Closed", "eval:self.status=='Closed'"], ["On Hold", "eval:self.status=='On Hold'"], ], - "Sales Invoice": [ - ["Draft", None], - ["Submitted", "eval:self.docstatus==1"], - ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], - ["Return", "eval:self.is_return==1 and self.docstatus==1"], - ["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"], - ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], - ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], - ["Cancelled", "eval:self.docstatus==2"], - ], "Purchase Invoice": [ ["Draft", None], ["Submitted", "eval:self.docstatus==1"], @@ -154,8 +146,9 @@ class StatusUpdater(Document): def validate_qty(self): """Validates qty at row level""" - self.tolerance = {} - self.global_tolerance = None + self.item_allowance = {} + self.global_qty_allowance = None + self.global_amount_allowance = None for args in self.status_updater: if "target_ref_field" not in args: @@ -186,32 +179,41 @@ class StatusUpdater(Document): # if not item[args['target_ref_field']]: # msgprint(_("Note: System will not check over-delivery and over-booking for Item {0} as quantity or amount is 0").format(item.item_code)) - if args.get('no_tolerance'): + if args.get('no_allowance'): item['reduce_by'] = item[args['target_field']] - item[args['target_ref_field']] if item['reduce_by'] > .01: self.limits_crossed_error(args, item) elif item[args['target_ref_field']]: - self.check_overflow_with_tolerance(item, args) + self.check_overflow_with_allowance(item, args) - def check_overflow_with_tolerance(self, item, args): + def check_overflow_with_allowance(self, item, args): """ - Checks if there is overflow condering a relaxation tolerance + Checks if there is overflow condering a relaxation allowance """ - # check if overflow is within tolerance - tolerance, self.tolerance, self.global_tolerance = get_tolerance_for(item['item_code'], - self.tolerance, self.global_tolerance) + qty_or_amount = "qty" if "qty" in args['target_ref_field'] else "amount" + + # check if overflow is within allowance + allowance, self.item_allowance, self.global_qty_allowance, self.global_amount_allowance = \ + get_allowance_for(item['item_code'], self.item_allowance, + self.global_qty_allowance, self.global_amount_allowance, qty_or_amount) + overflow_percent = ((item[args['target_field']] - item[args['target_ref_field']]) / item[args['target_ref_field']]) * 100 - if overflow_percent - tolerance > 0.01: - item['max_allowed'] = flt(item[args['target_ref_field']] * (100+tolerance)/100) + if overflow_percent - allowance > 0.01: + item['max_allowed'] = flt(item[args['target_ref_field']] * (100+allowance)/100) item['reduce_by'] = item[args['target_field']] - item['max_allowed'] - self.limits_crossed_error(args, item) + self.limits_crossed_error(args, item, qty_or_amount) - def limits_crossed_error(self, args, item): + def limits_crossed_error(self, args, item, qty_or_amount): '''Raise exception for limits crossed''' + if qty_or_amount == "qty": + action_msg = _('To allow over receipt / delivery, update "Over Receipt/Delivery Allowance" in Stock Settings or the Item.') + else: + action_msg = _('To allow over billing, update "Over Billing Allowance" in Accounts Settings or the Item.') + frappe.throw(_('This document is over limit by {0} {1} for item {4}. Are you making another {3} against the same {2}?') .format( frappe.bold(_(item["target_ref_field"].title())), @@ -219,9 +221,7 @@ class StatusUpdater(Document): frappe.bold(_(args.get('target_dt'))), frappe.bold(_(self.doctype)), frappe.bold(item.get('item_code')) - ) + '

' + - _('To allow over-billing or over-ordering, update "Allowance" in Stock Settings or the Item.'), - title = _('Limit Crossed')) + ) + '

' + action_msg, OverAllowanceError, title = _('Limit Crossed')) def update_qty(self, update_modified=True): """Updates qty or amount at row level @@ -294,7 +294,7 @@ class StatusUpdater(Document): frappe.db.sql("""update `tab%(target_parent_dt)s` set %(target_parent_field)s = round( ifnull((select - ifnull(sum(if(%(target_ref_field)s > %(target_field)s, abs(%(target_field)s), abs(%(target_ref_field)s))), 0) + ifnull(sum(if(abs(%(target_ref_field)s) > abs(%(target_field)s), abs(%(target_field)s), abs(%(target_ref_field)s))), 0) / sum(abs(%(target_ref_field)s)) * 100 from `tab%(target_dt)s` where parent="%(name)s" having sum(abs(%(target_ref_field)s)) > 0), 0), 6) %(update_modified)s @@ -358,19 +358,34 @@ class StatusUpdater(Document): ref_doc.db_set("per_billed", per_billed) ref_doc.set_status(update=True) -def get_tolerance_for(item_code, item_tolerance={}, global_tolerance=None): +def get_allowance_for(item_code, item_allowance={}, global_qty_allowance=None, global_amount_allowance=None, qty_or_amount="qty"): """ - Returns the tolerance for the item, if not set, returns global tolerance + Returns the allowance for the item, if not set, returns global allowance """ - if item_tolerance.get(item_code): - return item_tolerance[item_code], item_tolerance, global_tolerance + if qty_or_amount == "qty": + if item_allowance.get(item_code, frappe._dict()).get("qty"): + return item_allowance[item_code].qty, item_allowance, global_qty_allowance, global_amount_allowance + else: + if item_allowance.get(item_code, frappe._dict()).get("amount"): + return item_allowance[item_code].amount, item_allowance, global_qty_allowance, global_amount_allowance - tolerance = flt(frappe.db.get_value('Item',item_code,'tolerance') or 0) + qty_allowance, over_billing_allowance = \ + frappe.db.get_value('Item', item_code, ['over_delivery_receipt_allowance', 'over_billing_allowance']) - if not tolerance: - if global_tolerance == None: - global_tolerance = flt(frappe.db.get_value('Stock Settings', None, 'tolerance')) - tolerance = global_tolerance + if qty_or_amount == "qty" and not qty_allowance: + if global_qty_allowance == None: + global_qty_allowance = flt(frappe.db.get_single_value('Stock Settings', 'over_delivery_receipt_allowance')) + qty_allowance = global_qty_allowance + elif qty_or_amount == "amount" and not over_billing_allowance: + if global_amount_allowance == None: + global_amount_allowance = flt(frappe.db.get_single_value('Accounts Settings', 'over_billing_allowance')) + over_billing_allowance = global_amount_allowance - item_tolerance[item_code] = tolerance - return tolerance, item_tolerance, global_tolerance + if qty_or_amount == "qty": + allowance = qty_allowance + item_allowance.setdefault(item_code, frappe._dict()).setdefault("qty", qty_allowance) + else: + allowance = over_billing_allowance + item_allowance.setdefault(item_code, frappe._dict()).setdefault("amount", over_billing_allowance) + + return allowance, item_allowance, global_qty_allowance, global_amount_allowance diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index ebbe3d9d27..8e1510a836 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -15,6 +15,9 @@ class calculate_taxes_and_totals(object): self.calculate() def calculate(self): + if not len(self.doc.get("items")): + return + self.discount_amount_applied = False self._calculate() diff --git a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py index 29a1a2b0bb..96a533ee10 100644 --- a/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py +++ b/erpnext/erpnext_integrations/doctype/quickbooks_migrator/quickbooks_migrator.py @@ -7,7 +7,9 @@ import frappe from frappe import _ from frappe.model.document import Document from requests_oauthlib import OAuth2Session -import json, requests +import json +import requests +import traceback from erpnext import encode_company_abbr # QuickBooks requires a redirect URL, User will be redirect to this URL @@ -32,7 +34,6 @@ def callback(*args, **kwargs): class QuickBooksMigrator(Document): def __init__(self, *args, **kwargs): super(QuickBooksMigrator, self).__init__(*args, **kwargs) - from pprint import pprint self.oauth = OAuth2Session( client_id=self.client_id, redirect_uri=self.redirect_url, @@ -46,7 +47,9 @@ class QuickBooksMigrator(Document): if self.company: # We need a Cost Center corresponding to the selected erpnext Company self.default_cost_center = frappe.db.get_value('Company', self.company, 'cost_center') - self.default_warehouse = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0})[0]["name"] + company_warehouses = frappe.get_all('Warehouse', filters={"company": self.company, "is_group": 0}) + if company_warehouses: + self.default_warehouse = company_warehouses[0].name if self.authorization_endpoint: self.authorization_url = self.oauth.authorization_url(self.authorization_endpoint)[0] @@ -218,7 +221,7 @@ class QuickBooksMigrator(Document): def _fetch_general_ledger(self): try: - query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint ,self.quickbooks_company_id) + query_uri = "{}/company/{}/reports/GeneralLedger".format(self.api_endpoint, self.quickbooks_company_id) response = self._get(query_uri, params={ "columns": ",".join(["tx_date", "txn_type", "credit_amt", "debt_amt"]), @@ -493,17 +496,17 @@ class QuickBooksMigrator(Document): "account_currency": customer["CurrencyRef"]["value"], "company": self.company, })[0]["name"] - except Exception as e: + except Exception: receivable_account = None erpcustomer = frappe.get_doc({ "doctype": "Customer", "quickbooks_id": customer["Id"], - "customer_name" : encode_company_abbr(customer["DisplayName"], self.company), - "customer_type" : "Individual", - "customer_group" : "Commercial", + "customer_name": encode_company_abbr(customer["DisplayName"], self.company), + "customer_type": "Individual", + "customer_group": "Commercial", "default_currency": customer["CurrencyRef"]["value"], "accounts": [{"company": self.company, "account": receivable_account}], - "territory" : "All Territories", + "territory": "All Territories", "company": self.company, }).insert() if "BillAddr" in customer: @@ -521,7 +524,7 @@ class QuickBooksMigrator(Document): item_dict = { "doctype": "Item", "quickbooks_id": item["Id"], - "item_code" : encode_company_abbr(item["Name"], self.company), + "item_code": encode_company_abbr(item["Name"], self.company), "stock_uom": "Unit", "is_stock_item": 0, "item_group": "All Item Groups", @@ -549,14 +552,14 @@ class QuickBooksMigrator(Document): erpsupplier = frappe.get_doc({ "doctype": "Supplier", "quickbooks_id": vendor["Id"], - "supplier_name" : encode_company_abbr(vendor["DisplayName"], self.company), - "supplier_group" : "All Supplier Groups", + "supplier_name": encode_company_abbr(vendor["DisplayName"], self.company), + "supplier_group": "All Supplier Groups", "company": self.company, }).insert() if "BillAddr" in vendor: self._create_address(erpsupplier, "Supplier", vendor["BillAddr"], "Billing") if "ShipAddr" in vendor: - self._create_address(erpsupplier, "Supplier",vendor["ShipAddr"], "Shipping") + self._create_address(erpsupplier, "Supplier", vendor["ShipAddr"], "Shipping") except Exception as e: self._log_error(e) @@ -829,7 +832,7 @@ class QuickBooksMigrator(Document): "currency": invoice["CurrencyRef"]["value"], "conversion_rate": invoice.get("ExchangeRate", 1), "posting_date": invoice["TxnDate"], - "due_date": invoice.get("DueDate", invoice["TxnDate"]), + "due_date": invoice.get("DueDate", invoice["TxnDate"]), "credit_to": credit_to_account, "supplier": frappe.get_all("Supplier", filters={ @@ -1200,7 +1203,7 @@ class QuickBooksMigrator(Document): def _create_address(self, entity, doctype, address, address_type): - try : + try: if not frappe.db.exists({"doctype": "Address", "quickbooks_id": address["Id"]}): frappe.get_doc({ "doctype": "Address", @@ -1252,8 +1255,6 @@ class QuickBooksMigrator(Document): def _log_error(self, execption, data=""): - import json, traceback - traceback.print_exc() frappe.log_error(title="QuickBooks Migration Error", message="\n".join([ "Data", diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js index 169281430c..1a34fe8076 100644 --- a/erpnext/healthcare/doctype/patient/patient.js +++ b/erpnext/healthcare/doctype/patient/patient.js @@ -21,9 +21,9 @@ frappe.ui.form.on('Patient', { }); } if (frm.doc.patient_name && frappe.user.has_role("Physician")) { - frm.add_custom_button(__('Medical Record'), function () { + frm.add_custom_button(__('Patient History'), function () { frappe.route_options = { "patient": frm.doc.name }; - frappe.set_route("medical_record"); + frappe.set_route("patient_history"); },"View"); } if (!frm.doc.__islocal && (frappe.user.has_role("Nursing User") || frappe.user.has_role("Physician"))) { diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js index b3cbd1f753..858145eef3 100644 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js @@ -30,9 +30,9 @@ frappe.ui.form.on('Patient Appointment', { }; }); if(frm.doc.patient){ - frm.add_custom_button(__('Medical Record'), function() { + frm.add_custom_button(__('Patient History'), function() { frappe.route_options = {"patient": frm.doc.patient}; - frappe.set_route("medical_record"); + frappe.set_route("patient_history"); },__("View")); } if(frm.doc.status == "Open"){ diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js index 7ea45688fd..088bc8161b 100644 --- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js +++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js @@ -41,10 +41,10 @@ frappe.ui.form.on('Patient Encounter', { } }); } - frm.add_custom_button(__('Medical Record'), function() { + frm.add_custom_button(__('Patient History'), function() { if (frm.doc.patient) { frappe.route_options = {"patient": frm.doc.patient}; - frappe.set_route("medical_record"); + frappe.set_route("patient_history"); } else { frappe.msgprint(__("Please select Patient")); } diff --git a/erpnext/healthcare/page/medical_record/__init__.py b/erpnext/healthcare/page/medical_record/__init__.py deleted file mode 100644 index baffc48825..0000000000 --- a/erpnext/healthcare/page/medical_record/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from __future__ import unicode_literals diff --git a/erpnext/healthcare/page/medical_record/medical_record.js b/erpnext/healthcare/page/medical_record/medical_record.js deleted file mode 100644 index df19d8f4dc..0000000000 --- a/erpnext/healthcare/page/medical_record/medical_record.js +++ /dev/null @@ -1,182 +0,0 @@ -frappe.provide("frappe.medical_record"); -frappe.pages['medical_record'].on_page_load = function(wrapper) { - var me = this; - var page = frappe.ui.make_app_page({ - parent: wrapper, - title: 'Medical Record', - }); - - frappe.breadcrumbs.add("Medical"); - - page.main.html(frappe.render_template("patient_select", {})); - var patient = frappe.ui.form.make_control({ - parent: page.main.find(".patient"), - df: { - fieldtype: "Link", - options: "Patient", - fieldname: "patient", - change: function(){ - page.main.find(".frappe-list").html(""); - draw_page(patient.get_value(), me); - } - }, - only_input: true, - }); - patient.refresh(); - - - this.page.main.on("click", ".medical_record-message", function() { - var doctype = $(this).attr("data-doctype"), - docname = $(this).attr("data-docname"); - - if (doctype && docname) { - frappe.route_options = { - scroll_to: { "doctype": doctype, "name": docname } - }; - frappe.set_route(["Form", doctype, docname]); - } - }); - - this.page.sidebar.on("click", ".edit-details", function() { - patient = patient.get_value(); - if (patient) { - frappe.set_route(["Form", "Patient", patient]); - } - }); - -}; - -frappe.pages['medical_record'].refresh = function() { - var me = this; - - if(frappe.route_options) { - if(frappe.route_options.patient){ - me.page.main.find(".frappe-list").html(""); - var patient = frappe.route_options.patient; - draw_page(patient,me); - me.page.main.find("[data-fieldname='patient']").val(patient); - frappe.route_options = null; - } - } -}; -var show_patient_info = function(patient, me){ - frappe.call({ - "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", - args: { - patient: patient - }, - callback: function (r) { - var data = r.message; - var details = ""; - if(data.email) details += "
Email : " + data.email; - if(data.mobile) details += "
Mobile : " + data.mobile; - if(data.occupation) details += "
Occupation : " + data.occupation; - if(data.blood_group) details += "
Blood group : " + data.blood_group; - if(data.allergies) details += "

Allergies : "+ data.allergies; - if(data.medication) details += "
Medication : "+ data.medication; - if(data.alcohol_current_use) details += "

Alcohol use : "+ data.alcohol_current_use; - if(data.alcohol_past_use) details += "
Alcohol past use : "+ data.alcohol_past_use; - if(data.tobacco_current_use) details += "
Tobacco use : "+ data.tobacco_current_use; - if(data.tobacco_past_use) details += "
Tobacco past use : "+ data.tobacco_past_use; - if(data.medical_history) details += "

Medical history : "+ data.medical_history; - if(data.surgical_history) details += "
Surgical history : "+ data.surgical_history; - if(data.surrounding_factors) details += "

Occupational hazards : "+ data.surrounding_factors; - if(data.other_risk_factors) details += "
Other risk factors : " + data.other_risk_factors; - if(data.patient_details) details += "

More info : " + data.patient_details; - - if(details){ - details = "

Patient Details" + details + "
"; - } - - var vitals = ""; - if(data.temperature) vitals += "
Temperature : " + data.temperature; - if(data.pulse) vitals += "
Pulse : " + data.pulse; - if(data.respiratory_rate) vitals += "
Respiratory Rate : " + data.respiratory_rate; - if(data.bp) vitals += "
BP : " + data.bp; - if(data.bmi) vitals += "
BMI : " + data.bmi; - if(data.height) vitals += "
Height : " + data.height; - if(data.weight) vitals += "
Weight : " + data.weight; - if(data.signs_date) vitals += "
Date : " + data.signs_date; - - if(vitals){ - vitals = "

Vital Signs" + vitals + "
"; - details = vitals + details; - } - if(details) details += "

Edit Details
"; - - me.page.sidebar.addClass("col-sm-3"); - me.page.sidebar.html(details); - me.page.wrapper.find(".layout-main-section-wrapper").addClass("col-sm-9"); - } - }); -}; -var draw_page = function(patient, me){ - frappe.model.with_doctype("Patient Medical Record", function() { - me.page.list = new frappe.ui.BaseList({ - hide_refresh: true, - page: me.page, - method: 'erpnext.healthcare.page.medical_record.medical_record.get_feed', - args: {name: patient}, - parent: $("
").appendTo(me.page.main), - render_view: function(values) { - var me = this; - var wrapper = me.page.main.find(".result-list").get(0); - values.map(function (value) { - var row = $('
').data("data", value).appendTo($(wrapper)).get(0); - new frappe.medical_record.Feed(row, value); - }); - }, - show_filters: true, - doctype: "Patient Medical Record", - }); - show_patient_info(patient, me); - me.page.list.run(); - }); -}; - -frappe.medical_record.last_feed_date = false; -frappe.medical_record.Feed = Class.extend({ - init: function(row, data) { - this.scrub_data(data); - this.add_date_separator(row, data); - if(!data.add_class) - data.add_class = "label-default"; - - data.link = ""; - if (data.reference_doctype && data.reference_name) { - data.link = frappe.format(data.reference_name, {fieldtype: "Link", options: data.reference_doctype}, - {label: __(data.reference_doctype)}); - } - - $(row) - .append(frappe.render_template("medical_record_row", data)) - .find("a").addClass("grey"); - }, - scrub_data: function(data) { - data.by = frappe.user.full_name(data.owner); - data.imgsrc = frappe.utils.get_file_link(frappe.user_info(data.owner).image); - - data.icon = "icon-flag"; - }, - add_date_separator: function(row, data) { - var date = frappe.datetime.str_to_obj(data.creation); - var last = frappe.medical_record.last_feed_date; - - if((last && frappe.datetime.obj_to_str(last) != frappe.datetime.obj_to_str(date)) || (!last)) { - var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); - if(diff < 1) { - var pdate = 'Today'; - } else if(diff < 2) { - pdate = 'Yesterday'; - } else { - pdate = frappe.datetime.global_date_format(date); - } - data.date_sep = pdate; - data.date_class = pdate=='Today' ? "date-indicator blue" : "date-indicator"; - } else { - data.date_sep = null; - data.date_class = ""; - } - frappe.medical_record.last_feed_date = date; - } -}); diff --git a/erpnext/healthcare/page/medical_record/medical_record.py b/erpnext/healthcare/page/medical_record/medical_record.py deleted file mode 100644 index 22c5852b05..0000000000 --- a/erpnext/healthcare/page/medical_record/medical_record.py +++ /dev/null @@ -1,24 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, ESS LLP and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.utils import cint - -@frappe.whitelist() -def get_feed(start, page_length, name): - """get feed""" - result = frappe.db.sql("""select name, owner, modified, creation, - reference_doctype, reference_name, subject - from `tabPatient Medical Record` - where patient=%(patient)s - order by creation desc - limit %(start)s, %(page_length)s""", - { - "start": cint(start), - "page_length": cint(page_length), - "patient": name - }, as_dict=True) - - return result diff --git a/erpnext/healthcare/page/medical_record/medical_record_row.html b/erpnext/healthcare/page/medical_record/medical_record_row.html deleted file mode 100644 index 9a670c9aa7..0000000000 --- a/erpnext/healthcare/page/medical_record/medical_record_row.html +++ /dev/null @@ -1,21 +0,0 @@ -
-
- {%= date_sep || "" %} -
-
- -
- -
- - {% if (reference_doctype && reference_name) { %} - {%= __("{0}: {1}", [link, "" + subject + ""]) %} - {% } else { %} - {%= subject %} - {% } %} - -
-
diff --git a/erpnext/healthcare/page/medical_record/patient_select.html b/erpnext/healthcare/page/medical_record/patient_select.html deleted file mode 100644 index 321baf738d..0000000000 --- a/erpnext/healthcare/page/medical_record/patient_select.html +++ /dev/null @@ -1,5 +0,0 @@ -
- -

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

-

-
diff --git a/erpnext/projects/doctype/project_task/__init__.py b/erpnext/healthcare/page/patient_history/__init__.py similarity index 100% rename from erpnext/projects/doctype/project_task/__init__.py rename to erpnext/healthcare/page/patient_history/__init__.py diff --git a/erpnext/healthcare/page/medical_record/medical_record.css b/erpnext/healthcare/page/patient_history/patient_history.css similarity index 62% rename from erpnext/healthcare/page/medical_record/medical_record.css rename to erpnext/healthcare/page/patient_history/patient_history.css index 977625bbd1..865d6abee0 100644 --- a/erpnext/healthcare/page/medical_record/medical_record.css +++ b/erpnext/healthcare/page/patient_history/patient_history.css @@ -14,6 +14,10 @@ margin-bottom: -4px; } +.medical_record-row > * { + z-index: -999; +} + .date-indicator { background:none; font-size:12px; @@ -35,6 +39,61 @@ color: #5e64ff; } +.div-bg-color { + background: #fafbfc; +} + +.bg-color-white { + background: #FFFFFF; +} + +.d-flex { + display: flex; +} + +.width-full { + width: 100%; +} + +.p-3 { + padding: 16px; +} + +.mt-2 { + margin-top: 8px; +} + +.mr-3 { + margin-right: 16px; +} + +.Box { + background-color: #fff; + border: 1px solid #d1d5da; + border-radius: 3px; +} + +.flex-column { + flex-direction: column; +} + +.avatar { + display: inline-block; + overflow: hidden; + line-height: 1; + vertical-align: middle; + border-radius: 3px; +} + +.py-3 { + padding-top: 16px; + padding-bottom: 16px; +} + +.border-bottom { + border-bottom: 1px #e1e4e8 solid; +} + .date-indicator.blue::after { background: #5e64ff; } @@ -65,8 +124,3 @@ #page-medical_record .list-filters { display: none ; } - -#page-medical_record .octicon-heart { - color: #ff5858; - margin: 0px 5px; -} diff --git a/erpnext/healthcare/page/patient_history/patient_history.html b/erpnext/healthcare/page/patient_history/patient_history.html new file mode 100644 index 0000000000..7a9446dffd --- /dev/null +++ b/erpnext/healthcare/page/patient_history/patient_history.html @@ -0,0 +1,20 @@ +
+
+

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

+

+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
\ No newline at end of file diff --git a/erpnext/healthcare/page/patient_history/patient_history.js b/erpnext/healthcare/page/patient_history/patient_history.js new file mode 100644 index 0000000000..87fe7edd29 --- /dev/null +++ b/erpnext/healthcare/page/patient_history/patient_history.js @@ -0,0 +1,300 @@ +frappe.provide("frappe.patient_history"); +frappe.pages['patient_history'].on_page_load = function(wrapper) { + var me = this; + var page = frappe.ui.make_app_page({ + parent: wrapper, + title: 'Patient History', + single_column: true + }); + + frappe.breadcrumbs.add("Healthcare"); + let pid = ''; + page.main.html(frappe.render_template("patient_history", {})); + var patient = frappe.ui.form.make_control({ + parent: page.main.find(".patient"), + df: { + fieldtype: "Link", + options: "Patient", + fieldname: "patient", + change: function(){ + if(pid != patient.get_value() && patient.get_value()){ + me.start = 0; + me.page.main.find(".patient_documents_list").html(""); + get_documents(patient.get_value(), me); + show_patient_info(patient.get_value(), me); + show_patient_vital_charts(patient.get_value(), me, "bp", "mmHg", "Blood Pressure"); + } + pid = patient.get_value(); + } + }, + only_input: true, + }); + patient.refresh(); + + if (frappe.route_options){ + patient.set_value(frappe.route_options.patient); + } + + this.page.main.on("click", ".btn-show-chart", function() { + var btn_show_id = $(this).attr("data-show-chart-id"), pts = $(this).attr("data-pts"); + var title = $(this).attr("data-title"); + show_patient_vital_charts(patient.get_value(), me, btn_show_id, pts, title); + }); + + this.page.main.on("click", ".btn-more", function() { + var doctype = $(this).attr("data-doctype"), docname = $(this).attr("data-docname"); + if(me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched') == "1"){ + me.page.main.find("."+docname).hide(); + me.page.main.find("."+docname).parent().find('.document-html').show(); + }else{ + if(doctype && docname){ + let exclude = ["patient", "patient_name", 'patient_sex', "encounter_date"]; + frappe.call({ + method: "erpnext.healthcare.utils.render_doc_as_html", + args:{ + doctype: doctype, + docname: docname, + exclude_fields: exclude + }, + callback: function(r) { + if (r.message){ + me.page.main.find("."+docname).hide(); + me.page.main.find("."+docname).parent().find('.document-html').html(r.message.html+"\ +
"); + me.page.main.find("."+docname).parent().find('.document-html').show(); + me.page.main.find("."+docname).parent().find('.document-html').attr('data-fetched', "1"); + } + }, + freeze: true + }); + } + } + }); + + this.page.main.on("click", ".btn-less", function() { + var docname = $(this).attr("data-docname"); + me.page.main.find("."+docname).parent().find('.document-id').show(); + me.page.main.find("."+docname).parent().find('.document-html').hide(); + }); + me.start = 0; + me.page.main.on("click", ".btn-get-records", function(){ + get_documents(patient.get_value(), me); + }); +}; + +var get_documents = function(patient, me){ + frappe.call({ + "method": "erpnext.healthcare.page.patient_history.patient_history.get_feed", + args: { + name: patient, + start: me.start, + page_length: 20 + }, + callback: function (r) { + var data = r.message; + if(data.length){ + add_to_records(me, data); + }else{ + me.page.main.find(".patient_documents_list").append("


No more records..

"); + me.page.main.find(".btn-get-records").hide(); + } + } + }); +}; + +var add_to_records = function(me, data){ + var details = ""; + me.page.main.find(".patient_documents_list").append(details); + me.start += data.length; + if(data.length===20){ + me.page.main.find(".btn-get-records").show(); + }else{ + me.page.main.find(".btn-get-records").hide(); + me.page.main.find(".patient_documents_list").append("


No more records..

"); + } +}; + +var add_date_separator = function(data) { + var date = frappe.datetime.str_to_obj(data.creation); + + var diff = frappe.datetime.get_day_diff(frappe.datetime.get_today(), frappe.datetime.obj_to_str(date)); + if(diff < 1) { + var pdate = 'Today'; + } else if(diff < 2) { + pdate = 'Yesterday'; + } else { + pdate = frappe.datetime.global_date_format(date); + } + data.date_sep = pdate; + return data; +}; + +var show_patient_info = function(patient, me){ + frappe.call({ + "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", + args: { + patient: patient + }, + callback: function (r) { + var data = r.message; + var details = ""; + if(data.image){ + details += "
"; + } + details += "" + data.patient_name +"
" + data.sex; + if(data.email) details += "
" + data.email; + if(data.mobile) details += "
" + data.mobile; + if(data.occupation) details += "

Occupation : " + data.occupation; + if(data.blood_group) details += "
Blood group : " + data.blood_group; + if(data.allergies) details += "

Allergies : "+ data.allergies.replace("\n", "
"); + if(data.medication) details += "
Medication : "+ data.medication.replace("\n", "
"); + if(data.alcohol_current_use) details += "

Alcohol use : "+ data.alcohol_current_use; + if(data.alcohol_past_use) details += "
Alcohol past use : "+ data.alcohol_past_use; + if(data.tobacco_current_use) details += "
Tobacco use : "+ data.tobacco_current_use; + if(data.tobacco_past_use) details += "
Tobacco past use : "+ data.tobacco_past_use; + if(data.medical_history) details += "

Medical history : "+ data.medical_history.replace("\n", "
"); + if(data.surgical_history) details += "
Surgical history : "+ data.surgical_history.replace("\n", "
"); + if(data.surrounding_factors) details += "

Occupational hazards : "+ data.surrounding_factors.replace("\n", "
"); + if(data.other_risk_factors) details += "
Other risk factors : " + data.other_risk_factors.replace("\n", "
"); + if(data.patient_details) details += "

More info : " + data.patient_details.replace("\n", "
"); + + if(details){ + details = "
" + details + "
"; + } + me.page.main.find(".patient_details").html(details); + } + }); +}; + +var show_patient_vital_charts = function(patient, me, btn_show_id, pts, title) { + frappe.call({ + method: "erpnext.healthcare.utils.get_patient_vitals", + args:{ + patient: patient + }, + callback: function(r) { + if (r.message){ + var show_chart_btns_html = "
Blood Pressure\ + Respiratory/Pulse Rate\ + Temperature\ + BMI
"; + me.page.main.find(".show_chart_btns").html(show_chart_btns_html); + var data = r.message; + let labels = [], datasets = []; + let bp_systolic = [], bp_diastolic = [], temperature = []; + let pulse = [], respiratory_rate = [], bmi = [], height = [], weight = []; + for(var i=0; i (d + '').toUpperCase(), + formatTooltipY: d => d + ' ' + pts, + } + }); + }else{ + me.page.main.find(".patient_vital_charts").html(""); + me.page.main.find(".show_chart_btns").html(""); + } + } + }); +}; diff --git a/erpnext/healthcare/page/medical_record/medical_record.json b/erpnext/healthcare/page/patient_history/patient_history.json similarity index 57% rename from erpnext/healthcare/page/medical_record/medical_record.json rename to erpnext/healthcare/page/patient_history/patient_history.json index ca30c3be29..b3892a41c6 100644 --- a/erpnext/healthcare/page/medical_record/medical_record.json +++ b/erpnext/healthcare/page/patient_history/patient_history.json @@ -1,18 +1,21 @@ { "content": null, - "creation": "2016-06-09 11:33:14.025787", + "creation": "2018-08-08 17:09:13.816199", "docstatus": 0, "doctype": "Page", - "icon": "icon-play", + "icon": "", "idx": 0, - "modified": "2018-08-06 11:40:39.705660", + "modified": "2018-08-08 17:09:55.969424", "modified_by": "Administrator", "module": "Healthcare", - "name": "medical_record", + "name": "patient_history", "owner": "Administrator", - "page_name": "medical_record", + "page_name": "patient_history", "restrict_to_domain": "Healthcare", "roles": [ + { + "role": "Healthcare Administrator" + }, { "role": "Physician" } @@ -21,5 +24,5 @@ "standard": "Yes", "style": null, "system_page": 0, - "title": "Medical Record" + "title": "Patient History" } \ No newline at end of file diff --git a/erpnext/healthcare/page/patient_history/patient_history.py b/erpnext/healthcare/page/patient_history/patient_history.py new file mode 100644 index 0000000000..772aa4ef5e --- /dev/null +++ b/erpnext/healthcare/page/patient_history/patient_history.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2018, ESS LLP and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import cint +from erpnext.healthcare.utils import render_docs_as_html + +@frappe.whitelist() +def get_feed(name, start=0, page_length=20): + """get feed""" + result = frappe.db.sql("""select name, owner, creation, + reference_doctype, reference_name, subject + from `tabPatient Medical Record` + where patient=%(patient)s + order by creation desc + limit %(start)s, %(page_length)s""", + { + "patient": name, + "start": cint(start), + "page_length": cint(page_length) + }, as_dict=True) + return result + +@frappe.whitelist() +def get_feed_for_dt(doctype, docname): + """get feed""" + result = frappe.db.sql("""select name, owner, modified, creation, + reference_doctype, reference_name, subject + from `tabPatient Medical Record` + where reference_name=%(docname)s and reference_doctype=%(doctype)s + order by creation desc""", + { + "docname": docname, + "doctype": doctype + }, as_dict=True) + + return result diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py index 6a226d9c6b..97bb98f677 100644 --- a/erpnext/healthcare/utils.py +++ b/erpnext/healthcare/utils.py @@ -429,3 +429,116 @@ def get_children(doctype, parent, company, is_root=False): occupancy_msg = str(occupied) + " Occupied out of " + str(occupancy_total) each["occupied_out_of_vacant"] = occupancy_msg return hc_service_units + +@frappe.whitelist() +def get_patient_vitals(patient, from_date=None, to_date=None): + if not patient: return + vitals = frappe.db.sql("""select * from `tabVital Signs` where \ + docstatus=1 and patient=%s order by signs_date, signs_time""", \ + (patient), as_dict=1) + if vitals and vitals[0]: + return vitals + else: + return False + +@frappe.whitelist() +def render_docs_as_html(docs): + # docs key value pair {doctype: docname} + docs_html = "
" + for doc in docs: + docs_html += render_doc_as_html(doc['doctype'], doc['docname'])['html'] + "
" + return {'html': docs_html} + +@frappe.whitelist() +def render_doc_as_html(doctype, docname, exclude_fields = []): + #render document as html, three column layout will break + doc = frappe.get_doc(doctype, docname) + meta = frappe.get_meta(doctype) + doc_html = "
" + section_html = "" + section_label = "" + html = "" + sec_on = False + col_on = 0 + has_data = False + for df in meta.fields: + #on section break append append previous section and html to doc html + if df.fieldtype == "Section Break": + if has_data and col_on and sec_on: + doc_html += section_html + html + "
" + elif has_data and not col_on and sec_on: + doc_html += "
" \ + + section_html + html +"
" + while col_on: + doc_html += "
" + col_on -= 1 + sec_on = True + has_data= False + col_on = 0 + section_html = "" + html = "" + if df.label: + section_label = df.label + continue + #on column break append html to section html or doc html + if df.fieldtype == "Column Break": + if sec_on and has_data: + section_html += "
" + section_label + "" + html + "
" + elif has_data: + doc_html += "
" + html + "
" + elif sec_on and not col_on: + section_html += "
" + html = "" + col_on += 1 + if df.label: + html += '
' + df.label + continue + #on table iterate in items and create table based on in_list_view, append to section html or doc html + if df.fieldtype == "Table": + items = doc.get(df.fieldname) + if not items: continue + child_meta = frappe.get_meta(df.options) + if not has_data : has_data = True + table_head = "" + table_row = "" + create_head = True + for item in items: + table_row += '' + for cdf in child_meta.fields: + if cdf.in_list_view: + if create_head: + table_head += '' + cdf.label + '' + if item.get(cdf.fieldname): + table_row += '' + str(item.get(cdf.fieldname)) \ + + '' + else: + table_row += '' + create_head = False + table_row += '' + if sec_on: + section_html += '' + table_head + table_row + '
' + else: + html += '' \ + + table_head + table_row + '
' + continue + #on other field types add label and value to html + if not df.hidden and not df.print_hide and doc.get(df.fieldname) and df.fieldname not in exclude_fields: + html += "
{0} : {1}".format(df.label or df.fieldname, \ + doc.get(df.fieldname)) + if not has_data : has_data = True + if sec_on and col_on and has_data: + doc_html += section_html + html + "
" + elif sec_on and not col_on and has_data: + doc_html += "
" \ + + section_html + html +"
" + if doc_html: + doc_html = "
" %(doctype, docname) + doc_html + "
" + + return {'html': doc_html} diff --git a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py b/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py deleted file mode 100644 index b6c650207f..0000000000 --- a/erpnext/hr/doctype/employee_loan_application/employee_loan_application.py +++ /dev/null @@ -1,69 +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, math -from frappe import _ -from frappe.utils import flt, rounded -from frappe.model.mapper import get_mapped_doc -from frappe.model.document import Document - -from erpnext.hr.doctype.employee_loan.employee_loan import get_monthly_repayment_amount, check_repayment_method - -class EmployeeLoanApplication(Document): - def validate(self): - check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods) - self.validate_loan_amount() - self.get_repayment_details() - - def validate_loan_amount(self): - maximum_loan_limit = frappe.db.get_value('Loan Type', self.loan_type, 'maximum_loan_amount') - if maximum_loan_limit and self.loan_amount > maximum_loan_limit: - frappe.throw(_("Loan Amount cannot exceed Maximum Loan Amount of {0}").format(maximum_loan_limit)) - - def get_repayment_details(self): - if self.repayment_method == "Repay Over Number of Periods": - self.repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) - - if self.repayment_method == "Repay Fixed Amount per Period": - monthly_interest_rate = flt(self.rate_of_interest) / (12 *100) - if monthly_interest_rate: - monthly_interest_amount = self.loan_amount * monthly_interest_rate - if monthly_interest_amount >= self.repayment_amount: - frappe.throw(_("Repayment amount {} should be greater than monthly interest amount {}"). - format(self.repayment_amount, monthly_interest_amount)) - - self.repayment_periods = math.ceil((math.log(self.repayment_amount) - - math.log(self.repayment_amount - (monthly_interest_amount))) / - (math.log(1 + monthly_interest_rate))) - else: - self.repayment_periods = self.loan_amount / self.repayment_amount - - self.calculate_payable_amount() - - def calculate_payable_amount(self): - balance_amount = self.loan_amount - self.total_payable_amount = 0 - self.total_payable_interest = 0 - - while(balance_amount > 0): - interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100)) - balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount) - - self.total_payable_interest += interest_amount - - self.total_payable_amount = self.loan_amount + self.total_payable_interest - -@frappe.whitelist() -def make_employee_loan(source_name, target_doc = None): - doclist = get_mapped_doc("Employee Loan Application", source_name, { - "Employee Loan Application": { - "doctype": "Employee Loan", - "validation": { - "docstatus": ["=", 1] - } - } - }, target_doc) - - return doclist \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py index 5e7f276ccc..35c9f728b6 100644 --- a/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py +++ b/erpnext/hr/doctype/employee_onboarding/test_employee_onboarding.py @@ -12,7 +12,7 @@ from erpnext.hr.doctype.employee_onboarding.employee_onboarding import Incomplet class TestEmployeeOnboarding(unittest.TestCase): def test_employee_onboarding_incomplete_task(self): if frappe.db.exists('Employee Onboarding', {'employee_name': 'Test Researcher'}): - return frappe.get_doc('Employee Onboarding', {'employee_name': 'Test Researcher'}) + frappe.delete_doc('Employee Onboarding', {'employee_name': 'Test Researcher'}) _set_up() applicant = get_job_applicant() onboarding = frappe.new_doc('Employee Onboarding') @@ -39,9 +39,10 @@ class TestEmployeeOnboarding(unittest.TestCase): # complete the task project = frappe.get_doc('Project', onboarding.project) - project.load_tasks() - project.tasks[0].status = 'Completed' - project.save() + for task in frappe.get_all('Task', dict(project=project.name)): + task = frappe.get_doc('Task', task.name) + task.status = 'Completed' + task.save() # make employee onboarding.reload() @@ -71,4 +72,3 @@ def _set_up(): 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) - frappe.db.sql("delete from `tabProject Task` where parent=%s", project) diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 92fdc09443..6618a4f7c5 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -10,33 +10,36 @@ from erpnext.accounts.doctype.account.test_account import create_account test_records = frappe.get_test_records('Expense Claim') test_dependencies = ['Employee'] +company_name = '_Test Company 4' + class TestExpenseClaim(unittest.TestCase): def test_total_expense_claim_for_project(self): frappe.db.sql("""delete from `tabTask` where project = "_Test Project 1" """) - frappe.db.sql("""delete from `tabProject Task` where parent = "_Test Project 1" """) frappe.db.sql("""delete from `tabProject` where name = "_Test Project 1" """) - frappe.db.sql("delete from `tabExpense Claim` where project='_Test Project 1'") + frappe.db.sql("update `tabExpense Claim` set project = '', task = ''") frappe.get_doc({ "project_name": "_Test Project 1", - "doctype": "Project", + "doctype": "Project" }).save() - task = frappe.get_doc({ - "doctype": "Task", - "subject": "_Test Project Task 1", - "project": "_Test Project 1" - }).save() + task = frappe.get_doc(dict( + doctype = 'Task', + subject = '_Test Project Task 1', + status = 'Open', + project = '_Test Project 1' + )).insert() - task_name = frappe.db.get_value("Task", {"project": "_Test Project 1"}) - payable_account = get_payable_account("Wind Power LLC") - make_expense_claim(payable_account, 300, 200, "Wind Power LLC","Travel Expenses - WP", "_Test Project 1", task_name) + task_name = task.name + payable_account = get_payable_account(company_name) + + make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", "_Test Project 1", task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) - expense_claim2 = make_expense_claim(payable_account, 600, 500, "Wind Power LLC", "Travel Expenses - WP","_Test Project 1", task_name) + expense_claim2 = make_expense_claim(payable_account, 600, 500, company_name, "Travel Expenses - _TC4","_Test Project 1", task_name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 700) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700) @@ -48,8 +51,8 @@ class TestExpenseClaim(unittest.TestCase): self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) def test_expense_claim_status(self): - payable_account = get_payable_account("Wind Power LLC") - expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP") + payable_account = get_payable_account(company_name) + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4") je_dict = make_bank_entry("Expense Claim", expense_claim.name) je = frappe.get_doc(je_dict) @@ -66,9 +69,9 @@ class TestExpenseClaim(unittest.TestCase): self.assertEqual(expense_claim.status, "Unpaid") def test_expense_claim_gl_entry(self): - payable_account = get_payable_account("Wind Power LLC") + payable_account = get_payable_account(company_name) taxes = generate_taxes() - expense_claim = make_expense_claim(payable_account, 300, 200, "Wind Power LLC", "Travel Expenses - WP", do_not_submit=True, taxes=taxes) + expense_claim = make_expense_claim(payable_account, 300, 200, company_name, "Travel Expenses - _TC4", do_not_submit=True, taxes=taxes) expense_claim.submit() gl_entries = frappe.db.sql("""select account, debit, credit @@ -78,9 +81,9 @@ class TestExpenseClaim(unittest.TestCase): self.assertTrue(gl_entries) expected_values = dict((d[0], d) for d in [ - ['CGST - WP',10.0, 0.0], - [payable_account, 0.0, 210.0], - ["Travel Expenses - WP", 200.0, 0.0] + ['CGST - _TC4',18.0, 0.0], + [payable_account, 0.0, 218.0], + ["Travel Expenses - _TC4", 200.0, 0.0] ]) for gle in gl_entries: @@ -89,14 +92,14 @@ class TestExpenseClaim(unittest.TestCase): self.assertEquals(expected_values[gle.account][2], gle.credit) def test_rejected_expense_claim(self): - payable_account = get_payable_account("Wind Power LLC") + payable_account = get_payable_account(company_name) expense_claim = frappe.get_doc({ "doctype": "Expense Claim", "employee": "_T-Employee-00001", "payable_account": payable_account, "approval_status": "Rejected", "expenses": - [{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "amount": 300, "sanctioned_amount": 200 }] + [{ "expense_type": "Travel", "default_account": "Travel Expenses - _TC4", "amount": 300, "sanctioned_amount": 200 }] }) expense_claim.submit() @@ -111,9 +114,9 @@ def get_payable_account(company): def generate_taxes(): parent_account = frappe.db.get_value('Account', - {'company': "Wind Power LLC", 'is_group':1, 'account_type': 'Tax'}, + {'company': company_name, 'is_group':1, 'account_type': 'Tax'}, 'name') - account = create_account(company="Wind Power LLC", account_name="CGST", account_type="Tax", parent_account=parent_account) + account = create_account(company=company_name, account_name="CGST", account_type="Tax", parent_account=parent_account) return {'taxes':[{ "account_head": account, "rate": 0, @@ -124,15 +127,18 @@ def generate_taxes(): def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None): employee = frappe.db.get_value("Employee", {"status": "Active"}) + currency = frappe.db.get_value('Company', company, 'default_currency') expense_claim = { "doctype": "Expense Claim", "employee": employee, "payable_account": payable_account, "approval_status": "Approved", "company": company, + 'currency': currency, "expenses": [{"expense_type": "Travel", "default_account": account, + 'currency': currency, "amount": amount, "sanctioned_amount": sanctioned_amount}]} if taxes: diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js index 9e5effe6a3..4004c1cc2d 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.js +++ b/erpnext/hr/doctype/hr_settings/hr_settings.js @@ -15,7 +15,7 @@ frappe.ui.form.on('HR Settings', { let policy = frm.doc.password_policy; if (policy) { if (policy.includes(' ') || policy.includes('--')) { - frappe.msgprint(_("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically")); + frappe.msgprint(__("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically")); } frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); } diff --git a/erpnext/hr/doctype/job_offer/job_offer.js b/erpnext/hr/doctype/job_offer/job_offer.js index 1ee35afe10..c3d83c48cc 100755 --- a/erpnext/hr/doctype/job_offer/job_offer.js +++ b/erpnext/hr/doctype/job_offer/job_offer.js @@ -4,6 +4,12 @@ frappe.provide("erpnext.job_offer"); frappe.ui.form.on("Job Offer", { + onload: function (frm) { + frm.set_query("select_terms", function() { + return { filters: { hr: 1 } }; + }); + }, + select_terms: function (frm) { erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) { if (!r.exc) { diff --git a/erpnext/hr/doctype/loan/loan.js b/erpnext/hr/doctype/loan/loan.js index e1b41786f4..3f5c30c475 100644 --- a/erpnext/hr/doctype/loan/loan.js +++ b/erpnext/hr/doctype/loan/loan.js @@ -39,31 +39,19 @@ frappe.ui.form.on('Loan', { }, refresh: function (frm) { - if (frm.doc.docstatus == 1 && frm.doc.status == "Sanctioned") { - frm.add_custom_button(__('Create Disbursement Entry'), function() { - frm.trigger("make_jv"); - }) - } - if (frm.doc.repayment_schedule) { - let total_amount_paid = 0; - $.each(frm.doc.repayment_schedule || [], function(i, row) { - if (row.paid) { - total_amount_paid += row.total_payment; - } - }); - frm.set_value("total_amount_paid", total_amount_paid); -; } - if (frm.doc.docstatus == 1 && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) { - frm.add_custom_button(__('Create Repayment Entry'), function() { - frm.trigger("make_repayment_entry"); - }) + if (frm.doc.docstatus == 1) { + if (frm.doc.status == "Sanctioned") { + frm.add_custom_button(__('Create Disbursement Entry'), function() { + frm.trigger("make_jv"); + }).addClass("btn-primary"); + } else if (frm.doc.status == "Disbursed" && frm.doc.repayment_start_date && (frm.doc.applicant_type == 'Member' || frm.doc.repay_from_salary == 0)) { + frm.add_custom_button(__('Create Repayment Entry'), function() { + frm.trigger("make_repayment_entry"); + }).addClass("btn-primary"); + } } frm.trigger("toggle_fields"); }, - status: function (frm) { - frm.toggle_reqd("disbursement_date", frm.doc.status == 'Disbursed') - frm.toggle_reqd("repayment_start_date", frm.doc.status == 'Disbursed') - }, make_jv: function (frm) { frappe.call({ diff --git a/erpnext/hr/doctype/loan/loan.json b/erpnext/hr/doctype/loan/loan.json index 587b3010ca..505b601edd 100644 --- a/erpnext/hr/doctype/loan/loan.json +++ b/erpnext/hr/doctype/loan/loan.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_events_in_timeline": 0, "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 0, @@ -20,6 +21,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "applicant_type", "fieldtype": "Select", "hidden": 0, @@ -53,6 +55,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "applicant", "fieldtype": "Dynamic Link", "hidden": 0, @@ -86,6 +89,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "applicant_name", "fieldtype": "Data", "hidden": 0, @@ -118,6 +122,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_application", "fieldtype": "Link", "hidden": 0, @@ -151,6 +156,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_type", "fieldtype": "Link", "hidden": 0, @@ -184,6 +190,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, @@ -215,7 +222,8 @@ "bold": 0, "collapsible": 0, "columns": 0, - "default": "", + "default": "Today", + "fetch_if_empty": 0, "fieldname": "posting_date", "fieldtype": "Date", "hidden": 0, @@ -248,6 +256,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", "hidden": 0, @@ -282,6 +291,7 @@ "collapsible": 0, "columns": 0, "default": "Sanctioned", + "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -299,7 +309,7 @@ "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "remember_last_selected_value": 0, "report_hide": 0, "reqd": 0, @@ -316,6 +326,7 @@ "collapsible": 0, "columns": 0, "depends_on": "eval:doc.applicant_type==\"Employee\"", + "fetch_if_empty": 0, "fieldname": "repay_from_salary", "fieldtype": "Check", "hidden": 0, @@ -348,6 +359,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_8", "fieldtype": "Section Break", "hidden": 0, @@ -380,6 +392,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_amount", "fieldtype": "Currency", "hidden": 0, @@ -415,6 +428,7 @@ "columns": 0, "default": "", "fetch_from": "loan_type.rate_of_interest", + "fetch_if_empty": 0, "fieldname": "rate_of_interest", "fieldtype": "Percent", "hidden": 0, @@ -448,6 +462,8 @@ "bold": 0, "collapsible": 0, "columns": 0, + "depends_on": "eval:doc.status==\"Disbursed\"", + "fetch_if_empty": 0, "fieldname": "disbursement_date", "fieldtype": "Date", "hidden": 0, @@ -480,6 +496,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "repayment_start_date", "fieldtype": "Date", "hidden": 0, @@ -499,7 +516,7 @@ "read_only": 0, "remember_last_selected_value": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "translatable": 0, @@ -512,6 +529,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_11", "fieldtype": "Column Break", "hidden": 0, @@ -544,6 +562,7 @@ "collapsible": 0, "columns": 0, "default": "Repay Over Number of Periods", + "fetch_if_empty": 0, "fieldname": "repayment_method", "fieldtype": "Select", "hidden": 0, @@ -579,6 +598,7 @@ "columns": 0, "default": "", "depends_on": "", + "fetch_if_empty": 0, "fieldname": "repayment_periods", "fieldtype": "Int", "hidden": 0, @@ -613,6 +633,7 @@ "columns": 0, "default": "", "depends_on": "", + "fetch_if_empty": 0, "fieldname": "monthly_repayment_amount", "fieldtype": "Currency", "hidden": 0, @@ -646,6 +667,7 @@ "bold": 0, "collapsible": 1, "columns": 0, + "fetch_if_empty": 0, "fieldname": "account_info", "fieldtype": "Section Break", "hidden": 0, @@ -678,6 +700,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "mode_of_payment", "fieldtype": "Link", "hidden": 0, @@ -711,6 +734,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "payment_account", "fieldtype": "Link", "hidden": 0, @@ -744,6 +768,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_9", "fieldtype": "Column Break", "hidden": 0, @@ -775,6 +800,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "loan_account", "fieldtype": "Link", "hidden": 0, @@ -808,6 +834,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "interest_income_account", "fieldtype": "Link", "hidden": 0, @@ -841,6 +868,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_15", "fieldtype": "Section Break", "hidden": 0, @@ -873,6 +901,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "repayment_schedule", "fieldtype": "Table", "hidden": 0, @@ -906,6 +935,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "section_break_17", "fieldtype": "Section Break", "hidden": 0, @@ -939,6 +969,7 @@ "collapsible": 0, "columns": 0, "default": "0", + "fetch_if_empty": 0, "fieldname": "total_payment", "fieldtype": "Currency", "hidden": 0, @@ -972,6 +1003,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "column_break_19", "fieldtype": "Column Break", "hidden": 0, @@ -1004,6 +1036,7 @@ "collapsible": 0, "columns": 0, "default": "0", + "fetch_if_empty": 0, "fieldname": "total_interest_payable", "fieldtype": "Currency", "hidden": 0, @@ -1037,6 +1070,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "total_amount_paid", "fieldtype": "Currency", "hidden": 0, @@ -1070,6 +1104,7 @@ "bold": 0, "collapsible": 0, "columns": 0, + "fetch_if_empty": 0, "fieldname": "amended_from", "fieldtype": "Link", "hidden": 0, @@ -1106,7 +1141,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 16:15:53.267145", + "modified": "2019-07-10 13:04:20.953694", "modified_by": "Administrator", "module": "HR", "name": "Loan", @@ -1149,7 +1184,6 @@ "set_user_permissions": 0, "share": 0, "submit": 0, - "user_permission_doctypes": "[\"Employee\"]", "write": 0 } ], diff --git a/erpnext/hr/doctype/loan/loan.py b/erpnext/hr/doctype/loan/loan.py index 58c9b8f667..a803863124 100644 --- a/erpnext/hr/doctype/loan/loan.py +++ b/erpnext/hr/doctype/loan/loan.py @@ -6,29 +6,33 @@ from __future__ import unicode_literals import frappe, math, json import erpnext from frappe import _ -from frappe.utils import flt, rounded, add_months, nowdate +from frappe.utils import flt, rounded, add_months, nowdate, getdate from erpnext.controllers.accounts_controller import AccountsController class Loan(AccountsController): def validate(self): - check_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods) + validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods) + self.set_missing_fields() + self.make_repayment_schedule() + self.set_repayment_period() + self.calculate_totals() + + def set_missing_fields(self): if not self.company: self.company = erpnext.get_default_company() + if not self.posting_date: self.posting_date = nowdate() + if self.loan_type and not self.rate_of_interest: self.rate_of_interest = frappe.db.get_value("Loan Type", self.loan_type, "rate_of_interest") + if self.repayment_method == "Repay Over Number of Periods": self.monthly_repayment_amount = get_monthly_repayment_amount(self.repayment_method, self.loan_amount, self.rate_of_interest, self.repayment_periods) + if self.status == "Repaid/Closed": self.total_amount_paid = self.total_payment - if self.status == 'Disbursed' and self.repayment_start_date < self.disbursement_date: - frappe.throw(_("Repayment Start Date cannot be before Disbursement Date.")) - if self.status == "Disbursed": - self.make_repayment_schedule() - self.set_repayment_period() - self.calculate_totals() def make_jv_entry(self): self.check_permission('write') @@ -105,20 +109,31 @@ def update_total_amount_paid(doc): frappe.db.set_value("Loan", doc.name, "total_amount_paid", total_amount_paid) def update_disbursement_status(doc): - disbursement = frappe.db.sql("""select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount - from `tabGL Entry` where account = %s and against_voucher_type = 'Loan' and against_voucher = %s""", - (doc.payment_account, doc.name), as_dict=1)[0] - if disbursement.disbursed_amount == doc.loan_amount: - frappe.db.set_value("Loan", doc.name , "status", "Disbursed") - if disbursement.disbursed_amount == 0: - frappe.db.set_value("Loan", doc.name , "status", "Sanctioned") - if disbursement.disbursed_amount > doc.loan_amount: - frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount)) - if disbursement.disbursed_amount > 0: - frappe.db.set_value("Loan", doc.name , "disbursement_date", disbursement.posting_date) - frappe.db.set_value("Loan", doc.name , "repayment_start_date", disbursement.posting_date) + disbursement = frappe.db.sql(""" + select posting_date, ifnull(sum(credit_in_account_currency), 0) as disbursed_amount + from `tabGL Entry` + where account = %s and against_voucher_type = 'Loan' and against_voucher = %s + """, (doc.payment_account, doc.name), as_dict=1)[0] -def check_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods): + disbursement_date = None + if not disbursement or disbursement.disbursed_amount == 0: + status = "Sanctioned" + elif disbursement.disbursed_amount == doc.loan_amount: + disbursement_date = disbursement.posting_date + status = "Disbursed" + elif disbursement.disbursed_amount > doc.loan_amount: + frappe.throw(_("Disbursed Amount cannot be greater than Loan Amount {0}").format(doc.loan_amount)) + + if status == 'Disbursed' and getdate(disbursement_date) > getdate(frappe.db.get_value("Loan", doc.name, "repayment_start_date")): + frappe.throw(_("Disbursement Date cannot be after Loan Repayment Start Date")) + + frappe.db.sql(""" + update `tabLoan` + set status = %s, disbursement_date = %s + where name = %s + """, (status, disbursement_date, doc.name)) + +def validate_repayment_method(repayment_method, loan_amount, monthly_repayment_amount, repayment_periods): if repayment_method == "Repay Over Number of Periods" and not repayment_periods: frappe.throw(_("Please enter Repayment Periods")) @@ -222,4 +237,4 @@ def make_jv_entry(loan, company, loan_account, applicant_type, applicant, loan_a "reference_name": loan, }) 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/hr/doctype/loan_application/loan_application.js b/erpnext/hr/doctype/loan_application/loan_application.js index febcbd88e7..a73b62a894 100644 --- a/erpnext/hr/doctype/loan_application/loan_application.js +++ b/erpnext/hr/doctype/loan_application/loan_application.js @@ -23,9 +23,8 @@ frappe.ui.form.on('Loan Application', { }, add_toolbar_buttons: function(frm) { if (frm.doc.status == "Approved") { - frm.add_custom_button(__('Loan'), function() { + frm.add_custom_button(__('Create Loan'), function() { frappe.call({ - type: "GET", method: "erpnext.hr.doctype.loan_application.loan_application.make_loan", args: { "source_name": frm.doc.name @@ -37,7 +36,7 @@ frappe.ui.form.on('Loan Application', { } } }); - }) + }).addClass("btn-primary"); } } }); diff --git a/erpnext/hr/doctype/loan_application/loan_application.py b/erpnext/hr/doctype/loan_application/loan_application.py index 706c9646c7..67be9f2a1c 100644 --- a/erpnext/hr/doctype/loan_application/loan_application.py +++ b/erpnext/hr/doctype/loan_application/loan_application.py @@ -9,11 +9,11 @@ from frappe.utils import flt, rounded from frappe.model.mapper import get_mapped_doc from frappe.model.document import Document -from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, check_repayment_method +from erpnext.hr.doctype.loan.loan import get_monthly_repayment_amount, validate_repayment_method class LoanApplication(Document): def validate(self): - check_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods) + validate_repayment_method(self.repayment_method, self.loan_amount, self.repayment_amount, self.repayment_periods) self.validate_loan_amount() self.get_repayment_details() @@ -29,14 +29,17 @@ class LoanApplication(Document): if self.repayment_method == "Repay Fixed Amount per Period": monthly_interest_rate = flt(self.rate_of_interest) / (12 *100) if monthly_interest_rate: - self.repayment_periods = math.ceil((math.log(self.repayment_amount) - - math.log(self.repayment_amount - (self.loan_amount*monthly_interest_rate))) / - (math.log(1 + monthly_interest_rate))) + min_repayment_amount = self.loan_amount*monthly_interest_rate + if self.repayment_amount - min_repayment_amount < 0: + frappe.throw(_("Repayment Amount must be greater than " \ + + str(flt(min_repayment_amount, 2)))) + self.repayment_periods = math.ceil(math.log(self.repayment_amount) - + math.log(self.repayment_amount - min_repayment_amount) /(math.log(1 + monthly_interest_rate))) else: self.repayment_periods = self.loan_amount / self.repayment_amount self.calculate_payable_amount() - + def calculate_payable_amount(self): balance_amount = self.loan_amount self.total_payable_amount = 0 @@ -47,9 +50,9 @@ class LoanApplication(Document): balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount) self.total_payable_interest += interest_amount - + self.total_payable_amount = self.loan_amount + self.total_payable_interest - + @frappe.whitelist() def make_loan(source_name, target_doc = None): doclist = get_mapped_doc("Loan Application", source_name, { diff --git a/erpnext/hr/doctype/loan_application/test_loan_application.py b/erpnext/hr/doctype/loan_application/test_loan_application.py index 7644dd0c9f..b08b522503 100644 --- a/erpnext/hr/doctype/loan_application/test_loan_application.py +++ b/erpnext/hr/doctype/loan_application/test_loan_application.py @@ -31,21 +31,22 @@ class TestLoanApplication(unittest.TestCase): "rate_of_interest": 9.2, "loan_amount": 250000, "repayment_method": "Repay Over Number of Periods", - "repayment_periods": 24 + "repayment_periods": 18 }) loan_application.insert() - + def test_loan_totals(self): loan_application = frappe.get_doc("Loan Application", {"applicant":self.applicant}) - self.assertEquals(loan_application.repayment_amount, 11445) - self.assertEquals(loan_application.total_payable_interest, 24657) - self.assertEquals(loan_application.total_payable_amount, 274657) - loan_application.repayment_method = "Repay Fixed Amount per Period" - loan_application.repayment_amount = 15000 + self.assertEqual(loan_application.total_payable_interest, 18599) + self.assertEqual(loan_application.total_payable_amount, 268599) + self.assertEqual(loan_application.repayment_amount, 14923) + + loan_application.repayment_periods = 24 loan_application.save() + loan_application.reload() - self.assertEqual(loan_application.repayment_periods, 18) - self.assertEqual(loan_application.total_payable_interest, 18506) - self.assertEqual(loan_application.total_payable_amount, 268506) \ No newline at end of file + self.assertEqual(loan_application.total_payable_interest, 24657) + self.assertEqual(loan_application.total_payable_amount, 274657) + self.assertEqual(loan_application.repayment_amount, 11445) diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py index eaf6b1e2d3..b98f445c0f 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.py +++ b/erpnext/hr/doctype/shift_type/shift_type.py @@ -22,7 +22,7 @@ class ShiftType(Document): 'skip_auto_attendance':'0', 'attendance':('is', 'not set'), 'time':('>=', self.process_attendance_after), - 'shift_actual_start': ('<', self.last_sync_of_checkin), + 'shift_actual_end': ('<', self.last_sync_of_checkin), 'shift': self.name } logs = frappe.db.get_list('Employee Checkin', fields="*", filters=filters, order_by="employee,time") diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js index 89efb9369d..0bbf689d4a 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js @@ -2,6 +2,10 @@ // For license information, please see license.txt frappe.ui.form.on('Blanket Order', { + onload: function(frm) { + frm.trigger('set_tc_name_filter'); + }, + setup: function(frm) { frm.add_fetch("customer", "customer_name", "customer_name"); frm.add_fetch("supplier", "supplier_name", "supplier_name"); @@ -44,4 +48,23 @@ frappe.ui.form.on('Blanket Order', { } }); }, + + set_tc_name_filter: function(frm) { + if (frm.doc.blanket_order_type === 'Selling') { + frm.set_query("tc_name", function() { + return { filters: { selling: 1 } }; + }); + } + if (frm.doc.blanket_order_type === 'Purchasing') { + frm.set_query("tc_name", function() { + return { filters: { buying: 1 } }; + }); + } + }, + + blanket_order_type: function (frm) { + frm.trigger('set_tc_name_filter'); + } }); + + diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py new file mode 100644 index 0000000000..ed319a0cef --- /dev/null +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'blanket_order', + 'transactions': [ + { + 'items': ['Purchase Order', 'Sales Order'] + } + ] + } diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index b17f209584..0fb94e73a0 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -596,6 +596,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite sum(bom_item.{qty_field}/ifnull(bom.quantity, 1)) * %(qty)s as qty, item.description, item.image, + bom.project, item.stock_uom, item.allow_alternative_item, item_default.default_warehouse, diff --git a/erpnext/manufacturing/doctype/bom/bom_dashboard.py b/erpnext/manufacturing/doctype/bom/bom_dashboard.py new file mode 100644 index 0000000000..803ece7c78 --- /dev/null +++ b/erpnext/manufacturing/doctype/bom/bom_dashboard.py @@ -0,0 +1,27 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'bom_no', + 'non_standard_fieldnames': { + 'Item': 'default_bom', + 'Purchase Order': 'bom', + 'Purchase Receipt': 'bom', + 'Purchase Invoice': 'bom' + }, + 'transactions': [ + { + 'label': _('Stock'), + 'items': ['Item', 'Stock Entry', 'Quality Inspection'] + }, + { + 'label': _('Manufacture'), + 'items': ['BOM', 'Work Order', 'Job Card', 'Production Plan'] + }, + { + 'label': _('Purchase'), + 'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] + } + ] + } diff --git a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py index d48bccf9d4..c2aa2bd968 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py +++ b/erpnext/manufacturing/doctype/job_card/job_card_dashboard.py @@ -10,4 +10,4 @@ def get_data(): 'items': ['Material Request', 'Stock Entry'] } ] - } \ No newline at end of file + } diff --git a/erpnext/manufacturing/doctype/operation/operation_dashboard.py b/erpnext/manufacturing/doctype/operation/operation_dashboard.py new file mode 100644 index 0000000000..8deb9ec6e0 --- /dev/null +++ b/erpnext/manufacturing/doctype/operation/operation_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'operation', + 'transactions': [ + { + 'label': _('Manufacture'), + 'items': ['BOM', 'Work Order', 'Job Card', 'Timesheet'] + } + ] + } diff --git a/erpnext/manufacturing/doctype/routing/routing_dashboard.py b/erpnext/manufacturing/doctype/routing/routing_dashboard.py new file mode 100644 index 0000000000..ab309cc9d5 --- /dev/null +++ b/erpnext/manufacturing/doctype/routing/routing_dashboard.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'routing', + 'transactions': [ + { + 'items': ['BOM'] + } + ] + } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py new file mode 100644 index 0000000000..9e0d1d1739 --- /dev/null +++ b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'workstation', + 'transactions': [ + { + 'label': _('Manufacture'), + 'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet'] + } + ] + } diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f357b3d968..15e3166e38 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -505,6 +505,7 @@ erpnext.patches.v10_0.update_hub_connector_domain erpnext.patches.v10_0.set_student_party_type erpnext.patches.v10_0.update_project_in_sle erpnext.patches.v10_0.fix_reserved_qty_for_sub_contract +erpnext.patches.v10_0.repost_requested_qty_for_non_stock_uom_items erpnext.patches.v11_0.merge_land_unit_with_location erpnext.patches.v11_0.add_index_on_nestedset_doctypes erpnext.patches.v11_0.remove_modules_setup_page @@ -602,9 +603,11 @@ erpnext.patches.v11_1.set_salary_details_submittable erpnext.patches.v11_1.rename_depends_on_lwp execute:frappe.delete_doc("Report", "Inactive Items") erpnext.patches.v11_1.delete_scheduling_tool +erpnext.patches.v12_0.rename_tolerance_fields erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019 execute:frappe.delete_doc_if_exists("Page", "support-analytics") erpnext.patches.v12_0.make_item_manufacturer +erpnext.patches.v12_0.remove_patient_medical_record_page erpnext.patches.v11_1.move_customer_lead_to_dynamic_column erpnext.patches.v11_1.set_default_action_for_quality_inspection erpnext.patches.v11_1.delete_bom_browser @@ -616,3 +619,7 @@ erpnext.patches.v12_0.set_quotation_status erpnext.patches.v12_0.set_priority_for_support erpnext.patches.v12_0.delete_priority_property_setter erpnext.patches.v12_0.set_default_batch_size +execute:frappe.delete_doc("DocType", "Project Task") +erpnext.patches.v11_1.update_default_supplier_in_item_defaults +erpnext.patches.v12_0.update_due_date_in_gle +erpnext.patches.v12_0.add_default_buying_selling_terms_in_company diff --git a/erpnext/patches/v10_0/repost_requested_qty_for_non_stock_uom_items.py b/erpnext/patches/v10_0/repost_requested_qty_for_non_stock_uom_items.py new file mode 100644 index 0000000000..4fe4e97cf5 --- /dev/null +++ b/erpnext/patches/v10_0/repost_requested_qty_for_non_stock_uom_items.py @@ -0,0 +1,21 @@ +# Copyright (c) 2019, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + from erpnext.stock.stock_balance import update_bin_qty, get_indented_qty + + count=0 + for item_code, warehouse in frappe.db.sql("""select distinct item_code, warehouse + from `tabMaterial Request Item` where docstatus = 1 and stock_uom<>uom"""): + try: + count += 1 + update_bin_qty(item_code, warehouse, { + "indented_qty": get_indented_qty(item_code, warehouse), + }) + if count % 200 == 0: + frappe.db.commit() + except: + frappe.db.rollback() 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 new file mode 100644 index 0000000000..347dec1f74 --- /dev/null +++ b/erpnext/patches/v11_1/update_default_supplier_in_item_defaults.py @@ -0,0 +1,25 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + ''' + default supplier was not set in the item defaults for multi company instance, + this patch will set the default supplier + + ''' + if not frappe.db.has_column('Item', 'default_supplier'): + return + + frappe.reload_doc('stock', 'doctype', 'item_default') + frappe.reload_doc('stock', 'doctype', 'item') + + companies = frappe.get_all("Company") + if len(companies) > 1: + frappe.db.sql(""" UPDATE `tabItem Default`, `tabItem` + 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 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 new file mode 100644 index 0000000000..484f81a7ac --- /dev/null +++ b/erpnext/patches/v12_0/add_default_buying_selling_terms_in_company.py @@ -0,0 +1,19 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals + +import frappe +from frappe.model.utils.rename_field import rename_field + +def execute(): + frappe.reload_doc("setup", "doctype", "company") + if frappe.db.has_column('Company', 'default_terms'): + rename_field('Company', "default_terms", "default_selling_terms") + + 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/remove_patient_medical_record_page.py b/erpnext/patches/v12_0/remove_patient_medical_record_page.py new file mode 100644 index 0000000000..904bfe4bf1 --- /dev/null +++ b/erpnext/patches/v12_0/remove_patient_medical_record_page.py @@ -0,0 +1,7 @@ +# Copyright (c) 2019 + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.delete_doc("Page", "medical_record") diff --git a/erpnext/patches/v12_0/rename_tolerance_fields.py b/erpnext/patches/v12_0/rename_tolerance_fields.py new file mode 100644 index 0000000000..aa2fff4ca7 --- /dev/null +++ b/erpnext/patches/v12_0/rename_tolerance_fields.py @@ -0,0 +1,15 @@ +import frappe +from frappe.model.utils.rename_field import rename_field + +def execute(): + frappe.reload_doc("stock", "doctype", "item") + frappe.reload_doc("stock", "doctype", "stock_settings") + frappe.reload_doc("accounts", "doctype", "accounts_settings") + + rename_field('Stock Settings', "tolerance", "over_delivery_receipt_allowance") + rename_field('Item', "tolerance", "over_delivery_receipt_allowance") + + 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 diff --git a/erpnext/patches/v12_0/set_priority_for_support.py b/erpnext/patches/v12_0/set_priority_for_support.py index cc290396f8..5096ed4c3c 100644 --- a/erpnext/patches/v12_0/set_priority_for_support.py +++ b/erpnext/patches/v12_0/set_priority_for_support.py @@ -33,19 +33,23 @@ def set_priorities_service_level(): service_level_priorities = frappe.get_list("Service Level", fields=["name", "priority", "response_time", "response_time_period", "resolution_time", "resolution_time_period"]) frappe.reload_doc("support", "doctype", "service_level") + frappe.reload_doc("support", "doctype", "support_settings") + frappe.db.set_value('Support Settings', None, 'track_service_level_agreement', 1) for service_level in service_level_priorities: if service_level: doc = frappe.get_doc("Service Level", service_level.name) - doc.append("priorities", { - "priority": service_level.priority, - "default_priority": 1, - "response_time": service_level.response_time, - "response_time_period": service_level.response_time_period, - "resolution_time": service_level.resolution_time, - "resolution_time_period": service_level.resolution_time_period - }) - doc.save(ignore_permissions=True) + if not doc.priorities: + doc.append("priorities", { + "priority": service_level.priority, + "default_priority": 1, + "response_time": service_level.response_time, + "response_time_period": service_level.response_time_period, + "resolution_time": service_level.resolution_time, + "resolution_time_period": service_level.resolution_time_period + }) + doc.flags.ignore_validate = True + doc.save(ignore_permissions=True) except frappe.db.TableMissingError: frappe.reload_doc("support", "doctype", "service_level") @@ -73,6 +77,7 @@ def set_priorities_service_level_agreement(): "resolution_time": service_level_agreement.resolution_time, "resolution_time_period": service_level_agreement.resolution_time_period }) + 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 diff --git a/erpnext/patches/v12_0/set_task_status.py b/erpnext/patches/v12_0/set_task_status.py index 32b8177130..70f65097dc 100644 --- a/erpnext/patches/v12_0/set_task_status.py +++ b/erpnext/patches/v12_0/set_task_status.py @@ -2,10 +2,9 @@ import frappe def execute(): frappe.reload_doctype('Task') - frappe.reload_doctype('Project Task') # add "Completed" if customized - for doctype in ('Task', 'Project Task'): + for doctype in ('Task'): property_setter_name = frappe.db.exists('Property Setter', dict(doc_type = doctype, field_name = 'status', property = 'options')) if property_setter_name: property_setter = frappe.get_doc('Property Setter', property_setter_name) diff --git a/erpnext/patches/v12_0/update_due_date_in_gle.py b/erpnext/patches/v12_0/update_due_date_in_gle.py new file mode 100644 index 0000000000..34848725ce --- /dev/null +++ b/erpnext/patches/v12_0/update_due_date_in_gle.py @@ -0,0 +1,17 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("accounts", "doctype", "gl_entry") + + for doctype in ["Sales Invoice", "Purchase Invoice", "Journal Entry"]: + frappe.reload_doc("accounts", "doctype", frappe.scrub(doctype)) + + frappe.db.sql(""" UPDATE `tabGL Entry`, `tab{doctype}` + SET + `tabGL Entry`.due_date = `tab{doctype}`.due_date + WHERE + `tabGL Entry`.voucher_no = `tab{doctype}`.name and `tabGL Entry`.party is not null + and `tabGL Entry`.voucher_type in ('Sales Invoice', 'Purchase Invoice', 'Journal Entry') + and `tabGL Entry`.account in (select name from `tabAccount` where account_type in ('Receivable', 'Payable'))""" #nosec + .format(doctype=doctype)) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 528c7cd0c7..5613f088e1 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -1,23 +1,6 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Project", { - setup: function (frm) { - frm.set_indicator_formatter('title', - function (doc) { - let indicator = 'orange'; - if (doc.status == 'Overdue') { - indicator = 'red'; - } else if (doc.status == 'Cancelled') { - indicator = 'dark grey'; - } else if (doc.status == 'Completed') { - indicator = 'green'; - } - return indicator; - } - ); - }, - - onload: function (frm) { var so = frappe.meta.get_docfield("Project", "sales_order"); so.get_route_options_for_new_doc = function (field) { @@ -99,58 +82,4 @@ frappe.ui.form.on("Project", { }); }, - tasks_refresh: function (frm) { - var grid = frm.get_field('tasks').grid; - grid.wrapper.find('select[data-fieldname="status"]').each(function () { - if ($(this).val() === 'Open') { - $(this).addClass('input-indicator-open'); - } else { - $(this).removeClass('input-indicator-open'); - } - }); - }, - - status: function(frm) { - if (frm.doc.status === 'Cancelled') { - frappe.confirm(__('Set tasks in this project as cancelled?'), () => { - frm.doc.tasks = frm.doc.tasks.map(task => { - task.status = 'Cancelled'; - return task; - }); - frm.refresh_field('tasks'); - }); - } - } -}); - -frappe.ui.form.on("Project Task", { - edit_task: function(frm, doctype, name) { - var doc = frappe.get_doc(doctype, name); - if(doc.task_id) { - frappe.set_route("Form", "Task", doc.task_id); - } else { - frappe.msgprint(__("Save the document first.")); - } - }, - - edit_timesheet: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - frappe.route_options = {"project": frm.doc.project_name, "task": child.task_id}; - frappe.set_route("List", "Timesheet"); - }, - - make_timesheet: function(frm, cdt, cdn) { - var child = locals[cdt][cdn]; - frappe.model.with_doctype('Timesheet', function() { - var doc = frappe.model.get_new_doc('Timesheet'); - var row = frappe.model.add_child(doc, 'time_logs'); - row.project = frm.doc.project_name; - row.task = child.task_id; - frappe.set_route('Form', doc.doctype, doc.name); - }) - }, - - status: function(frm, doctype, name) { - frm.trigger('tasks_refresh'); - }, }); diff --git a/erpnext/projects/doctype/project/project.json b/erpnext/projects/doctype/project/project.json index 2fc507b252..b4536c085c 100644 --- a/erpnext/projects/doctype/project/project.json +++ b/erpnext/projects/doctype/project/project.json @@ -1,1974 +1,487 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "field:project_name", - "beta": 0, "creation": "2013-03-07 11:55:07", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "project_name", + "status", + "project_type", + "is_active", + "percent_complete_method", + "percent_complete", + "column_break_5", + "project_template", + "expected_start_date", + "expected_end_date", + "priority", + "department", + "customer_details", + "customer", + "column_break_14", + "sales_order", + "users_section", + "users", + "copied_from", + "section_break0", + "notes", + "section_break_18", + "actual_start_date", + "actual_time", + "column_break_20", + "actual_end_date", + "project_details", + "estimated_costing", + "total_costing_amount", + "total_expense_claim", + "total_purchase_cost", + "company", + "column_break_28", + "total_sales_amount", + "total_billable_amount", + "total_billed_amount", + "total_consumed_material_cost", + "cost_center", + "margin", + "gross_margin", + "column_break_37", + "per_gross_margin", + "monitor_progress", + "collect_progress", + "holiday_list", + "frequency", + "from_time", + "to_time", + "first_email", + "second_email", + "daily_time_to_send", + "day_to_send", + "weekly_time_to_send", + "column_break_45", + "message" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "project_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Project Name", - "length": 0, - "no_copy": 0, "oldfieldtype": "Data", - "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": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Open", "fieldname": "status", "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": 1, "label": "Status", - "length": 0, "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", "options": "Open\nCompleted\nCancelled", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "project_type", "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": "Project Type", - "length": 0, - "no_copy": 0, "oldfieldname": "project_type", "oldfieldtype": "Data", - "options": "Project Type", - "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 + "options": "Project Type" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "is_active", "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": "Is Active", - "length": 0, - "no_copy": 0, "oldfieldname": "is_active", "oldfieldtype": "Select", - "options": "Yes\nNo", - "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 + "options": "Yes\nNo" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Task Completion", "fieldname": "percent_complete_method", "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": "% Complete Method", - "length": 0, - "no_copy": 0, - "options": "Task Completion\nTask Progress\nTask Weight", - "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": "Task Completion\nTask Progress\nTask Weight" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "fieldname": "percent_complete", "fieldtype": "Percent", - "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": "% Completed", - "length": 0, "no_copy": 1, - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_5", - "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, "fieldname": "project_template", "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": "From Template", - "length": 0, - "no_copy": 0, - "options": "Project Template", - "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": "Project Template" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "expected_start_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Expected Start Date", - "length": 0, - "no_copy": 0, "oldfieldname": "project_start_date", - "oldfieldtype": "Date", - "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 + "oldfieldtype": "Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, "fieldname": "expected_end_date", "fieldtype": "Date", - "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": "Expected End Date", - "length": 0, - "no_copy": 0, "oldfieldname": "completion_date", - "oldfieldtype": "Date", - "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 + "oldfieldtype": "Date" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "priority", "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": 1, "label": "Priority", - "length": 0, - "no_copy": 0, "oldfieldname": "priority", "oldfieldtype": "Select", - "options": "Medium\nLow\nHigh", - "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 + "options": "Medium\nLow\nHigh" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "department", "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": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "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": "Department" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "customer_details", "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": "Customer Details", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-user", - "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 + "options": "fa fa-user" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "customer", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Customer", - "length": 0, - "no_copy": 0, "oldfieldname": "customer", "oldfieldtype": "Link", "options": "Customer", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_14", - "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, "fieldname": "sales_order", "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": "Sales Order", - "length": 0, - "no_copy": 0, - "options": "Sales Order", - "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 Order" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "users_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": "Users", - "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": "Users" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Project will be accessible on the website to these users", "fieldname": "users", "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": "Users", - "length": 0, - "no_copy": 0, - "options": "Project User", - "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": "Project User" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_milestones", - "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": "Tasks", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-flag", - "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": "tasks", - "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": "Tasks", - "length": 0, - "no_copy": 0, - "options": "Project Task", - "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": "copied_from", "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": "Copied From", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "section_break0", "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": "Notes", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-list", - "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 + "options": "fa fa-list" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "notes", "fieldtype": "Text Editor", - "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": "Notes", - "length": 0, - "no_copy": 0, "oldfieldname": "notes", - "oldfieldtype": "Text Editor", - "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 + "oldfieldtype": "Text Editor" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "section_break_18", "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": "Start and End Dates", - "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": "Start and End Dates" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "actual_start_date", "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": "Actual Start Date", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "actual_time", "fieldtype": "Float", - "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": "Actual Time (in Hours)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_20", - "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, "fieldname": "actual_end_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Actual End Date", - "length": 0, - "no_copy": 0, "oldfieldname": "act_completion_date", "oldfieldtype": "Date", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "project_details", "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": "Costing and Billing", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-money", - "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 + "options": "fa fa-money" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "estimated_costing", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Estimated Cost", - "length": 0, - "no_copy": 0, "oldfieldname": "project_value", "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "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 + "options": "Company:company:default_currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "total_costing_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Costing Amount (via Timesheets)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "total_expense_claim", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Expense Claim (via Expense Claims)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_purchase_cost", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Purchase Cost (via Purchase Invoice)", - "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 + "read_only": 1 }, { - "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": 0, - "in_standard_filter": 0, "label": "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": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "remember_last_selected_value": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_28", - "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, "fieldname": "total_sales_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Sales Amount (via Sales Order)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "total_billable_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Billable Amount (via Timesheets)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_billed_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Billed Amount (via Sales Invoices)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_consumed_material_cost", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Total Consumed Material Cost (via Stock Entry)", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "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 Cost Center", - "length": 0, - "no_copy": 0, - "options": "Cost Center", - "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 + "options": "Cost Center" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "margin", "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": "Margin", - "length": 0, - "no_copy": 0, "oldfieldtype": "Column Break", - "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, "width": "50%" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "gross_margin", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Gross Margin", - "length": 0, - "no_copy": 0, "oldfieldname": "gross_margin_value", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_37", - "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, "fieldname": "per_gross_margin", "fieldtype": "Percent", - "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": "Gross Margin %", - "length": 0, - "no_copy": 0, "oldfieldname": "per_gross_margin", "oldfieldtype": "Currency", - "options": "", - "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 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "monitor_progress", "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": "Monitor Progress", - "length": 0, - "no_copy": 0, - "options": "", - "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": "Monitor Progress" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "collect_progress", "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": "Collect Progress", - "length": 0, - "no_copy": 0, - "options": "", - "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": "Collect Progress" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "collect_progress", "fieldname": "holiday_list", "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": "Holiday List", - "length": 0, - "no_copy": 0, - "options": "Holiday 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 + "options": "Holiday List" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.collect_progress == true", "fieldname": "frequency", "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": "Frequency To Collect Progress", - "length": 0, - "no_copy": 0, - "options": "Hourly\nTwice Daily\nDaily\nWeekly", - "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": "Hourly\nTwice Daily\nDaily\nWeekly" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Hourly\" && doc.collect_progress)", "fieldname": "from_time", "fieldtype": "Time", - "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": "From Time", - "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": "From Time" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Hourly\" && doc.collect_progress)", "fieldname": "to_time", "fieldtype": "Time", - "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": "To Time", - "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": "To Time" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Twice Daily\" && doc.collect_progress == true)\n\n", "fieldname": "first_email", "fieldtype": "Time", - "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": "First Email", - "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": "First Email" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Twice Daily\" && doc.collect_progress == true)", "fieldname": "second_email", "fieldtype": "Time", - "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": "Second Email", - "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": "Second Email" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Daily\" && doc.collect_progress == true)", "fieldname": "daily_time_to_send", "fieldtype": "Time", - "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": "Time to send", - "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": "Time to send" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)", "fieldname": "day_to_send", "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": "Day to Send", - "length": 0, - "no_copy": 0, - "options": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday", - "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": "Monday\nTuesday\nWednesday\nThursday\nFriday\nSaturday\nSunday" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:(doc.frequency == \"Weekly\" && doc.collect_progress == true)", "fieldname": "weekly_time_to_send", "fieldtype": "Time", - "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": "Time to send", - "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": "Time to send" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_45", - "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": "collect_progress", "description": "Message will sent to users to get their status on the project", "fieldname": "message", "fieldtype": "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": "Message", - "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": "Message" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-puzzle-piece", "idx": 29, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, "max_attachments": 4, - "modified": "2019-02-18 17:56:04.789560", + "modified": "2019-06-25 16:14:43.887151", "modified_by": "Administrator", "module": "Projects", "name": "Project", "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": "Projects User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "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": 1, - "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "All" }, { - "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": "Projects Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "search_fields": "customer, status, priority, is_active", "show_name_in_global_search": 1, "sort_order": "DESC", "timeline_field": "customer", - "track_changes": 0, - "track_seen": 1, - "track_views": 0 + "track_seen": 1 } \ No newline at end of file diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index 55a689259a..6176cf89b4 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -19,10 +19,6 @@ class Project(Document): return '{0}: {1}'.format(_(self.status), frappe.safe_decode(self.project_name)) def onload(self): - """Load project tasks for quick view""" - if not self.get('__unsaved') and not self.get("tasks"): - self.load_tasks() - self.set_onload('activity_summary', frappe.db.sql('''select activity_type, sum(hours) as total_hours from `tabTimesheet Detail` where project=%s and docstatus < 2 group by activity_type @@ -33,57 +29,19 @@ class Project(Document): def before_print(self): self.onload() - def load_tasks(self): - """Load `tasks` from the database""" - if frappe.flags.in_import: - return - project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname") - - self.tasks = [] - for task in self.get_tasks(): - task_map = { - "title": task.subject, - "status": task.status, - "start_date": task.exp_start_date, - "end_date": task.exp_end_date, - "description": task.description, - "task_id": task.name, - "task_weight": task.task_weight - } - - self.map_custom_fields(task, task_map, project_task_custom_fields) - - self.append("tasks", task_map) - - def get_tasks(self): - if self.name is None: - return {} - else: - filters = {"project": self.name} - - if self.get("deleted_task_list"): - filters.update({ - 'name': ("not in", self.deleted_task_list) - }) - - return frappe.get_all("Task", "*", filters, order_by="exp_start_date asc, status asc") def validate(self): - self.validate_weights() - self.sync_tasks() - self.tasks = [] - self.load_tasks() if not self.is_new(): self.copy_from_template() - self.validate_dates() self.send_welcome_email() - self.update_percent_complete(from_validate=True) + self.update_costing() + self.update_percent_complete() def copy_from_template(self): ''' Copy tasks from template ''' - if self.project_template and not len(self.tasks or []): + if self.project_template and not frappe.db.get_all('Task', dict(project = self.name), limit=1): # has a template, and no loaded tasks, so lets create if not self.expected_start_date: @@ -108,104 +66,6 @@ class Project(Document): task_weight = task.task_weight )).insert() - # reload tasks after project - self.load_tasks() - - def validate_dates(self): - if self.tasks: - for d in self.tasks: - if self.expected_start_date: - if d.start_date and getdate(d.start_date) < getdate(self.expected_start_date): - frappe.throw(_("Start date of task {0} cannot be less than {1} expected start date {2}") - .format(d.title, self.name, self.expected_start_date)) - if d.end_date and getdate(d.end_date) < getdate(self.expected_start_date): - frappe.throw(_("End date of task {0} cannot be less than {1} expected start date {2}") - .format(d.title, self.name, self.expected_start_date)) - - if self.expected_end_date: - if d.start_date and getdate(d.start_date) > getdate(self.expected_end_date): - frappe.throw(_("Start date of task {0} cannot be greater than {1} expected end date {2}") - .format(d.title, self.name, self.expected_end_date)) - if d.end_date and getdate(d.end_date) > getdate(self.expected_end_date): - frappe.throw(_("End date of task {0} cannot be greater than {1} expected end date {2}") - .format(d.title, self.name, self.expected_end_date)) - - if self.expected_start_date and self.expected_end_date: - if getdate(self.expected_end_date) < getdate(self.expected_start_date): - frappe.throw(_("Expected End Date can not be less than Expected Start Date")) - - def validate_weights(self): - for task in self.tasks: - if task.task_weight is not None: - if task.task_weight < 0: - frappe.throw(_("Task weight cannot be negative")) - - def sync_tasks(self): - """sync tasks and remove table""" - if not hasattr(self, "deleted_task_list"): - self.set("deleted_task_list", []) - - if self.flags.dont_sync_tasks: return - task_names = [] - - existing_task_data = {} - - fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"] - exclude_fieldtype = ["Button", "Column Break", - "Section Break", "Table", "Read Only", "Attach", "Attach Image", "Color", "Geolocation", "HTML", "Image"] - - custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task", - "fieldtype": ("not in", exclude_fieldtype)}, "fieldname") - - for d in custom_fields: - fields.append(d.fieldname) - - for d in frappe.get_all('Project Task', - fields = fields, - filters = {'parent': self.name}): - existing_task_data.setdefault(d.task_id, d) - - for t in self.tasks: - if t.task_id: - task = frappe.get_doc("Task", t.task_id) - else: - task = frappe.new_doc("Task") - task.project = self.name - - if not t.task_id or self.is_row_updated(t, existing_task_data, fields): - task.update({ - "subject": t.title, - "status": t.status, - "exp_start_date": t.start_date, - "exp_end_date": t.end_date, - "description": t.description, - "task_weight": t.task_weight - }) - - self.map_custom_fields(t, task, custom_fields) - - task.flags.ignore_links = True - task.flags.from_project = True - task.flags.ignore_feed = True - - if t.task_id: - task.update({ - "modified_by": frappe.session.user, - "modified": now() - }) - - task.run_method("validate") - task.db_update() - else: - task.save(ignore_permissions = True) - task_names.append(task.name) - else: - task_names.append(task.name) - - # delete - for t in frappe.get_all("Task", ["name"], {"project": self.name, "name": ("not in", task_names)}): - self.deleted_task_list.append(t.name) - def is_row_updated(self, row, existing_task_data, fields): if self.get("__islocal") or not existing_task_data: return True @@ -215,48 +75,43 @@ class Project(Document): if row.get(field) != d.get(field): return True - def map_custom_fields(self, source, target, custom_fields): - for field in custom_fields: - target.update({ - field.fieldname: source.get(field.fieldname) - }) - def update_project(self): + '''Called externally by Task''' self.update_percent_complete() self.update_costing() + self.db_update() def after_insert(self): self.copy_from_template() if self.sales_order: frappe.db.set_value("Sales Order", self.sales_order, "project", self.name) - def update_percent_complete(self, from_validate=False): - if not self.tasks: return - total = frappe.db.sql("""select count(name) from tabTask where project=%s""", self.name)[0][0] + def update_percent_complete(self): + total = frappe.db.count('Task', dict(project=self.name)) - if not total and self.percent_complete: + if not total: self.percent_complete = 0 + else: + if (self.percent_complete_method == "Task Completion" and total > 0) or ( + not self.percent_complete_method and total > 0): + completed = frappe.db.sql("""select count(name) from tabTask where + project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0] + self.percent_complete = flt(flt(completed) / total * 100, 2) - if (self.percent_complete_method == "Task Completion" and total > 0) or ( - not self.percent_complete_method and total > 0): - completed = frappe.db.sql("""select count(name) from tabTask where - project=%s and status in ('Cancelled', 'Completed')""", self.name)[0][0] - self.percent_complete = flt(flt(completed) / total * 100, 2) + if (self.percent_complete_method == "Task Progress" and total > 0): + progress = frappe.db.sql("""select sum(progress) from tabTask where + project=%s""", self.name)[0][0] + self.percent_complete = flt(flt(progress) / total, 2) - if (self.percent_complete_method == "Task Progress" and total > 0): - progress = frappe.db.sql("""select sum(progress) from tabTask where - project=%s""", self.name)[0][0] - self.percent_complete = flt(flt(progress) / total, 2) - - if (self.percent_complete_method == "Task Weight" and total > 0): - weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where - project=%s""", self.name)[0][0] - weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where - project=%s""", self.name, as_dict=1) - pct_complete = 0 - for row in weighted_progress: - pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum) - self.percent_complete = flt(flt(pct_complete), 2) + if (self.percent_complete_method == "Task Weight" and total > 0): + weight_sum = frappe.db.sql("""select sum(task_weight) from tabTask where + project=%s""", self.name)[0][0] + weighted_progress = frappe.db.sql("""select progress, task_weight from tabTask where + project=%s""", self.name, as_dict=1) + pct_complete = 0 + for row in weighted_progress: + pct_complete += row["progress"] * frappe.utils.safe_div(row["task_weight"], weight_sum) + self.percent_complete = flt(flt(pct_complete), 2) # don't update status if it is cancelled if self.status == 'Cancelled': @@ -268,9 +123,6 @@ class Project(Document): else: self.status = "Open" - if not from_validate: - self.db_update() - def update_costing(self): from_time_sheet = frappe.db.sql("""select sum(costing_amount) as costing_amount, @@ -297,7 +149,6 @@ class Project(Document): self.update_sales_amount() self.update_billed_amount() self.calculate_gross_margin() - self.db_update() def calculate_gross_margin(self): expense_amount = (flt(self.total_costing_amount) + flt(self.total_expense_claim) @@ -348,57 +199,6 @@ class Project(Document): content=content.format(*messages)) user.welcome_email_sent = 1 - def on_update(self): - self.delete_task() - self.load_tasks() - self.update_project() - self.update_dependencies_on_duplicated_project() - - def delete_task(self): - if not self.get('deleted_task_list'): return - - for d in self.get('deleted_task_list'): - # unlink project - frappe.db.set_value('Task', d, 'project', '') - - self.deleted_task_list = [] - - def update_dependencies_on_duplicated_project(self): - if self.flags.dont_sync_tasks: return - if not self.copied_from: - self.copied_from = self.name - - if self.name != self.copied_from and self.get('__unsaved'): - # duplicated project - dependency_map = {} - for task in self.tasks: - _task = frappe.db.get_value( - 'Task', - {"subject": task.title, "project": self.copied_from}, - ['name', 'depends_on_tasks'], - as_dict=True - ) - - if _task is None: - continue - - name = _task.name - - dependency_map[task.title] = [x['subject'] for x in frappe.get_list( - 'Task Depends On', {"parent": name}, ['subject'])] - - for key, value in iteritems(dependency_map): - task_name = frappe.db.get_value('Task', {"subject": key, "project": self.name }) - - task_doc = frappe.get_doc('Task', task_name) - - for dt in value: - dt_name = frappe.db.get_value('Task', {"subject": dt, "project": self.name}) - task_doc.append('depends_on', {"task": dt_name}) - - task_doc.db_update() - - def get_timeline_data(doctype, name): '''Return timeline for attendance''' return dict(frappe.db.sql('''select unix_timestamp(from_time), count(*) diff --git a/erpnext/projects/doctype/project/test_project.py b/erpnext/projects/doctype/project/test_project.py index beb1f130f5..06c62b62d2 100644 --- a/erpnext/projects/doctype/project/test_project.py +++ b/erpnext/projects/doctype/project/test_project.py @@ -19,18 +19,18 @@ class TestProject(unittest.TestCase): project = get_project('Test Project with Template') - project.load_tasks() + tasks = frappe.get_all('Task', '*', dict(project=project.name), order_by='creation asc') - task1 = project.tasks[0] - self.assertEqual(task1.title, 'Task 1') + task1 = tasks[0] + self.assertEqual(task1.subject, 'Task 1') self.assertEqual(task1.description, 'Task 1 description') - self.assertEqual(getdate(task1.start_date), getdate('2019-01-01')) - self.assertEqual(getdate(task1.end_date), getdate('2019-01-04')) + self.assertEqual(getdate(task1.exp_start_date), getdate('2019-01-01')) + self.assertEqual(getdate(task1.exp_end_date), getdate('2019-01-04')) - self.assertEqual(len(project.tasks), 4) - task4 = project.tasks[3] - self.assertEqual(task4.title, 'Task 4') - self.assertEqual(getdate(task4.end_date), getdate('2019-01-06')) + self.assertEqual(len(tasks), 4) + task4 = tasks[3] + self.assertEqual(task4.subject, 'Task 4') + self.assertEqual(getdate(task4.exp_end_date), getdate('2019-01-06')) def get_project(name): template = get_project_template() diff --git a/erpnext/projects/doctype/project/test_records.json b/erpnext/projects/doctype/project/test_records.json index 9379c22b5c..567f359b50 100644 --- a/erpnext/projects/doctype/project/test_records.json +++ b/erpnext/projects/doctype/project/test_records.json @@ -1,12 +1,6 @@ [ { "project_name": "_Test Project", - "status": "Open", - "tasks":[ - { - "title": "_Test Task", - "status": "Open" - } - ] + "status": "Open" } ] \ No newline at end of file diff --git a/erpnext/projects/doctype/project_task/project_task.json b/erpnext/projects/doctype/project_task/project_task.json deleted file mode 100644 index e26c191e0b..0000000000 --- a/erpnext/projects/doctype/project_task/project_task.json +++ /dev/null @@ -1,430 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2015-02-22 11:15:28.201059", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "fieldname": "title", - "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": "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_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 3, - "default": "Open", - "fieldname": "status", - "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": "Status", - "length": 0, - "no_copy": 1, - "options": "Open\nWorking\nPending Review\nOverdue\nCompleted\nCancelled", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "task_id", - "fieldname": "edit_task", - "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": "View Task", - "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": "edit_timesheet", - "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": "View Timesheet", - "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": "make_timesheet", - "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": "Make Timesheet", - "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_6", - "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": 2, - "fieldname": "start_date", - "fieldtype": "Date", - "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": "Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 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": 2, - "default": "", - "fieldname": "end_date", - "fieldtype": "Date", - "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": "End Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 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": "task_weight", - "fieldtype": "Float", - "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": "Weight", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text Editor", - "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": "Description", - "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": "task_id", - "fieldtype": "Link", - "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": "Task ID", - "length": 0, - "no_copy": 1, - "options": "Task", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "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": "2019-02-19 12:30:52.648868", - "modified_by": "Administrator", - "module": "Projects", - "name": "Project Task", - "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, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/projects/doctype/project_task/project_task.py b/erpnext/projects/doctype/project_task/project_task.py deleted file mode 100644 index 5f9d8d7622..0000000000 --- a/erpnext/projects/doctype/project_task/project_task.py +++ /dev/null @@ -1,9 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document - -class ProjectTask(Document): - pass diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index d8fc199ec2..50557f1551 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -158,12 +158,6 @@ class Task(NestedSet): if check_if_child_exists(self.name): throw(_("Child Task exists for this Task. You can not delete this Task.")) - if self.project: - tasks = frappe.get_doc('Project', self.project).tasks - for task in tasks: - if task.get('task_id') == self.name: - frappe.delete_doc('Project Task', task.name) - self.update_nsm_model() def update_status(self): diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 91dfe809a4..89657a1837 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -109,7 +109,7 @@ class CallPopup { }); wrapper.append(`
- ${frappe.avatar(null, 'avatar-xl', contact.name, contact.image)} + ${frappe.avatar(null, 'avatar-xl', contact.name, contact.image || '')}
${contact_name}
${contact.mobile_no || ''}
diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js index ec71df3351..095e744926 100644 --- a/erpnext/public/js/conf.js +++ b/erpnext/public/js/conf.js @@ -52,3 +52,13 @@ $.extend(frappe.breadcrumbs.preferred, { "Sales Partner": "Selling", "Brand": "Selling" }); + +$.extend(frappe.breadcrumbs.module_map, { + 'ERPNext Integrations': 'Integrations', + 'Geo': 'Settings', + 'Accounts': 'Accounting', + 'Portal': 'Website', + 'Utilities': 'Settings', + 'Shopping Cart': 'Website', + 'Contacts': 'CRM' +}); diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 97c823d053..750900e3be 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -61,6 +61,14 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ }); } + if(this.frm.fields_dict.tc_name) { + this.frm.set_query("tc_name", function() { + return{ + filters: { 'buying': 1 } + } + }); + } + me.frm.set_query('supplier', erpnext.queries.supplier); me.frm.set_query('contact_person', erpnext.queries.contact_query); me.frm.set_query('supplier_address', erpnext.queries.address_query); @@ -133,6 +141,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ price_list_rate: function(doc, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); + frappe.model.round_floats_in(item, ["price_list_rate", "discount_percentage"]); let item_rate = item.price_list_rate; @@ -146,6 +155,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ if (item.discount_amount) { item.rate = flt((item.price_list_rate) - (item.discount_amount), precision('rate', item)); + } else { + item.rate = item_rate; } this.calculate_taxes_and_totals(); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index a3ecea168d..844db996a2 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -585,8 +585,17 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ me.frm.set_value("letter_head", company_doc.default_letter_head); } } - if (company_doc.default_terms && me.frm.doc.doctype != "Purchase Invoice" && frappe.meta.has_field(me.frm.doc.doctype, "tc_name")) { - me.frm.set_value("tc_name", company_doc.default_terms); + let selling_doctypes_for_tc = ["Sales Invoice", "Quotation", "Sales Order", "Delivery Note"]; + if (company_doc.default_selling_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && + selling_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) { + me.frm.set_value("tc_name", company_doc.default_selling_terms); + } + let buying_doctypes_for_tc = ["Request for Quotation", "Supplier Quotation", "Purchase Order", + "Material Request", "Purchase Receipt"]; + // Purchase Invoice is excluded as per issue #3345 + if (company_doc.default_buying_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && + buying_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) { + me.frm.set_value("tc_name", company_doc.default_buying_terms); } frappe.run_serially([ @@ -1415,11 +1424,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, callback: function(r) { if(!r.exc) { - me.frm.set_value("taxes", r.message); + if(me.frm.doc.shipping_rule && me.frm.doc.taxes) { + for (let tax of r.message) { + me.frm.add_child("taxes", tax); + } - if(me.frm.doc.shipping_rule) { - me.frm.script_manager.trigger("shipping_rule"); + refresh_field("taxes"); } else { + me.frm.set_value("taxes", r.message); me.calculate_taxes_and_totals(); } } diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index d1113a4ca4..89cb13d981 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -129,9 +129,7 @@ function get_filters(){ } ] - let dimension_filters = erpnext.get_dimension_filters(); - - dimension_filters.then((dimensions) => { + erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { filters.push({ "fieldname": dimension["fieldname"], diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index 560a5617da..84d2113c06 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -65,7 +65,7 @@ $.extend(erpnext.queries, { frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, frappe.dynamic_link.fieldname, doc.name))])); } - + console.log(frappe.dynamic_link) return { query: 'frappe.contacts.doctype.address.address.address_query', filters: { diff --git a/erpnext/public/js/sms_manager.js b/erpnext/public/js/sms_manager.js index 6ce8bb1150..a058da23ac 100644 --- a/erpnext/public/js/sms_manager.js +++ b/erpnext/public/js/sms_manager.js @@ -20,8 +20,10 @@ erpnext.SMSManager = function SMSManager(doc) { 'Purchase Receipt' : 'Items has been received against purchase receipt: ' + doc.name } - if (in_list(['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype)) + if (in_list(['Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype)) this.show(doc.contact_person, 'Customer', doc.customer, '', default_msg[doc.doctype]); + else if (doc.doctype === 'Quotation') + this.show(doc.contact_person, 'Customer', doc.party_name, '', default_msg[doc.doctype]); else if (in_list(['Purchase Order', 'Purchase Receipt'], doc.doctype)) this.show(doc.contact_person, 'Supplier', doc.supplier, '', default_msg[doc.doctype]); else if (doc.doctype == 'Lead') diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index cf48be4128..0cd648ea11 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -573,7 +573,6 @@ erpnext.utils.map_current_doc = function(opts) { if(!r.exc) { var doc = frappe.model.sync(r.message); cur_frm.dirty(); - erpnext.utils.clear_duplicates(); cur_frm.refresh(); } } @@ -604,28 +603,6 @@ erpnext.utils.map_current_doc = function(opts) { } } -erpnext.utils.clear_duplicates = function() { - if(!cur_frm.doc.items) return; - const unique_items = new Map(); - /* - Create a Map of items with - item_code => [qty, warehouse, batch_no] - */ - let items = []; - - for (let item of cur_frm.doc.items) { - if (!(unique_items.has(item.item_code) && unique_items.get(item.item_code)[0] === item.qty && - unique_items.get(item.item_code)[1] === item.warehouse && unique_items.get(item.item_code)[2] === item.batch_no && - unique_items.get(item.item_code)[3] === item.delivery_date && unique_items.get(item.item_code)[4] === item.required_date && - unique_items.get(item.item_code)[5] === item.rate)) { - - unique_items.set(item.item_code, [item.qty, item.warehouse, item.batch_no, item.delivery_date, item.required_date, item.rate]); - items.push(item); - } - } - cur_frm.doc.items = items; -} - frappe.form.link_formatters['Item'] = function(value, doc) { if(doc && doc.item_name && doc.item_name !== value) { return value? value + ': ' + doc.item_name: doc.item_name; diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index fef450795b..549f95e039 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -7,12 +7,12 @@ erpnext.doctypes_with_dimensions = ["GL Entry", "Sales Invoice", "Purchase Invoi "Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation", "Travel Request", "Fees", "POS Profile"]; -let dimension_filters = erpnext.get_dimension_filters(); +erpnext.dimension_filters = erpnext.get_dimension_filters(); erpnext.doctypes_with_dimensions.forEach((doctype) => { frappe.ui.form.on(doctype, { onload: function(frm) { - dimension_filters.then((dimensions) => { + erpnext.dimension_filters.then((dimensions) => { dimensions.forEach((dimension) => { frappe.model.with_doctype(dimension['document_type'], () => { if (frappe.meta.has_field(dimension['document_type'], 'is_group')) { diff --git a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json index d0b368f293..5bd8920a32 100644 --- a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json +++ b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json @@ -28,7 +28,7 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Rating", - "options": "1\n2\n3\n4\n5", + "options": "\n1\n2\n3\n4\n5", "reqd": 1 }, { @@ -44,7 +44,7 @@ } ], "istable": 1, - "modified": "2019-05-26 21:50:48.951264", + "modified": "2019-07-13 19:58:08.966141", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Feedback Parameter", diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json index 6985632e49..0849fd7aeb 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json @@ -26,7 +26,7 @@ "fieldname": "status", "fieldtype": "Select", "label": "Status", - "options": "Open\nClose" + "options": "Open\nClosed" }, { "fieldname": "minutes", @@ -55,7 +55,7 @@ "label": "Minutes" } ], - "modified": "2019-05-26 23:12:23.364357", + "modified": "2019-07-13 19:57:40.500541", "modified_by": "Administrator", "module": "Quality Management", "name": "Quality Meeting", diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index eff578001f..e8c170e721 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -74,7 +74,6 @@ class Gstr1Report(object): for inv, items_based_on_rate in self.items_based_on_tax_rate.items(): invoice_details = self.invoices.get(inv) - for rate, items in items_based_on_rate.items(): place_of_supply = invoice_details.get("place_of_supply") ecommerce_gstin = invoice_details.get("ecommerce_gstin") @@ -85,7 +84,7 @@ class Gstr1Report(object): "rate": "", "taxable_value": 0, "cess_amount": 0, - "type": 0 + "type": "" }) row = b2cs_output.get((rate, place_of_supply, ecommerce_gstin)) @@ -94,6 +93,7 @@ class Gstr1Report(object): row["rate"] = rate row["taxable_value"] += sum([abs(net_amount) for item_code, net_amount in self.invoice_items.get(inv).items() if item_code in items]) + row["cess_amount"] += flt(self.invoice_cess.get(inv), 2) row["type"] = "E" if ecommerce_gstin else "OE" for key, value in iteritems(b2cs_output): @@ -123,6 +123,10 @@ class Gstr1Report(object): row += [tax_rate or 0, taxable_value] + for column in self.other_columns: + if column.get('fieldname') == 'cess_amount': + row.append(flt(self.invoice_cess.get(invoice), 2)) + return row, taxable_value def get_invoice_data(self): @@ -327,7 +331,7 @@ class Gstr1Report(object): "fieldtype": "Data" }, { - "fieldname": "invoice_type", + "fieldname": "gst_category", "label": "Invoice Type", "fieldtype": "Data" }, @@ -564,12 +568,18 @@ def get_json(): out = get_b2b_json(res, gstin) gst_json["b2b"] = out + elif filters["type_of_business"] == "B2C Large": for item in report_data[:-1]: res.setdefault(item["place_of_supply"], []).append(item) out = get_b2cl_json(res, gstin) gst_json["b2cl"] = out + + elif filters["type_of_business"] == "B2C Small": + out = get_b2cs_json(report_data[:-1], gstin) + gst_json["b2cs"] = out + elif filters["type_of_business"] == "EXPORT": for item in report_data[:-1]: res.setdefault(item["export_type"], []).append(item) @@ -605,6 +615,45 @@ def get_b2b_json(res, gstin): return out +def get_b2cs_json(data, gstin): + + company_state_number = gstin[0:2] + + out = [] + for d in data: + + pos = d.get('place_of_supply').split('-')[0] + tax_details = {} + + rate = d.get('rate', 0) + tax = flt((d["taxable_value"]*rate)/100.0, 2) + + if company_state_number == pos: + tax_details.update({"camt": flt(tax/2.0, 2), "samt": flt(tax/2.0, 2)}) + else: + tax_details.update({"iamt": tax}) + + inv = { + "sply_ty": "INTRA" if company_state_number == pos else "INTER", + "pos": pos, + "typ": d.get('type'), + "txval": flt(d.get('taxable_value'), 2), + "rt": rate, + "iamt": flt(tax_details.get('iamt'), 2), + "camt": flt(tax_details.get('camt'), 2), + "samt": flt(tax_details.get('samt'), 2), + "csamt": flt(d.get('cess_amount'), 2) + } + + if d.get('type') == "E" and d.get('ecommerce_gstin'): + inv.update({ + "etin": d.get('ecommerce_gstin') + }) + + out.append(inv) + + return out + def get_b2cl_json(res, gstin): out = [] for pos in res: diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py index 87ca6a76bc..8e790bf9ce 100644 --- a/erpnext/selling/doctype/customer/customer_dashboard.py +++ b/erpnext/selling/doctype/customer/customer_dashboard.py @@ -25,10 +25,6 @@ def get_data(): 'label': _('Orders'), 'items': ['Sales Order', 'Delivery Note', 'Sales Invoice'] }, - { - 'label': _('Service Level Agreement'), - 'items': ['Service Level Agreement'] - }, { 'label': _('Payments'), 'items': ['Payment Entry'] diff --git a/erpnext/selling/doctype/installation_note/installation_note.js b/erpnext/selling/doctype/installation_note/installation_note.js index a8d9ae8a4e..7fd0877d11 100644 --- a/erpnext/selling/doctype/installation_note/installation_note.js +++ b/erpnext/selling/doctype/installation_note/installation_note.js @@ -3,7 +3,7 @@ frappe.ui.form.on('Installation Note', { setup: function(frm) { - frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'} + frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'} frm.set_query('customer_address', erpnext.queries.address_query); frm.set_query('contact_person', erpnext.queries.contact_query); frm.set_query('customer', erpnext.queries.customer); diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 26ca7c6e22..39dda92e3e 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -107,7 +107,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( refresh: function(doc, dt, dn) { var me = this; this._super(); - var allow_delivery = false; + let allow_delivery = false; if(doc.docstatus==1) { if(this.frm.has_perm("submit")) { @@ -132,6 +132,8 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( if(doc.status !== 'Closed') { if(doc.status !== 'On Hold') { + allow_delivery = this.frm.doc.items.some(item => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty)) + if (this.frm.has_perm("submit")) { if(flt(doc.per_delivered, 6) < 100 || flt(doc.per_billed) < 100) { // hold diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 8ad3bf0607..e9b310eb30 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -492,13 +492,27 @@ def close_or_unclose_sales_orders(names, status): frappe.local.message_log = [] +def get_requested_item_qty(sales_order): + return frappe._dict(frappe.db.sql(""" + select sales_order_item, sum(stock_qty) + from `tabMaterial Request Item` + where docstatus = 1 + and sales_order = %s + group by sales_order_item + """, sales_order)) + @frappe.whitelist() def make_material_request(source_name, target_doc=None): + requested_item_qty = get_requested_item_qty(source_name) + def postprocess(source, doc): doc.material_request_type = "Purchase" def update_item(source, target, source_parent): target.project = source_parent.project + target.qty = source.stock_qty - requested_item_qty.get(source.name, 0) + target.conversion_factor = 1 + target.stock_qty = source.stock_qty - requested_item_qty.get(source.name, 0) doc = get_mapped_doc("Sales Order", source_name, { "Sales Order": { @@ -523,7 +537,7 @@ def make_material_request(source_name, target_doc=None): "stock_uom": "uom", "stock_qty": "qty" }, - "condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code), + "condition": lambda doc: not frappe.db.exists('Product Bundle', doc.item_code) and doc.stock_qty > requested_item_qty.get(doc.name, 0), "postprocess": update_item } }, target_doc, postprocess) @@ -547,12 +561,6 @@ def make_project(source_name, target_doc=None): "base_grand_total" : "estimated_costing", } }, - "Sales Order Item": { - "doctype": "Project Task", - "field_map": { - "item_code": "title", - }, - } }, target_doc, postprocess) return doc diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js index 15c9eb550f..301ecde638 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_list.js +++ b/erpnext/selling/doctype/sales_order/sales_order_list.js @@ -34,7 +34,7 @@ frappe.listview_settings['Sales Order'] = { "per_delivered,<,100|per_billed,=,100|status,!=,Closed"]; } - } else if ((flt(doc.per_delivered, 6) == 100) + } else if ((flt(doc.per_delivered, 6) === 100) && flt(doc.grand_total) !== 0 && flt(doc.per_billed, 6) < 100 && doc.status !== "Closed") { // to bill @@ -48,7 +48,7 @@ frappe.listview_settings['Sales Order'] = { if(flt(doc.per_billed, 6) < 100 ){ return [__("To Deliver and Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"]; - }else if(flt(doc.per_billed, 6) == 100){ + }else if(flt(doc.per_billed, 6) === 100){ return [__("To Deliver"), "orange", "per_delivered,=,100|per_billed,=,100|status,!=,Closed"]; } } diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 569c53f628..bd07841488 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -192,8 +192,8 @@ class TestSalesOrder(unittest.TestCase): def test_reserved_qty_for_over_delivery(self): make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100) - # set over-delivery tolerance - frappe.db.set_value('Item', "_Test Item", 'tolerance', 50) + # set over-delivery allowance + frappe.db.set_value('Item', "_Test Item", 'over_delivery_receipt_allowance', 50) existing_reserved_qty = get_reserved_qty() @@ -209,8 +209,9 @@ class TestSalesOrder(unittest.TestCase): def test_reserved_qty_for_over_delivery_via_sales_invoice(self): make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100) - # set over-delivery tolerance - frappe.db.set_value('Item', "_Test Item", 'tolerance', 50) + # set over-delivery allowance + frappe.db.set_value('Item', "_Test Item", 'over_delivery_receipt_allowance', 50) + frappe.db.set_value('Item', "_Test Item", 'over_billing_allowance', 20) existing_reserved_qty = get_reserved_qty() @@ -291,8 +292,8 @@ class TestSalesOrder(unittest.TestCase): make_stock_entry(target="_Test Warehouse - _TC", qty=10, rate=100) make_stock_entry(item="_Test Item Home Desktop 100", target="_Test Warehouse - _TC", qty=10, rate=100) - # set over-delivery tolerance - frappe.db.set_value('Item', "_Test Product Bundle Item", 'tolerance', 50) + # set over-delivery allowance + frappe.db.set_value('Item', "_Test Product Bundle Item", 'over_delivery_receipt_allowance', 50) existing_reserved_qty_item1 = get_reserved_qty("_Test Item") existing_reserved_qty_item2 = get_reserved_qty("_Test Item Home Desktop 100") diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js index fbe045bf35..149c923d5c 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.js +++ b/erpnext/selling/report/sales_analytics/sales_analytics.js @@ -8,7 +8,7 @@ frappe.query_reports["Sales Analytics"] = { fieldname: "tree_type", label: __("Tree Type"), fieldtype: "Select", - options: ["Customer Group","Customer","Item Group","Item","Territory"], + options: ["Customer Group","Customer","Item Group","Item","Territory","Order Type"], default: "Customer", reqd: 1 }, diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index 3239fc626f..8a5e50a61e 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -23,15 +23,15 @@ class Analytics(object): self.get_columns() self.get_data() self.get_chart_data() - return self.columns, self.data , None, self.chart + return self.columns, self.data, None, self.chart def get_columns(self): - self.columns =[{ + self.columns = [{ "label": _(self.filters.tree_type + " ID"), - "options": self.filters.tree_type, + "options": self.filters.tree_type if self.filters.tree_type != "Order Type" else "", "fieldname": "entity", - "fieldtype": "Link", - "width": 140 + "fieldtype": "Link" if self.filters.tree_type != "Order Type" else "Data", + "width": 140 if self.filters.tree_type != "Order Type" else 200 }] if self.filters.tree_type in ["Customer", "Supplier", "Item"]: self.columns.append({ @@ -73,6 +73,28 @@ class Analytics(object): self.get_sales_transactions_based_on_item_group() self.get_rows_by_group() + elif self.filters.tree_type == "Order Type": + if self.filters.doc_type != "Sales Order": + self.data = [] + return + self.get_sales_transactions_based_on_order_type() + self.get_rows_by_group() + + def get_sales_transactions_based_on_order_type(self): + if self.filters["value_quantity"] == 'Value': + value_field = "base_net_total" + else: + value_field = "total_qty" + + self.entries = frappe.db.sql(""" select s.order_type as entity, s.{value_field} as value_field, s.{date_field} + from `tab{doctype}` s where s.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s + and ifnull(s.order_type, '') != '' order by s.order_type + """ + .format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), + (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1) + + self.get_teams() + def get_sales_transactions_based_on_customers_or_suppliers(self): if self.filters["value_quantity"] == 'Value': value_field = "base_net_total as value_field" @@ -88,7 +110,7 @@ class Analytics(object): self.entries = frappe.get_all(self.filters.doc_type, fields=[entity, entity_name, value_field, self.date_field], - filters = { + filters={ "docstatus": 1, "company": self.filters.company, self.date_field: ('between', [self.filters.from_date, self.filters.to_date]) @@ -112,7 +134,7 @@ class Analytics(object): where s.name = i.parent and i.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s """ - .format(date_field=self.date_field, value_field = value_field, doctype=self.filters.doc_type), + .format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1) self.entity_names = {} @@ -135,7 +157,7 @@ class Analytics(object): self.entries = frappe.get_all(self.filters.doc_type, fields=[entity_field, value_field, self.date_field], - filters = { + filters={ "docstatus": 1, "company": self.filters.company, self.date_field: ('between', [self.filters.from_date, self.filters.to_date]) @@ -154,13 +176,13 @@ class Analytics(object): from `tab{doctype} Item` i , `tab{doctype}` s where s.name = i.parent and i.docstatus = 1 and s.company = %s and s.{date_field} between %s and %s - """.format(date_field=self.date_field, value_field = value_field, doctype=self.filters.doc_type), + """.format(date_field=self.date_field, value_field=value_field, doctype=self.filters.doc_type), (self.filters.company, self.filters.from_date, self.filters.to_date), as_dict=1) self.get_groups() def get_rows(self): - self.data=[] + self.data = [] self.get_periodic_data() for entity, period_data in iteritems(self.entity_periodic_data): @@ -192,7 +214,7 @@ class Analytics(object): period = self.get_period(end_date) amount = flt(self.entity_periodic_data.get(d.name, {}).get(period, 0.0)) row[scrub(period)] = amount - if d.parent: + if d.parent and (self.filters.tree_type != "Order Type" or d.parent == "Order Types"): self.entity_periodic_data.setdefault(d.parent, frappe._dict()).setdefault(period, 0.0) self.entity_periodic_data[d.parent][period] += amount total += amount @@ -216,7 +238,7 @@ class Analytics(object): elif self.filters.range == 'Monthly': period = str(self.months[posting_date.month - 1]) + " " + str(posting_date.year) elif self.filters.range == 'Quarterly': - period = "Quarter " + str(((posting_date.month-1)//3)+1) +" " + str(posting_date.year) + period = "Quarter " + str(((posting_date.month - 1) // 3) + 1) + " " + str(posting_date.year) else: year = get_fiscal_year(posting_date, company=self.filters.company) period = str(year[0]) @@ -234,7 +256,7 @@ class Analytics(object): }.get(self.filters.range, 1) if self.filters.range in ['Monthly', 'Quarterly']: - from_date = from_date.replace(day = 1) + from_date = from_date.replace(day=1) elif self.filters.range == "Yearly": from_date = get_fiscal_year(from_date)[1] else: @@ -270,7 +292,22 @@ class Analytics(object): self.group_entries = frappe.db.sql("""select name, lft, rgt , {parent} as parent from `tab{tree}` order by lft""" - .format(tree=self.filters.tree_type, parent=parent), as_dict=1) + .format(tree=self.filters.tree_type, parent=parent), as_dict=1) + + for d in self.group_entries: + if d.parent: + self.depth_map.setdefault(d.name, self.depth_map.get(d.parent) + 1) + else: + self.depth_map.setdefault(d.name, 0) + + def get_teams(self): + self.depth_map = frappe._dict() + + self.group_entries = frappe.db.sql(""" select * from (select "Order Types" as name, 0 as lft, + 2 as rgt, '' as parent union select distinct order_type as name, 1 as lft, 1 as rgt, "Order Types" as parent + from `tab{doctype}` where ifnull(order_type, '') != '') as b order by lft, name + """ + .format(doctype=self.filters.doc_type), as_dict=1) for d in self.group_entries: if d.parent: @@ -285,13 +322,13 @@ class Analytics(object): length = len(self.columns) if self.filters.tree_type in ["Customer", "Supplier", "Item"]: - labels = [d.get("label") for d in self.columns[2:length-1]] + labels = [d.get("label") for d in self.columns[2:length - 1]] else: - labels = [d.get("label") for d in self.columns[1:length-1]] + labels = [d.get("label") for d in self.columns[1:length - 1]] self.chart = { "data": { 'labels': labels, - 'datasets':[] + 'datasets': [] }, "type": "line" - } \ No newline at end of file + } diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 9bae58b309..a2bae56ee0 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -59,6 +59,12 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }); } + if(this.frm.fields_dict.tc_name) { + this.frm.set_query("tc_name", function() { + return { filters: { selling: 1 } }; + }); + } + if(!this.frm.fields_dict["items"]) { return; } @@ -145,6 +151,11 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({ }, discount_amount: function(doc, cdt, cdn) { + + if(doc.name === cdn) { + return; + } + var item = frappe.get_doc(cdt, cdn); item.discount_percentage = 0.0; this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount'); diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 1e6056ec86..313de677fc 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -17,6 +17,14 @@ frappe.ui.form.on("Company", { filters: {"is_group": 1} } }); + + frm.set_query("default_selling_terms", function() { + return { filters: { selling: 1 } }; + }); + + frm.set_query("default_buying_terms", function() { + return { filters: { buying: 1 } }; + }); }, company_name: function(frm) { diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index bb652ca6bf..bc3418997d 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -1,769 +1,776 @@ { - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:company_name", - "creation": "2013-04-10 08:35:39", - "description": "Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "details", - "company_name", - "abbr", - "change_abbr", - "is_group", - "cb0", - "domain", - "parent_company", - "charts_section", - "default_currency", - "default_letter_head", - "default_holiday_list", - "default_finance_book", - "standard_working_hours", - "default_terms", - "default_warehouse_for_sales_return", - "column_break_10", - "country", - "create_chart_of_accounts_based_on", - "chart_of_accounts", - "existing_company", - "tax_id", - "date_of_establishment", - "sales_settings", - "monthly_sales_target", - "sales_monthly_history", - "column_break_goals", - "transactions_annual_history", - "total_monthly_sales", - "default_settings", - "default_bank_account", - "default_cash_account", - "default_receivable_account", - "round_off_account", - "round_off_cost_center", - "write_off_account", - "discount_allowed_account", - "discount_received_account", - "exchange_gain_loss_account", - "unrealized_exchange_gain_loss_account", - "column_break0", - "allow_account_creation_against_child_company", - "default_payable_account", - "default_employee_advance_account", - "default_expense_account", - "default_income_account", - "default_deferred_revenue_account", - "default_deferred_expense_account", - "default_payroll_payable_account", - "default_expense_claim_payable_account", - "section_break_22", - "cost_center", - "column_break_26", - "credit_limit", - "payment_terms", - "auto_accounting_for_stock_settings", - "enable_perpetual_inventory", - "default_inventory_account", - "stock_adjustment_account", - "column_break_32", - "stock_received_but_not_billed", - "expenses_included_in_valuation", - "fixed_asset_depreciation_settings", - "accumulated_depreciation_account", - "depreciation_expense_account", - "series_for_depreciation_entry", - "expenses_included_in_asset_valuation", - "column_break_40", - "disposal_account", - "depreciation_cost_center", - "capital_work_in_progress_account", - "asset_received_but_not_billed", - "budget_detail", - "exception_budget_approver_role", - "company_info", - "company_logo", - "date_of_incorporation", - "address_html", - "date_of_commencement", - "phone_no", - "fax", - "email", - "website", - "column_break1", - "company_description", - "registration_info", - "registration_details", - "delete_company_transactions", - "lft", - "rgt", - "old_parent" - ], - "fields": [ - { - "fieldname": "details", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break" - }, - { - "fieldname": "company_name", - "fieldtype": "Data", - "label": "Company", - "oldfieldname": "company_name", - "oldfieldtype": "Data", - "reqd": 1, - "unique": 1 - }, - { - "fieldname": "abbr", - "fieldtype": "Data", - "label": "Abbr", - "oldfieldname": "abbr", - "oldfieldtype": "Data", - "reqd": 1 - }, - { - "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", - "fieldname": "change_abbr", - "fieldtype": "Button", - "label": "Change Abbreviation" - }, - { - "bold": 1, - "default": "0", - "fieldname": "is_group", - "fieldtype": "Check", - "label": "Is Group" - }, - { - "fieldname": "default_finance_book", - "fieldtype": "Link", - "label": "Default Finance Book", - "options": "Finance Book" - }, - { - "fieldname": "cb0", - "fieldtype": "Column Break" - }, - { - "fieldname": "domain", - "fieldtype": "Link", - "label": "Domain", - "options": "Domain" - }, - { - "fieldname": "parent_company", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Parent Company", - "options": "Company" - }, - { - "fieldname": "company_logo", - "fieldtype": "Attach Image", - "hidden": 1, - "label": "Company Logo" - }, - { - "fieldname": "company_description", - "fieldtype": "Text Editor", - "label": "Company Description" - }, - { - "collapsible": 1, - "fieldname": "sales_settings", - "fieldtype": "Section Break", - "label": "Sales Settings" - }, - { - "fieldname": "sales_monthly_history", - "fieldtype": "Small Text", - "hidden": 1, - "label": "Sales Monthly History", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "transactions_annual_history", - "fieldtype": "Code", - "hidden": 1, - "label": "Transactions Annual History", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "monthly_sales_target", - "fieldtype": "Currency", - "label": "Monthly Sales Target", - "options": "default_currency" - }, - { - "fieldname": "column_break_goals", - "fieldtype": "Column Break" - }, - { - "fieldname": "total_monthly_sales", - "fieldtype": "Currency", - "label": "Total Monthly Sales", - "no_copy": 1, - "options": "default_currency", - "read_only": 1 - }, - { - "fieldname": "charts_section", - "fieldtype": "Section Break", - "label": "Default Values" - }, - { - "fieldname": "default_currency", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Currency", - "options": "Currency", - "reqd": 1 - }, - { - "fieldname": "default_letter_head", - "fieldtype": "Link", - "label": "Default Letter Head", - "options": "Letter Head" - }, - { - "fieldname": "default_holiday_list", - "fieldtype": "Link", - "label": "Default Holiday List", - "options": "Holiday List" - }, - { - "fieldname": "standard_working_hours", - "fieldtype": "Float", - "label": "Standard Working Hours" - }, - { - "fieldname": "default_terms", - "fieldtype": "Link", - "label": "Default Terms", - "options": "Terms and Conditions" - }, - { - "fieldname": "default_warehouse_for_sales_return", - "fieldtype": "Link", - "label": "Default warehouse for Sales Return", - "options": "Warehouse" - }, - { - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "fieldname": "country", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Country", - "options": "Country", - "reqd": 1 - }, - { - "fieldname": "create_chart_of_accounts_based_on", - "fieldtype": "Select", - "label": "Create Chart Of Accounts Based On", - "options": "\nStandard Template\nExisting Company" - }, - { - "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"", - "fieldname": "chart_of_accounts", - "fieldtype": "Select", - "label": "Chart Of Accounts Template", - "no_copy": 1 - }, - { - "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", - "fieldname": "existing_company", - "fieldtype": "Link", - "label": "Existing Company ", - "no_copy": 1, - "options": "Company" - }, - { - "fieldname": "tax_id", - "fieldtype": "Data", - "label": "Tax ID" - }, - { - "fieldname": "date_of_establishment", - "fieldtype": "Date", - "label": "Date of Establishment" - }, - { - "fieldname": "default_settings", - "fieldtype": "Section Break", - "label": "Accounts Settings", - "oldfieldtype": "Section Break" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_bank_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Bank Account", - "no_copy": 1, - "oldfieldname": "default_bank_account", - "oldfieldtype": "Link", - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_cash_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Cash Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_receivable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Receivable Account", - "no_copy": 1, - "oldfieldname": "receivables_group", - "oldfieldtype": "Link", - "options": "Account" - }, - { - "fieldname": "round_off_account", - "fieldtype": "Link", - "label": "Round Off Account", - "options": "Account" - }, - { - "fieldname": "round_off_cost_center", - "fieldtype": "Link", - "label": "Round Off Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "write_off_account", - "fieldtype": "Link", - "label": "Write Off Account", - "options": "Account" - }, - { - "fieldname": "discount_allowed_account", - "fieldtype": "Link", - "label": "Discount Allowed Account", - "options": "Account" - }, - { - "fieldname": "discount_received_account", - "fieldtype": "Link", - "label": "Discount Received Account", - "options": "Account" - }, - { - "fieldname": "exchange_gain_loss_account", - "fieldtype": "Link", - "label": "Exchange Gain / Loss Account", - "options": "Account" - }, - { - "fieldname": "unrealized_exchange_gain_loss_account", - "fieldtype": "Link", - "label": "Unrealized Exchange Gain/Loss Account", - "options": "Account" - }, - { - "fieldname": "column_break0", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "default": "0", - "depends_on": "eval:doc.parent_company", - "fieldname": "allow_account_creation_against_child_company", - "fieldtype": "Check", - "label": "Allow Account Creation Against Child Company" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_payable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Payable Account", - "no_copy": 1, - "oldfieldname": "payables_group", - "oldfieldtype": "Link", - "options": "Account" - }, - { - "fieldname": "default_employee_advance_account", - "fieldtype": "Link", - "label": "Default Employee Advance Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_expense_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Cost of Goods Sold Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_income_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Income Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_deferred_revenue_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Deferred Revenue Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_deferred_expense_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Deferred Expense Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_payroll_payable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Payroll Payable Account", - "no_copy": 1, - "options": "Account" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "default_expense_claim_payable_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Expense Claim Payable Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "section_break_22", - "fieldtype": "Section Break" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "cost_center", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Default Cost Center", - "no_copy": 1, - "options": "Cost Center" - }, - { - "fieldname": "column_break_26", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "credit_limit", - "fieldtype": "Currency", - "label": "Credit Limit", - "oldfieldname": "credit_limit", - "oldfieldtype": "Currency", - "options": "default_currency" - }, - { - "fieldname": "payment_terms", - "fieldtype": "Link", - "label": "Default Payment Terms Template", - "options": "Payment Terms Template" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "auto_accounting_for_stock_settings", - "fieldtype": "Section Break", - "label": "Stock Settings" - }, - { - "default": "1", - "fieldname": "enable_perpetual_inventory", - "fieldtype": "Check", - "label": "Enable Perpetual Inventory" - }, - { - "fieldname": "default_inventory_account", - "fieldtype": "Link", - "label": "Default Inventory Account", - "options": "Account" - }, - { - "fieldname": "stock_adjustment_account", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Stock Adjustment Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "column_break_32", - "fieldtype": "Column Break" - }, - { - "fieldname": "stock_received_but_not_billed", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Stock Received But Not Billed", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "expenses_included_in_valuation", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Expenses Included In Valuation", - "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", - "label": "Accumulated Depreciation Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "depreciation_expense_account", - "fieldtype": "Link", - "label": "Depreciation Expense Account", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "series_for_depreciation_entry", - "fieldtype": "Data", - "label": "Series for Asset Depreciation Entry (Journal Entry)" - }, - { - "fieldname": "expenses_included_in_asset_valuation", - "fieldtype": "Link", - "label": "Expenses Included In Asset Valuation", - "options": "Account" - }, - { - "fieldname": "column_break_40", - "fieldtype": "Column Break" - }, - { - "fieldname": "disposal_account", - "fieldtype": "Link", - "label": "Gain/Loss Account on Asset Disposal", - "no_copy": 1, - "options": "Account" - }, - { - "fieldname": "depreciation_cost_center", - "fieldtype": "Link", - "label": "Asset Depreciation Cost Center", - "no_copy": 1, - "options": "Cost Center" - }, - { - "fieldname": "capital_work_in_progress_account", - "fieldtype": "Link", - "label": "Capital Work In Progress Account", - "options": "Account" - }, - { - "fieldname": "asset_received_but_not_billed", - "fieldtype": "Link", - "label": "Asset Received But Not Billed", - "options": "Account" - }, - { - "collapsible": 1, - "fieldname": "budget_detail", - "fieldtype": "Section Break", - "label": "Budget Detail" - }, - { - "fieldname": "exception_budget_approver_role", - "fieldtype": "Link", - "label": "Exception Budget Approver Role", - "options": "Role" - }, - { - "collapsible": 1, - "description": "For reference only.", - "fieldname": "company_info", - "fieldtype": "Section Break", - "label": "Company Info" - }, - { - "fieldname": "date_of_incorporation", - "fieldtype": "Date", - "label": "Date of Incorporation" - }, - { - "fieldname": "address_html", - "fieldtype": "HTML" - }, - { - "fieldname": "column_break1", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "width": "50%" - }, - { - "depends_on": "eval:doc.date_of_incorporation", - "fieldname": "date_of_commencement", - "fieldtype": "Date", - "label": "Date of Commencement" - }, - { - "fieldname": "phone_no", - "fieldtype": "Data", - "label": "Phone No", - "oldfieldname": "phone_no", - "oldfieldtype": "Data", - "options": "Phone" - }, - { - "fieldname": "fax", - "fieldtype": "Data", - "label": "Fax", - "oldfieldname": "fax", - "oldfieldtype": "Data", - "options": "Phone" - }, - { - "fieldname": "email", - "fieldtype": "Data", - "label": "Email", - "oldfieldname": "email", - "oldfieldtype": "Data", - "options": "Email" - }, - { - "fieldname": "website", - "fieldtype": "Data", - "label": "Website", - "oldfieldname": "website", - "oldfieldtype": "Data" - }, - { - "fieldname": "registration_info", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break", - "width": "50%" - }, - { - "description": "Company registration numbers for your reference. Tax numbers etc.", - "fieldname": "registration_details", - "fieldtype": "Code", - "label": "Registration Details", - "oldfieldname": "registration_details", - "oldfieldtype": "Code" - }, - { - "fieldname": "delete_company_transactions", - "fieldtype": "Button", - "label": "Delete Company Transactions" - }, - { - "fieldname": "lft", - "fieldtype": "Int", - "hidden": 1, - "label": "Lft", - "print_hide": 1, - "read_only": 1, - "search_index": 1 - }, - { - "fieldname": "rgt", - "fieldtype": "Int", - "hidden": 1, - "label": "Rgt", - "print_hide": 1, - "read_only": 1, - "search_index": 1 - }, - { - "fieldname": "old_parent", - "fieldtype": "Data", - "hidden": 1, - "label": "old_parent", - "print_hide": 1, - "read_only": 1 - } - ], - "icon": "fa fa-building", - "idx": 1, - "image_field": "company_logo", - "modified": "2019-06-14 14:36:11.363309", - "modified_by": "Administrator", - "module": "Setup", - "name": "Company", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "share": 1, - "write": 1 - }, - { - "email": 1, - "print": 1, - "read": 1, - "role": "Accounts User" - }, - { - "read": 1, - "role": "Employee" - }, - { - "read": 1, - "role": "Sales User" - }, - { - "read": 1, - "role": "Purchase User" - }, - { - "read": 1, - "role": "Stock User" - }, - { - "read": 1, - "role": "Projects User" - } - ], - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1 - } \ No newline at end of file + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:company_name", + "creation": "2013-04-10 08:35:39", + "description": "Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Organization.", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "details", + "company_name", + "abbr", + "change_abbr", + "is_group", + "cb0", + "domain", + "parent_company", + "charts_section", + "default_currency", + "default_letter_head", + "default_holiday_list", + "default_finance_book", + "standard_working_hours", + "default_selling_terms", + "default_buying_terms", + "default_warehouse_for_sales_return", + "column_break_10", + "country", + "create_chart_of_accounts_based_on", + "chart_of_accounts", + "existing_company", + "tax_id", + "date_of_establishment", + "sales_settings", + "monthly_sales_target", + "sales_monthly_history", + "column_break_goals", + "transactions_annual_history", + "total_monthly_sales", + "default_settings", + "default_bank_account", + "default_cash_account", + "default_receivable_account", + "round_off_account", + "round_off_cost_center", + "write_off_account", + "discount_allowed_account", + "discount_received_account", + "exchange_gain_loss_account", + "unrealized_exchange_gain_loss_account", + "column_break0", + "allow_account_creation_against_child_company", + "default_payable_account", + "default_employee_advance_account", + "default_expense_account", + "default_income_account", + "default_deferred_revenue_account", + "default_deferred_expense_account", + "default_payroll_payable_account", + "default_expense_claim_payable_account", + "section_break_22", + "cost_center", + "column_break_26", + "credit_limit", + "payment_terms", + "auto_accounting_for_stock_settings", + "enable_perpetual_inventory", + "default_inventory_account", + "stock_adjustment_account", + "column_break_32", + "stock_received_but_not_billed", + "expenses_included_in_valuation", + "fixed_asset_depreciation_settings", + "accumulated_depreciation_account", + "depreciation_expense_account", + "series_for_depreciation_entry", + "expenses_included_in_asset_valuation", + "column_break_40", + "disposal_account", + "depreciation_cost_center", + "capital_work_in_progress_account", + "asset_received_but_not_billed", + "budget_detail", + "exception_budget_approver_role", + "company_info", + "company_logo", + "date_of_incorporation", + "address_html", + "date_of_commencement", + "phone_no", + "fax", + "email", + "website", + "column_break1", + "company_description", + "registration_info", + "registration_details", + "delete_company_transactions", + "lft", + "rgt", + "old_parent" + ], + "fields": [ + { + "fieldname": "details", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "company_name", + "fieldtype": "Data", + "label": "Company", + "oldfieldname": "company_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, + { + "fieldname": "abbr", + "fieldtype": "Data", + "label": "Abbr", + "oldfieldname": "abbr", + "oldfieldtype": "Data", + "reqd": 1 + }, + { + "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", + "fieldname": "change_abbr", + "fieldtype": "Button", + "label": "Change Abbreviation" + }, + { + "bold": 1, + "default": "0", + "fieldname": "is_group", + "fieldtype": "Check", + "label": "Is Group" + }, + { + "fieldname": "default_finance_book", + "fieldtype": "Link", + "label": "Default Finance Book", + "options": "Finance Book" + }, + { + "fieldname": "cb0", + "fieldtype": "Column Break" + }, + { + "fieldname": "domain", + "fieldtype": "Link", + "label": "Domain", + "options": "Domain" + }, + { + "fieldname": "parent_company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Parent Company", + "options": "Company" + }, + { + "fieldname": "company_logo", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Company Logo" + }, + { + "fieldname": "company_description", + "fieldtype": "Text Editor", + "label": "Company Description" + }, + { + "collapsible": 1, + "fieldname": "sales_settings", + "fieldtype": "Section Break", + "label": "Sales Settings" + }, + { + "fieldname": "sales_monthly_history", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Sales Monthly History", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "transactions_annual_history", + "fieldtype": "Code", + "hidden": 1, + "label": "Transactions Annual History", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "monthly_sales_target", + "fieldtype": "Currency", + "label": "Monthly Sales Target", + "options": "default_currency" + }, + { + "fieldname": "column_break_goals", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_monthly_sales", + "fieldtype": "Currency", + "label": "Total Monthly Sales", + "no_copy": 1, + "options": "default_currency", + "read_only": 1 + }, + { + "fieldname": "charts_section", + "fieldtype": "Section Break", + "label": "Default Values" + }, + { + "fieldname": "default_currency", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Currency", + "options": "Currency", + "reqd": 1 + }, + { + "fieldname": "default_letter_head", + "fieldtype": "Link", + "label": "Default Letter Head", + "options": "Letter Head" + }, + { + "fieldname": "default_holiday_list", + "fieldtype": "Link", + "label": "Default Holiday List", + "options": "Holiday List" + }, + { + "fieldname": "standard_working_hours", + "fieldtype": "Float", + "label": "Standard Working Hours" + }, + { + "fieldname": "default_warehouse_for_sales_return", + "fieldtype": "Link", + "label": "Default warehouse for Sales Return", + "options": "Warehouse" + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "country", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Country", + "options": "Country", + "reqd": 1 + }, + { + "fieldname": "create_chart_of_accounts_based_on", + "fieldtype": "Select", + "label": "Create Chart Of Accounts Based On", + "options": "\nStandard Template\nExisting Company" + }, + { + "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Standard Template\"", + "fieldname": "chart_of_accounts", + "fieldtype": "Select", + "label": "Chart Of Accounts Template", + "no_copy": 1 + }, + { + "depends_on": "eval:doc.create_chart_of_accounts_based_on===\"Existing Company\"", + "fieldname": "existing_company", + "fieldtype": "Link", + "label": "Existing Company ", + "no_copy": 1, + "options": "Company" + }, + { + "fieldname": "tax_id", + "fieldtype": "Data", + "label": "Tax ID" + }, + { + "fieldname": "date_of_establishment", + "fieldtype": "Date", + "label": "Date of Establishment" + }, + { + "fieldname": "default_settings", + "fieldtype": "Section Break", + "label": "Accounts Settings", + "oldfieldtype": "Section Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_bank_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Bank Account", + "no_copy": 1, + "oldfieldname": "default_bank_account", + "oldfieldtype": "Link", + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_cash_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Cash Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_receivable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Receivable Account", + "no_copy": 1, + "oldfieldname": "receivables_group", + "oldfieldtype": "Link", + "options": "Account" + }, + { + "fieldname": "round_off_account", + "fieldtype": "Link", + "label": "Round Off Account", + "options": "Account" + }, + { + "fieldname": "round_off_cost_center", + "fieldtype": "Link", + "label": "Round Off Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "write_off_account", + "fieldtype": "Link", + "label": "Write Off Account", + "options": "Account" + }, + { + "fieldname": "discount_allowed_account", + "fieldtype": "Link", + "label": "Discount Allowed Account", + "options": "Account" + }, + { + "fieldname": "discount_received_account", + "fieldtype": "Link", + "label": "Discount Received Account", + "options": "Account" + }, + { + "fieldname": "exchange_gain_loss_account", + "fieldtype": "Link", + "label": "Exchange Gain / Loss Account", + "options": "Account" + }, + { + "fieldname": "unrealized_exchange_gain_loss_account", + "fieldtype": "Link", + "label": "Unrealized Exchange Gain/Loss Account", + "options": "Account" + }, + { + "fieldname": "column_break0", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "default": "0", + "depends_on": "eval:doc.parent_company", + "fieldname": "allow_account_creation_against_child_company", + "fieldtype": "Check", + "label": "Allow Account Creation Against Child Company" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Payable Account", + "no_copy": 1, + "oldfieldname": "payables_group", + "oldfieldtype": "Link", + "options": "Account" + }, + { + "fieldname": "default_employee_advance_account", + "fieldtype": "Link", + "label": "Default Employee Advance Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_expense_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Cost of Goods Sold Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_income_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Income Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_deferred_revenue_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Deferred Revenue Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_deferred_expense_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Deferred Expense Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_payroll_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Payroll Payable Account", + "no_copy": 1, + "options": "Account" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "default_expense_claim_payable_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Expense Claim Payable Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "section_break_22", + "fieldtype": "Section Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "cost_center", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Cost Center", + "no_copy": 1, + "options": "Cost Center" + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "credit_limit", + "fieldtype": "Currency", + "label": "Credit Limit", + "oldfieldname": "credit_limit", + "oldfieldtype": "Currency", + "options": "default_currency" + }, + { + "fieldname": "payment_terms", + "fieldtype": "Link", + "label": "Default Payment Terms Template", + "options": "Payment Terms Template" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "auto_accounting_for_stock_settings", + "fieldtype": "Section Break", + "label": "Stock Settings" + }, + { + "default": "1", + "fieldname": "enable_perpetual_inventory", + "fieldtype": "Check", + "label": "Enable Perpetual Inventory" + }, + { + "fieldname": "default_inventory_account", + "fieldtype": "Link", + "label": "Default Inventory Account", + "options": "Account" + }, + { + "fieldname": "stock_adjustment_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Stock Adjustment Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "column_break_32", + "fieldtype": "Column Break" + }, + { + "fieldname": "stock_received_but_not_billed", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Stock Received But Not Billed", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "expenses_included_in_valuation", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Expenses Included In Valuation", + "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", + "label": "Accumulated Depreciation Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "depreciation_expense_account", + "fieldtype": "Link", + "label": "Depreciation Expense Account", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "series_for_depreciation_entry", + "fieldtype": "Data", + "label": "Series for Asset Depreciation Entry (Journal Entry)" + }, + { + "fieldname": "expenses_included_in_asset_valuation", + "fieldtype": "Link", + "label": "Expenses Included In Asset Valuation", + "options": "Account" + }, + { + "fieldname": "column_break_40", + "fieldtype": "Column Break" + }, + { + "fieldname": "disposal_account", + "fieldtype": "Link", + "label": "Gain/Loss Account on Asset Disposal", + "no_copy": 1, + "options": "Account" + }, + { + "fieldname": "depreciation_cost_center", + "fieldtype": "Link", + "label": "Asset Depreciation Cost Center", + "no_copy": 1, + "options": "Cost Center" + }, + { + "fieldname": "capital_work_in_progress_account", + "fieldtype": "Link", + "label": "Capital Work In Progress Account", + "options": "Account" + }, + { + "fieldname": "asset_received_but_not_billed", + "fieldtype": "Link", + "label": "Asset Received But Not Billed", + "options": "Account" + }, + { + "collapsible": 1, + "fieldname": "budget_detail", + "fieldtype": "Section Break", + "label": "Budget Detail" + }, + { + "fieldname": "exception_budget_approver_role", + "fieldtype": "Link", + "label": "Exception Budget Approver Role", + "options": "Role" + }, + { + "collapsible": 1, + "description": "For reference only.", + "fieldname": "company_info", + "fieldtype": "Section Break", + "label": "Company Info" + }, + { + "fieldname": "date_of_incorporation", + "fieldtype": "Date", + "label": "Date of Incorporation" + }, + { + "fieldname": "address_html", + "fieldtype": "HTML" + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "depends_on": "eval:doc.date_of_incorporation", + "fieldname": "date_of_commencement", + "fieldtype": "Date", + "label": "Date of Commencement" + }, + { + "fieldname": "phone_no", + "fieldtype": "Data", + "label": "Phone No", + "oldfieldname": "phone_no", + "oldfieldtype": "Data", + "options": "Phone" + }, + { + "fieldname": "fax", + "fieldtype": "Data", + "label": "Fax", + "oldfieldname": "fax", + "oldfieldtype": "Data", + "options": "Phone" + }, + { + "fieldname": "email", + "fieldtype": "Data", + "label": "Email", + "oldfieldname": "email", + "oldfieldtype": "Data", + "options": "Email" + }, + { + "fieldname": "website", + "fieldtype": "Data", + "label": "Website", + "oldfieldname": "website", + "oldfieldtype": "Data" + }, + { + "fieldname": "registration_info", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "width": "50%" + }, + { + "description": "Company registration numbers for your reference. Tax numbers etc.", + "fieldname": "registration_details", + "fieldtype": "Code", + "label": "Registration Details", + "oldfieldname": "registration_details", + "oldfieldtype": "Code" + }, + { + "fieldname": "delete_company_transactions", + "fieldtype": "Button", + "label": "Delete Company Transactions" + }, + { + "fieldname": "lft", + "fieldtype": "Int", + "hidden": 1, + "label": "Lft", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "rgt", + "fieldtype": "Int", + "hidden": 1, + "label": "Rgt", + "print_hide": 1, + "read_only": 1, + "search_index": 1 + }, + { + "fieldname": "old_parent", + "fieldtype": "Data", + "hidden": 1, + "label": "old_parent", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "default_selling_terms", + "fieldtype": "Link", + "label": "Default Selling Terms", + "options": "Terms and Conditions" + }, + { + "fieldname": "default_buying_terms", + "fieldtype": "Link", + "label": "Default Buying Terms", + "options": "Terms and Conditions" + } + ], + "icon": "fa fa-building", + "idx": 1, + "image_field": "company_logo", + "modified": "2019-07-04 22:20:45.104307", + "modified_by": "Administrator", + "module": "Setup", + "name": "Company", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts User" + }, + { + "read": 1, + "role": "Employee" + }, + { + "read": 1, + "role": "Sales User" + }, + { + "read": 1, + "role": "Purchase User" + }, + { + "read": 1, + "role": "Stock User" + }, + { + "read": 1, + "role": "Projects User" + } + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json index c1d459f265..aba6a791a4 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json @@ -1,288 +1,142 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "field:title", - "beta": 0, "creation": "2013-01-10 16:34:24", - "custom": 0, "description": "Standard Terms and Conditions that can be added to Sales and Purchases.\n\nExamples:\n\n1. Validity of the offer.\n1. Payment Terms (In Advance, On Credit, part advance etc).\n1. What is extra (or payable by the Customer).\n1. Safety / usage warning.\n1. Warranty if any.\n1. Returns Policy.\n1. Terms of shipping, if applicable.\n1. Ways of addressing disputes, indemnity, liability, etc.\n1. Address and Contact of your Company.", - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "title", + "disabled", + "applicable_modules_section", + "selling", + "buying", + "hr", + "section_break_7", + "terms", + "terms_and_conditions_help" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "title", "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": "Title", - "length": 0, "no_copy": 1, "oldfieldname": "title", "oldfieldtype": "Data", - "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": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "disabled", "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": "Disabled", - "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": "Disabled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "allow_in_quick_entry": 1, "fieldname": "terms", "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Terms and Conditions", - "length": 0, - "no_copy": 0, "oldfieldname": "terms", - "oldfieldtype": "Text Editor", - "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 + "oldfieldtype": "Text Editor" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "terms_and_conditions_help", "fieldtype": "HTML", - "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": "Terms and Conditions Help", - "length": 0, - "no_copy": 0, - "options": "

Standard Terms and Conditions Example

\n\n
Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.

", - "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": "

Standard Terms and Conditions Example

\n\n
Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n
\n\n

How to get fieldnames

\n\n

The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)

\n\n

Templating

\n\n

Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.

" + }, + { + "fieldname": "applicable_modules_section", + "fieldtype": "Section Break", + "label": "Applicable Modules" + }, + { + "default": "1", + "fieldname": "selling", + "fieldtype": "Check", + "label": "Selling" + }, + { + "default": "1", + "fieldname": "buying", + "fieldtype": "Check", + "label": "Buying" + }, + { + "default": "1", + "fieldname": "hr", + "fieldtype": "Check", + "label": "HR" + }, + { + "fieldname": "section_break_7", + "fieldtype": "Section Break" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "icon-legal", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-29 06:36:33.131473", + "modified": "2019-07-04 13:31:30.393425", "modified_by": "Administrator", "module": "Setup", "name": "Terms and Conditions", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Sales Master Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, - "report": 0, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales User" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, - "report": 0, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Purchase User" }, { - "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, "write": 1 }, { - "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": "Accounts User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, "read": 1, - "report": 0, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Stock User" } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "show_name_in_global_search": 1, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_field": "modified", + "sort_order": "ASC" } \ 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 a2152a6eb5..372cc6d3e3 100644 --- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py +++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.py @@ -3,9 +3,11 @@ from __future__ import unicode_literals import frappe +from frappe import _, throw import json from frappe.model.document import Document from frappe.utils.jinja import validate_template +from frappe.utils import cint from six import string_types @@ -13,6 +15,8 @@ class TermsandConditions(Document): def validate(self): if self.terms: validate_template(self.terms) + if not cint(self.buying) and not cint(self.selling) and not cint(self.hr) and not cint(self.disabled): + throw(_("At least one of the Applicable Modules should be selected")) @frappe.whitelist() def get_terms_and_conditions(template_name, doc): diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py index 95bd9ba636..62bde420a8 100644 --- a/erpnext/shopping_cart/cart.py +++ b/erpnext/shopping_cart/cart.py @@ -61,7 +61,7 @@ def place_order(): quotation.flags.ignore_permissions = True quotation.submit() - if quotation.lead: + if quotation.quotation_to == 'Lead' and quotation.party_name: # company used to create customer accounts frappe.defaults.set_user_default("company", quotation.company) @@ -251,11 +251,13 @@ def _get_cart_quotation(party=None): if quotation: qdoc = frappe.get_doc("Quotation", quotation[0].name) else: + [company, price_list] = frappe.db.get_value("Shopping Cart Settings", None, ["company", "price_list"]) qdoc = frappe.get_doc({ "doctype": "Quotation", "naming_series": get_shopping_cart_settings().quotation_series or "QTN-CART-", "quotation_to": party.doctype, - "company": frappe.db.get_value("Shopping Cart Settings", None, "company"), + "company": company, + "selling_price_list": price_list, "order_type": "Shopping Cart", "status": "Draft", "docstatus": 0, diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py index a5bd93fc2c..4ca43a89b8 100644 --- a/erpnext/startup/boot.py +++ b/erpnext/startup/boot.py @@ -33,7 +33,7 @@ def boot_session(bootinfo): FROM `tabCompany` LIMIT 1""") and 'Yes' or 'No' - bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_terms, + bootinfo.docs += frappe.db.sql("""select name, default_currency, cost_center, default_selling_terms, default_buying_terms, default_letter_head, default_bank_account, enable_perpetual_inventory, country from `tabCompany`""", as_dict=1, update={"doctype":":Company"}) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 6bcdc2b6d1..569a03a8f1 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -77,8 +77,34 @@ frappe.ui.form.on("Delivery Note", { }, + print_without_amount: function(frm) { erpnext.stock.delivery_note.set_print_hide(frm.doc); + }, + + refresh: function(frm) { + if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) { + frm.add_custom_button(__('Credit Note'), function() { + frappe.confirm(__("Are you sure you want to make credit note?"), + function() { + frm.trigger("make_credit_note"); + } + ); + }, __('Create')); + + frm.page.set_inner_btn_group_as_primary(__('Create')); + } + }, + + make_credit_note: function(frm) { + frm.call({ + method: "make_return_invoice", + doc: frm.doc, + freeze: true, + callback: function() { + frm.reload_doc(); + } + }); } }); diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 1e522b834e..2de9b975da 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -51,7 +51,7 @@ class DeliveryNote(SellingController): 'source_field': 'qty', 'percent_join_field': 'against_sales_invoice', 'overflow_type': 'delivery', - 'no_tolerance': 1 + 'no_allowance': 1 }] if cint(self.is_return): self.status_updater.append({ @@ -333,7 +333,10 @@ class DeliveryNote(SellingController): return_invoice.is_return = True return_invoice.save() return_invoice.submit() - frappe.msgprint(_("Credit Note {0} has been created automatically").format(return_invoice.name)) + + credit_note_link = frappe.utils.get_link_to_form('Sales Invoice', return_invoice.name) + + frappe.msgprint(_("Credit Note {0} has been created automatically").format(credit_note_link)) except: frappe.throw(_("Could not create Credit Note automatically, please uncheck 'Issue Credit Note' and submit again")) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 164ffa443b..a142bb9035 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -29,7 +29,8 @@ "is_fixed_asset", "asset_category", "asset_naming_series", - "tolerance", + "over_delivery_receipt_allowance", + "over_billing_allowance", "image", "section_break_11", "brand", @@ -284,14 +285,6 @@ "fieldtype": "Select", "label": "Asset Naming Series" }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "tolerance", - "fieldtype": "Float", - "label": "Allow over delivery or receipt upto this percent", - "oldfieldname": "tolerance", - "oldfieldtype": "Currency" - }, { "fieldname": "image", "fieldtype": "Attach Image", @@ -1021,6 +1014,26 @@ "fieldtype": "Check", "label": "Synced With Hub", "read_only": 1 + }, + { + "fieldname": "manufacturers", + "fieldtype": "Table", + "label": "Manufacturers", + "options": "Item Manufacturer" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "over_delivery_receipt_allowance", + "fieldtype": "Float", + "label": "Over Delivery/Receipt Allowance (%)", + "oldfieldname": "tolerance", + "oldfieldtype": "Currency" + }, + { + "fieldname": "over_billing_allowance", + "fieldtype": "Float", + "label": "Over Billing Allowance (%)", + "depends_on": "eval:!doc.__islocal" } ], "has_web_view": 1, @@ -1028,7 +1041,7 @@ "idx": 2, "image_field": "image", "max_attachments": 1, - "modified": "2019-07-05 12:18:13.977931", + "modified": "2019-07-12 12:18:13.977931", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 80d4e172a5..03e903c06f 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -122,6 +122,7 @@ class Item(WebsiteGenerator): self.validate_item_defaults() self.validate_customer_provided_part() self.update_defaults_from_item_group() + self.validate_auto_reorder_enabled_in_stock_settings() self.cant_change() if not self.get("__islocal"): @@ -859,6 +860,12 @@ class Item(WebsiteGenerator): filters={"production_item": self.name, "docstatus": 1}): return True + def validate_auto_reorder_enabled_in_stock_settings(self): + if self.reorder_levels: + enabled = frappe.db.get_single_value('Stock Settings', 'auto_indent') + if not enabled: + frappe.msgprint(msg=_("You have to enable auto re-order in Stock Settings to maintain re-order levels."), title=_("Enable Auto Re-Order"), indicator="orange") + def get_timeline_data(doctype, name): '''returns timeline data based on stock ledger entry''' out = {} @@ -1000,7 +1007,7 @@ def invalidate_item_variants_cache_for_website(doc): if item_code: item_cache = ItemVariantsCacheManager(item_code) - item_cache.rebuild_cache() + item_cache.clear_cache() def check_stock_uom_with_bin(item, stock_uom): diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py index b3733d357b..dd4676a037 100644 --- a/erpnext/stock/doctype/item/item_dashboard.py +++ b/erpnext/stock/doctype/item/item_dashboard.py @@ -41,7 +41,7 @@ def get_data(): }, { 'label': _('Manufacture'), - 'items': ['Work Order', 'Item Manufacturer'] + 'items': ['Production Plan', 'Work Order', 'Item Manufacturer'] } ] - } \ No newline at end of file + } diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index d182290427..30675b54b3 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -31,13 +31,16 @@ class ItemPrice(Document): frappe.throw(_("Valid From Date must be lesser than Valid Upto Date.")) def update_price_list_details(self): - self.buying, self.selling, self.currency = \ - frappe.db.get_value("Price List", - {"name": self.price_list, "enabled": 1}, - ["buying", "selling", "currency"]) + if self.price_list: + self.buying, self.selling, self.currency = \ + frappe.db.get_value("Price List", + {"name": self.price_list, "enabled": 1}, + ["buying", "selling", "currency"]) def update_item_details(self): - self.item_name, self.item_description = frappe.db.get_value("Item",self.item_code,["item_name", "description"]) + if self.item_code: + self.item_name, self.item_description = frappe.db.get_value("Item", + self.item_code,["item_name", "description"]) def check_duplicates(self): conditions = "where item_code=%(item_code)s and price_list=%(price_list)s and name != %(name)s" diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.js b/erpnext/stock/doctype/manufacturer/manufacturer.js index 67df1bf4c0..bb7e314e14 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.js +++ b/erpnext/stock/doctype/manufacturer/manufacturer.js @@ -3,6 +3,14 @@ frappe.ui.form.on('Manufacturer', { refresh: function(frm) { - + frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' }; + if (frm.doc.__islocal) { + hide_field(['address_html','contact_html']); + frappe.contacts.clear_address_and_contact(frm); + } + else { + unhide_field(['address_html','contact_html']); + frappe.contacts.render_address_and_contact(frm); + } } }); diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.json b/erpnext/stock/doctype/manufacturer/manufacturer.json index 5025917836..3a64fbeceb 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.json +++ b/erpnext/stock/doctype/manufacturer/manufacturer.json @@ -1,268 +1,120 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:short_name", - "beta": 0, - "creation": "2016-01-17 11:04:52.761731", - "custom": 0, - "description": "Manufacturers used in Items", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:short_name", + "creation": "2016-01-17 11:04:52.761731", + "description": "Manufacturers used in Items", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "short_name", + "full_name", + "website", + "country", + "logo", + "address_contacts", + "address_html", + "column_break_8", + "contact_html", + "section_break_10", + "notes" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Limited to 12 characters", - "fieldname": "short_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Short 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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "description": "Limited to 12 characters", + "fieldname": "short_name", + "fieldtype": "Data", + "label": "Short Name", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "full_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": "Full 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, - "unique": 0 - }, + "fieldname": "full_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Full Name" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "website", - "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": "Website", - "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": "website", + "fieldtype": "Data", + "label": "Website" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "country", - "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": "Country", - "length": 0, - "no_copy": 0, - "options": "Country", - "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": "country", + "fieldtype": "Link", + "label": "Country", + "options": "Country" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "logo", - "fieldtype": "Attach Image", - "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": "Logo", - "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": "logo", + "fieldtype": "Attach Image", + "label": "Logo" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "notes", - "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": "Notes", - "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": "notes", + "fieldtype": "Small Text", + "label": "Notes" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "address_contacts", + "fieldtype": "Section Break", + "label": "Address and Contacts", + "options": "fa fa-map-marker" + }, + { + "fieldname": "address_html", + "fieldtype": "HTML", + "label": "Address HTML", + "read_only": 1 + }, + { + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, + { + "fieldname": "contact_html", + "fieldtype": "HTML", + "label": "Contact HTML", + "read_only": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-certificate", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-08-03 06:27:57.182666", - "modified_by": "Administrator", - "module": "Stock", - "name": "Manufacturer", - "name_case": "", - "owner": "Administrator", + ], + "icon": "fa fa-certificate", + "modified": "2019-07-06 13:06:47.237014", + "modified_by": "Administrator", + "module": "Stock", + "name": "Manufacturer", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 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": "Stock Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "short_name, full_name", - "show_name_in_global_search": 1, - "sort_field": "", - "sort_order": "DESC", - "title_field": "short_name", - "track_changes": 0, - "track_seen": 0 + ], + "search_fields": "short_name, full_name", + "show_name_in_global_search": 1, + "sort_order": "DESC", + "title_field": "short_name" } \ No newline at end of file diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.py b/erpnext/stock/doctype/manufacturer/manufacturer.py index 7b85b05aa1..b624f73b77 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.py +++ b/erpnext/stock/doctype/manufacturer/manufacturer.py @@ -4,7 +4,10 @@ from __future__ import unicode_literals import frappe +from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address from frappe.model.document import Document class Manufacturer(Document): - pass + def onload(self): + """Load address and contacts in `__onload`""" + load_address_and_contact(self) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 5351f32186..96e31ff6ff 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -211,6 +211,7 @@ frappe.ui.form.on('Material Request', { d.stock_uom = item.stock_uom; d.conversion_factor = 1; d.qty = item.qty; + d.project = item.project; }); } d.hide(); diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index ef5f24e59d..f2fe44879d 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -370,19 +370,20 @@ def make_purchase_order_based_on_supplier(source_name, target_doc=None): def get_material_requests_based_on_supplier(supplier): supplier_items = [d.parent for d in frappe.db.get_all("Item Default", {"default_supplier": supplier}, 'parent')] - if supplier_items: - material_requests = frappe.db.sql_list("""select distinct mr.name - from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item - where mr.name = mr_item.parent - and mr_item.item_code in (%s) - and mr.material_request_type = 'Purchase' - and mr.per_ordered < 99.99 - and mr.docstatus = 1 - and mr.status != 'Stopped' - order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)), - tuple(supplier_items)) - else: - material_requests = [] + if not supplier_items: + frappe.throw(_("{0} is not the default supplier for any items.".format(supplier))) + + material_requests = frappe.db.sql_list("""select distinct mr.name + from `tabMaterial Request` mr, `tabMaterial Request Item` mr_item + where mr.name = mr_item.parent + and mr_item.item_code in (%s) + and mr.material_request_type = 'Purchase' + and mr.per_ordered < 99.99 + and mr.docstatus = 1 + and mr.status != 'Stopped' + order by mr_item.item_code ASC""" % ', '.join(['%s']*len(supplier_items)), + tuple(supplier_items)) + return material_requests, supplier_items @frappe.whitelist() @@ -449,7 +450,7 @@ def make_stock_entry(source_name, target_doc=None): "field_map": { "name": "material_request_item", "parent": "material_request", - "uom": "stock_uom", + "uom": "stock_uom" }, "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index e82aa2c63e..a2d3e75f23 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -38,6 +38,29 @@ frappe.ui.form.on("Purchase Receipt", { if(frm.doc.company) { frm.trigger("toggle_display_account_head"); } + + if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) { + frm.add_custom_button(__('Debit Note'), function() { + frappe.confirm(__("Are you sure you want to make debit note?"), + function() { + frm.trigger("make_debit_note"); + } + ); + }, __('Create')); + + frm.page.set_inner_btn_group_as_primary(__('Create')); + } + }, + + make_debit_note: function(frm) { + frm.call({ + method: "make_return_invoice", + doc: frm.doc, + freeze: true, + callback: function() { + frm.reload_doc(); + } + }); }, company: function(frm) { diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 67cd7f8678..df28becd95 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -387,6 +387,16 @@ class PurchaseReceipt(BuyingController): self.load_from_db() + def make_return_invoice(self): + return_invoice = make_purchase_invoice(self.name) + return_invoice.is_return = True + return_invoice.save() + return_invoice.submit() + + debit_note_link = frappe.utils.get_link_to_form('Purchase Invoice', return_invoice.name) + + frappe.msgprint(_("Debit Note {0} has been created automatically").format(debit_note_link)) + def update_billed_amount_based_on_po(po_detail, update_modified=True): # Billed against Sales Order directly billed_against_po = frappe.db.sql("""select sum(amount) from `tabPurchase Invoice Item` diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index 94d47947ef..a9f3cd09ef 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -1,857 +1,260 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-04-30 13:13:03", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 1, - "engine": "InnoDB", + "autoname": "naming_series:", + "creation": "2013-04-30 13:13:03", + "doctype": "DocType", + "document_type": "Setup", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "report_date", + "column_break_4", + "inspection_type", + "reference_type", + "reference_name", + "section_break_7", + "item_code", + "item_serial_no", + "batch_no", + "sample_size", + "column_break1", + "item_name", + "description", + "status", + "section_break_14", + "inspected_by", + "verified_by", + "bom_no", + "column_break_17", + "remarks", + "amended_from", + "specification_details", + "quality_inspection_template", + "readings" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "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": "Series", - "length": 0, - "no_copy": 1, - "options": "MAT-QA-.YYYY.-", - "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": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "MAT-QA-.YYYY.-", + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "report_date", - "fieldtype": "Date", - "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": "Report Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "report_date", - "oldfieldtype": "Date", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "report_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Report Date", + "oldfieldname": "report_date", + "oldfieldtype": "Date", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "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_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "inspection_type", - "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": "Inspection Type", - "length": 0, - "no_copy": 0, - "oldfieldname": "inspection_type", - "oldfieldtype": "Select", - "options": "\nIncoming\nOutgoing\nIn Process", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "inspection_type", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Inspection Type", + "oldfieldname": "inspection_type", + "oldfieldtype": "Select", + "options": "\nIncoming\nOutgoing\nIn Process", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_type", - "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": "Reference Type", - "length": 0, - "no_copy": 0, - "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry", - "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": "reference_type", + "fieldtype": "Select", + "label": "Reference Type", + "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "reference_name", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "purchase_receipt_no", - "oldfieldtype": "Link", - "options": "reference_type", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Reference Name", + "oldfieldname": "purchase_receipt_no", + "oldfieldtype": "Link", + "options": "reference_type" + }, { - "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, - "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": "section_break_7", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "oldfieldname": "item_code", - "oldfieldtype": "Link", - "options": "Item", - "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": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Item Code", + "oldfieldname": "item_code", + "oldfieldtype": "Link", + "options": "Item", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_serial_no", - "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 Serial No", - "length": 0, - "no_copy": 0, - "oldfieldname": "item_serial_no", - "oldfieldtype": "Link", - "options": "Serial No", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_serial_no", + "fieldtype": "Link", + "label": "Item Serial No", + "oldfieldname": "item_serial_no", + "oldfieldtype": "Link", + "options": "Serial No" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "batch_no", - "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": "Batch No", - "length": 0, - "no_copy": 0, - "oldfieldname": "batch_no", - "oldfieldtype": "Link", - "options": "Batch", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "batch_no", + "fieldtype": "Link", + "label": "Batch No", + "oldfieldname": "batch_no", + "oldfieldtype": "Link", + "options": "Batch" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample_size", - "fieldtype": "Float", - "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": "Sample Size", - "length": 0, - "no_copy": 0, - "oldfieldname": "sample_size", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample_size", + "fieldtype": "Float", + "label": "Sample Size", + "oldfieldname": "sample_size", + "oldfieldtype": "Currency", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Item Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "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": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Small Text", "width": "300px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 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, - "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": "section_break_14", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "user", - "fieldname": "inspected_by", - "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": "Inspected By", - "length": 0, - "no_copy": 0, - "oldfieldname": "inspected_by", - "oldfieldtype": "Data", - "options": "User", - "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 - }, + "default": "user", + "fieldname": "inspected_by", + "fieldtype": "Link", + "label": "Inspected By", + "oldfieldname": "inspected_by", + "oldfieldtype": "Data", + "options": "User", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "verified_by", - "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": "Verified By", - "length": 0, - "no_copy": 0, - "oldfieldname": "verified_by", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "verified_by", + "fieldtype": "Data", + "label": "Verified By", + "oldfieldname": "verified_by", + "oldfieldtype": "Data" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bom_no", - "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": "BOM No", - "length": 0, - "no_copy": 0, - "options": "BOM", - "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 - }, + "fieldname": "bom_no", + "fieldtype": "Link", + "label": "BOM No", + "options": "BOM", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_17", - "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_17", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "remarks", - "fieldtype": "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": "Remarks", - "length": 0, - "no_copy": 1, - "oldfieldname": "remarks", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "remarks", + "fieldtype": "Text", + "label": "Remarks", + "no_copy": 1, + "oldfieldname": "remarks", + "oldfieldtype": "Text" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Quality Inspection", - "permlevel": 0, - "print_hide": 1, - "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 - }, + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Quality Inspection", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "specification_details", - "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, - "oldfieldtype": "Section Break", - "options": "Simple", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "specification_details", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "Simple" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "quality_inspection_template", - "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": "Quality Inspection Template", - "length": 0, - "no_copy": 0, - "options": "Quality Inspection Template", - "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": "quality_inspection_template", + "fieldtype": "Link", + "label": "Quality Inspection Template", + "options": "Quality Inspection Template" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "readings", - "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": "Readings", - "length": 0, - "no_copy": 0, - "oldfieldname": "qa_specification_details", - "oldfieldtype": "Table", - "options": "Quality Inspection Reading", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "readings", + "fieldtype": "Table", + "label": "Readings", + "oldfieldname": "qa_specification_details", + "oldfieldtype": "Table", + "options": "Quality Inspection Reading" + }, + { + "default": "Accepted", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "\nAccepted\nRejected", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-search", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 14:44:45.996446", - "modified_by": "Administrator", - "module": "Stock", - "name": "Quality Inspection", - "owner": "Administrator", + ], + "icon": "fa fa-search", + "idx": 1, + "is_submittable": 1, + "modified": "2019-07-12 12:07:23.153698", + "modified_by": "Administrator", + "module": "Stock", + "name": "Quality Inspection", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Quality Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "item_code, report_date, reference_name", - "show_name_in_global_search": 1, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "search_fields": "item_code, report_date, reference_name", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "ASC" } \ No newline at end of file diff --git a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json index 94aecfe7e7..f9f8a71c02 100644 --- a/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json +++ b/erpnext/stock/doctype/quality_inspection_reading/quality_inspection_reading.json @@ -1,413 +1,138 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "hash", - "beta": 0, - "creation": "2013-02-22 01:27:43", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, + "autoname": "hash", + "creation": "2013-02-22 01:27:43", + "doctype": "DocType", + "editable_grid": 1, + "field_order": [ + "specification", + "value", + "reading_1", + "reading_2", + "reading_3", + "reading_4", + "reading_5", + "reading_6", + "reading_7", + "reading_8", + "reading_9", + "reading_10", + "status" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "specification", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Parameter", - "length": 0, - "no_copy": 0, - "oldfieldname": "specification", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "columns": 3, + "fieldname": "specification", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Parameter", + "oldfieldname": "specification", + "oldfieldtype": "Data", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Acceptance Criteria", - "length": 0, - "no_copy": 0, - "oldfieldname": "value", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "columns": 2, + "fieldname": "value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Acceptance Criteria", + "oldfieldname": "value", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_1", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reading 1", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_1", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "columns": 1, + "fieldname": "reading_1", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Reading 1", + "oldfieldname": "reading_1", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_2", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reading 2", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_2", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "columns": 1, + "fieldname": "reading_2", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Reading 2", + "oldfieldname": "reading_2", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_3", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reading 3", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_3", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "columns": 1, + "fieldname": "reading_3", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Reading 3", + "oldfieldname": "reading_3", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_4", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reading 4", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_4", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "fieldname": "reading_4", + "fieldtype": "Data", + "label": "Reading 4", + "oldfieldname": "reading_4", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_5", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 5", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_5", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "fieldname": "reading_5", + "fieldtype": "Data", + "label": "Reading 5", + "oldfieldname": "reading_5", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_6", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 6", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_6", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "fieldname": "reading_6", + "fieldtype": "Data", + "label": "Reading 6", + "oldfieldname": "reading_6", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_7", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 7", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_7", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "fieldname": "reading_7", + "fieldtype": "Data", + "label": "Reading 7", + "oldfieldname": "reading_7", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_8", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 8", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_8", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "fieldname": "reading_8", + "fieldtype": "Data", + "label": "Reading 8", + "oldfieldname": "reading_8", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_9", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 9", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_9", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "fieldname": "reading_9", + "fieldtype": "Data", + "label": "Reading 9", + "oldfieldname": "reading_9", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reading_10", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reading 10", - "length": 0, - "no_copy": 0, - "oldfieldname": "reading_10", - "oldfieldtype": "Data", - "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, - "unique": 0 - }, + "fieldname": "reading_10", + "fieldtype": "Data", + "label": "Reading 10", + "oldfieldname": "reading_10", + "oldfieldtype": "Data" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Accepted", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "Accepted\nRejected", - "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, - "unique": 0 + "columns": 2, + "default": "Accepted", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "Accepted\nRejected" } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2016-11-16 03:55:50.046712", - "modified_by": "Administrator", - "module": "Stock", - "name": "Quality Inspection Reading", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "track_seen": 0 + ], + "idx": 1, + "istable": 1, + "modified": "2019-07-11 18:48:12.667404", + "modified_by": "Administrator", + "module": "Stock", + "name": "Quality Inspection Reading", + "owner": "Administrator", + "permissions": [], + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 5933700457..912a4651b3 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -61,7 +61,8 @@ "ste_detail", "column_break_51", "transferred_qty", - "reference_purchase_receipt" + "reference_purchase_receipt", + "project" ], "fields": [ { @@ -472,11 +473,18 @@ "label": "Reference Purchase Receipt", "options": "Purchase Receipt", "read_only": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project", + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2019-06-14 11:58:41.958144", + "modified": "2019-07-12 11:34:53.190749", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json index c989907639..f43390f19d 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.json +++ b/erpnext/stock/doctype/stock_settings/stock_settings.json @@ -255,7 +255,7 @@ "columns": 0, "description": "Percentage you are allowed to receive or deliver more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to receive 110 units.", "fetch_if_empty": 0, - "fieldname": "tolerance", + "fieldname": "over_delivery_receipt_allowance", "fieldtype": "Float", "hidden": 0, "ignore_user_permissions": 0, @@ -264,7 +264,7 @@ "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, - "label": "Limit Percent", + "label": "Over Delivery/Receipt Allowance (%)", "length": 0, "no_copy": 0, "permlevel": 0, @@ -918,7 +918,7 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2019-06-18 01:19:07.738045", + "modified": "2019-07-04 01:19:07.738045", "modified_by": "Administrator", "module": "Stock", "name": "Stock Settings", diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py index 4d07a0e857..d68f97917f 100644 --- a/erpnext/stock/stock_balance.py +++ b/erpnext/stock/stock_balance.py @@ -110,7 +110,7 @@ def get_reserved_qty(item_code, warehouse): return flt(reserved_qty[0][0]) if reserved_qty else 0 def get_indented_qty(item_code, warehouse): - indented_qty = frappe.db.sql("""select sum(mr_item.qty - mr_item.ordered_qty) + indented_qty = frappe.db.sql("""select sum((mr_item.qty - mr_item.ordered_qty) * mr_item.conversion_factor) from `tabMaterial Request Item` mr_item, `tabMaterial Request` mr where mr_item.item_code=%s and mr_item.warehouse=%s and mr_item.qty > mr_item.ordered_qty and mr_item.parent=mr.name diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 1a272d1bc4..aec9db9d4a 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -2,6 +2,12 @@ frappe.ui.form.on("Issue", { onload: function(frm) { frm.email_field = "raised_by"; + frappe.db.get_value("Support Settings", {name: "Support Settings"}, "allow_resetting_service_level_agreement", (r) => { + if (!r.allow_resetting_service_level_agreement) { + frm.set_df_property("reset_service_level_agreement", "hidden", 1) ; + } + }); + if (frm.doc.service_level_agreement) { frappe.call({ method: "erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters", @@ -73,6 +79,42 @@ frappe.ui.form.on("Issue", { } }, + reset_service_level_agreement: function(frm) { + let reset_sla = new frappe.ui.Dialog({ + title: __("Reset Service Level Agreement"), + fields: [ + { + fieldtype: "Data", + fieldname: "reason", + label: __("Reason"), + reqd: 1 + } + ], + primary_action_label: __("Reset"), + primary_action: (values) => { + reset_sla.disable_primary_action(); + reset_sla.hide(); + reset_sla.clear(); + + frappe.show_alert({ + indicator: 'green', + message: __('Resetting Service Level Agreement.') + }); + + frm.call("reset_service_level_agreement", { + reason: values.reason, + user: frappe.session.user_email + }, () => { + reset_sla.enable_primary_action(); + frm.refresh(); + frappe.msgprint(__("Service Level Agreement Reset.")); + }); + } + }); + + reset_sla.show(); + }, + timeline_refresh: function(frm) { // create button for "Help Article" if(frappe.model.can_create('Help Article')) { @@ -129,8 +171,15 @@ frappe.ui.form.on("Issue", { function set_time_to_resolve_and_response(frm) { frm.dashboard.clear_headline(); - var time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled); - var time_to_resolve = get_time_left(frm.doc.resolution_by, frm.doc.agreement_fulfilled); + var time_to_respond = get_status(frm.doc.response_by_variance); + if (!frm.doc.first_responded_on && frm.doc.agreement_fulfilled === "Ongoing") { + time_to_respond = get_time_left(frm.doc.response_by, frm.doc.agreement_fulfilled); + } + + var time_to_resolve = get_status(frm.doc.resolution_by_variance); + if (!frm.doc.resolution_date && frm.doc.agreement_fulfilled === "Ongoing") { + time_to_resolve = get_time_left(frm.doc.resolution_by, frm.doc.agreement_fulfilled); + } frm.dashboard.set_headline_alert( '
' + @@ -146,7 +195,15 @@ function set_time_to_resolve_and_response(frm) { function get_time_left(timestamp, agreement_fulfilled) { const diff = moment(timestamp).diff(moment()); - const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : moment(0, 'seconds').format('HH:mm'); - let indicator = (diff_display == '00:00' && agreement_fulfilled != "Fulfilled") ? "red" : "green"; + const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : "Failed"; + let indicator = (diff_display == 'Failed' && agreement_fulfilled != "Fulfilled") ? "red" : "green"; return {"diff_display": diff_display, "indicator": indicator}; } + +function get_status(variance) { + if (variance > 0) { + return {"diff_display": "Fulfilled", "indicator": "green"}; + } else { + return {"diff_display": "Failed", "indicator": "red"}; + } +} \ No newline at end of file diff --git a/erpnext/support/doctype/issue/issue.json b/erpnext/support/doctype/issue/issue.json index b7c4166f6c..1e5d5f9ea7 100644 --- a/erpnext/support/doctype/issue/issue.json +++ b/erpnext/support/doctype/issue/issue.json @@ -1,365 +1,384 @@ { - "allow_import": 1, - "allow_rename": 1, - "autoname": "naming_series:", - "creation": "2013-02-01 10:36:25", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "subject_section", - "naming_series", - "subject", - "customer", - "raised_by", - "cb00", - "status", - "priority", - "issue_type", - "sb_details", - "description", - "service_level_section", - "service_level_agreement", - "response_by", - "response_by_variance", - "cb", - "agreement_fulfilled", - "resolution_by", - "resolution_by_variance", - "response", - "mins_to_first_response", - "first_responded_on", - "additional_info", - "lead", - "contact", - "email_account", - "column_break_16", - "customer_name", - "project", - "company", - "section_break_19", - "resolution_details", - "column_break1", - "opening_date", - "opening_time", - "resolution_date", - "content_type", - "attachment", - "via_customer_portal" - ], - "fields": [ - { - "fieldname": "subject_section", - "fieldtype": "Section Break", - "label": "Subject", - "options": "fa fa-flag" - }, - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "options": "ISS-.YYYY.-", - "print_hide": 1, - "set_only_once": 1 - }, - { - "bold": 1, - "fieldname": "subject", - "fieldtype": "Data", - "in_global_search": 1, - "label": "Subject", - "reqd": 1, - "in_standard_filter": 1 - }, - { - "fieldname": "customer", - "fieldtype": "Link", - "in_global_search": 1, - "label": "Customer", - "oldfieldname": "customer", - "oldfieldtype": "Link", - "options": "Customer", - "print_hide": 1, - "search_index": 1 - }, - { - "bold": 1, - "depends_on": "eval:doc.__islocal", - "fieldname": "raised_by", - "fieldtype": "Data", - "in_global_search": 1, - "in_list_view": 1, - "label": "Raised By (Email)", - "oldfieldname": "raised_by", - "oldfieldtype": "Data", - "options": "Email" - }, - { - "fieldname": "cb00", - "fieldtype": "Column Break" - }, - { - "default": "Open", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Status", - "no_copy": 1, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "Open\nReplied\nHold\nClosed", - "search_index": 1 - }, - { - "fieldname": "priority", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Priority", - "options": "Issue Priority" - }, - { - "fieldname": "issue_type", - "fieldtype": "Link", - "label": "Issue Type", - "options": "Issue Type" - }, - { - "collapsible": 1, - "collapsible_depends_on": "eval:doc.status!=\"Closed\"", - "fieldname": "sb_details", - "fieldtype": "Section Break", - "label": "Details" - }, - { - "bold": 1, - "fieldname": "description", - "fieldtype": "Text Editor", - "in_global_search": 1, - "label": "Description", - "oldfieldname": "problem_description", - "oldfieldtype": "Text" - }, - { - "collapsible": 1, - "depends_on": "eval: doc.service_level_agreement", - "fieldname": "service_level_section", - "fieldtype": "Section Break", - "label": "Service Level" - }, - { - "fieldname": "service_level_agreement", - "fieldtype": "Link", - "label": "Service Level Agreement", - "options": "Service Level Agreement" - }, - { - "fieldname": "response_by", - "fieldtype": "Datetime", - "label": "Response By", - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "cb", - "fieldtype": "Column Break", - "options": "fa fa-pushpin", - "read_only": 1 - }, - { - "fieldname": "resolution_by", - "fieldtype": "Datetime", - "label": "Resolution By", - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "response", - "fieldtype": "Section Break", - "label": "Response" - }, - { - "bold": 1, - "fieldname": "mins_to_first_response", - "fieldtype": "Float", - "label": "Mins to First Response", - "read_only": 1 - }, - { - "fieldname": "first_responded_on", - "fieldtype": "Datetime", - "label": "First Responded On" - }, - { - "collapsible": 1, - "fieldname": "additional_info", - "fieldtype": "Section Break", - "label": "Reference", - "options": "fa fa-pushpin", - "read_only": 1 - }, - { - "fieldname": "lead", - "fieldtype": "Link", - "label": "Lead", - "options": "Lead" - }, - { - "fieldname": "contact", - "fieldtype": "Link", - "label": "Contact", - "options": "Contact" - }, - { - "fieldname": "email_account", - "fieldtype": "Link", - "label": "Email Account", - "options": "Email Account" - }, - { - "fieldname": "column_break_16", - "fieldtype": "Column Break" - }, - { - "bold": 1, - "fieldname": "customer_name", - "fieldtype": "Data", - "label": "Customer Name", - "oldfieldname": "customer_name", - "oldfieldtype": "Data", - "read_only": 1 - }, - { - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "print_hide": 1 - }, - { - "collapsible": 1, - "fieldname": "section_break_19", - "fieldtype": "Section Break", - "label": "Resolution" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "resolution_details", - "fieldtype": "Text Editor", - "label": "Resolution Details", - "no_copy": 1, - "oldfieldname": "resolution_details", - "oldfieldtype": "Text" - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "column_break1", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "read_only": 1 - }, - { - "default": "Today", - "fieldname": "opening_date", - "fieldtype": "Date", - "label": "Opening Date", - "no_copy": 1, - "oldfieldname": "opening_date", - "oldfieldtype": "Date", - "read_only": 1 - }, - { - "fieldname": "opening_time", - "fieldtype": "Time", - "label": "Opening Time", - "no_copy": 1, - "oldfieldname": "opening_time", - "oldfieldtype": "Time", - "read_only": 1 - }, - { - "depends_on": "eval:!doc.__islocal", - "fieldname": "resolution_date", - "fieldtype": "Datetime", - "label": "Resolution Date", - "no_copy": 1, - "oldfieldname": "resolution_date", - "oldfieldtype": "Date", - "read_only": 1 - }, - { - "fieldname": "content_type", - "fieldtype": "Data", - "hidden": 1, - "label": "Content Type" - }, - { - "fieldname": "attachment", - "fieldtype": "Attach", - "hidden": 1, - "label": "Attachment" - }, - { - "default": "0", - "fieldname": "via_customer_portal", - "fieldtype": "Check", - "label": "Via Customer Portal" - }, - { - "default": "Ongoing", - "fieldname": "agreement_fulfilled", - "fieldtype": "Select", - "label": "Service Level Agreement Fulfilled", - "options": "Ongoing\nFulfilled\nFailed", - "read_only": 1 - }, - { - "description": "in hours", - "fieldname": "response_by_variance", - "fieldtype": "Float", - "label": "Response By Variance", - "read_only": 1 - }, - { - "description": "in hours", - "fieldname": "resolution_by_variance", - "fieldtype": "Float", - "label": "Resolution By Variance", - "read_only": 1 - } - ], - "icon": "fa fa-ticket", - "idx": 7, - "modified": "2019-06-27 15:19:00.771333", - "modified_by": "Administrator", - "module": "Support", - "name": "Issue", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Support Team", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "search_fields": "status,customer,subject,raised_by", - "sort_field": "modified", - "sort_order": "ASC", - "timeline_field": "customer", - "title_field": "subject", - "track_seen": 1 -} + "allow_import": 1, + "allow_rename": 1, + "autoname": "naming_series:", + "creation": "2013-02-01 10:36:25", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "subject_section", + "naming_series", + "subject", + "customer", + "raised_by", + "cb00", + "status", + "priority", + "issue_type", + "sb_details", + "description", + "service_level_section", + "service_level_agreement", + "response_by", + "response_by_variance", + "reset_service_level_agreement", + "cb", + "agreement_fulfilled", + "resolution_by", + "resolution_by_variance", + "service_level_agreement_creation", + "response", + "mins_to_first_response", + "first_responded_on", + "additional_info", + "lead", + "contact", + "email_account", + "column_break_16", + "customer_name", + "project", + "company", + "section_break_19", + "resolution_details", + "column_break1", + "opening_date", + "opening_time", + "resolution_date", + "content_type", + "attachment", + "via_customer_portal" + ], + "fields": [ + { + "fieldname": "subject_section", + "fieldtype": "Section Break", + "label": "Subject", + "options": "fa fa-flag" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "ISS-.YYYY.-", + "print_hide": 1, + "set_only_once": 1 + }, + { + "bold": 1, + "fieldname": "subject", + "fieldtype": "Data", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Subject", + "reqd": 1 + }, + { + "fieldname": "customer", + "fieldtype": "Link", + "in_global_search": 1, + "label": "Customer", + "oldfieldname": "customer", + "oldfieldtype": "Link", + "options": "Customer", + "print_hide": 1, + "search_index": 1 + }, + { + "bold": 1, + "depends_on": "eval:doc.__islocal", + "fieldname": "raised_by", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Raised By (Email)", + "oldfieldname": "raised_by", + "oldfieldtype": "Data", + "options": "Email" + }, + { + "fieldname": "cb00", + "fieldtype": "Column Break" + }, + { + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "Open\nReplied\nHold\nClosed", + "search_index": 1 + }, + { + "default": "Medium", + "fieldname": "priority", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Priority", + "options": "Issue Priority" + }, + { + "fieldname": "issue_type", + "fieldtype": "Link", + "label": "Issue Type", + "options": "Issue Type" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval:doc.status!=\"Closed\"", + "fieldname": "sb_details", + "fieldtype": "Section Break", + "label": "Details" + }, + { + "bold": 1, + "fieldname": "description", + "fieldtype": "Text Editor", + "in_global_search": 1, + "label": "Description", + "oldfieldname": "problem_description", + "oldfieldtype": "Text" + }, + { + "collapsible": 1, + "fieldname": "service_level_section", + "fieldtype": "Section Break", + "label": "Service Level" + }, + { + "fieldname": "service_level_agreement", + "fieldtype": "Link", + "label": "Service Level Agreement", + "options": "Service Level Agreement" + }, + { + "fieldname": "response_by", + "fieldtype": "Datetime", + "label": "Response By", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "cb", + "fieldtype": "Column Break", + "options": "fa fa-pushpin", + "read_only": 1 + }, + { + "fieldname": "resolution_by", + "fieldtype": "Datetime", + "label": "Resolution By", + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "response", + "fieldtype": "Section Break", + "label": "Response" + }, + { + "bold": 1, + "fieldname": "mins_to_first_response", + "fieldtype": "Float", + "label": "Mins to First Response", + "read_only": 1 + }, + { + "fieldname": "first_responded_on", + "fieldtype": "Datetime", + "label": "First Responded On" + }, + { + "collapsible": 1, + "fieldname": "additional_info", + "fieldtype": "Section Break", + "label": "Reference", + "options": "fa fa-pushpin", + "read_only": 1 + }, + { + "fieldname": "lead", + "fieldtype": "Link", + "label": "Lead", + "options": "Lead" + }, + { + "fieldname": "contact", + "fieldtype": "Link", + "label": "Contact", + "options": "Contact" + }, + { + "fieldname": "email_account", + "fieldtype": "Link", + "label": "Email Account", + "options": "Email Account" + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "fieldname": "customer_name", + "fieldtype": "Data", + "label": "Customer Name", + "oldfieldname": "customer_name", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "print_hide": 1 + }, + { + "collapsible": 1, + "fieldname": "section_break_19", + "fieldtype": "Section Break", + "label": "Resolution" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "resolution_details", + "fieldtype": "Text Editor", + "label": "Resolution Details", + "no_copy": 1, + "oldfieldname": "resolution_details", + "oldfieldtype": "Text" + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "read_only": 1 + }, + { + "default": "Today", + "fieldname": "opening_date", + "fieldtype": "Date", + "label": "Opening Date", + "no_copy": 1, + "oldfieldname": "opening_date", + "oldfieldtype": "Date", + "read_only": 1 + }, + { + "fieldname": "opening_time", + "fieldtype": "Time", + "label": "Opening Time", + "no_copy": 1, + "oldfieldname": "opening_time", + "oldfieldtype": "Time", + "read_only": 1 + }, + { + "depends_on": "eval:!doc.__islocal", + "fieldname": "resolution_date", + "fieldtype": "Datetime", + "label": "Resolution Date", + "no_copy": 1, + "oldfieldname": "resolution_date", + "oldfieldtype": "Date", + "read_only": 1 + }, + { + "fieldname": "content_type", + "fieldtype": "Data", + "hidden": 1, + "label": "Content Type" + }, + { + "fieldname": "attachment", + "fieldtype": "Attach", + "hidden": 1, + "label": "Attachment" + }, + { + "default": "0", + "fieldname": "via_customer_portal", + "fieldtype": "Check", + "label": "Via Customer Portal" + }, + { + "default": "Ongoing", + "depends_on": "eval: doc.service_level_agreement", + "fieldname": "agreement_fulfilled", + "fieldtype": "Select", + "label": "Service Level Agreement Fulfilled", + "options": "Ongoing\nFulfilled\nFailed", + "read_only": 1 + }, + { + "depends_on": "eval: doc.service_level_agreement", + "description": "in hours", + "fieldname": "response_by_variance", + "fieldtype": "Float", + "label": "Response By Variance", + "read_only": 1 + }, + { + "depends_on": "eval: doc.service_level_agreement", + "description": "in hours", + "fieldname": "resolution_by_variance", + "fieldtype": "Float", + "label": "Resolution By Variance", + "read_only": 1 + }, + { + "fieldname": "service_level_agreement_creation", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Service Level Agreement Creation", + "read_only": 1 + }, + { + "depends_on": "eval: doc.service_level_agreement", + "fieldname": "reset_service_level_agreement", + "fieldtype": "Button", + "label": "Reset Service Level Agreement" + } + ], + "icon": "fa fa-ticket", + "idx": 7, + "modified": "2019-07-11 23:57:22.015881", + "modified_by": "Administrator", + "module": "Support", + "name": "Issue", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Support Team", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "search_fields": "status,customer,subject,raised_by", + "sort_field": "modified", + "sort_order": "ASC", + "timeline_field": "customer", + "title_field": "subject", + "track_changes": 1, + "track_seen": 1 + } \ No newline at end of file diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 93f13f1f9f..98d8c99809 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -92,7 +92,6 @@ class Issue(Document): self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()), 2) self.agreement_fulfilled = "Fulfilled" if self.response_by_variance > 0 and self.resolution_by_variance > 0 else "Failed" - self.save(ignore_permissions=True) def create_communication(self): communication = frappe.new_doc("Communication") @@ -118,6 +117,18 @@ class Issue(Document): replicated_issue = deepcopy(self) replicated_issue.subject = subject + replicated_issue.creation = now_datetime() + + # Reset SLA + if replicated_issue.service_level_agreement: + replicated_issue.service_level_agreement_creation = now_datetime() + replicated_issue.service_level_agreement = None + replicated_issue.agreement_fulfilled = "Ongoing" + replicated_issue.response_by = None + replicated_issue.response_by_variance = None + replicated_issue.resolution_by = None + replicated_issue.resolution_by_variance = None + frappe.get_doc(replicated_issue).insert() # Replicate linked Communications @@ -136,7 +147,8 @@ class Issue(Document): return replicated_issue.name def before_insert(self): - self.set_response_and_resolution_time() + if frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + 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, @@ -162,8 +174,9 @@ class Issue(Document): if not self.creation: self.creation = now_datetime() + self.service_level_agreement_creation = now_datetime() - start_date_time = get_datetime(self.creation) + start_date_time = get_datetime(self.service_level_agreement_creation) self.response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time) self.resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time) @@ -171,13 +184,33 @@ class Issue(Document): self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime())) def change_service_level_agreement_and_priority(self): - if not self.priority == frappe.db.get_value("Issue", self.name, "priority"): - self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) - frappe.msgprint("Priority has been updated.") + if self.service_level_agreement and frappe.db.exists("Issue", self.name) and \ + frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): - if not self.service_level_agreement == frappe.db.get_value("Issue", self.name, "service_level_agreement"): - self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) - frappe.msgprint("Service Level Agreement has been updated.") + if not self.priority == frappe.db.get_value("Issue", self.name, "priority"): + self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) + frappe.msgprint(_("Priority has been changed to {0}.").format(self.priority)) + + if not self.service_level_agreement == frappe.db.get_value("Issue", self.name, "service_level_agreement"): + self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) + frappe.msgprint(_("Service Level Agreement has been changed to {0}.").format(self.service_level_agreement)) + + def reset_service_level_agreement(self, reason, user): + if not frappe.db.get_single_value("Support Settings", "allow_resetting_service_level_agreement"): + frappe.throw(_("Allow Resetting Service Level Agreement from Support Settings.")) + + frappe.get_doc({ + "doctype": "Comment", + "comment_type": "Info", + "reference_doctype": self.doctype, + "reference_name": self.name, + "comment_email": user, + "content": " resetted Service Level Agreement - {0}".format(_(reason)), + }).insert(ignore_permissions=True) + + self.service_level_agreement_creation = now_datetime() + self.set_response_and_resolution_time(priority=self.priority, service_level_agreement=self.service_level_agreement) + self.save() def get_expected_time_for(parameter, service_level, start_date_time): current_date_time = start_date_time @@ -237,7 +270,8 @@ def get_expected_time_for(parameter, service_level, start_date_time): allotted_days -= 1 expected_time_is_set = allotted_days <= 0 - current_date_time = add_to_date(current_date_time, days=1) + if not expected_time_is_set: + current_date_time = add_to_date(current_date_time, days=1) if end_time and time_period != 'Hour': current_date_time = datetime.combine(getdate(current_date_time), get_time(end_time)) @@ -258,15 +292,15 @@ def set_service_level_agreement_variance(issue=None): if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer variance = round(time_diff_in_hours(doc.response_by, current_time), 2) - frappe.db.set_value("Issue", doc.name, "response_by_variance", variance) + frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False) if variance < 0: - frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed") + frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) if not doc.resolution_date: # resolution_date set when issue has been closed variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2) - frappe.db.set_value("Issue", doc.name, "resolution_by_variance", variance) + frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False) if variance < 0: - frappe.db.set_value("Issue", doc.name, "agreement_fulfilled", "Failed") + frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False) def get_list_context(context=None): return { diff --git a/erpnext/support/doctype/issue/issue_list.js b/erpnext/support/doctype/issue/issue_list.js index e87c464d35..6d702f6bdf 100644 --- a/erpnext/support/doctype/issue/issue_list.js +++ b/erpnext/support/doctype/issue/issue_list.js @@ -23,8 +23,8 @@ frappe.listview_settings['Issue'] = { 'Low': 'yellow', 'Medium': 'orange', 'High': 'red' - } - return [__(doc.status), color[doc.priority] || 'Red', `status,=,Open`]; + }; + return [__(doc.status), color[doc.priority] || 'red', `status,=,Open`]; } else if (doc.status === 'Closed') { return [__(doc.status), "green", "status,=," + doc.status]; } else { diff --git a/erpnext/support/doctype/issue/test_issue.py b/erpnext/support/doctype/issue/test_issue.py index 75d70b1318..7a5e3e300d 100644 --- a/erpnext/support/doctype/issue/test_issue.py +++ b/erpnext/support/doctype/issue/test_issue.py @@ -11,6 +11,7 @@ from datetime import timedelta class TestIssue(unittest.TestCase): def test_response_time_and_resolution_time_based_on_different_sla(self): + frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) create_service_level_agreements_for_issues() creation = datetime.datetime(2019, 3, 4, 12, 0) @@ -79,7 +80,8 @@ def make_issue(creation=None, customer=None, index=0): "customer": customer, "raised_by": "test@example.com", "description": "Service Level Agreement Issue", - "creation": creation + "creation": creation, + "service_level_agreement_creation": creation }).insert(ignore_permissions=True) return issue 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 f91a80c60d..9a83ca7ac0 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.json +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.json @@ -5,6 +5,7 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "enable", "service_level", "default_service_level_agreement", "holiday_list", @@ -149,9 +150,15 @@ "in_standard_filter": 1, "label": "Entity Type", "options": "\nCustomer\nCustomer Group\nTerritory" + }, + { + "default": "1", + "fieldname": "enable", + "fieldtype": "Check", + "label": "Enable" } ], - "modified": "2019-06-20 18:04:14.293378", + "modified": "2019-07-09 17:22:16.402939", "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 82c0ffb65c..a399c58b16 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -6,19 +6,23 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document from frappe import _ +from frappe.utils import getdate class ServiceLevelAgreement(Document): def validate(self): + if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + frappe.throw(_("Service Level Agreement tracking is not enabled.")) + if self.default_service_level_agreement: if frappe.db.exists("Service Level Agreement", {"default_service_level_agreement": "1", "name": ["!=", self.name]}): frappe.throw(_("A Default Service Level Agreement already exists.")) else: if self.start_date and self.end_date: - if self.start_date >= self.end_date: + if getdate(self.start_date) >= getdate(self.end_date): frappe.throw(_("Start Date of Agreement can't be greater than or equal to End Date.")) - if self.end_date < frappe.utils.getdate(): + if getdate(self.end_date) < getdate(frappe.utils.getdate()): frappe.throw(_("End Date of Agreement can't be less than today.")) if self.entity_type and self.entity: @@ -44,12 +48,16 @@ def check_agreement_status(): for service_level_agreement in service_level_agreements: doc = frappe.get_doc("Service Level Agreement", service_level_agreement.name) - if doc.end_date and doc.end_date < frappe.utils.getdate(): + 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): + if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + return + filters = [ ["Service Level Agreement", "active", "=", 1], + ["Service Level Agreement", "enable", "=", 1] ] if priority: @@ -80,6 +88,14 @@ def get_customer_territory(customer): @frappe.whitelist() def get_service_level_agreement_filters(name, customer=None): + if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"): + return + + filters = [ + ["Service Level Agreement", "active", "=", 1], + ["Service Level Agreement", "enable", "=", 1] + ] + if not customer: or_filters = [ ["Service Level Agreement", "default_service_level_agreement", "=", 1] @@ -93,5 +109,5 @@ def get_service_level_agreement_filters(name, customer=None): return { "priority": [priority.priority for priority in frappe.get_list("Service Level Priority", filters={"parent": name}, fields=["priority"])], - "service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", or_filters=or_filters)] + "service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters)] } \ 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 6aa5394192..4a741ea4e1 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 @@ -10,6 +10,8 @@ from erpnext.support.doctype.service_level.test_service_level import create_serv class TestServiceLevelAgreement(unittest.TestCase): def test_service_level_agreement(self): + frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) + create_service_level_for_sla() # Default Service Level Agreement @@ -74,6 +76,7 @@ def create_service_level_agreement(default_service_level_agreement, service_leve service_level_agreement = frappe.get_doc({ "doctype": "Service Level Agreement", + "enable": 1, "default_service_level_agreement": default_service_level_agreement, "service_level": service_level, "holiday_list": holiday_list, diff --git a/erpnext/support/doctype/support_settings/support_settings.json b/erpnext/support/doctype/support_settings/support_settings.json index 2b79107269..be9e064591 100644 --- a/erpnext/support/doctype/support_settings/support_settings.json +++ b/erpnext/support/doctype/support_settings/support_settings.json @@ -1,560 +1,152 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, "creation": "2017-02-17 13:07:35.686409", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "sb_00", + "track_service_level_agreement", + "allow_resetting_service_level_agreement", + "issues_sb", + "close_issue_after_days", + "portal_sb", + "get_started_sections", + "show_latest_forum_posts", + "forum_sb", + "forum_url", + "get_latest_query", + "response_key_list", + "column_break_10", + "post_title_key", + "post_description_key", + "post_route_key", + "post_route_string", + "search_apis_sb", + "search_apis" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "issues_sb", "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": "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 + "label": "Issues" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "7", "description": "Auto close Issue after 7 days", "fieldname": "close_issue_after_days", "fieldtype": "Int", - "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": "Close Issue After Days", - "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": "Close Issue After Days" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "portal_sb", "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": "Support Portal", - "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": "Support Portal" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "get_started_sections", "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": "Get Started Sections", - "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": "Get Started Sections" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "show_latest_forum_posts", "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": "Show Latest Forum Posts", - "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": "Show Latest Forum Posts" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "show_latest_forum_posts", "fieldname": "forum_sb", "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": "Forum Posts", - "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": "Forum Posts" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "forum_url", "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": "Forum URL", - "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": "Forum URL" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "get_latest_query", "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": "Get Latest Query", - "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": "Get Latest Query" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "response_key_list", "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": "Response Key List", - "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": "Response Key List" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_10", - "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_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "post_title_key", "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": "Post Title Key", - "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": "Post Title Key" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "post_description_key", "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": "Post Description Key", - "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": "Post Description Key" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "post_route_key", "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": "Post Route Key", - "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": "Post Route Key" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "post_route_string", "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": "Post Route String", - "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": "Post Route String" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "search_apis_sb", "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": "Search APIs", - "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": "Search APIs" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "search_apis", "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": "Search APIs", - "length": 0, - "no_copy": 0, - "options": "Support Search Source", - "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": "Support Search Source" + }, + { + "fieldname": "sb_00", + "fieldtype": "Section Break", + "label": "Service Level Agreements" + }, + { + "default": "0", + "fieldname": "track_service_level_agreement", + "fieldtype": "Check", + "label": "Track Service Level Agreement" + }, + { + "default": "0", + "fieldname": "allow_resetting_service_level_agreement", + "fieldtype": "Check", + "label": "Allow Resetting Service Level Agreement" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-17 02:11:33.462444", + "modified": "2019-07-10 22:52:39.663873", "modified_by": "Administrator", "module": "Support", "name": "Support Settings", - "name_case": "", "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": 0, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], "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_changes": 1 } \ No newline at end of file diff --git a/erpnext/templates/includes/product_page.js b/erpnext/templates/includes/product_page.js index af98fc7987..65949bb18c 100644 --- a/erpnext/templates/includes/product_page.js +++ b/erpnext/templates/includes/product_page.js @@ -29,10 +29,10 @@ frappe.ready(function() { .html(r.message.product_info.price.formatted_price_sales_uom + "
\ (" + r.message.product_info.price.formatted_price + " / " + r.message.product_info.uom + ")
"); - if(r.message.product_info.in_stock==0) { + if(r.message.product_info.in_stock===0) { $(".item-stock").html("
{{ _("Not in stock") }}
"); } - else if(r.message.product_info.in_stock==1) { + else if(r.message.product_info.in_stock===1) { var qty_display = "{{ _("In stock") }}"; if (r.message.product_info.show_stock_qty) { qty_display += " ("+r.message.product_info.stock_qty+")"; @@ -75,13 +75,13 @@ frappe.ready(function() { newVal = 0; if (btn.attr('data-dir') == 'up') { - newVal = parseInt(oldValue) + 1; + newVal = Number.parseInt(oldValue) + 1; } else if (btn.attr('data-dir') == 'dwn') { - if (parseInt(oldValue) > 1) { - newVal = parseInt(oldValue) - 1; + if (Number.parseInt(oldValue) > 1) { + newVal = Number.parseInt(oldValue) - 1; } else { - newVal = parseInt(oldValue); + newVal = Number.parseInt(oldValue); } } input.val(newVal); diff --git a/erpnext/www/lms/macros/__init__.py b/erpnext/www/lms/macros/__init__.py new file mode 100644 index 0000000000..e69de29bb2