import frappe from 'frappe'; export default class AccountsReceivablePayable { async run(reportType, { date }) { const rows = await getReceivablePayable({ reportType, date, }); return { rows }; } } async function getReceivablePayable({ reportType = 'Receivable', date }) { let entries = []; const debitOrCredit = reportType === 'Receivable' ? 'debit' : 'credit'; const referenceType = reportType === 'Receivable' ? 'SalesInvoice' : 'PurchaseInvoice'; entries = await getLedgerEntries(); const vouchers = await getVouchers(); const futureEntries = getFutureEntries(); const returnEntries = getReturnEntries(); const pdc = getPDC(); const validEntries = getValidEntries(); let data = []; for (let entry of validEntries) { const { outStandingAmount, creditNoteAmount } = getOutstandingAmount(entry); if (outStandingAmount > 0.1 / 10) { const row = { date: entry.date, party: entry.party, }; // due date / bill date row.voucherType = entry.referenceType; row.voucherNo = entry.referenceName; // bill details const invoicedAmount = entry[debitOrCredit] || 0; const paidAmount = invoicedAmount - outStandingAmount - creditNoteAmount; Object.assign(row, { invoicedAmount, paidAmount, outStandingAmount, creditNoteAmount, }); // ageing data.push(row); } } return data; // helpers async function getVouchers() { return await frappe.db.getAll({ doctype: referenceType, fields: ['name', 'date'], filters: { submitted: 1, }, }); } function getValidEntries() { return entries.filter((entry) => { return ( entry.date <= date && entry.referenceType === referenceType && entry[debitOrCredit] > 0 ); }); } function getOutstandingAmount(entry) { let paymentAmount = 0.0, creditNoteAmount = 0.0; let reverseDebitOrCredit = debitOrCredit === 'debit' ? 'credit' : 'debit'; for (let e of getEntriesFor( entry.party, entry.referenceType, entry.referenceName )) { if (e.date <= date) { const amount = e[reverseDebitOrCredit] - e[debitOrCredit]; if (!Object.keys(returnEntries).includes(e.referenceName)) { paymentAmount += amount; } else { creditNoteAmount += amount; } } } return { outStandingAmount: entry[debitOrCredit] - entry[reverseDebitOrCredit] - paymentAmount - creditNoteAmount, creditNoteAmount, }; } function getEntriesFor(party, againstVoucherType, againstVoucher) { // TODO return []; } function getFutureEntries() { return entries.filter((entry) => entry.date > date); } function getReturnEntries() { // TODO return {}; } function getPDC() { return []; } async function getLedgerEntries() { if (entries.length) { return entries; } const partyType = reportType === 'Receivable' ? 'customer' : 'supplier'; const partyList = ( await frappe.db.getAll({ doctype: 'Party', filters: { [partyType]: 1, }, }) ).map((d) => d.name); return await frappe.db.getAll({ doctype: 'AccountingLedgerEntry', fields: [ 'name', 'date', 'account', 'party', 'referenceType', 'referenceName', 'sum(debit) as debit', 'sum(credit) as credit', ], filters: { party: ['in', partyList], }, groupBy: ['referenceType', 'referenceName', 'party'], orderBy: 'date', }); } }