diff --git a/electron-builder.yml b/electron-builder.yml index 95647edb..bed25e95 100644 --- a/electron-builder.yml +++ b/electron-builder.yml @@ -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 diff --git a/fyo/index.ts b/fyo/index.ts index 7d8ee855..e85f1ce7 100644 --- a/fyo/index.ts +++ b/fyo/index.ts @@ -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'; @@ -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); } diff --git a/fyo/model/types.ts b/fyo/model/types.ts index 42814452..36135212 100644 --- a/fyo/model/types.ts +++ b/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'; @@ -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; diff --git a/fyo/utils/format.ts b/fyo/utils/format.ts index 335addb9..5cadb6a9 100644 --- a/fyo/utils/format.ts +++ b/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, @@ -13,7 +12,7 @@ import { } from './consts'; export function format( - value: DocValue, + value: unknown, df: string | Field | null, doc: Doc | null, fyo: Fyo @@ -45,7 +44,7 @@ export function format( } if (field.fieldtype === FieldTypeEnum.Check) { - return Boolean(value).toString(); + return titleCase(Boolean(value).toString()); } if (getIsNullOrUndef(value)) { @@ -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 ''; @@ -83,7 +87,7 @@ 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 ''; } @@ -91,9 +95,12 @@ function formatDate(value: DocValue, fyo: Fyo): string { 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 ''; } @@ -102,7 +109,7 @@ function formatDate(value: DocValue, fyo: Fyo): string { } function formatCurrency( - value: DocValue, + value: unknown, field: Field, doc: Doc | null, fyo: Fyo @@ -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)); diff --git a/main.ts b/main.ts index 956b8207..792e6dd7 100644 --- a/main.ts +++ b/main.ts @@ -4,7 +4,7 @@ import { app, BrowserWindow, BrowserWindowConstructorOptions, - protocol + protocol, } from 'electron'; import Store from 'electron-store'; import { autoUpdater } from 'electron-updater'; diff --git a/main/getPrintTemplates.ts b/main/getPrintTemplates.ts new file mode 100644 index 00000000..d0aa48df --- /dev/null +++ b/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; + } +} diff --git a/main/registerIpcMainActionListeners.ts b/main/registerIpcMainActionListeners.ts index 17561f93..0a41cb74 100644 --- a/main/registerIpcMainActionListeners.ts +++ b/main/registerIpcMainActionListeners.ts @@ -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, @@ -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) => { @@ -137,6 +138,10 @@ export default function registerIpcMainActionListeners(main: Main) { }; }); + ipcMain.handle(IPC_ACTIONS.GET_TEMPLATES, async () => { + return getTemplates(); + }); + /** * Database Related Actions */ diff --git a/models/baseModels/Account/Account.ts b/models/baseModels/Account/Account.ts index 184a6662..55ebfe16 100644 --- a/models/baseModels/Account/Account.ts +++ b/models/baseModels/Account/Account.ts @@ -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'; @@ -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 = { diff --git a/models/baseModels/Defaults/Defaults.ts b/models/baseModels/Defaults/Defaults.ts index e78c7f28..e6436a2b 100644 --- a/models/baseModels/Defaults/Defaults.ts +++ b/models/baseModels/Defaults/Defaults.ts @@ -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; @@ -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, }), @@ -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; @@ -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(), }; } diff --git a/models/baseModels/Invoice/Invoice.ts b/models/baseModels/Invoice/Invoice.ts index b929c117..4037a7ab 100644 --- a/models/baseModels/Invoice/Invoice.ts +++ b/models/baseModels/Invoice/Invoice.ts @@ -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'; diff --git a/models/baseModels/JournalEntry/JournalEntry.ts b/models/baseModels/JournalEntry/JournalEntry.ts index 7241d345..828fe9fa 100644 --- a/models/baseModels/JournalEntry/JournalEntry.ts +++ b/models/baseModels/JournalEntry/JournalEntry.ts @@ -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'; diff --git a/models/baseModels/PrintSettings/PrintSettings.ts b/models/baseModels/PrintSettings/PrintSettings.ts index a8ffea2a..bb0da046 100644 --- a/models/baseModels/PrintSettings/PrintSettings.ts +++ b/models/baseModels/PrintSettings/PrintSettings.ts @@ -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 = {}; } diff --git a/models/baseModels/PrintTemplate.ts b/models/baseModels/PrintTemplate.ts new file mode 100644 index 00000000..832aa84b --- /dev/null +++ b/models/baseModels/PrintTemplate.ts @@ -0,0 +1,81 @@ +import { Doc } from 'fyo/model/doc'; +import { SchemaMap } from 'schemas/types'; +import { ListsMap, ListViewSettings, ReadOnlyMap } from 'fyo/model/types'; +import { ModelNameEnum } from 'models/types'; +import { Fyo } from 'fyo'; + +export class PrintTemplate extends Doc { + name?: string; + type?: string; + template?: string; + isCustom?: boolean; + + override get canDelete(): boolean { + if (this.isCustom === false) { + return false; + } + + return super.canDelete; + } + + static getListViewSettings(fyo: Fyo): ListViewSettings { + return { + formRoute: (name) => `/template-builder/${name}`, + columns: [ + 'name', + { + label: fyo.t`Type`, + fieldtype: 'AutoComplete', + fieldname: 'type', + display(value) { + return fyo.schemaMap[value as string]?.label ?? ''; + }, + }, + 'isCustom', + ], + }; + } + + readOnly: ReadOnlyMap = { + name: () => !this.isCustom, + type: () => !this.isCustom, + template: () => !this.isCustom, + }; + + static lists: ListsMap = { + type(doc?: Doc) { + let enableInventory: boolean = false; + let schemaMap: SchemaMap = {}; + if (doc) { + enableInventory = !!doc.fyo.singles.AccountingSettings?.enableInventory; + schemaMap = doc.fyo.schemaMap; + } + + const models = [ + ModelNameEnum.SalesInvoice, + ModelNameEnum.PurchaseInvoice, + ModelNameEnum.JournalEntry, + ModelNameEnum.Payment, + ]; + + if (enableInventory) { + models.push( + ModelNameEnum.Shipment, + ModelNameEnum.PurchaseReceipt, + ModelNameEnum.StockMovement + ); + } + + return models.map((value) => ({ + value, + label: schemaMap[value]?.label ?? value, + })); + }, + }; + + override duplicate(): Doc { + const doc = super.duplicate() as PrintTemplate; + doc.isCustom = true; + return doc; + } +} diff --git a/models/helpers.ts b/models/helpers.ts index f66ff961..cb0c50ea 100644 --- a/models/helpers.ts +++ b/models/helpers.ts @@ -311,7 +311,6 @@ export function getDocStatusListColumn(): ColumnConfig { label: t`Status`, fieldname: 'status', fieldtype: 'Select', - size: 'small', render(doc) { const status = getDocStatus(doc); const color = statusColor[status] ?? 'gray'; diff --git a/models/index.ts b/models/index.ts index 75899c29..14a7ea4d 100644 --- a/models/index.ts +++ b/models/index.ts @@ -29,6 +29,7 @@ import { StockLedgerEntry } from './inventory/StockLedgerEntry'; import { StockMovement } from './inventory/StockMovement'; import { StockMovementItem } from './inventory/StockMovementItem'; +import { PrintTemplate } from './baseModels/PrintTemplate'; export const models = { Account, AccountingLedgerEntry, @@ -48,6 +49,7 @@ export const models = { SalesInvoice, SalesInvoiceItem, SetupWizard, + PrintTemplate, Tax, TaxSummary, // Inventory Models diff --git a/models/inventory/StockMovement.ts b/models/inventory/StockMovement.ts index 9e203d55..68574beb 100644 --- a/models/inventory/StockMovement.ts +++ b/models/inventory/StockMovement.ts @@ -80,6 +80,13 @@ export class StockMovement extends Transfer { }; static getListViewSettings(fyo: Fyo): ListViewSettings { + const movementTypeMap = { + [MovementType.MaterialIssue]: fyo.t`Material Issue`, + [MovementType.MaterialReceipt]: fyo.t`Material Receipt`, + [MovementType.MaterialTransfer]: fyo.t`Material Transfer`, + [MovementType.Manufacture]: fyo.t`Manufacture`, + }; + return { formRoute: (name) => `/edit/StockMovement/${name}`, columns: [ @@ -90,20 +97,8 @@ export class StockMovement extends Transfer { label: fyo.t`Movement Type`, fieldname: 'movementType', fieldtype: 'Select', - size: 'small', - render(doc) { - const movementType = doc.movementType as MovementType; - const label = - { - [MovementType.MaterialIssue]: fyo.t`Material Issue`, - [MovementType.MaterialReceipt]: fyo.t`Material Receipt`, - [MovementType.MaterialTransfer]: fyo.t`Material Transfer`, - [MovementType.Manufacture]: fyo.t`Manufacture`, - }[movementType] ?? ''; - - return { - template: `${label}`, - }; + display(value): string { + return movementTypeMap[value as MovementType] ?? ''; }, }, ], diff --git a/models/types.ts b/models/types.ts index d6c4d3c2..e68fea77 100644 --- a/models/types.ts +++ b/models/types.ts @@ -6,7 +6,6 @@ export enum ModelNameEnum { Address = 'Address', Batch= 'Batch', Color = 'Color', - CompanySettings = 'CompanySettings', Currency = 'Currency', GetStarted = 'GetStarted', Defaults = 'Defaults', @@ -21,6 +20,7 @@ export enum ModelNameEnum { Payment = 'Payment', PaymentFor = 'PaymentFor', PrintSettings = 'PrintSettings', + PrintTemplate = 'PrintTemplate', PurchaseInvoice = 'PurchaseInvoice', PurchaseInvoiceItem = 'PurchaseInvoiceItem', SalesInvoice = 'SalesInvoice', diff --git a/package.json b/package.json index fd65cea2..0c05a90e 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,11 @@ "test": "scripts/test.sh" }, "dependencies": { + "@codemirror/autocomplete": "^6.4.2", + "@codemirror/lang-vue": "^0.1.1", "@popperjs/core": "^2.10.2", "better-sqlite3": "^7.5.3", + "codemirror": "^6.0.1", "core-js": "^3.19.0", "electron-store": "^8.0.1", "feather-icons": "^4.28.0", diff --git a/reports/GoodsAndServiceTax/gstExporter.ts b/reports/GoodsAndServiceTax/gstExporter.ts index 01849fdd..f0d55f55 100644 --- a/reports/GoodsAndServiceTax/gstExporter.ts +++ b/reports/GoodsAndServiceTax/gstExporter.ts @@ -162,7 +162,7 @@ async function exportReport(extention: ExportExtention, report: BaseGSTR) { return; } - await saveExportData(data, filePath, report.fyo); + await saveExportData(data, filePath); report.fyo.telemetry.log(Verb.Exported, report.reportName, { extention }); } diff --git a/reports/commonExporter.ts b/reports/commonExporter.ts index e8001e54..d4cdcdc5 100644 --- a/reports/commonExporter.ts +++ b/reports/commonExporter.ts @@ -52,7 +52,7 @@ async function exportReport(extention: ExportExtention, report: Report) { return; } - await saveExportData(data, filePath, report.fyo); + await saveExportData(data, filePath); report.fyo.telemetry.log(Verb.Exported, report.reportName, { extention }); } @@ -178,7 +178,12 @@ function getValueFromCell(cell: ReportCell, displayPrecision: number) { return rawValue; } -export async function saveExportData(data: string, filePath: string, fyo: Fyo) { +export async function saveExportData( + data: string, + filePath: string, + message?: string +) { await saveData(data, filePath); - showExportInFolder(fyo.t`Export Successful`, filePath); + message ??= t`Export Successful`; + showExportInFolder(message, filePath); } diff --git a/schemas/app/CompanySettings.json b/schemas/app/CompanySettings.json deleted file mode 100644 index 61f5e62b..00000000 --- a/schemas/app/CompanySettings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "CompanySettings", - "label": "Company Settings", - "naming": "autoincrement", - "isSingle": true, - "isChild": false, - "fields": [ - { - "fieldname": "companyName", - "label": "Company Name", - "fieldtype": "Data", - "required": true - }, - { - "fieldname": "companyAddress", - "label": "Company Address", - "fieldtype": "Link", - "required": true, - "target": "Address" - } - ] -} diff --git a/schemas/app/Defaults.json b/schemas/app/Defaults.json index d68c6c79..6bd9d1c3 100644 --- a/schemas/app/Defaults.json +++ b/schemas/app/Defaults.json @@ -83,6 +83,55 @@ "label": "Purchase Receipt Terms", "fieldtype": "Text", "section": "Terms" + }, + { + "fieldname": "salesInvoicePrintTemplate", + "label": "Sales Invoice Print Template", + "fieldtype": "Link", + "target": "PrintTemplate", + "section": "Print Templates" + }, + { + "fieldname": "purchaseInvoicePrintTemplate", + "label": "Purchase Invoice Print Template", + "fieldtype": "Link", + "target": "PrintTemplate", + "section": "Print Templates" + }, + { + "fieldname": "journalEntryPrintTemplate", + "label": "Journal Entry Print Template", + "fieldtype": "Link", + "target": "PrintTemplate", + "section": "Print Templates" + }, + { + "fieldname": "paymentPrintTemplate", + "label": "Payment Print Template", + "fieldtype": "Link", + "target": "PrintTemplate", + "section": "Print Templates" + }, + { + "fieldname": "shipmentPrintTemplate", + "label": "Shipment Print Template", + "fieldtype": "Link", + "target": "PrintTemplate", + "section": "Print Templates" + }, + { + "fieldname": "purchaseReceiptPrintTemplate", + "label": "Purchase Receipt Print Template", + "fieldtype": "Link", + "target": "PrintTemplate", + "section": "Print Templates" + }, + { + "fieldname": "stockMovementPrintTemplate", + "label": "Stock Movement Print Template", + "fieldtype": "Link", + "target": "PrintTemplate", + "section": "Print Templates" } ] } diff --git a/schemas/app/PrintSettings.json b/schemas/app/PrintSettings.json index c65cebdc..345c3f46 100644 --- a/schemas/app/PrintSettings.json +++ b/schemas/app/PrintSettings.json @@ -37,28 +37,6 @@ "create": true, "section": "Contacts" }, - { - "fieldname": "template", - "label": "Template", - "placeholder": "Template", - "fieldtype": "Select", - "options": [ - { - "value": "Basic", - "label": "Basic" - }, - { - "value": "Minimal", - "label": "Minimal" - }, - { - "value": "Business", - "label": "Business" - } - ], - "default": "Basic", - "section": "Customizations" - }, { "fieldname": "color", "label": "Color", @@ -136,30 +114,6 @@ "label": "Display Logo in Invoice", "fieldtype": "Check", "section": "Customizations" - }, - { - "fieldname": "displayTaxInvoice", - "label": "Display Tax Invoice", - "fieldtype": "Check", - "section": "Customizations" - }, - { - "fieldname": "displayBatch", - "label": "Display Batch", - "fieldtype": "Check", - "section": "Customizations" } - ], - "quickEditFields": [ - "logo", - "displayLogo", - "displayTaxInvoice", - "displayBatch", - "template", - "color", - "font", - "email", - "phone", - "address" ] } diff --git a/schemas/app/PrintTemplate.json b/schemas/app/PrintTemplate.json new file mode 100644 index 00000000..ada87c7c --- /dev/null +++ b/schemas/app/PrintTemplate.json @@ -0,0 +1,34 @@ +{ + "name": "PrintTemplate", + "label": "Print Template", + "naming": "manual", + "isSingle": false, + "fields": [ + { + "fieldname": "name", + "label": "Template Name", + "fieldtype": "Data", + "required": true + }, + { + "fieldname": "type", + "label": "Template Type", + "fieldtype": "AutoComplete", + "default": "SalesInvoice", + "required": true + }, + { + "fieldname": "template", + "label": "Template", + "fieldtype": "Text", + "required": true + }, + { + "fieldname": "isCustom", + "label": "Is Custom", + "fieldtype": "Check", + "default": true, + "readOnly": true + } + ] +} diff --git a/schemas/schemas.ts b/schemas/schemas.ts index 8d4fdf0f..811049e9 100644 --- a/schemas/schemas.ts +++ b/schemas/schemas.ts @@ -2,11 +2,12 @@ import Account from './app/Account.json'; import AccountingLedgerEntry from './app/AccountingLedgerEntry.json'; import AccountingSettings from './app/AccountingSettings.json'; import Address from './app/Address.json'; +import Batch from './app/Batch.json'; import Color from './app/Color.json'; -import CompanySettings from './app/CompanySettings.json'; import Currency from './app/Currency.json'; import Defaults from './app/Defaults.json'; import GetStarted from './app/GetStarted.json'; +import InventorySettings from './app/inventory/InventorySettings.json'; import Location from './app/inventory/Location.json'; import PurchaseReceipt from './app/inventory/PurchaseReceipt.json'; import PurchaseReceiptItem from './app/inventory/PurchaseReceiptItem.json'; @@ -17,6 +18,7 @@ import StockMovement from './app/inventory/StockMovement.json'; import StockMovementItem from './app/inventory/StockMovementItem.json'; import StockTransfer from './app/inventory/StockTransfer.json'; import StockTransferItem from './app/inventory/StockTransferItem.json'; +import UOMConversionItem from './app/inventory/UOMConversionItem.json'; import Invoice from './app/Invoice.json'; import InvoiceItem from './app/InvoiceItem.json'; import Item from './app/Item.json'; @@ -28,6 +30,7 @@ import Party from './app/Party.json'; import Payment from './app/Payment.json'; import PaymentFor from './app/PaymentFor.json'; import PrintSettings from './app/PrintSettings.json'; +import PrintTemplate from './app/PrintTemplate.json'; import PurchaseInvoice from './app/PurchaseInvoice.json'; import PurchaseInvoiceItem from './app/PurchaseInvoiceItem.json'; import SalesInvoice from './app/SalesInvoice.json'; @@ -37,7 +40,6 @@ import Tax from './app/Tax.json'; import TaxDetail from './app/TaxDetail.json'; import TaxSummary from './app/TaxSummary.json'; import UOM from './app/UOM.json'; -import UOMConversionItem from './app/inventory/UOMConversionItem.json'; import PatchRun from './core/PatchRun.json'; import SingleValue from './core/SingleValue.json'; import SystemSettings from './core/SystemSettings.json'; @@ -46,8 +48,6 @@ import child from './meta/child.json'; import submittable from './meta/submittable.json'; import tree from './meta/tree.json'; import { Schema, SchemaStub } from './types'; -import InventorySettings from './app/inventory/InventorySettings.json'; -import Batch from './app/Batch.json' export const coreSchemas: Schema[] = [ PatchRun as Schema, @@ -66,6 +66,7 @@ export const appSchemas: Schema[] | SchemaStub[] = [ Misc as Schema, SetupWizard as Schema, GetStarted as Schema, + PrintTemplate as Schema, Color as Schema, Currency as Schema, @@ -73,7 +74,6 @@ export const appSchemas: Schema[] | SchemaStub[] = [ NumberSeries as Schema, PrintSettings as Schema, - CompanySettings as Schema, Account as Schema, AccountingSettings as Schema, @@ -116,5 +116,5 @@ export const appSchemas: Schema[] | SchemaStub[] = [ PurchaseReceipt as Schema, PurchaseReceiptItem as Schema, - Batch as Schema + Batch as Schema, ]; diff --git a/src/App.vue b/src/App.vue index f3312327..ff8357f3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -54,6 +54,7 @@ import './styles/index.css'; import { initializeInstance } from './utils/initialization'; import { checkForUpdates } from './utils/ipcCalls'; import { updateConfigFiles } from './utils/misc'; +import { updatePrintTemplates } from './utils/printTemplates'; import { Search } from './utils/search'; import { setGlobalShortcuts } from './utils/shortcuts'; import { routeTo } from './utils/ui'; @@ -128,7 +129,7 @@ export default { 'companyName' ); await this.setSearcher(); - await updateConfigFiles(fyo); + updateConfigFiles(fyo); }, async setSearcher() { this.searcher = new Search(fyo); @@ -160,6 +161,7 @@ export default { } await initializeInstance(filePath, false, countryCode, fyo); + await updatePrintTemplates(fyo); await this.setDesk(filePath); }, async setDeskRoute() { diff --git a/src/components/Controls/AutoComplete.vue b/src/components/Controls/AutoComplete.vue index 3c6ad2a0..8b7ca303 100644 --- a/src/components/Controls/AutoComplete.vue +++ b/src/components/Controls/AutoComplete.vue @@ -13,6 +13,7 @@
({}) }, textRight: { type: [null, Boolean], default: null }, readOnly: { type: [null, Boolean], default: null }, required: { type: [null, Boolean], default: null }, diff --git a/src/components/Controls/Color.vue b/src/components/Controls/Color.vue index 88fb579f..9486616d 100644 --- a/src/components/Controls/Color.vue +++ b/src/components/Controls/Color.vue @@ -44,6 +44,7 @@ :placeholder="t`Custom Hex`" :class="[inputClasses, containerClasses]" :value="value" + style="padding: 0" @change="(e) => setColorValue(e.target.value)" />
diff --git a/src/components/Controls/FormControl.vue b/src/components/Controls/FormControl.vue index 323430ca..3740c9b4 100644 --- a/src/components/Controls/FormControl.vue +++ b/src/components/Controls/FormControl.vue @@ -54,6 +54,9 @@ export default { input.value = ''; } }, + select() { + this.$refs.control.$refs?.input?.select() + }, focus() { this.$refs.control.focus(); }, diff --git a/src/components/FormContainer.vue b/src/components/FormContainer.vue index 2ad11f78..5e940c0a 100644 --- a/src/components/FormContainer.vue +++ b/src/components/FormContainer.vue @@ -3,6 +3,9 @@
+ diff --git a/src/components/HorizontalResizer.vue b/src/components/HorizontalResizer.vue new file mode 100644 index 00000000..9674b5cd --- /dev/null +++ b/src/components/HorizontalResizer.vue @@ -0,0 +1,118 @@ + + diff --git a/src/components/PageHeader.vue b/src/components/PageHeader.vue index fd436dc2..31076740 100644 --- a/src/components/PageHeader.vue +++ b/src/components/PageHeader.vue @@ -8,14 +8,17 @@ >

{{ title }}

+
+ +
diff --git a/src/components/SalesInvoice/Templates/BaseTemplate.vue b/src/components/SalesInvoice/Templates/BaseTemplate.vue deleted file mode 100644 index e7b90e30..00000000 --- a/src/components/SalesInvoice/Templates/BaseTemplate.vue +++ /dev/null @@ -1,109 +0,0 @@ - diff --git a/src/components/SalesInvoice/Templates/Basic.vue b/src/components/SalesInvoice/Templates/Basic.vue deleted file mode 100644 index 04fc85fe..00000000 --- a/src/components/SalesInvoice/Templates/Basic.vue +++ /dev/null @@ -1,172 +0,0 @@ - - - diff --git a/src/components/SalesInvoice/Templates/Business.vue b/src/components/SalesInvoice/Templates/Business.vue deleted file mode 100644 index b75c7e36..00000000 --- a/src/components/SalesInvoice/Templates/Business.vue +++ /dev/null @@ -1,165 +0,0 @@ - - diff --git a/src/components/SalesInvoice/Templates/Minimal.vue b/src/components/SalesInvoice/Templates/Minimal.vue deleted file mode 100644 index 9d2ec8e8..00000000 --- a/src/components/SalesInvoice/Templates/Minimal.vue +++ /dev/null @@ -1,194 +0,0 @@ - - diff --git a/src/components/ShortcutKeys.vue b/src/components/ShortcutKeys.vue new file mode 100644 index 00000000..3507ceda --- /dev/null +++ b/src/components/ShortcutKeys.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/ShortcutsHelper.vue b/src/components/ShortcutsHelper.vue index 0c8321e6..a1132041 100644 --- a/src/components/ShortcutsHelper.vue +++ b/src/components/ShortcutsHelper.vue @@ -22,29 +22,7 @@ class="grid gap-4 items-start" style="grid-template-columns: 6rem auto" > - -
- {{ k }} -
+
{{ s.description }}
@@ -63,8 +41,10 @@ diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue index e4cf08d3..c01dde77 100644 --- a/src/components/Sidebar.vue +++ b/src/components/Sidebar.vue @@ -145,7 +145,7 @@

dev mode @@ -165,7 +165,7 @@ m-4 rtl-rotate-180 " - @click="$emit('toggle-sidebar')" + @click="() => toggleSidebar()" > @@ -182,7 +182,7 @@ import { fyo } from 'src/initFyo'; import { openLink } from 'src/utils/ipcCalls'; import { docsPathRef } from 'src/utils/refs'; import { getSidebarConfig } from 'src/utils/sidebarConfig'; -import { routeTo } from 'src/utils/ui'; +import { routeTo, toggleSidebar } from 'src/utils/ui'; import router from '../router'; import Icon from './Icon.vue'; import Modal from './Modal.vue'; @@ -191,7 +191,7 @@ import ShortcutsHelper from './ShortcutsHelper.vue'; export default { components: [Button], inject: ['languageDirection', 'shortcuts'], - emits: ['change-db-file', 'toggle-sidebar'], + emits: ['change-db-file'], data() { return { companyName: '', @@ -222,7 +222,7 @@ export default { this.shortcuts.shift.set(['KeyH'], () => { if (document.body === document.activeElement) { - this.$emit('toggle-sidebar'); + this.toggleSidebar(); } }); this.shortcuts.set(['F1'], () => this.openDocumentation()); @@ -234,6 +234,7 @@ export default { methods: { routeTo, reportIssue, + toggleSidebar, openDocumentation() { openLink('https://docs.frappebooks.com/' + docsPathRef.value); }, diff --git a/src/errorHandling.ts b/src/errorHandling.ts index 2d67e83e..784062c1 100644 --- a/src/errorHandling.ts +++ b/src/errorHandling.ts @@ -94,11 +94,15 @@ export async function handleError( } export async function handleErrorWithDialog( - error: Error, + error: unknown, doc?: Doc, reportError?: false, dontThrow?: false ) { + if (!(error instanceof Error)) { + return; + } + const errorMessage = getErrorMessage(error, doc); await handleError(false, error, { errorMessage, doc }); diff --git a/src/pages/CommonForm/CommonForm.vue b/src/pages/CommonForm/CommonForm.vue index 124ea130..7e9389a3 100644 --- a/src/pages/CommonForm/CommonForm.vue +++ b/src/pages/CommonForm/CommonForm.vue @@ -1,7 +1,16 @@