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',
|
||||
})
|
||||
.select({
|
||||
'month-year': dateAsMonthYear,
|
||||
yearmonth: dateAsMonthYear,
|
||||
})
|
||||
.where('account', 'in', cashAndBankAccounts)
|
||||
.whereBetween('date', [fromDate, toDate])
|
||||
.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
|
||||
type TopExpenses = { account: string; total: 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 {
|
||||
#fyo: Fyo;
|
||||
@ -252,6 +254,17 @@ export class DatabaseHandler extends DatabaseBase {
|
||||
)) as Cashflow;
|
||||
}
|
||||
|
||||
async getIncomeAndExpenses(
|
||||
fromDate: string,
|
||||
toDate: string
|
||||
): Promise<IncomeExpense> {
|
||||
return (await this.#demux.callBespoke(
|
||||
'getIncomeAndExpenses',
|
||||
fromDate,
|
||||
toDate
|
||||
)) as IncomeExpense;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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">
|
||||
<p>
|
||||
{{ xi > -1 ? xLabels[xi] : '' }}
|
||||
{{ xi > -1 ? formatX(xLabels[xi]) : '' }}
|
||||
</p>
|
||||
<p class="font-semibold">
|
||||
{{ yi > -1 ? format(points[yi][xi]) : '' }}
|
||||
@ -133,8 +133,8 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Tooltip from '../Tooltip.vue';
|
||||
import { prefixFormat } from 'src/utils/chart';
|
||||
import Tooltip from '../Tooltip.vue';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
@ -80,7 +80,7 @@ export default {
|
||||
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 format = (value) => fyo.format(value ?? 0, 'Currency');
|
||||
@ -94,8 +94,8 @@ export default {
|
||||
this.period
|
||||
);
|
||||
|
||||
const data = await fyo.db.getCashflow(fromDate, toDate);
|
||||
const dataMap = getMapFromList(data, 'month-year');
|
||||
const data = await fyo.db.getCashflow(fromDate.toISO(), toDate.toISO());
|
||||
const dataMap = getMapFromList(data, 'yearmonth');
|
||||
this.data = periodList.map((p) => {
|
||||
const key = p.toFormat('yyyy-MM');
|
||||
const item = dataMap[key];
|
||||
@ -106,7 +106,7 @@ export default {
|
||||
return {
|
||||
inflow: 0,
|
||||
outflow: 0,
|
||||
'month-year': key,
|
||||
yearmonth: key,
|
||||
};
|
||||
});
|
||||
},
|
||||
@ -129,22 +129,22 @@ const dummyData = [
|
||||
{
|
||||
inflow: 100,
|
||||
outflow: 250,
|
||||
'month-year': '2021-05',
|
||||
yearmonth: '2021-05',
|
||||
},
|
||||
{
|
||||
inflow: 350,
|
||||
outflow: 100,
|
||||
'month-year': '2021-06',
|
||||
yearmonth: '2021-06',
|
||||
},
|
||||
{
|
||||
inflow: 50,
|
||||
outflow: 300,
|
||||
'month-year': '2021-07',
|
||||
yearmonth: '2021-07',
|
||||
},
|
||||
{
|
||||
inflow: 320,
|
||||
outflow: 100,
|
||||
'month-year': '2021-08',
|
||||
yearmonth: '2021-08',
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
@ -7,12 +7,12 @@
|
||||
<hr class="border-t mt-10" />
|
||||
<UnpaidInvoices class="mt-10" />
|
||||
<hr class="border-t mt-10" />
|
||||
<!--
|
||||
<div class="flex justify-between mx-auto mt-10 ml-4 mr-4 gap-10">
|
||||
<ProfitAndLoss class="w-1/2" />
|
||||
<!--
|
||||
<Expenses class="w-1/2" />
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -21,7 +21,7 @@
|
||||
import PageHeader from 'src/components/PageHeader';
|
||||
import Cashflow from './Cashflow';
|
||||
// import Expenses from './Expenses';
|
||||
// import ProfitAndLoss from './ProfitAndLoss';
|
||||
import ProfitAndLoss from './ProfitAndLoss';
|
||||
import UnpaidInvoices from './UnpaidInvoices';
|
||||
|
||||
export default {
|
||||
@ -30,8 +30,8 @@ export default {
|
||||
PageHeader,
|
||||
UnpaidInvoices,
|
||||
Cashflow,
|
||||
/*
|
||||
ProfitAndLoss,
|
||||
/*
|
||||
Expenses,
|
||||
*/
|
||||
},
|
||||
|
@ -28,11 +28,11 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { fyo } from 'src/initFyo';
|
||||
import BarChart from 'src/components/Charts/BarChart.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { formatXLabels, getYMax, getYMin } from 'src/utils/chart';
|
||||
import { getDatesAndPeriodicity } from 'src/utils/misc';
|
||||
import ProfitAndLoss from '../../../reports/ProfitAndLoss/ProfitAndLoss';
|
||||
import { getDatesAndPeriodList } from 'src/utils/misc';
|
||||
import { getValueMapFromList } from 'utils';
|
||||
import PeriodSelector from './PeriodSelector';
|
||||
import SectionHeader from './SectionHeader';
|
||||
|
||||
@ -46,7 +46,7 @@ export default {
|
||||
data: () => ({
|
||||
period: 'This Year',
|
||||
data: [],
|
||||
periodList: [],
|
||||
hasData: false,
|
||||
}),
|
||||
activated() {
|
||||
this.setData();
|
||||
@ -56,13 +56,13 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
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 format = (value) => fyo.format(value ?? 0, 'Currency');
|
||||
const yMax = getYMax(points);
|
||||
const yMin = getYMin(points);
|
||||
return {
|
||||
xLabels: this.periodList,
|
||||
xLabels: this.data.map((d) => d.yearmonth),
|
||||
points,
|
||||
format,
|
||||
colors,
|
||||
@ -71,25 +71,31 @@ export default {
|
||||
formatX: formatXLabels,
|
||||
};
|
||||
},
|
||||
hasData() {
|
||||
return this.periodList.some((key) => this.data[key] !== 0);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async setData() {
|
||||
let { fromDate, toDate, periodicity } = await getDatesAndPeriodicity(
|
||||
const { fromDate, toDate, periodList } = await getDatesAndPeriodList(
|
||||
this.period
|
||||
);
|
||||
|
||||
let pl = new ProfitAndLoss(this.fyo);
|
||||
let res = await pl.run({
|
||||
fromDate,
|
||||
toDate,
|
||||
periodicity,
|
||||
});
|
||||
const data = await fyo.db.getIncomeAndExpenses(
|
||||
fromDate.toISO(),
|
||||
toDate.toISO()
|
||||
);
|
||||
const incomes = getValueMapFromList(data.income, 'yearmonth', 'balance');
|
||||
const expenses = getValueMapFromList(
|
||||
data.expense,
|
||||
'yearmonth',
|
||||
'balance'
|
||||
);
|
||||
|
||||
this.data = res.rows.at(-1);
|
||||
this.periodList = res.columns;
|
||||
this.data = periodList.map((d) => {
|
||||
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>
|
||||
<MouseFollower
|
||||
v-if="invoices[0].hasData || invoices[1].hasData"
|
||||
:offset="15"
|
||||
:show="idx >= 0"
|
||||
placement="top"
|
||||
class="text-sm shadow-md px-2 py-1 bg-white text-gray-900 border-l-2"
|
||||
:style="{ borderColor: colors[idx] }"
|
||||
>
|
||||
@ -169,14 +171,14 @@ export default {
|
||||
|
||||
const { total, outstanding } = await fyo.db.getTotalOutstanding(
|
||||
invoice.schemaName,
|
||||
fromDate,
|
||||
toDate
|
||||
fromDate.toISO(),
|
||||
toDate.toISO()
|
||||
);
|
||||
|
||||
const { countTotal, countOutstanding } = await this.getCounts(
|
||||
invoice.schemaName,
|
||||
DateTime.fromISO(fromDate),
|
||||
DateTime.fromISO(toDate)
|
||||
fromDate,
|
||||
toDate
|
||||
);
|
||||
|
||||
invoice.total = total ?? 0;
|
||||
|
@ -9,7 +9,7 @@ import { fyo } from 'src/initFyo';
|
||||
|
||||
export function getDatesAndPeriodList(
|
||||
period: 'This Year' | 'This Quarter' | 'This Month'
|
||||
): { periodList: DateTime[]; fromDate: string; toDate: string } {
|
||||
): { periodList: DateTime[]; fromDate: DateTime; toDate: DateTime } {
|
||||
const toDate: DateTime = DateTime.now();
|
||||
let fromDate: DateTime;
|
||||
|
||||
@ -39,8 +39,8 @@ export function getDatesAndPeriodList(
|
||||
|
||||
return {
|
||||
periodList,
|
||||
fromDate: fromDate.toISO(),
|
||||
toDate: toDate.toISO(),
|
||||
fromDate,
|
||||
toDate,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { t } from 'fyo';
|
||||
import { DocValueMap } from 'fyo/core/types';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
import reports from 'reports/view';
|
||||
import { OptionField } from 'schemas/types';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { GetAllOptions } from 'utils/db/types';
|
||||
@ -123,13 +122,15 @@ function getCreateList(): SearchItem[] {
|
||||
}
|
||||
|
||||
function getReportList(): SearchItem[] {
|
||||
return Object.values(reports).map((report) => {
|
||||
/*return Object.values(reports).map((report) => {
|
||||
return {
|
||||
label: report.title,
|
||||
route: `/report/${report.method}`,
|
||||
group: 'Report',
|
||||
};
|
||||
});
|
||||
*/
|
||||
return [];
|
||||
}
|
||||
|
||||
function getListViewList(): SearchItem[] {
|
||||
|
Loading…
Reference in New Issue
Block a user