2
0
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:
18alantom 2022-05-17 13:07:02 +05:30
parent e9226f38c8
commit 07c923c6b1
19 changed files with 105 additions and 1153 deletions

View File

@ -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 };
}
}

View File

@ -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
*/

View File

@ -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',
});
}
}

View File

@ -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;

View File

@ -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'
}
};

View File

@ -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',
},
];
},
};

View File

@ -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,
};
}

View File

@ -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;

View File

@ -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' },
];
},
};

View File

@ -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;

View File

@ -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' },
];
},
};

View File

@ -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>;

View File

@ -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: {

View File

@ -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>

View File

@ -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,
*/
},

View File

@ -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;
},
},
};

View File

@ -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;

View File

@ -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,
};
}

View File

@ -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[] {