From 513099d6240d253b83ca3fb993cd33f3becc4a2a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 17 May 2020 23:26:37 +0530 Subject: [PATCH 01/30] fix: Description for various fields --- .../loan_management/loan_management.json | 4 +- .../loan_application/loan_application.js | 23 ++++--- .../loan_interest_accrual.py | 65 ++++++++++--------- .../loan_repayment/loan_repayment.json | 4 +- .../loan_security_pledge.js | 23 ++++--- .../loan_security_type.json | 4 +- .../doctype/loan_type/loan_type.json | 7 +- 7 files changed, 72 insertions(+), 58 deletions(-) diff --git a/erpnext/loan_management/desk_page/loan_management/loan_management.json b/erpnext/loan_management/desk_page/loan_management/loan_management.json index f9ea978ed6e..6cabff9443c 100644 --- a/erpnext/loan_management/desk_page/loan_management/loan_management.json +++ b/erpnext/loan_management/desk_page/loan_management/loan_management.json @@ -3,7 +3,7 @@ { "hidden": 0, "label": "Loan", - "links": "[\n {\n \"description\": \"Loan Type for interest and penalty rates\",\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loan Applications from customers and employees.\",\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loans provided to customers and employees.\",\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n }\n]" + "links": "[\n {\n \"description\": \"Loan Type for interest and penalty rates\",\n \"label\": \"Loan Type\",\n \"name\": \"Loan Type\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Loan Applications from customers and employees.\",\n \"label\": \"Loan Application\",\n \"name\": \"Loan Application\",\n \"type\": \"doctype\"\n },\n { \"dependencies\": [\n \"Loan Type\"\n ],\n \"description\": \"Loans provided to customers and employees.\",\n \"label\": \"Loan\",\n \"name\": \"Loan\",\n \"type\": \"doctype\"\n }\n]" }, { "hidden": 0, @@ -37,7 +37,7 @@ "idx": 0, "is_standard": 1, "label": "Loan Management", - "modified": "2020-04-02 11:28:51.380509", + "modified": "2020-05-16 08:01:27.784621", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Management", diff --git a/erpnext/loan_management/doctype/loan_application/loan_application.js b/erpnext/loan_management/doctype/loan_application/loan_application.js index 6cf47bf85c7..b56fce1d7cb 100644 --- a/erpnext/loan_management/doctype/loan_application/loan_application.js +++ b/erpnext/loan_management/doctype/loan_application/loan_application.js @@ -112,16 +112,19 @@ frappe.ui.form.on('Loan Application', { frappe.ui.form.on("Proposed Pledge", { loan_security: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; - frappe.call({ - method: "erpnext.loan_management.doctype.loan_security_price.loan_security_price.get_loan_security_price", - args: { - loan_security: row.loan_security - }, - callback: function(r) { - frappe.model.set_value(cdt, cdn, 'loan_security_price', r.message); - frm.events.calculate_amounts(frm, cdt, cdn); - } - }) + + if (row.loan_security) { + frappe.call({ + method: "erpnext.loan_management.doctype.loan_security_price.loan_security_price.get_loan_security_price", + args: { + loan_security: row.loan_security + }, + callback: function(r) { + frappe.model.set_value(cdt, cdn, 'loan_security_price', r.message); + frm.events.calculate_amounts(frm, cdt, cdn); + } + }) + } }, amount: function(frm, cdt, cdn) { diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 094b9c698c7..9b486e805d0 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -19,8 +19,8 @@ class LoanInterestAccrual(AccountsController): if not self.posting_date: self.posting_date = nowdate() - if not self.interest_amount: - frappe.throw(_("Interest Amount is mandatory")) + if not self.interest_amount and not self.payable_principal_amount: + frappe.throw(_("Interest Amount or Principal Amount is mandatory")) def on_submit(self): @@ -38,37 +38,38 @@ class LoanInterestAccrual(AccountsController): def make_gl_entries(self, cancel=0, adv_adj=0): gle_map = [] - gle_map.append( - self.get_gl_dict({ - "account": self.loan_account, - "party_type": self.applicant_type, - "party": self.applicant, - "against": self.interest_income_account, - "debit": self.interest_amount, - "debit_in_account_currency": self.interest_amount, - "against_voucher_type": "Loan", - "against_voucher": self.loan, - "remarks": _("Against Loan:") + self.loan, - "cost_center": erpnext.get_default_cost_center(self.company), - "posting_date": self.posting_date - }) - ) + if self.interest_amount: + gle_map.append( + self.get_gl_dict({ + "account": self.loan_account, + "party_type": self.applicant_type, + "party": self.applicant, + "against": self.interest_income_account, + "debit": self.interest_amount, + "debit_in_account_currency": self.interest_amount, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _("Against Loan:") + self.loan, + "cost_center": erpnext.get_default_cost_center(self.company), + "posting_date": self.posting_date + }) + ) - gle_map.append( - self.get_gl_dict({ - "account": self.interest_income_account, - "party_type": self.applicant_type, - "party": self.applicant, - "against": self.loan_account, - "credit": self.interest_amount, - "credit_in_account_currency": self.interest_amount, - "against_voucher_type": "Loan", - "against_voucher": self.loan, - "remarks": _("Against Loan:") + self.loan, - "cost_center": erpnext.get_default_cost_center(self.company), - "posting_date": self.posting_date - }) - ) + gle_map.append( + self.get_gl_dict({ + "account": self.interest_income_account, + "party_type": self.applicant_type, + "party": self.applicant, + "against": self.loan_account, + "credit": self.interest_amount, + "credit_in_account_currency": self.interest_amount, + "against_voucher_type": "Loan", + "against_voucher": self.loan, + "remarks": _("Against Loan:") + self.loan, + "cost_center": erpnext.get_default_cost_center(self.company), + "posting_date": self.posting_date + }) + ) if gle_map: make_gl_entries(gle_map, cancel=cancel, adv_adj=adv_adj) diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json index 789c1299463..5942455919f 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.json @@ -173,7 +173,7 @@ { "fieldname": "references_section", "fieldtype": "Section Break", - "label": "References" + "label": "Payment References" }, { "fieldname": "reference_number", @@ -221,7 +221,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-16 18:14:45.166754", + "modified": "2020-05-16 09:40:15.581165", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Repayment", diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js index 82837b3dac8..11c932ff1c1 100644 --- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js +++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js @@ -22,16 +22,19 @@ frappe.ui.form.on('Loan Security Pledge', { frappe.ui.form.on("Pledge", { loan_security: function(frm, cdt, cdn) { let row = locals[cdt][cdn]; - frappe.call({ - method: "erpnext.loan_management.doctype.loan_security_price.loan_security_price.get_loan_security_price", - args: { - loan_security: row.loan_security - }, - callback: function(r) { - frappe.model.set_value(cdt, cdn, 'loan_security_price', r.message); - frm.events.calculate_amounts(frm, cdt, cdn); - } - }); + + if (row.loan_security) { + frappe.call({ + method: "erpnext.loan_management.doctype.loan_security_price.loan_security_price.get_loan_security_price", + args: { + loan_security: row.loan_security + }, + callback: function(r) { + frappe.model.set_value(cdt, cdn, 'loan_security_price', r.message); + frm.events.calculate_amounts(frm, cdt, cdn); + } + }); + } }, qty: function(frm, cdt, cdn) { diff --git a/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json b/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json index f46b88cbca6..871e82563af 100644 --- a/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json +++ b/erpnext/loan_management/doctype/loan_security_type/loan_security_type.json @@ -29,6 +29,7 @@ "unique": 1 }, { + "description": "Haircut percentage is the percentage difference between market value of the Loan Security and the value ascribed to that Loan Security when used as collateral for that loan.", "fieldname": "haircut", "fieldtype": "Percent", "label": "Haircut %" @@ -46,13 +47,14 @@ "fieldtype": "Column Break" }, { + "description": "Loan To Value Ratio expresses the ratio of the loan amount to the value of the security pledged. A loan security shortfall will be triggered if this falls below the specified value for any loan ", "fieldname": "loan_to_value_ratio", "fieldtype": "Percent", "label": "Loan To Value Ratio" } ], "links": [], - "modified": "2020-04-28 14:06:49.046177", + "modified": "2020-05-16 09:38:45.988080", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Security Type", diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.json b/erpnext/loan_management/doctype/loan_type/loan_type.json index 51c5cb98a63..90ae3b01bd2 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type.json +++ b/erpnext/loan_management/doctype/loan_type/loan_type.json @@ -75,6 +75,7 @@ "reqd": 1 }, { + "description": "This account is used for booking loan repayments from the borrower and also disbursing loans to the borrower", "fieldname": "payment_account", "fieldtype": "Link", "label": "Payment Account", @@ -82,6 +83,7 @@ "reqd": 1 }, { + "description": "This account is capital account which is used to allocate capital for loan disbursal account ", "fieldname": "loan_account", "fieldtype": "Link", "label": "Loan Account", @@ -93,6 +95,7 @@ "fieldtype": "Column Break" }, { + "description": "This account will be used for booking loan interest accruals", "fieldname": "interest_income_account", "fieldtype": "Link", "label": "Interest Income Account", @@ -100,6 +103,7 @@ "reqd": 1 }, { + "description": "This account will be used for booking penalties levied due to delayed repayments", "fieldname": "penalty_income_account", "fieldtype": "Link", "label": "Penalty Income Account", @@ -108,6 +112,7 @@ }, { "default": "0", + "description": "If this is not checked the loan by default will be considered as a Demand Loan", "fieldname": "is_term_loan", "fieldtype": "Check", "label": "Is Term Loan" @@ -143,7 +148,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2020-04-15 00:24:43.259963", + "modified": "2020-05-16 09:08:09.029921", "modified_by": "Administrator", "module": "Loan Management", "name": "Loan Type", From 5ccefdc08a1bbbfc0ff24b8f1ca3de835a4f52a9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 17 May 2020 23:26:59 +0530 Subject: [PATCH 02/30] fix: Patch to update old loans --- erpnext/patches.txt | 1 + erpnext/patches/v13_0/update_old_loans.py | 83 +++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 erpnext/patches/v13_0/update_old_loans.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ce0e4ac4711..f3907f4b179 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -680,3 +680,4 @@ erpnext.patches.v12_0.fix_quotation_expired_status erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry erpnext.patches.v12_0.retain_permission_rules_for_video_doctype erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive +erpnext.patches.v13_0.update_old_loans diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py new file mode 100644 index 00000000000..e924f6234b2 --- /dev/null +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -0,0 +1,83 @@ +from __future__ import unicode_literals +import frappe +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 + +def execute(): + + # Create a penalty account for loan types + + frappe.reload_doc('loan_management', 'doctype', 'loan_type') + frappe.reload_doc('loan_management', 'doctype', 'loan') + frappe.reload_doc('loan_management', 'doctype', 'repayment_schedule') + frappe.reload_doc('loan_management', 'doctype', 'process_loan_interest_accrual') + frappe.reload_doc('loan_management', 'doctype', 'loan_repayment') + 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') + + 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']) + + for loan in loans: + # Update details in Loan Types and Loan + loan_type_company = frappe.db.get_value('Loan Type', loan.loan_type, 'company') + + parent_income = frappe.get_value('Account', {'company': loan.company, + 'is_group': 1, 'root_type': 'Income'}) + + penalty_account = create_account(company=loan.company, account_type='Income Account', + account_name='Penalty Account', parent_account=parent_income) + + if 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 + 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() + updated_loan_types.append(loan.loan_type) + + if loan.loan_type in updated_loan_types: + if loan.status == 'Fully Disbursed': + status = 'Disbursed' + elif loan.status == 'Repaid/Closed': + status = 'Closed' + else: + status = loan.status + + frappe.db.set_value('Loan', loan.name, { + 'is_term_loan': 1, + 'penalty_income_account': penalty_account, + 'status': status + }) + + process_loan_interest_accrual_for_term_loans(posting_date=nowdate(), loan_type=loan.loan_type, + loan=loan.name) + + 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 + ''', (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.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() + + jv = frappe.get_doc('Journal Entry', payment.name) + jv.flags.ignore_links = True + jv.cancel() + From 8cfa00920ed53f44bc9cb8ba05b46ca5778b376d Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 29 Jul 2020 15:24:03 +0530 Subject: [PATCH 03/30] fix: cannot submit value adjustment with custom dimension --- .../asset_value_adjustment.py | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 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 155597e8565..fd702c74c73 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -8,6 +8,7 @@ from frappe import _ from frappe.utils import flt, getdate, cint, date_diff, formatdate from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts from frappe.model.document import Document +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_checks_for_pl_and_bs_accounts class AssetValueAdjustment(Document): def validate(self): @@ -53,17 +54,33 @@ class AssetValueAdjustment(Document): je.company = self.company je.remark = "Depreciation Entry against {0} worth {1}".format(self.asset, self.difference_amount) - je.append("accounts", { + credit_entry = { "account": accumulated_depreciation_account, "credit_in_account_currency": self.difference_amount, "cost_center": depreciation_cost_center or self.cost_center - }) + } - je.append("accounts", { + debit_entry = { "account": depreciation_expense_account, "debit_in_account_currency": self.difference_amount, "cost_center": depreciation_cost_center or self.cost_center - }) + } + + accounting_dimensions = get_checks_for_pl_and_bs_accounts() + + for dimension in accounting_dimensions: + if dimension.get('mandatory_for_bs'): + credit_entry.update({ + dimension['fieldname']: self.get(dimension['fieldname']) or dimension.get('default_dimension') + }) + + if dimension.get('mandatory_for_pl'): + debit_entry.update({ + dimension['fieldname']: self.get(dimension['fieldname']) or dimension.get('default_dimension') + }) + + je.append("accounts", credit_entry) + je.append("accounts", debit_entry) je.flags.ignore_permissions = True je.submit() From ab611d5fd8a4a87b0a07469e09e1efcdd97f89a7 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 7 Aug 2020 14:47:40 +0530 Subject: [PATCH 04/30] fix: GSTR 1 report for exports without payment of Tax --- erpnext/regional/report/gstr_1/gstr_1.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 8885b88c2a4..282efe47901 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -131,6 +131,9 @@ class Gstr1Report(object): taxable_value += abs(net_amount) elif tax_rate: taxable_value += abs(net_amount) + elif not tax_rate and self.filters.get('type_of_business') == 'EXPORT' \ + and invoice_details.get('export_type') == "Without Payment of Tax": + taxable_value += abs(net_amount) row += [tax_rate or 0, taxable_value] From 8118dd9e62971db02c44183c7574bd730dc18eff Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Fri, 7 Aug 2020 14:52:50 +0530 Subject: [PATCH 05/30] fix: escape company field --- erpnext/accounts/party.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 28a65196502..6f043a012e0 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -611,7 +611,7 @@ def get_partywise_advanced_payment_amount(party_type, posting_date = None, futur cond = "posting_date <= '{0}'".format(posting_date) if company: - cond += "and company = '{0}'".format(company) + cond += "and company = '{0}'".format(frappe.db.escape(company)) data = frappe.db.sql(""" SELECT party, sum({0}) as amount FROM `tabGL Entry` From 1f3cc7553cab51571290ce1481ec43b5265cad15 Mon Sep 17 00:00:00 2001 From: Afshan Date: Fri, 7 Aug 2020 15:57:31 +0530 Subject: [PATCH 06/30] fix: grand_total assigment before reference --- erpnext/accounts/doctype/dunning/dunning.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py index 3e372affa12..1a6dbedf560 100644 --- a/erpnext/accounts/doctype/dunning/dunning.py +++ b/erpnext/accounts/doctype/dunning/dunning.py @@ -93,6 +93,7 @@ def resolve_dunning(doc, state): def calculate_interest_and_amount(posting_date, outstanding_amount, rate_of_interest, dunning_fee, overdue_days): interest_amount = 0 + grand_total = 0 if rate_of_interest: interest_per_year = flt(outstanding_amount) * flt(rate_of_interest) / 100 interest_amount = (interest_per_year * cint(overdue_days)) / 365 From bad269c3549adcef9021bd4c034d370ffae87257 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 7 Aug 2020 16:21:41 +0530 Subject: [PATCH 07/30] fix: Parent income account check --- erpnext/patches/v13_0/update_old_loans.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py index e924f6234b2..77239429c51 100644 --- a/erpnext/patches/v13_0/update_old_loans.py +++ b/erpnext/patches/v13_0/update_old_loans.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals import frappe +from frappe import _ 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 @@ -27,11 +28,15 @@ def execute(): # Update details in Loan Types and Loan loan_type_company = frappe.db.get_value('Loan Type', loan.loan_type, 'company') - parent_income = frappe.get_value('Account', {'company': loan.company, - 'is_group': 1, 'root_type': 'Income'}) + group_income_account = frappe.get_value('Account', {'company': loan.company, + 'is_group': 1, 'root_type': 'Income', 'account_name': _('Indirect Income')}) + + if not group_income_account: + group_income_account = frappe.get_value('Account', {'company': loan.company, + 'is_group': 1, 'root_type': 'Income'}) penalty_account = create_account(company=loan.company, account_type='Income Account', - account_name='Penalty Account', parent_account=parent_income) + account_name='Penalty Account', parent_account=group_income_account) if not loan_type_company: loan_type_doc = frappe.get_doc('Loan Type', loan.loan_type) From 5ac2fd94f0fa361d496bcd2137a36c9433435261 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 8 Aug 2020 22:28:13 +0530 Subject: [PATCH 08/30] fix: Taxable value for RCM --- .../doctype/gstr_3b_report/gstr_3b_report.py | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index 2d306ba1726..fa5f5811eb4 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -234,9 +234,6 @@ class GSTR3BReport(Document): self.report_dict[supply_type][supply_category][account_map.get(account_type)] += \ flt(tax_details.get((account_name, gst_category), {}).get("amount"), 2) - for k, v in iteritems(account_map): - txval -= self.report_dict.get(supply_type, {}).get(supply_category, {}).get(v, 0) - self.report_dict[supply_type][supply_category]["txval"] += flt(txval, 2) def set_inter_state_supply(self, inter_state_supply): @@ -256,7 +253,7 @@ class GSTR3BReport(Document): def get_total_taxable_value(self, doctype, reverse_charge): return frappe._dict(frappe.db.sql(""" - select gst_category, sum(base_grand_total) as total + select gst_category, sum(net_total) as total from `tab{doctype}` where docstatus = 1 and month(posting_date) = %s and year(posting_date) = %s and reverse_charge = %s @@ -309,26 +306,27 @@ class GSTR3BReport(Document): inter_state_supply_tax_mapping.setdefault(d.name, { 'place_of_supply': d.place_of_supply, 'taxable_value': d.net_total, + 'gst_category': d.gst_category, 'camt': 0.0, 'samt': 0.0, 'iamt': 0.0, 'csamt': 0.0 }) - if d.account_head in [d.cgst_account for d in self.account_heads]: + if d.account_head in [a.cgst_account for a in self.account_heads]: inter_state_supply_tax_mapping[d.name]['camt'] += d.tax_amount - if d.account_head in [d.sgst_account for d in self.account_heads]: + if d.account_head in [a.sgst_account for a in self.account_heads]: inter_state_supply_tax_mapping[d.name]['samt'] += d.tax_amount - if d.account_head in [d.igst_account for d in self.account_heads]: + if d.account_head in [a.igst_account for a in self.account_heads]: inter_state_supply_tax_mapping[d.name]['iamt'] += d.tax_amount - if d.account_head in [d.cess_account for d in self.account_heads]: + if d.account_head in [a.cess_account for a in self.account_heads]: inter_state_supply_tax_mapping[d.name]['csamt'] += d.tax_amount for key, value in iteritems(inter_state_supply_tax_mapping): - if d.place_of_supply: + if value.get('place_of_supply'): osup_det = self.report_dict["sup_details"]["osup_det"] osup_det["txval"] = flt(osup_det["txval"] + value['taxable_value'], 2) osup_det["iamt"] = flt(osup_det["iamt"] + value['iamt'], 2) @@ -336,15 +334,15 @@ class GSTR3BReport(Document): osup_det["samt"] = flt(osup_det["samt"] + value['samt'], 2) osup_det["csamt"] = flt(osup_det["csamt"] + value['csamt'], 2) - if state_number != d.place_of_supply.split("-")[0]: - inter_state_supply_details.setdefault((d.gst_category, d.place_of_supply), { + if state_number != value.get('place_of_supply').split("-")[0]: + inter_state_supply_details.setdefault((value.get('gst_category'), value.get('place_of_supply')), { "txval": 0.0, "pos": d.place_of_supply.split("-")[0], "iamt": 0.0 }) - inter_state_supply_details[(d.gst_category, d.place_of_supply)]['txval'] += value['taxable_value'] - inter_state_supply_details[(d.gst_category, d.place_of_supply)]['iamt'] += value['iamt'] + inter_state_supply_details[(value.get('gst_category'), value.get('place_of_supply'))]['txval'] += value['taxable_value'] + inter_state_supply_details[(value.get('gst_category'), value.get('place_of_supply'))]['iamt'] += value['iamt'] return inter_state_supply_details From d4ecf426b713f683cfc50df55384e9c79163903b Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 8 Aug 2020 22:33:18 +0530 Subject: [PATCH 09/30] fix: Place of supply fix --- erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py index fa5f5811eb4..787d557e805 100644 --- a/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py +++ b/erpnext/regional/doctype/gstr_3b_report/gstr_3b_report.py @@ -337,7 +337,7 @@ class GSTR3BReport(Document): if state_number != value.get('place_of_supply').split("-")[0]: inter_state_supply_details.setdefault((value.get('gst_category'), value.get('place_of_supply')), { "txval": 0.0, - "pos": d.place_of_supply.split("-")[0], + "pos": value.get('place_of_supply').split("-")[0], "iamt": 0.0 }) From a53f15ca35fd5c832885f975868014b7c30648e4 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Sun, 9 Aug 2020 13:12:47 +0530 Subject: [PATCH 10/30] fix: remove mentions of display items in stock (#22963) --- erpnext/selling/page/point_of_sale/point_of_sale.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index 9f8410f40bc..83bd71d5f32 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -14,10 +14,9 @@ from six import string_types def get_items(start, page_length, price_list, item_group, search_value="", pos_profile=None): data = dict() warehouse = "" - display_items_in_stock = 0 if pos_profile: - warehouse, display_items_in_stock = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'display_items_in_stock']) + warehouse = frappe.db.get_value('POS Profile', pos_profile, ['warehouse']) if not frappe.db.exists('Item Group', item_group): item_group = get_root_of('Item Group') @@ -85,7 +84,7 @@ def get_items(start, page_length, price_list, item_group, search_value="", pos_p item_price = item_prices.get(item_code) or {} item_stock_qty = get_stock_availability(item_code, warehouse) - if display_items_in_stock and not item_stock_qty: + if not item_stock_qty: pass else: row = {} From bc8a281f83640ccbefa9c8d2b99b05db6b9c25b2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Mon, 10 Aug 2020 11:00:08 +0530 Subject: [PATCH 11/30] fix: Subscripition link in Customer dashboard (#22960) Co-authored-by: Rucha Mahabal --- erpnext/selling/doctype/customer/customer_dashboard.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer_dashboard.py b/erpnext/selling/doctype/customer/customer_dashboard.py index 22e30e31139..09e474dc2ef 100644 --- a/erpnext/selling/doctype/customer/customer_dashboard.py +++ b/erpnext/selling/doctype/customer/customer_dashboard.py @@ -12,7 +12,8 @@ def get_data(): 'Payment Entry': 'party', 'Quotation': 'party_name', 'Opportunity': 'party_name', - 'Bank Account': 'party' + 'Bank Account': 'party', + 'Subscription': 'party' }, 'dynamic_links': { 'party_name': ['Customer', 'quotation_to'] From 5c0439f768d179f43e3169004fca14fe6e00a65d Mon Sep 17 00:00:00 2001 From: Prssanna Desai Date: Mon, 10 Aug 2020 11:12:52 +0530 Subject: [PATCH 12/30] fix: set correct default value from from date in downtime analysis report (#22903) Co-authored-by: Rucha Mahabal --- .../manufacturing/report/downtime_analysis/downtime_analysis.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js index ff32dbed98a..f6486743aa3 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js @@ -8,7 +8,7 @@ frappe.query_reports["Downtime Analysis"] = { label: __("From Date"), fieldname:"from_date", fieldtype: "Datetime", - default: frappe.datetime.add_months(frappe.datetime.now_datetime(), -1), + default: frappe.datetime.convert_to_system_tz(frappe.datetime.add_months(frappe.datetime.now_datetime(), -1)), reqd: 1 }, { From 97ecf52d6fcba8c1be5e0a6fecc011e99b44e58f Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Mon, 10 Aug 2020 12:13:35 +0530 Subject: [PATCH 13/30] fix: failing Fees test --- erpnext/education/doctype/fees/test_fees.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/education/doctype/fees/test_fees.py b/erpnext/education/doctype/fees/test_fees.py index b182992922b..eedc2ae7301 100644 --- a/erpnext/education/doctype/fees/test_fees.py +++ b/erpnext/education/doctype/fees/test_fees.py @@ -7,7 +7,7 @@ import frappe import unittest from frappe.utils import nowdate from frappe.utils.make_random import get_random - +from erpnext.education.doctype.program.test_program import make_program_and_linked_courses # test_records = frappe.get_test_records('Fees') @@ -15,6 +15,7 @@ class TestFees(unittest.TestCase): def test_fees(self): student = get_random("Student") + program = make_program_and_linked_courses("_Test Program 1", ["_Test Course 1", "_Test Course 2"]) fee = frappe.new_doc("Fees") fee.posting_date = nowdate() fee.due_date = nowdate() @@ -23,6 +24,7 @@ class TestFees(unittest.TestCase): fee.income_account = "Sales - _TC" fee.cost_center = "_Test Cost Center - _TC" fee.company = "_Test Company" + fee.program = program.name fee.extend("components", [ { From a07973cfa57f6bc0ebf367fa32af89e0ec8d4008 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 10 Aug 2020 12:39:42 +0530 Subject: [PATCH 14/30] fix: Creating opportunity from email --- erpnext/crm/doctype/opportunity/opportunity.py | 2 +- erpnext/public/js/communication.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 1b071ea1b70..efaeca0e938 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -330,7 +330,7 @@ def make_opportunity_from_communication(communication, ignore_communication_link opportunity = frappe.get_doc({ "doctype": "Opportunity", "opportunity_from": opportunity_from, - "lead": lead + "party_name": lead }).insert(ignore_permissions=True) link_communication_to_document(doc, "Opportunity", opportunity.name, ignore_communication_links) diff --git a/erpnext/public/js/communication.js b/erpnext/public/js/communication.js index 5316eb45b5b..9432d421752 100644 --- a/erpnext/public/js/communication.js +++ b/erpnext/public/js/communication.js @@ -13,7 +13,7 @@ frappe.ui.form.on("Communication", { frappe.confirm(__(confirm_msg, [__("Issue")]), () => { frm.trigger('make_issue_from_communication'); }) - }, "Make"); + }, "Create"); } if(!in_list(["Lead", "Opportunity"], frm.doc.reference_doctype)) { From d3d3db654ef4bedf308ee5563ccca55090fdf070 Mon Sep 17 00:00:00 2001 From: Anoop Date: Mon, 10 Aug 2020 13:21:12 +0530 Subject: [PATCH 15/30] fix: Inpatient Record - Transfer check-in time validation (#22958) * fix: inpatient transfer should not allow future checkin time * Update erpnext/healthcare/doctype/inpatient_record/inpatient_record.js Error message corrected Co-authored-by: Rucha Mahabal Co-authored-by: Rucha Mahabal --- .../doctype/inpatient_record/inpatient_record.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js index 971e166067e..60f0f9d56d6 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.js @@ -134,7 +134,7 @@ let transfer_patient_dialog = function(frm) { {fieldtype: 'Link', label: 'Leave From', fieldname: 'leave_from', options: 'Healthcare Service Unit', reqd: 1, read_only:1}, {fieldtype: 'Link', label: 'Service Unit Type', fieldname: 'service_unit_type', options: 'Healthcare Service Unit Type'}, {fieldtype: 'Link', label: 'Transfer To', fieldname: 'service_unit', options: 'Healthcare Service Unit', reqd: 1}, - {fieldtype: 'Datetime', label: 'Check In', fieldname: 'check_in', reqd: 1} + {fieldtype: 'Datetime', label: 'Check In', fieldname: 'check_in', reqd: 1, default: frappe.datetime.now_datetime()} ], primary_action_label: __('Transfer'), primary_action : function() { @@ -147,7 +147,12 @@ let transfer_patient_dialog = function(frm) { if(dialog.get_value('service_unit')){ service_unit = dialog.get_value('service_unit'); } - if(!check_in){ + if(check_in > frappe.datetime.now_datetime()){ + frappe.msgprint({ + title: __('Not Allowed'), + message: __('Check-in time cannot be greater than the current time'), + indicator: 'red' + }); return; } frappe.call({ From f1091534cd5f269f7554dde44e5e453f0f8a068c Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 10 Aug 2020 14:45:58 +0530 Subject: [PATCH 16/30] fix: [pos] minor bugs --- erpnext/accounts/doctype/pos_profile/pos_profile.js | 7 +------ erpnext/selling/page/point_of_sale/pos_controller.js | 7 ++++--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.js b/erpnext/accounts/doctype/pos_profile/pos_profile.js index ef431d7d41a..8ec6a536269 100755 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.js +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.js @@ -31,8 +31,7 @@ frappe.ui.form.on('POS Profile', { frm.set_query("print_format", function() { return { filters: [ - ['Print Format', 'doc_type', '=', 'Sales Invoice'], - ['Print Format', 'print_format_type', '=', 'Jinja'], + ['Print Format', 'doc_type', '=', 'POS Invoice'] ] }; }); @@ -45,10 +44,6 @@ frappe.ui.form.on('POS Profile', { }; }); - frm.set_query("print_format", function() { - return { filters: { doc_type: "Sales Invoice", print_format_type: "JS"} }; - }); - frm.set_query('company_address', function(doc) { if(!doc.company) { frappe.throw(__('Please set Company')); diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 483ef78d64c..ae5471b9000 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -35,7 +35,8 @@ erpnext.PointOfSale.Controller = class { create_opening_voucher() { const table_fields = [ { fieldname: "mode_of_payment", fieldtype: "Link", in_list_view: 1, label: "Mode of Payment", options: "Mode of Payment", reqd: 1 }, - { fieldname: "opening_amount", fieldtype: "Currency", in_list_view: 1, label: "Opening Amount", options: "company:company_currency", reqd: 1 } + { fieldname: "opening_amount", fieldtype: "Currency", default: 0, in_list_view: 1, label: "Opening Amount", + options: "company:company_currency", reqd: 1 } ]; const dialog = new frappe.ui.Dialog({ @@ -66,7 +67,7 @@ erpnext.PointOfSale.Controller = class { frappe.db.get_doc("POS Closing Entry", pos_closing_entry.name).then(({ payment_reconciliation }) => { dialog.fields_dict.balance_details.df.data = []; payment_reconciliation.forEach(pay => { - const { mode_of_payment, closing_amount } = pay; + const { mode_of_payment } = pay; dialog.fields_dict.balance_details.df.data.push({ mode_of_payment: mode_of_payment }); @@ -152,7 +153,7 @@ erpnext.PointOfSale.Controller = class { }, () => this.make_new_invoice(), () => frappe.dom.unfreeze(), - () => this.page.set_title(__('Point of Sale Beta')), + () => this.page.set_title(__('Point of Sale')), ]); } From 0fcb05a3aa399db7066637fd43576aa12f5df85c Mon Sep 17 00:00:00 2001 From: Marica Date: Mon, 10 Aug 2020 14:48:13 +0530 Subject: [PATCH 17/30] fix: Misleading filters on Item tax Template Link field (#22918) Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Co-authored-by: Rucha Mahabal --- erpnext/controllers/queries.py | 5 ++++- erpnext/public/js/controllers/transaction.js | 3 +-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index babc5bdd797..37b7e31e611 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -613,9 +613,12 @@ def get_tax_template(doctype, txt, searchfield, start, page_len, filters): if not taxes: return frappe.db.sql(""" SELECT name FROM `tabItem Tax Template` """) else: + valid_from = filters.get('valid_from') + valid_from = valid_from[1] if isinstance(valid_from, list) else valid_from + args = { 'item_code': filters.get('item_code'), - 'posting_date': filters.get('valid_from'), + 'posting_date': valid_from, 'tax_category': filters.get('tax_category'), 'company': filters.get('company') } diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 4e50f3d7f67..436a232a551 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1821,7 +1821,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }, set_query_for_item_tax_template: function(doc, cdt, cdn) { - var item = frappe.get_doc(cdt, cdn); if(!item.item_code) { frappe.throw(__("Please enter Item Code to get item taxes")); @@ -1829,7 +1828,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ let filters = { 'item_code': item.item_code, - 'valid_from': doc.transaction_date || doc.bill_date || doc.posting_date, + 'valid_from': ["<=", doc.transaction_date || doc.bill_date || doc.posting_date], 'item_group': item.item_group, } From 0f27500197f3fa4dbcd165626f7307d094126f39 Mon Sep 17 00:00:00 2001 From: marination Date: Mon, 10 Aug 2020 15:17:58 +0530 Subject: [PATCH 18/30] fix: Susbcription in Sales Taxes and Charges Dashboard --- .../sales_taxes_and_charges_template_dashboard.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py index 0e9c808608b..d825c6fd325 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template_dashboard.py @@ -8,7 +8,7 @@ def get_data(): 'fieldname': 'taxes_and_charges', 'non_standard_fieldnames': { 'Tax Rule': 'sales_tax_template', - 'Subscription': 'tax_template', + 'Subscription': 'sales_tax_template', 'Restaurant': 'default_tax_template' }, 'transactions': [ From dbbb864ba511a3029389a2b16e7854c90bf5b53d Mon Sep 17 00:00:00 2001 From: Jai Chavan <40264279+jaichavan@users.noreply.github.com> Date: Mon, 10 Aug 2020 16:55:03 +0530 Subject: [PATCH 19/30] fix: Consistent capitalization --- erpnext/setup/doctype/company/company.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 221044df3a7..03703fd82ef 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -242,7 +242,7 @@ { "fieldname": "default_warehouse_for_sales_return", "fieldtype": "Link", - "label": "Default warehouse for Sales Return", + "label": "Default Warehouse for Sales Return", "options": "Warehouse" }, { @@ -801,4 +801,4 @@ "sort_field": "modified", "sort_order": "ASC", "track_changes": 1 -} \ No newline at end of file +} From 19b51762efdf8602be556833344d759e67e940d6 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 11 Aug 2020 13:12:00 +0530 Subject: [PATCH 20/30] fix: escape fields for Payroll Entry (#22994) --- erpnext/payroll/doctype/payroll_entry/payroll_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 554484febbb..30ea432678c 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -90,7 +90,7 @@ class PayrollEntry(Document): cond = '' for f in ['company', 'branch', 'department', 'designation']: if self.get(f): - cond += " and t1." + f + " = '" + self.get(f).replace("'", "\'") + "'" + cond += " and t1." + f + " = " + frappe.db.escape(self.get(f)) return cond From acc3d42cf0d208f0295c09ee36a18b82f705c076 Mon Sep 17 00:00:00 2001 From: Afshan <33727827+AfshanKhan@users.noreply.github.com> Date: Tue, 11 Aug 2020 14:40:44 +0530 Subject: [PATCH 21/30] fix: escape apostrophe in company name if exist (#22956) Co-authored-by: Rucha Mahabal --- erpnext/accounts/party.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 6f043a012e0..2f800bb2abd 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -611,7 +611,7 @@ def get_partywise_advanced_payment_amount(party_type, posting_date = None, futur cond = "posting_date <= '{0}'".format(posting_date) if company: - cond += "and company = '{0}'".format(frappe.db.escape(company)) + cond += "and company = {0}".format(frappe.db.escape(company)) data = frappe.db.sql(""" SELECT party, sum({0}) as amount FROM `tabGL Entry` From c992616d3514d8ba7994d1f01d4f85e563f4db1e Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Tue, 11 Aug 2020 15:22:54 +0530 Subject: [PATCH 22/30] fix: change opportunity to 'Converted' when items not selected in opportunity itself for making quotation and sales order --- erpnext/crm/doctype/opportunity/opportunity.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index efaeca0e938..e152850f170 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -119,11 +119,19 @@ class Opportunity(TransactionBase): and q.status not in ('Lost', 'Closed')""", self.name) def has_ordered_quotation(self): - return frappe.db.sql(""" - select q.name - from `tabQuotation` q, `tabQuotation Item` qi - where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s - and q.status = 'Ordered'""", self.name) + if not self.with_items: + return frappe.get_all('Quotation', + { + 'opportunity': self.name, + 'status': 'Ordered', + 'docstatus': 1 + }, 'name') + else: + return frappe.db.sql(""" + select q.name + from `tabQuotation` q, `tabQuotation Item` qi + where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s + and q.status = 'Ordered'""", self.name) def has_lost_quotation(self): lost_quotation = frappe.db.sql(""" From 3325be1d8760e004fc562908afe1690d9bf815a0 Mon Sep 17 00:00:00 2001 From: Shivam Mishra Date: Tue, 11 Aug 2020 10:33:46 +0000 Subject: [PATCH 23/30] fix: spacing for slideshow (#22996) --- erpnext/public/less/website.less | 4 ++++ erpnext/templates/generators/item_group.html | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/public/less/website.less b/erpnext/public/less/website.less index 57a0a332a98..ac878de105b 100644 --- a/erpnext/public/less/website.less +++ b/erpnext/public/less/website.less @@ -297,6 +297,10 @@ margin-top: 30px; } +.item-group-slideshow { + margin-bottom: 1rem; +} + .product-image-img { border: 1px solid @light-border-color; border-radius: 3px; diff --git a/erpnext/templates/generators/item_group.html b/erpnext/templates/generators/item_group.html index 3f984536037..40a064fc768 100644 --- a/erpnext/templates/generators/item_group.html +++ b/erpnext/templates/generators/item_group.html @@ -4,7 +4,7 @@ {% block page_content %}
-
+
{% if slideshow %} {% include "templates/includes/slideshow.html" %} {% endif %} From ef3b82dae2e94e45ec88e0784975675bd8d8ef2d Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Tue, 11 Aug 2020 16:05:02 +0530 Subject: [PATCH 24/30] fix(CRM): Move Source section above Follow Up section In Opportunity (#22974) * fix: move source section above follow up * fix: only moving source field to 1st section --- erpnext/crm/doctype/opportunity/opportunity.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 545e2324acf..219ed890f5e 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -16,6 +16,7 @@ "opportunity_from", "party_name", "customer_name", + "source", "column_break0", "title", "opportunity_type", @@ -49,7 +50,6 @@ "contact_email", "contact_mobile", "more_info", - "source", "campaign", "column_break1", "company", @@ -424,7 +424,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2020-07-14 16:49:15.888503", + "modified": "2020-08-11 14:49:13.496297", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From 2621016884a208d164e2b478e7945b1324872bec Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Tue, 11 Aug 2020 16:06:13 +0530 Subject: [PATCH 25/30] fix: Allocated advance amount for multi-currency payment (#22923) * fix: Allocated advance amount for multicurrency payment * fix: Get grand total based on party currency * fix: Remove unwanted code --- erpnext/controllers/accounts_controller.py | 36 +++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 89c38c710b4..66b5f3035db 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -479,7 +479,11 @@ class AccountsController(TransactionBase): if d.against_order: allocated_amount = flt(d.amount) else: - amount = self.rounded_total or self.grand_total + if self.get('party_account_currency') == self.company_currency: + amount = self.get('base_rounded_total') or self.base_grand_total + else: + amount = self.get('rounded_total') or self.grand_total + allocated_amount = min(amount - advance_allocated, d.amount) advance_allocated += flt(allocated_amount) @@ -802,10 +806,22 @@ class AccountsController(TransactionBase): self.payment_terms_template = '' return + party_account_currency = self.get('party_account_currency') + if not party_account_currency: + party_type, party = self.get_party() + + if party_type and party: + party_account_currency = get_party_account_currency(party_type, party, self.company) + posting_date = self.get("bill_date") or self.get("posting_date") or self.get("transaction_date") date = self.get("due_date") due_date = date or posting_date - grand_total = self.get("rounded_total") or self.grand_total + + if party_account_currency == self.company_currency: + grand_total = self.get("base_rounded_total") or self.base_grand_total + else: + grand_total = self.get("rounded_total") or self.grand_total + if self.doctype in ("Sales Invoice", "Purchase Invoice"): grand_total = grand_total - flt(self.write_off_amount) @@ -850,13 +866,25 @@ class AccountsController(TransactionBase): def validate_payment_schedule_amount(self): if self.doctype == 'Sales Invoice' and self.is_pos: return + party_account_currency = self.get('party_account_currency') + if not party_account_currency: + party_type, party = self.get_party() + + if party_type and party: + party_account_currency = get_party_account_currency(party_type, party, self.company) + if self.get("payment_schedule"): total = 0 for d in self.get("payment_schedule"): total += flt(d.payment_amount) - total = flt(total, self.precision("grand_total")) - grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total')) + if party_account_currency == self.company_currency: + total = flt(total, self.precision("base_grand_total")) + grand_total = flt(self.get("base_rounded_total") or self.base_grand_total, self.precision('base_grand_total')) + else: + total = flt(total, self.precision("grand_total")) + grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total')) + if self.get("total_advance"): grand_total -= self.get("total_advance") From 79d731dcd80b73fabeb5f510d4deddf8c5119806 Mon Sep 17 00:00:00 2001 From: Anupam Kumar Date: Tue, 11 Aug 2020 16:11:20 +0530 Subject: [PATCH 26/30] feat: Naming series for Sales/Purchase Return (#22942) * feat: adding return naming series * Update purchase_invoice.json Co-authored-by: Nabin Hait --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 6 +++--- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 5 ++--- erpnext/stock/doctype/delivery_note/delivery_note.json | 4 ++-- .../stock/doctype/purchase_receipt/purchase_receipt.json | 5 ++--- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 2e91c8ef19f..d62e73b6ac6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -180,7 +180,7 @@ "no_copy": 1, "oldfieldname": "naming_series", "oldfieldtype": "Select", - "options": "ACC-PINV-.YYYY.-", + "options": "ACC-PINV-.YYYY.-\nACC-PINV-RET-.YYYY.-", "print_hide": 1, "reqd": 1, "set_only_once": 1 @@ -1334,7 +1334,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2020-08-03 12:46:01.411074", + "modified": "2020-08-03 23:20:04.466153", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", @@ -1396,4 +1396,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 4dc81e90875..31613e50b04 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1,7 +1,6 @@ { "actions": [], "allow_import": 1, - "allow_workflow": 1, "autoname": "naming_series:", "creation": "2013-05-24 19:29:05", "doctype": "DocType", @@ -217,7 +216,7 @@ "no_copy": 1, "oldfieldname": "naming_series", "oldfieldtype": "Select", - "options": "ACC-SINV-.YYYY.-", + "options": "ACC-SINV-.YYYY.-\nACC-SINV-RET-.YYYY.-", "print_hide": 1, "reqd": 1, "set_only_once": 1 @@ -1947,7 +1946,7 @@ "idx": 181, "is_submittable": 1, "links": [], - "modified": "2020-07-18 05:07:16.725974", + "modified": "2020-08-03 23:31:12.675040", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 66efcf8cd85..ea385c8b2a9 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -175,7 +175,7 @@ "no_copy": 1, "oldfieldname": "naming_series", "oldfieldtype": "Select", - "options": "MAT-DN-.YYYY.-", + "options": "MAT-DN-.YYYY.-\nMAT-DN-RET-.YYYY.-", "print_hide": 1, "reqd": 1, "set_only_once": 1 @@ -1255,7 +1255,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-07-18 05:13:55.580420", + "modified": "2020-08-03 23:18:47.739997", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 92e33ca64e3..ce54fc883f6 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -1,7 +1,6 @@ { "actions": [], "allow_import": 1, - "allow_workflow": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", "doctype": "DocType", @@ -160,7 +159,7 @@ "no_copy": 1, "oldfieldname": "naming_series", "oldfieldtype": "Select", - "options": "MAT-PRE-.YYYY.-", + "options": "MAT-PRE-.YYYY.-\nMAT-PR-RET-.YYYY.-", "print_hide": 1, "reqd": 1, "set_only_once": 1 @@ -1110,7 +1109,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-07-18 05:19:12.148115", + "modified": "2020-08-03 23:20:26.381024", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From 9f6d114265b2c8b156fb60491f90e2599ef50dee Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Tue, 11 Aug 2020 16:16:05 +0530 Subject: [PATCH 27/30] fix: Change Source section name to 'Company Information' --- erpnext/crm/doctype/opportunity/opportunity.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 219ed890f5e..80e707cea7e 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -344,7 +344,7 @@ "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", - "label": "Source", + "label": "Company Information", "oldfieldtype": "Section Break", "options": "fa fa-file-text" }, @@ -424,7 +424,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2020-08-11 14:49:13.496297", + "modified": "2020-08-11 16:15:45.052240", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From ee5b9c76912d193ae2fe4ea1ae062619fc1c8673 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Tue, 11 Aug 2020 16:23:47 +0530 Subject: [PATCH 28/30] feat(Accounting): Process Statement Of Accounts (#22901) * feat: Process Statement Of Accounts initial commit * fix: add jinja supported inputs for subject and body in email settings * feat: utils support in template, tested autoemail, fixed issues --- .../process_statement_of_accounts/__init__.py | 0 .../process_statement_of_accounts.html | 89 +++++ .../process_statement_of_accounts.js | 132 ++++++++ .../process_statement_of_accounts.json | 310 ++++++++++++++++++ .../process_statement_of_accounts.py | 271 +++++++++++++++ .../test_process_statement_of_accounts.py | 10 + .../__init__.py | 0 ...rocess_statement_of_accounts_customer.json | 47 +++ .../process_statement_of_accounts_customer.py | 10 + .../doctype/psoa_cost_center/__init__.py | 0 .../psoa_cost_center/psoa_cost_center.json | 30 ++ .../psoa_cost_center/psoa_cost_center.py | 10 + .../accounts/doctype/psoa_project/__init__.py | 0 .../doctype/psoa_project/psoa_project.json | 30 ++ .../doctype/psoa_project/psoa_project.py | 10 + erpnext/hooks.py | 3 +- 16 files changed, 951 insertions(+), 1 deletion(-) create mode 100644 erpnext/accounts/doctype/process_statement_of_accounts/__init__.py create mode 100644 erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html create mode 100644 erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js create mode 100644 erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json create mode 100644 erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py create mode 100644 erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py create mode 100644 erpnext/accounts/doctype/process_statement_of_accounts_customer/__init__.py create mode 100644 erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json create mode 100644 erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.py create mode 100644 erpnext/accounts/doctype/psoa_cost_center/__init__.py create mode 100644 erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json create mode 100644 erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py create mode 100644 erpnext/accounts/doctype/psoa_project/__init__.py create mode 100644 erpnext/accounts/doctype/psoa_project/psoa_project.json create mode 100644 erpnext/accounts/doctype/psoa_project/psoa_project.py diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/__init__.py b/erpnext/accounts/doctype/process_statement_of_accounts/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html new file mode 100644 index 00000000000..e1ddeff61f7 --- /dev/null +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html @@ -0,0 +1,89 @@ +

{{ filters.party[0] }}

+

{{ _("Statement of Accounts") }}

+ +
+ {{ frappe.format(filters.from_date, 'Date')}} + {{ _("to") }} + {{ frappe.format(filters.to_date, 'Date')}} +
+ + + + + + + + + + + + + + {% for row in data %} + + {% if(row.posting_date) %} + + + + + + {% else %} + + + + + + {% endif %} + + + {% endfor %} + +
{{ _("Date") }}{{ _("Ref") }}{{ _("Party") }}{{ _("Debit") }}{{ _("Credit") }}{{ _("Balance (Dr - Cr)") }}
{{ frappe.format(row.posting_date, 'Date') }}{{ row.voucher_type }} +
{{ row.voucher_no }}
+ {% if not (filters.party or filters.account) %} + {{ row.party or row.account }} +
+ {% endif %} + + {{ _("Against") }}: {{ row.against }} +
{{ _("Remarks") }}: {{ row.remarks }} + {% if row.bill_no %} +
{{ _("Supplier Invoice No") }}: {{ row.bill_no }} + {% endif %} +
+ {{ frappe.utils.fmt_money(row.debit, filters.presentation_currency) }} + {{ frappe.utils.fmt_money(row.credit, filters.presentation_currency) }}{{ frappe.format(row.account, {fieldtype: "Link"}) or " " }} + {{ row.account and frappe.utils.fmt_money(row.debit, filters.presentation_currency) }} + + {{ row.account and frappe.utils.fmt_money(row.credit, filters.presentation_currency) }} + + {{ frappe.utils.fmt_money(row.balance, filters.presentation_currency) }} +
+

+{% if aging %} +

{{ _("Ageing Report Based On ") }} {{ aging.ageing_based_on }}

+
+ {{ _("Up to " ) }} {{ frappe.format(filters.to_date, 'Date')}} +
+
+ + + + + + + + + + + + + + + + + + +
30 Days60 Days90 Days120 Days
{{ aging.range1 }}{{ aging.range2 }}{{ aging.range3 }}{{ aging.range4 }}
+{% endif %} +

Printed On {{ frappe.format(frappe.utils.get_datetime(), 'Datetime') }}

\ No newline at end of file diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js new file mode 100644 index 00000000000..7425132c468 --- /dev/null +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js @@ -0,0 +1,132 @@ +// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Process Statement Of Accounts', { + view_properties: function(frm) { + frappe.route_options = {doc_type: 'Customer'}; + frappe.set_route("Form", "Customize Form"); + }, + refresh: function(frm){ + if(!frm.doc.__islocal) { + frm.add_custom_button('Send Emails',function(){ + frappe.call({ + method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails", + args: { + "document_name": frm.doc.name, + }, + callback: function(r) { + if(r && r.message) { + frappe.show_alert({message: __('Emails Queued'), indicator: 'blue'}); + } + else{ + frappe.msgprint('No Records for these settings.') + } + } + }); + }); + frm.add_custom_button('Download',function(){ + var url = frappe.urllib.get_full_url( + '/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?' + + 'document_name='+encodeURIComponent(frm.doc.name)) + $.ajax({ + url: url, + type: 'GET', + success: function(result) { + if(jQuery.isEmptyObject(result)){ + frappe.msgprint('No Records for these settings.'); + } + else{ + window.location = url; + } + } + }); + }); + } + }, + onload: function(frm) { + frm.set_query('currency', function(){ + return { + filters: { + 'enabled': 1 + } + } + }); + if(frm.doc.__islocal){ + frm.set_value('from_date', frappe.datetime.add_months(frappe.datetime.get_today(), -1)); + frm.set_value('to_date', frappe.datetime.get_today()); + } + }, + customer_collection: function(frm){ + frm.set_value('collection_name', ''); + if(frm.doc.customer_collection){ + frm.get_field('collection_name').set_label(frm.doc.customer_collection); + } + }, + frequency: function(frm){ + if(frm.doc.frequency != ''){ + frm.set_value('start_date', frappe.datetime.get_today()); + } + else{ + frm.set_value('start_date', ''); + } + }, + fetch_customers: function(frm){ + if(frm.doc.collection_name){ + frappe.call({ + method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.fetch_customers", + args: { + 'customer_collection': frm.doc.customer_collection, + 'collection_name': frm.doc.collection_name, + 'primary_mandatory': frm.doc.primary_mandatory + }, + callback: function(r) { + if(!r.exc) { + if(r.message.length){ + frm.clear_table('customers'); + for (const customer of r.message){ + var row = frm.add_child('customers'); + row.customer = customer.name; + row.primary_email = customer.primary_email; + row.billing_email = customer.billing_email; + } + frm.refresh_field('customers'); + } + else{ + frappe.msgprint('No Customers found with selected options.'); + } + } + } + }); + } + else { + frappe.throw('Enter ' + frm.doc.customer_collection + ' name.'); + } + } +}); + +frappe.ui.form.on('Process Statement Of Accounts Customer', { + customer: function(frm, cdt, cdn){ + var row = locals[cdt][cdn]; + if (!row.customer){ + return; + } + frappe.call({ + method: 'erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.get_customer_emails', + args: { + 'customer_name': row.customer, + 'primary_mandatory': frm.doc.primary_mandatory + }, + callback: function(r){ + if(!r.exe){ + if(r.message.length){ + frappe.model.set_value(cdt, cdn, "primary_email", r.message[0]) + frappe.model.set_value(cdt, cdn, "billing_email", r.message[1]) + } + else { + return + } + } + } + }) + } +}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json new file mode 100644 index 00000000000..4be0e2ec068 --- /dev/null +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json @@ -0,0 +1,310 @@ +{ + "actions": [], + "allow_workflow": 1, + "autoname": "Prompt", + "creation": "2020-05-22 16:46:18.712954", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "section_break_11", + "from_date", + "company", + "account", + "group_by", + "cost_center", + "column_break_14", + "to_date", + "finance_book", + "currency", + "project", + "section_break_3", + "customer_collection", + "collection_name", + "fetch_customers", + "column_break_6", + "primary_mandatory", + "column_break_17", + "customers", + "preferences", + "orientation", + "section_break_14", + "include_ageing", + "ageing_based_on", + "section_break_1", + "enable_auto_email", + "section_break_18", + "frequency", + "filter_duration", + "column_break_21", + "start_date", + "section_break_33", + "subject", + "column_break_28", + "cc_to", + "section_break_30", + "body", + "help_text" + ], + "fields": [ + { + "fieldname": "frequency", + "fieldtype": "Select", + "label": "Frequency", + "options": "Weekly\nMonthly\nQuarterly" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "depends_on": "eval:doc.enable_auto_email == 0;", + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "mandatory_depends_on": "eval:doc.frequency == '';" + }, + { + "depends_on": "eval:doc.enable_auto_email == 0;", + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date", + "mandatory_depends_on": "eval:doc.frequency == '';" + }, + { + "fieldname": "cost_center", + "fieldtype": "Table MultiSelect", + "label": "Cost Center", + "options": "PSOA Cost Center" + }, + { + "fieldname": "project", + "fieldtype": "Table MultiSelect", + "label": "Project", + "options": "PSOA Project" + }, + { + "fieldname": "section_break_3", + "fieldtype": "Section Break", + "label": "Customers" + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_11", + "fieldtype": "Section Break", + "label": "General Ledger Filters" + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "fieldname": "customer_collection", + "fieldtype": "Select", + "label": "Select Customers By", + "options": "\nCustomer Group\nTerritory\nSales Partner\nSales Person" + }, + { + "depends_on": "eval: doc.customer_collection !== ''", + "fieldname": "collection_name", + "fieldtype": "Dynamic Link", + "label": "Recipient", + "options": "customer_collection" + }, + { + "fieldname": "section_break_1", + "fieldtype": "Section Break", + "label": "Email Settings" + }, + { + "fieldname": "account", + "fieldtype": "Link", + "label": "Account", + "options": "Account" + }, + { + "fieldname": "finance_book", + "fieldtype": "Link", + "label": "Finance Book", + "options": "Finance Book" + }, + { + "fieldname": "preferences", + "fieldtype": "Section Break", + "label": "Print Preferences" + }, + { + "fieldname": "orientation", + "fieldtype": "Select", + "label": "Orientation", + "options": "Landscape\nPortrait" + }, + { + "default": "Today", + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date" + }, + { + "default": "Group by Voucher (Consolidated)", + "fieldname": "group_by", + "fieldtype": "Select", + "label": "Group By", + "options": "\nGroup by Voucher\nGroup by Voucher (Consolidated)" + }, + { + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "options": "Currency" + }, + { + "default": "0", + "fieldname": "include_ageing", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Include Ageing Summary" + }, + { + "default": "Due Date", + "depends_on": "eval:doc.include_ageing === 1", + "fieldname": "ageing_based_on", + "fieldtype": "Select", + "label": "Ageing Based On", + "options": "Due Date\nPosting Date" + }, + { + "default": "0", + "fieldname": "enable_auto_email", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Enable Auto Email" + }, + { + "fieldname": "section_break_14", + "fieldtype": "Column Break", + "hide_border": 1 + }, + { + "depends_on": "eval: doc.enable_auto_email ==1", + "fieldname": "section_break_18", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "fieldname": "column_break_21", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval: doc.customer_collection !== ''", + "fieldname": "fetch_customers", + "fieldtype": "Button", + "label": "Fetch Customers", + "options": "fetch_customers", + "print_hide": 1, + "report_hide": 1 + }, + { + "default": "1", + "fieldname": "primary_mandatory", + "fieldtype": "Check", + "label": "Send To Primary Contact" + }, + { + "fieldname": "cc_to", + "fieldtype": "Link", + "label": "CC To", + "options": "User" + }, + { + "default": "1", + "fieldname": "filter_duration", + "fieldtype": "Int", + "label": "Filter Duration (Months)" + }, + { + "fieldname": "customers", + "fieldtype": "Table", + "label": "Customers", + "options": "Process Statement Of Accounts Customer", + "reqd": 1 + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_30", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "fieldname": "section_break_33", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "fieldname": "help_text", + "fieldtype": "HTML", + "label": "Help Text", + "options": "
\n

Note

\n
    \n
  • \nYou can use Jinja tags in Subject and Body fields for dynamic values.\n
  • \n All fields in this doctype are available under the doc object and all fields for the customer to whom the mail will go to is available under the customer object.\n
\n

Examples

\n\n
    \n
  • Subject:

    Statement Of Accounts for {{ customer.name }}

  • \n
  • Body:

    \n
    Hello {{ customer.name }},
    PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.
  • \n
\n" + }, + { + "fieldname": "subject", + "fieldtype": "Data", + "label": "Subject" + }, + { + "fieldname": "body", + "fieldtype": "Text Editor", + "label": "Body" + } + ], + "links": [], + "modified": "2020-08-08 08:47:09.185728", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Process Statement Of Accounts", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py new file mode 100644 index 00000000000..d50e4a8af95 --- /dev/null +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py @@ -0,0 +1,271 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document +from erpnext.accounts.report.general_ledger.general_ledger import execute as get_soa +from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_summary import execute as get_ageing +from frappe.core.doctype.communication.email import make + +from frappe.utils.print_format import report_to_pdf +from frappe.utils.pdf import get_pdf +from frappe.utils import today, add_days, add_months, getdate, format_date +from frappe.utils.jinja import validate_template + +import copy +from datetime import timedelta +from frappe.www.printview import get_print_style + +class ProcessStatementOfAccounts(Document): + def validate(self): + if not self.subject: + self.subject = 'Statement Of Accounts for {{ customer.name }}' + if not self.body: + self.body = 'Hello {{ customer.name }},
PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}.' + + validate_template(self.subject) + validate_template(self.body) + + if not self.customers: + frappe.throw(frappe._('Customers not selected.')) + + if self.enable_auto_email: + self.to_date = self.start_date + self.from_date = add_months(self.to_date, -1 * self.filter_duration) + + +def get_report_pdf(doc, consolidated=True): + statement_dict = {} + aging = '' + base_template_path = "frappe/www/printview.html" + template_path = "erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html" + + for entry in doc.customers: + if doc.include_ageing: + ageing_filters = frappe._dict({ + 'company': doc.company, + 'report_date': doc.to_date, + 'ageing_based_on': doc.ageing_based_on, + 'range1': 30, + 'range2': 60, + 'range3': 90, + 'range4': 120, + 'customer': entry.customer + }) + col1, aging = get_ageing(ageing_filters) + aging[0]['ageing_based_on'] = doc.ageing_based_on + + tax_id = frappe.get_doc('Customer', entry.customer).tax_id + + filters= frappe._dict({ + 'from_date': doc.from_date, + 'to_date': doc.to_date, + 'company': doc.company, + 'finance_book': doc.finance_book if doc.finance_book else None, + "account": doc.account if doc.account else None, + 'party_type': 'Customer', + 'party': [entry.customer], + 'group_by': doc.group_by, + 'currency': doc.currency, + 'cost_center': [cc.cost_center_name for cc in doc.cost_center], + 'project': [p.project_name for p in doc.project], + 'show_opening_entries': 0, + 'include_default_book_entries': 0, + 'show_cancelled_entries': 1, + 'tax_id': tax_id if tax_id else None + }) + col, res = get_soa(filters) + + for x in [0, -2, -1]: + res[x]['account'] = res[x]['account'].replace("'","") + + if len(res) == 3: + continue + html = frappe.render_template(template_path, \ + {"filters": filters, "data": res, "aging": aging[0] if doc.include_ageing else None}) + html = frappe.render_template(base_template_path, {"body": html, \ + "css": get_print_style(), "title": "Statement For " + entry.customer}) + statement_dict[entry.customer] = html + if not bool(statement_dict): + return False + elif consolidated: + result = ''.join(list(statement_dict.values())) + return get_pdf(result, {'orientation': doc.orientation}) + else: + for customer, statement_html in statement_dict.items(): + statement_dict[customer]=get_pdf(statement_html, {'orientation': doc.orientation}) + return statement_dict + +def get_customers_based_on_territory_or_customer_group(customer_collection, collection_name): + fields_dict = { + 'Customer Group': 'customer_group', + 'Territory': 'territory', + } + collection = frappe.get_doc(customer_collection, collection_name) + selected = [customer.name for customer in frappe.get_list(customer_collection, filters=[ + ['lft', '>=', collection.lft], + ['rgt', '<=', collection.rgt] + ], + fields=['name'], + order_by='lft asc, rgt desc' + )] + return frappe.get_list('Customer', fields=['name', 'email_id'], \ + filters=[[fields_dict[customer_collection], 'IN', selected]]) + +def get_customers_based_on_sales_person(sales_person): + lft, rgt = frappe.db.get_value("Sales Person", + sales_person, ["lft", "rgt"]) + records = frappe.db.sql(""" + select distinct parent, parenttype + from `tabSales Team` steam + where parenttype = 'Customer' + and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name = steam.sales_person) + """, (lft, rgt), as_dict=1) + sales_person_records = frappe._dict() + for d in records: + sales_person_records.setdefault(d.parenttype, set()).add(d.parent) + customers = frappe.get_list('Customer', fields=['name', 'email_id'], \ + filters=[['name', 'in', list(sales_person_records['Customer'])]]) + return customers + +def get_recipients_and_cc(customer, doc): + recipients = [] + for clist in doc.customers: + if clist.customer == customer: + recipients.append(clist.billing_email) + if doc.primary_mandatory and clist.primary_email: + recipients.append(clist.primary_email) + cc = [] + if doc.cc_to != '': + try: + cc=[frappe.get_value('User', doc.cc_to, 'email')] + except: + pass + + return recipients, cc + +def get_context(customer, doc): + template_doc = copy.deepcopy(doc) + del template_doc.customers + template_doc.from_date = format_date(template_doc.from_date) + template_doc.to_date = format_date(template_doc.to_date) + return { + 'doc': template_doc, + 'customer': frappe.get_doc('Customer', customer), + 'frappe': frappe.utils + } + +@frappe.whitelist() +def fetch_customers(customer_collection, collection_name, primary_mandatory): + customer_list = [] + customers = [] + + if customer_collection == 'Sales Person': + customers = get_customers_based_on_sales_person(collection_name) + if not bool(customers): + frappe.throw('No Customers found with selected options.') + else: + if customer_collection == 'Sales Partner': + customers = frappe.get_list('Customer', fields=['name', 'email_id'], \ + filters=[['default_sales_partner', '=', collection_name]]) + else: + customers = get_customers_based_on_territory_or_customer_group(customer_collection, collection_name) + + for customer in customers: + primary_email = customer.get('email_id') or '' + billing_email = get_customer_emails(customer.name, 1, billing_and_primary=False) + + if billing_email == '' or (primary_email == '' and int(primary_mandatory)): + continue + + customer_list.append({ + 'name': customer.name, + 'primary_email': primary_email, + 'billing_email': billing_email + }) + return customer_list + +@frappe.whitelist() +def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=True): + billing_email = frappe.db.sql(""" + SELECT c.email_id FROM `tabContact` AS c JOIN `tabDynamic Link` AS l ON c.name=l.parent \ + WHERE l.link_doctype='Customer' and l.link_name='""" + customer_name + """' and \ + c.is_billing_contact=1 \ + order by c.creation desc""") + + if len(billing_email) == 0 or (billing_email[0][0] is None): + if billing_and_primary: + frappe.throw('No billing email found for customer: '+ customer_name) + else: + return '' + + if billing_and_primary: + primary_email = frappe.get_value('Customer', customer_name, 'email_id') + if primary_email is None and int(primary_mandatory): + frappe.throw('No primary email found for customer: '+ customer_name) + return [primary_email or '', billing_email[0][0]] + else: + return billing_email[0][0] or '' + +@frappe.whitelist() +def download_statements(document_name): + doc = frappe.get_doc('Process Statement Of Accounts', document_name) + report = get_report_pdf(doc) + if report: + frappe.local.response.filename = doc.name + '.pdf' + frappe.local.response.filecontent = report + frappe.local.response.type = "download" + +@frappe.whitelist() +def send_emails(document_name, from_scheduler=False): + doc = frappe.get_doc('Process Statement Of Accounts', document_name) + report = get_report_pdf(doc, consolidated=False) + + if report: + for customer, report_pdf in report.items(): + attachments = [{ + 'fname': customer + '.pdf', + 'fcontent': report_pdf + }] + + recipients, cc = get_recipients_and_cc(customer, doc) + context = get_context(customer, doc) + subject = frappe.render_template(doc.subject, context) + message = frappe.render_template(doc.body, context) + + frappe.enqueue( + queue='short', + method=frappe.sendmail, + recipients=recipients, + sender=frappe.session.user, + cc=cc, + subject=subject, + message=message, + now=True, + reference_doctype='Process Statement Of Accounts', + reference_name=document_name, + attachments=attachments + ) + + if doc.enable_auto_email and from_scheduler: + new_to_date = getdate(today()) + if doc.frequency == 'Weekly': + new_to_date = add_days(new_to_date, 7) + else: + new_to_date = add_months(new_to_date, 1 if doc.frequency == 'Monthly' else 3) + new_from_date = add_months(new_to_date, -1 * doc.filter_duration) + doc.add_comment('Comment', 'Emails sent on: ' + frappe.utils.format_datetime(frappe.utils.now())) + doc.db_set('to_date', new_to_date, commit=True) + doc.db_set('from_date', new_from_date, commit=True) + return True + else: + return False + +@frappe.whitelist() +def send_auto_email(): + selected = frappe.get_list('Process Statement Of Accounts', filters={'to_date': format_date(today()), 'enable_auto_email': 1}) + for entry in selected: + send_emails(entry.name, from_scheduler=True) + return True \ No newline at end of file diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py new file mode 100644 index 00000000000..30efbb36833 --- /dev/null +++ b/erpnext/accounts/doctype/process_statement_of_accounts/test_process_statement_of_accounts.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestProcessStatementOfAccounts(unittest.TestCase): + pass diff --git a/erpnext/accounts/doctype/process_statement_of_accounts_customer/__init__.py b/erpnext/accounts/doctype/process_statement_of_accounts_customer/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json b/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json new file mode 100644 index 00000000000..dd04dc1b3c6 --- /dev/null +++ b/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.json @@ -0,0 +1,47 @@ +{ + "actions": [], + "allow_workflow": 1, + "creation": "2020-08-03 16:35:21.852178", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "customer", + "billing_email", + "primary_email" + ], + "fields": [ + { + "fieldname": "customer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Customer", + "options": "Customer", + "reqd": 1 + }, + { + "fieldname": "primary_email", + "fieldtype": "Read Only", + "in_list_view": 1, + "label": "Primary Contact Email" + }, + { + "fieldname": "billing_email", + "fieldtype": "Read Only", + "in_list_view": 1, + "label": "Billing Email" + } + ], + "istable": 1, + "links": [], + "modified": "2020-08-03 22:55:38.875601", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Process Statement Of Accounts Customer", + "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/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.py b/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.py new file mode 100644 index 00000000000..1a760101dba --- /dev/null +++ b/erpnext/accounts/doctype/process_statement_of_accounts_customer/process_statement_of_accounts_customer.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class ProcessStatementOfAccountsCustomer(Document): + pass diff --git a/erpnext/accounts/doctype/psoa_cost_center/__init__.py b/erpnext/accounts/doctype/psoa_cost_center/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json new file mode 100644 index 00000000000..e292b60d68d --- /dev/null +++ b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.json @@ -0,0 +1,30 @@ +{ + "actions": [], + "creation": "2020-08-03 16:56:45.744905", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "cost_center_name" + ], + "fields": [ + { + "fieldname": "cost_center_name", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + } + ], + "istable": 1, + "links": [], + "modified": "2020-08-03 16:56:45.744905", + "modified_by": "Administrator", + "module": "Accounts", + "name": "PSOA Cost Center", + "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/accounts/doctype/psoa_cost_center/psoa_cost_center.py b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py new file mode 100644 index 00000000000..0aeef3ed3a8 --- /dev/null +++ b/erpnext/accounts/doctype/psoa_cost_center/psoa_cost_center.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class PSOACostCenter(Document): + pass diff --git a/erpnext/accounts/doctype/psoa_project/__init__.py b/erpnext/accounts/doctype/psoa_project/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/psoa_project/psoa_project.json b/erpnext/accounts/doctype/psoa_project/psoa_project.json new file mode 100644 index 00000000000..20a03eed96e --- /dev/null +++ b/erpnext/accounts/doctype/psoa_project/psoa_project.json @@ -0,0 +1,30 @@ +{ + "actions": [], + "creation": "2020-08-03 16:52:14.731978", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "project_name" + ], + "fields": [ + { + "fieldname": "project_name", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + } + ], + "istable": 1, + "links": [], + "modified": "2020-08-03 16:53:39.219736", + "modified_by": "Administrator", + "module": "Accounts", + "name": "PSOA Project", + "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/accounts/doctype/psoa_project/psoa_project.py b/erpnext/accounts/doctype/psoa_project/psoa_project.py new file mode 100644 index 00000000000..f4a5dee9752 --- /dev/null +++ b/erpnext/accounts/doctype/psoa_project/psoa_project.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class PSOAProject(Document): + pass diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 95a836fe652..463ad6c94b4 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -322,7 +322,8 @@ scheduler_events = { "erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status", "erpnext.selling.doctype.quotation.quotation.set_expired_status", "erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status", - "erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status" + "erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status", + "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email" ], "daily_long": [ "erpnext.setup.doctype.email_digest.email_digest.send", From d0ae92d4bd11603bbdd8c646b7b17ecd3708b8d3 Mon Sep 17 00:00:00 2001 From: Abhishek Balam Date: Tue, 11 Aug 2020 17:39:17 +0530 Subject: [PATCH 29/30] fix: moved company field, renamed to 'More Information' --- erpnext/crm/doctype/opportunity/opportunity.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 80e707cea7e..5cd5233b2e4 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -50,9 +50,9 @@ "contact_email", "contact_mobile", "more_info", + "company", "campaign", "column_break1", - "company", "transaction_date", "amended_from", "lost_reasons" @@ -344,7 +344,7 @@ "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", - "label": "Company Information", + "label": "More Information", "oldfieldtype": "Section Break", "options": "fa fa-file-text" }, @@ -424,7 +424,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2020-08-11 16:15:45.052240", + "modified": "2020-08-11 17:34:35.066961", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", From ed6e9550ca12bce53e581ebee6b8a96f7f8aa79e Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Tue, 11 Aug 2020 18:04:42 +0530 Subject: [PATCH 30/30] fix: removed payroll from HR. (#22991) Co-authored-by: Rucha Mahabal --- erpnext/hr/desk_page/hr/hr.json | 17 +++++++++-------- erpnext/payroll/desk_page/payroll/payroll.json | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/erpnext/hr/desk_page/hr/hr.json b/erpnext/hr/desk_page/hr/hr.json index 0fed8d322f5..895cf7290c9 100644 --- a/erpnext/hr/desk_page/hr/hr.json +++ b/erpnext/hr/desk_page/hr/hr.json @@ -78,7 +78,7 @@ "idx": 0, "is_standard": 1, "label": "HR", - "modified": "2020-06-16 19:20:50.976045", + "modified": "2020-08-11 17:04:38.655417", "modified_by": "Administrator", "module": "HR", "name": "HR", @@ -88,7 +88,7 @@ "pin_to_top": 0, "shortcuts": [ { - "color": "#9deca2", + "color": "#cef6d1", "format": "{} Active", "label": "Employee", "link_to": "Employee", @@ -96,18 +96,19 @@ "type": "DocType" }, { - "label": "Attendance", - "link_to": "Attendance", - "stats_filter": "", - "type": "DocType" - }, - { + "color": "#ffe8cd", "format": "{} Open", "label": "Leave Application", "link_to": "Leave Application", "stats_filter": "{\"status\":\"Open\"}", "type": "DocType" }, + { + "label": "Attendance", + "link_to": "Attendance", + "stats_filter": "", + "type": "DocType" + }, { "label": "Job Applicant", "link_to": "Job Applicant", diff --git a/erpnext/payroll/desk_page/payroll/payroll.json b/erpnext/payroll/desk_page/payroll/payroll.json index b5eac465c82..285e3b3a135 100644 --- a/erpnext/payroll/desk_page/payroll/payroll.json +++ b/erpnext/payroll/desk_page/payroll/payroll.json @@ -8,7 +8,7 @@ { "hidden": 0, "label": "Taxation", - "links": "[\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n \n }\n]" + "links": "[\n {\n \"label\": \"Payroll Period\",\n \"name\": \"Payroll Period\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Income Tax Slab\",\n \"name\": \"Income Tax Slab\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Other Income\",\n \"name\": \"Employee Other Income\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Declaration\",\n \"name\": \"Employee Tax Exemption Declaration\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Proof Submission\",\n \"name\": \"Employee Tax Exemption Proof Submission\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Category\",\n \"name\": \"Employee Tax Exemption Category\",\n \"type\": \"doctype\"\n \n },\n {\n \"label\": \"Employee Tax Exemption Sub Category\",\n \"name\": \"Employee Tax Exemption Sub Category\",\n \"type\": \"doctype\"\n \n }\n]" }, { "hidden": 0, @@ -38,7 +38,7 @@ "idx": 0, "is_standard": 1, "label": "Payroll", - "modified": "2020-06-19 12:23:06.034046", + "modified": "2020-08-10 19:38:45.976209", "modified_by": "Administrator", "module": "Payroll", "name": "Payroll",