feat: UI for unreconcile
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 642e99c..fe931ee 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -183,6 +183,50 @@
}, __('Create'));
}
}
+
+ if (doc.docstatus == 1) {
+ frappe.call({
+ "method": "erpnext.accounts.doctype.unreconcile_payments.unreconcile_payments.doc_has_payments",
+ "args": {
+ "doctype": this.frm.doc.doctype,
+ "docname": this.frm.doc.name
+ },
+ callback: function(r) {
+ if (r.message) {
+ me.frm.add_custom_button(__("Un-Reconcile"), function() {
+ me.unreconcile_prompt();
+ });
+ }
+ }
+ });
+ }
+ }
+
+ unreconcile_prompt() {
+ // get linked payments
+ let query_args = {
+ query:"erpnext.accounts.doctype.unreconcile_payments.unreconcile_payments.get_linked_payments_for_doc",
+ filters: {
+ doctype: this.frm.doc.doctype,
+ docname: this.frm.doc.name
+ }
+ }
+
+ new frappe.ui.form.MultiSelectDialog({
+ doctype: "Payment Ledger Entry",
+ target: this.cur_frm,
+ setters: { },
+ add_filters_group: 0,
+ date_field: "posting_date",
+ columns: ["voucher_type", "voucher_no", "allocated_amount"],
+ get_query() {
+ return query_args;
+ },
+ action(selections) {
+ console.log(selections);
+ }
+ });
+
}
make_maintenance_schedule() {
diff --git a/erpnext/accounts/doctype/unreconcile_payments/unreconcile_payments.py b/erpnext/accounts/doctype/unreconcile_payments/unreconcile_payments.py
index ab2cc71..ed978cb 100644
--- a/erpnext/accounts/doctype/unreconcile_payments/unreconcile_payments.py
+++ b/erpnext/accounts/doctype/unreconcile_payments/unreconcile_payments.py
@@ -4,7 +4,7 @@
import frappe
from frappe import qb
from frappe.model.document import Document
-from frappe.query_builder.functions import Sum
+from frappe.query_builder.functions import Abs, Sum
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries, update_voucher_outstanding
@@ -55,3 +55,50 @@
alloc.reference_doctype, alloc.reference_name, account, party_type, party
)
frappe.db.set_value("Unreconcile Payment Entries", alloc.name, "unlinked", True)
+
+
+@frappe.whitelist()
+def doc_has_payments(doctype, docname):
+ if doctype in ["Sales Invoice", "Purchase Invoice"]:
+ return frappe.db.count(
+ "Payment Ledger Entry",
+ filters={"delinked": 0, "against_voucher_no": docname, "amount": ["<", 0]},
+ )
+ else:
+ return frappe.db.count(
+ "Payment Ledger Entry",
+ filters={"delinked": 0, "voucher_no": docname, "against_voucher_no": ["!=", docname]},
+ )
+
+
+@frappe.whitelist()
+def get_linked_payments_for_doc(doctype, txt, searchfield, start, page_len, filters):
+ if filters.get("doctype") and filters.get("docname"):
+ _dt = filters.get("doctype")
+ _dn = filters.get("docname")
+ ple = qb.DocType("Payment Ledger Entry")
+ if _dt in ["Sales Invoice", "Purchase Invoice"]:
+ res = (
+ qb.from_(ple)
+ .select(
+ ple.voucher_type,
+ ple.voucher_no,
+ Abs(Sum(ple.amount_in_account_currency)).as_("allocated_amount"),
+ )
+ .where((ple.delinked == 0) & (ple.against_voucher_no == _dn) & (ple.amount < 0))
+ .groupby(ple.against_voucher_no)
+ .run(as_dict=True)
+ )
+ return res
+ else:
+ return frappe.db.get_all(
+ "Payment Ledger Entry",
+ filters={
+ "delinked": 0,
+ "voucher_no": _dn,
+ "against_voucher_no": ["!=", _dn],
+ "amount": ["<", 0],
+ },
+ group_by="against_voucher_no",
+ fields=["against_voucher_type", "against_voucher_no", "Sum(amount_in_account_currency)"],
+ )