From db76e8a277f4f62dde079b2429f02689ea3fc25b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 4 May 2023 16:27:43 +0530 Subject: [PATCH 01/23] refactor: cr notes will post for itself --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 4 +--- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index f33439989a8..37d1ec62678 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -628,9 +628,7 @@ class PurchaseInvoice(BuyingController): "credit_in_account_currency": base_grand_total if self.party_account_currency == self.company_currency else grand_total, - "against_voucher": self.return_against - if cint(self.is_return) and self.return_against - else self.name, + "against_voucher": self.name, "against_voucher_type": self.doctype, "project": self.project, "cost_center": self.cost_center, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0bc5aa2ed2d..d03afe36b1c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1104,9 +1104,7 @@ class SalesInvoice(SellingController): "debit_in_account_currency": base_grand_total if self.party_account_currency == self.company_currency else grand_total, - "against_voucher": self.return_against - if cint(self.is_return) and self.return_against - else self.name, + "against_voucher": self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center, "project": self.project, From 00878707aecb9ee7efae49d1dea81150b9adbb1a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Jul 2023 15:59:57 +0530 Subject: [PATCH 02/23] refactor: remove return_against for cr/dr note filter --- .../payment_reconciliation/payment_reconciliation.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index ea06e0ec9ae..8c84f836a54 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -174,15 +174,12 @@ class PaymentReconciliation(Document): self.common_filter_conditions.append(ple.account == self.receivable_payable_account) self.get_return_invoices() - return_invoices = [ - x for x in self.return_invoices if x.return_against == None or x.return_against == "" - ] outstanding_dr_or_cr = [] - if return_invoices: + if self.return_invoices: ple_query = QueryPaymentLedger() return_outstanding = ple_query.get_voucher_outstandings( - vouchers=return_invoices, + vouchers=self.return_invoices, common_filter=self.common_filter_conditions, posting_date=self.ple_posting_date_filter, min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None, From b30c1e1abfcb45950709e7b12481d9e187b845f4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 13 Aug 2023 15:48:25 +0530 Subject: [PATCH 03/23] refactor(test): return invoice will have -ve outstanding --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 63c0c453045..b310f3b788f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1500,8 +1500,8 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(party_credited, 1000) # Check outstanding amount - self.assertFalse(si1.outstanding_amount) - self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500) + self.assertEqual(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"), -1000) + self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 2500) def test_gle_made_when_asset_is_returned(self): create_asset_data() From 0e2fb1188a66d6f1e6fe89564106c024e940107f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 15 Aug 2023 09:02:41 +0530 Subject: [PATCH 04/23] refactor(test): ledger entries will be against itself --- .../test_payment_ledger_entry.py | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py b/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py index fc6dbba7e7f..ce9579ed613 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py +++ b/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py @@ -294,7 +294,7 @@ class TestPaymentLedgerEntry(FrappeTestCase): cr_note1.return_against = si3.name cr_note1 = cr_note1.save().submit() - pl_entries = ( + pl_entries_si3 = ( qb.from_(ple) .select( ple.voucher_type, @@ -309,7 +309,24 @@ class TestPaymentLedgerEntry(FrappeTestCase): .run(as_dict=True) ) - expected_values = [ + pl_entries_cr_note1 = ( + qb.from_(ple) + .select( + ple.voucher_type, + ple.voucher_no, + ple.against_voucher_type, + ple.against_voucher_no, + ple.amount, + ple.delinked, + ) + .where( + (ple.against_voucher_type == cr_note1.doctype) & (ple.against_voucher_no == cr_note1.name) + ) + .orderby(ple.creation) + .run(as_dict=True) + ) + + expected_values_for_si3 = [ { "voucher_type": si3.doctype, "voucher_no": si3.name, @@ -317,18 +334,21 @@ class TestPaymentLedgerEntry(FrappeTestCase): "against_voucher_no": si3.name, "amount": amount, "delinked": 0, - }, + } + ] + # credit/debit notes post ledger entries against itself + expected_values_for_cr_note1 = [ { "voucher_type": cr_note1.doctype, "voucher_no": cr_note1.name, - "against_voucher_type": si3.doctype, - "against_voucher_no": si3.name, + "against_voucher_type": cr_note1.doctype, + "against_voucher_no": cr_note1.name, "amount": -amount, "delinked": 0, }, ] - self.assertEqual(pl_entries[0], expected_values[0]) - self.assertEqual(pl_entries[1], expected_values[1]) + self.assertEqual(pl_entries_si3, expected_values_for_si3) + self.assertEqual(pl_entries_cr_note1, expected_values_for_cr_note1) def test_je_against_inv_and_note(self): ple = self.ple From f6e4ac2b6267ac0b88f399ada1482fc1366a126a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Jul 2023 16:31:10 +0530 Subject: [PATCH 05/23] refactor(test): payments to invoice with -ve outstanding --- .../payment_entry/test_payment_entry.py | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 8f9f7ce3bea..c8bf6644a51 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -702,7 +702,50 @@ class TestPaymentEntry(FrappeTestCase): pe2.submit() # create return entry against si1 - create_sales_invoice(is_return=1, return_against=si1.name, qty=-1) + cr_note = create_sales_invoice(is_return=1, return_against=si1.name, qty=-1) + si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount") + + # create JE(credit note) manually against si1 and cr_note + je = frappe.get_doc( + { + "doctype": "Journal Entry", + "company": si1.company, + "voucher_type": "Credit Note", + "posting_date": nowdate(), + } + ) + je.append( + "accounts", + { + "account": si1.debit_to, + "party_type": "Customer", + "party": si1.customer, + "debit": 0, + "credit": 100, + "debit_in_account_currency": 0, + "credit_in_account_currency": 100, + "reference_type": si1.doctype, + "reference_name": si1.name, + "cost_center": si1.items[0].cost_center, + }, + ) + je.append( + "accounts", + { + "account": cr_note.debit_to, + "party_type": "Customer", + "party": cr_note.customer, + "debit": 100, + "credit": 0, + "debit_in_account_currency": 100, + "credit_in_account_currency": 0, + "reference_type": cr_note.doctype, + "reference_name": cr_note.name, + "cost_center": cr_note.items[0].cost_center, + }, + ) + je.save().submit() + si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount") self.assertEqual(si1_outstanding, -100) From 8f695123cd1e1e931b9ccac314687e010db1524d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 18 Aug 2023 10:10:42 +0530 Subject: [PATCH 06/23] refactor: criteria for `Credit Note Issued` and `Debit Note Issued` --- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 8 ++------ erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 37d1ec62678..9f1224d65e8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1642,12 +1642,8 @@ class PurchaseInvoice(BuyingController): elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(): self.status = "Unpaid" # Check if outstanding amount is 0 due to debit note issued against invoice - elif ( - outstanding_amount <= 0 - and self.is_return == 0 - and frappe.db.get_value( - "Purchase Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1} - ) + elif self.is_return == 0 and frappe.db.get_value( + "Purchase Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1} ): self.status = "Debit Note Issued" elif self.is_return == 1: diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index d03afe36b1c..fba2fa7552e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1730,12 +1730,8 @@ class SalesInvoice(SellingController): elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(): self.status = "Unpaid" # Check if outstanding amount is 0 due to credit note issued against invoice - elif ( - outstanding_amount <= 0 - and self.is_return == 0 - and frappe.db.get_value( - "Sales Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1} - ) + elif self.is_return == 0 and frappe.db.get_value( + "Sales Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1} ): self.status = "Credit Note Issued" elif self.is_return == 1: From f10a93f6ee2bb32d89647007eecf7b5ae74c7f61 Mon Sep 17 00:00:00 2001 From: avc <94137451+git-avc@users.noreply.github.com> Date: Fri, 18 Aug 2023 17:33:37 +0200 Subject: [PATCH 07/23] fix: lost reason opportunity dialog don't appears Fix #36719 --- erpnext/crm/doctype/opportunity/opportunity.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 6ef82971f5c..0b485bb896c 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -1,7 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt frappe.provide("erpnext.crm"); -erpnext.pre_sales.set_as_lost("Quotation"); +erpnext.pre_sales.set_as_lost("Opportunity"); erpnext.sales_common.setup_selling_controller(); From faf9f13215c834ef951fc26303ff596727cb3e67 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 21 Aug 2023 15:51:57 +0530 Subject: [PATCH 08/23] feat: allow `Update Items` for Subcontracted PO not having SCO created --- .../doctype/purchase_order/purchase_order.js | 3 +- .../doctype/purchase_order/purchase_order.py | 13 ++++++++ erpnext/public/js/utils.js | 31 +++++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 7c33056a91f..f6a19514392 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -185,8 +185,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e if(!in_list(["Closed", "Delivered"], doc.status)) { if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received, 2) < 100 && flt(this.frm.doc.per_billed, 2) < 100) { - // Don't add Update Items button if the PO is following the new subcontracting flow. - if (!(this.frm.doc.is_subcontracted && !this.frm.doc.is_old_subcontracting_flow)) { + if (!this.frm.doc.__onload || this.frm.doc.__onload.can_update_items) { this.frm.add_custom_button(__('Update Items'), () => { erpnext.utils.update_child_items({ frm: this.frm, diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 06b9d29e69c..4da1e76c2dc 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -49,9 +49,22 @@ class PurchaseOrder(BuyingController): } ] + def can_update_items(self) -> bool: + result = True + + if self.is_subcontracted and not self.is_old_subcontracting_flow: + # Check - 1: NOT ALLOWED if non-cancelled Subcontracting Order exists for this Purchase Order + if frappe.db.exists( + "Subcontracting Order", {"purchase_order": self.name, "docstatus": ["!=", 2]} + ): + return False + + return result + def onload(self): supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category") self.set_onload("supplier_tds", supplier_tds) + self.set_onload("can_update_items", self.can_update_items()) def validate(self): super(PurchaseOrder, self).validate() diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index f456e5e500c..9116fd1f036 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -678,6 +678,37 @@ erpnext.utils.update_child_items = function(opts) { }) } + if (frm.doc.doctype == 'Purchase Order' && frm.doc.is_subcontracted && !frm.doc.is_old_subcontracting_flow) { + fields.push({ + fieldtype:'Link', + fieldname:'fg_item', + options: 'Item', + reqd: 1, + in_list_view: 0, + read_only: 0, + disabled: 0, + label: __('Finished Good Item'), + get_query: () => { + return { + filters: { + 'is_stock_item': 1, + 'is_sub_contracted_item': 1, + 'default_bom': ['!=', ''] + } + } + }, + }, { + fieldtype:'Float', + fieldname:'fg_item_qty', + reqd: 1, + default: 0, + read_only: 0, + in_list_view: 0, + label: __('Finished Good Item Qty'), + precision: get_precision('fg_item_qty') + }) + } + let dialog = new frappe.ui.Dialog({ title: __("Update Items"), size: "extra-large", From 7a381affce02e5aea2c5a9a38ebf2638e8128f9a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 3 May 2023 20:45:12 +0530 Subject: [PATCH 09/23] refactor: limit output to 50 in reconciliation tool --- .../payment_reconciliation.json | 16 ++++++++++++- .../payment_reconciliation.py | 2 ++ erpnext/accounts/utils.py | 23 +++++++++++++++++-- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index 5f6c7034ed3..b88791d3f91 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -27,8 +27,10 @@ "bank_cash_account", "cost_center", "sec_break1", + "invoice_name", "invoices", "column_break_15", + "payment_name", "payments", "sec_break2", "allocation" @@ -137,6 +139,7 @@ "label": "Minimum Invoice Amount" }, { + "default": "50", "description": "System will fetch all the entries if limit value is zero.", "fieldname": "invoice_limit", "fieldtype": "Int", @@ -167,6 +170,7 @@ "label": "Maximum Payment Amount" }, { + "default": "50", "description": "System will fetch all the entries if limit value is zero.", "fieldname": "payment_limit", "fieldtype": "Int", @@ -194,13 +198,23 @@ "label": "Default Advance Account", "mandatory_depends_on": "doc.party_type", "options": "Account" + }, + { + "fieldname": "invoice_name", + "fieldtype": "Data", + "label": "Filter on Invoice" + }, + { + "fieldname": "payment_name", + "fieldtype": "Data", + "label": "Filter on Payment" } ], "hide_toolbar": 1, "icon": "icon-resize-horizontal", "issingle": 1, "links": [], - "modified": "2023-06-09 13:02:48.718362", + "modified": "2023-08-15 05:35:50.109290", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 3a9e80a9d94..5cc159ff4a0 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -226,6 +226,8 @@ class PaymentReconciliation(Document): min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None, max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None, accounting_dimensions=self.accounting_dimension_filter_conditions, + limit=self.invoice_limit, + voucher_no=self.invoice_name, ) cr_dr_notes = ( diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index bccf6f10b63..9d6d0f91fba 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -908,7 +908,9 @@ def get_outstanding_invoices( min_outstanding=None, max_outstanding=None, accounting_dimensions=None, - vouchers=None, + vouchers=None, # list of dicts [{'voucher_type': '', 'voucher_no': ''}] for filtering + limit=None, # passed by reconciliation tool + voucher_no=None, # filter passed by reconciliation tool ): ple = qb.DocType("Payment Ledger Entry") @@ -941,6 +943,8 @@ def get_outstanding_invoices( max_outstanding=max_outstanding, get_invoices=True, accounting_dimensions=accounting_dimensions or [], + limit=limit, + voucher_no=voucher_no, ) for d in invoice_list: @@ -1678,12 +1682,13 @@ class QueryPaymentLedger(object): self.voucher_posting_date = [] self.min_outstanding = None self.max_outstanding = None + self.limit = self.voucher_no = None def reset(self): # clear filters self.vouchers.clear() self.common_filter.clear() - self.min_outstanding = self.max_outstanding = None + self.min_outstanding = self.max_outstanding = self.limit = None # clear result self.voucher_outstandings.clear() @@ -1697,6 +1702,7 @@ class QueryPaymentLedger(object): filter_on_voucher_no = [] filter_on_against_voucher_no = [] + if self.vouchers: voucher_types = set([x.voucher_type for x in self.vouchers]) voucher_nos = set([x.voucher_no for x in self.vouchers]) @@ -1707,6 +1713,10 @@ class QueryPaymentLedger(object): filter_on_against_voucher_no.append(ple.against_voucher_type.isin(voucher_types)) filter_on_against_voucher_no.append(ple.against_voucher_no.isin(voucher_nos)) + if self.voucher_no: + filter_on_voucher_no.append(ple.voucher_no.like(f"%{self.voucher_no}%")) + filter_on_against_voucher_no.append(ple.against_voucher_no.like(f"%{self.voucher_no}%")) + # build outstanding amount filter filter_on_outstanding_amount = [] if self.min_outstanding: @@ -1822,6 +1832,11 @@ class QueryPaymentLedger(object): ) ) + if self.limit: + self.cte_query_voucher_amount_and_outstanding = ( + self.cte_query_voucher_amount_and_outstanding.limit(self.limit) + ) + # execute SQL self.voucher_outstandings = self.cte_query_voucher_amount_and_outstanding.run(as_dict=True) @@ -1835,6 +1850,8 @@ class QueryPaymentLedger(object): get_payments=False, get_invoices=False, accounting_dimensions=None, + limit=None, + voucher_no=None, ): """ Fetch voucher amount and outstanding amount from Payment Ledger using Database CTE @@ -1856,6 +1873,8 @@ class QueryPaymentLedger(object): self.max_outstanding = max_outstanding self.get_payments = get_payments self.get_invoices = get_invoices + self.limit = limit + self.voucher_no = voucher_no self.query_for_outstanding() return self.voucher_outstandings From e48f8139ebdd5aa64e3b9139157948a7a17afb61 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Jul 2023 08:40:47 +0530 Subject: [PATCH 10/23] refactor: trigger on value change --- .../payment_reconciliation/payment_reconciliation.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 2adc1238b70..7b7ce7a8920 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -163,6 +163,15 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.frm.refresh(); } + invoice_name() { + this.frm.trigger("get_unreconciled_entries"); + } + + payment_name() { + this.frm.trigger("get_unreconciled_entries"); + } + + clear_child_tables() { this.frm.clear_table("invoices"); this.frm.clear_table("payments"); From 52f609e67a30988e498cf03bc0da7fa946ab3f5d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Jul 2023 08:49:30 +0530 Subject: [PATCH 11/23] refactor: filter on cr/dr notes --- .../payment_reconciliation.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 5cc159ff4a0..a724dbfa757 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -5,6 +5,7 @@ import frappe from frappe import _, msgprint, qb from frappe.model.document import Document +from frappe.query_builder import Criterion from frappe.query_builder.custom import ConstantColumn from frappe.utils import flt, fmt_money, get_link_to_form, getdate, nowdate, today @@ -146,6 +147,15 @@ class PaymentReconciliation(Document): def get_return_invoices(self): voucher_type = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice" doc = qb.DocType(voucher_type) + + conditions = [] + conditions.append(doc.docstatus == 1) + conditions.append(doc[frappe.scrub(self.party_type)] == self.party) + conditions.append(doc.is_return == 1) + + if self.payment_name: + conditions.append(doc.name.like(f"%{self.payment_name}%")) + self.return_invoices = ( qb.from_(doc) .select( @@ -153,11 +163,7 @@ class PaymentReconciliation(Document): doc.name.as_("voucher_no"), doc.return_against, ) - .where( - (doc.docstatus == 1) - & (doc[frappe.scrub(self.party_type)] == self.party) - & (doc.is_return == 1) - ) + .where(Criterion.all(conditions)) .run(as_dict=True) ) From 86bac2cf52e6094755e8f21df871a7f56a4d53a5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Jul 2023 09:02:05 +0530 Subject: [PATCH 12/23] refactor: filter on advance payments --- .../doctype/payment_reconciliation/payment_reconciliation.py | 3 +++ erpnext/controllers/accounts_controller.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index a724dbfa757..5f69c6720b6 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -75,6 +75,9 @@ class PaymentReconciliation(Document): } ) + if self.payment_name: + condition.update({"name": self.payment_name}) + payment_entries = get_advance_payment_entries( self.party_type, self.party, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index fbf97aab594..7e43dbcd206 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2396,6 +2396,9 @@ def get_common_query( q = q.select((payment_entry.target_exchange_rate).as_("exchange_rate")) if condition: + if condition.get("name", None): + q = q.where(payment_entry.name.like(f"%{condition.get('name')}%")) + q = q.where(payment_entry.company == condition["company"]) q = ( q.where(payment_entry.posting_date >= condition["from_payment_date"]) From d01f0f2e965d776b9b142e33559efe012b4734e5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Jul 2023 09:06:08 +0530 Subject: [PATCH 13/23] refactor: filter for journal entries --- .../doctype/payment_reconciliation/payment_reconciliation.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 5f69c6720b6..68e3da3e854 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -93,6 +93,9 @@ class PaymentReconciliation(Document): def get_jv_entries(self): condition = self.get_conditions() + if self.payment_name: + condition += f" and t1.name like '%%{self.payment_name}%%'" + if self.get("cost_center"): condition += f" and t2.cost_center = '{self.cost_center}' " From 9349bc77c58aedf36e80739412f5786b40c97c19 Mon Sep 17 00:00:00 2001 From: RitvikSardana Date: Tue, 22 Aug 2023 14:12:42 +0530 Subject: [PATCH 14/23] fix: Accounts Payable Currency bug --- .../report/accounts_receivable/accounts_receivable.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index a7b35a579a9..751063ad8e6 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -214,8 +214,8 @@ class ReceivablePayableReport(object): for party_type in self.party_type: if self.filters.get(scrub(party_type)): amount = ple.amount_in_account_currency - else: - amount = ple.amount + else: + amount = ple.amount amount_in_account_currency = ple.amount_in_account_currency # update voucher From 87e2309e8e841a97aef06e854151a6ed6d3d32fc Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Tue, 22 Aug 2023 15:30:04 +0530 Subject: [PATCH 15/23] fix: avoid duplicate asset activity entries on asset capitalization (#36756) --- erpnext/assets/doctype/asset/asset.py | 13 ++++++++----- .../asset_capitalization/asset_capitalization.py | 1 + 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index ce894eb00d9..ddb09c1f44b 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -96,11 +96,14 @@ class Asset(AccountsController): "Asset Depreciation Schedules created:
{0}

Please check, edit if needed, and submit the Asset." ).format(asset_depr_schedules_links) ) - if not frappe.db.exists( - { - "doctype": "Asset Activity", - "asset": self.name, - } + if ( + not frappe.db.exists( + { + "doctype": "Asset Activity", + "asset": self.name, + } + ) + and not self.flags.asset_created_via_asset_capitalization ): add_asset_activity(self.name, _("Asset created")) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 324b7392a8b..0bf2fbb14b6 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -509,6 +509,7 @@ class AssetCapitalization(StockController): asset_doc.gross_purchase_amount = total_target_asset_value asset_doc.purchase_receipt_amount = total_target_asset_value asset_doc.flags.ignore_validate = True + asset_doc.flags.asset_created_via_asset_capitalization = True asset_doc.insert() self.target_asset = asset_doc.name From 9588bb74439de0c17cd0b30461bb3eadbb0b798e Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 21 Aug 2023 22:23:57 +0530 Subject: [PATCH 16/23] fix: validate FG Item and Qty --- .../doctype/purchase_order/purchase_order.py | 23 ++++++------ erpnext/controllers/accounts_controller.py | 35 +++++++++++++++++++ erpnext/public/js/utils.js | 4 ++- 3 files changed, 49 insertions(+), 13 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 4da1e76c2dc..3576cd426d6 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -49,18 +49,6 @@ class PurchaseOrder(BuyingController): } ] - def can_update_items(self) -> bool: - result = True - - if self.is_subcontracted and not self.is_old_subcontracting_flow: - # Check - 1: NOT ALLOWED if non-cancelled Subcontracting Order exists for this Purchase Order - if frappe.db.exists( - "Subcontracting Order", {"purchase_order": self.name, "docstatus": ["!=", 2]} - ): - return False - - return result - def onload(self): supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category") self.set_onload("supplier_tds", supplier_tds) @@ -463,6 +451,17 @@ class PurchaseOrder(BuyingController): else: self.db_set("per_received", 0, update_modified=False) + def can_update_items(self) -> bool: + result = True + + if self.is_subcontracted and not self.is_old_subcontracting_flow: + if frappe.db.exists( + "Subcontracting Order", {"purchase_order": self.name, "docstatus": ["!=", 2]} + ): + result = False + + return result + def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0): """get last purchase rate for an item""" diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 340ec01bee7..081abe12104 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2855,6 +2855,27 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil return update_supplied_items + def validate_fg_item_for_subcontracting(new_data, is_new): + if is_new: + if not new_data.get("fg_item"): + frappe.throw( + _("Finished Good Item is not specified for service item {0}").format(new_data["item_code"]) + ) + else: + is_sub_contracted_item, default_bom = frappe.db.get_value( + "Item", new_data["fg_item"], ["is_sub_contracted_item", "default_bom"] + ) + + if not is_sub_contracted_item: + frappe.throw( + _("Finished Good Item {0} must be a sub-contracted item").format(new_data["fg_item"]) + ) + elif not default_bom: + frappe.throw(_("Default BOM not found for FG Item {0}").format(new_data["fg_item"])) + + if not new_data.get("fg_item_qty"): + frappe.throw(_("Finished Good Item {0} Qty can not be zero").format(new_data["fg_item"])) + data = json.loads(trans_items) any_qty_changed = False # updated to true if any item's qty changes @@ -2886,6 +2907,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil prev_rate, new_rate = flt(child_item.get("rate")), flt(d.get("rate")) prev_qty, new_qty = flt(child_item.get("qty")), flt(d.get("qty")) + prev_fg_qty, new_fg_qty = flt(child_item.get("fg_item_qty")), flt(d.get("fg_item_qty")) prev_con_fac, new_con_fac = flt(child_item.get("conversion_factor")), flt( d.get("conversion_factor") ) @@ -2898,6 +2920,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil rate_unchanged = prev_rate == new_rate qty_unchanged = prev_qty == new_qty + fg_qty_unchanged = prev_fg_qty == new_fg_qty uom_unchanged = prev_uom == new_uom conversion_factor_unchanged = prev_con_fac == new_con_fac any_conversion_factor_changed |= not conversion_factor_unchanged @@ -2907,6 +2930,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if ( rate_unchanged and qty_unchanged + and fg_qty_unchanged and conversion_factor_unchanged and uom_unchanged and date_unchanged @@ -2917,6 +2941,17 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil if flt(child_item.get("qty")) != flt(d.get("qty")): any_qty_changed = True + if ( + parent.doctype == "Purchase Order" + and parent.is_subcontracted + and not parent.is_old_subcontracting_flow + ): + validate_fg_item_for_subcontracting(d, new_child_flag) + child_item.fg_item_qty = flt(d["fg_item_qty"]) + + if new_child_flag: + child_item.fg_item = d["fg_item"] + child_item.qty = flt(d.get("qty")) rate_precision = child_item.precision("rate") or 2 conv_fac_precision = child_item.precision("conversion_factor") or 2 diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 9116fd1f036..c11d123982f 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -579,7 +579,9 @@ erpnext.utils.update_child_items = function(opts) { "conversion_factor": d.conversion_factor, "qty": d.qty, "rate": d.rate, - "uom": d.uom + "uom": d.uom, + "fg_item": d.fg_item, + "fg_item_qty": d.fg_item_qty, } }); From b9b1717e96f2dbb78ff9816d41270ed5aae626bb Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 22 Aug 2023 16:53:54 +0530 Subject: [PATCH 17/23] fix: re-validate PO while updating items --- erpnext/controllers/accounts_controller.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 081abe12104..843103da9bf 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -3055,11 +3055,20 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil parent.update_ordered_qty() parent.update_ordered_and_reserved_qty() parent.update_receiving_percentage() - if parent.is_old_subcontracting_flow: - if should_update_supplied_items(parent): - parent.update_reserved_qty_for_subcontract() - parent.create_raw_materials_supplied() - parent.save() + + if parent.is_subcontracted: + if parent.is_old_subcontracting_flow: + if should_update_supplied_items(parent): + parent.update_reserved_qty_for_subcontract() + parent.create_raw_materials_supplied() + parent.save() + else: + if not parent.can_update_items(): + frappe.throw( + _( + "Items cannot be updated as Subcontracting Order is created against the Purchase Order {0}." + ).format(frappe.bold(parent.name)) + ) else: # Sales Order parent.validate_warehouse() parent.update_reserved_qty() From 611c2bf775673be1b6fe5aba313df60d371ccc18 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 17:36:56 +0530 Subject: [PATCH 18/23] fix: not able to make stock entry (backport #36759) (#36767) fix: not able to make stock entry (#36759) (cherry picked from commit 873ee384a1f0832faf84015167f7bdc697f1dc46) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/material_request/material_request.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 1139c4b83a5..9efae6a9d60 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -656,7 +656,10 @@ def make_stock_entry(source_name, target_doc=None): "job_card_item": "job_card_item", }, "postprocess": update_item, - "condition": lambda doc: doc.ordered_qty < doc.stock_qty, + "condition": lambda doc: ( + flt(doc.ordered_qty, doc.precision("ordered_qty")) + < flt(doc.stock_qty, doc.precision("ordered_qty")) + ), }, }, target_doc, From 305d39f6a157abd285df523e4b1c4beb3e5af7d2 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 22 Aug 2023 18:24:30 +0530 Subject: [PATCH 19/23] test: add test case for Subcontract PO update items --- .../purchase_order/test_purchase_order.py | 65 +++++++++++++++++++ .../tests/test_subcontracting_controller.py | 2 +- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 3edaffae2ad..55c01e85137 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -901,6 +901,71 @@ class TestPurchaseOrder(FrappeTestCase): self.assertRaises(frappe.ValidationError, po.save) + def test_update_items_for_subcontracting_purchase_order(self): + from erpnext.controllers.tests.test_subcontracting_controller import ( + get_subcontracting_order, + make_bom_for_subcontracted_items, + make_raw_materials, + make_service_items, + make_subcontracted_items, + ) + + def update_items(po, qty): + trans_items = [po.items[0].as_dict()] + trans_items[0]["qty"] = qty + trans_items[0]["fg_item_qty"] = qty + trans_items = json.dumps(trans_items, default=str) + + return update_child_qty_rate( + po.doctype, + trans_items, + po.name, + ) + + make_subcontracted_items() + make_raw_materials() + make_service_items() + make_bom_for_subcontracted_items() + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 7", + "qty": 10, + "rate": 100, + "fg_item": "Subcontracted Item SA7", + "fg_item_qty": 10, + }, + ] + po = create_purchase_order( + rm_items=service_items, + is_subcontracted=1, + supplier_warehouse="_Test Warehouse 1 - _TC", + ) + + update_items(po, qty=20) + po.reload() + + # Test - 1: Items should be updated as there is no Subcontracting Order against PO + self.assertEqual(po.items[0].qty, 20) + self.assertEqual(po.items[0].fg_item_qty, 20) + + sco = get_subcontracting_order(po_name=po.name, warehouse="_Test Warehouse - _TC") + + # Test - 2: ValidationError should be raised as there is Subcontracting Order against PO + self.assertRaises(frappe.ValidationError, update_items, po=po, qty=30) + + sco.reload() + sco.cancel() + po.reload() + + update_items(po, qty=30) + po.reload() + + # Test - 3: Items should be updated as the Subcontracting Order is cancelled + self.assertEqual(po.items[0].qty, 30) + self.assertEqual(po.items[0].fg_item_qty, 30) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py index eeb35c4d964..6b61ae949da 100644 --- a/erpnext/controllers/tests/test_subcontracting_controller.py +++ b/erpnext/controllers/tests/test_subcontracting_controller.py @@ -1090,7 +1090,7 @@ def get_subcontracting_order(**args): po = frappe.get_doc("Purchase Order", args.get("po_name")) if po.is_subcontracted: - return create_subcontracting_order(po_name=po.name, **args) + return create_subcontracting_order(**args) if not args.service_items: service_items = [ From a77e9d36cc557293f4612cd1491b764613db0198 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 22:09:45 +0530 Subject: [PATCH 20/23] fix: Procurement Tracker report not showing material request items (backport #36768) (#36774) fix: Procurement Tracker report not showing material request items (#36768) (cherry picked from commit 6a9935c00e502a28fa976c963a218f7e11b891ec) Co-authored-by: rohitwaghchaure --- .../procurement_tracker.py | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index 71019e80377..a7e03c08fac 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -154,31 +154,35 @@ def get_data(filters): procurement_record = [] if procurement_record_against_mr: procurement_record += procurement_record_against_mr + for po in purchase_order_entry: # fetch material records linked to the purchase order item - mr_record = mr_records.get(po.material_request_item, [{}])[0] - procurement_detail = { - "material_request_date": mr_record.get("transaction_date"), - "cost_center": po.cost_center, - "project": po.project, - "requesting_site": po.warehouse, - "requestor": po.owner, - "material_request_no": po.material_request, - "item_code": po.item_code, - "quantity": flt(po.qty), - "unit_of_measurement": po.stock_uom, - "status": po.status, - "purchase_order_date": po.transaction_date, - "purchase_order": po.parent, - "supplier": po.supplier, - "estimated_cost": flt(mr_record.get("amount")), - "actual_cost": flt(pi_records.get(po.name)), - "purchase_order_amt": flt(po.amount), - "purchase_order_amt_in_company_currency": flt(po.base_amount), - "expected_delivery_date": po.schedule_date, - "actual_delivery_date": pr_records.get(po.name), - } - procurement_record.append(procurement_detail) + material_requests = mr_records.get(po.material_request_item, [{}]) + + for mr_record in material_requests: + procurement_detail = { + "material_request_date": mr_record.get("transaction_date"), + "cost_center": po.cost_center, + "project": po.project, + "requesting_site": po.warehouse, + "requestor": po.owner, + "material_request_no": po.material_request, + "item_code": po.item_code, + "quantity": flt(po.qty), + "unit_of_measurement": po.stock_uom, + "status": po.status, + "purchase_order_date": po.transaction_date, + "purchase_order": po.parent, + "supplier": po.supplier, + "estimated_cost": flt(mr_record.get("amount")), + "actual_cost": flt(pi_records.get(po.name)), + "purchase_order_amt": flt(po.amount), + "purchase_order_amt_in_company_currency": flt(po.base_amount), + "expected_delivery_date": po.schedule_date, + "actual_delivery_date": pr_records.get(po.name), + } + procurement_record.append(procurement_detail) + return procurement_record @@ -301,7 +305,7 @@ def get_po_entries(filters): & (parent.name == child.parent) & (parent.status.notin(("Closed", "Completed", "Cancelled"))) ) - .groupby(parent.name, child.item_code) + .groupby(parent.name, child.material_request_item) ) query = apply_filters_on_query(filters, parent, child, query) From 60eee564bfaa473ecb1afcf16d6e6f65b9928e34 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 23 Aug 2023 17:58:15 +0530 Subject: [PATCH 21/23] refactor: Payment btn criteria for Cr/Dr notes --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 3 +-- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 66438a7efab..efe97415a55 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -86,8 +86,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. } } - if(doc.docstatus == 1 && doc.outstanding_amount != 0 - && !(doc.is_return && doc.return_against) && !doc.on_hold) { + if(doc.docstatus == 1 && doc.outstanding_amount != 0 && !doc.on_hold) { this.frm.add_custom_button( __('Payment'), () => this.make_payment_entry(), diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index a4bcdb41db5..642e99cd58a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -98,8 +98,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm); } - if (doc.docstatus == 1 && doc.outstanding_amount!=0 - && !(cint(doc.is_return) && doc.return_against)) { + if (doc.docstatus == 1 && doc.outstanding_amount!=0) { this.frm.add_custom_button( __('Payment'), () => this.make_payment_entry(), From 56b26852f31bf61d149953cc89134ca9441cc2d0 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Wed, 23 Aug 2023 18:01:59 +0530 Subject: [PATCH 22/23] fix: use current asset depr schedule to make temp schedule (#36783) fix: use current depr schedule to make temp schedule --- .../asset_depreciation_schedule.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 2b4b248a302..83350aaf0c9 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -779,9 +779,20 @@ def make_new_active_asset_depr_schedules_and_cancel_current_ones( def get_temp_asset_depr_schedule_doc( asset_doc, row, date_of_disposal=None, date_of_return=None, update_asset_finance_book_row=False ): - asset_depr_schedule_doc = frappe.new_doc("Asset Depreciation Schedule") + current_asset_depr_schedule_doc = get_asset_depr_schedule_doc( + asset_doc.name, "Active", row.finance_book + ) - asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data( + if not current_asset_depr_schedule_doc: + frappe.throw( + _("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format( + asset_doc.name, row.finance_book + ) + ) + + temp_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) + + temp_asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data( asset_doc, row, date_of_disposal, @@ -789,7 +800,7 @@ def get_temp_asset_depr_schedule_doc( update_asset_finance_book_row, ) - return asset_depr_schedule_doc + return temp_asset_depr_schedule_doc @frappe.whitelist() From bb3bd02f530f44bc99cc3af6b6ff4634b8ca7a40 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Wed, 23 Aug 2023 19:22:25 +0530 Subject: [PATCH 23/23] chore: styling improvements for asset depr sch table (#36792) * chore: improve asset depr sch table * chore: fix text color --- erpnext/assets/doctype/asset/asset.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 0a2f61d23bd..962292b8eed 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -228,15 +228,19 @@ frappe.ui.form.on('Asset', { {name: __("Schedule Date"), editable: false, resizable: false, width: 270}, {name: __("Depreciation Amount"), editable: false, resizable: false, width: 164}, {name: __("Accumulated Depreciation Amount"), editable: false, resizable: false, width: 164}, - {name: __("Journal Entry"), editable: false, resizable: false, format: value => `${value}`, width: 312} + {name: __("Journal Entry"), editable: false, resizable: false, format: value => `${value}`, width: 304} ], data: data, + layout: "fluid", serialNoColumn: false, checkboxColumn: true, cellHeight: 35 }); - datatable.style.setStyle(`.dt-scrollable`, {'font-size': '0.75rem', 'margin-bottom': '1rem'}); + datatable.style.setStyle(`.dt-scrollable`, {'font-size': '0.75rem', 'margin-bottom': '1rem', 'margin-left': '0.35rem', 'margin-right': '0.35rem'}); + datatable.style.setStyle(`.dt-header`, {'margin-left': '0.35rem', 'margin-right': '0.35rem'}); + datatable.style.setStyle(`.dt-cell--header`, {'color': 'var(--text-muted)'}); + datatable.style.setStyle(`.dt-cell`, {'color': 'var(--text-color)'}); datatable.style.setStyle(`.dt-cell--col-1`, {'text-align': 'center'}); datatable.style.setStyle(`.dt-cell--col-2`, {'font-weight': 600}); datatable.style.setStyle(`.dt-cell--col-3`, {'font-weight': 600});