mirror of
https://github.com/frappe/books.git
synced 2024-11-10 07:40:55 +00:00
Merge pull request #661 from frappe/report-print
feat: print reports to PDF
This commit is contained in:
commit
b1849928d2
@ -19,6 +19,8 @@ import * as errors from './utils/errors';
|
||||
import { format } from './utils/format';
|
||||
import { t, T } from './utils/translation';
|
||||
import { ErrorLog } from './utils/types';
|
||||
import type { reports } from 'reports/index';
|
||||
import type { Report } from 'reports/Report';
|
||||
|
||||
export class Fyo {
|
||||
t = t;
|
||||
@ -234,6 +236,7 @@ export class Fyo {
|
||||
deviceId: '',
|
||||
openCount: -1,
|
||||
appFlags: {} as Record<string, boolean>,
|
||||
reports: {} as Record<keyof typeof reports, Report | undefined>,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,15 @@
|
||||
import { Attachment } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { HiddenMap } from 'fyo/model/types';
|
||||
|
||||
export class PrintSettings extends Doc {
|
||||
logo?: Attachment;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
address?: string;
|
||||
companyName?: string;
|
||||
color?: string;
|
||||
font?: string;
|
||||
displayLogo?: boolean;
|
||||
override hidden: HiddenMap = {};
|
||||
}
|
||||
|
@ -2,11 +2,10 @@ import { BalanceSheet } from './BalanceSheet/BalanceSheet';
|
||||
import { GeneralLedger } from './GeneralLedger/GeneralLedger';
|
||||
import { GSTR1 } from './GoodsAndServiceTax/GSTR1';
|
||||
import { GSTR2 } from './GoodsAndServiceTax/GSTR2';
|
||||
import { StockLedger } from './inventory/StockLedger';
|
||||
import { StockBalance } from './inventory/StockBalance';
|
||||
import { ProfitAndLoss } from './ProfitAndLoss/ProfitAndLoss';
|
||||
import { Report } from './Report';
|
||||
import { TrialBalance } from './TrialBalance/TrialBalance';
|
||||
import { StockBalance } from './inventory/StockBalance';
|
||||
import { StockLedger } from './inventory/StockLedger';
|
||||
|
||||
export const reports = {
|
||||
GeneralLedger,
|
||||
@ -17,4 +16,4 @@ export const reports = {
|
||||
GSTR2,
|
||||
StockLedger,
|
||||
StockBalance,
|
||||
} as Record<string, typeof Report>;
|
||||
} as const;
|
||||
|
@ -1,47 +1,45 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<div class="flex flex-col flex-1 bg-gray-25">
|
||||
<PageHeader :border="true">
|
||||
<template #left>
|
||||
<AutoComplete
|
||||
v-if="templateList.length"
|
||||
:df="{
|
||||
fieldtype: 'AutoComplete',
|
||||
fieldname: 'templateName',
|
||||
label: t`Template Name`,
|
||||
options: templateList.map((n) => ({ label: n, value: n })),
|
||||
}"
|
||||
input-class="text-base py-0 h-8"
|
||||
class="w-56"
|
||||
:border="true"
|
||||
:value="templateName ?? ''"
|
||||
@change="onTemplateNameChange"
|
||||
/>
|
||||
</template>
|
||||
<DropdownWithActions :actions="actions" :title="t`More`" />
|
||||
<Button class="text-xs" type="primary" @click="savePDF">
|
||||
{{ t`Save as PDF` }}
|
||||
</Button>
|
||||
</PageHeader>
|
||||
|
||||
<!-- Template Display Area -->
|
||||
<div class="overflow-auto custom-scroll p-4">
|
||||
<!-- Display Hints -->
|
||||
<div v-if="helperMessage" class="text-sm text-gray-700">
|
||||
{{ helperMessage }}
|
||||
</div>
|
||||
|
||||
<!-- Template Container -->
|
||||
<PrintContainer
|
||||
ref="printContainer"
|
||||
v-if="printProps"
|
||||
:template="printProps.template"
|
||||
:values="printProps.values"
|
||||
:scale="scale"
|
||||
:width="templateDoc?.width"
|
||||
:height="templateDoc?.height"
|
||||
<div class="flex flex-col flex-1 bg-gray-25">
|
||||
<PageHeader :border="true">
|
||||
<template #left>
|
||||
<AutoComplete
|
||||
v-if="templateList.length"
|
||||
:df="{
|
||||
fieldtype: 'AutoComplete',
|
||||
fieldname: 'templateName',
|
||||
label: t`Template Name`,
|
||||
options: templateList.map((n) => ({ label: n, value: n })),
|
||||
}"
|
||||
input-class="text-base py-0 h-8"
|
||||
class="w-56"
|
||||
:border="true"
|
||||
:value="templateName ?? ''"
|
||||
@change="onTemplateNameChange"
|
||||
/>
|
||||
</template>
|
||||
<DropdownWithActions :actions="actions" :title="t`More`" />
|
||||
<Button class="text-xs" type="primary" @click="savePDF">
|
||||
{{ t`Save as PDF` }}
|
||||
</Button>
|
||||
</PageHeader>
|
||||
|
||||
<!-- Template Display Area -->
|
||||
<div class="overflow-auto custom-scroll p-4">
|
||||
<!-- Display Hints -->
|
||||
<div v-if="helperMessage" class="text-sm text-gray-700">
|
||||
{{ helperMessage }}
|
||||
</div>
|
||||
|
||||
<!-- Template Container -->
|
||||
<PrintContainer
|
||||
ref="printContainer"
|
||||
v-if="printProps"
|
||||
:template="printProps.template"
|
||||
:values="printProps.values"
|
||||
:scale="scale"
|
||||
:width="templateDoc?.width"
|
||||
:height="templateDoc?.height"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -61,6 +59,7 @@ import { PrintValues } from 'src/utils/types';
|
||||
import { getFormRoute, openSettings, routeTo } from 'src/utils/ui';
|
||||
import { defineComponent } from 'vue';
|
||||
import PrintContainer from '../TemplateBuilder/PrintContainer.vue';
|
||||
import { showSidebar } from 'src/utils/refs';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'PrintView',
|
||||
@ -204,11 +203,22 @@ export default defineComponent({
|
||||
this.values = await getPrintTemplatePropValues(this.doc as Doc);
|
||||
}
|
||||
},
|
||||
setScale() {
|
||||
this.scale = 1;
|
||||
const width = (this.templateDoc?.width ?? 21) * 37.8;
|
||||
let containerWidth = window.innerWidth - 32;
|
||||
if (showSidebar.value) {
|
||||
containerWidth -= 12 * 16;
|
||||
}
|
||||
|
||||
this.scale = Math.min(containerWidth / width, 1);
|
||||
},
|
||||
reset() {
|
||||
this.doc = null;
|
||||
this.values = null;
|
||||
this.templateList = [];
|
||||
this.templateDoc = null;
|
||||
this.scale = 1;
|
||||
},
|
||||
async onTemplateNameChange(value: string | null): Promise<void> {
|
||||
if (!value) {
|
||||
@ -225,6 +235,7 @@ export default defineComponent({
|
||||
} catch (error) {
|
||||
await handleErrorWithDialog(error);
|
||||
}
|
||||
this.setScale();
|
||||
},
|
||||
async setTemplateList(): Promise<void> {
|
||||
const list = (await this.fyo.db.getAllRaw(ModelNameEnum.PrintTemplate, {
|
||||
|
310
src/pages/PrintView/ReportPrintView.vue
Normal file
310
src/pages/PrintView/ReportPrintView.vue
Normal file
@ -0,0 +1,310 @@
|
||||
<template>
|
||||
<div class="flex flex-col w-full h-full">
|
||||
<PageHeader :title="t`Print ${title}`">
|
||||
<Button class="text-xs" type="primary" @click="savePDF">
|
||||
{{ t`Save as PDF` }}
|
||||
</Button>
|
||||
</PageHeader>
|
||||
|
||||
<div class="outer-container">
|
||||
<!-- Report Print Display Area -->
|
||||
<div
|
||||
class="p-4 bg-gray-25 overflow-auto flex justify-center custom-scroll"
|
||||
>
|
||||
<!-- Report Print Display Container -->
|
||||
<ScaledContainer
|
||||
class="shadow-lg border bg-white"
|
||||
ref="scaledContainer"
|
||||
:scale="scale"
|
||||
:width="size.width"
|
||||
:height="size.height"
|
||||
:show-overflow="true"
|
||||
>
|
||||
<div class="bg-white mx-auto">
|
||||
<div class="p-2">
|
||||
<div class="font-semibold text-xl w-full flex justify-between">
|
||||
<h1>
|
||||
{{ `${fyo.singles.PrintSettings?.companyName}` }}
|
||||
</h1>
|
||||
<p class="text-gray-600">
|
||||
{{ title }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Report Data -->
|
||||
<div class="grid" :style="rowStyles">
|
||||
<template v-for="(row, r) of matrix" :key="`row-${r}`">
|
||||
<div
|
||||
v-for="(cell, c) of row"
|
||||
:key="`cell-${r}.${c}`"
|
||||
:class="cellClasses(cell.idx, r)"
|
||||
class="text-sm p-2"
|
||||
style="min-height: 2rem"
|
||||
>
|
||||
{{ cell.value }}
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="border-t p-2">
|
||||
<p class="text-xs text-right w-full">
|
||||
{{ fyo.format(new Date(), 'Datetime') }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</ScaledContainer>
|
||||
</div>
|
||||
|
||||
<!-- Report Print Settings -->
|
||||
<div class="border-l flex flex-col" v-if="report">
|
||||
<p class="p-4 text-sm text-gray-600">
|
||||
{{
|
||||
[
|
||||
t`Hidden values will be visible on Print on.`,
|
||||
t`Report will use more than one page if required.`,
|
||||
].join(' ')
|
||||
}}
|
||||
</p>
|
||||
<!-- Row Selection -->
|
||||
<div class="p-4 border-t">
|
||||
<Int
|
||||
:show-label="true"
|
||||
:border="true"
|
||||
:df="{
|
||||
label: t`Start From Row Index`,
|
||||
fieldtype: 'Int',
|
||||
fieldname: 'numRows',
|
||||
minvalue: 1,
|
||||
maxvalue: report?.reportData.length ?? 1000,
|
||||
}"
|
||||
:value="start"
|
||||
@change="(v) => (start = v)"
|
||||
/>
|
||||
<Int
|
||||
class="mt-4"
|
||||
:show-label="true"
|
||||
:border="true"
|
||||
:df="{
|
||||
label: t`Number of Rows`,
|
||||
fieldtype: 'Int',
|
||||
fieldname: 'numRows',
|
||||
minvalue: 0,
|
||||
maxvalue: report?.reportData.length ?? 1000,
|
||||
}"
|
||||
:value="limit"
|
||||
@change="(v) => (limit = v)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Size Selection -->
|
||||
<div class="border-t p-4">
|
||||
<Select
|
||||
:show-label="true"
|
||||
:border="true"
|
||||
:df="printSizeDf"
|
||||
:value="printSize"
|
||||
@change="(v) => (printSize = v)"
|
||||
/>
|
||||
<Check
|
||||
class="mt-4"
|
||||
:show-label="true"
|
||||
:border="true"
|
||||
:df="{
|
||||
label: t`Is Landscape`,
|
||||
fieldname: 'isLandscape',
|
||||
fieldtype: 'Check',
|
||||
}"
|
||||
:value="isLandscape"
|
||||
@change="(v) => (isLandscape = v)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Pick Columns -->
|
||||
<div class="border-t p-4">
|
||||
<h2 class="text-sm text-gray-600">
|
||||
{{ t`Pick Columns` }}
|
||||
</h2>
|
||||
<div class="border rounded grid grid-cols-2 mt-1">
|
||||
<Check
|
||||
v-for="(col, i) of report?.columns"
|
||||
:show-label="true"
|
||||
:key="col.fieldname"
|
||||
:df="{
|
||||
label: col.label,
|
||||
fieldname: col.fieldname,
|
||||
fieldtype: 'Check',
|
||||
}"
|
||||
:value="columnSelection[i]"
|
||||
@change="(v) => (columnSelection[i] = v)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { Report } from 'reports/Report';
|
||||
import { reports } from 'reports/index';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import Check from 'src/components/Controls/Check.vue';
|
||||
import Int from 'src/components/Controls/Int.vue';
|
||||
import PageHeader from 'src/components/PageHeader.vue';
|
||||
import { getReport } from 'src/utils/misc';
|
||||
import { PropType, defineComponent } from 'vue';
|
||||
import ScaledContainer from '../TemplateBuilder/ScaledContainer.vue';
|
||||
import { getPathAndMakePDF } from 'src/utils/printTemplates';
|
||||
import { OptionField } from 'schemas/types';
|
||||
import { paperSizeMap, printSizes } from 'src/utils/ui';
|
||||
import Select from 'src/components/Controls/Select.vue';
|
||||
import { showSidebar } from 'src/utils/refs';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
reportName: {
|
||||
type: String as PropType<keyof typeof reports>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
start: 1,
|
||||
limit: 0,
|
||||
printSize: 'A4' as typeof printSizes[number],
|
||||
isLandscape: false,
|
||||
scale: 0.65,
|
||||
report: null as null | Report,
|
||||
columnSelection: [] as boolean[],
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
this.report = await getReport(this.reportName);
|
||||
this.limit = this.report.reportData.length;
|
||||
this.columnSelection = this.report.columns.map(() => true);
|
||||
this.setScale();
|
||||
},
|
||||
watch: {
|
||||
size() {
|
||||
this.setScale();
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
title(): string {
|
||||
return reports[this.reportName]?.title ?? this.t`Report`;
|
||||
},
|
||||
printSizeDf(): OptionField {
|
||||
return {
|
||||
label: 'Print Size',
|
||||
fieldname: 'printSize',
|
||||
fieldtype: 'Select',
|
||||
options: printSizes
|
||||
.filter((p) => p !== 'Custom')
|
||||
.map((name) => ({ value: name, label: name })),
|
||||
};
|
||||
},
|
||||
matrix(): { value: string; idx: number }[][] {
|
||||
if (!this.report) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const columns = this.report.columns
|
||||
.map((col, idx) => ({ value: col.label, idx }))
|
||||
.filter((_, i) => this.columnSelection[i]);
|
||||
|
||||
const matrix: { value: string; idx: number }[][] = [columns];
|
||||
const start = Math.max(this.start - 1, 1);
|
||||
const end = Math.min(start + this.limit, this.report.reportData.length);
|
||||
for (const i in this.report.reportData.slice(start, end)) {
|
||||
const row = this.report.reportData[Number(i) + start];
|
||||
|
||||
matrix.push([]);
|
||||
for (const j in row.cells) {
|
||||
if (!this.columnSelection[j]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = row.cells[j].value;
|
||||
matrix.at(-1)?.push({ value, idx: Number(j) });
|
||||
}
|
||||
}
|
||||
|
||||
return matrix;
|
||||
},
|
||||
rowStyles(): Record<string, string> {
|
||||
const style: Record<string, string> = {};
|
||||
const numColumns = this.columnSelection.filter(Boolean).length;
|
||||
style['grid-template-columns'] = `repeat(${numColumns}, minmax(0, auto))`;
|
||||
return style;
|
||||
},
|
||||
size(): { width: number; height: number } {
|
||||
const size = paperSizeMap[this.printSize];
|
||||
const long = size.width > size.height ? size.width : size.height;
|
||||
const short = size.width <= size.height ? size.width : size.height;
|
||||
|
||||
if (this.isLandscape) {
|
||||
return { width: long, height: short };
|
||||
}
|
||||
|
||||
return { width: short, height: long };
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setScale() {
|
||||
const width = this.size.width * 37.2;
|
||||
let containerWidth = window.innerWidth - 26 * 16;
|
||||
if (showSidebar.value) {
|
||||
containerWidth -= 12 * 16;
|
||||
}
|
||||
|
||||
this.scale = Math.min(containerWidth / width, 1);
|
||||
},
|
||||
async savePDF(): Promise<void> {
|
||||
// @ts-ignore
|
||||
const innerHTML = this.$refs.scaledContainer.$el.children[0].innerHTML;
|
||||
if (typeof innerHTML !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const name = this.title + ' - ' + this.fyo.format(new Date(), 'Date');
|
||||
await getPathAndMakePDF(
|
||||
name,
|
||||
innerHTML,
|
||||
this.size.width,
|
||||
this.size.height
|
||||
);
|
||||
},
|
||||
cellClasses(cIdx: number, rIdx: number): string[] {
|
||||
const classes: string[] = [];
|
||||
if (!this.report) {
|
||||
return classes;
|
||||
}
|
||||
|
||||
const col = this.report.columns[cIdx];
|
||||
const isFirst = cIdx === 0;
|
||||
if (col.align) {
|
||||
classes.push(`text-${col.align}`);
|
||||
}
|
||||
|
||||
if (rIdx === 0) {
|
||||
classes.push('font-semibold');
|
||||
}
|
||||
|
||||
classes.push('border-t');
|
||||
if (!isFirst) {
|
||||
classes.push('border-l');
|
||||
}
|
||||
|
||||
return classes;
|
||||
},
|
||||
},
|
||||
components: { PageHeader, Button, Check, Int, ScaledContainer, Select },
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.outer-container {
|
||||
display: grid;
|
||||
grid-template-columns: auto var(--w-quick-edit);
|
||||
@apply h-full overflow-auto;
|
||||
}
|
||||
</style>
|
@ -11,6 +11,14 @@
|
||||
>
|
||||
{{ group.group }}
|
||||
</DropdownWithActions>
|
||||
<Button
|
||||
ref="printButton"
|
||||
:icon="true"
|
||||
:title="t`Open Report Print View`"
|
||||
@click="routeTo(`/report-print/${reportClassName}`)"
|
||||
>
|
||||
<feather-icon name="printer" class="w-4 h-4"></feather-icon>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
|
||||
<!-- Filters -->
|
||||
@ -28,7 +36,7 @@
|
||||
:df="field"
|
||||
:value="report.get(field.fieldname)"
|
||||
:read-only="loading"
|
||||
@change="async (value) => await report.set(field.fieldname, value)"
|
||||
@change="async (value) => await report?.set(field.fieldname, value)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -36,22 +44,33 @@
|
||||
<ListReport v-if="report" :report="report" class="" />
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
<script lang="ts">
|
||||
import { computed } from '@vue/reactivity';
|
||||
import { t } from 'fyo';
|
||||
import { reports } from 'reports';
|
||||
import { Report } from 'reports/Report';
|
||||
import Button from 'src/components/Button.vue';
|
||||
import FormControl from 'src/components/Controls/FormControl.vue';
|
||||
import DropdownWithActions from 'src/components/DropdownWithActions.vue';
|
||||
import PageHeader from 'src/components/PageHeader.vue';
|
||||
import ListReport from 'src/components/Report/ListReport.vue';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { docsPathMap } from 'src/utils/misc';
|
||||
import { shortcutsKey } from 'src/utils/injectionKeys';
|
||||
import { docsPathMap, getReport } from 'src/utils/misc';
|
||||
import { docsPathRef } from 'src/utils/refs';
|
||||
import { defineComponent } from 'vue';
|
||||
import { ActionGroup } from 'src/utils/types';
|
||||
import { routeTo } from 'src/utils/ui';
|
||||
import { PropType, defineComponent, inject } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return { shortcuts: inject(shortcutsKey) };
|
||||
},
|
||||
props: {
|
||||
reportClassName: String,
|
||||
reportClassName: {
|
||||
type: String as PropType<keyof typeof reports>,
|
||||
required: true,
|
||||
},
|
||||
defaultFilters: {
|
||||
type: String,
|
||||
default: '{}',
|
||||
@ -60,7 +79,7 @@ export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
report: null,
|
||||
report: null as null | Report,
|
||||
};
|
||||
},
|
||||
provide() {
|
||||
@ -68,27 +87,40 @@ export default defineComponent({
|
||||
report: computed(() => this.report),
|
||||
};
|
||||
},
|
||||
components: { PageHeader, FormControl, ListReport, DropdownWithActions },
|
||||
components: {
|
||||
PageHeader,
|
||||
FormControl,
|
||||
ListReport,
|
||||
DropdownWithActions,
|
||||
Button,
|
||||
},
|
||||
async activated() {
|
||||
docsPathRef.value = docsPathMap[this.reportClassName] ?? docsPathMap.Reports;
|
||||
docsPathRef.value =
|
||||
docsPathMap[this.reportClassName] ?? docsPathMap.Reports!;
|
||||
await this.setReportData();
|
||||
|
||||
const filters = JSON.parse(this.defaultFilters);
|
||||
const filterKeys = Object.keys(filters);
|
||||
for (const key of filterKeys) {
|
||||
await this.report.set(key, filters[key]);
|
||||
await this.report?.set(key, filters[key]);
|
||||
}
|
||||
|
||||
if (filterKeys.length) {
|
||||
await this.report.updateData();
|
||||
await this.report?.updateData();
|
||||
}
|
||||
|
||||
if (fyo.store.isDevelopment) {
|
||||
// @ts-ignore
|
||||
window.rep = this;
|
||||
}
|
||||
|
||||
this.shortcuts?.pmod.set(this.reportClassName, ['KeyP'], () => {
|
||||
routeTo(`/report-print/${this.reportClassName}`);
|
||||
});
|
||||
},
|
||||
deactivated() {
|
||||
docsPathRef.value = '';
|
||||
this.shortcuts?.delete(this.reportClassName);
|
||||
},
|
||||
computed: {
|
||||
title() {
|
||||
@ -104,24 +136,22 @@ export default defineComponent({
|
||||
acc[ac.group] ??= {
|
||||
group: ac.group,
|
||||
label: ac.label ?? '',
|
||||
e: ac.type,
|
||||
type: ac.type ?? 'secondary',
|
||||
actions: [],
|
||||
};
|
||||
|
||||
acc[ac.group].actions.push(ac);
|
||||
return acc;
|
||||
}, {});
|
||||
}, {} as Record<string, ActionGroup>);
|
||||
|
||||
return Object.values(actionsMap);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
routeTo,
|
||||
async setReportData() {
|
||||
const Report = reports[this.reportClassName];
|
||||
|
||||
if (this.report === null) {
|
||||
this.report = new Report(fyo);
|
||||
await this.report.initialize();
|
||||
this.report = await getReport(this.reportClassName);
|
||||
}
|
||||
|
||||
if (!this.report.reportData.length) {
|
||||
|
@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div class="overflow-hidden" :style="outerContainerStyle">
|
||||
<div :style="innerContainerStyle">
|
||||
<div
|
||||
:style="innerContainerStyle"
|
||||
:class="showOverflow ? 'overflow-auto no-scrollbar' : ''"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
@ -21,6 +24,7 @@ export default defineComponent({
|
||||
height: { type: Number, default: 29.7 },
|
||||
width: { type: Number, default: 21 },
|
||||
scale: { type: Number, default: 0.65 },
|
||||
showOverflow: { type: Boolean, default: false },
|
||||
},
|
||||
computed: {
|
||||
innerContainerStyle(): Record<string, string> {
|
||||
|
@ -48,165 +48,10 @@ import Button from 'src/components/Button.vue';
|
||||
import Float from 'src/components/Controls/Float.vue';
|
||||
import Select from 'src/components/Controls/Select.vue';
|
||||
import FormHeader from 'src/components/FormHeader.vue';
|
||||
import { paperSizeMap, printSizes } from 'src/utils/ui';
|
||||
import { defineComponent } from 'vue';
|
||||
|
||||
const printSizes = [
|
||||
'A0',
|
||||
'A1',
|
||||
'A2',
|
||||
'A3',
|
||||
'A4',
|
||||
'A5',
|
||||
'A6',
|
||||
'A7',
|
||||
'A8',
|
||||
'A9',
|
||||
'B0',
|
||||
'B1',
|
||||
'B2',
|
||||
'B3',
|
||||
'B4',
|
||||
'B5',
|
||||
'B6',
|
||||
'B7',
|
||||
'B8',
|
||||
'B9',
|
||||
'Letter',
|
||||
'Legal',
|
||||
'Executive',
|
||||
'C5E',
|
||||
'Comm10',
|
||||
'DLE',
|
||||
'Folio',
|
||||
'Ledger',
|
||||
'Tabloid',
|
||||
'Custom',
|
||||
] as const;
|
||||
|
||||
type SizeName = typeof printSizes[number];
|
||||
const paperSizeMap: Record<SizeName, { width: number; height: number }> = {
|
||||
A0: {
|
||||
width: 84.1,
|
||||
height: 118.9,
|
||||
},
|
||||
A1: {
|
||||
width: 59.4,
|
||||
height: 84.1,
|
||||
},
|
||||
A2: {
|
||||
width: 42,
|
||||
height: 59.4,
|
||||
},
|
||||
A3: {
|
||||
width: 29.7,
|
||||
height: 42,
|
||||
},
|
||||
A4: {
|
||||
width: 21,
|
||||
height: 29.7,
|
||||
},
|
||||
A5: {
|
||||
width: 14.8,
|
||||
height: 21,
|
||||
},
|
||||
A6: {
|
||||
width: 10.5,
|
||||
height: 14.8,
|
||||
},
|
||||
A7: {
|
||||
width: 7.4,
|
||||
height: 10.5,
|
||||
},
|
||||
A8: {
|
||||
width: 5.2,
|
||||
height: 7.4,
|
||||
},
|
||||
A9: {
|
||||
width: 3.7,
|
||||
height: 5.2,
|
||||
},
|
||||
B0: {
|
||||
width: 100,
|
||||
height: 141.4,
|
||||
},
|
||||
B1: {
|
||||
width: 70.7,
|
||||
height: 100,
|
||||
},
|
||||
B2: {
|
||||
width: 50,
|
||||
height: 70.7,
|
||||
},
|
||||
B3: {
|
||||
width: 35.3,
|
||||
height: 50,
|
||||
},
|
||||
B4: {
|
||||
width: 25,
|
||||
height: 35.3,
|
||||
},
|
||||
B5: {
|
||||
width: 17.6,
|
||||
height: 25,
|
||||
},
|
||||
B6: {
|
||||
width: 12.5,
|
||||
height: 17.6,
|
||||
},
|
||||
B7: {
|
||||
width: 8.8,
|
||||
height: 12.5,
|
||||
},
|
||||
B8: {
|
||||
width: 6.2,
|
||||
height: 8.8,
|
||||
},
|
||||
B9: {
|
||||
width: 4.4,
|
||||
height: 6.2,
|
||||
},
|
||||
Letter: {
|
||||
width: 21.59,
|
||||
height: 27.94,
|
||||
},
|
||||
Legal: {
|
||||
width: 21.59,
|
||||
height: 35.56,
|
||||
},
|
||||
Executive: {
|
||||
width: 19.05,
|
||||
height: 25.4,
|
||||
},
|
||||
C5E: {
|
||||
width: 16.3,
|
||||
height: 22.9,
|
||||
},
|
||||
Comm10: {
|
||||
width: 10.5,
|
||||
height: 24.1,
|
||||
},
|
||||
DLE: {
|
||||
width: 11,
|
||||
height: 22,
|
||||
},
|
||||
Folio: {
|
||||
width: 21,
|
||||
height: 33,
|
||||
},
|
||||
Ledger: {
|
||||
width: 43.2,
|
||||
height: 27.9,
|
||||
},
|
||||
Tabloid: {
|
||||
width: 27.9,
|
||||
height: 43.2,
|
||||
},
|
||||
Custom: {
|
||||
width: -1,
|
||||
height: -1,
|
||||
},
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
props: { doc: { type: PrintTemplate, required: true } },
|
||||
data() {
|
||||
|
@ -5,6 +5,7 @@ import GetStarted from 'src/pages/GetStarted.vue';
|
||||
import ImportWizard from 'src/pages/ImportWizard.vue';
|
||||
import ListView from 'src/pages/ListView/ListView.vue';
|
||||
import PrintView from 'src/pages/PrintView/PrintView.vue';
|
||||
import ReportPrintView from 'src/pages/PrintView/ReportPrintView.vue';
|
||||
import QuickEditForm from 'src/pages/QuickEditForm.vue';
|
||||
import Report from 'src/pages/Report.vue';
|
||||
import Settings from 'src/pages/Settings/Settings.vue';
|
||||
@ -69,6 +70,12 @@ const routes: RouteRecordRaw[] = [
|
||||
component: PrintView,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/report-print/:reportName',
|
||||
name: 'ReportPrintView',
|
||||
component: ReportPrintView,
|
||||
props: true,
|
||||
},
|
||||
{
|
||||
path: '/report/:reportClassName',
|
||||
name: 'Report',
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { Fyo } from 'fyo';
|
||||
import { ConfigFile, ConfigKeys } from 'fyo/core/types';
|
||||
import { Doc } from 'fyo/model/doc';
|
||||
import { DateTime } from 'luxon';
|
||||
import { SetupWizard } from 'models/baseModels/SetupWizard/SetupWizard';
|
||||
import { ModelNameEnum } from 'models/types';
|
||||
@ -9,6 +8,7 @@ import { Schema } from 'schemas/types';
|
||||
import { fyo } from 'src/initFyo';
|
||||
import { QueryFilter } from 'utils/db/types';
|
||||
import { PeriodKey } from './types';
|
||||
import { reports } from 'reports/index';
|
||||
|
||||
export function getDatesAndPeriodList(period: PeriodKey): {
|
||||
periodList: DateTime[];
|
||||
@ -167,3 +167,15 @@ export function getCreateFiltersFromListViewFilters(filters: QueryFilter) {
|
||||
export function getIsMac() {
|
||||
return navigator.userAgent.indexOf('Mac') !== -1;
|
||||
}
|
||||
|
||||
export async function getReport(name: keyof typeof reports) {
|
||||
const cachedReport = fyo.store.reports[name];
|
||||
if (cachedReport) {
|
||||
return cachedReport;
|
||||
}
|
||||
|
||||
const report = new reports[name](fyo);
|
||||
await report.initialize();
|
||||
fyo.store.reports[name] = report;
|
||||
return report;
|
||||
}
|
||||
|
@ -157,7 +157,8 @@ function getCreateList(fyo: Fyo): SearchItem[] {
|
||||
function getReportList(fyo: Fyo): SearchItem[] {
|
||||
const hasGstin = !!fyo.singles?.AccountingSettings?.gstin;
|
||||
const hasInventory = !!fyo.singles?.AccountingSettings?.enableInventory;
|
||||
return Object.keys(reports)
|
||||
const reportNames = Object.keys(reports) as (keyof typeof reports)[];
|
||||
return reportNames
|
||||
.filter((r) => {
|
||||
const report = reports[r];
|
||||
if (report.isInventory && !hasInventory) {
|
||||
|
159
src/utils/ui.ts
159
src/utils/ui.ts
@ -711,3 +711,162 @@ function getDocReferenceLabel(doc: Doc) {
|
||||
|
||||
return doc.name || label;
|
||||
}
|
||||
|
||||
export const printSizes = [
|
||||
'A0',
|
||||
'A1',
|
||||
'A2',
|
||||
'A3',
|
||||
'A4',
|
||||
'A5',
|
||||
'A6',
|
||||
'A7',
|
||||
'A8',
|
||||
'A9',
|
||||
'B0',
|
||||
'B1',
|
||||
'B2',
|
||||
'B3',
|
||||
'B4',
|
||||
'B5',
|
||||
'B6',
|
||||
'B7',
|
||||
'B8',
|
||||
'B9',
|
||||
'Letter',
|
||||
'Legal',
|
||||
'Executive',
|
||||
'C5E',
|
||||
'Comm10',
|
||||
'DLE',
|
||||
'Folio',
|
||||
'Ledger',
|
||||
'Tabloid',
|
||||
'Custom',
|
||||
] as const;
|
||||
|
||||
export const paperSizeMap: Record<
|
||||
typeof printSizes[number],
|
||||
{ width: number; height: number }
|
||||
> = {
|
||||
A0: {
|
||||
width: 84.1,
|
||||
height: 118.9,
|
||||
},
|
||||
A1: {
|
||||
width: 59.4,
|
||||
height: 84.1,
|
||||
},
|
||||
A2: {
|
||||
width: 42,
|
||||
height: 59.4,
|
||||
},
|
||||
A3: {
|
||||
width: 29.7,
|
||||
height: 42,
|
||||
},
|
||||
A4: {
|
||||
width: 21,
|
||||
height: 29.7,
|
||||
},
|
||||
A5: {
|
||||
width: 14.8,
|
||||
height: 21,
|
||||
},
|
||||
A6: {
|
||||
width: 10.5,
|
||||
height: 14.8,
|
||||
},
|
||||
A7: {
|
||||
width: 7.4,
|
||||
height: 10.5,
|
||||
},
|
||||
A8: {
|
||||
width: 5.2,
|
||||
height: 7.4,
|
||||
},
|
||||
A9: {
|
||||
width: 3.7,
|
||||
height: 5.2,
|
||||
},
|
||||
B0: {
|
||||
width: 100,
|
||||
height: 141.4,
|
||||
},
|
||||
B1: {
|
||||
width: 70.7,
|
||||
height: 100,
|
||||
},
|
||||
B2: {
|
||||
width: 50,
|
||||
height: 70.7,
|
||||
},
|
||||
B3: {
|
||||
width: 35.3,
|
||||
height: 50,
|
||||
},
|
||||
B4: {
|
||||
width: 25,
|
||||
height: 35.3,
|
||||
},
|
||||
B5: {
|
||||
width: 17.6,
|
||||
height: 25,
|
||||
},
|
||||
B6: {
|
||||
width: 12.5,
|
||||
height: 17.6,
|
||||
},
|
||||
B7: {
|
||||
width: 8.8,
|
||||
height: 12.5,
|
||||
},
|
||||
B8: {
|
||||
width: 6.2,
|
||||
height: 8.8,
|
||||
},
|
||||
B9: {
|
||||
width: 4.4,
|
||||
height: 6.2,
|
||||
},
|
||||
Letter: {
|
||||
width: 21.59,
|
||||
height: 27.94,
|
||||
},
|
||||
Legal: {
|
||||
width: 21.59,
|
||||
height: 35.56,
|
||||
},
|
||||
Executive: {
|
||||
width: 19.05,
|
||||
height: 25.4,
|
||||
},
|
||||
C5E: {
|
||||
width: 16.3,
|
||||
height: 22.9,
|
||||
},
|
||||
Comm10: {
|
||||
width: 10.5,
|
||||
height: 24.1,
|
||||
},
|
||||
DLE: {
|
||||
width: 11,
|
||||
height: 22,
|
||||
},
|
||||
Folio: {
|
||||
width: 21,
|
||||
height: 33,
|
||||
},
|
||||
Ledger: {
|
||||
width: 43.2,
|
||||
height: 27.9,
|
||||
},
|
||||
Tabloid: {
|
||||
width: 27.9,
|
||||
height: 43.2,
|
||||
},
|
||||
Custom: {
|
||||
width: -1,
|
||||
height: -1,
|
||||
},
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user