mirror of
https://github.com/frappe/books.git
synced 2024-11-08 14:50:56 +00:00
AccountsReceivablePayable first cut
This commit is contained in:
parent
a5b4b81051
commit
55d4058b02
@ -33,6 +33,8 @@ module.exports = {
|
||||
frappe.desk.menu.addItem('Trial Balance', '#report/trial-balance');
|
||||
frappe.desk.menu.addItem('Sales Register', '#report/sales-register');
|
||||
frappe.desk.menu.addItem('Purchase Register', '#report/purchase-register');
|
||||
frappe.desk.menu.addItem('Accounts Receivable', '#report/accounts-receivable');
|
||||
frappe.desk.menu.addItem('Accounts Payable', '#report/accounts-payable');
|
||||
|
||||
frappe.router.default = '#tree/Account';
|
||||
|
||||
|
41
reports/AccountsReceivablePayable/AccountsPayableView.js
Normal file
41
reports/AccountsReceivablePayable/AccountsPayableView.js
Normal file
@ -0,0 +1,41 @@
|
||||
const frappe = require('frappejs');
|
||||
const ReportPage = require('frappejs/client/desk/reportpage');
|
||||
const { DateTime } = require('luxon');
|
||||
|
||||
module.exports = class AccountsReceivableView extends ReportPage {
|
||||
constructor(opts) {
|
||||
super({
|
||||
title: frappe._('Accounts Payable'),
|
||||
filterFields: [
|
||||
{fieldtype: 'Date', fieldname: 'date', label: 'As on Date', required: 1},
|
||||
]
|
||||
});
|
||||
|
||||
this.method = 'accounts-payable';
|
||||
this.datatableOptions = {
|
||||
layout: 'fixed'
|
||||
}
|
||||
}
|
||||
|
||||
async setDefaultFilterValues() {
|
||||
const today = DateTime.local();
|
||||
this.filters.setValue('date', today.toISODate());
|
||||
this.run();
|
||||
}
|
||||
|
||||
getRowsForDataTable(data) {
|
||||
return data.rows || [];
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
const columns = [
|
||||
{ label: 'Posting Date', fieldtype: 'Data', fieldname: 'date' },
|
||||
{ label: 'Supplier', fieldtype: 'Data', fieldname: 'party' },
|
||||
{ label: 'Voucher Type', fieldtype: 'Data', fieldname: 'voucherType' },
|
||||
{ label: 'Voucher No', fieldtype: 'Data', fieldname: 'voucherNo' },
|
||||
{ label: 'Due Date', fieldtype: 'Data', fieldname: 'dueDate' },
|
||||
];
|
||||
|
||||
return columns;
|
||||
}
|
||||
}
|
155
reports/AccountsReceivablePayable/AccountsReceivablePayable.js
Normal file
155
reports/AccountsReceivablePayable/AccountsReceivablePayable.js
Normal file
@ -0,0 +1,155 @@
|
||||
const frappe = require('frappejs');
|
||||
|
||||
module.exports = 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' ? 'Invoice' : 'Bill';
|
||||
|
||||
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) {
|
||||
// console.log(entry);
|
||||
|
||||
const { outStandingAmount, creditNoteAmount } = getOutstandingAmount(entry);
|
||||
|
||||
console.log(outStandingAmount);
|
||||
|
||||
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'
|
||||
});
|
||||
}
|
||||
}
|
41
reports/AccountsReceivablePayable/AccountsReceivableView.js
Normal file
41
reports/AccountsReceivablePayable/AccountsReceivableView.js
Normal file
@ -0,0 +1,41 @@
|
||||
const frappe = require('frappejs');
|
||||
const ReportPage = require('frappejs/client/desk/reportpage');
|
||||
const { DateTime } = require('luxon');
|
||||
|
||||
module.exports = class AccountsReceivableView extends ReportPage {
|
||||
constructor(opts) {
|
||||
super({
|
||||
title: frappe._('Accounts Receivable'),
|
||||
filterFields: [
|
||||
{fieldtype: 'Date', fieldname: 'date', label: 'As on Date', required: 1},
|
||||
]
|
||||
});
|
||||
|
||||
this.method = 'accounts-receivable';
|
||||
this.datatableOptions = {
|
||||
layout: 'fixed'
|
||||
}
|
||||
}
|
||||
|
||||
async setDefaultFilterValues() {
|
||||
const today = DateTime.local();
|
||||
this.filters.setValue('date', today.toISODate());
|
||||
this.run();
|
||||
}
|
||||
|
||||
getRowsForDataTable(data) {
|
||||
return data.rows || [];
|
||||
}
|
||||
|
||||
getColumns() {
|
||||
const columns = [
|
||||
{ label: 'Posting Date', fieldtype: 'Data', fieldname: 'date' },
|
||||
{ label: 'Customer', fieldtype: 'Data', fieldname: 'party' },
|
||||
{ label: 'Voucher Type', fieldtype: 'Data', fieldname: 'voucherType' },
|
||||
{ label: 'Voucher No', fieldtype: 'Data', fieldname: 'voucherNo' },
|
||||
{ label: 'Due Date', fieldtype: 'Data', fieldname: 'dueDate' },
|
||||
];
|
||||
|
||||
return columns;
|
||||
}
|
||||
}
|
@ -5,37 +5,52 @@ const BalanceSheet = require('./BalanceSheet/BalanceSheet');
|
||||
const TrialBalance = require('./TrialBalance/TrialBalance');
|
||||
const SalesRegister = require('./SalesRegister/SalesRegister');
|
||||
const PurchaseRegister = require('./PurchaseRegister/PurchaseRegister');
|
||||
const AccountsReceivablePayable = require('./AccountsReceivablePayable/AccountsReceivablePayable');
|
||||
|
||||
// called on server side
|
||||
function registerReportMethods() {
|
||||
frappe.registerMethod({
|
||||
method: 'general-ledger',
|
||||
handler: getReportData(GeneralLedger)
|
||||
const reports = [
|
||||
{
|
||||
method: 'general-ledger',
|
||||
class: GeneralLedger
|
||||
},
|
||||
{
|
||||
method: 'profit-and-loss',
|
||||
class: ProfitAndLoss
|
||||
},
|
||||
{
|
||||
method: 'balance-sheet',
|
||||
class: BalanceSheet
|
||||
},
|
||||
{
|
||||
method: 'trial-balance',
|
||||
class: TrialBalance
|
||||
},
|
||||
{
|
||||
method: 'sales-register',
|
||||
class: SalesRegister
|
||||
},
|
||||
{
|
||||
method: 'purchase-register',
|
||||
class: PurchaseRegister
|
||||
},
|
||||
];
|
||||
|
||||
reports.forEach(report => {
|
||||
frappe.registerMethod({
|
||||
method: report.method,
|
||||
handler: getReportData(report.class)
|
||||
});
|
||||
});
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'profit-and-loss',
|
||||
handler: getReportData(ProfitAndLoss)
|
||||
method: 'accounts-receivable',
|
||||
handler: args => new AccountsReceivablePayable().run('Receivable', args)
|
||||
});
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'balance-sheet',
|
||||
handler: getReportData(BalanceSheet)
|
||||
});
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'trial-balance',
|
||||
handler: getReportData(TrialBalance)
|
||||
});
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'sales-register',
|
||||
handler: getReportData(SalesRegister)
|
||||
});
|
||||
|
||||
frappe.registerMethod({
|
||||
method: 'purchase-register',
|
||||
handler: getReportData(PurchaseRegister)
|
||||
method: 'accounts-payable',
|
||||
handler: args => new AccountsReceivablePayable().run('Payable', args)
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,8 @@ const BalanceSheetView = require('./BalanceSheet/BalanceSheetView');
|
||||
const TrialBalanceView = require('./TrialBalance/TrialBalanceView');
|
||||
const SalesRegisterView = require('./SalesRegister/SalesRegisterView');
|
||||
const PurchaseRegisterView = require('./PurchaseRegister/PurchaseRegisterView');
|
||||
const AccountsReceivableView = require('./AccountsReceivablePayable/AccountsReceivableView');
|
||||
const AccountsPayableView = require('./AccountsReceivablePayable/AccountsPayableView');
|
||||
|
||||
// called on client side
|
||||
function registerReportRoutes() {
|
||||
@ -49,6 +51,20 @@ function registerReportRoutes() {
|
||||
}
|
||||
await frappe.views.PurchaseRegister.show(params);
|
||||
});
|
||||
|
||||
frappe.router.add('report/accounts-receivable', async (params) => {
|
||||
if (!frappe.views.AccountsReceivable) {
|
||||
frappe.views.AccountsReceivable = new AccountsReceivableView();
|
||||
}
|
||||
await frappe.views.AccountsReceivable.show(params);
|
||||
});
|
||||
|
||||
frappe.router.add('report/accounts-payable', async (params) => {
|
||||
if (!frappe.views.AccountsPayable) {
|
||||
frappe.views.AccountsPayable = new AccountsPayableView();
|
||||
}
|
||||
await frappe.views.AccountsPayable.show(params);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = registerReportRoutes;
|
||||
|
Loading…
Reference in New Issue
Block a user