diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 9b0c25fd5592..73437c406583 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1062,7 +1062,8 @@ def make_gl_entries(self, cancel=0, adv_adj=0): else: self.make_exchange_gain_loss_journal() - self.make_advance_gl_entries(cancel=cancel) + if self.book_advance_payments_in_separate_party_account: + self.make_advance_gl_entries(cancel=cancel) def add_party_gl_entries(self, gl_entries): if self.party_account: @@ -1089,15 +1090,14 @@ def add_party_gl_entries(self, gl_entries): if self.book_advance_payments_in_separate_party_account: gle = party_gl_dict.copy() - if self.payment_type == "Receive": - amount = self.base_paid_amount - else: - amount = self.base_received_amount - gle.update( { - dr_or_cr: amount, - dr_or_cr + "_in_account_currency": amount, + dr_or_cr: self.base_paid_amount + if self.payment_type == "Receive" + else self.base_received_amount, + dr_or_cr + "_in_account_currency": self.paid_amount + if self.payment_type == "Receive" + else self.received_amount, "against_voucher_type": "Payment Entry", "against_voucher": self.name, "cost_center": self.cost_center, @@ -1139,53 +1139,28 @@ def add_party_gl_entries(self, gl_entries): gl_entries.append(gle) - def make_advance_gl_entries(self, against_voucher_type=None, against_voucher=None, cancel=0): - if self.book_advance_payments_in_separate_party_account: - gl_entries = [] - for d in self.get("references"): - if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Journal Entry"): - if not (against_voucher_type and against_voucher) or ( - d.reference_doctype == against_voucher_type and d.reference_name == against_voucher - ): - self.make_invoice_liability_entry(gl_entries, d) - - if cancel: - for entry in gl_entries: - frappe.db.set_value( - "GL Entry", - { - "voucher_no": self.name, - "voucher_type": self.doctype, - "voucher_detail_no": entry.voucher_detail_no, - "against_voucher_type": entry.against_voucher_type, - "against_voucher": entry.against_voucher, - }, - "is_cancelled", - 1, - ) + def make_advance_gl_entries( + self, entry: object | dict = None, cancel: bool = 0, update_outstanding: str = "Yes" + ): + gl_entries = [] + self.add_advance_gl_entries(gl_entries, entry) - make_reverse_gl_entries(gl_entries=gl_entries, partial_cancel=True) - return + if cancel: + make_reverse_gl_entries(gl_entries, partial_cancel=True) + else: + make_gl_entries(gl_entries, update_outstanding=update_outstanding) - # same reference added to payment entry - for gl_entry in gl_entries.copy(): - if frappe.db.exists( - "GL Entry", - { - "account": gl_entry.account, - "voucher_type": gl_entry.voucher_type, - "voucher_no": gl_entry.voucher_no, - "voucher_detail_no": gl_entry.voucher_detail_no, - "debit": gl_entry.debit, - "credit": gl_entry.credit, - "is_cancelled": 0, - }, - ): - gl_entries.remove(gl_entry) + def add_advance_gl_entries(self, gl_entries: list, entry: object | dict): + if self.book_advance_payments_in_separate_party_account: + references = [x for x in self.get("references")] + if entry: + references = [x for x in self.get("references") if x.name == entry.name] - make_gl_entries(gl_entries) + for ref in references: + if ref.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Journal Entry"): + self.add_advance_gl_for_reference(gl_entries, ref) - def make_invoice_liability_entry(self, gl_entries, invoice): + def add_advance_gl_for_reference(self, gl_entries, invoice): args_dict = { "party_type": self.party_type, "party": self.party, diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 70a847061476..33773bb441da 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -597,7 +597,31 @@ def make_reverse_gl_entries( is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries) validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"]) - if not partial_cancel: + if partial_cancel: + # Partial cancel is only used by `Advance` in separate account feature. + # Only cancel GL entries for unlinked reference using `voucher_detail_no` + gle = frappe.qb.DocType("GL Entry") + for x in gl_entries: + query = ( + frappe.qb.update(gle) + .set(gle.is_cancelled, True) + .set(gle.modified, now()) + .set(gle.modified_by, frappe.session.user) + .where( + (gle.company == x.company) + & (gle.account == x.account) + & (gle.party_type == x.party_type) + & (gle.party == x.party) + & (gle.voucher_type == x.voucher_type) + & (gle.voucher_no == x.voucher_no) + & (gle.against_voucher_type == x.against_voucher_type) + & (gle.against_voucher == x.against_voucher) + & (gle.voucher_detail_no == x.voucher_detail_no) + ) + ) + query.run() + + else: set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"]) for entry in gl_entries: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index d64bb6b7f885..68d97e1dabdc 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -466,8 +466,10 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n # cancel advance entry doc = frappe.get_doc(voucher_type, voucher_no) frappe.flags.ignore_party_validation = True + # For payments with `Advance` in separate account feature enabled, only new ledger entries are posted for each reference. + # No need to cancel/delete payment ledger entries if not (voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account): - _delete_pl_entries(voucher_type, voucher_no) + _delete_gl_entries(voucher_type, voucher_no) for entry in entries: check_if_advance_entry_modified(entry) @@ -482,7 +484,7 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n entry.update({"referenced_row": referenced_row}) doc.make_exchange_gain_loss_journal([entry]) else: - update_reference_in_payment_entry( + referenced_row = update_reference_in_payment_entry( entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe ) @@ -491,8 +493,8 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n doc = frappe.get_doc(entry.voucher_type, entry.voucher_no) if voucher_type == "Payment Entry" and doc.book_advance_payments_in_separate_party_account: - # both ledgers must be posted to for `Advance as Liability` - doc.make_advance_gl_entries() + # both ledgers must be posted to for `Advance` in separate account feature + doc.make_advance_gl_entries(referenced_row, update_outstanding="No") else: gl_map = doc.build_gl_map() create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1) @@ -669,11 +671,12 @@ def update_reference_in_payment_entry( new_row.docstatus = 1 for field in list(reference_details): new_row.set(field, reference_details[field]) - + row = new_row else: new_row = payment_entry.append("references") new_row.docstatus = 1 new_row.update(reference_details) + row = new_row payment_entry.flags.ignore_validate_update_after_submit = True payment_entry.clear_unallocated_reference_document_rows() @@ -689,6 +692,8 @@ def update_reference_in_payment_entry( if not do_not_save: payment_entry.save(ignore_permissions=True) + return row + def cancel_exchange_gain_loss_journal( parent_doc: dict | object, referenced_dt: str = None, referenced_dn: str = None @@ -864,7 +869,10 @@ def remove_ref_doc_link_from_pe( try: pe_doc = frappe.get_doc("Payment Entry", pe) pe_doc.set_amounts() - pe_doc.make_advance_gl_entries(against_voucher_type=ref_type, against_voucher=ref_no, cancel=1) + references = [ + x for x in pe_doc.references if x.reference_doctype == ref_type and x.reference_name == ref_no + ] + [pe_doc.make_advance_gl_entries(x, cancel=1) for x in references] pe_doc.clear_unallocated_reference_document_rows() pe_doc.validate_payment_type_with_outstanding() except Exception as e: