mirror of
https://github.com/frappe/books.git
synced 2025-02-08 15:08:29 +00:00
incr: add indentation support, format PNL
This commit is contained in:
parent
ad22005cc2
commit
79650d6c8e
@ -23,6 +23,7 @@ type ReferenceType =
|
|||||||
export class GeneralLedger extends LedgerReport {
|
export class GeneralLedger extends LedgerReport {
|
||||||
static title = t`General Ledger`;
|
static title = t`General Ledger`;
|
||||||
static reportName = 'general-ledger';
|
static reportName = 'general-ledger';
|
||||||
|
usePagination: boolean = true;
|
||||||
|
|
||||||
ascending: boolean = false;
|
ascending: boolean = false;
|
||||||
reverted: boolean = false;
|
reverted: boolean = false;
|
||||||
@ -102,19 +103,23 @@ export class GeneralLedger extends LedgerReport {
|
|||||||
|
|
||||||
_getRowFromEntry(entry: LedgerEntry, columns: ColumnField[]): ReportRow {
|
_getRowFromEntry(entry: LedgerEntry, columns: ColumnField[]): ReportRow {
|
||||||
if (entry.name === -3) {
|
if (entry.name === -3) {
|
||||||
return columns.map((c) => ({
|
return {
|
||||||
value: '',
|
cells: columns.map((c) => ({
|
||||||
width: c.width ?? 1,
|
rawValue: '',
|
||||||
})) as ReportRow;
|
value: '',
|
||||||
|
width: c.width ?? 1,
|
||||||
|
})),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const row: ReportRow = [];
|
const row: ReportRow = { cells: [] };
|
||||||
for (const col of columns) {
|
for (const col of columns) {
|
||||||
const align = col.align ?? 'left';
|
const align = col.align ?? 'left';
|
||||||
const width = col.width ?? 1;
|
const width = col.width ?? 1;
|
||||||
const fieldname = col.fieldname;
|
const fieldname = col.fieldname;
|
||||||
|
|
||||||
let value = entry[fieldname as keyof LedgerEntry];
|
let value = entry[fieldname as keyof LedgerEntry];
|
||||||
|
const rawValue = value;
|
||||||
if (value === null || value === undefined) {
|
if (value === null || value === undefined) {
|
||||||
value = '';
|
value = '';
|
||||||
}
|
}
|
||||||
@ -133,10 +138,11 @@ export class GeneralLedger extends LedgerReport {
|
|||||||
value = String(value);
|
value = String(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
row.push({
|
row.cells.push({
|
||||||
italics: entry.name === -1,
|
italics: entry.name === -1,
|
||||||
bold: entry.name === -2,
|
bold: entry.name === -2,
|
||||||
value,
|
value,
|
||||||
|
rawValue,
|
||||||
align,
|
align,
|
||||||
width,
|
width,
|
||||||
});
|
});
|
||||||
|
@ -35,6 +35,13 @@ interface Account {
|
|||||||
parentAccount: string | null;
|
parentAccount: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface TreeNode {
|
||||||
|
name: string;
|
||||||
|
children?: TreeNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tree = Record<string, TreeNode>;
|
||||||
|
|
||||||
type AccountTree = Record<string, AccountTreeNode>;
|
type AccountTree = Record<string, AccountTreeNode>;
|
||||||
interface AccountTreeNode extends Account {
|
interface AccountTreeNode extends Account {
|
||||||
children?: AccountTreeNode[];
|
children?: AccountTreeNode[];
|
||||||
@ -53,7 +60,7 @@ const PNL_ROOT_TYPES: AccountRootType[] = [
|
|||||||
AccountRootTypeEnum.Expense,
|
AccountRootTypeEnum.Expense,
|
||||||
];
|
];
|
||||||
|
|
||||||
const ACC_NAME_WIDTH = 1.5;
|
const ACC_NAME_WIDTH = 2;
|
||||||
const ACC_BAL_WIDTH = 1;
|
const ACC_BAL_WIDTH = 1;
|
||||||
|
|
||||||
export class ProfitAndLoss extends LedgerReport {
|
export class ProfitAndLoss extends LedgerReport {
|
||||||
@ -65,6 +72,7 @@ export class ProfitAndLoss extends LedgerReport {
|
|||||||
fromYear?: number;
|
fromYear?: number;
|
||||||
toYear?: number;
|
toYear?: number;
|
||||||
singleColumn: boolean = false;
|
singleColumn: boolean = false;
|
||||||
|
hideGroupBalance: boolean = false;
|
||||||
periodicity: Periodicity = 'Monthly';
|
periodicity: Periodicity = 'Monthly';
|
||||||
basedOn: BasedOn = 'Date Range';
|
basedOn: BasedOn = 'Date Range';
|
||||||
|
|
||||||
@ -98,29 +106,204 @@ export class ProfitAndLoss extends LedgerReport {
|
|||||||
delete accountTree[name];
|
delete accountTree[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountList = convertAccountTreeToAccountList(accountTree);
|
const rootNodeList = Object.values(accountTree);
|
||||||
this.reportData = this.getReportDataFromAccountList(accountList);
|
const incomeTree = {
|
||||||
|
[AccountRootTypeEnum.Income]: rootNodeList.find(
|
||||||
|
(n) => n.rootType === AccountRootTypeEnum.Income
|
||||||
|
)!,
|
||||||
|
};
|
||||||
|
const expenseTree = {
|
||||||
|
[AccountRootTypeEnum.Expense]: rootNodeList.find(
|
||||||
|
(n) => n.rootType === AccountRootTypeEnum.Expense
|
||||||
|
)!,
|
||||||
|
};
|
||||||
|
|
||||||
|
const incomeList = convertAccountTreeToAccountList(incomeTree);
|
||||||
|
const expenseList = convertAccountTreeToAccountList(expenseTree);
|
||||||
|
const incomeRows = this.getPnlRowsFromAccountList(incomeList);
|
||||||
|
const expenseRows = this.getPnlRowsFromAccountList(expenseList);
|
||||||
|
this.reportData = await this.getReportDataFromRows(
|
||||||
|
incomeRows,
|
||||||
|
expenseRows,
|
||||||
|
incomeTree,
|
||||||
|
expenseTree
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getReportDataFromAccountList(accountList: AccountList): ReportData {
|
async getReportDataFromRows(
|
||||||
const dateKeys = [...accountList[0].valueMap!.keys()].sort(
|
incomeRows: ReportData,
|
||||||
(a, b) => b.toDate.toMillis() - a.toDate.toMillis()
|
expenseRows: ReportData,
|
||||||
|
incomeTree: AccountTree,
|
||||||
|
expenseTree: AccountTree
|
||||||
|
): Promise<ReportData> {
|
||||||
|
const totalIncome = await this.getTotalNode(
|
||||||
|
incomeTree,
|
||||||
|
t`Total Income (Credit)`
|
||||||
|
);
|
||||||
|
const totalExpense = await this.getTotalNode(
|
||||||
|
expenseTree,
|
||||||
|
t`Total Expense (Debit)`
|
||||||
);
|
);
|
||||||
|
|
||||||
return accountList.map((al) => {
|
const totalValueMap = new Map();
|
||||||
const nameCell = { value: al.name, align: 'left', width: ACC_NAME_WIDTH };
|
for (const key of totalIncome.valueMap!.keys()) {
|
||||||
const balanceCells = dateKeys.map(
|
const income = totalIncome.valueMap!.get(key)!;
|
||||||
(k) =>
|
const expense = totalExpense.valueMap!.get(key)!;
|
||||||
|
totalValueMap.set(key, income - expense);
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalProfit = {
|
||||||
|
name: t`Total Profit`,
|
||||||
|
valueMap: totalValueMap,
|
||||||
|
level: 0,
|
||||||
|
} as AccountListNode;
|
||||||
|
|
||||||
|
const dateKeys = this.getSortedDateKeys(totalValueMap);
|
||||||
|
const totalIncomeRow = this.getRowFromAccountListNode(
|
||||||
|
totalIncome,
|
||||||
|
dateKeys
|
||||||
|
);
|
||||||
|
const totalExpenseRow = this.getRowFromAccountListNode(
|
||||||
|
totalExpense,
|
||||||
|
dateKeys
|
||||||
|
);
|
||||||
|
|
||||||
|
const totalProfitRow = this.getRowFromAccountListNode(
|
||||||
|
totalProfit,
|
||||||
|
dateKeys
|
||||||
|
);
|
||||||
|
totalProfitRow.cells.forEach((c) => {
|
||||||
|
c.bold = true;
|
||||||
|
if (typeof c.rawValue !== 'number') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.rawValue > 0) {
|
||||||
|
c.color = 'green';
|
||||||
|
} else if (c.rawValue < 0) {
|
||||||
|
c.color = 'red';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emptyRow = await this.getEmptyRow();
|
||||||
|
|
||||||
|
return [
|
||||||
|
incomeRows,
|
||||||
|
totalIncomeRow,
|
||||||
|
emptyRow,
|
||||||
|
expenseRows,
|
||||||
|
totalExpenseRow,
|
||||||
|
emptyRow,
|
||||||
|
emptyRow,
|
||||||
|
totalProfitRow,
|
||||||
|
].flat() as ReportData;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getEmptyRow(): Promise<ReportRow> {
|
||||||
|
const columns = await this.getColumns();
|
||||||
|
return {
|
||||||
|
cells: columns.map(
|
||||||
|
(c) =>
|
||||||
({
|
({
|
||||||
value: this.fyo.format(al.valueMap?.get(k)!, 'Currency'),
|
value: '',
|
||||||
align: 'right',
|
rawValue: '',
|
||||||
width: ACC_BAL_WIDTH,
|
width: c.width,
|
||||||
|
align: 'left',
|
||||||
} as ReportCell)
|
} as ReportCell)
|
||||||
);
|
),
|
||||||
return [nameCell, balanceCells].flat() as ReportRow;
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async getTotalNode(
|
||||||
|
accountTree: AccountTree,
|
||||||
|
name: string
|
||||||
|
): Promise<AccountListNode> {
|
||||||
|
const leafNodes = getListOfLeafNodes(accountTree) as AccountTreeNode[];
|
||||||
|
let keys: DateRange[] | undefined = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys need to be from the nodes cause they are ref keys.
|
||||||
|
*/
|
||||||
|
for (const node of leafNodes) {
|
||||||
|
const drs = [...(node?.valueMap?.keys() ?? [])];
|
||||||
|
if (!drs || !drs.length) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = drs;
|
||||||
|
if (keys && keys.length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keys || !keys.length) {
|
||||||
|
keys = await this._getDateRanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalMap = leafNodes.reduce((acc, node) => {
|
||||||
|
for (const key of keys!) {
|
||||||
|
const bal = acc.get(key) ?? 0;
|
||||||
|
const val = node.valueMap?.get(key) ?? 0;
|
||||||
|
acc.set(key, bal + val);
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, new Map() as ValueMap);
|
||||||
|
|
||||||
|
return { name, valueMap: totalMap, level: 0 } as AccountListNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
getPnlRowsFromAccountList(accountList: AccountList): ReportData {
|
||||||
|
const dateKeys = this.getSortedDateKeys(accountList[0].valueMap!);
|
||||||
|
|
||||||
|
return accountList.map((al) => {
|
||||||
|
return this.getRowFromAccountListNode(al, dateKeys);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSortedDateKeys(valueMap: ValueMap) {
|
||||||
|
return [...valueMap.keys()].sort(
|
||||||
|
(a, b) => b.toDate.toMillis() - a.toDate.toMillis()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRowFromAccountListNode(al: AccountListNode, dateKeys: DateRange[]) {
|
||||||
|
const nameCell = {
|
||||||
|
value: al.name,
|
||||||
|
rawValue: al.name,
|
||||||
|
align: 'left',
|
||||||
|
width: ACC_NAME_WIDTH,
|
||||||
|
bold: !al.level,
|
||||||
|
italics: al.isGroup,
|
||||||
|
indent: al.level ?? 0,
|
||||||
|
} as ReportCell;
|
||||||
|
|
||||||
|
const balanceCells = dateKeys.map((k) => {
|
||||||
|
const rawValue = al.valueMap?.get(k) ?? 0;
|
||||||
|
let value = this.fyo.format(rawValue, 'Currency');
|
||||||
|
if (this.hideGroupBalance && al.isGroup) {
|
||||||
|
value = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
rawValue,
|
||||||
|
value,
|
||||||
|
align: 'right',
|
||||||
|
width: ACC_BAL_WIDTH,
|
||||||
|
} as ReportCell;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
cells: [nameCell, balanceCells].flat(),
|
||||||
|
level: al.level,
|
||||||
|
isGroup: !!al.isGroup,
|
||||||
|
folded: false,
|
||||||
|
foldedBelow: false,
|
||||||
|
} as ReportRow;
|
||||||
|
}
|
||||||
|
|
||||||
|
// async getTotalProfitNode()
|
||||||
|
|
||||||
async _getGroupedByDateRanges(
|
async _getGroupedByDateRanges(
|
||||||
map: GroupedMap
|
map: GroupedMap
|
||||||
): Promise<AccountNameValueMapMap> {
|
): Promise<AccountNameValueMapMap> {
|
||||||
@ -346,6 +529,11 @@ export class ProfitAndLoss extends LedgerReport {
|
|||||||
label: t`Single Column`,
|
label: t`Single Column`,
|
||||||
fieldname: 'singleColumn',
|
fieldname: 'singleColumn',
|
||||||
} as Field,
|
} as Field,
|
||||||
|
{
|
||||||
|
fieldtype: 'Check',
|
||||||
|
label: t`Hide Group Balance`,
|
||||||
|
fieldname: 'hideGroupBalance',
|
||||||
|
} as Field,
|
||||||
].flat();
|
].flat();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -439,17 +627,13 @@ function setValueMapOnAccountTreeNodes(
|
|||||||
* also prevent pruning of the parent accounts.
|
* also prevent pruning of the parent accounts.
|
||||||
*/
|
*/
|
||||||
let parentAccountName: string | null = accountTree[name].parentAccount;
|
let parentAccountName: string | null = accountTree[name].parentAccount;
|
||||||
let parentValueMap = valueMap;
|
|
||||||
|
|
||||||
while (parentAccountName !== null) {
|
while (parentAccountName !== null) {
|
||||||
const update = updateParentAccountWithChildValues(
|
parentAccountName = updateParentAccountWithChildValues(
|
||||||
accountTree,
|
accountTree,
|
||||||
parentAccountName,
|
parentAccountName,
|
||||||
parentValueMap
|
valueMap
|
||||||
);
|
);
|
||||||
|
|
||||||
parentAccountName = update.parentAccountName;
|
|
||||||
parentValueMap = update.parentValueMap;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,24 +641,18 @@ function setValueMapOnAccountTreeNodes(
|
|||||||
function updateParentAccountWithChildValues(
|
function updateParentAccountWithChildValues(
|
||||||
accountTree: AccountTree,
|
accountTree: AccountTree,
|
||||||
parentAccountName: string,
|
parentAccountName: string,
|
||||||
parentValueMap: ValueMap
|
valueMap: ValueMap
|
||||||
): {
|
): string {
|
||||||
parentAccountName: string | null;
|
|
||||||
parentValueMap: ValueMap;
|
|
||||||
} {
|
|
||||||
const parentAccount = accountTree[parentAccountName];
|
const parentAccount = accountTree[parentAccountName];
|
||||||
parentAccount.prune = false;
|
parentAccount.prune = false;
|
||||||
parentAccount.valueMap ??= new Map();
|
parentAccount.valueMap ??= new Map();
|
||||||
|
|
||||||
for (const key of parentValueMap.keys()) {
|
for (const key of valueMap.keys()) {
|
||||||
const value = parentAccount.valueMap!.get(key) ?? 0;
|
const value = parentAccount.valueMap!.get(key) ?? 0;
|
||||||
parentAccount.valueMap!.set(key, value + parentValueMap.get(key)!);
|
parentAccount.valueMap!.set(key, value + valueMap.get(key)!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return parentAccount.parentAccount!;
|
||||||
parentAccountName: parentAccount.parentAccount,
|
|
||||||
parentValueMap: parentAccount.valueMap!,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setChildrenOnAccountTreeNodes(accountTree: AccountTree) {
|
function setChildrenOnAccountTreeNodes(accountTree: AccountTree) {
|
||||||
@ -559,3 +737,22 @@ function pushToAccountList(
|
|||||||
pushToAccountList(childNode, accountList, childLevel);
|
pushToAccountList(childNode, accountList, childLevel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getListOfLeafNodes(tree: Tree): TreeNode[] {
|
||||||
|
const nonGroupChildren: TreeNode[] = [];
|
||||||
|
for (const node of Object.values(tree)) {
|
||||||
|
const groupChildren = node.children ?? [];
|
||||||
|
|
||||||
|
while (groupChildren.length) {
|
||||||
|
const child = groupChildren.shift()!;
|
||||||
|
if (!child?.children?.length) {
|
||||||
|
nonGroupChildren.push(child);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
groupChildren.unshift(...(child.children ?? []));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nonGroupChildren;
|
||||||
|
}
|
||||||
|
@ -13,6 +13,7 @@ export abstract class Report extends Observable<RawValue> {
|
|||||||
columns: ColumnField[] = [];
|
columns: ColumnField[] = [];
|
||||||
filters: Field[] = [];
|
filters: Field[] = [];
|
||||||
reportData: ReportData;
|
reportData: ReportData;
|
||||||
|
usePagination: boolean = false;
|
||||||
|
|
||||||
constructor(fyo: Fyo) {
|
constructor(fyo: Fyo) {
|
||||||
super();
|
super();
|
||||||
|
@ -9,9 +9,18 @@ export interface ReportCell {
|
|||||||
align?: 'left' | 'right' | 'center';
|
align?: 'left' | 'right' | 'center';
|
||||||
width?: number;
|
width?: number;
|
||||||
value: string;
|
value: string;
|
||||||
|
rawValue: RawValue | undefined | Date;
|
||||||
|
indent?: number;
|
||||||
|
color?: 'red' | 'green';
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ReportRow = ReportCell[];
|
export interface ReportRow {
|
||||||
|
cells: ReportCell[];
|
||||||
|
level?: number;
|
||||||
|
isGroup?: boolean;
|
||||||
|
folded?: boolean;
|
||||||
|
foldedBelow?: boolean;
|
||||||
|
}
|
||||||
export type ReportData = ReportRow[];
|
export type ReportData = ReportRow[];
|
||||||
export interface ColumnField extends BaseField {
|
export interface ColumnField extends BaseField {
|
||||||
align?: 'left' | 'right' | 'center';
|
align?: 'left' | 'right' | 'center';
|
||||||
@ -58,4 +67,4 @@ export interface LedgerEntry {
|
|||||||
reverts: string;
|
reverts: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GroupedMap = Map<string, LedgerEntry[]>;
|
export type GroupedMap = Map<string, LedgerEntry[]>;
|
||||||
|
@ -30,41 +30,47 @@
|
|||||||
@scroll="scroll"
|
@scroll="scroll"
|
||||||
>
|
>
|
||||||
<!-- Report Rows -->
|
<!-- Report Rows -->
|
||||||
<div
|
<template v-for="(row, r) in dataSlice" :key="r + '-row'">
|
||||||
v-for="(row, r) in report.reportData.slice(pageStart, pageEnd)"
|
|
||||||
:key="r + '-row'"
|
|
||||||
class="flex items-center w-max"
|
|
||||||
:style="{ height: `${hconst}px` }"
|
|
||||||
:class="r !== pageEnd - 1 ? 'border-b' : ''"
|
|
||||||
>
|
|
||||||
<!-- Report Cell -->
|
|
||||||
<div
|
<div
|
||||||
v-for="(cell, c) in row"
|
v-if="!row.folded"
|
||||||
:key="`${c}-${r}-cell`"
|
class="flex items-center w-max"
|
||||||
:style="getCellStyle(cell, c)"
|
:style="{ height: `${hconst}px` }"
|
||||||
class="
|
:class="[
|
||||||
text-gray-900 text-base
|
r !== pageEnd - 1 ? 'border-b' : '',
|
||||||
px-3
|
row.isGroup ? 'hover:bg-gray-100 cursor-pointer' : '',
|
||||||
flex-shrink-0
|
]"
|
||||||
overflow-x-scroll
|
@click="() => onRowClick(row, r)"
|
||||||
whitespace-nowrap
|
|
||||||
"
|
|
||||||
>
|
>
|
||||||
{{ cell.value }}
|
<!-- Report Cell -->
|
||||||
|
<div
|
||||||
|
v-for="(cell, c) in row.cells"
|
||||||
|
:key="`${c}-${r}-cell`"
|
||||||
|
:style="getCellStyle(cell, c)"
|
||||||
|
class="
|
||||||
|
text-gray-900 text-base
|
||||||
|
px-3
|
||||||
|
flex-shrink-0
|
||||||
|
overflow-x-scroll
|
||||||
|
whitespace-nowrap
|
||||||
|
"
|
||||||
|
>
|
||||||
|
{{ cell.value }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</template>
|
||||||
</WithScroll>
|
</WithScroll>
|
||||||
<!-- Report Rows Container -->
|
<!-- Report Rows Container -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Pagination Footer -->
|
<!-- Pagination Footer -->
|
||||||
<div class="mt-auto flex-shrink-0">
|
<div class="mt-auto flex-shrink-0" v-if="report.usePagination">
|
||||||
<hr />
|
<hr />
|
||||||
<Paginator
|
<Paginator
|
||||||
:item-count="report?.reportData?.length ?? 0"
|
:item-count="report?.reportData?.length ?? 0"
|
||||||
@index-change="setPageIndices"
|
@index-change="setPageIndices"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-else class="h-4" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@ -85,6 +91,15 @@ export default defineComponent({
|
|||||||
pageEnd: 0,
|
pageEnd: 0,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
dataSlice() {
|
||||||
|
if (this.report?.usePagination) {
|
||||||
|
return this.report.reportData.slice(this.pageStart, this.pageEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.report.reportData;
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
scroll({ scrollLeft }) {
|
scroll({ scrollLeft }) {
|
||||||
this.$refs.titlerow.scrollLeft = scrollLeft;
|
this.$refs.titlerow.scrollLeft = scrollLeft;
|
||||||
@ -93,24 +108,55 @@ export default defineComponent({
|
|||||||
this.pageStart = start;
|
this.pageStart = start;
|
||||||
this.pageEnd = end;
|
this.pageEnd = end;
|
||||||
},
|
},
|
||||||
|
onRowClick(clickedRow, r) {
|
||||||
|
if (!clickedRow.isGroup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
r += 1;
|
||||||
|
clickedRow.foldedBelow = !clickedRow.foldedBelow;
|
||||||
|
const folded = clickedRow.foldedBelow;
|
||||||
|
let row = this.dataSlice[r];
|
||||||
|
|
||||||
|
while (row && row.level > clickedRow.level) {
|
||||||
|
row.folded = folded;
|
||||||
|
r += 1;
|
||||||
|
row = this.dataSlice[r];
|
||||||
|
}
|
||||||
|
},
|
||||||
getCellStyle(cell, i) {
|
getCellStyle(cell, i) {
|
||||||
const styles = {};
|
const styles = {};
|
||||||
const width = cell.width ?? 1;
|
const width = cell.width ?? 1;
|
||||||
const align = cell.align ?? 'left';
|
const align = cell.align ?? 'left';
|
||||||
styles['width'] = `${width * this.wconst}rem`;
|
styles['width'] = `${width * this.wconst}rem`;
|
||||||
styles['text-align'] = align;
|
styles['text-align'] = align;
|
||||||
|
|
||||||
if (cell.bold) {
|
if (cell.bold) {
|
||||||
styles['font-weight'] = 'bold';
|
styles['font-weight'] = 'bold';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cell.italics) {
|
if (cell.italics) {
|
||||||
styles['font-style'] = 'italic';
|
styles['font-style'] = 'italic';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i === 0) {
|
if (i === 0) {
|
||||||
styles['padding-left'] = '0px';
|
styles['padding-left'] = '0px';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i === this.report.columns.length - 1) {
|
if (i === this.report.columns.length - 1) {
|
||||||
styles['padding-right'] = '0px';
|
styles['padding-right'] = '0px';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cell.indent) {
|
||||||
|
styles['padding-left'] = `${cell.indent * 2}rem`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell.color === 'red') {
|
||||||
|
styles['color'] = '#e53e3e';
|
||||||
|
} else if (cell.color === 'green') {
|
||||||
|
styles['color'] = '#38a169';
|
||||||
|
}
|
||||||
|
|
||||||
return styles;
|
return styles;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
:show-label="field.fieldtype === 'Check'"
|
:show-label="field.fieldtype === 'Check'"
|
||||||
:key="field.fieldname + '-filter'"
|
:key="field.fieldname + '-filter'"
|
||||||
class="bg-gray-100 rounded"
|
class="bg-gray-100 rounded"
|
||||||
:class="field.fieldtype === 'Check' ? 'flex pl-2' : ''"
|
:class="field.fieldtype === 'Check' ? 'flex pl-2 py-1' : ''"
|
||||||
input-class="bg-transparent text-sm"
|
input-class="bg-transparent text-sm"
|
||||||
:df="field"
|
:df="field"
|
||||||
:value="report.get(field.fieldname)"
|
:value="report.get(field.fieldname)"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user