From 4f2a64479dfcaf6a4bc164315abb4865723f75fe Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 9 Nov 2020 17:00:09 +0530 Subject: [PATCH 01/20] fix: Patch for old loans --- erpnext/patches/v13_0/update_old_loans.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 77239429c51..eaeda093f5f 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -70,7 +70,7 @@ def execute(): payments = frappe.db.sql(''' SELECT j.name, a.debit, a.debit_in_account_currency, j.posting_date FROM `tabJournal Entry` j, `tabJournal Entry Account` a WHERE a.parent = j.name and a.reference_type='Loan' and a.reference_name = %s - and account = %s + and a.account = %s and j.docstatus = 1 ''', (loan.name, loan.loan_account), as_dict=1) for payment in payments: From 928dc432aba2cde284072b958c2ba3d801e4aeee Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 9 Nov 2020 17:17:12 +0530 Subject: [PATCH 02/20] fix: Reload journal entry account doc --- erpnext/patches/v13_0/update_old_loans.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index eaeda093f5f..dd15f10e097 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -18,6 +18,7 @@ def execute(): frappe.reload_doc('loan_management', 'doctype', 'loan_repayment_detail') frappe.reload_doc('loan_management', 'doctype', 'loan_interest_accrual') frappe.reload_doc('accounts', 'doctype', 'gl_entry') + frappe.reload_doc('accounts', 'doctype', 'journal_entry_account') updated_loan_types = [] From 1c969d64a2f928f78470c4dd42899d2393c6291f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Nov 2020 20:25:35 +0530 Subject: [PATCH 03/20] fix: Handle cases where same loan type is used for multiple companies --- erpnext/patches/v13_0/update_old_loans.py | 35 +++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index dd15f10e097..2925f0a5bcb 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -23,7 +23,8 @@ def execute(): updated_loan_types = [] loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment', - 'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account']) + 'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'], + filters={'docstatus': 1}) for loan in loans: # Update details in Loan Types and Loan @@ -39,7 +40,26 @@ def execute(): penalty_account = create_account(company=loan.company, account_type='Income Account', account_name='Penalty Account', parent_account=group_income_account) - if not loan_type_company: + # Same loan type used for multiple companies + if loan_type_company and loan_type_company != loan.company: + # get loan type for appropriate company + loan_type_name = frappe.get_value('Loan Type', {'company': loan.company, + 'mode_of_payment': loan.mode_of_payment, 'loan_account': loan.loan_account, + 'payment_account': loan.payment_account, 'interest_income_account': loan.interest_income_account, + 'penalty_income_account': loan.penalty_income_account}, 'name') + + if not loan_type_name: + loan_type_name = loan.loan_type + " - " + ''.join([c[0] for c in loan.company.split()]).upper() + create_loan_type(loan, loan_type_name, penalty_account) + + # update loan type in loan + frappe.db.sql("UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name, + loan.name)) + + if loan_type_name not in updated_loan_types: + updated_loan_types.append(loan_type_name) + + elif not loan_type_company: loan_type_doc = frappe.get_doc('Loan Type', loan.loan_type) loan_type_doc.is_term_loan = 1 loan_type_doc.company = loan.company @@ -87,3 +107,14 @@ def execute(): jv.flags.ignore_links = True jv.cancel() +def create_loan_type(loan, loan_type_name, penalty_account): + loan_type_doc = frappe.new_doc('Loan Type') + loan_type_doc.loan_name = loan_type_name + loan_type_doc.is_term_loan = 1 + loan_type_doc.company = loan.company + loan_type_doc.mode_of_payment = loan.mode_of_payment + loan_type_doc.payment_account = loan.payment_account + loan_type_doc.loan_account = loan.loan_account + loan_type_doc.interest_income_account = loan.interest_income_account + loan_type_doc.penalty_income_account = penalty_account + loan_type_doc.submit() From 73bde45bc5f488906f911cef57e6035712b38cb0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Nov 2020 22:08:02 +0530 Subject: [PATCH 04/20] fix: Pass updated loan type --- erpnext/patches/v13_0/update_old_loans.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 2925f0a5bcb..c16c2c81b86 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -29,6 +29,7 @@ def execute(): for loan in loans: # Update details in Loan Types and Loan loan_type_company = frappe.db.get_value('Loan Type', loan.loan_type, 'company') + loan_type = loan.loan_type group_income_account = frappe.get_value('Account', {'company': loan.company, 'is_group': 1, 'root_type': 'Income', 'account_name': _('Indirect Income')}) @@ -56,6 +57,7 @@ def execute(): frappe.db.sql("UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name, loan.name)) + loan_type = loan_type_name if loan_type_name not in updated_loan_types: updated_loan_types.append(loan_type_name) @@ -70,8 +72,9 @@ def execute(): loan_type_doc.penalty_income_account = penalty_account loan_type_doc.submit() updated_loan_types.append(loan.loan_type) + loan_type = loan.loan_type - if loan.loan_type in updated_loan_types: + if loan_type in updated_loan_types: if loan.status == 'Fully Disbursed': status = 'Disbursed' elif loan.status == 'Repaid/Closed': @@ -85,7 +88,7 @@ def execute(): 'status': status }) - process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan.loan_type, + process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan_type, loan=loan.name) payments = frappe.db.sql(''' SELECT j.name, a.debit, a.debit_in_account_currency, j.posting_date @@ -96,7 +99,7 @@ def execute(): for payment in payments: repayment_entry = make_repayment_entry(loan.name, loan.loan_applicant_type, loan.applicant, - loan.loan_type, loan.company) + loan_type, loan.company) repayment_entry.amount_paid = payment.debit_in_account_currency repayment_entry.posting_date = payment.posting_date From 13d1dda74b88c8c46d0e3adf618433e687c6cda2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 11 Nov 2020 11:07:17 +0530 Subject: [PATCH 05/20] fix: Handle loan type naming collisions --- erpnext/patches/v13_0/update_old_loans.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index c16c2c81b86..70c1b7eb39f 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -51,7 +51,7 @@ def execute(): if not loan_type_name: loan_type_name = loan.loan_type + " - " + ''.join([c[0] for c in loan.company.split()]).upper() - create_loan_type(loan, loan_type_name, penalty_account) + loan_type_name = create_loan_type(loan, loan_type_name, penalty_account) # update loan type in loan frappe.db.sql("UPDATE `tabLoan` set loan_type = %s where name = %s", (loan_type_name, @@ -111,6 +111,10 @@ def execute(): jv.cancel() def create_loan_type(loan, loan_type_name, penalty_account): + + if frappe.db.get_value('Loan Type', loan_type_name): + loan_type_name = loan_type_name + '-1' + loan_type_doc = frappe.new_doc('Loan Type') loan_type_doc.loan_name = loan_type_name loan_type_doc.is_term_loan = 1 From 0dc052e635d5b9846265807af29f704105c9afc4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 11 Nov 2020 12:57:16 +0530 Subject: [PATCH 06/20] fix: Return loan type name --- erpnext/patches/v13_0/update_old_loans.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 70c1b7eb39f..fcadc6273e3 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -125,3 +125,5 @@ def create_loan_type(loan, loan_type_name, penalty_account): loan_type_doc.interest_income_account = loan.interest_income_account loan_type_doc.penalty_income_account = penalty_account loan_type_doc.submit() + + return loan_type_name From a2dc1740df6d4dea70d76d19fabadbd2dc885c2e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 11 Nov 2020 13:57:10 +0530 Subject: [PATCH 07/20] fix: Use autoname for loan creation --- erpnext/patches/v13_0/update_old_loans.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index fcadc6273e3..23e4803029a 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -5,6 +5,7 @@ from frappe.utils import nowdate from erpnext.accounts.doctype.account.test_account import create_account from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans from erpnext.loan_management.doctype.loan.loan import make_repayment_entry +from frappe.model.naming import make_autoname def execute(): @@ -50,7 +51,6 @@ def execute(): 'penalty_income_account': loan.penalty_income_account}, 'name') if not loan_type_name: - loan_type_name = loan.loan_type + " - " + ''.join([c[0] for c in loan.company.split()]).upper() loan_type_name = create_loan_type(loan, loan_type_name, penalty_account) # update loan type in loan @@ -111,12 +111,8 @@ def execute(): jv.cancel() def create_loan_type(loan, loan_type_name, penalty_account): - - if frappe.db.get_value('Loan Type', loan_type_name): - loan_type_name = loan_type_name + '-1' - loan_type_doc = frappe.new_doc('Loan Type') - loan_type_doc.loan_name = loan_type_name + loan_type_doc.loan_name = make_autoname("Loan Type-.####") loan_type_doc.is_term_loan = 1 loan_type_doc.company = loan.company loan_type_doc.mode_of_payment = loan.mode_of_payment @@ -126,4 +122,4 @@ def create_loan_type(loan, loan_type_name, penalty_account): loan_type_doc.penalty_income_account = penalty_account loan_type_doc.submit() - return loan_type_name + return loan_type_doc.name From b58dca8d942c18bc3ab0e1afa7ca7e744967c5c8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 12 Nov 2020 13:37:11 +0530 Subject: [PATCH 08/20] fix: Only update open loans --- erpnext/patches/v13_0/update_old_loans.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 23e4803029a..8ed789cf451 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -25,7 +25,7 @@ def execute(): loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment', 'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'], - filters={'docstatus': 1}) + filters={'docstatus': 1, 'status': ('!=', 'Closed')}) for loan in loans: # Update details in Loan Types and Loan From c51b340ddf46486cc98d92af03a9332c3e899517 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 12 Nov 2020 18:43:43 +0530 Subject: [PATCH 09/20] fix: Update closed loans --- erpnext/patches/v13_0/update_old_loans.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 8ed789cf451..3042db331a9 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -23,6 +23,14 @@ def execute(): updated_loan_types = [] + # Update old loan status as closed + loans_list = frappe.db.sql("""SELECT distinct parent from `tabRepayment Schedule` + where paid = 0 and docstatus = 1""", as_dict=1) + + loans_to_close = [d.parent for d in loans_list] + + frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close)) + loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment', 'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'], filters={'docstatus': 1, 'status': ('!=', 'Closed')}) @@ -91,7 +99,7 @@ def execute(): process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan_type, loan=loan.name) - payments = frappe.db.sql(''' SELECT j.name, a.debit, a.debit_in_account_currency, j.posting_date + payments = frappe.db.sql(''' SELECT j.name, a.credit, a.credit_in_account_currency, j.posting_date FROM `tabJournal Entry` j, `tabJournal Entry Account` a WHERE a.parent = j.name and a.reference_type='Loan' and a.reference_name = %s and a.account = %s and j.docstatus = 1 From 78690af440ca67b3dd9de60b585522f172dfc423 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 12 Nov 2020 18:47:34 +0530 Subject: [PATCH 10/20] fix: Update only if loans to close --- erpnext/patches/v13_0/update_old_loans.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index 3042db331a9..c7f372e26f8 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -29,7 +29,8 @@ def execute(): loans_to_close = [d.parent for d in loans_list] - frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close)) + if loans_to_close: + frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close)) loans = frappe.get_all('Loan', fields=['name', 'loan_type', 'company', 'status', 'mode_of_payment', 'applicant_type', 'applicant', 'loan_account', 'payment_account', 'interest_income_account'], From a862eb25e6c51b31eafe96f0bbfdb5f20d9d3cf2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 13 Nov 2020 17:57:57 +0530 Subject: [PATCH 11/20] fix: Make repayment entry only if amount exists --- erpnext/patches/v13_0/update_old_loans.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index c7f372e26f8..c4d9bdb7af7 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -107,17 +107,18 @@ def execute(): ''', (loan.name, loan.loan_account), as_dict=1) for payment in payments: - repayment_entry = make_repayment_entry(loan.name, loan.loan_applicant_type, loan.applicant, - loan_type, loan.company) + if payment.credit_in_account_currency: + repayment_entry = make_repayment_entry(loan.name, loan.loan_applicant_type, loan.applicant, + loan_type, loan.company) - repayment_entry.amount_paid = payment.debit_in_account_currency - repayment_entry.posting_date = payment.posting_date - repayment_entry.save() - repayment_entry.submit() + repayment_entry.amount_paid = payment.credit_in_account_currency + repayment_entry.posting_date = payment.posting_date + repayment_entry.save() + repayment_entry.submit() - jv = frappe.get_doc('Journal Entry', payment.name) - jv.flags.ignore_links = True - jv.cancel() + jv = frappe.get_doc('Journal Entry', payment.name) + jv.flags.ignore_links = True + jv.cancel() def create_loan_type(loan, loan_type_name, penalty_account): loan_type_doc = frappe.new_doc('Loan Type') From 452cbcd6eaa1d63538261ab91296b3cf2116ff79 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 30 Nov 2020 10:55:12 +0530 Subject: [PATCH 12/20] fix: Update payments directly in Loan Interest Accrual --- erpnext/patches/v13_0/update_old_loans.py | 40 ++++++++++++++--------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index c4d9bdb7af7..de29d329d11 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -5,6 +5,7 @@ from frappe.utils import nowdate from erpnext.accounts.doctype.account.test_account import create_account from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans from erpnext.loan_management.doctype.loan.loan import make_repayment_entry +from erpnext.loan_management.doctype.loan_repayment.loan_repayment import get_accrued_interest_entries from frappe.model.naming import make_autoname def execute(): @@ -100,25 +101,32 @@ def execute(): process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan_type, loan=loan.name) - payments = frappe.db.sql(''' SELECT j.name, a.credit, a.credit_in_account_currency, j.posting_date - FROM `tabJournal Entry` j, `tabJournal Entry Account` a - WHERE a.parent = j.name and a.reference_type='Loan' and a.reference_name = %s - and a.account = %s and j.docstatus = 1 - ''', (loan.name, loan.loan_account), as_dict=1) + accrued_entries = get_accrued_interest_entries(loan.name) + total_principal, total_interest = frappe.db.get_value('Repayment Schedule', fields=['sum(principal_amount) as total_principal', + 'sum(interest_amount) as total_interest'], filters={'is_paid': 1, 'parent': loan.name}) - for payment in payments: - if payment.credit_in_account_currency: - repayment_entry = make_repayment_entry(loan.name, loan.loan_applicant_type, loan.applicant, - loan_type, loan.company) + for entry in accrued_entries: + interest_paid = 0 + principal_paid = 0 - repayment_entry.amount_paid = payment.credit_in_account_currency - repayment_entry.posting_date = payment.posting_date - repayment_entry.save() - repayment_entry.submit() + if total_interest > entry.interest_amount: + interest_paid = entry.interest_amount + else: + interest_paid = total_interest - jv = frappe.get_doc('Journal Entry', payment.name) - jv.flags.ignore_links = True - jv.cancel() + if total_principal > entry.payable_principal_amount: + principal_paid = entry.payable_principal_amount + else: + principal_paid = total_principal + + frappe.db.sql(""" UPDATE `tabLoan Interest Accrual` + SET paid_principal_amount = `paid_principal_amount` + %s, + paid_interest_amount = `paid_interest_amount` + %s + WHERE name = %s""", + (principal_paid, interest_paid, entry.name)) + + total_principal -= principal_paid + total_interest -= interest_paid def create_loan_type(loan, loan_type_name, penalty_account): loan_type_doc = frappe.new_doc('Loan Type') From 2d1cfae5967b1969f6fa7351ca9239dac85cb0ab Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 5 Dec 2020 22:44:45 +0530 Subject: [PATCH 13/20] fix: fieldname --- erpnext/patches/v13_0/update_old_loans.py | 44 ++++++++++++----------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index de29d329d11..caec53b3fdf 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -101,32 +101,34 @@ def execute(): process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan_type, loan=loan.name) - accrued_entries = get_accrued_interest_entries(loan.name) - total_principal, total_interest = frappe.db.get_value('Repayment Schedule', fields=['sum(principal_amount) as total_principal', - 'sum(interest_amount) as total_interest'], filters={'is_paid': 1, 'parent': loan.name}) - for entry in accrued_entries: - interest_paid = 0 - principal_paid = 0 + if frappe.db.has_column('Repayment Schedule', 'paid'): + total_principal, total_interest = frappe.db.get_value('Repayment Schedule', {'paid': 1, 'parent': loan.name}, + ['sum(principal_amount) as total_principal', 'sum(interest_amount) as total_interest']) - if total_interest > entry.interest_amount: - interest_paid = entry.interest_amount - else: - interest_paid = total_interest + accrued_entries = get_accrued_interest_entries(loan.name) + for entry in accrued_entries: + interest_paid = 0 + principal_paid = 0 - if total_principal > entry.payable_principal_amount: - principal_paid = entry.payable_principal_amount - else: - principal_paid = total_principal + if total_interest > entry.interest_amount: + interest_paid = entry.interest_amount + else: + interest_paid = total_interest - frappe.db.sql(""" UPDATE `tabLoan Interest Accrual` - SET paid_principal_amount = `paid_principal_amount` + %s, - paid_interest_amount = `paid_interest_amount` + %s - WHERE name = %s""", - (principal_paid, interest_paid, entry.name)) + if total_principal > entry.payable_principal_amount: + principal_paid = entry.payable_principal_amount + else: + principal_paid = total_principal - total_principal -= principal_paid - total_interest -= interest_paid + frappe.db.sql(""" UPDATE `tabLoan Interest Accrual` + SET paid_principal_amount = `paid_principal_amount` + %s, + paid_interest_amount = `paid_interest_amount` + %s + WHERE name = %s""", + (principal_paid, interest_paid, entry.name)) + + total_principal -= principal_paid + total_interest -= interest_paid def create_loan_type(loan, loan_type_name, penalty_account): loan_type_doc = frappe.new_doc('Loan Type') From f82997e3c3245b2cf24faa183407d742412b8292 Mon Sep 17 00:00:00 2001 From: Mohammad Hasnain Date: Thu, 10 Dec 2020 13:34:01 +0530 Subject: [PATCH 14/20] fix(asset_val_adj): fb not getting added in je --- .../doctype/asset_value_adjustment/asset_value_adjustment.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index fd702c74c73..c2579ebf708 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -25,7 +25,7 @@ class AssetValueAdjustment(Document): frappe.throw(_("Cancel the journal entry {0} first").format(self.journal_entry)) self.reschedule_depreciations(self.current_asset_value) - + def validate_date(self): asset_purchase_date = frappe.db.get_value('Asset', self.asset, 'purchase_date') if getdate(self.date) < getdate(asset_purchase_date): @@ -53,6 +53,7 @@ class AssetValueAdjustment(Document): je.posting_date = self.date je.company = self.company je.remark = "Depreciation Entry against {0} worth {1}".format(self.asset, self.difference_amount) + je.finance_book = self.finance_book credit_entry = { "account": accumulated_depreciation_account, @@ -78,7 +79,7 @@ class AssetValueAdjustment(Document): debit_entry.update({ dimension['fieldname']: self.get(dimension['fieldname']) or dimension.get('default_dimension') }) - + je.append("accounts", credit_entry) je.append("accounts", debit_entry) From 3eea3c6c954ad9a3e774aca4afd41f219a9bc57a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 18 Nov 2020 20:17:52 +0530 Subject: [PATCH 15/20] fix: Table 'tabStock Entry Detail' is specified twice --- erpnext/controllers/status_updater.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 2555edf06b0..8c05134ae41 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -254,22 +254,26 @@ class StatusUpdater(Document): if not args.get("second_source_extra_cond"): args["second_source_extra_cond"] = "" - args['second_source_condition'] = """ + ifnull((select sum(%(second_source_field)s) + args['second_source_condition'] = frappe.db.sql(""" select ifnull((select sum(%(second_source_field)s) from `tab%(second_source_dt)s` where `%(second_join_field)s`="%(detail_id)s" - and (`tab%(second_source_dt)s`.docstatus=1) %(second_source_extra_cond)s FOR UPDATE), 0)""" % args + and (`tab%(second_source_dt)s`.docstatus=1) + %(second_source_extra_cond)s), 0) """ % args)[0][0] if args['detail_id']: if not args.get("extra_cond"): args["extra_cond"] = "" - frappe.db.sql("""update `tab%(target_dt)s` - set %(target_field)s = ( + args["source_dt_value"] = frappe.db.sql(""" (select ifnull(sum(%(source_field)s), 0) from `tab%(source_dt)s` where `%(join_field)s`="%(detail_id)s" and (docstatus=1 %(cond)s) %(extra_cond)s) - %(second_source_condition)s - ) - %(update_modified)s + """ % args)[0][0] or 0.0 + + if args['second_source_condition']: + args["source_dt_value"] += flt(args['second_source_condition']) + + frappe.db.sql("""update `tab%(target_dt)s` + set %(target_field)s = %(source_dt_value)s %(update_modified)s where name='%(detail_id)s'""" % args) def _update_percent_field_in_targets(self, args, update_modified=True): From 67dfe5db0d6241292aaad9e0f5a14f85536ea446 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 26 Nov 2020 12:55:27 +0530 Subject: [PATCH 16/20] fix: shipping chanrges not sync in erpnext from shopify --- .../connectors/shopify_connection.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index efbaa71924e..f0a05ed192f 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -260,6 +260,15 @@ def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings): """Shipping lines represents the shipping details, each such shipping detail consists of a list of tax_lines""" for shipping_charge in shipping_lines: + if shipping_charge.get("price"): + taxes.append({ + "charge_type": _("Actual"), + "account_head": get_tax_account_head(shipping_charge), + "description": shipping_charge["title"], + "tax_amount": shipping_charge["price"], + "cost_center": shopify_settings.cost_center + }) + for tax in shipping_charge.get("tax_lines"): taxes.append({ "charge_type": _("Actual"), From 5bfd6831c4bbe5a451ed0fc54ea0cf74d6fdbf38 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 6 Dec 2020 17:21:30 +0530 Subject: [PATCH 17/20] fix: delete Receive at Warehouse entry on cancellation of Send to Warehouse entry --- erpnext/stock/doctype/stock_entry/stock_entry.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index e3159b95c30..ab4f347d3a8 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -120,6 +120,7 @@ class StockEntry(StockController): self.update_transferred_qty() self.update_quality_inspection() self.delete_auto_created_batches() + self.delete_linked_stock_entry() if self.purpose == 'Material Transfer' and self.add_to_transit: self.set_material_request_transfer_status('Not Started') @@ -152,6 +153,12 @@ class StockEntry(StockController): frappe.throw(_("For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry") .format(self.job_card)) + def delete_linked_stock_entry(self): + if self.purpose == "Send to Warehouse": + for d in frappe.get_all("Stock Entry", filters={"docstatus": 0, + "outgoing_stock_entry": self.name, "purpose": "Receive at Warehouse"}): + frappe.delete_doc("Stock Entry", d.name) + def set_transfer_qty(self): for item in self.get("items"): if not flt(item.qty): From edb99d4e53ac590bd09760f5b7635b6a2e4cf14b Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 24 Nov 2020 23:23:00 +0530 Subject: [PATCH 18/20] fix: incorrect stock quantity if 'Allow Multiple Material Consumption' has enabled --- .../doctype/work_order/test_work_order.py | 33 +++++++++++++++++++ .../doctype/work_order/work_order.js | 3 +- .../stock/doctype/stock_entry/stock_entry.js | 5 +++ .../stock/doctype/stock_entry/stock_entry.py | 30 ++++++++--------- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index e53927918eb..2bf3fbf75e9 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -491,6 +491,39 @@ class TestWorkOrder(unittest.TestCase): work_order1.save() self.assertEqual(work_order1.operations[0].time_in_mins, 40.0) + def test_partial_material_consumption(self): + frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 1) + wo_order = make_wo_order_test_record(planned_start_date=now(), qty=4) + + ste_cancel_list = [] + ste1 = test_stock_entry.make_stock_entry(item_code="_Test Item", + target="_Test Warehouse - _TC", qty=20, basic_rate=5000.0) + ste2 = test_stock_entry.make_stock_entry(item_code="_Test Item Home Desktop 100", + target="_Test Warehouse - _TC", qty=20, basic_rate=1000.0) + + ste_cancel_list.extend([ste1, ste2]) + + s = frappe.get_doc(make_stock_entry(wo_order.name, "Material Transfer for Manufacture", 4)) + s.submit() + ste_cancel_list.append(s) + + ste1 = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 2)) + ste1.submit() + ste_cancel_list.append(ste1) + + print(wo_order.name) + ste3 = frappe.get_doc(make_stock_entry(wo_order.name, "Material Consumption for Manufacture", 2)) + self.assertEquals(ste3.fg_completed_qty, 2) + + expected_qty = {"_Test Item": 2, "_Test Item Home Desktop 100": 4} + for row in ste3.items: + self.assertEquals(row.qty, expected_qty.get(row.item_code)) + + for ste_doc in ste_cancel_list: + ste_doc.cancel() + + frappe.db.set_value("Manufacturing Settings", None, "material_consumption", 0) + def get_scrap_item_details(bom_no): scrap_items = {} for item in frappe.db.sql("""select item_code, stock_qty from `tabBOM Scrap Item` diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 9ce465ccaf7..a6086fb88da 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -545,7 +545,8 @@ erpnext.work_order = { var tbl = frm.doc.required_items || []; var tbl_lenght = tbl.length; for (var i = 0, len = tbl_lenght; i < len; i++) { - if (flt(frm.doc.required_items[i].required_qty) > flt(frm.doc.required_items[i].consumed_qty)) { + let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty; + if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) { counter += 1; } } diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 91217582ca4..27fcbb7e2a5 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -841,6 +841,10 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ } }, + fg_completed_qty: function() { + this.get_items(); + }, + get_items: function() { var me = this; if(!this.frm.doc.fg_completed_qty || !this.frm.doc.bom_no) @@ -850,6 +854,7 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ // if work order / bom is mentioned, get items return this.frm.call({ doc: me.frm.doc, + freeze: true, method: "get_items", callback: function(r) { if(!r.exc) refresh_field("items"); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index e3159b95c30..415f5243655 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1033,26 +1033,22 @@ class StockEntry(StockController): wo = frappe.get_doc("Work Order", self.work_order) wo_items = frappe.get_all('Work Order Item', filters={'parent': self.work_order}, - fields=["item_code", "required_qty", "consumed_qty"] + fields=["item_code", "required_qty", "consumed_qty", "transferred_qty"] ) + work_order_qty = wo.material_transferred_for_manufacturing or wo.qty for item in wo_items: - qty = item.required_qty - item_account_details = get_item_defaults(item.item_code, self.company) # Take into account consumption if there are any. - if self.purpose == 'Manufacture': - req_qty_each = flt(item.required_qty / wo.qty) - if (flt(item.consumed_qty) != 0): - remaining_qty = flt(item.consumed_qty) - (flt(wo.produced_qty) * req_qty_each) - exhaust_qty = req_qty_each * wo.produced_qty - if remaining_qty > exhaust_qty : - if (remaining_qty/(req_qty_each * flt(self.fg_completed_qty))) >= 1: - qty =0 - else: - qty = (req_qty_each * flt(self.fg_completed_qty)) - remaining_qty - else: - qty = req_qty_each * flt(self.fg_completed_qty) + + wo_item_qty = item.transferred_qty or item.required_qty + + req_qty_each = ( + (flt(wo_item_qty) - flt(item.consumed_qty)) / + (flt(work_order_qty) - flt(wo.produced_qty)) + ) + + qty = req_qty_each * flt(self.fg_completed_qty) if qty > 0: self.add_to_stock_entry_detail({ @@ -1134,13 +1130,15 @@ class StockEntry(StockController): else: qty = req_qty_each * flt(self.fg_completed_qty) - elif backflushed_materials.get(item.item_code): for d in backflushed_materials.get(item.item_code): if d.get(item.warehouse): if (qty > req_qty): qty = (qty/trans_qty) * flt(self.fg_completed_qty) + if consumed_qty: + qty -= consumed_qty + if cint(frappe.get_cached_value('UOM', item.stock_uom, 'must_be_whole_number')): qty = frappe.utils.ceil(qty) From ced3b13492b3ce1c95255d4edba54839ab3dbc78 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 12 Dec 2020 19:31:05 +0530 Subject: [PATCH 19/20] fix: Check for paid field --- erpnext/patches/v13_0/update_old_loans.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index caec53b3fdf..561e967d6df 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -23,12 +23,14 @@ def execute(): frappe.reload_doc('accounts', 'doctype', 'journal_entry_account') updated_loan_types = [] + loans_to_close = [] # Update old loan status as closed - loans_list = frappe.db.sql("""SELECT distinct parent from `tabRepayment Schedule` - where paid = 0 and docstatus = 1""", as_dict=1) + if frappe.db.has_column('Repayment Schedule', 'paid'): + loans_list = frappe.db.sql("""SELECT distinct parent from `tabRepayment Schedule` + where paid = 0 and docstatus = 1""", as_dict=1) - loans_to_close = [d.parent for d in loans_list] + loans_to_close = [d.parent for d in loans_list] if loans_to_close: frappe.db.sql("UPDATE `tabLoan` set status = 'Closed' where name not in (%s)" % (', '.join(['%s'] * len(loans_to_close))), tuple(loans_to_close)) From c838682188a71d2a71da68f132584414ed8bad83 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 27 Nov 2020 21:55:02 +0530 Subject: [PATCH 20/20] fix: Opening invoices in GSTR-1 report --- erpnext/regional/report/gstr_1/gstr_1.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 837929709ec..ad3de5f398d 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -151,6 +151,7 @@ class Gstr1Report(object): {select_columns} from `tab{doctype}` where docstatus = 1 {where_conditions} + and is_opening = 'No' order by posting_date desc """.format(select_columns=self.select_columns, doctype=self.doctype, where_conditions=conditions), self.filters, as_dict=1)