mirror of
https://github.com/frappe/books.git
synced 2025-01-08 17:24:05 +00:00
feat: Reports
This commit is contained in:
parent
bd5d812d77
commit
86993edee2
@ -5,7 +5,8 @@ export default {
|
|||||||
title: _('Item'),
|
title: _('Item'),
|
||||||
columns: [
|
columns: [
|
||||||
'name',
|
'name',
|
||||||
|
'unit',
|
||||||
|
'tax',
|
||||||
'rate',
|
'rate',
|
||||||
'tax'
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -95,39 +95,48 @@ const viewConfig = {
|
|||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
label: 'Date',
|
label: 'Date',
|
||||||
fieldtype: 'Date'
|
fieldtype: 'Date',
|
||||||
|
fieldname: 'date'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Account',
|
label: 'Account',
|
||||||
fieldtype: 'Link'
|
fieldtype: 'Link',
|
||||||
|
fieldname: 'account'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Debit',
|
label: 'Debit',
|
||||||
fieldtype: 'Currency'
|
fieldtype: 'Currency',
|
||||||
|
fieldname: 'debit'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Credit',
|
label: 'Credit',
|
||||||
fieldtype: 'Currency'
|
fieldtype: 'Currency',
|
||||||
|
fieldname: 'credit'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Balance',
|
label: 'Balance',
|
||||||
fieldtype: 'Currency'
|
fieldtype: 'Currency',
|
||||||
|
fieldname: 'balance'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Reference Type',
|
label: 'Reference Type',
|
||||||
fieldtype: 'Data'
|
fieldtype: 'Data',
|
||||||
|
fieldname: 'referenceType'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Reference Name',
|
label: 'Reference Name',
|
||||||
fieldtype: 'Data'
|
fieldtype: 'Data',
|
||||||
|
fieldname: 'referenceName'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Party',
|
label: 'Party',
|
||||||
fieldtype: 'Link'
|
fieldtype: 'Link',
|
||||||
|
fieldname: 'party'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Description',
|
label: 'Description',
|
||||||
fieldtype: 'Data'
|
fieldtype: 'Data',
|
||||||
|
fieldname: 'description'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -14,21 +14,24 @@ export default {
|
|||||||
ratio: {
|
ratio: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
}
|
},
|
||||||
|
gap: String
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
style() {
|
style() {
|
||||||
|
let obj = {};
|
||||||
if (this.columnCount) {
|
if (this.columnCount) {
|
||||||
return {
|
obj['grid-template-columns'] = `repeat(${this.columnCount}, 1fr)`;
|
||||||
'grid-template-columns': `repeat(${this.columnCount}, 1fr)`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (this.ratio.length) {
|
if (this.ratio.length) {
|
||||||
return {
|
obj['grid-template-columns'] = this.ratio.map(r => `${r}fr`).join(' ');
|
||||||
'grid-template-columns': this.ratio.map(r => `${r}fr`).join(' ')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (this.gap) {
|
||||||
|
obj['grid-gap'] = this.gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,15 +1,16 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="px-8 pb-8 mt-2 text-sm flex flex-col justify-between">
|
<div class="px-8 pb-8 mt-2 text-sm flex flex-col justify-between">
|
||||||
<div>
|
<div>
|
||||||
<Row class="text-gray-700" :columnCount="columns.length">
|
<Row class="text-gray-700" :columnCount="columns.length" gap="1rem">
|
||||||
<div
|
<div
|
||||||
v-for="column in columns"
|
v-for="column in columns"
|
||||||
:key="column.label"
|
:key="column.label"
|
||||||
class="py-4 truncate"
|
class="py-4 truncate"
|
||||||
:class="['Float', 'Currency'].includes(column.fieldtype) ? 'text-right pr-10' : ''"
|
:class="['Float', 'Currency'].includes(column.fieldtype) ? 'text-right' : ''"
|
||||||
>{{ column.label }}</div>
|
>{{ column.label }}</div>
|
||||||
</Row>
|
</Row>
|
||||||
<Row
|
<Row
|
||||||
|
gap="1rem"
|
||||||
class="cursor-pointer text-gray-900 hover:text-gray-600"
|
class="cursor-pointer text-gray-900 hover:text-gray-600"
|
||||||
v-for="doc in data"
|
v-for="doc in data"
|
||||||
:key="doc.name"
|
:key="doc.name"
|
||||||
@ -19,14 +20,19 @@
|
|||||||
<ListCell
|
<ListCell
|
||||||
v-for="column in columns"
|
v-for="column in columns"
|
||||||
:key="column.label"
|
:key="column.label"
|
||||||
:class="['Float', 'Currency'].includes(column.fieldtype) ? 'text-right pr-10' : ''"
|
:class="['Float', 'Currency'].includes(column.fieldtype) ? 'text-right' : ''"
|
||||||
:doc="doc"
|
:doc="doc"
|
||||||
:column="column"
|
:column="column"
|
||||||
></ListCell>
|
></ListCell>
|
||||||
</Row>
|
</Row>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center justify-center">
|
<div class="flex items-center justify-center">
|
||||||
<Button :icon="true" :class="start == 0 && 'text-gray-600'" :disabled="start == 0" @click="prevPage">
|
<Button
|
||||||
|
:icon="true"
|
||||||
|
:class="start == 0 && 'text-gray-600'"
|
||||||
|
:disabled="start == 0"
|
||||||
|
@click="prevPage"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
@ -45,7 +51,12 @@
|
|||||||
<span class="text-gray-600">of</span>
|
<span class="text-gray-600">of</span>
|
||||||
<span class="font-medium">{{ totalCount }}</span>
|
<span class="font-medium">{{ totalCount }}</span>
|
||||||
</div>
|
</div>
|
||||||
<Button :icon="true" :class="start + pageLength >= totalCount && 'text-gray-600'" :disabled="start + pageLength >= totalCount" @click="nextPage">
|
<Button
|
||||||
|
:icon="true"
|
||||||
|
:class="start + pageLength >= totalCount && 'text-gray-600'"
|
||||||
|
:disabled="start + pageLength >= totalCount"
|
||||||
|
@click="nextPage"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
|
91
src/pages/Report.vue
Normal file
91
src/pages/Report.vue
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex flex-col">
|
||||||
|
<PageHeader>
|
||||||
|
<h1 slot="title" class="text-xl font-bold">{{ report.title }}</h1>
|
||||||
|
<template slot="actions">
|
||||||
|
<SearchBar class="ml-2" />
|
||||||
|
</template>
|
||||||
|
</PageHeader>
|
||||||
|
<div class="flex flex-col flex-1 px-8 mt-4">
|
||||||
|
<Row :columnCount="columns.length" gap="1rem">
|
||||||
|
<div
|
||||||
|
class="text-gray-600 text-sm truncate py-4"
|
||||||
|
v-for="column in columns"
|
||||||
|
:key="column.label"
|
||||||
|
>{{ column.label }}</div>
|
||||||
|
</Row>
|
||||||
|
<div class="flex-1 overflow-auto">
|
||||||
|
<Row v-for="row in rows" :columnCount="columns.length" gap="1rem">
|
||||||
|
<div
|
||||||
|
class="text-gray-900 text-sm truncate py-4"
|
||||||
|
v-for="column in columns"
|
||||||
|
:key="column.label"
|
||||||
|
v-html="row[column.fieldname]"
|
||||||
|
></div>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import frappe from 'frappejs';
|
||||||
|
import PageHeader from '@/components/PageHeader';
|
||||||
|
import Button from '@/components/Button';
|
||||||
|
import SearchBar from '@/components/SearchBar';
|
||||||
|
import Row from '@/components/Row';
|
||||||
|
import reportViewConfig from '@/../reports/view';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Report',
|
||||||
|
props: ['reportName'],
|
||||||
|
components: {
|
||||||
|
PageHeader,
|
||||||
|
Button,
|
||||||
|
SearchBar,
|
||||||
|
Row
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
rows: [],
|
||||||
|
columns: []
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.columns = this.report.getColumns();
|
||||||
|
this.fetchReportData();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async fetchReportData() {
|
||||||
|
let data = await frappe.call({
|
||||||
|
method: this.report.method,
|
||||||
|
args: {
|
||||||
|
fromDate: '2019-09-01',
|
||||||
|
toDate: '2019-10-31'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let rows, columns;
|
||||||
|
if (data.rows) {
|
||||||
|
rows = data.rows;
|
||||||
|
} else {
|
||||||
|
rows = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.columns) {
|
||||||
|
this.columns = this.report.getColumns(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rows) {
|
||||||
|
rows = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.rows = rows;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
report() {
|
||||||
|
return reportViewConfig[this.reportName];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
@ -7,7 +7,7 @@ import FormView from '@/pages/FormView/FormView';
|
|||||||
import PrintView from '@/pages/PrintView';
|
import PrintView from '@/pages/PrintView';
|
||||||
import QuickEditForm from '@/pages/QuickEditForm';
|
import QuickEditForm from '@/pages/QuickEditForm';
|
||||||
|
|
||||||
import Report from '@/pages/Report';
|
import Report from '@/pages/Report.vue';
|
||||||
import reportViewConfig from '../reports/view';
|
import reportViewConfig from '../reports/view';
|
||||||
|
|
||||||
import DataImport from '@/pages/DataImport';
|
import DataImport from '@/pages/DataImport';
|
||||||
@ -29,7 +29,7 @@ const routes = [
|
|||||||
component: Dashboard
|
component: Dashboard
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/edit/SalesInvoice/:name',
|
path: '/edit/:doctype/:name',
|
||||||
name: 'InvoiceForm',
|
name: 'InvoiceForm',
|
||||||
components: {
|
components: {
|
||||||
default: InvoiceForm,
|
default: InvoiceForm,
|
||||||
@ -74,14 +74,7 @@ const routes = [
|
|||||||
path: '/report/:reportName',
|
path: '/report/:reportName',
|
||||||
name: 'Report',
|
name: 'Report',
|
||||||
component: Report,
|
component: Report,
|
||||||
props: route => {
|
props: true
|
||||||
const { reportName } = route.params;
|
|
||||||
return {
|
|
||||||
reportName,
|
|
||||||
reportConfig: reportViewConfig[reportName] || null,
|
|
||||||
filters: route.query
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/data-import',
|
path: '/data-import',
|
||||||
@ -119,6 +112,6 @@ const routes = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
let router = new Router({ routes });
|
let router = new Router({ routes });
|
||||||
router.replace('/list/Item');
|
router.replace('/report/general-ledger');
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
Loading…
Reference in New Issue
Block a user