Skip to content

Commit

Permalink
Merge pull request #558 from frappe/custom-templates
Browse files Browse the repository at this point in the history
feat: Template Builder
  • Loading branch information
18alantom committed Mar 14, 2023
2 parents 205ef4a + 94f3425 commit bc1e8d2
Show file tree
Hide file tree
Showing 74 changed files with 3,264 additions and 1,121 deletions.
1 change: 1 addition & 0 deletions electron-builder.yml
Expand Up @@ -5,6 +5,7 @@ extraResources:
[
{ from: 'log_creds.txt', to: '../creds/log_creds.txt' },
{ from: 'translations', to: '../translations' },
{ from: 'templates', to: '../templates' },
]
mac:
type: distribution
Expand Down
4 changes: 2 additions & 2 deletions fyo/index.ts
Expand Up @@ -13,7 +13,7 @@ import { TelemetryManager } from './telemetry/telemetry';
import {
DEFAULT_CURRENCY,
DEFAULT_DISPLAY_PRECISION,
DEFAULT_INTERNAL_PRECISION
DEFAULT_INTERNAL_PRECISION,
} from './utils/consts';
import * as errors from './utils/errors';
import { format } from './utils/format';
Expand Down Expand Up @@ -88,7 +88,7 @@ export class Fyo {
return this.db.fieldMap;
}

format(value: DocValue, field: string | Field, doc?: Doc) {
format(value: unknown, field: string | Field, doc?: Doc) {
return format(value, field, doc ?? null, this);
}

Expand Down
8 changes: 4 additions & 4 deletions fyo/model/types.ts
@@ -1,3 +1,4 @@
import { Fyo } from 'fyo';
import { DocValue, DocValueMap } from 'fyo/core/types';
import SystemSettings from 'fyo/models/SystemSettings';
import { FieldType, Schema, SelectOption } from 'schemas/types';
Expand Down Expand Up @@ -76,13 +77,12 @@ export interface RenderData {
[key: string]: DocValue | Schema
}

export interface ColumnConfig {
export type ColumnConfig = {
label: string;
fieldtype: FieldType;
fieldname?: string;
size?: string;
fieldname: string;
render?: (doc: RenderData) => { template: string };
getValue?: (doc: Doc) => string;
display?: (value: unknown, fyo: Fyo) => string;
}

export type ListViewColumn = string | ColumnConfig;
Expand Down
37 changes: 22 additions & 15 deletions fyo/utils/format.ts
@@ -1,10 +1,9 @@
import { Fyo } from 'fyo';
import { DocValue } from 'fyo/core/types';
import { Doc } from 'fyo/model/doc';
import { DateTime } from 'luxon';
import { Money } from 'pesa';
import { Field, FieldType, FieldTypeEnum } from 'schemas/types';
import { getIsNullOrUndef, safeParseFloat } from 'utils';
import { getIsNullOrUndef, safeParseFloat, titleCase } from 'utils';
import {
DEFAULT_CURRENCY,
DEFAULT_DATE_FORMAT,
Expand All @@ -13,7 +12,7 @@ import {
} from './consts';

export function format(
value: DocValue,
value: unknown,
df: string | Field | null,
doc: Doc | null,
fyo: Fyo
Expand Down Expand Up @@ -45,7 +44,7 @@ export function format(
}

if (field.fieldtype === FieldTypeEnum.Check) {
return Boolean(value).toString();
return titleCase(Boolean(value).toString());
}

if (getIsNullOrUndef(value)) {
Expand All @@ -55,26 +54,31 @@ export function format(
return String(value);
}

function toDatetime(value: DocValue) {
function toDatetime(value: unknown): DateTime | null {
if (typeof value === 'string') {
return DateTime.fromISO(value);
} else if (value instanceof Date) {
return DateTime.fromJSDate(value);
} else {
} else if (typeof value === 'number') {
return DateTime.fromSeconds(value as number);
}

return null;
}

function formatDatetime(value: DocValue, fyo: Fyo): string {
function formatDatetime(value: unknown, fyo: Fyo): string {
if (value == null) {
return '';
}

const dateFormat =
(fyo.singles.SystemSettings?.dateFormat as string) ?? DEFAULT_DATE_FORMAT;
const formattedDatetime = toDatetime(value).toFormat(
`${dateFormat} HH:mm:ss`
);
const dateTime = toDatetime(value);
if (!dateTime) {
return '';
}

const formattedDatetime = dateTime.toFormat(`${dateFormat} HH:mm:ss`);

if (value === 'Invalid DateTime') {
return '';
Expand All @@ -83,17 +87,20 @@ function formatDatetime(value: DocValue, fyo: Fyo): string {
return formattedDatetime;
}

function formatDate(value: DocValue, fyo: Fyo): string {
function formatDate(value: unknown, fyo: Fyo): string {
if (value == null) {
return '';
}

const dateFormat =
(fyo.singles.SystemSettings?.dateFormat as string) ?? DEFAULT_DATE_FORMAT;

const dateValue: DateTime = toDatetime(value);
const dateTime = toDatetime(value);
if (!dateTime) {
return '';
}

const formattedDate = dateValue.toFormat(dateFormat);
const formattedDate = dateTime.toFormat(dateFormat);
if (value === 'Invalid DateTime') {
return '';
}
Expand All @@ -102,7 +109,7 @@ function formatDate(value: DocValue, fyo: Fyo): string {
}

function formatCurrency(
value: DocValue,
value: unknown,
field: Field,
doc: Doc | null,
fyo: Fyo
Expand All @@ -125,7 +132,7 @@ function formatCurrency(
return valueString;
}

function formatNumber(value: DocValue, fyo: Fyo): string {
function formatNumber(value: unknown, fyo: Fyo): string {
const numberFormatter = getNumberFormatter(fyo);
if (typeof value === 'number') {
value = fyo.pesa(value.toFixed(20));
Expand Down
2 changes: 1 addition & 1 deletion main.ts
Expand Up @@ -4,7 +4,7 @@ import {
app,
BrowserWindow,
BrowserWindowConstructorOptions,
protocol
protocol,
} from 'electron';
import Store from 'electron-store';
import { autoUpdater } from 'electron-updater';
Expand Down
41 changes: 41 additions & 0 deletions main/getPrintTemplates.ts
@@ -0,0 +1,41 @@
import fs from 'fs/promises';
import path from 'path';
import { TemplateFile } from 'utils/types';

export async function getTemplates() {
const paths = await getPrintTemplatePaths();
if (!paths) {
return [];
}

const templates: TemplateFile[] = [];
for (const file of paths.files) {
const filePath = path.join(paths.root, file);
const template = await fs.readFile(filePath, 'utf-8');
const { mtime } = await fs.stat(filePath);
templates.push({ template, file, modified: mtime.toISOString() });
}

return templates;
}

async function getPrintTemplatePaths(): Promise<{
files: string[];
root: string;
} | null> {
let root = path.join(process.resourcesPath, `../templates`);

try {
const files = await fs.readdir(root);
return { files, root };
} catch {
root = path.join(__dirname, `../templates`);
}

try {
const files = await fs.readdir(root);
return { files, root };
} catch {
return null;
}
}
7 changes: 6 additions & 1 deletion main/registerIpcMainActionListeners.ts
Expand Up @@ -10,6 +10,7 @@ import { DatabaseMethod } from '../utils/db/types';
import { IPC_ACTIONS } from '../utils/messages';
import { getUrlAndTokenString, sendError } from './contactMothership';
import { getLanguageMap } from './getLanguageMap';
import { getTemplates } from './getPrintTemplates';
import {
getConfigFilesWithModified,
getErrorHandledReponse,
Expand Down Expand Up @@ -117,7 +118,7 @@ export default function registerIpcMainActionListeners(main: Main) {
);

ipcMain.handle(IPC_ACTIONS.GET_CREDS, async (event) => {
return await getUrlAndTokenString();
return getUrlAndTokenString();
});

ipcMain.handle(IPC_ACTIONS.DELETE_FILE, async (_, filePath) => {
Expand All @@ -137,6 +138,10 @@ export default function registerIpcMainActionListeners(main: Main) {
};
});

ipcMain.handle(IPC_ACTIONS.GET_TEMPLATES, async () => {
return getTemplates();
});

/**
* Database Related Actions
*/
Expand Down
18 changes: 18 additions & 0 deletions models/baseModels/Account/Account.ts
Expand Up @@ -7,7 +7,9 @@ import {
RequiredMap,
TreeViewSettings,
ReadOnlyMap,
FormulaMap,
} from 'fyo/model/types';
import { ModelNameEnum } from 'models/types';
import { QueryFilter } from 'utils/db/types';
import { AccountRootType, AccountRootTypeEnum, AccountType } from './types';

Expand Down Expand Up @@ -76,6 +78,22 @@ export class Account extends Doc {
};
}

formulas: FormulaMap = {
rootType: {
formula: async () => {
if (!this.parentAccount) {
return;
}

return await this.fyo.getValue(
ModelNameEnum.Account,
this.parentAccount,
'rootType'
);
},
},
};

static filters: FiltersMap = {
parentAccount: (doc: Doc) => {
const filter: QueryFilter = {
Expand Down
27 changes: 27 additions & 0 deletions models/baseModels/Defaults/Defaults.ts
Expand Up @@ -3,6 +3,7 @@ import { FiltersMap, HiddenMap } from 'fyo/model/types';
import { ModelNameEnum } from 'models/types';

export class Defaults extends Doc {
// Number Series
salesInvoiceNumberSeries?: string;
purchaseInvoiceNumberSeries?: string;
journalEntryNumberSeries?: string;
Expand All @@ -11,12 +12,23 @@ export class Defaults extends Doc {
shipmentNumberSeries?: string;
purchaseReceiptNumberSeries?: string;

// Terms
salesInvoiceTerms?: string;
purchaseInvoiceTerms?: string;
shipmentTerms?: string;
purchaseReceiptTerms?: string;

// Print Templates
salesInvoicePrintTemplate?: string;
purchaseInvoicePrintTemplate?: string;
journalEntryPrintTemplate?: string;
paymentPrintTemplate?: string;
shipmentPrintTemplate?: string;
purchaseReceiptPrintTemplate?: string;
stockMovementPrintTemplate?: string;

static commonFilters = {
// Number Series
salesInvoiceNumberSeries: () => ({
referenceType: ModelNameEnum.SalesInvoice,
}),
Expand All @@ -38,6 +50,18 @@ export class Defaults extends Doc {
purchaseReceiptNumberSeries: () => ({
referenceType: ModelNameEnum.PurchaseReceipt,
}),
// Print Templates
salesInvoicePrintTemplate: () => ({ type: ModelNameEnum.SalesInvoice }),
purchaseInvoicePrintTemplate: () => ({
type: ModelNameEnum.PurchaseInvoice,
}),
journalEntryPrintTemplate: () => ({ type: ModelNameEnum.JournalEntry }),
paymentPrintTemplate: () => ({ type: ModelNameEnum.Payment }),
shipmentPrintTemplate: () => ({ type: ModelNameEnum.Shipment }),
purchaseReceiptPrintTemplate: () => ({
type: ModelNameEnum.PurchaseReceipt,
}),
stockMovementPrintTemplate: () => ({ type: ModelNameEnum.StockMovement }),
};

static filters: FiltersMap = this.commonFilters;
Expand All @@ -53,6 +77,9 @@ export class Defaults extends Doc {
purchaseReceiptNumberSeries: this.getInventoryHidden(),
shipmentTerms: this.getInventoryHidden(),
purchaseReceiptTerms: this.getInventoryHidden(),
shipmentPrintTemplate: this.getInventoryHidden(),
purchaseReceiptPrintTemplate: this.getInventoryHidden(),
stockMovementPrintTemplate: this.getInventoryHidden(),
};
}

Expand Down
1 change: 0 additions & 1 deletion models/baseModels/Invoice/Invoice.ts
Expand Up @@ -11,7 +11,6 @@ import {
import { DEFAULT_CURRENCY } from 'fyo/utils/consts';
import { ValidationError } from 'fyo/utils/errors';
import { addItem, getExchangeRate, getNumberSeries } from 'models/helpers';
import { validateBatch } from 'models/inventory/helpers';
import { InventorySettings } from 'models/inventory/InventorySettings';
import { StockTransfer } from 'models/inventory/StockTransfer';
import { Transactional } from 'models/Transactional/Transactional';
Expand Down
2 changes: 1 addition & 1 deletion models/baseModels/JournalEntry/JournalEntry.ts
Expand Up @@ -70,8 +70,8 @@ export class JournalEntry extends Transactional {
'name',
{
label: t`Status`,
fieldname: 'status',
fieldtype: 'Select',
size: 'small',
render(doc) {
const status = getDocStatus(doc);
const color = statusColor[status] ?? 'gray';
Expand Down
4 changes: 1 addition & 3 deletions models/baseModels/PrintSettings/PrintSettings.ts
Expand Up @@ -2,7 +2,5 @@ import { Doc } from 'fyo/model/doc';
import { HiddenMap } from 'fyo/model/types';

export class PrintSettings extends Doc {
override hidden: HiddenMap = {
displayBatch: () => !this.fyo.singles.InventorySettings?.enableBatches,
};
override hidden: HiddenMap = {};
}

0 comments on commit bc1e8d2

Please sign in to comment.