mirror of
https://github.com/frappe/books.git
synced 2025-01-24 15:48:25 +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('Trial Balance', '#report/trial-balance');
|
||||||
frappe.desk.menu.addItem('Sales Register', '#report/sales-register');
|
frappe.desk.menu.addItem('Sales Register', '#report/sales-register');
|
||||||
frappe.desk.menu.addItem('Purchase Register', '#report/purchase-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';
|
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 TrialBalance = require('./TrialBalance/TrialBalance');
|
||||||
const SalesRegister = require('./SalesRegister/SalesRegister');
|
const SalesRegister = require('./SalesRegister/SalesRegister');
|
||||||
const PurchaseRegister = require('./PurchaseRegister/PurchaseRegister');
|
const PurchaseRegister = require('./PurchaseRegister/PurchaseRegister');
|
||||||
|
const AccountsReceivablePayable = require('./AccountsReceivablePayable/AccountsReceivablePayable');
|
||||||
|
|
||||||
// called on server side
|
// called on server side
|
||||||
function registerReportMethods() {
|
function registerReportMethods() {
|
||||||
frappe.registerMethod({
|
const reports = [
|
||||||
|
{
|
||||||
method: 'general-ledger',
|
method: 'general-ledger',
|
||||||
handler: getReportData(GeneralLedger)
|
class: GeneralLedger
|
||||||
});
|
},
|
||||||
|
{
|
||||||
frappe.registerMethod({
|
|
||||||
method: 'profit-and-loss',
|
method: 'profit-and-loss',
|
||||||
handler: getReportData(ProfitAndLoss)
|
class: ProfitAndLoss
|
||||||
});
|
},
|
||||||
|
{
|
||||||
frappe.registerMethod({
|
|
||||||
method: 'balance-sheet',
|
method: 'balance-sheet',
|
||||||
handler: getReportData(BalanceSheet)
|
class: BalanceSheet
|
||||||
});
|
},
|
||||||
|
{
|
||||||
frappe.registerMethod({
|
|
||||||
method: 'trial-balance',
|
method: 'trial-balance',
|
||||||
handler: getReportData(TrialBalance)
|
class: TrialBalance
|
||||||
});
|
},
|
||||||
|
{
|
||||||
frappe.registerMethod({
|
|
||||||
method: 'sales-register',
|
method: 'sales-register',
|
||||||
handler: getReportData(SalesRegister)
|
class: SalesRegister
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'purchase-register',
|
||||||
|
class: PurchaseRegister
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
reports.forEach(report => {
|
||||||
|
frappe.registerMethod({
|
||||||
|
method: report.method,
|
||||||
|
handler: getReportData(report.class)
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
frappe.registerMethod({
|
frappe.registerMethod({
|
||||||
method: 'purchase-register',
|
method: 'accounts-receivable',
|
||||||
handler: getReportData(PurchaseRegister)
|
handler: args => new AccountsReceivablePayable().run('Receivable', args)
|
||||||
|
});
|
||||||
|
|
||||||
|
frappe.registerMethod({
|
||||||
|
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 TrialBalanceView = require('./TrialBalance/TrialBalanceView');
|
||||||
const SalesRegisterView = require('./SalesRegister/SalesRegisterView');
|
const SalesRegisterView = require('./SalesRegister/SalesRegisterView');
|
||||||
const PurchaseRegisterView = require('./PurchaseRegister/PurchaseRegisterView');
|
const PurchaseRegisterView = require('./PurchaseRegister/PurchaseRegisterView');
|
||||||
|
const AccountsReceivableView = require('./AccountsReceivablePayable/AccountsReceivableView');
|
||||||
|
const AccountsPayableView = require('./AccountsReceivablePayable/AccountsPayableView');
|
||||||
|
|
||||||
// called on client side
|
// called on client side
|
||||||
function registerReportRoutes() {
|
function registerReportRoutes() {
|
||||||
@ -49,6 +51,20 @@ function registerReportRoutes() {
|
|||||||
}
|
}
|
||||||
await frappe.views.PurchaseRegister.show(params);
|
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;
|
module.exports = registerReportRoutes;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user