mirror of
https://github.com/frappe/books.git
synced 2024-12-22 19:09:01 +00:00
incr: remove unused reports/**
- get profit and loss to display
This commit is contained in:
parent
e9226f38c8
commit
07c923c6b1
@ -75,10 +75,50 @@ export class BespokeQueries {
|
|||||||
outflow: 'credit',
|
outflow: 'credit',
|
||||||
})
|
})
|
||||||
.select({
|
.select({
|
||||||
'month-year': dateAsMonthYear,
|
yearmonth: dateAsMonthYear,
|
||||||
})
|
})
|
||||||
.where('account', 'in', cashAndBankAccounts)
|
.where('account', 'in', cashAndBankAccounts)
|
||||||
.whereBetween('date', [fromDate, toDate])
|
.whereBetween('date', [fromDate, toDate])
|
||||||
.groupBy(dateAsMonthYear);
|
.groupBy(dateAsMonthYear);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async getIncomeAndExpenses(
|
||||||
|
db: DatabaseCore,
|
||||||
|
fromDate: string,
|
||||||
|
toDate: string
|
||||||
|
) {
|
||||||
|
const income = await db.knex!.raw(
|
||||||
|
`
|
||||||
|
select sum(credit - debit) as balance, strftime('%Y-%m', date) as yearmonth
|
||||||
|
from AccountingLedgerEntry
|
||||||
|
where
|
||||||
|
reverted = false and
|
||||||
|
date between date(?) and date(?) and
|
||||||
|
account in (
|
||||||
|
select name
|
||||||
|
from Account
|
||||||
|
where rootType = 'Income'
|
||||||
|
)
|
||||||
|
group by yearmonth`,
|
||||||
|
[fromDate, toDate]
|
||||||
|
);
|
||||||
|
|
||||||
|
const expense = await db.knex!.raw(
|
||||||
|
`
|
||||||
|
select sum(debit - credit) as balance, strftime('%Y-%m', date) as yearmonth
|
||||||
|
from AccountingLedgerEntry
|
||||||
|
where
|
||||||
|
reverted = false and
|
||||||
|
date between date(?) and date(?) and
|
||||||
|
account in (
|
||||||
|
select name
|
||||||
|
from Account
|
||||||
|
where rootType = 'Expense'
|
||||||
|
)
|
||||||
|
group by yearmonth`,
|
||||||
|
[fromDate, toDate]
|
||||||
|
);
|
||||||
|
|
||||||
|
return { income, expense };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,9 @@ import {
|
|||||||
// Return types of Bespoke Queries
|
// Return types of Bespoke Queries
|
||||||
type TopExpenses = { account: string; total: number }[];
|
type TopExpenses = { account: string; total: number }[];
|
||||||
type TotalOutstanding = { total: number; outstanding: number };
|
type TotalOutstanding = { total: number; outstanding: number };
|
||||||
type Cashflow = { inflow: number; outflow: number; 'month-year': string }[];
|
type Cashflow = { inflow: number; outflow: number; yearmonth: string }[];
|
||||||
|
type Balance = { balance: number; yearmonth: string }[];
|
||||||
|
type IncomeExpense = { income: Balance; expense: Balance };
|
||||||
|
|
||||||
export class DatabaseHandler extends DatabaseBase {
|
export class DatabaseHandler extends DatabaseBase {
|
||||||
#fyo: Fyo;
|
#fyo: Fyo;
|
||||||
@ -252,6 +254,17 @@ export class DatabaseHandler extends DatabaseBase {
|
|||||||
)) as Cashflow;
|
)) as Cashflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getIncomeAndExpenses(
|
||||||
|
fromDate: string,
|
||||||
|
toDate: string
|
||||||
|
): Promise<IncomeExpense> {
|
||||||
|
return (await this.#demux.callBespoke(
|
||||||
|
'getIncomeAndExpenses',
|
||||||
|
fromDate,
|
||||||
|
toDate
|
||||||
|
)) as IncomeExpense;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal methods
|
* Internal methods
|
||||||
*/
|
*/
|
||||||
|
@ -1,170 +0,0 @@
|
|||||||
import fyo from 'fyo';
|
|
||||||
|
|
||||||
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 fyo.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 fyo.db.getAll({
|
|
||||||
doctype: 'Party',
|
|
||||||
filters: {
|
|
||||||
[partyType]: 1,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
).map((d) => d.name);
|
|
||||||
|
|
||||||
return await fyo.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',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
import { fyo } from 'src/initFyo'
|
|
||||||
|
|
||||||
class BankReconciliation {
|
|
||||||
async run(params) {
|
|
||||||
if (!Object.keys(params).length) return [];
|
|
||||||
|
|
||||||
const filters = {};
|
|
||||||
if (params.paymentAccount) filters.paymentAccount = params.paymentAccount;
|
|
||||||
if (params.party) filters.party = params.party;
|
|
||||||
if (params.toDate || params.fromDate) {
|
|
||||||
filters.date = [];
|
|
||||||
if (params.toDate) filters.date.push('<=', params.toDate);
|
|
||||||
if (params.fromDate) filters.date.push('>=', params.fromDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
filters.paymentMethod = ['in', ['Cheque', 'Transfer']];
|
|
||||||
|
|
||||||
let data = await fyo.db.getAll({
|
|
||||||
doctype: 'Payment',
|
|
||||||
fields: [
|
|
||||||
'date',
|
|
||||||
'account',
|
|
||||||
'paymentAccount',
|
|
||||||
'party',
|
|
||||||
'name',
|
|
||||||
'referenceDate',
|
|
||||||
'referenceId',
|
|
||||||
'clearanceDate',
|
|
||||||
],
|
|
||||||
filters: filters,
|
|
||||||
});
|
|
||||||
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
|
||||||
let ledger = await fyo.db.getAll({
|
|
||||||
doctype: 'AccountingLedgerEntry',
|
|
||||||
fields: ['date', 'referenceType', 'referenceName', 'debit', 'credit'],
|
|
||||||
filters: {
|
|
||||||
referenceType: 'Payment',
|
|
||||||
account: data[i].paymentAccount,
|
|
||||||
referenceName: data[i].name,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
data[i].credit = ledger[0].credit;
|
|
||||||
data[i].debit = ledger[0].debit;
|
|
||||||
data[i].debit = ledger[0].debit;
|
|
||||||
data[i].referenceName = ledger[0].referenceName;
|
|
||||||
data[i].referenceType = ledger[0].referenceType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BankReconciliation;
|
|
@ -1,89 +0,0 @@
|
|||||||
import csv2json from 'csvjson-csv2json';
|
|
||||||
import { fyo } from 'src/initFyo';
|
|
||||||
import ReconciliationValidation from '../../src/components/ReconciliationValidation';
|
|
||||||
|
|
||||||
export const fileImportHandler = (file, report) => {
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = () => {
|
|
||||||
const csv = reader.result;
|
|
||||||
const json = csvToJsonHandler(csv);
|
|
||||||
findMatchingReferences(json, report);
|
|
||||||
};
|
|
||||||
reader.readAsBinaryString(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const csvToJsonHandler = (csv) => {
|
|
||||||
const json = csv2json(csv, { parseNumbers: true });
|
|
||||||
return json;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const findMatchingReferences = async (json, report) => {
|
|
||||||
const referenceField = Object.keys(json[0]).filter((field) => {
|
|
||||||
return field.toLowerCase().indexOf('ref') > -1 ? true : false;
|
|
||||||
});
|
|
||||||
const clearanceDateField = Object.keys(json[0]).filter((field) => {
|
|
||||||
return field.toLowerCase().indexOf('date') > -1 ? true : false;
|
|
||||||
});
|
|
||||||
const debitField = Object.keys(json[0]).filter((field) => {
|
|
||||||
return field.toLowerCase().indexOf('debit') > -1 ||
|
|
||||||
field.toLowerCase().indexOf('deposit') > -1
|
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
});
|
|
||||||
const creditField = Object.keys(json[0]).filter((field) => {
|
|
||||||
return field.toLowerCase().indexOf('credit') > -1 ||
|
|
||||||
field.toLowerCase().indexOf('withdraw') > -1
|
|
||||||
? true
|
|
||||||
: false;
|
|
||||||
});
|
|
||||||
const balanceField = Object.keys(json[0]).filter((field) => {
|
|
||||||
return field.toLowerCase().indexOf('balance') > -1 ? true : false;
|
|
||||||
});
|
|
||||||
const references = json.map((row) => {
|
|
||||||
return row[referenceField];
|
|
||||||
});
|
|
||||||
const payments = await fyo.db.getAll({
|
|
||||||
doctype: 'Payment',
|
|
||||||
fields: ['*'],
|
|
||||||
filters: {
|
|
||||||
referenceId: ['in', references],
|
|
||||||
paymentAccount: report.currentFilters.paymentAccount,
|
|
||||||
clearanceDate: ['in', [null, undefined, '']],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (payments.length) {
|
|
||||||
const entries = payments.map((payment) => {
|
|
||||||
const jsonEntry = json.filter((row) => {
|
|
||||||
return row[referenceField] === payment.referenceId;
|
|
||||||
});
|
|
||||||
return Object.assign(payment, jsonEntry[0]);
|
|
||||||
});
|
|
||||||
const normalizedEntries = entries.map((entry) => {
|
|
||||||
return {
|
|
||||||
'Posting Date': fyo.format(entry.date, 'Date'),
|
|
||||||
'Payment Entry': entry.name,
|
|
||||||
'Ref/Cheq. ID': entry[referenceField],
|
|
||||||
'Cr/Dr':
|
|
||||||
fyo.parseNumber(entry[debitField]) > 0
|
|
||||||
? entry[debitField] + ' Dr.'
|
|
||||||
: entry[creditField] + ' Cr.',
|
|
||||||
'Clearance Date': entry[clearanceDateField],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
report.$modal.show({
|
|
||||||
modalProps: {
|
|
||||||
title: `Validate Matching Entries`,
|
|
||||||
noFooter: true,
|
|
||||||
},
|
|
||||||
component: ReconciliationValidation,
|
|
||||||
props: {
|
|
||||||
entries: normalizedEntries,
|
|
||||||
afterReconcile: async () => {
|
|
||||||
await report.getReportData(report.currentFilters);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 'No entries found with matching Ref / Cheque ID'
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,122 +0,0 @@
|
|||||||
const title = 'Bank Reconciliation';
|
|
||||||
import { t } from 'fyo';
|
|
||||||
import ImportWizard from '../../src/components/ImportWizart';
|
|
||||||
import BankReconciliationImport from './BankReconciliationImport';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: title,
|
|
||||||
method: 'bank-reconciliation',
|
|
||||||
filterFields: [
|
|
||||||
{
|
|
||||||
fieldtype: 'Link',
|
|
||||||
target: 'Account',
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`Payment Account`,
|
|
||||||
label: t`Payment Account`,
|
|
||||||
fieldname: 'paymentAccount',
|
|
||||||
getFilters: () => {
|
|
||||||
return {
|
|
||||||
accountType: 'Bank',
|
|
||||||
isGroup: 0,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: 'Link',
|
|
||||||
target: 'Party',
|
|
||||||
size: 'small',
|
|
||||||
label: t`Party`,
|
|
||||||
placeholder: t`Party`,
|
|
||||||
fieldname: 'party',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: 'Date',
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`From Date`,
|
|
||||||
label: t`From Date`,
|
|
||||||
fieldname: 'fromDate',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: 'Date',
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`To Date`,
|
|
||||||
label: t`To Date`,
|
|
||||||
fieldname: 'toDate',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
label: t`Reconcile`,
|
|
||||||
type: 'secondary',
|
|
||||||
condition: (report) => report.currentFilters.paymentAccount,
|
|
||||||
action: async (report) => {
|
|
||||||
report.$modal.show({
|
|
||||||
modalProps: {
|
|
||||||
title: `Import Bank Account Statement`,
|
|
||||||
noFooter: true,
|
|
||||||
},
|
|
||||||
component: ImportWizard,
|
|
||||||
props: {
|
|
||||||
importHandler: BankReconciliationImport.fileImportHandler,
|
|
||||||
report,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
getColumns() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: t`Posting Date`,
|
|
||||||
fieldtype: 'Date',
|
|
||||||
fieldname: 'date',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Payment Account`,
|
|
||||||
fieldtype: 'Link',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Debit`,
|
|
||||||
fieldtype: 'Currency',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Credit`,
|
|
||||||
fieldtype: 'Currency',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Balance`,
|
|
||||||
fieldtype: 'Currency',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Ref/Cheque ID`,
|
|
||||||
fieldtype: 'Data',
|
|
||||||
fieldname: 'referenceId',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Clearance Date`,
|
|
||||||
fieldtype: 'Date',
|
|
||||||
fieldname: 'clearanceDate',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Ref. Type`,
|
|
||||||
fieldtype: 'Data',
|
|
||||||
fieldname: 'referenceType',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Ref. Name`,
|
|
||||||
fieldtype: 'Data',
|
|
||||||
fieldname: 'referenceName',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t`Ref. Date`,
|
|
||||||
fieldtype: 'Date',
|
|
||||||
fieldname: 'referenceDate',
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
label: t`Party`,
|
|
||||||
fieldtype: 'Link',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,426 +0,0 @@
|
|||||||
import { Fyo } from 'fyo';
|
|
||||||
import { DocValueMap } from 'fyo/core/types';
|
|
||||||
import { DateTime } from 'luxon';
|
|
||||||
import { AccountRootType } from 'models/baseModels/Account/types';
|
|
||||||
import Money from 'pesa/dist/types/src/money';
|
|
||||||
import {
|
|
||||||
BalanceType,
|
|
||||||
FinancialStatementOptions,
|
|
||||||
Periodicity,
|
|
||||||
} from 'reports/types';
|
|
||||||
import { convertPesaValuesToFloat } from 'src/utils';
|
|
||||||
|
|
||||||
interface FiscalYear {
|
|
||||||
start: string;
|
|
||||||
end: string;
|
|
||||||
quarters: number[];
|
|
||||||
isSplit: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AccountInfo extends DocValueMap {
|
|
||||||
name: string;
|
|
||||||
parentAccount: string;
|
|
||||||
isGroup: boolean;
|
|
||||||
account?: string;
|
|
||||||
indent?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface LedgerInfo extends DocValueMap {
|
|
||||||
account: string;
|
|
||||||
debit: Money;
|
|
||||||
credit: Money;
|
|
||||||
date: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FinancialStatements {
|
|
||||||
fyo: Fyo;
|
|
||||||
constructor(fyo: Fyo) {
|
|
||||||
this.fyo = fyo;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getData(options: FinancialStatementOptions) {
|
|
||||||
const rootType = options.rootType;
|
|
||||||
const balanceMustBe = options.balanceMustBe ?? 'Debit';
|
|
||||||
const fromDate = options.fromDate;
|
|
||||||
const toDate = options.toDate;
|
|
||||||
const periodicity = options.periodicity ?? 'Monthly';
|
|
||||||
const accumulateValues = options.accumulateValues ?? false;
|
|
||||||
|
|
||||||
const accounts = await this.getAccounts(rootType);
|
|
||||||
const fiscalYear = await getFiscalYear(this.fyo);
|
|
||||||
const ledgerEntries = await this.getLedgerEntries(
|
|
||||||
fromDate,
|
|
||||||
toDate,
|
|
||||||
accounts
|
|
||||||
);
|
|
||||||
const periodList = getPeriodList(fromDate, toDate, periodicity, fiscalYear);
|
|
||||||
this.setPeriodAmounts(
|
|
||||||
accounts,
|
|
||||||
ledgerEntries,
|
|
||||||
periodicity,
|
|
||||||
fiscalYear,
|
|
||||||
balanceMustBe
|
|
||||||
);
|
|
||||||
|
|
||||||
if (accumulateValues) {
|
|
||||||
this.accumulateValues(accounts, periodList);
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalRow = this.getTotalRow(
|
|
||||||
rootType,
|
|
||||||
balanceMustBe,
|
|
||||||
periodList,
|
|
||||||
accounts
|
|
||||||
);
|
|
||||||
accounts.forEach(convertPesaValuesToFloat);
|
|
||||||
|
|
||||||
return { accounts, totalRow, periodList };
|
|
||||||
}
|
|
||||||
|
|
||||||
setPeriodAmounts(
|
|
||||||
accounts: AccountInfo[],
|
|
||||||
ledgerEntries: LedgerInfo[],
|
|
||||||
periodicity: Periodicity,
|
|
||||||
fiscalYear: FiscalYear,
|
|
||||||
balanceMustBe: BalanceType
|
|
||||||
) {
|
|
||||||
for (const account of accounts) {
|
|
||||||
const entries = ledgerEntries.filter(
|
|
||||||
(entry) => entry.account === account.name
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const entry of entries) {
|
|
||||||
const periodKey = getPeriodKey(entry.date, periodicity, fiscalYear);
|
|
||||||
|
|
||||||
if (account[periodKey] === undefined) {
|
|
||||||
account[periodKey] = this.fyo.pesa(0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const multiplier = balanceMustBe === 'Debit' ? 1 : -1;
|
|
||||||
const value = entry.debit.sub(entry.credit).mul(multiplier);
|
|
||||||
account[periodKey] = value.add(account[periodKey] as Money);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getTotalRow(
|
|
||||||
rootType: AccountRootType,
|
|
||||||
balanceMustBe: BalanceType,
|
|
||||||
periodList: string[],
|
|
||||||
accounts: AccountInfo[]
|
|
||||||
) {
|
|
||||||
const totalRow: DocValueMap = {
|
|
||||||
account: `Total ${rootType} (${balanceMustBe})`,
|
|
||||||
};
|
|
||||||
|
|
||||||
periodList.forEach((periodKey) => {
|
|
||||||
if (totalRow[periodKey] === undefined) {
|
|
||||||
totalRow[periodKey] = this.fyo.pesa(0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const account of accounts) {
|
|
||||||
totalRow[periodKey] = (totalRow[periodKey] as Money).add(
|
|
||||||
(account[periodKey] as Money) ?? 0.0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
convertPesaValuesToFloat(totalRow);
|
|
||||||
return totalRow;
|
|
||||||
}
|
|
||||||
|
|
||||||
async accumulateValues(accounts: AccountInfo[], periodList: string[]) {
|
|
||||||
periodList.forEach((periodKey, i) => {
|
|
||||||
if (i === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const previousPeriodKey = periodList[i - 1];
|
|
||||||
|
|
||||||
for (const account of accounts) {
|
|
||||||
if (!account[periodKey]) {
|
|
||||||
account[periodKey] = this.fyo.pesa(0.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
account[periodKey] = (account[periodKey] as Money).add(
|
|
||||||
(account[previousPeriodKey] as Money | undefined) ?? 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAccounts(rootType: AccountRootType) {
|
|
||||||
let accounts = (await this.fyo.db.getAll('Account', {
|
|
||||||
fields: ['name', 'parentAccount', 'isGroup'],
|
|
||||||
filters: {
|
|
||||||
rootType,
|
|
||||||
},
|
|
||||||
})) as AccountInfo[];
|
|
||||||
|
|
||||||
accounts = setIndentLevel(accounts);
|
|
||||||
accounts = sortAccounts(accounts);
|
|
||||||
|
|
||||||
accounts.forEach((account) => {
|
|
||||||
account.account = account.name;
|
|
||||||
});
|
|
||||||
|
|
||||||
return accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getLedgerEntries(
|
|
||||||
fromDate: string | null,
|
|
||||||
toDate: string,
|
|
||||||
accounts: AccountInfo[]
|
|
||||||
) {
|
|
||||||
const accountFilter = ['in', accounts.map((d) => d.name)];
|
|
||||||
let dateFilter: string[] = ['<=', toDate];
|
|
||||||
if (fromDate) {
|
|
||||||
dateFilter = ['>=', fromDate, '<=', toDate];
|
|
||||||
}
|
|
||||||
|
|
||||||
const ledgerEntries = (await this.fyo.db.getAll('AccountingLedgerEntry', {
|
|
||||||
fields: ['account', 'debit', 'credit', 'date'],
|
|
||||||
filters: {
|
|
||||||
account: accountFilter,
|
|
||||||
date: dateFilter,
|
|
||||||
},
|
|
||||||
})) as LedgerInfo[];
|
|
||||||
|
|
||||||
return ledgerEntries;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getTrialBalance(options: FinancialStatementOptions) {
|
|
||||||
const { rootType, fromDate, toDate } = options;
|
|
||||||
const accounts = await this.getAccounts(rootType);
|
|
||||||
const ledgerEntries = await this.getLedgerEntries(null, toDate, accounts);
|
|
||||||
|
|
||||||
for (const account of accounts) {
|
|
||||||
const accountEntries = ledgerEntries.filter(
|
|
||||||
(entry) => entry.account === account.name
|
|
||||||
);
|
|
||||||
// opening
|
|
||||||
const beforePeriodEntries = accountEntries.filter(
|
|
||||||
(entry) => entry.date < fromDate
|
|
||||||
);
|
|
||||||
|
|
||||||
account.opening = beforePeriodEntries.reduce(
|
|
||||||
(acc, entry) => acc.add(entry.debit).sub(entry.credit),
|
|
||||||
this.fyo.pesa(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (account.opening.gte(0)) {
|
|
||||||
account.openingDebit = account.opening;
|
|
||||||
account.openingCredit = this.fyo.pesa(0);
|
|
||||||
} else {
|
|
||||||
account.openingCredit = account.opening.neg();
|
|
||||||
account.openingDebit = this.fyo.pesa(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// debit / credit
|
|
||||||
const periodEntries = accountEntries.filter(
|
|
||||||
(entry) => entry.date >= fromDate && entry.date < toDate
|
|
||||||
);
|
|
||||||
account.debit = periodEntries.reduce(
|
|
||||||
(acc, entry) => acc.add(entry.debit),
|
|
||||||
this.fyo.pesa(0)
|
|
||||||
);
|
|
||||||
account.credit = periodEntries.reduce(
|
|
||||||
(acc, entry) => acc.add(entry.credit),
|
|
||||||
this.fyo.pesa(0)
|
|
||||||
);
|
|
||||||
|
|
||||||
// closing
|
|
||||||
account.closing = account.opening.add(account.debit).sub(account.credit);
|
|
||||||
|
|
||||||
if (account.closing.gte(0)) {
|
|
||||||
account.closingDebit = account.closing;
|
|
||||||
account.closingCredit = this.fyo.pesa(0);
|
|
||||||
} else {
|
|
||||||
account.closingCredit = account.closing.neg();
|
|
||||||
account.closingDebit = this.fyo.pesa(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (account.debit.neq(0) || account.credit.neq(0)) {
|
|
||||||
setParentEntry(account, account.parentAccount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setParentEntry(leafAccount: AccountInfo, parentName: string) {
|
|
||||||
for (const acc of accounts) {
|
|
||||||
if (acc.name === parentName) {
|
|
||||||
acc.debit = (acc.debit as Money).add(leafAccount.debit as Money);
|
|
||||||
acc.credit = (acc.credit as Money).add(leafAccount.credit as Money);
|
|
||||||
acc.closing = (acc.opening as Money).add(acc.debit).sub(acc.credit);
|
|
||||||
|
|
||||||
if (acc.closing.gte(0)) {
|
|
||||||
acc.closingDebit = acc.closing;
|
|
||||||
} else {
|
|
||||||
acc.closingCredit = acc.closing.neg();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (acc.parentAccount) {
|
|
||||||
setParentEntry(leafAccount, acc.parentAccount);
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
accounts.forEach(convertPesaValuesToFloat);
|
|
||||||
return accounts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setIndentLevel(
|
|
||||||
accounts: AccountInfo[],
|
|
||||||
parentAccount?: string | null,
|
|
||||||
level?: number
|
|
||||||
): AccountInfo[] {
|
|
||||||
if (parentAccount === undefined) {
|
|
||||||
parentAccount = null;
|
|
||||||
level = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
accounts.forEach((account) => {
|
|
||||||
if (
|
|
||||||
account.parentAccount === parentAccount &&
|
|
||||||
account.indent === undefined
|
|
||||||
) {
|
|
||||||
account.indent = level;
|
|
||||||
setIndentLevel(accounts, account.name, (level ?? 0) + 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return accounts;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortAccounts(accounts: AccountInfo[]) {
|
|
||||||
const out: AccountInfo[] = [];
|
|
||||||
const pushed: Record<string, boolean> = {};
|
|
||||||
|
|
||||||
pushToOut(null);
|
|
||||||
|
|
||||||
function pushToOut(parentAccount: string | null) {
|
|
||||||
accounts.forEach((account) => {
|
|
||||||
if (pushed[account.name] && account.parentAccount !== parentAccount) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
out.push(account);
|
|
||||||
pushed[account.name] = true;
|
|
||||||
|
|
||||||
pushToOut(account.name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPeriodList(
|
|
||||||
fromDate: string,
|
|
||||||
toDate: string,
|
|
||||||
periodicity: Periodicity,
|
|
||||||
fiscalYear: FiscalYear
|
|
||||||
) {
|
|
||||||
if (!fromDate) {
|
|
||||||
fromDate = fiscalYear.start;
|
|
||||||
}
|
|
||||||
|
|
||||||
const monthsToAdd = {
|
|
||||||
Monthly: 1,
|
|
||||||
Quarterly: 3,
|
|
||||||
'Half Yearly': 6,
|
|
||||||
Yearly: 12,
|
|
||||||
}[periodicity];
|
|
||||||
|
|
||||||
const startDate = DateTime.fromISO(fromDate).startOf('month');
|
|
||||||
const endDate = DateTime.fromISO(toDate).endOf('month');
|
|
||||||
let curDate = startDate;
|
|
||||||
const periodKeyList: string[] = [];
|
|
||||||
|
|
||||||
while (curDate <= endDate) {
|
|
||||||
const periodKey = getPeriodKey(curDate, periodicity, fiscalYear);
|
|
||||||
periodKeyList.push(periodKey);
|
|
||||||
curDate = curDate.plus({ months: monthsToAdd });
|
|
||||||
}
|
|
||||||
|
|
||||||
return periodKeyList;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPeriodKey(
|
|
||||||
dateObj: DateTime | string,
|
|
||||||
periodicity: Periodicity,
|
|
||||||
fiscalYear: FiscalYear
|
|
||||||
) {
|
|
||||||
if (typeof dateObj === 'string') {
|
|
||||||
dateObj = DateTime.fromISO(dateObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { start, quarters, isSplit } = fiscalYear;
|
|
||||||
const { month, year } = dateObj;
|
|
||||||
const fisacalStart = DateTime.fromISO(start);
|
|
||||||
|
|
||||||
if (periodicity === 'Monthly') {
|
|
||||||
return `${dateObj.monthShort} ${year}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (periodicity === 'Quarterly') {
|
|
||||||
const key =
|
|
||||||
month < fisacalStart.month
|
|
||||||
? `${year - 1} - ${year}`
|
|
||||||
: `${year} - ${year + 1}`;
|
|
||||||
const strYear = isSplit ? key : `${year}`;
|
|
||||||
return {
|
|
||||||
1: `Q1 ${strYear}`,
|
|
||||||
2: `Q2 ${strYear}`,
|
|
||||||
3: `Q3 ${strYear}`,
|
|
||||||
4: `Q4 ${strYear}`,
|
|
||||||
}[quarters[month - 1]] as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (periodicity === 'Half Yearly') {
|
|
||||||
const key =
|
|
||||||
month < fisacalStart.month
|
|
||||||
? `${year - 1} - ${year}`
|
|
||||||
: `${year} - ${year + 1}`;
|
|
||||||
const strYear = isSplit ? key : `${year}`;
|
|
||||||
return {
|
|
||||||
1: `1st Half ${strYear}`,
|
|
||||||
2: `1st Half ${strYear}`,
|
|
||||||
3: `2nd Half ${strYear}`,
|
|
||||||
4: `2nd Half ${strYear}`,
|
|
||||||
}[quarters[month - 1]] as string;
|
|
||||||
}
|
|
||||||
|
|
||||||
const key =
|
|
||||||
month < fisacalStart.month
|
|
||||||
? `${year - 1} - ${year}`
|
|
||||||
: `${year} - ${year + 1}`;
|
|
||||||
const strYear = isSplit ? key : `${year}`;
|
|
||||||
return `FY ${strYear}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getFiscalYear(fyo: Fyo): Promise<FiscalYear> {
|
|
||||||
const accountingSettings = await fyo.doc.getSingle('AccountingSettings');
|
|
||||||
|
|
||||||
const fiscalYearStart = accountingSettings.fiscalYearStart as string;
|
|
||||||
const fiscalYearEnd = accountingSettings.fiscalYearEnd as string;
|
|
||||||
|
|
||||||
//right now quaters received from luxon lib is fixed to Jan as starting quarter
|
|
||||||
//moving the financial quarters, according to of start of fiscal year month
|
|
||||||
const quarters = [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4];
|
|
||||||
const start = DateTime.fromISO(fiscalYearStart);
|
|
||||||
quarters.unshift(...quarters.splice(13 - start.month, 11));
|
|
||||||
|
|
||||||
//check if fiscal year ends in next year
|
|
||||||
const end = DateTime.fromISO(fiscalYearEnd);
|
|
||||||
const isFiscalSplit = start.year - end.year;
|
|
||||||
|
|
||||||
return {
|
|
||||||
start: fiscalYearStart,
|
|
||||||
end: fiscalYearEnd,
|
|
||||||
quarters: quarters,
|
|
||||||
isSplit: isFiscalSplit,
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
import { fyo } from 'src/initFyo';
|
|
||||||
|
|
||||||
class PurchaseRegister {
|
|
||||||
async run({ fromDate, toDate, supplier }) {
|
|
||||||
if (!fromDate && !toDate && !supplier) return { rows: [] };
|
|
||||||
|
|
||||||
let filters = {};
|
|
||||||
if (supplier) {
|
|
||||||
filters.supplier = supplier;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fromDate && toDate) {
|
|
||||||
filters.date = ['>=', fromDate, '<=', toDate];
|
|
||||||
} else if (fromDate) {
|
|
||||||
filters.date = ['>=', fromDate];
|
|
||||||
} else if (toDate) {
|
|
||||||
filters.date = ['<=', toDate];
|
|
||||||
}
|
|
||||||
filters.submitted = 1;
|
|
||||||
|
|
||||||
const bills = await fyo.db.getAll({
|
|
||||||
doctype: 'PurchaseInvoice',
|
|
||||||
fields: ['name', 'date', 'supplier', 'account', 'netTotal', 'grandTotal'],
|
|
||||||
filters,
|
|
||||||
orderBy: 'date',
|
|
||||||
order: 'desc',
|
|
||||||
});
|
|
||||||
|
|
||||||
const billNames = bills.map((d) => d.name);
|
|
||||||
|
|
||||||
const taxes = await fyo.db.getAll({
|
|
||||||
doctype: 'TaxSummary',
|
|
||||||
fields: ['parent', 'amount'],
|
|
||||||
filters: {
|
|
||||||
parenttype: 'PurchaseInvoice',
|
|
||||||
parent: ['in', billNames],
|
|
||||||
},
|
|
||||||
orderBy: 'name',
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let bill of bills) {
|
|
||||||
bill.totalTax = taxes
|
|
||||||
.filter((tax) => tax.parent === bill.name)
|
|
||||||
.reduce((acc, tax) => {
|
|
||||||
if (tax.amount) {
|
|
||||||
acc = acc + tax.amount;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { rows: bills };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PurchaseRegister;
|
|
@ -1,56 +0,0 @@
|
|||||||
const title = 'Purchase Register';
|
|
||||||
import { t } from 'fyo';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: title,
|
|
||||||
method: 'purchase-register',
|
|
||||||
filterFields: [
|
|
||||||
{
|
|
||||||
fieldtype: 'Link',
|
|
||||||
target: 'Party',
|
|
||||||
label: t`Supplier Name`,
|
|
||||||
fieldname: 'supplier',
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`Supplier Name`,
|
|
||||||
getFilters: (query) => {
|
|
||||||
if (query)
|
|
||||||
return {
|
|
||||||
keywords: ['like', query],
|
|
||||||
supplier: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
supplier: 1,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: 'Date',
|
|
||||||
fieldname: 'fromDate',
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`From Date`,
|
|
||||||
label: t`From Date`,
|
|
||||||
required: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: 'Date',
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`To Date`,
|
|
||||||
fieldname: 'toDate',
|
|
||||||
label: t`To Date`,
|
|
||||||
required: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
actions: [],
|
|
||||||
getColumns() {
|
|
||||||
return [
|
|
||||||
{ label: t`PurchaseInvoice`, fieldname: 'name' },
|
|
||||||
{ label: t`Posting Date`, fieldname: 'date' },
|
|
||||||
{ label: t`Supplier`, fieldname: 'supplier' },
|
|
||||||
{ label: t`Payable Account`, fieldname: 'account' },
|
|
||||||
{ label: t`Net Total`, fieldname: 'netTotal', fieldtype: 'Currency' },
|
|
||||||
{ label: t`Total Tax`, fieldname: 'totalTax', fieldtype: 'Currency' },
|
|
||||||
{ label: t`Grand Total`, fieldname: 'grandTotal', fieldtype: 'Currency' },
|
|
||||||
];
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,55 +0,0 @@
|
|||||||
import { fyo } from 'src/initFyo';
|
|
||||||
|
|
||||||
class SalesRegister {
|
|
||||||
async run({ fromDate, toDate, customer }) {
|
|
||||||
if (!fromDate && !toDate && !customer) return { rows: [] };
|
|
||||||
|
|
||||||
let filters = {};
|
|
||||||
if (customer) {
|
|
||||||
filters.customer = customer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fromDate && toDate) {
|
|
||||||
filters.date = ['>=', fromDate, '<=', toDate];
|
|
||||||
} else if (fromDate) {
|
|
||||||
filters.date = ['>=', fromDate];
|
|
||||||
} else if (toDate) {
|
|
||||||
filters.date = ['<=', toDate];
|
|
||||||
}
|
|
||||||
|
|
||||||
const invoices = await fyo.db.getAll({
|
|
||||||
doctype: 'SalesInvoice',
|
|
||||||
fields: ['name', 'date', 'customer', 'account', 'netTotal', 'grandTotal'],
|
|
||||||
filters: filters,
|
|
||||||
orderBy: 'date',
|
|
||||||
order: 'desc',
|
|
||||||
});
|
|
||||||
|
|
||||||
const invoiceNames = invoices.map((d) => d.name);
|
|
||||||
|
|
||||||
const taxes = await fyo.db.getAll({
|
|
||||||
doctype: 'TaxSummary',
|
|
||||||
fields: ['parent', 'amount'],
|
|
||||||
filters: {
|
|
||||||
parenttype: 'Invoice',
|
|
||||||
parent: ['in', invoiceNames],
|
|
||||||
},
|
|
||||||
orderBy: 'name',
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let invoice of invoices) {
|
|
||||||
invoice.totalTax = taxes
|
|
||||||
.filter((tax) => tax.parent === invoice.name)
|
|
||||||
.reduce((acc, tax) => {
|
|
||||||
if (tax.amount) {
|
|
||||||
acc = acc + tax.amount;
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { rows: invoices };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default SalesRegister;
|
|
@ -1,56 +0,0 @@
|
|||||||
const title = 'Sales Register';
|
|
||||||
import { t } from 'fyo';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: title,
|
|
||||||
method: 'sales-register',
|
|
||||||
filterFields: [
|
|
||||||
{
|
|
||||||
fieldtype: 'Link',
|
|
||||||
target: 'Party',
|
|
||||||
label: t`Customer Name`,
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`Customer Name`,
|
|
||||||
fieldname: 'customer',
|
|
||||||
getFilters: (query) => {
|
|
||||||
if (query)
|
|
||||||
return {
|
|
||||||
keywords: ['like', query],
|
|
||||||
customer: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
customer: 1,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: 'Date',
|
|
||||||
fieldname: 'fromDate',
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`From Date`,
|
|
||||||
label: t`From Date`,
|
|
||||||
required: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fieldtype: 'Date',
|
|
||||||
size: 'small',
|
|
||||||
placeholder: t`To Date`,
|
|
||||||
fieldname: 'toDate',
|
|
||||||
label: t`To Date`,
|
|
||||||
required: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
actions: [],
|
|
||||||
getColumns() {
|
|
||||||
return [
|
|
||||||
{ label: t`SalesInvoice`, fieldname: 'name' },
|
|
||||||
{ label: t`Posting Date`, fieldname: 'date', fieldtype: 'Date' },
|
|
||||||
{ label: t`Customer`, fieldname: 'customer' },
|
|
||||||
{ label: t`Receivable Account`, fieldname: 'account' },
|
|
||||||
{ label: t`Net Total`, fieldname: 'netTotal', fieldtype: 'Currency' },
|
|
||||||
{ label: t`Total Tax`, fieldname: 'totalTax', fieldtype: 'Currency' },
|
|
||||||
{ label: t`Grand Total`, fieldname: 'grandTotal', fieldtype: 'Currency' },
|
|
||||||
];
|
|
||||||
},
|
|
||||||
};
|
|
@ -1,26 +0,0 @@
|
|||||||
// import BalanceSheetViewConfig from './BalanceSheet/viewConfig';
|
|
||||||
// import GeneralLedgerViewConfig from './GeneralLedger/viewConfig';
|
|
||||||
// import GoodsAndServiceTaxGSTR1View from './GoodsAndServiceTax/GSTR1View';
|
|
||||||
// import GoodsAndServiceTaxGSTR2View from './GoodsAndServiceTax/GSTR2View';
|
|
||||||
// import ProfitAndLossViewConfig from './ProfitAndLoss/viewConfig';
|
|
||||||
// import PurchaseRegisterViewConfig from './PurchaseRegister/viewConfig';
|
|
||||||
// import SalesRegisterViewConfig from './SalesRegister/viewConfig';
|
|
||||||
// import TrialBalanceViewConfig from './TrialBalance/viewConfig';
|
|
||||||
|
|
||||||
// export default {
|
|
||||||
// 'general-ledger': GeneralLedgerViewConfig,
|
|
||||||
// 'sales-register': SalesRegisterViewConfig,
|
|
||||||
// 'purchase-register': PurchaseRegisterViewConfig,
|
|
||||||
// 'balance-sheet': BalanceSheetViewConfig,
|
|
||||||
// 'profit-and-loss': ProfitAndLossViewConfig,
|
|
||||||
// 'trial-balance': TrialBalanceViewConfig,
|
|
||||||
// 'gstr-1': GoodsAndServiceTaxGSTR1View,
|
|
||||||
// 'gstr-2': GoodsAndServiceTaxGSTR2View,
|
|
||||||
// };
|
|
||||||
|
|
||||||
interface ReportView {
|
|
||||||
title: string;
|
|
||||||
method: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {} as Record<string, ReportView>;
|
|
@ -123,7 +123,7 @@
|
|||||||
>
|
>
|
||||||
<div class="flex flex-col justify-center items-center">
|
<div class="flex flex-col justify-center items-center">
|
||||||
<p>
|
<p>
|
||||||
{{ xi > -1 ? xLabels[xi] : '' }}
|
{{ xi > -1 ? formatX(xLabels[xi]) : '' }}
|
||||||
</p>
|
</p>
|
||||||
<p class="font-semibold">
|
<p class="font-semibold">
|
||||||
{{ yi > -1 ? format(points[yi][xi]) : '' }}
|
{{ yi > -1 ? format(points[yi][xi]) : '' }}
|
||||||
@ -133,8 +133,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Tooltip from '../Tooltip.vue';
|
|
||||||
import { prefixFormat } from 'src/utils/chart';
|
import { prefixFormat } from 'src/utils/chart';
|
||||||
|
import Tooltip from '../Tooltip.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
|
@ -80,7 +80,7 @@ export default {
|
|||||||
colors = ['#E9EBED', '#B7BFC6'];
|
colors = ['#E9EBED', '#B7BFC6'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const xLabels = data.map((cf) => cf['month-year']);
|
const xLabels = data.map((cf) => cf['yearmonth']);
|
||||||
const points = ['inflow', 'outflow'].map((k) => data.map((d) => d[k]));
|
const points = ['inflow', 'outflow'].map((k) => data.map((d) => d[k]));
|
||||||
|
|
||||||
const format = (value) => fyo.format(value ?? 0, 'Currency');
|
const format = (value) => fyo.format(value ?? 0, 'Currency');
|
||||||
@ -94,8 +94,8 @@ export default {
|
|||||||
this.period
|
this.period
|
||||||
);
|
);
|
||||||
|
|
||||||
const data = await fyo.db.getCashflow(fromDate, toDate);
|
const data = await fyo.db.getCashflow(fromDate.toISO(), toDate.toISO());
|
||||||
const dataMap = getMapFromList(data, 'month-year');
|
const dataMap = getMapFromList(data, 'yearmonth');
|
||||||
this.data = periodList.map((p) => {
|
this.data = periodList.map((p) => {
|
||||||
const key = p.toFormat('yyyy-MM');
|
const key = p.toFormat('yyyy-MM');
|
||||||
const item = dataMap[key];
|
const item = dataMap[key];
|
||||||
@ -106,7 +106,7 @@ export default {
|
|||||||
return {
|
return {
|
||||||
inflow: 0,
|
inflow: 0,
|
||||||
outflow: 0,
|
outflow: 0,
|
||||||
'month-year': key,
|
yearmonth: key,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -129,22 +129,22 @@ const dummyData = [
|
|||||||
{
|
{
|
||||||
inflow: 100,
|
inflow: 100,
|
||||||
outflow: 250,
|
outflow: 250,
|
||||||
'month-year': '2021-05',
|
yearmonth: '2021-05',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
inflow: 350,
|
inflow: 350,
|
||||||
outflow: 100,
|
outflow: 100,
|
||||||
'month-year': '2021-06',
|
yearmonth: '2021-06',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
inflow: 50,
|
inflow: 50,
|
||||||
outflow: 300,
|
outflow: 300,
|
||||||
'month-year': '2021-07',
|
yearmonth: '2021-07',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
inflow: 320,
|
inflow: 320,
|
||||||
outflow: 100,
|
outflow: 100,
|
||||||
'month-year': '2021-08',
|
yearmonth: '2021-08',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
</script>
|
</script>
|
||||||
|
@ -7,12 +7,12 @@
|
|||||||
<hr class="border-t mt-10" />
|
<hr class="border-t mt-10" />
|
||||||
<UnpaidInvoices class="mt-10" />
|
<UnpaidInvoices class="mt-10" />
|
||||||
<hr class="border-t mt-10" />
|
<hr class="border-t mt-10" />
|
||||||
<!--
|
|
||||||
<div class="flex justify-between mx-auto mt-10 ml-4 mr-4 gap-10">
|
<div class="flex justify-between mx-auto mt-10 ml-4 mr-4 gap-10">
|
||||||
<ProfitAndLoss class="w-1/2" />
|
<ProfitAndLoss class="w-1/2" />
|
||||||
|
<!--
|
||||||
<Expenses class="w-1/2" />
|
<Expenses class="w-1/2" />
|
||||||
</div>
|
|
||||||
-->
|
-->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -21,7 +21,7 @@
|
|||||||
import PageHeader from 'src/components/PageHeader';
|
import PageHeader from 'src/components/PageHeader';
|
||||||
import Cashflow from './Cashflow';
|
import Cashflow from './Cashflow';
|
||||||
// import Expenses from './Expenses';
|
// import Expenses from './Expenses';
|
||||||
// import ProfitAndLoss from './ProfitAndLoss';
|
import ProfitAndLoss from './ProfitAndLoss';
|
||||||
import UnpaidInvoices from './UnpaidInvoices';
|
import UnpaidInvoices from './UnpaidInvoices';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@ -30,8 +30,8 @@ export default {
|
|||||||
PageHeader,
|
PageHeader,
|
||||||
UnpaidInvoices,
|
UnpaidInvoices,
|
||||||
Cashflow,
|
Cashflow,
|
||||||
/*
|
|
||||||
ProfitAndLoss,
|
ProfitAndLoss,
|
||||||
|
/*
|
||||||
Expenses,
|
Expenses,
|
||||||
*/
|
*/
|
||||||
},
|
},
|
||||||
|
@ -28,11 +28,11 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { fyo } from 'src/initFyo';
|
|
||||||
import BarChart from 'src/components/Charts/BarChart.vue';
|
import BarChart from 'src/components/Charts/BarChart.vue';
|
||||||
|
import { fyo } from 'src/initFyo';
|
||||||
import { formatXLabels, getYMax, getYMin } from 'src/utils/chart';
|
import { formatXLabels, getYMax, getYMin } from 'src/utils/chart';
|
||||||
import { getDatesAndPeriodicity } from 'src/utils/misc';
|
import { getDatesAndPeriodList } from 'src/utils/misc';
|
||||||
import ProfitAndLoss from '../../../reports/ProfitAndLoss/ProfitAndLoss';
|
import { getValueMapFromList } from 'utils';
|
||||||
import PeriodSelector from './PeriodSelector';
|
import PeriodSelector from './PeriodSelector';
|
||||||
import SectionHeader from './SectionHeader';
|
import SectionHeader from './SectionHeader';
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ export default {
|
|||||||
data: () => ({
|
data: () => ({
|
||||||
period: 'This Year',
|
period: 'This Year',
|
||||||
data: [],
|
data: [],
|
||||||
periodList: [],
|
hasData: false,
|
||||||
}),
|
}),
|
||||||
activated() {
|
activated() {
|
||||||
this.setData();
|
this.setData();
|
||||||
@ -56,13 +56,13 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
chartData() {
|
chartData() {
|
||||||
const points = [this.periodList.map((p) => this.data[p])];
|
const points = [this.data.map((d) => d.balance)];
|
||||||
const colors = [{ positive: '#2490EF', negative: '#B7BFC6' }];
|
const colors = [{ positive: '#2490EF', negative: '#B7BFC6' }];
|
||||||
const format = (value) => fyo.format(value ?? 0, 'Currency');
|
const format = (value) => fyo.format(value ?? 0, 'Currency');
|
||||||
const yMax = getYMax(points);
|
const yMax = getYMax(points);
|
||||||
const yMin = getYMin(points);
|
const yMin = getYMin(points);
|
||||||
return {
|
return {
|
||||||
xLabels: this.periodList,
|
xLabels: this.data.map((d) => d.yearmonth),
|
||||||
points,
|
points,
|
||||||
format,
|
format,
|
||||||
colors,
|
colors,
|
||||||
@ -71,25 +71,31 @@ export default {
|
|||||||
formatX: formatXLabels,
|
formatX: formatXLabels,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
hasData() {
|
|
||||||
return this.periodList.some((key) => this.data[key] !== 0);
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async setData() {
|
async setData() {
|
||||||
let { fromDate, toDate, periodicity } = await getDatesAndPeriodicity(
|
const { fromDate, toDate, periodList } = await getDatesAndPeriodList(
|
||||||
this.period
|
this.period
|
||||||
);
|
);
|
||||||
|
|
||||||
let pl = new ProfitAndLoss(this.fyo);
|
const data = await fyo.db.getIncomeAndExpenses(
|
||||||
let res = await pl.run({
|
fromDate.toISO(),
|
||||||
fromDate,
|
toDate.toISO()
|
||||||
toDate,
|
);
|
||||||
periodicity,
|
const incomes = getValueMapFromList(data.income, 'yearmonth', 'balance');
|
||||||
});
|
const expenses = getValueMapFromList(
|
||||||
|
data.expense,
|
||||||
|
'yearmonth',
|
||||||
|
'balance'
|
||||||
|
);
|
||||||
|
|
||||||
this.data = res.rows.at(-1);
|
this.data = periodList.map((d) => {
|
||||||
this.periodList = res.columns;
|
const key = d.toFormat('yyyy-MM');
|
||||||
|
const inc = incomes[key] ?? 0;
|
||||||
|
const exp = expenses[key] ?? 0;
|
||||||
|
return { yearmonth: key, balance: inc - exp };
|
||||||
|
});
|
||||||
|
this.hasData = data.income.length > 0 || data.expense.length > 0;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -80,7 +80,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<MouseFollower
|
<MouseFollower
|
||||||
v-if="invoices[0].hasData || invoices[1].hasData"
|
v-if="invoices[0].hasData || invoices[1].hasData"
|
||||||
|
:offset="15"
|
||||||
:show="idx >= 0"
|
:show="idx >= 0"
|
||||||
|
placement="top"
|
||||||
class="text-sm shadow-md px-2 py-1 bg-white text-gray-900 border-l-2"
|
class="text-sm shadow-md px-2 py-1 bg-white text-gray-900 border-l-2"
|
||||||
:style="{ borderColor: colors[idx] }"
|
:style="{ borderColor: colors[idx] }"
|
||||||
>
|
>
|
||||||
@ -169,14 +171,14 @@ export default {
|
|||||||
|
|
||||||
const { total, outstanding } = await fyo.db.getTotalOutstanding(
|
const { total, outstanding } = await fyo.db.getTotalOutstanding(
|
||||||
invoice.schemaName,
|
invoice.schemaName,
|
||||||
fromDate,
|
fromDate.toISO(),
|
||||||
toDate
|
toDate.toISO()
|
||||||
);
|
);
|
||||||
|
|
||||||
const { countTotal, countOutstanding } = await this.getCounts(
|
const { countTotal, countOutstanding } = await this.getCounts(
|
||||||
invoice.schemaName,
|
invoice.schemaName,
|
||||||
DateTime.fromISO(fromDate),
|
fromDate,
|
||||||
DateTime.fromISO(toDate)
|
toDate
|
||||||
);
|
);
|
||||||
|
|
||||||
invoice.total = total ?? 0;
|
invoice.total = total ?? 0;
|
||||||
|
@ -9,7 +9,7 @@ import { fyo } from 'src/initFyo';
|
|||||||
|
|
||||||
export function getDatesAndPeriodList(
|
export function getDatesAndPeriodList(
|
||||||
period: 'This Year' | 'This Quarter' | 'This Month'
|
period: 'This Year' | 'This Quarter' | 'This Month'
|
||||||
): { periodList: DateTime[]; fromDate: string; toDate: string } {
|
): { periodList: DateTime[]; fromDate: DateTime; toDate: DateTime } {
|
||||||
const toDate: DateTime = DateTime.now();
|
const toDate: DateTime = DateTime.now();
|
||||||
let fromDate: DateTime;
|
let fromDate: DateTime;
|
||||||
|
|
||||||
@ -39,8 +39,8 @@ export function getDatesAndPeriodList(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
periodList,
|
periodList,
|
||||||
fromDate: fromDate.toISO(),
|
fromDate,
|
||||||
toDate: toDate.toISO(),
|
toDate,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { t } from 'fyo';
|
import { t } from 'fyo';
|
||||||
import { DocValueMap } from 'fyo/core/types';
|
import { DocValueMap } from 'fyo/core/types';
|
||||||
import { ModelNameEnum } from 'models/types';
|
import { ModelNameEnum } from 'models/types';
|
||||||
import reports from 'reports/view';
|
|
||||||
import { OptionField } from 'schemas/types';
|
import { OptionField } from 'schemas/types';
|
||||||
import { fyo } from 'src/initFyo';
|
import { fyo } from 'src/initFyo';
|
||||||
import { GetAllOptions } from 'utils/db/types';
|
import { GetAllOptions } from 'utils/db/types';
|
||||||
@ -123,13 +122,15 @@ function getCreateList(): SearchItem[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getReportList(): SearchItem[] {
|
function getReportList(): SearchItem[] {
|
||||||
return Object.values(reports).map((report) => {
|
/*return Object.values(reports).map((report) => {
|
||||||
return {
|
return {
|
||||||
label: report.title,
|
label: report.title,
|
||||||
route: `/report/${report.method}`,
|
route: `/report/${report.method}`,
|
||||||
group: 'Report',
|
group: 'Report',
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getListViewList(): SearchItem[] {
|
function getListViewList(): SearchItem[] {
|
||||||
|
Loading…
Reference in New Issue
Block a user