From dec2c90866a2f6df3fd95f9ad5c5923704a83bb8 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 29 Jul 2020 17:14:23 +0200 Subject: [PATCH 01/60] refactor: move datev-specific stuff to utils --- erpnext/regional/germany/utils/__init__.py | 0 .../regional/germany/utils/datev/__init__.py | 0 .../utils}/datev/datev_constants.py | 73 ----- .../regional/germany/utils/datev/datev_csv.py | 176 +++++++++++ erpnext/regional/report/datev/datev.py | 279 ++++++------------ erpnext/regional/report/datev/test_datev.py | 18 +- 6 files changed, 275 insertions(+), 271 deletions(-) create mode 100644 erpnext/regional/germany/utils/__init__.py create mode 100644 erpnext/regional/germany/utils/datev/__init__.py rename erpnext/regional/{report => germany/utils}/datev/datev_constants.py (89%) create mode 100644 erpnext/regional/germany/utils/datev/datev_csv.py diff --git a/erpnext/regional/germany/utils/__init__.py b/erpnext/regional/germany/utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/germany/utils/datev/__init__.py b/erpnext/regional/germany/utils/datev/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/regional/report/datev/datev_constants.py b/erpnext/regional/germany/utils/datev/datev_constants.py similarity index 89% rename from erpnext/regional/report/datev/datev_constants.py rename to erpnext/regional/germany/utils/datev/datev_constants.py index e063703005..88dc02dfca 100644 --- a/erpnext/regional/report/datev/datev_constants.py +++ b/erpnext/regional/germany/utils/datev/datev_constants.py @@ -460,79 +460,6 @@ ACCOUNT_NAME_COLUMNS = [ "Sprach-ID" ] -QUERY_REPORT_COLUMNS = [ - { - "label": "Umsatz (ohne Soll/Haben-Kz)", - "fieldname": "Umsatz (ohne Soll/Haben-Kz)", - "fieldtype": "Currency", - "width": 100 - }, - { - "label": "Soll/Haben-Kennzeichen", - "fieldname": "Soll/Haben-Kennzeichen", - "fieldtype": "Data", - "width": 100 - }, - { - "label": "Konto", - "fieldname": "Konto", - "fieldtype": "Data", - "width": 100 - }, - { - "label": "Gegenkonto (ohne BU-Schlüssel)", - "fieldname": "Gegenkonto (ohne BU-Schlüssel)", - "fieldtype": "Data", - "width": 100 - }, - { - "label": "Belegdatum", - "fieldname": "Belegdatum", - "fieldtype": "Date", - "width": 100 - }, - { - "label": "Belegfeld 1", - "fieldname": "Belegfeld 1", - "fieldtype": "Data", - "width": 150 - }, - { - "label": "Buchungstext", - "fieldname": "Buchungstext", - "fieldtype": "Text", - "width": 300 - }, - { - "label": "Beleginfo - Art 1", - "fieldname": "Beleginfo - Art 1", - "fieldtype": "Link", - "options": "DocType", - "width": 100 - }, - { - "label": "Beleginfo - Inhalt 1", - "fieldname": "Beleginfo - Inhalt 1", - "fieldtype": "Dynamic Link", - "options": "Beleginfo - Art 1", - "width": 150 - }, - { - "label": "Beleginfo - Art 2", - "fieldname": "Beleginfo - Art 2", - "fieldtype": "Link", - "options": "DocType", - "width": 100 - }, - { - "label": "Beleginfo - Inhalt 2", - "fieldname": "Beleginfo - Inhalt 2", - "fieldtype": "Dynamic Link", - "options": "Beleginfo - Art 2", - "width": 150 - } -] - class DataCategory(): """Field of the CSV Header.""" diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py new file mode 100644 index 0000000000..be41a4ab45 --- /dev/null +++ b/erpnext/regional/germany/utils/datev/datev_csv.py @@ -0,0 +1,176 @@ +# coding: utf-8 +from __future__ import unicode_literals + +import datetime +import zipfile +import six +import frappe +import pandas as pd +from frappe import _ +from csv import QUOTE_NONNUMERIC +from six import BytesIO +from six import string_types +from .datev_constants import DataCategory +from .datev_constants import Transactions +from .datev_constants import AccountNames + + +def get_datev_csv(data, filters, csv_class): + """ + Fill in missing columns and return a CSV in DATEV Format. + + For automatic processing, DATEV requires the first line of the CSV file to + hold meta data such as the length of account numbers oder the category of + the data. + + Arguments: + data -- array of dictionaries + filters -- dict + csv_class -- defines DATA_CATEGORY, FORMAT_NAME and COLUMNS + """ + empty_df = pd.DataFrame(columns=csv_class.COLUMNS) + data_df = pd.DataFrame.from_records(data) + result = empty_df.append(data_df, sort=True) + + if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS: + result['Belegdatum'] = pd.to_datetime(result['Belegdatum']) + + if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES: + result['Sprach-ID'] = 'de-DE' + + data = result.to_csv( + # Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035 + sep=str(';'), + # European decimal seperator + decimal=',', + # Windows "ANSI" encoding + encoding='latin_1', + # format date as DDMM + date_format='%d%m', + # Windows line terminator + line_terminator='\r\n', + # Do not number rows + index=False, + # Use all columns defined above + columns=csv_class.COLUMNS, + # Quote most fields, even currency values with "," separator + quoting=QUOTE_NONNUMERIC + ) + + if not six.PY2: + data = data.encode('latin_1') + + header = get_header(filters, csv_class) + header = ';'.join(header).encode('latin_1') + + # 1st Row: Header with meta data + # 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here. + # 3rd - nth Row: Data (Nutzdaten) + return header + b'\r\n' + data + + +def get_header(filters, csv_class): + description = filters.get('voucher_type', csv_class.FORMAT_NAME) + company = filters.get('company') + datev_settings = frappe.get_doc('DATEV Settings', {'client': company}) + default_currency = frappe.get_value('Company', company, 'default_currency') + coa = frappe.get_value('Company', company, 'chart_of_accounts') + coa_short_code = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '') + + header = [ + # DATEV format + # "DTVF" = created by DATEV software, + # "EXTF" = created by other software + '"EXTF"', + # version of the DATEV format + # 141 = 1.41, + # 510 = 5.10, + # 720 = 7.20 + '700', + csv_class.DATA_CATEGORY, + '"%s"' % csv_class.FORMAT_NAME, + # Format version (regarding format name) + csv_class.FORMAT_VERSION, + # Generated on + datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '000', + # Imported on -- stays empty + '', + # Origin. Any two symbols, will be replaced by "SV" on import. + '"EN"', + # I = Exported by + '"%s"' % frappe.session.user, + # J = Imported by -- stays empty + '', + # K = Tax consultant number (Beraternummer) + datev_settings.get('consultant_number', '0000000'), + # L = Tax client number (Mandantennummer) + datev_settings.get('client_number', '00000'), + # M = Start of the fiscal year (Wirtschaftsjahresbeginn) + frappe.utils.formatdate(frappe.defaults.get_user_default('year_start_date'), 'yyyyMMdd'), + # N = Length of account numbers (Sachkontenlänge) + datev_settings.get('account_number_length', '4'), + # O = Transaction batch start date (YYYYMMDD) + frappe.utils.formatdate(filters.get('from_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # P = Transaction batch end date (YYYYMMDD) + frappe.utils.formatdate(filters.get('to_date'), 'yyyyMMdd') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # Q = Description (for example, "Sales Invoice") Max. 30 chars + '"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # R = Diktatkürzel + '', + # S = Buchungstyp + # 1 = Transaction batch (Finanzbuchführung), + # 2 = Annual financial statement (Jahresabschluss) + '1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # T = Rechnungslegungszweck + # 0 oder leer = vom Rechnungslegungszweck unabhängig + # 50 = Handelsrecht + # 30 = Steuerrecht + # 64 = IFRS + # 40 = Kalkulatorik + # 11 = Reserviert + # 12 = Reserviert + '0' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # U = Festschreibung + # TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1" + '0', + # V = Default currency, for example, "EUR" + '"%s"' % default_currency if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', + # reserviert + '', + # Derivatskennzeichen + '', + # reserviert + '', + # reserviert + '', + # SKR + '"%s"' % coa_short_code, + # Branchen-Lösungs-ID + '', + # reserviert + '', + # reserviert + '', + # Anwendungsinformation (Verarbeitungskennzeichen der abgebenden Anwendung) + '' + ] + return header + + +def download_csv_files_as_zip(csv_data_list): + """ + Put CSV files in a zip archive and send that to the client. + + Params: + csv_data_list -- list of dicts [{'file_name': 'EXTF_Buchunsstapel.zip', 'csv_data': get_datev_csv()}] + """ + zip_buffer = BytesIO() + + datev_zip = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED) + for csv_file in csv_data_list: + datev_zip.writestr(csv_file.get('file_name'), csv_file.get('csv_data')) + datev_zip.close() + + frappe.response['filecontent'] = zip_buffer.getvalue() + frappe.response['filename'] = 'DATEV.zip' + frappe.response['type'] = 'binary' diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index 7fec94e740..97d93d3b80 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -9,31 +9,92 @@ Provide a report and downloadable CSV according to the German DATEV format. """ from __future__ import unicode_literals -import datetime import json -import zipfile import six import frappe -import pandas as pd - from frappe import _ -from csv import QUOTE_NONNUMERIC -from six import BytesIO from six import string_types -from .datev_constants import DataCategory -from .datev_constants import Transactions -from .datev_constants import DebtorsCreditors -from .datev_constants import AccountNames -from .datev_constants import QUERY_REPORT_COLUMNS +from erpnext.regional.germany.utils.datev.datev_csv import download_csv_files_as_zip, get_datev_csv +from erpnext.regional.germany.utils.datev.datev_constants import Transactions, DebtorsCreditors, AccountNames + +COLUMNS = [ + { + "label": "Umsatz (ohne Soll/Haben-Kz)", + "fieldname": "Umsatz (ohne Soll/Haben-Kz)", + "fieldtype": "Currency", + "width": 100 + }, + { + "label": "Soll/Haben-Kennzeichen", + "fieldname": "Soll/Haben-Kennzeichen", + "fieldtype": "Data", + "width": 100 + }, + { + "label": "Konto", + "fieldname": "Konto", + "fieldtype": "Data", + "width": 100 + }, + { + "label": "Gegenkonto (ohne BU-Schlüssel)", + "fieldname": "Gegenkonto (ohne BU-Schlüssel)", + "fieldtype": "Data", + "width": 100 + }, + { + "label": "Belegdatum", + "fieldname": "Belegdatum", + "fieldtype": "Date", + "width": 100 + }, + { + "label": "Belegfeld 1", + "fieldname": "Belegfeld 1", + "fieldtype": "Data", + "width": 150 + }, + { + "label": "Buchungstext", + "fieldname": "Buchungstext", + "fieldtype": "Text", + "width": 300 + }, + { + "label": "Beleginfo - Art 1", + "fieldname": "Beleginfo - Art 1", + "fieldtype": "Link", + "options": "DocType", + "width": 100 + }, + { + "label": "Beleginfo - Inhalt 1", + "fieldname": "Beleginfo - Inhalt 1", + "fieldtype": "Dynamic Link", + "options": "Beleginfo - Art 1", + "width": 150 + }, + { + "label": "Beleginfo - Art 2", + "fieldname": "Beleginfo - Art 2", + "fieldtype": "Link", + "options": "DocType", + "width": 100 + }, + { + "label": "Beleginfo - Inhalt 2", + "fieldname": "Beleginfo - Inhalt 2", + "fieldtype": "Dynamic Link", + "options": "Beleginfo - Art 2", + "width": 150 + } +] def execute(filters=None): """Entry point for frappe.""" validate(filters) - result = get_transactions(filters, as_dict=0) - columns = QUERY_REPORT_COLUMNS - - return columns, result + return COLUMNS, get_transactions(filters, as_dict=0) def validate(filters): @@ -240,146 +301,8 @@ def get_account_names(filters): """, filters, as_dict=1) -def get_datev_csv(data, filters, csv_class): - """ - Fill in missing columns and return a CSV in DATEV Format. - - For automatic processing, DATEV requires the first line of the CSV file to - hold meta data such as the length of account numbers oder the category of - the data. - - Arguments: - data -- array of dictionaries - filters -- dict - csv_class -- defines DATA_CATEGORY, FORMAT_NAME and COLUMNS - """ - empty_df = pd.DataFrame(columns=csv_class.COLUMNS) - data_df = pd.DataFrame.from_records(data) - - result = empty_df.append(data_df, sort=True) - - if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS: - result['Belegdatum'] = pd.to_datetime(result['Belegdatum']) - - if csv_class.DATA_CATEGORY == DataCategory.ACCOUNT_NAMES: - result['Sprach-ID'] = 'de-DE' - - data = result.to_csv( - # Reason for str(';'): https://github.com/pandas-dev/pandas/issues/6035 - sep=str(';'), - # European decimal seperator - decimal=',', - # Windows "ANSI" encoding - encoding='latin_1', - # format date as DDMM - date_format='%d%m', - # Windows line terminator - line_terminator='\r\n', - # Do not number rows - index=False, - # Use all columns defined above - columns=csv_class.COLUMNS, - # Quote most fields, even currency values with "," separator - quoting=QUOTE_NONNUMERIC - ) - - if not six.PY2: - data = data.encode('latin_1') - - header = get_header(filters, csv_class) - header = ';'.join(header).encode('latin_1') - - # 1st Row: Header with meta data - # 2nd Row: Data heading (Überschrift der Nutzdaten), included in `data` here. - # 3rd - nth Row: Data (Nutzdaten) - return header + b'\r\n' + data - - -def get_header(filters, csv_class): - description = filters.get('voucher_type', csv_class.FORMAT_NAME) - - header = [ - # DATEV format - # "DTVF" = created by DATEV software, - # "EXTF" = created by other software - '"EXTF"', - # version of the DATEV format - # 141 = 1.41, - # 510 = 5.10, - # 720 = 7.20 - '700', - csv_class.DATA_CATEGORY, - '"%s"' % csv_class.FORMAT_NAME, - # Format version (regarding format name) - csv_class.FORMAT_VERSION, - # Generated on - datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '000', - # Imported on -- stays empty - '', - # Origin. Any two symbols, will be replaced by "SV" on import. - '"EN"', - # I = Exported by - '"%s"' % frappe.session.user, - # J = Imported by -- stays empty - '', - # K = Tax consultant number (Beraternummer) - filters.get('consultant_number', '0000000'), - # L = Tax client number (Mandantennummer) - filters.get('client_number', '00000'), - # M = Start of the fiscal year (Wirtschaftsjahresbeginn) - frappe.utils.formatdate(frappe.defaults.get_user_default("year_start_date"), "yyyyMMdd"), - # N = Length of account numbers (Sachkontenlänge) - '%d' % filters.get('acc_len', 4), - # O = Transaction batch start date (YYYYMMDD) - frappe.utils.formatdate(filters.get('from_date'), "yyyyMMdd") if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # P = Transaction batch end date (YYYYMMDD) - frappe.utils.formatdate(filters.get('to_date'), "yyyyMMdd") if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # Q = Description (for example, "Sales Invoice") Max. 30 chars - '"{}"'.format(_(description)) if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # R = Diktatkürzel - '', - # S = Buchungstyp - # 1 = Transaction batch (Finanzbuchführung), - # 2 = Annual financial statement (Jahresabschluss) - '1' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # T = Rechnungslegungszweck - # 0 oder leer = vom Rechnungslegungszweck unabhängig - # 50 = Handelsrecht - # 30 = Steuerrecht - # 64 = IFRS - # 40 = Kalkulatorik - # 11 = Reserviert - # 12 = Reserviert - '0' if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # U = Festschreibung - # TODO: Filter by Accounting Period. In export for closed Accounting Period, this will be "1" - '0', - # V = Default currency, for example, "EUR" - '"%s"' % filters.get('default_currency', 'EUR') if csv_class.DATA_CATEGORY == DataCategory.TRANSACTIONS else '', - # reserviert - '', - # Derivatskennzeichen - '', - # reserviert - '', - # reserviert - '', - # SKR - '"%s"' % filters.get('skr', '04'), - # Branchen-Lösungs-ID - '', - # reserviert - '', - # reserviert - '', - # Anwendungsinformation (Verarbeitungskennzeichen der abgebenden Anwendung) - '' - ] - return header - - @frappe.whitelist() -def download_datev_csv(filters=None): +def download_datev_csv(filters): """ Provide accounting entries for download in DATEV format. @@ -400,38 +323,26 @@ def download_datev_csv(filters=None): coa = frappe.get_value('Company', filters.get('company'), 'chart_of_accounts') filters['skr'] = '04' if 'SKR04' in coa else ('03' if 'SKR03' in coa else '') - # set account number length - account_numbers = frappe.get_list('Account', fields=['account_number'], filters={'is_group': 0, 'account_number': ('!=', '')}) - filters['acc_len'] = max([len(a.account_number) for a in account_numbers]) - - filters['consultant_number'] = frappe.get_value('DATEV Settings', filters.get('company'), 'consultant_number') - filters['client_number'] = frappe.get_value('DATEV Settings', filters.get('company'), 'client_number') - filters['default_currency'] = frappe.get_value('Company', filters.get('company'), 'default_currency') - - # This is where my zip will be written - zip_buffer = BytesIO() - # This is my zip file - datev_zip = zipfile.ZipFile(zip_buffer, mode='w', compression=zipfile.ZIP_DEFLATED) - transactions = get_transactions(filters) - transactions_csv = get_datev_csv(transactions, filters, csv_class=Transactions) - datev_zip.writestr('EXTF_Buchungsstapel.csv', transactions_csv) - account_names = get_account_names(filters) - account_names_csv = get_datev_csv(account_names, filters, csv_class=AccountNames) - datev_zip.writestr('EXTF_Kontenbeschriftungen.csv', account_names_csv) - customers = get_customers(filters) - customers_csv = get_datev_csv(customers, filters, csv_class=DebtorsCreditors) - datev_zip.writestr('EXTF_Kunden.csv', customers_csv) - suppliers = get_suppliers(filters) - suppliers_csv = get_datev_csv(suppliers, filters, csv_class=DebtorsCreditors) - datev_zip.writestr('EXTF_Lieferanten.csv', suppliers_csv) - - # You must call close() before exiting your program or essential records will not be written. - datev_zip.close() - frappe.response['filecontent'] = zip_buffer.getvalue() - frappe.response['filename'] = 'DATEV.zip' - frappe.response['type'] = 'binary' + download_csv_files_as_zip([ + { + 'file_name': 'EXTF_Buchungsstapel.csv', + 'csv_data': get_datev_csv(transactions, filters, csv_class=Transactions) + }, + { + 'file_name': 'EXTF_Kontenbeschriftungen.csv', + 'csv_data': get_datev_csv(account_names, filters, csv_class=AccountNames) + }, + { + 'file_name': 'EXTF_Kunden.csv', + 'csv_data': get_datev_csv(customers, filters, csv_class=DebtorsCreditors) + }, + { + 'file_name': 'EXTF_Lieferanten.csv', + 'csv_data': get_datev_csv(suppliers, filters, csv_class=DebtorsCreditors) + }, + ]) diff --git a/erpnext/regional/report/datev/test_datev.py b/erpnext/regional/report/datev/test_datev.py index eed62a8690..9529923a73 100644 --- a/erpnext/regional/report/datev/test_datev.py +++ b/erpnext/regional/report/datev/test_datev.py @@ -1,32 +1,22 @@ # coding=utf-8 from __future__ import unicode_literals -import os -import json import zipfile +import frappe from six import BytesIO from unittest import TestCase - -import frappe -from frappe.utils import getdate, today, now_datetime, cstr -from frappe.test_runner import make_test_objects +from frappe.utils import today, now_datetime, cstr from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice -from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts from erpnext.regional.report.datev.datev import validate from erpnext.regional.report.datev.datev import get_transactions from erpnext.regional.report.datev.datev import get_customers from erpnext.regional.report.datev.datev import get_suppliers from erpnext.regional.report.datev.datev import get_account_names -from erpnext.regional.report.datev.datev import get_datev_csv -from erpnext.regional.report.datev.datev import get_header from erpnext.regional.report.datev.datev import download_datev_csv -from erpnext.regional.report.datev.datev_constants import DataCategory -from erpnext.regional.report.datev.datev_constants import Transactions -from erpnext.regional.report.datev.datev_constants import DebtorsCreditors -from erpnext.regional.report.datev.datev_constants import AccountNames -from erpnext.regional.report.datev.datev_constants import QUERY_REPORT_COLUMNS +from erpnext.regional.germany.utils.datev.datev_csv import get_datev_csv, get_header +from erpnext.regional.germany.utils.datev.datev_constants import Transactions, DebtorsCreditors, AccountNames def make_company(company_name, abbr): if not frappe.db.exists("Company", company_name): From b719378ee5e2d1d8701db82650d14f0536d0b4cd Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 29 Jul 2020 18:11:04 +0200 Subject: [PATCH 02/60] fix: codacy --- erpnext/regional/germany/utils/datev/datev_csv.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py index be41a4ab45..df95a5b95a 100644 --- a/erpnext/regional/germany/utils/datev/datev_csv.py +++ b/erpnext/regional/germany/utils/datev/datev_csv.py @@ -9,15 +9,12 @@ import pandas as pd from frappe import _ from csv import QUOTE_NONNUMERIC from six import BytesIO -from six import string_types from .datev_constants import DataCategory from .datev_constants import Transactions -from .datev_constants import AccountNames def get_datev_csv(data, filters, csv_class): - """ - Fill in missing columns and return a CSV in DATEV Format. + """Fill in missing columns and return a CSV in DATEV Format. For automatic processing, DATEV requires the first line of the CSV file to hold meta data such as the length of account numbers oder the category of @@ -158,8 +155,7 @@ def get_header(filters, csv_class): def download_csv_files_as_zip(csv_data_list): - """ - Put CSV files in a zip archive and send that to the client. + """Put CSV files in a zip archive and send that to the client. Params: csv_data_list -- list of dicts [{'file_name': 'EXTF_Buchunsstapel.zip', 'csv_data': get_datev_csv()}] From 517df5fb0eba9e06c3623c80b05bfba42b75b470 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 29 Jul 2020 18:16:49 +0200 Subject: [PATCH 03/60] fix: codacy (again) --- erpnext/regional/germany/utils/datev/datev_constants.py | 2 ++ erpnext/regional/report/datev/datev.py | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/regional/germany/utils/datev/datev_constants.py b/erpnext/regional/germany/utils/datev/datev_constants.py index 88dc02dfca..63f9a777bb 100644 --- a/erpnext/regional/germany/utils/datev/datev_constants.py +++ b/erpnext/regional/germany/utils/datev/datev_constants.py @@ -461,6 +461,7 @@ ACCOUNT_NAME_COLUMNS = [ ] class DataCategory(): + """Field of the CSV Header.""" DEBTORS_CREDITORS = "16" @@ -469,6 +470,7 @@ class DataCategory(): POSTING_TEXT_CONSTANTS = "67" class FormatName(): + """Field of the CSV Header, corresponds to DataCategory.""" DEBTORS_CREDITORS = "Debitoren/Kreditoren" diff --git a/erpnext/regional/report/datev/datev.py b/erpnext/regional/report/datev/datev.py index 97d93d3b80..dd818e6054 100644 --- a/erpnext/regional/report/datev/datev.py +++ b/erpnext/regional/report/datev/datev.py @@ -10,7 +10,6 @@ Provide a report and downloadable CSV according to the German DATEV format. from __future__ import unicode_literals import json -import six import frappe from frappe import _ from six import string_types From 440c7525292a804995d9decc48b76310a793a480 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 12:16:18 +0530 Subject: [PATCH 04/60] chore: Lab Test Template form clean-up --- .../lab_test_template/lab_test_template.json | 8 ++-- .../lab_test_template/lab_test_template.py | 37 ++++++++++--------- .../lab_test_template_list.js | 2 +- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json index db64297269..fc6a1e1790 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json @@ -112,7 +112,7 @@ { "default": "1", "depends_on": "eval:doc.lab_test_template_type != 'Grouped'", - "description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ", + "description": "If unchecked, the item will not be available in Sales Invoices for billing but can be used in group test creation. ", "fieldname": "is_billable", "fieldtype": "Check", "label": "Is Billable", @@ -184,7 +184,7 @@ "depends_on": "eval:doc.lab_test_template_type == 'Descriptive'", "fieldname": "section_break_special", "fieldtype": "Section Break", - "label": "Descriptive" + "label": "Descriptive Test" }, { "default": "0", @@ -196,7 +196,7 @@ "depends_on": "eval:doc.lab_test_template_type == 'Grouped'", "fieldname": "section_break_group", "fieldtype": "Section Break", - "label": "Group" + "label": "Group Tests" }, { "fieldname": "lab_test_groups", @@ -314,7 +314,7 @@ } ], "links": [], - "modified": "2020-07-13 12:57:09.925436", + "modified": "2020-07-30 11:55:43.093828", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test Template", diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py index 6f0d08cf85..e4fbdd9d86 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -15,7 +15,8 @@ class LabTestTemplate(Document): def validate(self): if self.is_billable and (not self.lab_test_rate or self.lab_test_rate <= 0.0): - frappe.throw(_("Standard Selling Rate should be greater than zero.")) + frappe.throw(_('Standard Selling Rate should be greater than zero.')) + self.validate_conversion_factor() self.enable_disable_item() @@ -42,7 +43,9 @@ class LabTestTemplate(Document): # Remove template reference from item and disable item if self.item: try: - frappe.delete_doc('Item', self.item) + item = self.item + self.db_set('item', '') + frappe.delete_doc('Item', item) except Exception: frappe.throw(_('Not permitted. Please disable the Lab Test Template')) @@ -63,26 +66,26 @@ class LabTestTemplate(Document): 'standard_rate': self.lab_test_rate, 'description': self.lab_test_description }) - item.save() + item.flags.ignore_mandatory = True + item.save(ignore_permissions=True) def item_price_exists(self): item_price = frappe.db.exists({'doctype': 'Item Price', 'item_code': self.lab_test_code}) if item_price: return item_price[0][0] - else: - return False + return False def validate_conversion_factor(self): - if self.lab_test_template_type == "Single" and self.secondary_uom and not self.conversion_factor: - frappe.throw(_("Conversion Factor is mandatory")) - if self.lab_test_template_type == "Compound": + if self.lab_test_template_type == 'Single' and self.secondary_uom and not self.conversion_factor: + frappe.throw(_('Conversion Factor is mandatory')) + if self.lab_test_template_type == 'Compound': for item in self.normal_test_templates: if item.secondary_uom and not item.conversion_factor: - frappe.throw(_("Conversion Factor is mandatory")) - if self.lab_test_template_type == "Grouped": + frappe.throw(_('Conversion Factor is mandatory')) + if self.lab_test_template_type == 'Grouped': for group in self.lab_test_groups: - if group.template_or_new_line == "Add New Line" and group.secondary_uom and not group.conversion_factor: - frappe.throw(_("Conversion Factor is mandatory")) + if group.template_or_new_line == 'Add New Line' and group.secondary_uom and not group.conversion_factor: + frappe.throw(_('Conversion Factor is mandatory')) def create_item_from_template(doc): @@ -101,9 +104,9 @@ def create_item_from_template(doc): 'include_item_in_manufacturing': 0, 'show_in_website': 0, 'is_pro_applicable': 0, - 'disabled': 0 if doc.is_billable and not doc.disabled else doc.disabled, + 'disabled': 0 if doc.is_billable and not doc.disabled else doc.disabled, 'stock_uom': uom - }).insert(ignore_permissions = True, ignore_mandatory = True) + }).insert(ignore_permissions=True, ignore_mandatory=True) # Insert item price if doc.is_billable and doc.lab_test_rate != 0.0: @@ -123,7 +126,7 @@ def make_item_price(item, price_list_name, item_price): 'price_list': price_list_name, 'item_code': item, 'price_list_rate': item_price - }).insert(ignore_permissions = True, ignore_mandatory = True) + }).insert(ignore_permissions=True, ignore_mandatory=True) @frappe.whitelist() def change_test_code_from_template(lab_test_code, doc): @@ -132,8 +135,8 @@ def change_test_code_from_template(lab_test_code, doc): if frappe.db.exists({'doctype': 'Item', 'item_code': lab_test_code}): frappe.throw(_('Lab Test Item {0} already exist').format(lab_test_code)) else: - rename_doc('Item', doc.name, lab_test_code, ignore_permissions = True) + rename_doc('Item', doc.name, lab_test_code, ignore_permissions=True) frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_code', lab_test_code) frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_name', lab_test_code) - rename_doc('Lab Test Template', doc.name, lab_test_code, ignore_permissions = True) + rename_doc('Lab Test Template', doc.name, lab_test_code, ignore_permissions=True) return lab_test_code diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js index a3417ebdfc..08fc2cddda 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js @@ -3,5 +3,5 @@ */ frappe.listview_settings['Lab Test Template'] = { add_fields: ['lab_test_name', 'lab_test_code', 'lab_test_rate'], - filters: [['disabled', '=', 0]] + filters: [['disabled', '=', 'No']] }; From ff0b9bffcf373a8781ed76008aec551b2648cba1 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 12:16:56 +0530 Subject: [PATCH 05/60] feat: added dashboard for Lab Test Template --- .../lab_test_template_dashboard.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py new file mode 100644 index 0000000000..94dfeea7a4 --- /dev/null +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py @@ -0,0 +1,13 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'template', + 'transactions': [ + { + 'label': _('Lab Tests'), + 'items': ['Lab Test'] + } + ] + } From b00f870c77c2fcd0e6ee829a5c858550fe8c9e88 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 13:49:13 +0530 Subject: [PATCH 06/60] fix: Lab Test Completed status not visible in list view --- .../healthcare/doctype/lab_test/lab_test_list.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test_list.js b/erpnext/healthcare/doctype/lab_test/lab_test_list.js index b7f157c38b..0a6ed20f3e 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test_list.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test_list.js @@ -3,13 +3,16 @@ */ frappe.listview_settings['Lab Test'] = { add_fields: ['name', 'status', 'invoiced'], - filters: [['docstatus', '=', '0']], + filters: [['docstatus', '=', '1']], get_indicator: function (doc) { - if (doc.status == 'Approved') { - return [__('Approved'), 'green', 'status, = ,Approved']; - } - if (doc.status == 'Rejected') { + if (doc.status === 'Approved') { + return [__('Approved'), 'green', 'status, =, Approved']; + } else if (doc.status === 'Rejected') { return [__('Rejected'), 'orange', 'status, =, Rejected']; + } else if (doc.status === 'Completed') { + return [__('Completed'), 'green', 'status, =, Completed']; + } else if (doc.status === 'Cancelled') { + return [__('Cancelled'), 'red', 'status, =, Cancelled']; } }, onload: function (listview) { From c33703d54c3294793d4d644d7a541216c6b34338 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 14:25:40 +0530 Subject: [PATCH 07/60] fix: move result value validations to server side --- .../healthcare/doctype/lab_test/lab_test.js | 17 -------- .../healthcare/doctype/lab_test/lab_test.json | 23 ++++++----- .../healthcare/doctype/lab_test/lab_test.py | 40 +++++++++++++------ .../lab_test_group_template.json | 5 ++- .../lab_test_template/lab_test_template.py | 4 +- 5 files changed, 46 insertions(+), 43 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js index 8036c7dc13..87d9c83763 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -179,23 +179,6 @@ var show_lab_tests = function (frm, lab_test_list) { d.show(); }; -cur_frm.cscript.custom_before_submit = function (doc) { - if (doc.normal_test_items) { - for (let result in doc.normal_test_items) { - if (!doc.normal_test_items[result].result_value && !doc.normal_test_items[result].allow_blank && doc.normal_test_items[result].require_result_value) { - frappe.throw(__('Please input all required result values')); - } - } - } - if (doc.descriptive_test_items) { - for (let result in doc.descriptive_test_items) { - if (!doc.descriptive_test_items[result].result_value && !doc.descriptive_test_items[result].allow_blank && doc.descriptive_test_items[result].require_result_value) { - frappe.throw(__('Please input all required result values')); - } - } - } -}; - var make_dialog = function (frm, emailed, printed) { var number = frm.doc.mobile; diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index 2eb8014b7e..575a2659db 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -84,7 +84,7 @@ "fieldname": "naming_series", "fieldtype": "Select", "label": "Series", - "options": "LP-", + "options": "HLC-LAB-.YYYY.-", "print_hide": 1, "report_hide": 1, "reqd": 1 @@ -197,11 +197,10 @@ { "fieldname": "status", "fieldtype": "Select", + "in_list_view": 1, "label": "Status", "options": "Draft\nCompleted\nApproved\nRejected\nCancelled", - "print_hide": 1, "read_only": 1, - "report_hide": 1, "search_index": 1 }, { @@ -354,7 +353,8 @@ }, { "fieldname": "sb_normal", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Compound Test Result" }, { "fieldname": "normal_test_items", @@ -369,11 +369,13 @@ { "depends_on": "descriptive_toggle", "fieldname": "organisms_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Organism Test Result" }, { "fieldname": "sb_sensitivity", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Sensitivity Test Result" }, { "fieldname": "sensitivity_test_items", @@ -383,8 +385,10 @@ "report_hide": 1 }, { + "collapsible": 1, "fieldname": "sb_comments", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Comments" }, { "fieldname": "lab_test_comment", @@ -531,7 +535,8 @@ }, { "fieldname": "sb_descriptive", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Descriptive Test Result" }, { "default": "0", @@ -550,7 +555,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-07-16 13:35:24.811062", + "modified": "2020-07-30 14:03:00.166003", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test", diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 865f4a14e3..c676dfb8a8 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -9,20 +9,21 @@ from frappe.model.document import Document from frappe.utils import getdate, cstr class LabTest(Document): + def validate(self): + if not self.is_new(): + self.set_secondary_uom_result() + def on_submit(self): + self.validate_result_values() self.db_set('submitted_date', getdate()) self.db_set('status', 'Completed') insert_lab_test_to_medical_record(self) def on_cancel(self): - delete_lab_test_from_medical_record(self) self.db_set('status', 'Cancelled') + delete_lab_test_from_medical_record(self) self.reload() - def validate(self): - if not self.is_new(): - self.set_secondary_uom_result() - def on_update(self): if self.sensitivity_test_items: sensitivity = sorted(self.sensitivity_test_items, key=lambda x: x.antibiotic_sensitivity) @@ -51,7 +52,20 @@ class LabTest(Document): item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor) except: item.secondary_uom_result = '' - frappe.msgprint(_('Result for Secondary UOM not calculated for row #{0}'.format(item.idx)), title = _('Warning')) + frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated'.format(item.idx)), title = _('Warning')) + + def validate_result_values(self): + if self.normal_test_items: + for item in self.normal_test_items: + if not item.result_value and not item.allow_blank and item.require_result_value: + frappe.throw(_('Row #{0}: Please enter the result value for {1}').format( + item.idx, frappe.bold(item.lab_test_name)), title=_('Mandatory Results')) + + if self.descriptive_test_items: + for item in self.descriptive_test_items: + if not item.result_value and not item.allow_blank and item.require_result_value: + frappe.throw(_('Row #{0}: Please enter the result value {1}').format( + item.idx, frappe.bold(item.lab_test_name)), title=_('Mandatory Results')) def create_test_from_template(lab_test): @@ -263,8 +277,7 @@ def load_result_format(lab_test, template, prescription, invoice): for lab_test_group in template.lab_test_groups: # Template_in_group = None if lab_test_group.lab_test_template: - template_in_group = frappe.get_doc('Lab Test Template', - lab_test_group.lab_test_template) + template_in_group = frappe.get_doc('Lab Test Template', lab_test_group.lab_test_template) if template_in_group: if template_in_group.lab_test_template_type == 'Single': create_normals(template_in_group, lab_test) @@ -302,9 +315,10 @@ def load_result_format(lab_test, template, prescription, invoice): @frappe.whitelist() def get_employee_by_user_id(user_id): - emp_id = frappe.db.get_value('Employee', { 'user_id': user_id }) - employee = frappe.get_doc('Employee', emp_id) - return employee + emp_id = frappe.db.exists('Employee', { 'user_id': user_id }) + if emp_id: + return frappe.get_doc('Employee', emp_id) + return None def insert_lab_test_to_medical_record(doc): table_row = False @@ -325,7 +339,7 @@ def insert_lab_test_to_medical_record(doc): table_row += ' ' + frappe.bold(_('Lab Test Result: ')) + item.result_value if item.normal_range: - table_row += ' ' + _('Normal Range:') + item.normal_range + table_row += ' ' + _('Normal Range: ') + item.normal_range table_row += ' ' + comment elif doc.descriptive_test_items: @@ -356,7 +370,7 @@ def insert_lab_test_to_medical_record(doc): medical_record.save(ignore_permissions = True) def delete_lab_test_from_medical_record(self): - medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name= %s', (self.name)) + medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name=%s', (self.name)) if medical_record_id and medical_record_id[0][0]: frappe.delete_doc('Patient Medical Record', medical_record_id[0][0]) diff --git a/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json b/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json index beea7a357e..2767f7ec77 100644 --- a/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json +++ b/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json @@ -93,7 +93,8 @@ "depends_on": "secondary_uom", "fieldname": "conversion_factor", "fieldtype": "Float", - "label": "Conversion Factor" + "label": "Conversion Factor", + "mandatory_depends_on": "secondary_uom" }, { "default": "0", @@ -106,7 +107,7 @@ ], "istable": 1, "links": [], - "modified": "2020-06-24 10:59:01.921924", + "modified": "2020-07-30 12:36:03.082391", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test Group Template", diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py index e4fbdd9d86..543dee27eb 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -81,11 +81,11 @@ class LabTestTemplate(Document): if self.lab_test_template_type == 'Compound': for item in self.normal_test_templates: if item.secondary_uom and not item.conversion_factor: - frappe.throw(_('Conversion Factor is mandatory')) + frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(item.idx)) if self.lab_test_template_type == 'Grouped': for group in self.lab_test_groups: if group.template_or_new_line == 'Add New Line' and group.secondary_uom and not group.conversion_factor: - frappe.throw(_('Conversion Factor is mandatory')) + frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(group.idx)) def create_item_from_template(doc): From 73edb12ab3a579794782ae63173517f1c01a20ba Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 14:50:30 +0530 Subject: [PATCH 08/60] fix: msgprint for sample collection doc created on Lab Test creation --- erpnext/healthcare/doctype/lab_test/lab_test.py | 6 ++++-- .../lab_test_template/lab_test_template.json | 15 ++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index c676dfb8a8..2bf4a3a7db 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import getdate, cstr +from frappe.utils import getdate, cstr, get_link_to_form class LabTest(Document): def validate(self): @@ -262,7 +262,9 @@ def create_sample_collection(lab_test, template, patient, invoice): sample_collection = create_sample_doc(template, patient, invoice, lab_test.company) if sample_collection: lab_test.sample = sample_collection.name - + sample_collection_doc = get_link_to_form('Sample Collection', sample_collection.name) + frappe.msgprint(_('Sample Collection {0} has been created').format(sample_collection_doc), + title=_('Sample Collection'), indicator='green') return lab_test def load_result_format(lab_test, template, prescription, invoice): diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json index fc6a1e1790..c3fc842047 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json @@ -34,14 +34,15 @@ "descriptive_test_templates", "section_break_group", "lab_test_groups", - "medical_coding_section", - "medical_code_standard", - "medical_code", "sb_sample_collection", "sample", "sample_uom", "sample_qty", + "column_break_33", "sample_details", + "medical_coding_section", + "medical_code", + "medical_code_standard", "worksheet_section", "worksheet_instructions", "result_legend_section", @@ -128,6 +129,7 @@ "mandatory_depends_on": "eval:doc.is_billable == 1" }, { + "collapsible": 1, "fieldname": "medical_coding_section", "fieldtype": "Section Break", "label": "Medical Coding" @@ -217,7 +219,6 @@ "no_copy": 1 }, { - "collapsible": 1, "fieldname": "sb_sample_collection", "fieldtype": "Section Break", "label": "Sample Collection" @@ -311,10 +312,14 @@ "fieldname": "descriptive_test_templates", "fieldtype": "Table", "options": "Descriptive Test Template" + }, + { + "fieldname": "column_break_33", + "fieldtype": "Column Break" } ], "links": [], - "modified": "2020-07-30 11:55:43.093828", + "modified": "2020-07-30 14:32:40.449818", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test Template", From afa001a1d1f6a756b22e362535ac79b0e61f9a7d Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 15:25:04 +0530 Subject: [PATCH 09/60] fix: code clean-up --- erpnext/healthcare/doctype/lab_test/lab_test.js | 16 ++++++++-------- erpnext/healthcare/doctype/lab_test/lab_test.py | 14 +++++++++++--- .../healthcare/doctype/lab_test/lab_test_list.js | 4 ++-- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js index 87d9c83763..f1634c1294 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test.js @@ -34,10 +34,10 @@ frappe.ui.form.on('Lab Test', { if (frm.doc.docstatus === 1 && frm.doc.status !== 'Approved' && frm.doc.status !== 'Rejected') { frm.add_custom_button(__('Approve'), function () { status_update(1, frm); - }); + }, __('Actions')); frm.add_custom_button(__('Reject'), function () { status_update(0, frm); - }); + }, __('Actions')); } } @@ -186,7 +186,7 @@ var make_dialog = function (frm, emailed, printed) { title: 'Send SMS', width: 400, fields: [ - { fieldname: 'sms_type', fieldtype: 'Select', label: 'Type', options: ['Emailed', 'Printed'] }, + { fieldname: 'result_format', fieldtype: 'Select', label: 'Result Format', options: ['Emailed', 'Printed'] }, { fieldname: 'number', fieldtype: 'Data', label: 'Mobile Number', reqd: 1 }, { fieldname: 'message', fieldtype: 'Small Text', label: 'Message', reqd: 1 } ], @@ -200,22 +200,22 @@ var make_dialog = function (frm, emailed, printed) { dialog.hide(); } }); - if (frm.doc.report_preference == 'Print') { + if (frm.doc.report_preference === 'Print') { dialog.set_values({ - 'sms_type': 'Printed', + 'result_format': 'Printed', 'number': number, 'message': printed }); } else { dialog.set_values({ - 'sms_type': 'Emailed', + 'result_format': 'Emailed', 'number': number, 'message': emailed }); } var fd = dialog.fields_dict; - $(fd.sms_type.input).change(function () { - if (dialog.get_value('sms_type') == 'Emailed') { + $(fd.result_format.input).change(function () { + if (dialog.get_value('result_format') === 'Emailed') { dialog.set_values({ 'number': number, 'message': emailed diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 2bf4a3a7db..8dc26b09bc 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -103,7 +103,7 @@ def create_multiple(doctype, docname): lab_test_created = create_lab_test_from_encounter(docname) if lab_test_created: - frappe.msgprint(_('Lab Test(s) {0} created'.format(lab_test_created))) + frappe.msgprint(_('Lab Test(s) {0} created'.format(lab_test_created)), indicator='green') else: frappe.msgprint(_('No Lab Tests created')) @@ -225,8 +225,9 @@ def create_sample_doc(template, patient, invoice, company = None): 'docstatus': 0, 'sample': template.sample }) + if sample_exists: - # Update Sample Collection by adding quantity + # update sample collection by adding quantity sample_collection = frappe.get_doc('Sample Collection', sample_exists[0][0]) quantity = int(sample_collection.sample_qty) + int(template.sample_qty) if template.sample_details: @@ -252,7 +253,7 @@ def create_sample_doc(template, patient, invoice, company = None): sample_collection.company = company if template.sample_details: - sample_collection.sample_details = 'Test :' + (template.get('lab_test_name') or template.get('template')) + '\n' + 'Collection Detials:\n\t' + template.sample_details + sample_collection.sample_details = _('Test :') + (template.get('lab_test_name') or template.get('template')) + '\n' + 'Collection Detials:\n\t' + template.sample_details sample_collection.save(ignore_permissions=True) return sample_collection @@ -270,10 +271,13 @@ def create_sample_collection(lab_test, template, patient, invoice): def load_result_format(lab_test, template, prescription, invoice): if template.lab_test_template_type == 'Single': create_normals(template, lab_test) + elif template.lab_test_template_type == 'Compound': create_compounds(template, lab_test, False) + elif template.lab_test_template_type == 'Descriptive': create_descriptives(template, lab_test) + elif template.lab_test_template_type == 'Grouped': # Iterate for each template in the group and create one result for all. for lab_test_group in template.lab_test_groups: @@ -283,6 +287,7 @@ def load_result_format(lab_test, template, prescription, invoice): if template_in_group: if template_in_group.lab_test_template_type == 'Single': create_normals(template_in_group, lab_test) + elif template_in_group.lab_test_template_type == 'Compound': normal_heading = lab_test.append('normal_test_items') normal_heading.lab_test_name = template_in_group.lab_test_name @@ -290,6 +295,7 @@ def load_result_format(lab_test, template, prescription, invoice): normal_heading.allow_blank = 1 normal_heading.template = template_in_group.name create_compounds(template_in_group, lab_test, True) + elif template_in_group.lab_test_template_type == 'Descriptive': descriptive_heading = lab_test.append('descriptive_test_items') descriptive_heading.lab_test_name = template_in_group.lab_test_name @@ -297,6 +303,7 @@ def load_result_format(lab_test, template, prescription, invoice): descriptive_heading.allow_blank = 1 descriptive_heading.template = template_in_group.name create_descriptives(template_in_group, lab_test) + else: # Lab Test Group - Add New Line normal = lab_test.append('normal_test_items') normal.lab_test_name = lab_test_group.group_event @@ -307,6 +314,7 @@ def load_result_format(lab_test, template, prescription, invoice): normal.allow_blank = lab_test_group.allow_blank normal.require_result_value = 1 normal.template = template.name + if template.lab_test_template_type != 'No Result': if prescription: lab_test.prescription = prescription diff --git a/erpnext/healthcare/doctype/lab_test/lab_test_list.js b/erpnext/healthcare/doctype/lab_test/lab_test_list.js index 0a6ed20f3e..7b5b9d922a 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test_list.js +++ b/erpnext/healthcare/doctype/lab_test/lab_test_list.js @@ -24,7 +24,7 @@ frappe.listview_settings['Lab Test'] = { var create_multiple_dialog = function (listview) { var dialog = new frappe.ui.Dialog({ - title: 'Create Multiple Lab Test', + title: 'Create Multiple Lab Tests', width: 100, fields: [ { fieldtype: 'Link', label: 'Patient', fieldname: 'patient', options: 'Patient', reqd: 1 }, @@ -44,7 +44,7 @@ var create_multiple_dialog = function (listview) { } } ], - primary_action_label: __('Create Lab Test'), + primary_action_label: __('Create'), primary_action: function () { frappe.call({ method: 'erpnext.healthcare.doctype.lab_test.lab_test.create_multiple', From fa2c20ea9e9f127adc871b7ba188f7ce44b1a155 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 16:56:11 +0530 Subject: [PATCH 10/60] chore: Sample Collection Form clean-up --- .../healthcare/doctype/lab_test/lab_test.py | 6 +- .../sample_collection/sample_collection.js | 20 ++--- .../sample_collection/sample_collection.json | 85 ++++++++++++++++--- 3 files changed, 85 insertions(+), 26 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py index 8dc26b09bc..2db7743865 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/lab_test.py @@ -64,8 +64,8 @@ class LabTest(Document): if self.descriptive_test_items: for item in self.descriptive_test_items: if not item.result_value and not item.allow_blank and item.require_result_value: - frappe.throw(_('Row #{0}: Please enter the result value {1}').format( - item.idx, frappe.bold(item.lab_test_name)), title=_('Mandatory Results')) + frappe.throw(_('Row #{0}: Please enter the result value for {1}').format( + item.idx, frappe.bold(item.lab_test_particulars)), title=_('Mandatory Results')) def create_test_from_template(lab_test): @@ -103,7 +103,7 @@ def create_multiple(doctype, docname): lab_test_created = create_lab_test_from_encounter(docname) if lab_test_created: - frappe.msgprint(_('Lab Test(s) {0} created'.format(lab_test_created)), indicator='green') + frappe.msgprint(_('Lab Test(s) {0} created successfully').format(lab_test_created), indicator='green') else: frappe.msgprint(_('No Lab Tests created')) diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.js b/erpnext/healthcare/doctype/sample_collection/sample_collection.js index 2f5278b2d5..0390391235 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.js +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.js @@ -3,29 +3,29 @@ frappe.ui.form.on('Sample Collection', { refresh: function(frm) { - if(frappe.defaults.get_default("create_sample_collection_for_lab_test")){ - frm.add_custom_button(__("View Lab Tests"), function() { - frappe.route_options = {"sample": frm.doc.name}; - frappe.set_route("List", "Lab Test"); + if (frappe.defaults.get_default('create_sample_collection_for_lab_test')) { + frm.add_custom_button(__('View Lab Tests'), function() { + frappe.route_options = {'sample': frm.doc.name}; + frappe.set_route('List', 'Lab Test'); }); } } }); -frappe.ui.form.on("Sample Collection", "patient", function(frm) { +frappe.ui.form.on('Sample Collection', 'patient', function(frm) { if(frm.doc.patient){ frappe.call({ - "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail", + 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail', args: { patient: frm.doc.patient }, callback: function (data) { var age = null; - if(data.message.dob){ + if (data.message.dob){ age = calculate_age(data.message.dob); } - frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age); - frappe.model.set_value(frm.doctype,frm.docname, "patient_sex", data.message.sex); + frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age); + frappe.model.set_value(frm.doctype,frm.docname, 'patient_sex', data.message.sex); } }); } @@ -36,5 +36,5 @@ var calculate_age = function(birth) { var age = new Date(); age.setTime(ageMS); var years = age.getFullYear() - 1970; - return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)"; + return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)'; }; diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json index 016cfbc3ae..83383e3445 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json @@ -9,8 +9,10 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ + "patient_details_section", "naming_series", "patient", + "patient_name", "patient_age", "patient_sex", "column_break_4", @@ -25,15 +27,17 @@ "collected_by", "collected_time", "num_print", - "amended_from", "section_break_15", - "sample_details" + "sample_details", + "amended_from" ], "fields": [ { "fetch_from": "patient.inpatient_record", "fieldname": "inpatient_record", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Inpatient Record", "options": "Inpatient Record", "read_only": 1 @@ -42,6 +46,8 @@ "bold": 1, "fieldname": "naming_series", "fieldtype": "Select", + "hide_days": 1, + "hide_seconds": 1, "label": "Series", "no_copy": 1, "options": "HLC-SC-.YYYY.-", @@ -52,6 +58,8 @@ "default": "0", "fieldname": "invoiced", "fieldtype": "Check", + "hide_days": 1, + "hide_seconds": 1, "label": "Invoiced", "no_copy": 1, "read_only": 1, @@ -61,41 +69,60 @@ "fetch_from": "inpatient_record.patient", "fieldname": "patient", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "ignore_user_permissions": 1, "in_standard_filter": 1, "label": "Patient", "options": "Patient", + "reqd": 1, "search_index": 1 }, { "fieldname": "column_break_4", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "patient_age", "fieldtype": "Data", - "label": "Age" + "hide_days": 1, + "hide_seconds": 1, + "label": "Age", + "read_only": 1 }, { "fetch_from": "patient.sex", "fieldname": "patient_sex", - "fieldtype": "Data", - "label": "Gender" + "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, + "label": "Gender", + "options": "Gender", + "read_only": 1 }, { "fieldname": "company", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "in_standard_filter": 1, "label": "Company", "options": "Company" }, { "fieldname": "section_break_6", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, + "label": "Sample Details" }, { "fieldname": "sample", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "ignore_user_permissions": 1, "in_list_view": 1, "in_standard_filter": 1, @@ -108,16 +135,23 @@ "fetch_from": "sample.sample_uom", "fieldname": "sample_uom", "fieldtype": "Data", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, - "label": "UOM" + "label": "UOM", + "read_only": 1 }, { "fieldname": "column_break_10", - "fieldtype": "Column Break" + "fieldtype": "Column Break", + "hide_days": 1, + "hide_seconds": 1 }, { "fieldname": "collected_by", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "ignore_user_permissions": 1, "label": "Collected By", "options": "User" @@ -125,20 +159,27 @@ { "fieldname": "collected_time", "fieldtype": "Datetime", - "label": "Collected Time" + "hide_days": 1, + "hide_seconds": 1, + "label": "Collected On" }, { "allow_on_submit": 1, "default": "1", + "description": "Number of prints required for labelling the samples", "fieldname": "num_print", "fieldtype": "Int", - "label": "No. of print", + "hide_days": 1, + "hide_seconds": 1, + "label": "No. of prints", "print_hide": 1, "report_hide": 1 }, { "fieldname": "amended_from", "fieldtype": "Link", + "hide_days": 1, + "hide_seconds": 1, "label": "Amended From", "no_copy": 1, "options": "Sample Collection", @@ -147,25 +188,43 @@ }, { "fieldname": "section_break_15", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1 }, { "default": "0", "fieldname": "sample_qty", "fieldtype": "Float", + "hide_days": 1, + "hide_seconds": 1, "in_list_view": 1, "label": "Quantity" }, { "fieldname": "sample_details", "fieldtype": "Long Text", + "hide_days": 1, + "hide_seconds": 1, "ignore_xss_filter": 1, "label": "Collection Details" + }, + { + "fieldname": "patient_details_section", + "fieldtype": "Section Break", + "label": "Patient Details" + }, + { + "fetch_from": "patient.patient_name", + "fieldname": "patient_name", + "fieldtype": "Data", + "label": "Patient Name", + "read_only": 1 } ], "is_submittable": 1, "links": [], - "modified": "2020-05-25 14:36:46.990469", + "modified": "2020-07-30 16:53:13.076104", "modified_by": "Administrator", "module": "Healthcare", "name": "Sample Collection", From ca1b389c99529eeccf892fbfde8a2b34ce7a0153 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 16:58:19 +0530 Subject: [PATCH 11/60] fix: validate negative quantity for sample collection --- .../doctype/sample_collection/sample_collection.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.py b/erpnext/healthcare/doctype/sample_collection/sample_collection.py index 2c64320fac..461f809507 100644 --- a/erpnext/healthcare/doctype/sample_collection/sample_collection.py +++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.py @@ -3,7 +3,12 @@ # For license information, please see license.txt from __future__ import unicode_literals +import frappe from frappe.model.document import Document +from frappe.utils import flt +from frappe import _ class SampleCollection(Document): - pass + def validate(self): + if flt(self.sample_qty) <= 0: + frappe.throw(_('Sample Quantity cannot be negative or 0'), title=_('Invalid Quantity')) From c5ccf38cdf849431f27d3601756159391c237cd4 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 18:56:16 +0530 Subject: [PATCH 12/60] refactor: Lab Test Report --- .../healthcare/doctype/lab_test/lab_test.json | 4 +- .../report/lab_test_report/lab_test_report.js | 39 ++++- .../lab_test_report/lab_test_report.json | 39 ++--- .../report/lab_test_report/lab_test_report.py | 139 ++++++++++++++---- 4 files changed, 165 insertions(+), 56 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json index 575a2659db..edf1d911aa 100644 --- a/erpnext/healthcare/doctype/lab_test/lab_test.json +++ b/erpnext/healthcare/doctype/lab_test/lab_test.json @@ -248,8 +248,8 @@ { "fieldname": "result_date", "fieldtype": "Date", - "hidden": 1, "label": "Result Date", + "read_only": 1, "search_index": 1 }, { @@ -555,7 +555,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-07-30 14:03:00.166003", + "modified": "2020-07-30 18:18:38.516215", "modified_by": "Administrator", "module": "Healthcare", "name": "Lab Test", diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.js b/erpnext/healthcare/report/lab_test_report/lab_test_report.js index 3128f819bb..7754e2e196 100644 --- a/erpnext/healthcare/report/lab_test_report/lab_test_report.js +++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.js @@ -4,29 +4,54 @@ frappe.query_reports["Lab Test Report"] = { "filters": [ { - "fieldname":"from_date", + "fieldname": "from_date", "label": __("From Date"), "fieldtype": "Date", - "default": frappe.datetime.now_date(), - "width": "80" + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + "reqd": 1 }, { - "fieldname":"to_date", + "fieldname": "to_date", "label": __("To Date"), "fieldtype": "Date", - "default": frappe.datetime.now_date() + "default": frappe.datetime.now_date(), + "reqd": 1 }, { - "fieldname":"patient", + "fieldname": "company", + "label": __("Company"), + "fieldtype": "Link", + "default": frappe.defaults.get_default("Company"), + "options": "Company" + }, + { + "fieldname": "template", + "label": __("Lab Test Template"), + "fieldtype": "Link", + "options": "Lab Test Template" + }, + { + "fieldname": "patient", "label": __("Patient"), "fieldtype": "Link", "options": "Patient" }, { - "fieldname":"department", + "fieldname": "department", "label": __("Medical Department"), "fieldtype": "Link", "options": "Medical Department" + }, + { + "fieldname": "status", + "label": __("Status"), + "fieldtype": "Select", + "options": "\nCompleted\nApproved\nRejected" + }, + { + "fieldname": "invoiced", + "label": __("Invoiced"), + "fieldtype": "Check" } ] }; diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.json b/erpnext/healthcare/report/lab_test_report/lab_test_report.json index 30e5a5fd56..aeb42897b8 100644 --- a/erpnext/healthcare/report/lab_test_report/lab_test_report.json +++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.json @@ -1,30 +1,31 @@ { - "add_total_row": 1, - "creation": "2013-04-23 18:15:29", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 1, - "is_standard": "Yes", - "modified": "2018-08-06 11:41:50.218737", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Lab Test Report", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Lab Test", - "report_name": "Lab Test Report", - "report_type": "Script Report", + "add_total_row": 0, + "creation": "2013-04-23 18:15:29", + "disable_prepared_report": 0, + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "idx": 1, + "is_standard": "Yes", + "modified": "2020-07-30 18:53:20.102873", + "modified_by": "Administrator", + "module": "Healthcare", + "name": "Lab Test Report", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Lab Test", + "report_name": "Lab Test Report", + "report_type": "Script Report", "roles": [ { "role": "Laboratory User" - }, + }, { "role": "Nursing User" - }, + }, { "role": "LabTest Approver" - }, + }, { "role": "Healthcare Administrator" } diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.py b/erpnext/healthcare/report/lab_test_report/lab_test_report.py index 17f25fa7a7..be2d06193e 100644 --- a/erpnext/healthcare/report/lab_test_report/lab_test_report.py +++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.py @@ -8,51 +8,134 @@ from frappe import msgprint, _ def execute(filters=None): if not filters: filters = {} - lab_test_list = get_lab_test(filters) + data, columns = [], [] + columns = get_columns() + lab_test_list = get_lab_tests(filters) if not lab_test_list: - msgprint(_("No record found")) + msgprint(_("No records found")) return columns, lab_test_list data = [] for lab_test in lab_test_list: - row = [ lab_test.lab_test_name, lab_test.patient, lab_test.practitioner, lab_test.invoiced, lab_test.status, lab_test.result_date, lab_test.department] + row = frappe._dict({ + 'test': lab_test.name, + 'template': lab_test.template, + 'company': lab_test.company, + 'patient': lab_test.patient, + 'patient_name': lab_test.patient_name, + 'practitioner': lab_test.practitioner, + 'employee': lab_test.employee, + 'status': lab_test.status, + 'invoiced': lab_test.invoiced, + 'result_date': lab_test.result_date, + 'department': lab_test.department + }) data.append(row) return columns, data def get_columns(): - columns = [ - _("Test") + ":Data:120", - _("Patient") + ":Link/Patient:180", - _("Healthcare Practitioner") + ":Link/Healthcare Practitioner:120", - _("Invoiced") + ":Check:100", - _("Status") + ":Data:120", - _("Result Date") + ":Date:120", - _("Department") + ":Data:120", + return [ + { + "fieldname": "test", + "label": _("Lab Test"), + "fieldtype": "Link", + "options": "Lab Test", + "width": "120" + }, + { + "fieldname": "template", + "label": _("Lab Test Template"), + "fieldtype": "Link", + "options": "Lab Test Template", + "width": "120" + }, + { + "fieldname": "company", + "label": _("Company"), + "fieldtype": "Link", + "options": "Company", + "width": "120" + }, + { + "fieldname": "patient", + "label": _("Patient"), + "fieldtype": "Link", + "options": "Patient", + "width": "120" + }, + { + "fieldname": "patient_name", + "label": _("Patient Name"), + "fieldtype": "Data", + "width": "120" + }, + { + "fieldname": "practitioner", + "label": _("Requesting Practitioner"), + "fieldtype": "Link", + "options": "Healthcare Practitioner", + "width": "120" + }, + { + "fieldname": "employee", + "label": _("Lab Technician"), + "fieldtype": "Link", + "options": "Employee", + "width": "120" + }, + { + "fieldname": "status", + "label": _("Status"), + "fieldtype": "Data", + "width": "100" + }, + { + "fieldname": "invoiced", + "label": _("Invoiced"), + "fieldtype": "Check", + "width": "100" + }, + { + "fieldname": "result_date", + "label": _("Result Date"), + "fieldtype": "Date", + "width": "100" + }, + { + "fieldname": "department", + "label": _("Medical Department"), + "fieldtype": "Link", + "options": "Medical Department", + "width": "100" + } ] - return columns +def get_lab_tests(filters): + conditions = get_conditions(filters) + data = frappe.get_all( + doctype='Lab Test', + fields=['name', 'template', 'company', 'patient', 'patient_name', 'practitioner', 'employee', 'status', 'invoiced', 'result_date', 'department'], + filters=conditions, + order_by='submitted_date desc' + ) + return data def get_conditions(filters): - conditions = "" + conditions = { + 'docstatus': ('=', 1) + } - if filters.get("patient"): - conditions += "and patient = %(patient)s" - if filters.get("from_date"): - conditions += "and result_date >= %(from_date)s" - if filters.get("to_date"): - conditions += " and result_date <= %(to_date)s" - if filters.get("department"): - conditions += " and department = %(department)s" + if filters.get('from_date') and filters.get('to_date'): + conditions['result_date'] = ('between', (filters.get('from_date'), filters.get('to_date'))) + filters.pop('from_date') + filters.pop('to_date') - return conditions + for key, value in filters.items(): + if filters.get(key): + conditions[key] = value -def get_lab_test(filters): - conditions = get_conditions(filters) - return frappe.db.sql("""select name, patient, lab_test_name, patient_name, status, result_date, practitioner, invoiced, department - from `tabLab Test` - where docstatus<2 %s order by submitted_date desc, name desc""" % - conditions, filters, as_dict=1) + return conditions \ No newline at end of file From b6675f8dd4f3c70bfbe8d1cedbc5590809041dc7 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 30 Jul 2020 19:33:08 +0530 Subject: [PATCH 13/60] feat: Lab Test Report Summary and Chart --- .../report/lab_test_report/lab_test_report.py | 178 ++++++++++++------ 1 file changed, 124 insertions(+), 54 deletions(-) diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.py b/erpnext/healthcare/report/lab_test_report/lab_test_report.py index be2d06193e..2e59bed037 100644 --- a/erpnext/healthcare/report/lab_test_report/lab_test_report.py +++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.py @@ -14,7 +14,7 @@ def execute(filters=None): lab_test_list = get_lab_tests(filters) if not lab_test_list: - msgprint(_("No records found")) + msgprint(_('No records found')) return columns, lab_test_list data = [] @@ -34,83 +34,85 @@ def execute(filters=None): }) data.append(row) - return columns, data + chart = get_chart_data(data) + report_summary = get_report_summary(data) + return columns, data, None, chart, report_summary def get_columns(): return [ { - "fieldname": "test", - "label": _("Lab Test"), - "fieldtype": "Link", - "options": "Lab Test", - "width": "120" + 'fieldname': 'test', + 'label': _('Lab Test'), + 'fieldtype': 'Link', + 'options': 'Lab Test', + 'width': '120' }, { - "fieldname": "template", - "label": _("Lab Test Template"), - "fieldtype": "Link", - "options": "Lab Test Template", - "width": "120" + 'fieldname': 'template', + 'label': _('Lab Test Template'), + 'fieldtype': 'Link', + 'options': 'Lab Test Template', + 'width': '120' }, { - "fieldname": "company", - "label": _("Company"), - "fieldtype": "Link", - "options": "Company", - "width": "120" + 'fieldname': 'company', + 'label': _('Company'), + 'fieldtype': 'Link', + 'options': 'Company', + 'width': '120' }, { - "fieldname": "patient", - "label": _("Patient"), - "fieldtype": "Link", - "options": "Patient", - "width": "120" + 'fieldname': 'patient', + 'label': _('Patient'), + 'fieldtype': 'Link', + 'options': 'Patient', + 'width': '120' }, { - "fieldname": "patient_name", - "label": _("Patient Name"), - "fieldtype": "Data", - "width": "120" + 'fieldname': 'patient_name', + 'label': _('Patient Name'), + 'fieldtype': 'Data', + 'width': '120' }, { - "fieldname": "practitioner", - "label": _("Requesting Practitioner"), - "fieldtype": "Link", - "options": "Healthcare Practitioner", - "width": "120" + 'fieldname': 'employee', + 'label': _('Lab Technician'), + 'fieldtype': 'Link', + 'options': 'Employee', + 'width': '120' }, { - "fieldname": "employee", - "label": _("Lab Technician"), - "fieldtype": "Link", - "options": "Employee", - "width": "120" + 'fieldname': 'status', + 'label': _('Status'), + 'fieldtype': 'Data', + 'width': '100' }, { - "fieldname": "status", - "label": _("Status"), - "fieldtype": "Data", - "width": "100" + 'fieldname': 'invoiced', + 'label': _('Invoiced'), + 'fieldtype': 'Check', + 'width': '100' }, { - "fieldname": "invoiced", - "label": _("Invoiced"), - "fieldtype": "Check", - "width": "100" + 'fieldname': 'result_date', + 'label': _('Result Date'), + 'fieldtype': 'Date', + 'width': '100' }, { - "fieldname": "result_date", - "label": _("Result Date"), - "fieldtype": "Date", - "width": "100" + 'fieldname': 'practitioner', + 'label': _('Requesting Practitioner'), + 'fieldtype': 'Link', + 'options': 'Healthcare Practitioner', + 'width': '120' }, { - "fieldname": "department", - "label": _("Medical Department"), - "fieldtype": "Link", - "options": "Medical Department", - "width": "100" + 'fieldname': 'department', + 'label': _('Medical Department'), + 'fieldtype': 'Link', + 'options': 'Medical Department', + 'width': '100' } ] @@ -138,4 +140,72 @@ def get_conditions(filters): if filters.get(key): conditions[key] = value - return conditions \ No newline at end of file + return conditions + +def get_chart_data(data): + if not data: + return None + + labels = ['Completed', 'Approved', 'Rejected'] + + status_wise_data = { + 'Completed': 0, + 'Approved': 0, + 'Rejected': 0 + } + + datasets = [] + + for entry in data: + status_wise_data[entry.status] += 1 + + datasets.append({ + 'name': 'Lab Test Status', + 'values': [status_wise_data.get('Completed'), status_wise_data.get('Approved'), status_wise_data.get('Rejected')] + }) + + chart = { + 'data': { + 'labels': labels, + 'datasets': datasets + }, + 'type': 'donut', + 'height': 300, + } + + return chart + + +def get_report_summary(data): + if not data: + return None + + total_lab_tests = len(data) + invoiced_lab_tests, unbilled_lab_tests = 0, 0 + + for entry in data: + if entry.invoiced: + invoiced_lab_tests += 1 + else: + unbilled_lab_tests += 1 + + return [ + { + 'value': total_lab_tests, + 'indicator': 'Blue', + 'label': 'Total Lab Tests', + 'datatype': 'Int', + }, + { + 'value': invoiced_lab_tests, + 'indicator': 'Green', + 'label': 'Invoiced Lab Tests', + 'datatype': 'Int', + }, + { + 'value': unbilled_lab_tests, + 'indicator': 'Red', + 'label': 'Unbilled Lab Tests', + 'datatype': 'Int', + } + ] From 821eeb9852bd586fc4b98fa9ab37c4075c787e73 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Sun, 9 Aug 2020 14:07:32 +0200 Subject: [PATCH 14/60] fix(membership): currency should be a link --- erpnext/non_profit/doctype/membership/membership.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/non_profit/doctype/membership/membership.json b/erpnext/non_profit/doctype/membership/membership.json index 238f4c31fd..b95ae9738c 100644 --- a/erpnext/non_profit/doctype/membership/membership.json +++ b/erpnext/non_profit/doctype/membership/membership.json @@ -90,9 +90,9 @@ }, { "fieldname": "currency", - "fieldtype": "Select", + "fieldtype": "Link", "label": "Currency", - "options": "USD\nINR" + "options": "Currency" }, { "fieldname": "amount", @@ -163,4 +163,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} From 698d983eefb6cb068b9fffaaa5bfac1718b1ce0b Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 19 Aug 2020 14:59:46 +0530 Subject: [PATCH 15/60] fix: Item Tax Updation via Update Items in SO/PO --- erpnext/controllers/accounts_controller.py | 16 +++++++++++++++- erpnext/controllers/taxes_and_totals.py | 12 ++++++++---- .../public/js/controllers/taxes_and_totals.js | 11 ++++++++--- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 3091193b8d..791b932695 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -19,7 +19,7 @@ from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_t from erpnext.exceptions import InvalidCurrency from six import text_type from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions -from erpnext.stock.get_item_details import get_item_warehouse +from erpnext.stock.get_item_details import get_item_warehouse, _get_item_tax_template, get_item_tax_map from erpnext.stock.doctype.packed_item.packed_item import make_packing_list force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules") @@ -1157,6 +1157,18 @@ def get_supplier_block_status(party_name): } return info +def set_child_tax_template_and_map(item, child_item, parent_doc): + args = { + 'item_code': item.item_code, + 'posting_date': parent_doc.transaction_date, + 'tax_category': parent_doc.get('tax_category'), + 'company': parent_doc.get('company') + } + + child_item.item_tax_template = _get_item_tax_template(args, item.taxes) + if child_item.get("item_tax_template"): + child_item.item_tax_rate = get_item_tax_map(parent_doc.get('company'), child_item.item_tax_template, as_json=True) + def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, trans_item): """ Returns a Sales Order Item child item containing the default values @@ -1170,6 +1182,7 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 child_item.uom = item.stock_uom + set_child_tax_template_and_map(item, child_item, p_doc) child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") @@ -1192,6 +1205,7 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.uom = item.stock_uom child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation + set_child_tax_template_and_map(item, child_item, p_doc) return child_item def check_and_delete_children(parent, data): diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 2a14be8532..59c60f7fdc 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -209,7 +209,7 @@ class calculate_taxes_and_totals(object): elif tax.charge_type == "On Previous Row Total": current_tax_fraction = (tax_rate / 100.0) * \ self.doc.get("taxes")[cint(tax.row_id) - 1].grand_total_fraction_for_current_item - + elif tax.charge_type == "On Item Quantity": inclusive_tax_amount_per_qty = flt(tax_rate) @@ -220,10 +220,14 @@ class calculate_taxes_and_totals(object): return current_tax_fraction, inclusive_tax_amount_per_qty def _get_tax_rate(self, tax, item_tax_map): - if tax.account_head in item_tax_map: - return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax)) + if item_tax_map: + if tax.account_head in item_tax_map: + return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax)) + else: + return tax.rate else: - return tax.rate + # If no item tax template against item dont calculate tax against it + return 0 def calculate_net_total(self): self.doc.total_qty = self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0 diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 6951539026..9659124960 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -223,8 +223,13 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }, _get_tax_rate: function(tax, item_tax_map) { - return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ? - flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; + if(!$.isEmptyObject(item_tax_map)) { + return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ? + flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; + } else { + // If no item tax template against item dont calculate tax against it + return 0 + } }, calculate_net_total: function() { @@ -595,7 +600,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ $.each(actual_taxes_dict, function(key, value) { if (value) total_actual_tax += value; }); - + return flt(this.frm.doc.grand_total - total_actual_tax, precision("grand_total")); } }, From 22d3729427671e5d412c5634163413ae0ba507c1 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 19 Aug 2020 15:42:40 +0530 Subject: [PATCH 16/60] fix: Codacy --- erpnext/public/js/controllers/taxes_and_totals.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 9659124960..87f6d6a947 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -228,7 +228,7 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; } else { // If no item tax template against item dont calculate tax against it - return 0 + return 0; } }, From 41f421d4003fb80c62f2dc872b266a543e1b87e9 Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Wed, 2 Sep 2020 13:47:18 +0530 Subject: [PATCH 17/60] feat: added option to add custom remarks in payment entry --- .../doctype/payment_entry/payment_entry.json | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index 997937738b..72149a665d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2016-06-01 14:38:51.012597", @@ -63,6 +64,7 @@ "cost_center", "section_break_12", "status", + "custom_remarks", "remarks", "column_break_16", "letter_head", @@ -462,7 +464,8 @@ "fieldname": "remarks", "fieldtype": "Small Text", "label": "Remarks", - "no_copy": 1 + "no_copy": 1, + "read_only_depends_on": "eval:doc.custom_remarks == 0" }, { "fieldname": "column_break_16", @@ -573,10 +576,18 @@ "label": "Status", "options": "\nDraft\nSubmitted\nCancelled", "read_only": 1 + }, + { + "default": "0", + "fieldname": "custom_remarks", + "fieldtype": "Check", + "label": "Custom Remarks" } ], + "index_web_pages_for_search": 1, "is_submittable": 1, - "modified": "2019-12-08 13:02:30.016610", + "links": [], + "modified": "2020-09-02 13:39:43.383705", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", From fa98e81f0b4041e9496cb7f90c852203e7d39355 Mon Sep 17 00:00:00 2001 From: barry86m <65860880+barry86m@users.noreply.github.com> Date: Mon, 7 Sep 2020 01:36:15 +0100 Subject: [PATCH 18/60] fix: sql error on saving sales invoice without a sales order --- erpnext/controllers/selling_controller.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 17f3ae53e7..ad06f97b7d 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -81,6 +81,7 @@ class SellingController(StockController): party_details = _get_party_details(customer, ignore_permissions=self.flags.ignore_permissions, doctype=self.doctype, company=self.company, + posting_date=self.posting_date if hasattr(self, 'posting_date') else None, fetch_payment_terms_template=fetch_payment_terms_template, party_address=self.customer_address, shipping_address=self.shipping_address_name) if not self.meta.get_field("sales_team"): From 06f89a1b94e98aeaa76c4745914a676d8654bb16 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 7 Sep 2020 19:46:53 +0530 Subject: [PATCH 19/60] fix: Set student email id mandatory only if skip user creation is disabled --- erpnext/education/doctype/student/student.js | 20 +- .../education/doctype/student/student.json | 3 +- .../student_applicant/student_applicant.js | 13 +- .../student_applicant/student_applicant.json | 1437 +++-------------- 4 files changed, 272 insertions(+), 1201 deletions(-) diff --git a/erpnext/education/doctype/student/student.js b/erpnext/education/doctype/student/student.js index 1936dcbd3e..9272d35543 100644 --- a/erpnext/education/doctype/student/student.js +++ b/erpnext/education/doctype/student/student.js @@ -3,15 +3,15 @@ frappe.ui.form.on('Student', { setup: function(frm) { - frm.add_fetch("guardian", "guardian_name", "guardian_name"); - frm.add_fetch("student", "title", "full_name"); - frm.add_fetch("student", "gender", "gender"); - frm.add_fetch("student", "date_of_birth", "date_of_birth"); + frm.add_fetch('guardian', 'guardian_name', 'guardian_name'); + frm.add_fetch('student', 'title', 'full_name'); + frm.add_fetch('student', 'gender', 'gender'); + frm.add_fetch('student', 'date_of_birth', 'date_of_birth'); - frm.set_query("student", "siblings", function(doc, cdt, cdn) { + frm.set_query('student', 'siblings', function(doc, cdt, cdn) { return { - "filters": { - "name": ["!=", doc.name] + 'filters': { + 'name': ['!=', doc.name] } }; }) @@ -25,6 +25,12 @@ frappe.ui.form.on('Student', { {party_type:'Student', party:frm.doc.name}); }); } + + frappe.db.get_value('Education Settings', {name: 'Education Settings'}, 'user_creation_skip', (r) => { + if (r.user_creation_skip === "0") { + frm.set_df_property('student_email_id', 'reqd', 1); + } + }); } }); diff --git a/erpnext/education/doctype/student/student.json b/erpnext/education/doctype/student/student.json index ac65c0cd7b..8ba9a7fa11 100644 --- a/erpnext/education/doctype/student/student.json +++ b/erpnext/education/doctype/student/student.json @@ -102,7 +102,6 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Student Email Address", - "reqd": 1, "unique": 1 }, { @@ -255,7 +254,7 @@ ], "image_field": "image", "links": [], - "modified": "2020-07-23 18:14:06.366442", + "modified": "2020-09-07 19:28:08.914568", "modified_by": "Administrator", "module": "Education", "name": "Student", diff --git a/erpnext/education/doctype/student_applicant/student_applicant.js b/erpnext/education/doctype/student_applicant/student_applicant.js index 83621c5725..27b3377267 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.js +++ b/erpnext/education/doctype/student_applicant/student_applicant.js @@ -7,7 +7,7 @@ frappe.ui.form.on("Student Applicant", { }, refresh: function(frm) { - if(frm.doc.application_status== "Applied" && frm.doc.docstatus== 1 ) { + if (frm.doc.application_status==="Applied" && frm.doc.docstatus===1 ) { frm.add_custom_button(__("Approve"), function() { frm.set_value("application_status", "Approved"); frm.save_or_update(); @@ -20,10 +20,11 @@ frappe.ui.form.on("Student Applicant", { }, 'Actions'); } - if(frm.doc.application_status== "Approved" && frm.doc.docstatus== 1 ) { + if (frm.doc.application_status === "Approved" && frm.doc.docstatus === 1) { frm.add_custom_button(__("Enroll"), function() { frm.events.enroll(frm) }).addClass("btn-primary"); + frm.add_custom_button(__("Reject"), function() { frm.set_value("application_status", "Rejected"); frm.save_or_update(); @@ -35,7 +36,13 @@ frappe.ui.form.on("Student Applicant", { frappe.hide_msgprint(true); frappe.show_progress(__("Enrolling student"), data.progress[0],data.progress[1]); } - }) + }); + + frappe.db.get_value("Education Settings", {name: "Education Settings"}, "user_creation_skip", (r) => { + if (r.user_creation_skip === "0") { + frm.set_df_property("student_email_id", "reqd", 1); + } + }); }, enroll: function(frm) { diff --git a/erpnext/education/doctype/student_applicant/student_applicant.json b/erpnext/education/doctype/student_applicant/student_applicant.json index e5d0bd37de..bca38fb264 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.json +++ b/erpnext/education/doctype/student_applicant/student_applicant.json @@ -1,1233 +1,292 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2015-09-11 11:50:09.740807", - "custom": 0, - "default_print_format": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2015-09-11 11:50:09.740807", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "first_name", + "middle_name", + "last_name", + "program", + "lms_only", + "paid", + "column_break_8", + "naming_series", + "application_status", + "application_date", + "academic_year", + "academic_term", + "student_admission", + "image", + "section_break_4", + "date_of_birth", + "gender", + "blood_group", + "column_break_12", + "student_email_id", + "student_mobile_number", + "nationality", + "home_address", + "address_line_1", + "address_line_2", + "pincode", + "column_break_23", + "city", + "state", + "section_break_20", + "guardians", + "section_break_21", + "siblings", + "section_break_23", + "title", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "first_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": "First 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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "first_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "First Name", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "middle_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": "Middle Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "middle_name", + "fieldtype": "Data", + "label": "Middle Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "last_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": "Last Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "last_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Last Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "program", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Program", - "length": 0, - "no_copy": 0, - "options": "Program", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "program", + "fieldtype": "Link", + "in_filter": 1, + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Program", + "options": "Program", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "lms_only", - "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": "LMS Only", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "lms_only", + "fieldtype": "Check", + "label": "LMS Only" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "paid", - "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": "Paid", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "paid", + "fieldtype": "Check", + "label": "Paid" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "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": "Naming Series", - "length": 0, - "no_copy": 1, - "options": "EDU-APP-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "no_copy": 1, + "options": "EDU-APP-.YYYY.-", + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.docstatus != 0", - "fieldname": "application_status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Application Status", - "length": 0, - "no_copy": 1, - "options": "Applied\nApproved\nRejected\nAdmitted", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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_on_submit": 1, + "depends_on": "eval:doc.docstatus != 0", + "fieldname": "application_status", + "fieldtype": "Select", + "in_filter": 1, + "label": "Application Status", + "no_copy": 1, + "options": "Applied\nApproved\nRejected\nAdmitted" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "application_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": "Application Date", - "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 - }, + "default": "Today", + "fieldname": "application_date", + "fieldtype": "Date", + "label": "Application Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "academic_year", - "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": "Academic Year", - "length": 0, - "no_copy": 0, - "options": "Academic Year", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "academic_year", + "fieldtype": "Link", + "label": "Academic Year", + "options": "Academic Year" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "academic_term", - "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": "Academic Term", - "length": 0, - "no_copy": 0, - "options": "Academic Term", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "academic_term", + "fieldtype": "Link", + "label": "Academic Term", + "options": "Academic Term" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "student_admission", - "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": "Student Admission", - "length": 0, - "no_copy": 0, - "options": "Student Admission", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "student_admission", + "fieldtype": "Link", + "label": "Student Admission", + "options": "Student Admission" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "image", - "fieldtype": "Attach Image", - "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": "Image", - "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": "image", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Image" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "fieldname": "section_break_4", - "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": "Personal Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_4", + "fieldtype": "Section Break", + "label": "Personal Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "date_of_birth", - "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": "Date of Birth", - "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": "date_of_birth", + "fieldtype": "Date", + "label": "Date of Birth" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "gender", - "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": "Gender", - "length": 0, - "no_copy": 0, - "options": "\nMale\nFemale", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "gender", + "fieldtype": "Select", + "label": "Gender", + "options": "\nMale\nFemale" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "blood_group", - "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": "Blood Group", - "length": 0, - "no_copy": 0, - "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "blood_group", + "fieldtype": "Select", + "label": "Blood Group", + "options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "student_email_id", - "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": "Student Email Address", - "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, + "fieldname": "student_email_id", + "fieldtype": "Data", + "label": "Student Email Address", "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "student_mobile_number", - "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": "Student Mobile Number", - "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": "student_mobile_number", + "fieldtype": "Data", + "label": "Student Mobile Number" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "nationality", - "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": "Nationality", - "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 - }, + "fieldname": "nationality", + "fieldtype": "Data", + "label": "Nationality" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "home_address", - "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": "Home Address", - "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": "home_address", + "fieldtype": "Section Break", + "label": "Home Address" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_line_1", - "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": "Address Line 1", - "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": "address_line_1", + "fieldtype": "Data", + "label": "Address Line 1" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_line_2", - "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": "Address Line 2", - "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": "address_line_2", + "fieldtype": "Data", + "label": "Address Line 2" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pincode", - "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": "Pincode", - "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": "pincode", + "fieldtype": "Data", + "label": "Pincode" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_23", - "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_23", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "city", - "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": "City", - "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": "city", + "fieldtype": "Data", + "label": "City" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "state", - "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": "State", - "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": "state", + "fieldtype": "Data", + "label": "State" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "section_break_20", - "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": "Guardian Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "section_break_20", + "fieldtype": "Section Break", + "label": "Guardian Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "guardians", - "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": "Guardians", - "length": 0, - "no_copy": 0, - "options": "Student Guardian", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "guardians", + "fieldtype": "Table", + "label": "Guardians", + "options": "Student Guardian" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "section_break_21", - "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": "Sibling Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "section_break_21", + "fieldtype": "Section Break", + "label": "Sibling Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "siblings", - "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": "Siblings", - "length": 0, - "no_copy": 0, - "options": "Student Sibling", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "siblings", + "fieldtype": "Table", + "label": "Siblings", + "options": "Student Sibling" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_23", - "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_23", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title" + }, { - "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": "Student Applicant", - "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", + "label": "Amended From", + "no_copy": 1, + "options": "Student Applicant", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_field": "image", - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2018-10-05 13:15:59.283862", - "modified_by": "Administrator", - "module": "Education", - "name": "Student Applicant", - "name_case": "", - "owner": "Administrator", + ], + "image_field": "image", + "is_submittable": 1, + "links": [], + "modified": "2020-09-07 19:31:30.063563", + "modified_by": "Administrator", + "module": "Education", + "name": "Student Applicant", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Academics User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Academics User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} + ], + "restrict_to_domain": "Education", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "title" +} \ No newline at end of file From 4673bed67ed79e6e95f39da67dc547b2cfd4a0f3 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 7 Sep 2020 20:12:29 +0530 Subject: [PATCH 20/60] fix: codacy issues --- erpnext/education/doctype/student/student.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/education/doctype/student/student.js b/erpnext/education/doctype/student/student.js index 9272d35543..5b05bc4f9a 100644 --- a/erpnext/education/doctype/student/student.js +++ b/erpnext/education/doctype/student/student.js @@ -8,7 +8,7 @@ frappe.ui.form.on('Student', { frm.add_fetch('student', 'gender', 'gender'); frm.add_fetch('student', 'date_of_birth', 'date_of_birth'); - frm.set_query('student', 'siblings', function(doc, cdt, cdn) { + frm.set_query('student', 'siblings', function(doc) { return { 'filters': { 'name': ['!=', doc.name] @@ -27,7 +27,7 @@ frappe.ui.form.on('Student', { } frappe.db.get_value('Education Settings', {name: 'Education Settings'}, 'user_creation_skip', (r) => { - if (r.user_creation_skip === "0") { + if (r.user_creation_skip === "0") { frm.set_df_property('student_email_id', 'reqd', 1); } }); From 1915a608348d89f8dac040a8c81832fb502723dc Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 10 Sep 2020 12:13:58 +0530 Subject: [PATCH 21/60] fix: education settings check --- erpnext/education/doctype/student/student.js | 2 +- .../education/doctype/student_applicant/student_applicant.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/education/doctype/student/student.js b/erpnext/education/doctype/student/student.js index 5b05bc4f9a..fd23ae41ef 100644 --- a/erpnext/education/doctype/student/student.js +++ b/erpnext/education/doctype/student/student.js @@ -27,7 +27,7 @@ frappe.ui.form.on('Student', { } frappe.db.get_value('Education Settings', {name: 'Education Settings'}, 'user_creation_skip', (r) => { - if (r.user_creation_skip === "0") { + if (cint(r.user_creation_skip) !== 1) { frm.set_df_property('student_email_id', 'reqd', 1); } }); diff --git a/erpnext/education/doctype/student_applicant/student_applicant.js b/erpnext/education/doctype/student_applicant/student_applicant.js index 27b3377267..b4cfdf16e0 100644 --- a/erpnext/education/doctype/student_applicant/student_applicant.js +++ b/erpnext/education/doctype/student_applicant/student_applicant.js @@ -39,7 +39,7 @@ frappe.ui.form.on("Student Applicant", { }); frappe.db.get_value("Education Settings", {name: "Education Settings"}, "user_creation_skip", (r) => { - if (r.user_creation_skip === "0") { + if (cint(r.user_creation_skip) !== 1) { frm.set_df_property("student_email_id", "reqd", 1); } }); From c0f9d1b65a46b92d97e76a4f0a3341302bc252f6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 10 Sep 2020 17:33:52 +0530 Subject: [PATCH 22/60] test: Lab Module --- .../doctype/lab_test/test_lab_test.py | 202 +++++++++++++++++- .../test_patient_medical_record.py | 6 +- 2 files changed, 202 insertions(+), 6 deletions(-) diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.py b/erpnext/healthcare/doctype/lab_test/test_lab_test.py index 4131ad99b7..d949c46d16 100644 --- a/erpnext/healthcare/doctype/lab_test/test_lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/test_lab_test.py @@ -3,8 +3,204 @@ # See license.txt from __future__ import unicode_literals import unittest - -# test_records = frappe.get_test_records('Lab Test') +import frappe +from frappe.utils import getdate, nowtime +from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient +from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple +from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account +from erpnext.healthcare.doctype.patient_medical_record.test_patient_medical_record import create_lab_test_template as create_blood_test_template class TestLabTest(unittest.TestCase): - pass + def test_lab_test_item(self): + lab_template = create_lab_test_template() + self.assertTrue(frappe.db.exists('Item', lab_template.item)) + self.assertEqual(frappe.db.get_value('Item Price', {'item_code':lab_template.item}, 'price_list_rate'), lab_template.lab_test_rate) + + lab_template.disabled = 1 + lab_template.save() + self.assertEquals(frappe.db.get_value('Item', lab_template.item, 'disabled'), 1) + + lab_template.reload() + + lab_template.disabled = 0 + lab_template.save() + + def test_descriptive_lab_test(self): + lab_template = create_lab_test_template() + + # blank result value not allowed as per template + lab_test = create_lab_test(lab_template) + lab_test.descriptive_test_items[0].result_value = 12 + lab_test.descriptive_test_items[2].result_value = 1 + lab_test.save() + self.assertRaises(frappe.ValidationError, lab_test.submit) + + def test_sample_collection(self): + frappe.db.set_value('Healthcare Settings', 'Healthcare Settings', 'create_sample_collection_for_lab_test', 1) + lab_template = create_lab_test_template() + + lab_test = create_lab_test(lab_template) + lab_test.descriptive_test_items[0].result_value = 12 + lab_test.descriptive_test_items[1].result_value = 1 + lab_test.descriptive_test_items[2].result_value = 2.3 + lab_test.save() + + # check sample collection created + self.assertTrue(frappe.db.exists('Sample Collection', {'sample': lab_template.sample})) + + frappe.db.set_value('Healthcare Settings', 'Healthcare Settings', 'create_sample_collection_for_lab_test', 0) + lab_test = create_lab_test(lab_template) + lab_test.descriptive_test_items[0].result_value = 12 + lab_test.descriptive_test_items[1].result_value = 1 + lab_test.descriptive_test_items[2].result_value = 2.3 + lab_test.save() + + # sample collection should not be created + lab_test.reload() + self.assertEquals(lab_test.sample, None) + + def test_create_lab_tests_from_sales_invoice(self): + sales_invoice = create_sales_invoice() + create_multiple('Sales Invoice', sales_invoice.name) + sales_invoice.reload() + self.assertIsNotNone(sales_invoice.items[0].reference_dn) + self.assertIsNotNone(sales_invoice.items[1].reference_dn) + + def test_create_lab_tests_from_patient_encounter(self): + patient_encounter = create_patient_encounter() + create_multiple('Patient Encounter', patient_encounter.name) + patient_encounter.reload() + self.assertTrue(patient_encounter.lab_test_prescription[0].lab_test_created) + self.assertTrue(patient_encounter.lab_test_prescription[0].lab_test_created) + + +def create_lab_test_template(test_sensitivity=0, sample_collection=1): + medical_department = create_medical_department() + if frappe.db.exists('Lab Test Template', 'Insulin Resistance'): + return frappe.get_doc('Lab Test Template', 'Insulin Resistance') + template = frappe.new_doc('Lab Test Template') + template.lab_test_name = 'Insulin Resistance' + template.lab_test_template_type = 'Descriptive' + template.lab_test_code = 'Insulin Resistance' + template.lab_test_group = 'Services' + template.department = medical_department + template.is_billable = 1 + template.lab_test_description = 'Insulin Resistance' + template.lab_test_rate = 2000 + + for entry in ['FBS', 'Insulin', 'IR']: + template.append('descriptive_test_templates', { + 'particulars': entry, + 'allow_blank': 1 if entry=='IR' else 0 + }) + + if test_sensitivity: + template.sensitivity = 1 + + if sample_collection: + template.sample = create_lab_test_sample() + template.sample_qty = 5.0 + + template.save() + return template + +def create_medical_department(): + medical_department = frappe.db.exists('Medical Department', '_Test Medical Department') + if not medical_department: + medical_department = frappe.new_doc('Medical Department') + medical_department.department = '_Test Medical Department' + medical_department.save() + medical_department = medical_department.name + + return medical_department + +def create_lab_test(lab_template): + patient = create_patient() + lab_test = frappe.new_doc('Lab Test') + lab_test.template = lab_template.name + lab_test.patient = patient + lab_test.patient_sex = 'Female' + lab_test.save() + + return lab_test + +def create_lab_test_sample(): + blood_sample = frappe.db.exists('Lab Test Sample', 'Blood Sample') + if blood_sample: + return blood_sample + + sample = frappe.new_doc('Lab Test Sample') + sample.sample = 'Blood Sample' + sample.sample_uom = 'U/ml' + sample.save() + + return sample.name + +def create_sales_invoice(): + patient = create_patient() + medical_department = create_medical_department() + insulin_resistance_template = create_lab_test_template() + blood_test_template = create_blood_test_template(medical_department) + + sales_invoice = frappe.new_doc('Sales Invoice') + sales_invoice.patient = patient + sales_invoice.customer = frappe.db.get_value('Patient', patient, 'customer') + sales_invoice.due_date = getdate() + sales_invoice.company = '_Test Company' + sales_invoice.debit_to = get_receivable_account('_Test Company') + + tests = [insulin_resistance_template, blood_test_template] + for entry in tests: + item_line = sales_invoice.append('items', { + 'item_code': entry.item, + 'item_name': entry.lab_test_name, + 'description': entry.lab_test_description, + 'qty': 1, + 'uom': 'Nos', + 'conversion_factor': 1, + 'income_account': get_income_account(None, '_Test Company'), + 'rate': entry.lab_test_rate, + 'amount': entry.lab_test_rate + }) + + sales_invoice.set_missing_values() + + sales_invoice.submit() + return sales_invoice + +def create_patient_encounter(): + patient = create_patient() + medical_department = create_medical_department() + insulin_resistance_template = create_lab_test_template() + blood_test_template = create_blood_test_template(medical_department) + + patient_encounter = frappe.new_doc('Patient Encounter') + patient_encounter.patient = patient + patient_encounter.practitioner = create_practitioner() + patient_encounter.encounter_date = getdate() + patient_encounter.encounter_time = nowtime() + + tests = [insulin_resistance_template, blood_test_template] + for entry in tests: + patient_encounter.append('lab_test_prescription', { + 'lab_test_code': entry.item, + 'lab_test_name': entry.lab_test_name + }) + + patient_encounter.submit() + return patient_encounter + + +def create_practitioner(): + practitioner = frappe.db.exists('Healthcare Practitioner', '_Test Healthcare Practitioner') + + if not practitioner: + practitioner = frappe.new_doc('Healthcare Practitioner') + practitioner.first_name = '_Test Healthcare Practitioner' + practitioner.gender = 'Female' + practitioner.op_consulting_charge = 500 + practitioner.inpatient_visit_charge = 500 + practitioner.save(ignore_permissions=True) + practitioner = practitioner.name + + return practitioner diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py index e5a5e4c010..aa85a23113 100644 --- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py +++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py @@ -34,7 +34,7 @@ class TestPatientMedicalRecord(unittest.TestCase): self.assertTrue(medical_rec) template = create_lab_test_template(medical_department) - lab_test = create_lab_test(template, patient) + lab_test = create_lab_test(template.name, patient) # check for lab test medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name}) self.assertTrue(medical_rec) @@ -66,7 +66,7 @@ def create_vital_signs(appointment): def create_lab_test_template(medical_department): if frappe.db.exists('Lab Test Template', 'Blood Test'): - return 'Blood Test' + return frappe.get_doc('Lab Test Template', 'Blood Test') template = frappe.new_doc('Lab Test Template') template.lab_test_name = 'Blood Test' @@ -76,7 +76,7 @@ def create_lab_test_template(medical_department): template.is_billable = 1 template.lab_test_rate = 2000 template.save() - return template.name + return template def create_lab_test(template, patient): lab_test = frappe.new_doc('Lab Test') From a29436dc91f75abe619d53d6b53689faef109a07 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 10 Sep 2020 17:45:17 +0530 Subject: [PATCH 23/60] fix: codacy --- erpnext/healthcare/doctype/lab_test/test_lab_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.py b/erpnext/healthcare/doctype/lab_test/test_lab_test.py index d949c46d16..79ab8a4d7f 100644 --- a/erpnext/healthcare/doctype/lab_test/test_lab_test.py +++ b/erpnext/healthcare/doctype/lab_test/test_lab_test.py @@ -151,7 +151,7 @@ def create_sales_invoice(): tests = [insulin_resistance_template, blood_test_template] for entry in tests: - item_line = sales_invoice.append('items', { + sales_invoice.append('items', { 'item_code': entry.item, 'item_name': entry.lab_test_name, 'description': entry.lab_test_description, From ed08e593c37c6cb213007f32578cfe8db9621a77 Mon Sep 17 00:00:00 2001 From: Anupam K Date: Fri, 11 Sep 2020 13:59:07 +0530 Subject: [PATCH 24/60] feat: added project in Sales Analytics report --- .../report/sales_analytics/sales_analytics.js | 2 +- .../report/sales_analytics/sales_analytics.py | 28 ++++++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js index 80874c1deb..0e565a3fb6 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","Order Type"], + options: ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Order Type", "Project"], 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 4d113c8e9e..dbaf2acab9 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -34,7 +34,7 @@ class Analytics(object): def get_columns(self): self.columns = [{ - "label": _(self.filters.tree_type + " ID"), + "label": _(self.filters.tree_type), "options": self.filters.tree_type if self.filters.tree_type != "Order Type" else "", "fieldname": "entity", "fieldtype": "Link" if self.filters.tree_type != "Order Type" else "Data", @@ -97,6 +97,10 @@ class Analytics(object): self.get_sales_transactions_based_on_order_type() self.get_rows_by_group() + elif self.filters.tree_type == "Project": + self.get_sales_transactions_based_on_project() + self.get_rows() + def get_sales_transactions_based_on_order_type(self): if self.filters["value_quantity"] == 'Value': value_field = "base_net_total" @@ -198,6 +202,28 @@ class Analytics(object): self.get_groups() + def get_sales_transactions_based_on_project(self): + if self.filters["value_quantity"] == 'Value': + value_field = "base_net_total as value_field" + else: + value_field = "total_qty as value_field" + + entity = "project as entity" + + self.entries = frappe.get_all(self.filters.doc_type, + fields=[entity, value_field, self.date_field], + filters={ + "docstatus": 1, + "company": self.filters.company, + "project": ["!=", ""], + self.date_field: ('between', [self.filters.from_date, self.filters.to_date]) + }, debug =True + ) + + self.entity_names = {} + for d in self.entries: + self.entity_names.setdefault(d.entity, d.entity_name) + def get_rows(self): self.data = [] self.get_periodic_data() From 16f1435d630eff379ff809b975bec430fccc6c7e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 11 Sep 2020 18:57:58 +0200 Subject: [PATCH 25/60] fix: codacy --- erpnext/regional/germany/utils/datev/datev_csv.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py index df95a5b95a..aae734f8e2 100644 --- a/erpnext/regional/germany/utils/datev/datev_csv.py +++ b/erpnext/regional/germany/utils/datev/datev_csv.py @@ -3,18 +3,19 @@ from __future__ import unicode_literals import datetime import zipfile +from csv import QUOTE_NONNUMERIC +from six import BytesIO + import six import frappe import pandas as pd from frappe import _ -from csv import QUOTE_NONNUMERIC -from six import BytesIO from .datev_constants import DataCategory -from .datev_constants import Transactions def get_datev_csv(data, filters, csv_class): - """Fill in missing columns and return a CSV in DATEV Format. + """ + Fill in missing columns and return a CSV in DATEV Format. For automatic processing, DATEV requires the first line of the CSV file to hold meta data such as the length of account numbers oder the category of @@ -89,7 +90,7 @@ def get_header(filters, csv_class): # Format version (regarding format name) csv_class.FORMAT_VERSION, # Generated on - datetime.datetime.now().strftime("%Y%m%d%H%M%S") + '000', + datetime.datetime.now().strftime('%Y%m%d%H%M%S') + '000', # Imported on -- stays empty '', # Origin. Any two symbols, will be replaced by "SV" on import. @@ -155,7 +156,8 @@ def get_header(filters, csv_class): def download_csv_files_as_zip(csv_data_list): - """Put CSV files in a zip archive and send that to the client. + """ + Put CSV files in a zip archive and send that to the client. Params: csv_data_list -- list of dicts [{'file_name': 'EXTF_Buchunsstapel.zip', 'csv_data': get_datev_csv()}] From a91225f1522a877ab68fd95ac4a33831555cba06 Mon Sep 17 00:00:00 2001 From: Kwabena Adu-Darkwa Date: Fri, 11 Sep 2020 17:47:32 +0000 Subject: [PATCH 26/60] inventory management link updated --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 80ebdb6b2a..0f6a52142b 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ ERPNext as a monolith includes the following areas for managing businesses: 1. [Accounting](https://erpnext.com/open-source-accounting) -1. [Inventory](https://erpnext.com/distribution/inventory-management-system) +1. [Warehouse Management](https://erpnext.com/distribution/warehouse-management-system) 1. [CRM](https://erpnext.com/open-source-crm) 1. [Sales](https://erpnext.com/open-source-sales-purchase) 1. [Purchase](https://erpnext.com/open-source-sales-purchase) From 0a688005be30440ad0be86ca80911b3807281f33 Mon Sep 17 00:00:00 2001 From: marination Date: Sat, 12 Sep 2020 15:39:27 +0530 Subject: [PATCH 27/60] fix: Make sure Supplier/Customer is selected before fetching Items. --- erpnext/stock/doctype/delivery_note/delivery_note.js | 8 +++++++- .../stock/doctype/purchase_receipt/purchase_receipt.js | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 60f6a68f49..19d0bec26c 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -130,12 +130,18 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend( if (this.frm.doc.docstatus===0) { this.frm.add_custom_button(__('Sales Order'), function() { + if (!me.frm.doc.customer) { + frappe.throw({ + title: __("Mandatory"), + message: __("Please Select a Customer") + }); + } erpnext.utils.map_current_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note", source_doctype: "Sales Order", target: me.frm, setters: { - customer: me.frm.doc.customer || undefined, + customer: me.frm.doc.customer, }, get_query_filters: { docstatus: 1, diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 50c18f6282..c504e23677 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -116,12 +116,18 @@ erpnext.stock.PurchaseReceiptController = erpnext.buying.BuyingController.extend if (this.frm.doc.docstatus == 0) { this.frm.add_custom_button(__('Purchase Order'), function () { + if (!me.frm.doc.supplier) { + frappe.throw({ + title: __("Mandatory"), + message: __("Please Select a Supplier") + }); + } erpnext.utils.map_current_doc({ method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt", source_doctype: "Purchase Order", target: me.frm, setters: { - supplier: me.frm.doc.supplier || undefined, + supplier: me.frm.doc.supplier, }, get_query_filters: { docstatus: 1, From 9205101ecffbfa358fcd9a0eefa2a297c3da29ce Mon Sep 17 00:00:00 2001 From: marination Date: Sat, 12 Sep 2020 16:17:13 +0530 Subject: [PATCH 28/60] fix: Make Reference fields mandatory in Quality Inspection --- .../doctype/quality_inspection/quality_inspection.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index c951066aa8..3643174fb4 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -73,7 +73,8 @@ "fieldname": "reference_type", "fieldtype": "Select", "label": "Reference Type", - "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry" + "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry", + "reqd": 1 }, { "fieldname": "reference_name", @@ -84,7 +85,8 @@ "label": "Reference Name", "oldfieldname": "purchase_receipt_no", "oldfieldtype": "Link", - "options": "reference_type" + "options": "reference_type", + "reqd": 1 }, { "fieldname": "section_break_7", @@ -231,9 +233,10 @@ ], "icon": "fa fa-search", "idx": 1, + "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-04-26 17:50:25.068222", + "modified": "2020-09-12 16:11:31.910508", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection", From 6db92fbb6a38b40818022c607a02ccf1f30e92f0 Mon Sep 17 00:00:00 2001 From: Saqib Date: Mon, 14 Sep 2020 19:54:17 +0530 Subject: [PATCH 29/60] feat: validate workflow before so/po update items (#23024) * feat: validate workflow before so/po update items * fix: incorrect workflow validation on so/po update items Co-authored-by: Marica --- erpnext/controllers/accounts_controller.py | 50 ++++++++++---- .../doctype/sales_order/test_sales_order.py | 69 +++++++++++++++++++ 2 files changed, 106 insertions(+), 13 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d61e44b53d..5244777558 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -7,6 +7,7 @@ import json from frappe import _, throw from frappe.utils import (today, flt, cint, fmt_money, formatdate, getdate, add_days, add_months, get_last_day, nowdate, get_link_to_form) +from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied, WorkflowPermissionError from erpnext.stock.get_item_details import get_conversion_factor, get_item_details from erpnext.setup.utils import get_exchange_rate from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency @@ -1194,7 +1195,7 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item -def check_and_delete_children(parent, data): +def validate_and_delete_children(parent, data): deleted_children = [] updated_item_names = [d.get("docname") for d in data] for item in parent.items: @@ -1221,18 +1222,37 @@ def check_and_delete_children(parent, data): @frappe.whitelist() def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): - def check_permissions(doc, perm_type='create'): + def check_doc_permissions(doc, perm_type='create'): try: doc.check_permission(perm_type) - except: - action = "add" if perm_type == 'create' else "update" - frappe.throw(_("You do not have permissions to {} items in a Sales Order.").format(action), title=_("Insufficient Permissions")) + except frappe.PermissionError: + actions = { 'create': 'add', 'write': 'update', 'cancel': 'remove' } + + frappe.throw(_("You do not have permissions to {} items in a {}.") + .format(actions[perm_type], parent_doctype), title=_("Insufficient Permissions")) + + def validate_workflow_conditions(doc): + workflow = get_workflow_name(doc.doctype) + if not workflow: + return + + workflow_doc = frappe.get_doc("Workflow", workflow) + current_state = doc.get(workflow_doc.workflow_state_field) + roles = frappe.get_roles() + + transitions = [] + for transition in workflow_doc.transitions: + if transition.next_state == current_state and transition.allowed in roles: + if not is_transition_condition_satisfied(transition, doc): + continue + transitions.append(transition.as_dict()) + + if not transitions: + frappe.throw(_("You do not have workflow access to update this document."), title=_("Insufficient Workflow Permissions")) def get_new_child_item(item_row): - if parent_doctype == "Sales Order": - return set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) - if parent_doctype == "Purchase Order": - return set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docname, item_row) + new_child_function = set_sales_order_defaults if parent_doctype == "Sales Order" else set_purchase_order_defaults + return new_child_function(parent_doctype, parent_doctype_name, child_docname, item_row) def validate_quantity(child_item, d): if parent_doctype == "Sales Order" and flt(d.get("qty")) < flt(child_item.delivered_qty): @@ -1245,17 +1265,18 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation'] parent = frappe.get_doc(parent_doctype, parent_doctype_name) - - check_and_delete_children(parent, data) + + check_doc_permissions(parent, 'cancel') + validate_and_delete_children(parent, data) for d in data: new_child_flag = False if not d.get("docname"): new_child_flag = True - check_permissions(parent, 'create') + check_doc_permissions(parent, 'create') child_item = get_new_child_item(d) else: - check_permissions(parent, 'write') + check_doc_permissions(parent, 'write') child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname")) prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate")) @@ -1362,6 +1383,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.update_prevdoc_status('submit') parent.update_delivery_status() + parent.reload() + validate_workflow_conditions(parent) + parent.update_blanket_order() parent.update_billing_percentage() parent.set_status() diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index accaa9c3b3..34aaf08395 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -417,7 +417,42 @@ class TestSalesOrder(unittest.TestCase): # add new item trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 100, 'qty' : 2}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) + test_user.remove_roles("Accounts User") frappe.set_user("Administrator") + + def test_update_child_qty_rate_with_workflow(self): + from frappe.model.workflow import apply_workflow + + workflow = make_sales_order_workflow() + so = make_sales_order(item_code= "_Test Item", qty=1, rate=150, do_not_submit=1) + apply_workflow(so, 'Approve') + + frappe.set_user("Administrator") + user = 'test@example.com' + test_user = frappe.get_doc('User', user) + test_user.add_roles("Sales User", "Test Junior Approver") + frappe.set_user(user) + + # user shouldn't be able to edit since grand_total will become > 200 if qty is doubled + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 150, 'qty' : 2, 'docname': so.items[0].name}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Sales Order', trans_item, so.name) + + frappe.set_user("Administrator") + user2 = 'test2@example.com' + test_user2 = frappe.get_doc('User', user2) + test_user2.add_roles("Sales User", "Test Approver") + frappe.set_user(user2) + + # Test Approver is allowed to edit with grand_total > 200 + update_child_qty_rate("Sales Order", trans_item, so.name) + so.reload() + self.assertEqual(so.items[0].qty, 2) + + frappe.set_user("Administrator") + test_user.remove_roles("Sales User", "Test Junior Approver", "Test Approver") + test_user2.remove_roles("Sales User", "Test Junior Approver", "Test Approver") + workflow.is_active = 0 + workflow.save() def test_update_child_qty_rate_product_bundle(self): # test Update Items with product bundle @@ -973,3 +1008,37 @@ def get_reserved_qty(item_code="_Test Item", warehouse="_Test Warehouse - _TC"): "reserved_qty")) test_dependencies = ["Currency Exchange"] + +def make_sales_order_workflow(): + if frappe.db.exists('Workflow', 'SO Test Workflow'): + doc = frappe.get_doc("Workflow", "SO Test Workflow") + doc.set("is_active", 1) + doc.save() + return doc + + frappe.get_doc(dict(doctype='Role', role_name='Test Junior Approver')).insert(ignore_if_duplicate=True) + frappe.get_doc(dict(doctype='Role', role_name='Test Approver')).insert(ignore_if_duplicate=True) + frappe.db.commit() + frappe.cache().hdel('roles', frappe.session.user) + + workflow = frappe.get_doc({ + "doctype": "Workflow", + "workflow_name": "SO Test Workflow", + "document_type": "Sales Order", + "workflow_state_field": "workflow_state", + "is_active": 1, + "send_email_alert": 0, + }) + workflow.append('states', dict( state = 'Pending', allow_edit = 'All' )) + workflow.append('states', dict( state = 'Approved', allow_edit = 'Test Approver', doc_status = 1 )) + workflow.append('transitions', dict( + state = 'Pending', action = 'Approve', next_state = 'Approved', allowed = 'Test Junior Approver', allow_self_approval = 1, + condition = 'doc.grand_total < 200' + )) + workflow.append('transitions', dict( + state = 'Pending', action = 'Approve', next_state = 'Approved', allowed = 'Test Approver', allow_self_approval = 1, + condition = 'doc.grand_total > 200' + )) + workflow.insert(ignore_permissions=True) + + return workflow \ No newline at end of file From fcb6ba6ec7fb245f201a22374d1c4a0c8227905e Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 14 Sep 2020 21:12:36 +0530 Subject: [PATCH 30/60] fix: get_transaction_entries function arguments in Bank Statement Transaction Entry (#23051) --- .../bank_statement_transaction_entry.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py index 5b6eb9dc24..27dd8e463f 100644 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py +++ b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py @@ -55,7 +55,7 @@ class BankStatementTransactionEntry(Document): def populate_payment_entries(self): if self.bank_statement is None: return - filename = self.bank_statement.split("/")[-1] + file_url = self.bank_statement if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0): frappe.throw(_("Transactions already retreived from the statement")) @@ -65,7 +65,7 @@ class BankStatementTransactionEntry(Document): if self.bank_settings: mapped_items = frappe.get_doc("Bank Statement Settings", self.bank_settings).mapped_items statement_headers = self.get_statement_headers() - transactions = get_transaction_entries(filename, statement_headers) + transactions = get_transaction_entries(file_url, statement_headers) for entry in transactions: date = entry[statement_headers["Date"]].strip() #print("Processing entry DESC:{0}-W:{1}-D:{2}-DT:{3}".format(entry["Particulars"], entry["Withdrawals"], entry["Deposits"], entry["Date"])) @@ -398,20 +398,21 @@ def get_transaction_info(headers, header_index, row): transaction[header] = "" return transaction -def get_transaction_entries(filename, headers): +def get_transaction_entries(file_url, headers): header_index = {} rows, transactions = [], [] - if (filename.lower().endswith("xlsx")): + if (file_url.lower().endswith("xlsx")): from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file - rows = read_xlsx_file_from_attached_file(file_id=filename) - elif (filename.lower().endswith("csv")): + rows = read_xlsx_file_from_attached_file(file_url=file_url) + elif (file_url.lower().endswith("csv")): from frappe.utils.csvutils import read_csv_content - _file = frappe.get_doc("File", {"file_name": filename}) + _file = frappe.get_doc("File", {"file_url": file_url}) filepath = _file.get_full_path() with open(filepath,'rb') as csvfile: rows = read_csv_content(csvfile.read()) - elif (filename.lower().endswith("xls")): + elif (file_url.lower().endswith("xls")): + filename = file_url.split("/")[-1] rows = get_rows_from_xls_file(filename) else: frappe.throw(_("Only .csv and .xlsx files are supported currently")) From b7cd84196d79f4a728a9a3aaf1504e62601aac5e Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 15 Sep 2020 09:42:51 +0530 Subject: [PATCH 31/60] fix: Revert tax calculation changes --- erpnext/controllers/taxes_and_totals.py | 10 +++------- erpnext/public/js/controllers/taxes_and_totals.js | 9 ++------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 59c60f7fdc..f578e7eac4 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -220,14 +220,10 @@ class calculate_taxes_and_totals(object): return current_tax_fraction, inclusive_tax_amount_per_qty def _get_tax_rate(self, tax, item_tax_map): - if item_tax_map: - if tax.account_head in item_tax_map: - return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax)) - else: - return tax.rate + if tax.account_head in item_tax_map: + return flt(item_tax_map.get(tax.account_head), self.doc.precision("rate", tax)) else: - # If no item tax template against item dont calculate tax against it - return 0 + return tax.rate def calculate_net_total(self): self.doc.total_qty = self.doc.total = self.doc.base_total = self.doc.net_total = self.doc.base_net_total = 0.0 diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 87f6d6a947..3bc70216db 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -223,13 +223,8 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ }, _get_tax_rate: function(tax, item_tax_map) { - if(!$.isEmptyObject(item_tax_map)) { - return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ? - flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; - } else { - // If no item tax template against item dont calculate tax against it - return 0; - } + return (Object.keys(item_tax_map).indexOf(tax.account_head) != -1) ? + flt(item_tax_map[tax.account_head], precision("rate", tax)) : tax.rate; }, calculate_net_total: function() { From 36bc0577a2a0c11fa10d669d76172cbb62a8ae90 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 15 Sep 2020 09:44:46 +0530 Subject: [PATCH 32/60] fix: Item GL entries for purchase invoice (#23027) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index b4ee7c999e..079f599706 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -405,8 +405,6 @@ class PurchaseInvoice(BuyingController): update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference) def make_gl_entries(self, gl_entries=None): - if not self.grand_total: - return if not gl_entries: gl_entries = self.get_gl_entries() From 61314248bb733191f58cf033e2b0312bcf1de481 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 15 Sep 2020 11:14:31 +0530 Subject: [PATCH 33/60] feat: uom in update items for SO & PO (#22869) * feat: uom in update items for SO & PO * fix: supplied items doesn't updates on uom change * chore: add uom and conv factor change test * fix: test * fix: purchase order updates are not tracked * fix: fetch conversion factor on uom change * fix: codacy Co-authored-by: Marica --- .../purchase_order/purchase_order.json | 5 ++-- .../purchase_order/test_purchase_order.py | 27 +++++++++++++++--- erpnext/controllers/accounts_controller.py | 21 ++++++++++---- erpnext/public/js/utils.js | 28 +++++++++++++++++++ .../doctype/sales_order/test_sales_order.py | 24 ++++++++++++---- 5 files changed, 89 insertions(+), 16 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 4201e0b635..d1063b1503 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -1084,7 +1084,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2020-07-31 14:13:44.610190", + "modified": "2020-09-14 14:36:12.418690", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", @@ -1135,5 +1135,6 @@ "sort_field": "modified", "sort_order": "DESC", "timeline_field": "supplier", - "title_field": "supplier" + "title_field": "supplier", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 813286f7fa..69817a4675 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -89,7 +89,7 @@ class TestPurchaseOrder(unittest.TestCase): frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0) - def test_update_child_qty_rate(self): + def test_update_child(self): mr = make_material_request(qty=10) po = make_purchase_order(mr.name) po.supplier = "_Test Supplier" @@ -119,7 +119,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3) - def test_add_new_item_in_update_child_qty_rate(self): + def test_update_child_adding_new_item(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 po.save() @@ -145,7 +145,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(po.status, 'To Receive and Bill') - def test_remove_item_in_update_child_qty_rate(self): + def test_update_child_removing_item(self): po = create_purchase_order(do_not_save=1) po.items[0].qty = 4 po.save() @@ -185,7 +185,7 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEquals(len(po.get('items')), 1) self.assertEqual(po.status, 'To Receive and Bill') - def test_update_child_qty_rate_perm(self): + def test_update_child_perm(self): po = create_purchase_order(item_code= "_Test Item", qty=4) user = 'test@example.com' @@ -202,6 +202,25 @@ class TestPurchaseOrder(unittest.TestCase): self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) frappe.set_user("Administrator") + def test_update_child_uom_conv_factor_change(self): + po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes") + total_reqd_qty = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")]) + + trans_item = json.dumps([{ + 'item_code': po.get("items")[0].item_code, + 'rate': po.get("items")[0].rate, + 'qty': po.get("items")[0].qty, + 'uom': "_Test UOM 1", + 'conversion_factor': 2, + 'docname': po.get("items")[0].name + }]) + update_child_qty_rate('Purchase Order', trans_item, po.name) + po.reload() + + total_reqd_qty_after_change = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")]) + + self.assertEqual(total_reqd_qty_after_change, 2 * total_reqd_qty) + def test_update_qty(self): po = create_purchase_order() diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 5244777558..9093cd570a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1169,8 +1169,9 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.item_name = item.item_name child_item.description = item.description child_item.delivery_date = trans_item.get('delivery_date') or p_doc.delivery_date - child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 - child_item.uom = item.stock_uom + child_item.uom = trans_item.get("uom") or item.stock_uom + conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") @@ -1189,8 +1190,9 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.item_name = item.item_name child_item.description = item.description child_item.schedule_date = trans_item.get('schedule_date') or p_doc.schedule_date - child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or get_conversion_factor(item.item_code, item.stock_uom).get("conversion_factor") or 1.0 - child_item.uom = item.stock_uom + child_item.uom = trans_item.get("uom") or item.stock_uom + conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) + child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor child_item.base_rate = 1 # Initiallize value will update in parent validation child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item @@ -1282,6 +1284,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate")) prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty")) prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt(d.get("conversion_factor")) + prev_uom, new_uom = child_item.get("uom"), d.get("uom") if parent_doctype == 'Sales Order': prev_date, new_date = child_item.get("delivery_date"), d.get("delivery_date") @@ -1290,9 +1293,10 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil rate_unchanged = prev_rate == new_rate qty_unchanged = prev_qty == new_qty + uom_unchanged = prev_uom == new_uom conversion_factor_unchanged = prev_con_fac == new_con_fac date_unchanged = prev_date == new_date if prev_date and new_date else False # in case of delivery note etc - if rate_unchanged and qty_unchanged and conversion_factor_unchanged and date_unchanged: + if rate_unchanged and qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged: continue validate_quantity(child_item, d) @@ -1311,6 +1315,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil child_item.conversion_factor = 1 else: child_item.conversion_factor = flt(d.get('conversion_factor')) + + if d.get("uom"): + child_item.uom = d.get("uom") + conversion_factor = flt(get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor")) + child_item.conversion_factor = flt(d.get('conversion_factor')) or conversion_factor if d.get("delivery_date") and parent_doctype == 'Sales Order': child_item.delivery_date = d.get('delivery_date') @@ -1377,6 +1386,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.update_receiving_percentage() if parent.is_subcontracted == "Yes": parent.update_reserved_qty_for_subcontract() + parent.create_raw_materials_supplied("supplied_items") + parent.save() else: parent.update_reserved_qty() parent.update_project() diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index bcab0d84c6..87982f14a6 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -466,6 +466,33 @@ erpnext.utils.update_child_items = function(opts) { read_only: 0, disabled: 0, label: __('Item Code') + }, { + fieldtype:'Link', + fieldname:'uom', + options: 'UOM', + read_only: 0, + label: __('UOM'), + reqd: 1, + onchange: function () { + frappe.call({ + method: "erpnext.stock.get_item_details.get_conversion_factor", + args: { item_code: this.doc.item_code, uom: this.value }, + callback: r => { + if(!r.exc) { + if (this.doc.conversion_factor == r.message.conversion_factor) return; + + const docname = this.doc.docname; + dialog.fields_dict.trans_items.df.data.some(doc => { + if (doc.docname == docname) { + doc.conversion_factor = r.message.conversion_factor; + dialog.fields_dict.trans_items.grid.refresh(); + return true; + } + }) + } + } + }); + } }, { fieldtype:'Float', fieldname:"qty", @@ -546,6 +573,7 @@ erpnext.utils.update_child_items = function(opts) { "conversion_factor": d.conversion_factor, "qty": d.qty, "rate": d.rate, + "uom": d.uom }); this.data = dialog.fields_dict.trans_items.df.data; dialog.fields_dict.trans_items.grid.refresh(); diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 34aaf08395..735b071f44 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -318,7 +318,7 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(get_reserved_qty("_Test Item Home Desktop 100"), existing_reserved_qty_item2 + 20) - def test_add_new_item_in_update_child_qty_rate(self): + def test_update_child_adding_new_item(self): so = make_sales_order(item_code= "_Test Item", qty=4) create_dn_against_so(so.name, 4) make_sales_invoice(so.name) @@ -338,7 +338,7 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.get("items")[-1].amount, 1400) self.assertEqual(so.status, 'To Deliver and Bill') - def test_remove_item_in_update_child_qty_rate(self): + def test_update_child_removing_item(self): so = make_sales_order(**{ "item_list": [{ "item_code": '_Test Item', @@ -381,7 +381,7 @@ class TestSalesOrder(unittest.TestCase): self.assertEqual(so.status, 'To Deliver and Bill') - def test_update_child_qty_rate(self): + def test_update_child(self): so = make_sales_order(item_code= "_Test Item", qty=4) create_dn_against_so(so.name, 4) make_sales_invoice(so.name) @@ -402,7 +402,7 @@ class TestSalesOrder(unittest.TestCase): trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 2, 'docname': so.items[0].name}]) self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Sales Order', trans_item, so.name) - def test_update_child_qty_rate_perm(self): + def test_update_child_perm(self): so = make_sales_order(item_code= "_Test Item", qty=4) user = 'test@example.com' @@ -454,7 +454,7 @@ class TestSalesOrder(unittest.TestCase): workflow.is_active = 0 workflow.save() - def test_update_child_qty_rate_product_bundle(self): + def test_update_child_product_bundle(self): # test Update Items with product bundle if not frappe.db.exists("Item", "_Product Bundle Item"): bundle_item = make_item("_Product Bundle Item", {"is_stock_item": 0}) @@ -474,6 +474,20 @@ class TestSalesOrder(unittest.TestCase): so.reload() self.assertEqual(so.packed_items[0].qty, 4) + # test uom and conversion factor change + update_uom_conv_factor = json.dumps([{ + 'item_code': so.get("items")[0].item_code, + 'rate': so.get("items")[0].rate, + 'qty': so.get("items")[0].qty, + 'uom': "_Test UOM 1", + 'conversion_factor': 2, + 'docname': so.get("items")[0].name + }]) + update_child_qty_rate('Sales Order', update_uom_conv_factor, so.name) + + so.reload() + self.assertEqual(so.packed_items[0].qty, 8) + def test_warehouse_user(self): frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 1 - _TC", "test@example.com") frappe.permissions.add_user_permission("Warehouse", "_Test Warehouse 2 - _TC1", "test2@example.com") From 4bb8d0c193ab9e967ad224a0e37743f1ef2cd648 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 15 Sep 2020 11:18:02 +0530 Subject: [PATCH 34/60] chore: Test for PO --- .../purchase_order/test_purchase_order.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 813286f7fa..71d100dd05 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -202,6 +202,49 @@ class TestPurchaseOrder(unittest.TestCase): self.assertRaises(frappe.ValidationError, update_child_qty_rate,'Purchase Order', trans_item, po.name) frappe.set_user("Administrator") + def test_update_child_with_tax_template(self): + tax_template = "_Test Account Excise Duty @ 10" + item = "_Test Item Home Desktop 100" + + if not frappe.db.exists("Item Tax", {"parent":item, "item_tax_template":tax_template}): + item_doc = frappe.get_doc("Item", item) + item_doc.append("taxes", { + "item_tax_template": tax_template, + "valid_from": nowdate() + }) + item_doc.save() + else: + # update valid from + frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = CURDATE() + where parent = %(item)s and item_tax_template = %(tax)s""", + {"item": item, "tax": tax_template}) + + po = create_purchase_order(item_code=item, qty=1, do_not_save=1) + + po.append("taxes", { + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Purchase Taxes and Charges", + "rate": 10 + }) + po.insert() + po.submit() + + self.assertEqual(po.taxes[0].tax_amount, 50) + self.assertEqual(po.taxes[0].total, 550) + + items = json.dumps([ + {'item_code' : item, 'rate' : 500, 'qty' : 1, 'docname': po.items[0].name}, + {'item_code' : item, 'rate' : 100, 'qty' : 1} # added item + ]) + update_child_qty_rate('Purchase Order', items, po.name) + + po.reload() + self.assertEqual(po.taxes[0].tax_amount, 60) + self.assertEqual(po.taxes[0].total, 660) + def test_update_qty(self): po = create_purchase_order() From 665c27af58dd2cdd581209eeecbcfd3bca1d2426 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 15 Sep 2020 12:04:38 +0530 Subject: [PATCH 35/60] fix: Merge conflict missing line --- erpnext/controllers/accounts_controller.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 72e5df85e7..046fb2ca68 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1184,6 +1184,7 @@ def set_sales_order_defaults(parent_doctype, parent_doctype_name, child_docname, child_item.uom = trans_item.get("uom") or item.stock_uom conversion_factor = flt(get_conversion_factor(item.item_code, child_item.uom).get("conversion_factor")) child_item.conversion_factor = flt(trans_item.get('conversion_factor')) or conversion_factor + set_child_tax_template_and_map(item, child_item, p_doc) child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True) if not child_item.warehouse: frappe.throw(_("Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.") @@ -1245,7 +1246,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil frappe.throw(_("You do not have permissions to {} items in a {}.") .format(actions[perm_type], parent_doctype), title=_("Insufficient Permissions")) - + def validate_workflow_conditions(doc): workflow = get_workflow_name(doc.doctype) if not workflow: @@ -1280,7 +1281,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation'] parent = frappe.get_doc(parent_doctype, parent_doctype_name) - + check_doc_permissions(parent, 'cancel') validate_and_delete_children(parent, data) @@ -1328,7 +1329,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil child_item.conversion_factor = 1 else: child_item.conversion_factor = flt(d.get('conversion_factor')) - + if d.get("uom"): child_item.uom = d.get("uom") conversion_factor = flt(get_conversion_factor(child_item.item_code, child_item.uom).get("conversion_factor")) From bae2d5d485646ee39798eda865fe6b84158450e3 Mon Sep 17 00:00:00 2001 From: Anupam Date: Tue, 15 Sep 2020 12:12:31 +0530 Subject: [PATCH 36/60] fix: review changes --- erpnext/selling/report/sales_analytics/sales_analytics.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py index dbaf2acab9..d036a1cb09 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.py +++ b/erpnext/selling/report/sales_analytics/sales_analytics.py @@ -217,13 +217,9 @@ class Analytics(object): "company": self.filters.company, "project": ["!=", ""], self.date_field: ('between', [self.filters.from_date, self.filters.to_date]) - }, debug =True + } ) - self.entity_names = {} - for d in self.entries: - self.entity_names.setdefault(d.entity, d.entity_name) - def get_rows(self): self.data = [] self.get_periodic_data() @@ -231,7 +227,7 @@ class Analytics(object): for entity, period_data in iteritems(self.entity_periodic_data): row = { "entity": entity, - "entity_name": self.entity_names.get(entity) + "entity_name": self.entity_names.get(entity) if hasattr(self, 'entity_names') else None } total = 0 for end_date in self.periodic_daterange: From 9397b7f58bdb8f3c32170be3da9423f3bbc8910d Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Tue, 15 Sep 2020 12:36:01 +0530 Subject: [PATCH 37/60] fix: requested changes --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 842c64fdbe..b42efdfe8b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -453,7 +453,7 @@ class PaymentEntry(AccountsController): frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction")) def set_remarks(self): - if self.remarks: return + if self.custom_remarks: return if self.payment_type=="Internal Transfer": remarks = [_("Amount {0} {1} transferred from {2} to {3}") From 4f3b7da9baea791d2f5bb2731989e730ba491c2f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 15 Sep 2020 14:47:36 +0530 Subject: [PATCH 38/60] fix: set_taxes() missing 1 required positional argument: 'company' --- erpnext/public/js/utils/party.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 065326744c..af1f433514 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -224,6 +224,10 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) { party = frm.doc.party_name; } + if (!frm.doc.company) { + frappe.throw(_("Kindly select the company first")); + } + frappe.call({ method: "erpnext.accounts.party.set_taxes", args: { From 2f50cb3e58458a9679bfc6022c15f910b757aa5b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 15 Sep 2020 15:00:32 +0530 Subject: [PATCH 39/60] fix: Supplier trigger onload --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 2bfa4a572e..fe5301d5c8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -28,7 +28,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ // Trigger supplier event on load if supplier is available // The reason for this is PI can be created from PR or PO and supplier is pre populated - if (this.frm.doc.supplier) { + if (this.frm.doc.supplier && this.frm.doc.__islocal) { this.frm.trigger('supplier'); } }, From f995f517c95a9995e5d16333b341d9a79072f836 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 15 Sep 2020 17:06:27 +0530 Subject: [PATCH 40/60] fix: Test --- erpnext/buying/doctype/purchase_order/test_purchase_order.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 83f002e358..158799ce63 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -245,6 +245,10 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(po.taxes[0].tax_amount, 60) self.assertEqual(po.taxes[0].total, 660) + frappe.db.sql("""UPDATE `tabItem Tax` set valid_from = NULL + where parent = %(item)s and item_tax_template = %(tax)s""", + {"item": item, "tax": tax_template}) + def test_update_child_uom_conv_factor_change(self): po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes") total_reqd_qty = sum([d.get("required_qty") for d in po.as_dict().get("supplied_items")]) From c78ca36ee5a35c8bff6547037ec0a1120d521af4 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 16 Sep 2020 23:47:33 +0530 Subject: [PATCH 41/60] fix(Non Profit): Hide Address and Contact section for unsaved docs --- erpnext/non_profit/doctype/donor/donor.json | 407 +++-------- erpnext/non_profit/doctype/member/member.json | 3 +- .../doctype/volunteer/volunteer.json | 678 ++++-------------- 3 files changed, 213 insertions(+), 875 deletions(-) diff --git a/erpnext/non_profit/doctype/donor/donor.json b/erpnext/non_profit/doctype/donor/donor.json index 7e24dacbe1..96392658f1 100644 --- a/erpnext/non_profit/doctype/donor/donor.json +++ b/erpnext/non_profit/doctype/donor/donor.json @@ -1,336 +1,105 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "autoname": "field:email", - "beta": 0, - "creation": "2017-09-19 16:20:27.510196", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:email", + "creation": "2017-09-19 16:20:27.510196", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "donor_name", + "column_break_5", + "donor_type", + "email", + "image", + "address_contacts", + "address_html", + "column_break_9", + "contact_html" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "donor_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": "Donor 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 - }, + "fieldname": "donor_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Donor Name", + "reqd": 1 + }, { - "allow_bulk_edit": 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, - "unique": 0 - }, + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "donor_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": 0, - "label": "Donor Type", - "length": 0, - "no_copy": 0, - "options": "Donor Type", - "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 - }, + "fieldname": "donor_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Donor Type", + "options": "Donor Type", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email", - "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": "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": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "email", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Email", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "image", - "fieldtype": "Attach Image", - "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": "Image", - "length": 0, - "no_copy": 1, - "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, - "unique": 0 - }, + "fieldname": "image", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Image", + "no_copy": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_contacts", - "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": "Address and Contact", - "length": 0, - "no_copy": 0, - "options": "fa fa-map-marker", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_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 - }, + "depends_on": "eval:!doc.__islocal;", + "fieldname": "address_contacts", + "fieldtype": "Section Break", + "label": "Address and Contact", + "options": "fa fa-map-marker" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_html", - "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": "Address HTML", - "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": "address_html", + "fieldtype": "HTML", + "label": "Address HTML" + }, { - "allow_bulk_edit": 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, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_html", - "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": "Contact HTML", - "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": "contact_html", + "fieldtype": "HTML", + "label": "Contact HTML" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_field": "image", - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-01-22 15:53:35.059946", - "modified_by": "Administrator", - "module": "Non Profit", - "name": "Donor", - "name_case": "", - "owner": "Administrator", + ], + "image_field": "image", + "links": [], + "modified": "2020-09-16 23:46:04.083274", + "modified_by": "Administrator", + "module": "Non Profit", + "name": "Donor", + "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": "Non Profit Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Non Profit Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Non Profit", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "donor_name", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "restrict_to_domain": "Non Profit", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "donor_name", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/non_profit/doctype/member/member.json b/erpnext/non_profit/doctype/member/member.json index 77cdb94b3d..992ef16d64 100644 --- a/erpnext/non_profit/doctype/member/member.json +++ b/erpnext/non_profit/doctype/member/member.json @@ -111,6 +111,7 @@ "options": "Supplier" }, { + "depends_on": "eval:!doc.__islocal;", "fieldname": "address_contacts", "fieldtype": "Section Break", "label": "Address and Contact", @@ -177,7 +178,7 @@ ], "image_field": "image", "links": [], - "modified": "2020-08-06 10:06:01.153564", + "modified": "2020-09-16 23:44:13.596948", "modified_by": "Administrator", "module": "Non Profit", "name": "Member", diff --git a/erpnext/non_profit/doctype/volunteer/volunteer.json b/erpnext/non_profit/doctype/volunteer/volunteer.json index 052b288089..08b7f87b2a 100644 --- a/erpnext/non_profit/doctype/volunteer/volunteer.json +++ b/erpnext/non_profit/doctype/volunteer/volunteer.json @@ -1,580 +1,148 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 1, - "autoname": "field:email", - "beta": 0, - "creation": "2017-09-19 16:16:45.676019", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_rename": 1, + "autoname": "field:email", + "creation": "2017-09-19 16:16:45.676019", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "volunteer_name", + "column_break_5", + "volunteer_type", + "email", + "image", + "address_contacts", + "address_html", + "column_break_9", + "contact_html", + "volunteer_availability_and_skills_details", + "availability", + "availability_timeslot", + "column_break_12", + "volunteer_skills", + "section_break_15", + "note" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "volunteer_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": "Volunteer 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, - "translatable": 0, - "unique": 0 - }, + "fieldname": "volunteer_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Volunteer Name", + "reqd": 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 - }, + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "volunteer_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": 0, - "label": "Volunteer Type", - "length": 0, - "no_copy": 0, - "options": "Volunteer Type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "volunteer_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Volunteer Type", + "options": "Volunteer Type", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "email", - "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": "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": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "email", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Email", + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "image", - "fieldtype": "Attach Image", - "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": "Image", - "length": 0, - "no_copy": 1, - "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 - }, + "fieldname": "image", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Image", + "no_copy": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_contacts", - "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": "Address and Contact", - "length": 0, - "no_copy": 0, - "options": "fa fa-map-marker", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:!doc.__islocal;", + "fieldname": "address_contacts", + "fieldtype": "Section Break", + "label": "Address and Contact", + "options": "fa fa-map-marker" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_html", - "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": "Address HTML", - "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": "address_html", + "fieldtype": "HTML", + "label": "Address HTML" + }, { - "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 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_html", - "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": "Contact HTML", - "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": "contact_html", + "fieldtype": "HTML", + "label": "Contact HTML" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "volunteer_availability_and_skills_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": "Availability and Skills", - "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": "volunteer_availability_and_skills_details", + "fieldtype": "Section Break", + "label": "Availability and Skills" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "availability", - "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": "Availability", - "length": 0, - "no_copy": 0, - "options": "\nWeekly\nWeekdays\nWeekends", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "availability", + "fieldtype": "Select", + "label": "Availability", + "options": "\nWeekly\nWeekdays\nWeekends" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "availability_timeslot", - "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": "Availability Timeslot", - "length": 0, - "no_copy": 0, - "options": "\nMorning\nAfternoon\nEvening\nAnytime", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "availability_timeslot", + "fieldtype": "Select", + "label": "Availability Timeslot", + "options": "\nMorning\nAfternoon\nEvening\nAnytime" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_12", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "volunteer_skills", - "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": "Volunteer Skills", - "length": 0, - "no_copy": 0, - "options": "Volunteer Skill", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "volunteer_skills", + "fieldtype": "Table", + "label": "Volunteer Skills", + "options": "Volunteer Skill" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_15", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "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_15", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "note", - "fieldtype": "Long 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": "Note", - "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": "note", + "fieldtype": "Long Text", + "label": "Note" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_field": "image", - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-04 03:36:25.776211", - "modified_by": "Administrator", - "module": "Non Profit", - "name": "Volunteer", - "name_case": "", - "owner": "Administrator", + ], + "image_field": "image", + "links": [], + "modified": "2020-09-16 23:45:15.595952", + "modified_by": "Administrator", + "module": "Non Profit", + "name": "Volunteer", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Non Profit Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Non Profit Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Non Profit", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "volunteer_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "restrict_to_domain": "Non Profit", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "volunteer_name", + "track_changes": 1 } \ No newline at end of file From e813684433f9165583e752e3335786e4b2e4cca5 Mon Sep 17 00:00:00 2001 From: Afshan Date: Thu, 17 Sep 2020 12:57:26 +0530 Subject: [PATCH 42/60] fix: warehouse address filtered based on warehouse --- .../stock/doctype/stock_entry/stock_entry.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 9845bc2f70..592ab5d2ed 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -23,6 +23,24 @@ frappe.ui.form.on('Stock Entry', { } }); + frm.set_query('source_warehouse_address', function() { + return { + filters: { + link_doctype: 'Warehouse', + link_name: frm.doc.from_warehouse + } + } + }); + + frm.set_query('target_warehouse_address', function() { + return { + filters: { + link_doctype: 'Warehouse', + link_name: frm.doc.to_warehouse + } + } + }); + frappe.db.get_value('Stock Settings', {name: 'Stock Settings'}, 'sample_retention_warehouse', (r) => { if (r.sample_retention_warehouse) { var filters = [ @@ -81,6 +99,9 @@ frappe.ui.form.on('Stock Entry', { frm.add_fetch("bom_no", "inspection_required", "inspection_required"); }, + + + setup_quality_inspection: function(frm) { if (!frm.doc.inspection_required) { return; From d5710b4d6f014e25358f7cd38e14b91013062f78 Mon Sep 17 00:00:00 2001 From: Afshan Date: Thu, 17 Sep 2020 13:01:47 +0530 Subject: [PATCH 43/60] style: removed extra spaces --- erpnext/stock/doctype/stock_entry/stock_entry.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 592ab5d2ed..1f95447951 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -99,9 +99,6 @@ frappe.ui.form.on('Stock Entry', { frm.add_fetch("bom_no", "inspection_required", "inspection_required"); }, - - - setup_quality_inspection: function(frm) { if (!frm.doc.inspection_required) { return; From 68672af8ab1b8e1f1061cbc43443fcc23eaec939 Mon Sep 17 00:00:00 2001 From: SDLyu Date: Fri, 18 Sep 2020 13:58:23 +0800 Subject: [PATCH 44/60] Fix missing tax autoload when frm.doc.taxes is [] --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 792235f7a3..33911793f6 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -417,7 +417,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ var taxes_and_charges_field = frappe.meta.get_docfield(me.frm.doc.doctype, "taxes_and_charges", me.frm.doc.name); - if (!this.frm.doc.taxes_and_charges && this.frm.doc.taxes) { + if (!this.frm.doc.taxes_and_charges && this.frm.doc.taxes && this.frm.doc.taxes.length > 0) { return; } From 170ecdc76e288a66c59e6337a9e712df9016ce5c Mon Sep 17 00:00:00 2001 From: Anurag Mishra Date: Fri, 18 Sep 2020 13:26:46 +0530 Subject: [PATCH 45/60] fix: No handlefor Cost centers in Accounts Receivable --- .../report/accounts_receivable/accounts_receivable.js | 2 +- .../report/accounts_receivable/accounts_receivable.py | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index c999eb9b8e..29c4f7d394 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -34,7 +34,7 @@ frappe.query_reports["Accounts Receivable"] = { filters: { 'company': company } - } + }; } }, { diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 59117c8174..044fc1d3ab 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -617,9 +617,19 @@ class ReceivablePayableReport(object): elif party_type_field=="supplier": self.add_supplier_filters(conditions, values) + if self.filters.cost_center: + self.get_cost_center_conditions(conditions) + self.add_accounting_dimensions_filters(conditions, values) return " and ".join(conditions), values + def get_cost_center_conditions(self, conditions): + lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"]) + cost_center_list = [center.name for center in frappe.get_list("Cost Center", filters = {'lft': (">=", lft), 'rgt': ("<=", rgt)})] + + cost_center_string = '", "'.join(cost_center_list) + conditions.append('cost_center in ("{0}")'.format(cost_center_string)) + def get_order_by_condition(self): if self.filters.get('group_by_party'): return "order by party, posting_date" From c29ee691d4e7c5f738f0ce837c968d73599d337a Mon Sep 17 00:00:00 2001 From: Saqib Date: Fri, 18 Sep 2020 15:27:17 +0530 Subject: [PATCH 46/60] fix: button click event not working in POS custom fields (#23358) --- .../doctype/pos_settings/pos_settings.js | 8 ++++---- .../selling/page/point_of_sale/pos_payment.js | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.js b/erpnext/accounts/doctype/pos_settings/pos_settings.js index 504941d8b6..05cb7f0b4b 100644 --- a/erpnext/accounts/doctype/pos_settings/pos_settings.js +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.js @@ -7,10 +7,10 @@ frappe.ui.form.on('POS Settings', { }, get_invoice_fields: function(frm) { - frappe.model.with_doctype("Sales Invoice", () => { - var fields = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) { + frappe.model.with_doctype("POS Invoice", () => { + var fields = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) { if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 || - d.fieldtype === 'Table') { + ['Table', 'Button'].includes(d.fieldtype)) { return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname }; } else { return null; @@ -25,7 +25,7 @@ frappe.ui.form.on('POS Settings', { frappe.ui.form.on("POS Field", { fieldname: function(frm, doctype, name) { var doc = frappe.get_doc(doctype, name); - var df = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) { + var df = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) { return doc.fieldname == d.fieldname ? d : null; })[0]; diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index e1c54f64a7..7f0cabed8b 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -70,13 +70,23 @@ erpnext.PointOfSale.Payment = class { this.$invoice_fields.append( `
` ); + let df_events = { + onchange: function() { frm.set_value(this.df.fieldname, this.value); } + } + if (df.fieldtype == "Button") { + df_events = { + click: function() { + if (frm.script_manager.has_handlers(df.fieldname, frm.doc.doctype)) { + frm.script_manager.trigger(df.fieldname, frm.doc.doctype, frm.doc.docname); + } + } + } + } this[`${df.fieldname}_field`] = frappe.ui.form.make_control({ df: { ...df, - onchange: function() { - frm.set_value(this.df.fieldname, this.value); - } + ...df_events }, parent: this.$invoice_fields.find(`.${df.fieldname}-field`), render_input: true, From cc6ca97dc53fed52ba3e3ec5b47569c752a18aa7 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Fri, 18 Sep 2020 15:50:29 +0530 Subject: [PATCH 47/60] fix: Item-wise Sales History Report Error (#23349) Co-authored-by: Rucha Mahabal --- .../report/item_wise_sales_history/item_wise_sales_history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py index 0a70b97648..c716aa96e0 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py @@ -188,7 +188,7 @@ def get_conditions(filters): conditions += "AND so.transaction_date <= '%s'" %filters.to_date if filters.get("item_code"): - conditions += "AND so_item.item_code = '%s'" %frappe.db.escape(filters.item_code) + conditions += "AND so_item.item_code = %s" %frappe.db.escape(filters.item_code) if filters.get("customer"): conditions += "AND so.customer = %s" %frappe.db.escape(filters.customer) From 123eaea1a60f14fc6d464c8d816dcc9782db3773 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Fri, 18 Sep 2020 16:30:50 +0530 Subject: [PATCH 48/60] fix: added new doctypes in get_level --- erpnext/utilities/activation.py | 35 +++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/erpnext/utilities/activation.py b/erpnext/utilities/activation.py index 63c36b35d1..7b17c8c464 100644 --- a/erpnext/utilities/activation.py +++ b/erpnext/utilities/activation.py @@ -11,8 +11,39 @@ def get_level(): activation_level = 0 sales_data = [] min_count = 0 - doctypes = {"Item": 5, "Customer": 5, "Sales Order": 2, "Sales Invoice": 2, "Purchase Order": 2, "Employee": 3, "Lead": 3, "Quotation": 3, - "Payment Entry": 2, "User": 5, "Student": 5, "Instructor": 5, "BOM": 3, "Journal Entry": 3, "Stock Entry": 3} + doctypes = { + "Asset": 5, + "BOM": 3, + "Customer": 5, + "Delivery Note": 5, + "Employee": 3, + "Instructor": 5, + "Instructor": 5, + "Issue": 5, + "Item": 5, + "Journal Entry": 3, + "Lead": 3, + "Leave Application": 5, + "Material Request": 5, + "Opportunity": 5, + "Payment Entry": 2, + "Project": 5, + "Purchase Order": 2, + "Purchase Invoice": 5, + "Purchase Receipt": 5, + "Quotation": 3, + "Salary Slip": 5, + "Salary Structure": 5, + "Sales Order": 2, + "Sales Invoice": 2, + "Stock Entry": 3, + "Student": 5, + "Supplier": 5, + "Task": 5, + "User": 5, + "Work Order": 5 + } + for doctype, min_count in iteritems(doctypes): count = frappe.db.count(doctype) if count > min_count: From bf166736b098574aecbbaa2329e58a5c75a938dd Mon Sep 17 00:00:00 2001 From: Anupam Date: Fri, 18 Sep 2020 17:15:49 +0530 Subject: [PATCH 49/60] fix: Batch-Wise Balance History filter validation --- .../batch_wise_balance_history/batch_wise_balance_history.js | 4 +++- .../batch_wise_balance_history/batch_wise_balance_history.py | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js index 23700c94ee..2499c801d2 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js @@ -9,13 +9,15 @@ frappe.query_reports["Batch-Wise Balance History"] = { "fieldtype": "Date", "width": "80", "default": frappe.sys_defaults.year_start_date, + "reqd": 1 }, { "fieldname":"to_date", "label": __("To Date"), "fieldtype": "Date", "width": "80", - "default": frappe.datetime.get_today() + "default": frappe.datetime.get_today(), + "reqd": 1 }, { "fieldname": "item", diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index 2c95084b81..8f3e246e7f 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -11,6 +11,9 @@ from frappe.utils import cint, flt, getdate def execute(filters=None): if not filters: filters = {} + if filters.from_date > filters.to_date: + frappe.throw(_("From Date must be before To Date")) + float_precision = cint(frappe.db.get_default("float_precision")) or 3 columns = get_columns(filters) From 4e6733293f7bbec2d265373ca8646a37856f8cb4 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Fri, 18 Sep 2020 18:27:24 +0530 Subject: [PATCH 50/60] fix(DocTypes): Reset "owner" values in DocTypes to Administrator (#23350) * fix: reset owner values in doctype to Administrator * fix: updated modified value * fix: also fixed modified by * fix: change modified_by to Administrator --- .../doctype/item_tax_template/item_tax_template.json | 4 ++-- .../accounts/doctype/mode_of_payment/mode_of_payment.json | 6 +++--- .../period_closing_voucher/period_closing_voucher.json | 4 ++-- .../purchase_taxes_and_charges.json | 2 +- .../purchase_taxes_and_charges_template.json | 2 +- .../purchase_order_item_supplied.json | 4 ++-- .../purchase_receipt_item_supplied.json | 4 ++-- .../doctype/shopify_settings/shopify_settings.json | 4 ++-- .../healthcare_schedule_time_slot.json | 4 ++-- .../practitioner_schedule/practitioner_schedule.json | 4 ++-- erpnext/hr/doctype/appraisal/appraisal.json | 4 ++-- erpnext/hr/doctype/appraisal_goal/appraisal_goal.json | 4 ++-- .../hr/doctype/appraisal_template/appraisal_template.json | 4 ++-- .../appraisal_template_goal/appraisal_template_goal.json | 4 ++-- erpnext/hr/doctype/attendance/attendance.json | 4 ++-- erpnext/hr/doctype/expense_claim/expense_claim.json | 4 ++-- .../doctype/expense_claim_detail/expense_claim_detail.json | 4 ++-- .../hr/doctype/expense_claim_type/expense_claim_type.json | 4 ++-- erpnext/hr/doctype/upload_attendance/upload_attendance.json | 4 ++-- .../company_to_hub_company/company_to_hub_company.json | 4 ++-- .../hub_message_to_lead/hub_message_to_lead.json | 6 +++--- erpnext/hub_node/doctype/hub_user/hub_user.json | 4 ++-- erpnext/hub_node/doctype/hub_users/hub_users.json | 4 ++-- .../doctype/marketplace_settings/marketplace_settings.json | 4 ++-- .../doctype/maintenance_schedule/maintenance_schedule.json | 2 +- .../doctype/maintenance_visit/maintenance_visit.json | 4 ++-- .../maintenance_visit_purpose.json | 4 ++-- erpnext/projects/doctype/dependent_task/dependent_task.json | 4 ++-- erpnext/selling/doctype/industry_type/industry_type.json | 4 ++-- erpnext/selling/doctype/product_bundle/product_bundle.json | 4 ++-- erpnext/stock/doctype/batch/batch.json | 4 ++-- .../stock/doctype/landed_cost_item/landed_cost_item.json | 4 ++-- .../landed_cost_purchase_receipt.json | 4 ++-- erpnext/support/doctype/warranty_claim/warranty_claim.json | 4 ++-- 34 files changed, 67 insertions(+), 67 deletions(-) diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json index 856c371ecf..8915f79b92 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json @@ -38,8 +38,8 @@ "reqd": 1 } ], - "modified": "2020-06-18 20:27:42.615842", - "modified_by": "ahmad@havenir.com", + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", "module": "Accounts", "name": "Item Tax Template", "owner": "Administrator", diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json index f3df1f0bc9..50fc3bbab7 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.json @@ -45,11 +45,11 @@ ], "icon": "fa fa-credit-card", "idx": 1, - "modified": "2019-08-14 14:58:42.079115", - "modified_by": "sammish.thundiyil@gmail.com", + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", "module": "Accounts", "name": "Mode of Payment", - "owner": "harshada@webnotestech.com", + "owner": "Administrator", "permissions": [ { "create": 1, diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json index d04f25b9ac..47546c07a4 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json @@ -291,11 +291,11 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 16:15:49.089450", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Accounts", "name": "Period Closing Voucher", - "owner": "jai@webnotestech.com", + "owner": "Administrator", "permissions": [ { "amend": 1, diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index 0e748f84bb..f9fdc4b605 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -210,7 +210,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-12 14:53:47.679439", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json index a18fec61cf..b46d2e32f2 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json @@ -78,7 +78,7 @@ "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges Template", - "owner": "wasim@webnotestech.com", + "owner": "Administrator", "permissions": [ { "email": 1, diff --git a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json index c3e1bf5303..d7ea9c1ccc 100644 --- a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json +++ b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json @@ -148,11 +148,11 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-03-12 15:43:53.862897", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item Supplied", - "owner": "dhanalekshmi@webnotestech.com", + "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC" diff --git a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json index ea5863020a..dc00bca5cc 100644 --- a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json +++ b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json @@ -188,11 +188,11 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-04-10 18:09:33.997618", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Receipt Item Supplied", - "owner": "wasim@webnotestech.com", + "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC", diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json index 5339c99155..2e10751f96 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json @@ -258,8 +258,8 @@ } ], "issingle": 1, - "modified": "2020-05-28 12:32:11.384757", - "modified_by": "umair@erpnext.com", + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Shopify Settings", "owner": "Administrator", diff --git a/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json b/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json index 9faa5b2f03..cf54e82199 100644 --- a/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json +++ b/erpnext/healthcare/doctype/healthcare_schedule_time_slot/healthcare_schedule_time_slot.json @@ -117,12 +117,12 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-05-08 13:45:57.226530", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Schedule Time Slot", "name_case": "", - "owner": "rmehta@gmail.com", + "owner": "Administrator", "permissions": [], "quick_entry": 1, "read_only": 0, diff --git a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json index cff100cc70..a21825ea8e 100644 --- a/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json +++ b/erpnext/healthcare/doctype/practitioner_schedule/practitioner_schedule.json @@ -44,11 +44,11 @@ } ], "links": [], - "modified": "2020-01-31 12:21:45.975488", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Healthcare", "name": "Practitioner Schedule", - "owner": "rmehta@gmail.com", + "owner": "Administrator", "permissions": [ { "create": 1, diff --git a/erpnext/hr/doctype/appraisal/appraisal.json b/erpnext/hr/doctype/appraisal/appraisal.json index 4f6da975d5..91201d4b82 100644 --- a/erpnext/hr/doctype/appraisal/appraisal.json +++ b/erpnext/hr/doctype/appraisal/appraisal.json @@ -696,11 +696,11 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2019-01-30 11:28:08.401459", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "HR", "name": "Appraisal", - "owner": "ashwini@webnotestech.com", + "owner": "Administrator", "permissions": [ { "amend": 0, diff --git a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json index f22969b7c1..b8ec5a8afb 100644 --- a/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json +++ b/erpnext/hr/doctype/appraisal_goal/appraisal_goal.json @@ -207,11 +207,11 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-07-11 03:27:57.897071", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "HR", "name": "Appraisal Goal", - "owner": "ashwini@webnotestech.com", + "owner": "Administrator", "permissions": [], "quick_entry": 0, "read_only": 0, diff --git a/erpnext/hr/doctype/appraisal_template/appraisal_template.json b/erpnext/hr/doctype/appraisal_template/appraisal_template.json index ac6e400e09..7329c35dee 100644 --- a/erpnext/hr/doctype/appraisal_template/appraisal_template.json +++ b/erpnext/hr/doctype/appraisal_template/appraisal_template.json @@ -113,11 +113,11 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-12-13 12:37:56.937023", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "HR", "name": "Appraisal Template", - "owner": "ashwini@webnotestech.com", + "owner": "Administrator", "permissions": [ { "amend": 0, diff --git a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json index 34ea5d8225..2858419f33 100644 --- a/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json +++ b/erpnext/hr/doctype/appraisal_template_goal/appraisal_template_goal.json @@ -78,11 +78,11 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-07-11 03:27:57.979215", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "HR", "name": "Appraisal Template Goal", - "owner": "ashwini@webnotestech.com", + "owner": "Administrator", "permissions": [], "quick_entry": 0, "read_only": 0, diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json index a656a7ea5f..134098f252 100644 --- a/erpnext/hr/doctype/attendance/attendance.json +++ b/erpnext/hr/doctype/attendance/attendance.json @@ -205,11 +205,11 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2020-05-29 13:51:37.177231", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "HR", "name": "Attendance", - "owner": "ashwini@webnotestech.com", + "owner": "Administrator", "permissions": [ { "cancel": 1, diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index fa28470af8..e3e6e80616 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -371,12 +371,12 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2020-06-15 12:43:04.099803", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim", "name_case": "Title Case", - "owner": "harshada@webnotestech.com", + "owner": "Administrator", "permissions": [ { "amend": 1, diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json index 3cce50e090..70a48f93b7 100644 --- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json +++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json @@ -120,11 +120,11 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-05-11 18:54:35.601592", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim Detail", - "owner": "harshada@webnotestech.com", + "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC" diff --git a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json index cf2cbb4308..02ab4cb6ac 100644 --- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json +++ b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json @@ -48,11 +48,11 @@ "icon": "fa fa-flag", "idx": 1, "links": [], - "modified": "2019-12-11 13:38:59.534034", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim Type", - "owner": "harshada@webnotestech.com", + "owner": "Administrator", "permissions": [ { "create": 1, diff --git a/erpnext/hr/doctype/upload_attendance/upload_attendance.json b/erpnext/hr/doctype/upload_attendance/upload_attendance.json index 16b7a83173..a1451fde1d 100644 --- a/erpnext/hr/doctype/upload_attendance/upload_attendance.json +++ b/erpnext/hr/doctype/upload_attendance/upload_attendance.json @@ -229,11 +229,11 @@ "issingle": 1, "istable": 0, "max_attachments": 1, - "modified": "2017-11-14 12:51:34.980103", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "HR", "name": "Upload Attendance", - "owner": "harshada@webnotestech.com", + "owner": "Administrator", "permissions": [ { "amend": 0, diff --git a/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json b/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json index ae0d492d72..b1e421dada 100644 --- a/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json +++ b/erpnext/hub_node/data_migration_mapping/company_to_hub_company/company_to_hub_company.json @@ -40,8 +40,8 @@ "mapping_name": "Company to Hub Company", "mapping_type": "Push", "migration_id_field": "hub_sync_id", - "modified": "2018-02-14 15:57:05.571142", - "modified_by": "achilles@erpnext.com", + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", "name": "Company to Hub Company", "owner": "Administrator", "page_length": 10, diff --git a/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json b/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json index d2af755d04..d11abeb4b3 100644 --- a/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json +++ b/erpnext/hub_node/data_migration_mapping/hub_message_to_lead/hub_message_to_lead.json @@ -21,10 +21,10 @@ "mapping_name": "Hub Message to Lead", "mapping_type": "Pull", "migration_id_field": "hub_sync_id", - "modified": "2018-02-14 15:57:05.606597", - "modified_by": "achilles@erpnext.com", + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", "name": "Hub Message to Lead", - "owner": "frappetest@gmail.com", + "owner": "Administrator", "page_length": 10, "remote_objectname": "Hub Message", "remote_primary_key": "name" diff --git a/erpnext/hub_node/doctype/hub_user/hub_user.json b/erpnext/hub_node/doctype/hub_user/hub_user.json index a660022736..f51ffb4387 100644 --- a/erpnext/hub_node/doctype/hub_user/hub_user.json +++ b/erpnext/hub_node/doctype/hub_user/hub_user.json @@ -121,8 +121,8 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-09-01 13:56:07.816894", - "modified_by": "netchamp@rawcoderz.com", + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", "module": "Hub Node", "name": "Hub User", "name_case": "", diff --git a/erpnext/hub_node/doctype/hub_users/hub_users.json b/erpnext/hub_node/doctype/hub_users/hub_users.json index 2027e72fa0..d42f3fdf1b 100644 --- a/erpnext/hub_node/doctype/hub_users/hub_users.json +++ b/erpnext/hub_node/doctype/hub_users/hub_users.json @@ -54,12 +54,12 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2018-03-06 04:41:17.916243", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Hub Node", "name": "Hub Users", "name_case": "", - "owner": "test1@example.com", + "owner": "Administrator", "permissions": [], "quick_entry": 1, "read_only": 0, diff --git a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json index 4b49f1978a..e784f68fcf 100644 --- a/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json +++ b/erpnext/hub_node/doctype/marketplace_settings/marketplace_settings.json @@ -352,12 +352,12 @@ "issingle": 1, "istable": 0, "max_attachments": 0, - "modified": "2019-02-01 14:21:16.729848", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Hub Node", "name": "Marketplace Settings", "name_case": "", - "owner": "netchamp@rawcoderz.com", + "owner": "Administrator", "permissions": [ { "amend": 0, diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json index 3788bc3700..606d22f52b 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json @@ -813,7 +813,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-08-21 14:44:33.670332", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Maintenance", "name": "Maintenance Schedule", diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json index 11925681df..32bfa0e324 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json @@ -1001,11 +1001,11 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2020-07-15 14:44:44.911402", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Maintenance", "name": "Maintenance Visit", - "owner": "ashwini@webnotestech.com", + "owner": "Administrator", "permissions": [ { "amend": 1, diff --git a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json index 84dc72cd8a..467441d841 100644 --- a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json +++ b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json @@ -125,11 +125,11 @@ ], "idx": 1, "istable": 1, - "modified": "2019-10-03 14:55:52.786805", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Maintenance", "name": "Maintenance Visit Purpose", - "owner": "ashwini@webnotestech.com", + "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC", diff --git a/erpnext/projects/doctype/dependent_task/dependent_task.json b/erpnext/projects/doctype/dependent_task/dependent_task.json index e00a2b287a..858a554bad 100644 --- a/erpnext/projects/doctype/dependent_task/dependent_task.json +++ b/erpnext/projects/doctype/dependent_task/dependent_task.json @@ -48,8 +48,8 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-10-26 17:37:25.638190", - "modified_by": "rohitw1991@gmail.com", + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", "module": "Projects", "name": "Dependent Task", "name_case": "", diff --git a/erpnext/selling/doctype/industry_type/industry_type.json b/erpnext/selling/doctype/industry_type/industry_type.json index f4fcae428e..6c49f0f6dd 100644 --- a/erpnext/selling/doctype/industry_type/industry_type.json +++ b/erpnext/selling/doctype/industry_type/industry_type.json @@ -49,11 +49,11 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-07-25 05:24:25.881361", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Selling", "name": "Industry Type", - "owner": "harshada@webnotestech.com", + "owner": "Administrator", "permissions": [ { "amend": 0, diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.json b/erpnext/selling/doctype/product_bundle/product_bundle.json index b63fb4bdcf..56155fb750 100644 --- a/erpnext/selling/doctype/product_bundle/product_bundle.json +++ b/erpnext/selling/doctype/product_bundle/product_bundle.json @@ -238,8 +238,8 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2017-10-18 14:23:06.538568", - "modified_by": "tundebabzy@gmail.com", + "modified": "2020-09-18 17:26:09.703215", + "modified_by": "Administrator", "module": "Selling", "name": "Product Bundle", "owner": "Administrator", diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json index 1eb457734e..943cb3401f 100644 --- a/erpnext/stock/doctype/batch/batch.json +++ b/erpnext/stock/doctype/batch/batch.json @@ -166,11 +166,11 @@ "idx": 1, "image_field": "image", "max_attachments": 5, - "modified": "2019-10-03 22:38:45.104056", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Stock", "name": "Batch", - "owner": "harshada@webnotestech.com", + "owner": "Administrator", "permissions": [ { "create": 1, diff --git a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json index 90a392c145..b24d621c31 100644 --- a/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json +++ b/erpnext/stock/doctype/landed_cost_item/landed_cost_item.json @@ -133,11 +133,11 @@ ], "idx": 1, "istable": 1, - "modified": "2019-11-12 15:41:21.053462", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Item", - "owner": "wasim@webnotestech.com", + "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC" diff --git a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json index f49c147682..9b2b5da9cb 100644 --- a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json +++ b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json @@ -173,11 +173,11 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-07-20 10:49:34.228751", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Stock", "name": "Landed Cost Purchase Receipt", - "owner": "wasim@webnotestech.com", + "owner": "Administrator", "permissions": [], "quick_entry": 0, "read_only": 0, diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.json b/erpnext/support/doctype/warranty_claim/warranty_claim.json index ae1a7a569d..88ee4a3beb 100644 --- a/erpnext/support/doctype/warranty_claim/warranty_claim.json +++ b/erpnext/support/doctype/warranty_claim/warranty_claim.json @@ -361,11 +361,11 @@ ], "icon": "fa fa-bug", "idx": 1, - "modified": "2019-05-24 10:56:30.626200", + "modified": "2020-09-18 17:26:09.703215", "modified_by": "Administrator", "module": "Support", "name": "Warranty Claim", - "owner": "harshada@webnotestech.com", + "owner": "Administrator", "permissions": [ { "create": 1, From 0aff6d83af06f56bcbf30986b5659247754250f5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sat, 19 Sep 2020 18:05:58 +0530 Subject: [PATCH 51/60] Update erpnext/public/js/utils/party.js Co-authored-by: Sagar Vora --- erpnext/public/js/utils/party.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index af1f433514..44e75aee36 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -225,7 +225,7 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) { } if (!frm.doc.company) { - frappe.throw(_("Kindly select the company first")); + frappe.throw(__("Kindly select the company first")); } frappe.call({ @@ -296,4 +296,4 @@ erpnext.utils.get_shipping_address = function(frm, callback){ } else { frappe.msgprint(__("Select company first")); } -} \ No newline at end of file +} From c646a56d68a8ab6b13aaa289a214f3f3bed918ee Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sat, 19 Sep 2020 18:28:20 +0530 Subject: [PATCH 52/60] fix: Update modified timestamp --- erpnext/non_profit/doctype/membership/membership.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/non_profit/doctype/membership/membership.json b/erpnext/non_profit/doctype/membership/membership.json index b95ae9738c..96dea84a92 100644 --- a/erpnext/non_profit/doctype/membership/membership.json +++ b/erpnext/non_profit/doctype/membership/membership.json @@ -128,7 +128,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2020-07-27 14:28:11.532696", + "modified": "2020-09-19 14:28:11.532696", "modified_by": "Administrator", "module": "Non Profit", "name": "Membership", From 4b25eb4db48047ea3d9d8b85efe6b66a26fe160c Mon Sep 17 00:00:00 2001 From: Saqib Date: Sat, 19 Sep 2020 19:33:21 +0530 Subject: [PATCH 53/60] fix: mode of payment not fetched if no account is set (#23334) Co-authored-by: Sagar Vora --- .../pos_opening_entry/pos_opening_entry.py | 11 +++++++- .../doctype/pos_profile/pos_profile.py | 27 ++++++++++++------- .../doctype/sales_invoice/sales_invoice.py | 18 ++++++------- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py index 15f23b63dc..b9e07b8030 100644 --- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py +++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py @@ -5,13 +5,14 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cint +from frappe.utils import cint, get_link_to_form from frappe.model.document import Document from erpnext.controllers.status_updater import StatusUpdater class POSOpeningEntry(StatusUpdater): def validate(self): self.validate_pos_profile_and_cashier() + self.validate_payment_method_account() self.set_status() def validate_pos_profile_and_cashier(self): @@ -20,6 +21,14 @@ class POSOpeningEntry(StatusUpdater): if not cint(frappe.db.get_value("User", self.user, "enabled")): frappe.throw(_("User {} has been disabled. Please select valid user/cashier".format(self.user))) + + def validate_payment_method_account(self): + for d in self.balance_details: + account = frappe.db.get_value("Mode of Payment Account", + {"parent": d.mode_of_payment, "company": self.company}, "default_account") + if not account: + frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}") + .format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account")) def on_submit(self): self.set_status(update=True) \ No newline at end of file diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.py b/erpnext/accounts/doctype/pos_profile/pos_profile.py index 789b4c3bd9..1386b70f55 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.py +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import msgprint, _ -from frappe.utils import cint, now +from frappe.utils import cint, now, get_link_to_form from six import iteritems from frappe.model.document import Document @@ -13,7 +13,7 @@ class POSProfile(Document): self.validate_default_profile() self.validate_all_link_fields() self.validate_duplicate_groups() - self.check_default_payment() + self.validate_payment_methods() def validate_default_profile(self): for row in self.applicable_for_users: @@ -52,14 +52,23 @@ class POSProfile(Document): if len(customer_groups) != len(set(customer_groups)): frappe.throw(_("Duplicate customer group found in the cutomer group table"), title = "Duplicate Customer Group") - def check_default_payment(self): - if self.payments: - default_mode_of_payment = [d.default for d in self.payments if d.default] - if not default_mode_of_payment: - frappe.throw(_("Set default mode of payment")) + def validate_payment_methods(self): + if not self.payments: + frappe.throw(_("Payment methods are mandatory. Please add at least one payment method.")) - if len(default_mode_of_payment) > 1: - frappe.throw(_("Multiple default mode of payment is not allowed")) + default_mode_of_payment = [d.default for d in self.payments if d.default] + if not default_mode_of_payment: + frappe.throw(_("Please select a default mode of payment")) + + if len(default_mode_of_payment) > 1: + frappe.throw(_("You can only select one mode of payment as default")) + + for d in self.payments: + account = frappe.db.get_value("Mode of Payment Account", + {"parent": d.mode_of_payment, "company": self.company}, "default_account") + if not account: + frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}") + .format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account")) def on_update(self): self.set_defaults() diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 71f2e120cc..92e49d59da 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe, erpnext import frappe.defaults -from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate +from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate, get_link_to_form from frappe import _, msgprint, throw from erpnext.accounts.party import get_party_account, get_due_date from frappe.model.mapper import get_mapped_doc @@ -1372,7 +1372,7 @@ def get_bank_cash_account(mode_of_payment, company): {"parent": mode_of_payment, "company": company}, "default_account") if not account: frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}") - .format(mode_of_payment)) + .format(get_link_to_form("Mode of Payment", mode_of_payment)), title=_("Missing Account")) return { "account": account } @@ -1612,18 +1612,16 @@ def update_multi_mode_option(doc, pos_profile): payment.type = payment_mode.type doc.set('payments', []) - if not pos_profile or not pos_profile.get('payments'): - for payment_mode in get_all_mode_of_payments(doc): - append_payment(payment_mode) - return - for pos_payment_method in pos_profile.get('payments'): pos_payment_method = pos_payment_method.as_dict() payment_mode = get_mode_of_payment_info(pos_payment_method.mode_of_payment, doc.company) - if payment_mode: - payment_mode[0].default = pos_payment_method.default - append_payment(payment_mode[0]) + if not payment_mode: + frappe.throw(_("Please set default Cash or Bank account in Mode of Payment {0}") + .format(get_link_to_form("Mode of Payment", pos_payment_method.mode_of_payment)), title=_("Missing Account")) + + payment_mode[0].default = pos_payment_method.default + append_payment(payment_mode[0]) def get_all_mode_of_payments(doc): return frappe.db.sql(""" From f2ea3877c8f045d8fd7107f0c567748e13005628 Mon Sep 17 00:00:00 2001 From: Saqib Date: Sun, 20 Sep 2020 19:31:18 +0530 Subject: [PATCH 54/60] fix: depreciation start date ux fixes (#23339) --- erpnext/assets/doctype/asset/asset.js | 16 +- erpnext/assets/doctype/asset/asset.py | 9 +- erpnext/assets/doctype/asset/test_asset.py | 18 +- .../asset_finance_book.json | 420 ++++-------------- .../asset_movement/test_asset_movement.py | 6 +- .../purchase_receipt/test_purchase_receipt.py | 3 +- 6 files changed, 113 insertions(+), 359 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index fba20c0c87..7ad164a8b9 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -252,13 +252,6 @@ frappe.ui.form.on('Asset', { }) }, - available_for_use_date: function(frm) { - $.each(frm.doc.finance_books || [], function(i, d) { - if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date; - }); - refresh_field("finance_books"); - }, - is_existing_asset: function(frm) { frm.trigger("toggle_reference_doc"); // frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation)); @@ -438,6 +431,15 @@ frappe.ui.form.on('Asset Finance Book', { } frappe.flags.dont_change_rate = false; + }, + + depreciation_start_date: function(frm, cdt, cdn) { + const book = locals[cdt][cdn]; + if (frm.doc.available_for_use_date && book.depreciation_start_date == frm.doc.available_for_use_date) { + frappe.msgprint(__(`Depreciation Posting Date should not be equal to Available for Use Date.`)); + book.depreciation_start_date = ""; + frm.refresh_field("finance_books"); + } } }); diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 9d08d9212d..72debb7eba 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext, math, json from frappe import _ from six import string_types -from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days +from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days, get_last_day from frappe.model.document import Document from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.assets.doctype.asset.depreciation \ @@ -83,6 +83,11 @@ class Asset(AccountsController): if not self.available_for_use_date: frappe.throw(_("Available for use date is required")) + for d in self.finance_books: + if d.depreciation_start_date == self.available_for_use_date: + frappe.throw(_("Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.").format(d.idx), + title=_("Incorrect Date")) + def set_missing_values(self): if not self.asset_category: self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category") @@ -294,7 +299,7 @@ class Asset(AccountsController): if not row.depreciation_start_date: if not self.available_for_use_date: frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx)) - row.depreciation_start_date = self.available_for_use_date + row.depreciation_start_date = get_last_day(self.available_for_use_date) if not self.is_existing_asset: self.opening_accumulated_depreciation = 0 diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index aed78e7746..52039c183b 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -371,19 +371,18 @@ class TestAsset(unittest.TestCase): asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') asset = frappe.get_doc('Asset', asset_name) asset.calculate_depreciation = 1 - asset.available_for_use_date = nowdate() - asset.purchase_date = nowdate() + asset.available_for_use_date = '2020-01-01' + asset.purchase_date = '2020-01-01' asset.append("finance_books", { "expected_value_after_useful_life": 10000, "depreciation_method": "Straight Line", - "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10, - "depreciation_start_date": nowdate() + "total_number_of_depreciations": 10, + "frequency_of_depreciation": 1 }) asset.insert() asset.submit() - post_depreciation_entries(date=add_months(nowdate(), 10)) + post_depreciation_entries(date=add_months('2020-01-01', 4)) scrap_asset(asset.name) @@ -392,9 +391,9 @@ class TestAsset(unittest.TestCase): self.assertTrue(asset.journal_entry_for_scrap) expected_gle = ( - ("_Test Accumulated Depreciations - _TC", 30000.0, 0.0), + ("_Test Accumulated Depreciations - _TC", 36000.0, 0.0), ("_Test Fixed Asset - _TC", 0.0, 100000.0), - ("_Test Gain/Loss on Asset Disposal - _TC", 70000.0, 0.0) + ("_Test Gain/Loss on Asset Disposal - _TC", 64000.0, 0.0) ) gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` @@ -466,8 +465,7 @@ class TestAsset(unittest.TestCase): "expected_value_after_useful_life": 10000, "depreciation_method": "Straight Line", "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10, - "depreciation_start_date": "2020-06-06" + "frequency_of_depreciation": 10 }) asset.insert() accumulated_depreciation_after_full_schedule = \ diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index c80f95e155..79fcb957d4 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -1,347 +1,99 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-05-08 14:44:37.095570", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2018-05-08 14:44:37.095570", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "finance_book", + "depreciation_method", + "total_number_of_depreciations", + "column_break_5", + "frequency_of_depreciation", + "depreciation_start_date", + "expected_value_after_useful_life", + "value_after_depreciation", + "rate_of_depreciation" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "finance_book", - "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": "Finance Book", - "length": 0, - "no_copy": 0, - "options": "Finance Book", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "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": "finance_book", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Finance Book", + "options": "Finance Book" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "depreciation_method", - "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": "Depreciation Method", - "length": 0, - "no_copy": 0, - "options": "\nStraight Line\nDouble Declining Balance\nWritten Down Value\nManual", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "depreciation_method", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Depreciation Method", + "options": "\nStraight Line\nDouble Declining Balance\nWritten Down Value\nManual", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_number_of_depreciations", - "fieldtype": "Int", - "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": "Total Number of Depreciations", - "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 - }, + "fieldname": "total_number_of_depreciations", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Total Number of Depreciations", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "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, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "frequency_of_depreciation", - "fieldtype": "Int", - "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": "Frequency of Depreciation (Months)", - "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 - }, + "fieldname": "frequency_of_depreciation", + "fieldtype": "Int", + "in_list_view": 1, + "label": "Frequency of Depreciation (Months)", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:parent.doctype == 'Asset'", - "fetch_if_empty": 0, - "fieldname": "depreciation_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": "Depreciation 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 - }, + "depends_on": "eval:parent.doctype == 'Asset'", + "fieldname": "depreciation_start_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Depreciation Posting Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:parent.doctype == 'Asset'", - "fetch_if_empty": 0, - "fieldname": "expected_value_after_useful_life", - "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": "Expected Value After Useful Life", - "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 - }, + "default": "0", + "depends_on": "eval:parent.doctype == 'Asset'", + "fieldname": "expected_value_after_useful_life", + "fieldtype": "Currency", + "label": "Expected Value After Useful Life", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "value_after_depreciation", - "fieldtype": "Currency", - "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": "Value After Depreciation", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "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": "value_after_depreciation", + "fieldtype": "Currency", + "hidden": 1, + "label": "Value After Depreciation", + "no_copy": 1, + "options": "Company:company:default_currency", + "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, - "depends_on": "eval:doc.depreciation_method == 'Written Down Value'", - "description": "In Percentage", - "fetch_if_empty": 0, - "fieldname": "rate_of_depreciation", - "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": "Rate of Depreciation", - "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 + "depends_on": "eval:doc.depreciation_method == 'Written Down Value'", + "description": "In Percentage", + "fieldname": "rate_of_depreciation", + "fieldtype": "Percent", + "label": "Rate of Depreciation" } - ], - "has_web_view": 0, - "hide_toolbar": 0, - "idx": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-04-09 19:45:14.523488", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Finance Book", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2020-09-16 12:11:30.631788", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Finance Book", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py index c3755a3fb9..cddee5fa0f 100644 --- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py @@ -32,8 +32,7 @@ class TestAssetMovement(unittest.TestCase): "next_depreciation_date": "2020-12-31", "depreciation_method": "Straight Line", "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10, - "depreciation_start_date": "2020-06-06" + "frequency_of_depreciation": 10 }) if asset.docstatus == 0: @@ -82,8 +81,7 @@ class TestAssetMovement(unittest.TestCase): "next_depreciation_date": "2020-12-31", "depreciation_method": "Straight Line", "total_number_of_depreciations": 3, - "frequency_of_depreciation": 10, - "depreciation_start_date": "2020-06-06" + "frequency_of_depreciation": 10 }) if asset.docstatus == 0: asset.submit() diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 67161aa6dd..1e7153e774 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -494,8 +494,7 @@ class TestPurchaseReceipt(unittest.TestCase): "expected_value_after_useful_life": 10, "depreciation_method": "Straight Line", "total_number_of_depreciations": 3, - "frequency_of_depreciation": 1, - "depreciation_start_date": frappe.utils.nowdate() + "frequency_of_depreciation": 1 }) asset.submit() From c152b92ef4ffa5ffb2ac18dc879106f10f8f4a79 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sun, 20 Sep 2020 20:04:10 +0530 Subject: [PATCH 55/60] Update erpnext/controllers/selling_controller.py Co-authored-by: Sagar Vora --- erpnext/controllers/selling_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index ad06f97b7d..7f7aae31b1 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -81,7 +81,7 @@ class SellingController(StockController): party_details = _get_party_details(customer, ignore_permissions=self.flags.ignore_permissions, doctype=self.doctype, company=self.company, - posting_date=self.posting_date if hasattr(self, 'posting_date') else None, + posting_date=self.get('posting_date'), fetch_payment_terms_template=fetch_payment_terms_template, party_address=self.customer_address, shipping_address=self.shipping_address_name) if not self.meta.get_field("sales_team"): From 97f61d233e75a8b0eeb757a7c6085976cf9031ed Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Mon, 21 Sep 2020 13:00:43 +0530 Subject: [PATCH 56/60] fix: breadcrumbs for maintenance visit and schedule under support --- erpnext/public/js/conf.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js index 2af9140f9e..eb709e5e85 100644 --- a/erpnext/public/js/conf.js +++ b/erpnext/public/js/conf.js @@ -11,7 +11,9 @@ $.extend(frappe.breadcrumbs.preferred, { "Territory": "Selling", "Sales Person": "Selling", "Sales Partner": "Selling", - "Brand": "Stock" + "Brand": "Stock", + "Maintenance Schedule": "Support", + "Maintenance Visit": "Support" }); $.extend(frappe.breadcrumbs.module_map, { From e42f08085b9f165422210e056e8c04820a619e09 Mon Sep 17 00:00:00 2001 From: Rohan Date: Mon, 21 Sep 2020 13:14:07 +0530 Subject: [PATCH 57/60] fix: use Plaid's new API (develop) (#23318) Co-authored-by: Mangesh-Khairnar --- .../bank_reconciliation.js | 38 ++++---- .../doctype/plaid_settings/plaid_connector.py | 97 ++++++++++--------- .../doctype/plaid_settings/plaid_settings.js | 57 +++++------ .../plaid_settings/plaid_settings.json | 11 +-- .../doctype/plaid_settings/plaid_settings.py | 50 ++++++---- .../plaid_settings/test_plaid_settings.py | 21 ++-- .../v12_0/move_plaid_settings_to_doctype.py | 5 +- requirements.txt | 2 +- 8 files changed, 149 insertions(+), 132 deletions(-) diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js index efc76f9158..9703527875 100644 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js +++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js @@ -72,7 +72,7 @@ erpnext.accounts.bankReconciliation = class BankReconciliation { check_plaid_status() { const me = this; frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => { - if (r && r.enabled == "1") { + if (r && r.enabled === "1") { me.plaid_status = "active" } else { me.plaid_status = "inactive" @@ -139,7 +139,7 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload { } make() { - const me = this; + const me = this; new frappe.ui.FileUploader({ method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement', allow_multiple: 0, @@ -214,31 +214,35 @@ erpnext.accounts.bankTransactionSync = class bankTransactionSync { init_config() { const me = this; - frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration') - .then(result => { - me.plaid_env = result.plaid_env; - me.plaid_public_key = result.plaid_public_key; - me.client_name = result.client_name; - me.sync_transactions() - }) + frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_plaid_configuration') + .then(result => { + me.plaid_env = result.plaid_env; + me.client_name = result.client_name; + me.link_token = result.link_token; + me.sync_transactions(); + }) } sync_transactions() { const me = this; - frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (v) => { + frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (r) => { frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', { - bank: v['bank'], + bank: r.bank, bank_account: me.parent.bank_account, freeze: true }) .then((result) => { - let result_title = (result.length > 0) ? __("{0} bank transaction(s) created", [result.length]) : __("This bank account is already synchronized") + let result_title = (result && result.length > 0) + ? __("{0} bank transaction(s) created", [result.length]) + : __("This bank account is already synchronized"); + let result_msg = ` -
-
${result_title}
-
` +
+
${result_title}
+
` + this.parent.$main_section.append(result_msg) - frappe.show_alert({message:__("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator:'green'}); + frappe.show_alert({ message: __("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator: 'green' }); }) }) } @@ -384,7 +388,7 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow { }) frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments', - {bank_transaction: data, freeze:true, freeze_message:__("Finding linked payments")} + { bank_transaction: data, freeze: true, freeze_message: __("Finding linked payments") } ).then((result) => { me.make_dialog(result) }) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py index 532e19cffd..f8154f2edd 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py @@ -2,81 +2,84 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals -from frappe import _ -from frappe.utils.password import get_decrypted_password -from plaid import Client -from plaid.errors import APIError, ItemError +import plaid +import requests +from plaid.errors import APIError, ItemError, InvalidRequestError import frappe -import requests +from frappe import _ + class PlaidConnector(): def __init__(self, access_token=None): - - plaid_settings = frappe.get_single("Plaid Settings") - - self.config = { - "plaid_client_id": plaid_settings.plaid_client_id, - "plaid_secret": get_decrypted_password("Plaid Settings", "Plaid Settings", 'plaid_secret'), - "plaid_public_key": plaid_settings.plaid_public_key, - "plaid_env": plaid_settings.plaid_env - } - - self.client = Client(client_id=self.config.get("plaid_client_id"), - secret=self.config.get("plaid_secret"), - public_key=self.config.get("plaid_public_key"), - environment=self.config.get("plaid_env") - ) - self.access_token = access_token + self.settings = frappe.get_single("Plaid Settings") + self.products = ["auth", "transactions"] + self.client_name = frappe.local.site + self.client = plaid.Client( + client_id=self.settings.plaid_client_id, + secret=self.settings.get_password("plaid_secret"), + environment=self.settings.plaid_env, + api_version="2019-05-29" + ) def get_access_token(self, public_token): if public_token is None: frappe.log_error(_("Public token is missing for this bank"), _("Plaid public token error")) - response = self.client.Item.public_token.exchange(public_token) - access_token = response['access_token'] - + access_token = response["access_token"] return access_token + def get_link_token(self): + token_request = { + "client_name": self.client_name, + "client_id": self.settings.plaid_client_id, + "secret": self.settings.plaid_secret, + "products": self.products, + # only allow Plaid-supported languages and countries (LAST: Sep-19-2020) + "language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en", + "country_codes": ["US", "CA", "FR", "IE", "NL", "ES", "GB"], + "user": { + "client_user_id": frappe.generate_hash(frappe.session.user, length=32) + } + } + + try: + response = self.client.LinkToken.create(token_request) + except InvalidRequestError: + frappe.log_error(frappe.get_traceback(), _("Plaid invalid request error")) + frappe.msgprint(_("Please check your Plaid client ID and secret values")) + except APIError as e: + frappe.log_error(frappe.get_traceback(), _("Plaid authentication error")) + frappe.throw(_(str(e)), title=_("Authentication Failed")) + else: + return response["link_token"] + def auth(self): try: self.client.Auth.get(self.access_token) - print("Authentication successful.....") except ItemError as e: - if e.code == 'ITEM_LOGIN_REQUIRED': - pass - else: + if e.code == "ITEM_LOGIN_REQUIRED": pass except APIError as e: - if e.code == 'PLANNED_MAINTENANCE': - pass - else: + if e.code == "PLANNED_MAINTENANCE": pass except requests.Timeout: pass except Exception as e: - print(e) frappe.log_error(frappe.get_traceback(), _("Plaid authentication error")) - frappe.msgprint({"title": _("Authentication Failed"), "message":e, "raise_exception":1, "indicator":'red'}) + frappe.throw(_(str(e)), title=_("Authentication Failed")) def get_transactions(self, start_date, end_date, account_id=None): + self.auth() + account_ids = list(account_id) if account_id else None + try: - self.auth() - if account_id: - account_ids = [account_id] - - response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, account_ids=account_ids) - - else: - response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date) - - transactions = response['transactions'] - - while len(transactions) < response['total_transactions']: + response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, account_ids=account_ids) + transactions = response["transactions"] + while len(transactions) < response["total_transactions"]: response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions)) - transactions.extend(response['transactions']) + transactions.extend(response["transactions"]) return transactions except Exception: frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js index 0ffbb877ea..22a4004955 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js @@ -4,14 +4,14 @@ frappe.provide("erpnext.integrations"); frappe.ui.form.on('Plaid Settings', { - enabled: function(frm) { + enabled: function (frm) { frm.toggle_reqd('plaid_client_id', frm.doc.enabled); frm.toggle_reqd('plaid_secret', frm.doc.enabled); - frm.toggle_reqd('plaid_public_key', frm.doc.enabled); frm.toggle_reqd('plaid_env', frm.doc.enabled); }, - refresh: function(frm) { - if(frm.doc.enabled) { + + refresh: function (frm) { + if (frm.doc.enabled) { frm.add_custom_button('Link a new bank account', () => { new erpnext.integrations.plaidLink(frm); }); @@ -22,17 +22,16 @@ frappe.ui.form.on('Plaid Settings', { erpnext.integrations.plaidLink = class plaidLink { constructor(parent) { this.frm = parent; - this.product = ["transactions", "auth"]; this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js'; this.init_config(); } - init_config() { - const me = this; - me.plaid_env = me.frm.doc.plaid_env; - me.plaid_public_key = me.frm.doc.plaid_public_key; - me.client_name = frappe.boot.sitename; - me.init_plaid(); + async init_config() { + this.product = ["auth", "transactions"]; + this.plaid_env = this.frm.doc.plaid_env; + this.client_name = frappe.boot.sitename; + this.token = await this.frm.call("get_link_token").then(resp => resp.message); + this.init_plaid(); } init_plaid() { @@ -69,17 +68,17 @@ erpnext.integrations.plaidLink = class plaidLink { } onScriptLoaded(me) { - me.linkHandler = window.Plaid.create({ + me.linkHandler = Plaid.create({ clientName: me.client_name, + product: me.product, env: me.plaid_env, - key: me.plaid_public_key, - onSuccess: me.plaid_success, - product: me.product + token: me.token, + onSuccess: me.plaid_success }); } onScriptError(error) { - frappe.msgprint('There was an issue loading the link-initialize.js script'); + frappe.msgprint("There was an issue connecting to Plaid's authentication server"); frappe.msgprint(error); } @@ -87,21 +86,25 @@ erpnext.integrations.plaidLink = class plaidLink { const me = this; frappe.prompt({ - fieldtype:"Link", + fieldtype: "Link", options: "Company", - label:__("Company"), - fieldname:"company", - reqd:1 + label: __("Company"), + fieldname: "company", + reqd: 1 }, (data) => { me.company = data.company; - frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', {token: token, response: response}) - .then((result) => { - frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', {response: response, - bank: result, company: me.company}); - }) - .then(() => { - frappe.show_alert({message:__("Bank accounts added"), indicator:'green'}); + frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_institution', { + token: token, + response: response + }).then((result) => { + frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.add_bank_accounts', { + response: response, + bank: result, + company: me.company }); + }).then(() => { + frappe.show_alert({ message: __("Bank accounts added"), indicator: 'green' }); + }); }, __("Select a company"), __("Continue")); } }; diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json index d8203d7390..2706217223 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json @@ -1,5 +1,4 @@ { - "actions": [], "creation": "2018-10-25 10:02:48.656165", "doctype": "DocType", "editable_grid": 1, @@ -12,7 +11,6 @@ "plaid_client_id", "plaid_secret", "column_break_7", - "plaid_public_key", "plaid_env" ], "fields": [ @@ -41,12 +39,6 @@ "in_list_view": 1, "label": "Plaid Secret" }, - { - "fieldname": "plaid_public_key", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Plaid Public Key" - }, { "fieldname": "plaid_env", "fieldtype": "Select", @@ -69,8 +61,7 @@ } ], "issingle": 1, - "links": [], - "modified": "2020-02-07 15:21:11.616231", + "modified": "2020-09-12 02:31:44.542385", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Plaid Settings", diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index c3371ed5df..e535e81bde 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -2,30 +2,36 @@ # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt -from __future__ import unicode_literals -import frappe import json -from frappe import _ -from frappe.model.document import Document + +import frappe from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector -from frappe.utils import getdate, formatdate, today, add_months +from frappe import _ from frappe.desk.doctype.tag.tag import add_tag +from frappe.model.document import Document +from frappe.utils import add_months, formatdate, getdate, today + class PlaidSettings(Document): - pass + @staticmethod + def get_link_token(): + plaid = PlaidConnector() + return plaid.get_link_token() + @frappe.whitelist() -def plaid_configuration(): +def get_plaid_configuration(): if frappe.db.get_single_value("Plaid Settings", "enabled"): plaid_settings = frappe.get_single("Plaid Settings") return { - "plaid_public_key": plaid_settings.plaid_public_key, "plaid_env": plaid_settings.plaid_env, + "link_token": plaid_settings.get_link_token(), "client_name": frappe.local.site } - else: - return "disabled" + + return "disabled" + @frappe.whitelist() def add_institution(token, response): @@ -33,6 +39,7 @@ def add_institution(token, response): plaid = PlaidConnector() access_token = plaid.get_access_token(token) + bank = None if not frappe.db.exists("Bank", response["institution"]["name"]): try: @@ -44,7 +51,6 @@ def add_institution(token, response): bank.insert() except Exception: frappe.throw(frappe.get_traceback()) - else: bank = frappe.get_doc("Bank", response["institution"]["name"]) bank.plaid_access_token = access_token @@ -52,6 +58,7 @@ def add_institution(token, response): return bank + @frappe.whitelist() def add_bank_accounts(response, bank, company): try: @@ -92,9 +99,8 @@ def add_bank_accounts(response, bank, company): new_account.insert() result.append(new_account.name) - except frappe.UniqueValidationError: - frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(new_account.account_name)) + frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"])) except Exception: frappe.throw(frappe.get_traceback()) @@ -103,6 +109,7 @@ def add_bank_accounts(response, bank, company): return result + def add_account_type(account_type): try: frappe.get_doc({ @@ -122,10 +129,11 @@ def add_account_subtype(account_subtype): except Exception: frappe.throw(frappe.get_traceback()) + @frappe.whitelist() def sync_transactions(bank, bank_account): - '''Sync transactions based on the last integration date as the start date, after the sync is completed - add the transaction date of the oldest transaction as the last integration date''' + """Sync transactions based on the last integration date as the start date, after sync is completed + add the transaction date of the oldest transaction as the last integration date.""" last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date") if last_transaction_date: start_date = formatdate(last_transaction_date, "YYYY-MM-dd") @@ -147,10 +155,10 @@ def sync_transactions(bank, bank_account): len(result), bank_account, start_date, end_date)) frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date) - except Exception: frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error")) + def get_transactions(bank, bank_account=None, start_date=None, end_date=None): access_token = None @@ -168,6 +176,7 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None): return transactions + def new_bank_transaction(transaction): result = [] @@ -182,8 +191,8 @@ def new_bank_transaction(transaction): status = "Pending" if transaction["pending"] == "True" else "Settled" + tags = [] try: - tags = [] tags += transaction["category"] tags += ["Plaid Cat. {}".format(transaction["category_id"])] except KeyError: @@ -216,6 +225,7 @@ def new_bank_transaction(transaction): return result + def automatic_synchronization(): settings = frappe.get_doc("Plaid Settings", "Plaid Settings") @@ -223,4 +233,8 @@ def automatic_synchronization(): plaid_accounts = frappe.get_all("Bank Account", filters={"integration_id": ["!=", ""]}, fields=["name", "bank"]) for plaid_account in plaid_accounts: - frappe.enqueue("erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions", bank=plaid_account.bank, bank_account=plaid_account.name) + frappe.enqueue( + "erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions", + bank=plaid_account.bank, + bank_account=plaid_account.name + ) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py index 1a063d6b6f..3c906374c4 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py @@ -1,14 +1,17 @@ # -*- coding: utf-8 -*- # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # See license.txt -from __future__ import unicode_literals -import unittest -import frappe -from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import plaid_configuration, add_account_type, add_account_subtype, new_bank_transaction, add_bank_accounts import json -from frappe.utils.response import json_handler +import unittest + +import frappe from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account +from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import ( + add_account_subtype, add_account_type, add_bank_accounts, + new_bank_transaction, get_plaid_configuration) +from frappe.utils.response import json_handler + class TestPlaidSettings(unittest.TestCase): def setUp(self): @@ -31,7 +34,7 @@ class TestPlaidSettings(unittest.TestCase): def test_plaid_disabled(self): frappe.db.set_value("Plaid Settings", None, "enabled", 0) - self.assertTrue(plaid_configuration() == "disabled") + self.assertTrue(get_plaid_configuration() == "disabled") def test_add_account_type(self): add_account_type("brokerage") @@ -64,7 +67,7 @@ class TestPlaidSettings(unittest.TestCase): 'mask': '0000', 'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK', 'name': 'Plaid Checking' - }], + }], 'institution': { 'institution_id': 'ins_6', 'name': 'Citi' @@ -100,7 +103,7 @@ class TestPlaidSettings(unittest.TestCase): 'mask': '0000', 'id': '6GbM6RRQgdfy3lAqGz4JUnpmR948WZFg8DjQK', 'name': 'Plaid Checking' - }], + }], 'institution': { 'institution_id': 'ins_6', 'name': 'Citi' @@ -152,4 +155,4 @@ class TestPlaidSettings(unittest.TestCase): new_bank_transaction(transactions) - self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1) \ No newline at end of file + self.assertTrue(len(frappe.get_all("Bank Transaction")) == 1) diff --git a/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py b/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py index 8e60d33f85..d2bcb12070 100644 --- a/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py +++ b/erpnext/patches/v12_0/move_plaid_settings_to_doctype.py @@ -4,17 +4,16 @@ from __future__ import unicode_literals import frappe + def execute(): frappe.reload_doc("erpnext_integrations", "doctype", "plaid_settings") plaid_settings = frappe.get_single("Plaid Settings") if plaid_settings.enabled: - if not (frappe.conf.plaid_client_id and frappe.conf.plaid_env \ - and frappe.conf.plaid_public_key and frappe.conf.plaid_secret): + if not (frappe.conf.plaid_client_id and frappe.conf.plaid_env and frappe.conf.plaid_secret): plaid_settings.enabled = 0 else: plaid_settings.update({ "plaid_client_id": frappe.conf.plaid_client_id, - "plaid_public_key": frappe.conf.plaid_public_key, "plaid_env": frappe.conf.plaid_env, "plaid_secret": frappe.conf.plaid_secret }) diff --git a/requirements.txt b/requirements.txt index 912d61f7a6..b7ba412893 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ frappe gocardless-pro==1.11.0 googlemaps==3.1.1 pandas==1.0.5 -plaid-python==3.4.0 +plaid-python==6.0.0 pycountry==19.8.18 PyGithub==1.44.1 python-stdnum==1.12 From de7a2bc4be092133709133cb7c1fcb3ffd608201 Mon Sep 17 00:00:00 2001 From: prssanna Date: Mon, 21 Sep 2020 13:57:04 +0530 Subject: [PATCH 58/60] fix: apply user permissions in tax accounts query --- erpnext/controllers/queries.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index c88bf66411..10ff6ad8d9 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -165,9 +165,14 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters): AND company = %(company)s AND account_currency = %(currency)s AND `{searchfield}` LIKE %(txt)s + {mcond} ORDER BY idx DESC, name LIMIT %(offset)s, %(limit)s - """.format(account_type_condition=account_type_condition, searchfield=searchfield), + """.format( + account_type_condition=account_type_condition, + searchfield=searchfield, + mcond=get_match_cond(doctype) + ), dict( account_types=filters.get("account_type"), company=filters.get("company"), From e3c61286385c80f0bde88c03271702841fefc30f Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 21 Sep 2020 18:50:22 +0530 Subject: [PATCH 59/60] fix(plaid): do not send null list in account ids (#23376) --- .../doctype/plaid_settings/plaid_connector.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py index f8154f2edd..a033a2a722 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py @@ -72,10 +72,16 @@ class PlaidConnector(): def get_transactions(self, start_date, end_date, account_id=None): self.auth() - account_ids = list(account_id) if account_id else None + kwargs = dict( + access_token=self.access_token, + start_date=start_date, + end_date=end_date + ) + if account_id: + kwargs.update(dict(account_ids=[account_id])) try: - response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, account_ids=account_ids) + response = self.client.Transactions.get(**kwargs) transactions = response["transactions"] while len(transactions) < response["total_transactions"]: response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions)) From 2b53b98c1d438c6e43e1d5cdf1086001e7b1faec Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 22 Sep 2020 12:18:12 +0530 Subject: [PATCH 60/60] fix: reload dashboard (#23380) --- erpnext/patches.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 9ce570e6d0..a919b670cb 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -690,6 +690,7 @@ erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation erpnext.patches.v13_0.update_old_loans erpnext.patches.v12_0.set_serial_no_status #2020-05-21 erpnext.patches.v12_0.update_price_list_currency_in_bom +execute:frappe.reload_doctype('Dashboard') execute:frappe.delete_doc_if_exists('Dashboard', 'Accounts') erpnext.patches.v13_0.update_actual_start_and_end_date_in_wo erpnext.patches.v13_0.set_company_field_in_healthcare_doctypes #2020-05-25