From 79be4266bbfb6e6c46b6d7437fc355c936b332bd Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Fri, 31 May 2024 18:12:09 +0200 Subject: [PATCH] feat: style resource printable columns --- .../scss/modules/export-resource-table.scss | 13 ++----- packages/server/src/interfaces/Model.ts | 6 ++++ .../server/src/models/Account.Settings.ts | 4 +++ .../server/src/models/Customer.Settings.ts | 24 +++++++++++++ .../server/src/models/Expense.Settings.ts | 9 ++++- packages/server/src/models/Item.Settings.ts | 9 +++++ .../src/models/ManualJournal.Settings.ts | 12 +++++++ .../src/models/SaleEstimate.Settings.ts | 13 +++++++ .../server/src/models/SaleInvoice.Settings.ts | 17 +++++++++ .../server/src/models/SaleReceipt.Settings.ts | 6 ++++ packages/server/src/models/Vendor.Settings.ts | 20 +++++++++++ .../src/services/Export/ExportService.ts | 35 ++++++++++++++++--- packages/server/src/services/Export/utils.ts | 5 ++- 13 files changed, 157 insertions(+), 16 deletions(-) diff --git a/packages/server/resources/scss/modules/export-resource-table.scss b/packages/server/resources/scss/modules/export-resource-table.scss index 5f1d9a065..aa2c5497b 100644 --- a/packages/server/resources/scss/modules/export-resource-table.scss +++ b/packages/server/resources/scss/modules/export-resource-table.scss @@ -4,16 +4,11 @@ body { font-family: system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue","Noto Sans","Liberation Sans",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"; font-size: 12px; line-height: 1.4; + margin: 0; } - -.sheet{ - padding: 20px; -} - .sheet__title{ margin-bottom: 18px; } - .sheet__title h2{ line-height: 1; margin-top: 0; @@ -23,14 +18,13 @@ body { .sheet__table { font-size: inherit; line-height: inherit; + width: 100%; } - .sheet__table { table-layout: auto; border-collapse: collapse; width: 100%; } - .sheet__table thead tr th { border-top: 1px solid #000; border-bottom: 1px solid #000; @@ -38,8 +32,7 @@ body { padding: 8px; line-height: 1.2; } - .sheet__table tbody tr td { padding: 4px 8px; - border-bottom: 1px solid #CCC; + border-bottom: 1px solid #CCC; } \ No newline at end of file diff --git a/packages/server/src/interfaces/Model.ts b/packages/server/src/interfaces/Model.ts index 93bb1f7fc..bb6e7720b 100644 --- a/packages/server/src/interfaces/Model.ts +++ b/packages/server/src/interfaces/Model.ts @@ -122,6 +122,10 @@ export type IModelMetaCollectionField = IModelMetaCollectionFieldCommon & export type IModelMetaRelationField = IModelMetaRelationFieldCommon & IModelMetaRelationEnumerationField; +interface IModelPrintMeta{ + pageTitle: string; +} + export interface IModelMeta { defaultFilterField: string; defaultSort: IModelMetaDefaultSort; @@ -134,6 +138,8 @@ export interface IModelMeta { importAggregateOn?: string; importAggregateBy?: string; + print?: IModelPrintMeta; + fields: { [key: string]: IModelMetaField }; columns: { [key: string]: IModelMetaColumn }; } diff --git a/packages/server/src/models/Account.Settings.ts b/packages/server/src/models/Account.Settings.ts index fff2ed581..0d4690041 100644 --- a/packages/server/src/models/Account.Settings.ts +++ b/packages/server/src/models/Account.Settings.ts @@ -8,6 +8,9 @@ export default { }, importable: true, exportable: true, + print: { + pageTitle: 'Chart of Accounts', + }, fields: { name: { name: 'account.field.name', @@ -133,6 +136,7 @@ export default { }, createdAt: { name: 'account.field.created_at', + printable: false, }, }, fields2: { diff --git a/packages/server/src/models/Customer.Settings.ts b/packages/server/src/models/Customer.Settings.ts index 71f631032..96e75c017 100644 --- a/packages/server/src/models/Customer.Settings.ts +++ b/packages/server/src/models/Customer.Settings.ts @@ -6,6 +6,9 @@ export default { sortOrder: 'DESC', sortField: 'created_at', }, + print: { + pageTitle: 'Customers', + }, fields: { first_name: { name: 'vendor.field.first_name', @@ -127,100 +130,121 @@ export default { balance: { name: 'vendor.field.balance', type: 'number', + accessor: 'formattedBalance', }, openingBalance: { name: 'vendor.field.opening_balance', type: 'number', + printable: false }, openingBalanceAt: { name: 'vendor.field.opening_balance_at', type: 'date', + printable: false }, currencyCode: { name: 'vendor.field.currency', type: 'text', + printable: false }, status: { name: 'vendor.field.status', + printable: false }, note: { name: 'vendor.field.note', + printable: false }, // Billing Address billingAddress1: { name: 'Billing Address 1', column: 'billing_address1', type: 'text', + printable: false }, billingAddress2: { name: 'Billing Address 2', column: 'billing_address2', type: 'text', + printable: false }, billingAddressCity: { name: 'Billing Address City', column: 'billing_address_city', type: 'text', + printable: false }, billingAddressCountry: { name: 'Billing Address Country', column: 'billing_address_country', type: 'text', + printable: false }, billingAddressPostcode: { name: 'Billing Address Postcode', column: 'billing_address_postcode', type: 'text', + printable: false }, billingAddressState: { name: 'Billing Address State', column: 'billing_address_state', type: 'text', + printable: false }, billingAddressPhone: { name: 'Billing Address Phone', column: 'billing_address_phone', type: 'text', + printable: false }, // Shipping Address shippingAddress1: { name: 'Shipping Address 1', column: 'shipping_address1', type: 'text', + printable: false }, shippingAddress2: { name: 'Shipping Address 2', column: 'shipping_address2', type: 'text', + printable: false }, shippingAddressCity: { name: 'Shipping Address City', column: 'shipping_address_city', type: 'text', + printable: false }, shippingAddressCountry: { name: 'Shipping Address Country', column: 'shipping_address_country', type: 'text', + printable: false }, shippingAddressPostcode: { name: 'Shipping Address Postcode', column: 'shipping_address_postcode', type: 'text', + printable: false }, shippingAddressPhone: { name: 'Shipping Address Phone', column: 'shipping_address_phone', type: 'text', + printable: false }, shippingAddressState: { name: 'Shipping Address State', column: 'shipping_address_state', type: 'text', + printable: false }, createdAt: { name: 'vendor.field.created_at', type: 'date', + printable: false }, }, fields2: { diff --git a/packages/server/src/models/Expense.Settings.ts b/packages/server/src/models/Expense.Settings.ts index 12c539782..0ba73c4d9 100644 --- a/packages/server/src/models/Expense.Settings.ts +++ b/packages/server/src/models/Expense.Settings.ts @@ -10,6 +10,9 @@ export default { importable: true, exportFlattenOn: 'categories', exportable: true, + print: { + pageTitle: 'Expenses', + }, fields: { payment_date: { name: 'expense.field.payment_date', @@ -67,7 +70,7 @@ export default { paymentReceive: { name: 'expense.field.payment_account', type: 'text', - accessor: 'paymentAccount.name' + accessor: 'paymentAccount.name', }, referenceNo: { name: 'expense.field.reference_no', @@ -75,15 +78,18 @@ export default { }, paymentDate: { name: 'expense.field.payment_date', + accessor: 'formattedDate', type: 'date', }, currencyCode: { name: 'expense.field.currency_code', type: 'text', + printable: false, }, exchangeRate: { name: 'expense.field.exchange_rate', type: 'number', + printable: false, }, description: { name: 'expense.field.description', @@ -111,6 +117,7 @@ export default { publish: { name: 'expense.field.publish', type: 'boolean', + printable: false, }, }, fields2: { diff --git a/packages/server/src/models/Item.Settings.ts b/packages/server/src/models/Item.Settings.ts index 9c8a50ce8..51e217dc0 100644 --- a/packages/server/src/models/Item.Settings.ts +++ b/packages/server/src/models/Item.Settings.ts @@ -127,6 +127,7 @@ export default { name: 'item.field.type', type: 'text', exportable: true, + accessor: 'typeFormatted', }, name: { name: 'item.field.name', @@ -142,11 +143,13 @@ export default { name: 'item.field.sellable', type: 'boolean', exportable: true, + printable: false, }, purchasable: { name: 'item.field.purchasable', type: 'boolean', exportable: true, + printable: false, }, sellPrice: { name: 'item.field.cost_price', @@ -163,12 +166,14 @@ export default { type: 'text', accessor: 'costAccount.name', exportable: true, + printable: false, }, sellAccount: { name: 'item.field.sell_description', type: 'text', accessor: 'sellAccount.name', exportable: true, + printable: false, }, inventoryAccount: { name: 'item.field.inventory_account', @@ -180,11 +185,13 @@ export default { name: 'Sell description', type: 'text', exportable: true, + printable: false, }, purchaseDescription: { name: 'Purchase description', type: 'text', exportable: true, + printable: false, }, quantityOnHand: { name: 'item.field.quantity_on_hand', @@ -206,11 +213,13 @@ export default { name: 'item.field.active', fieldType: 'boolean', exportable: true, + printable: false, }, createdAt: { name: 'item.field.created_at', type: 'date', exportable: true, + printable: false, }, }, fields2: { diff --git a/packages/server/src/models/ManualJournal.Settings.ts b/packages/server/src/models/ManualJournal.Settings.ts index db2712220..84ce31367 100644 --- a/packages/server/src/models/ManualJournal.Settings.ts +++ b/packages/server/src/models/ManualJournal.Settings.ts @@ -11,6 +11,11 @@ export default { importAggregator: 'group', importAggregateOn: 'entries', importAggregateBy: 'journalNumber', + + print: { + pageTitle: 'Manual Journals', + }, + fields: { date: { name: 'manual_journal.field.date', @@ -63,6 +68,7 @@ export default { date: { name: 'manual_journal.field.date', type: 'date', + accessor: 'formattedDate', }, journalNumber: { name: 'manual_journal.field.journal_number', @@ -83,10 +89,12 @@ export default { currencyCode: { name: 'manual_journal.field.currency', type: 'text', + printable: false, }, exchangeRate: { name: 'manual_journal.field.exchange_rate', type: 'number', + printable: false, }, description: { name: 'manual_journal.field.description', @@ -120,13 +128,17 @@ export default { publish: { name: 'Publish', type: 'boolean', + printable: false, }, publishedAt: { name: 'Published At', + printable: false, }, }, createdAt: { name: 'Created At', + accessor: 'formattedCreatedAt', + printable: false, }, }, fields2: { diff --git a/packages/server/src/models/SaleEstimate.Settings.ts b/packages/server/src/models/SaleEstimate.Settings.ts index a9577b4f4..5462e9717 100644 --- a/packages/server/src/models/SaleEstimate.Settings.ts +++ b/packages/server/src/models/SaleEstimate.Settings.ts @@ -11,6 +11,11 @@ export default { importAggregator: 'group', importAggregateOn: 'entries', importAggregateBy: 'estimateNumber', + + print: { + pageTitle: 'Sale Estimates' + }, + fields: { amount: { name: 'estimate.field.amount', @@ -86,11 +91,13 @@ export default { estimateDate: { name: 'Estimate Date', type: 'date', + accessor: 'formattedEstimateDate', exportable: true, }, expirationDate: { name: 'Expiration Date', type: 'date', + accessor: 'formattedExpirationDate', exportable: true, }, estimateNumber: { @@ -112,26 +119,31 @@ export default { name: 'Exchange Rate', type: 'number', exportable: true, + printable: false, }, currencyCode: { name: 'Currency', type: 'text', exportable: true, + printable: false, }, note: { name: 'Note', type: 'text', exportable: true, + printable: false, }, termsConditions: { name: 'Terms & Conditions', type: 'text', exportable: true, + printable: false, }, delivered: { name: 'Delivered', type: 'boolean', exportable: true, + printable: false, }, entries: { name: 'Entries', @@ -153,6 +165,7 @@ export default { }, description: { name: 'Item Description', + printable: false, }, amount: { name: 'Item Amount', diff --git a/packages/server/src/models/SaleInvoice.Settings.ts b/packages/server/src/models/SaleInvoice.Settings.ts index 24728522e..da6719ba2 100644 --- a/packages/server/src/models/SaleInvoice.Settings.ts +++ b/packages/server/src/models/SaleInvoice.Settings.ts @@ -11,6 +11,10 @@ export default { importAggregator: 'group', importAggregateOn: 'entries', importAggregateBy: 'invoiceNo', + + print: { + pageTitle: 'Sale invoices', + }, fields: { customer: { name: 'invoice.field.customer', @@ -94,10 +98,12 @@ export default { invoiceDate: { name: 'invoice.field.invoice_date', type: 'date', + accessor: 'invoiceDateFormatted', }, dueDate: { name: 'invoice.field.due_date', type: 'date', + accessor: 'dueDateFormatted', }, referenceNo: { name: 'invoice.field.reference_no', @@ -120,10 +126,12 @@ export default { exchangeRate: { name: 'invoice.field.exchange_rate', type: 'number', + printable: false, }, currencyCode: { name: 'invoice.field.currency', type: 'text', + printable: false, }, paidAmount: { name: 'Paid Amount', @@ -136,14 +144,17 @@ export default { invoiceMessage: { name: 'invoice.field.invoice_message', type: 'text', + printable: false, }, termsConditions: { name: 'invoice.field.terms_conditions', type: 'text', + printable: false, }, delivered: { name: 'invoice.field.delivered', type: 'boolean', + printable: false, }, entries: { name: 'Entries', @@ -165,6 +176,7 @@ export default { }, description: { name: 'Item Description', + printable: false, }, amount: { name: 'Item Amount', @@ -202,18 +214,22 @@ export default { exchangeRate: { name: 'invoice.field.exchange_rate', fieldType: 'number', + printable: false, }, currencyCode: { name: 'invoice.field.currency', fieldType: 'text', + printable: false, }, invoiceMessage: { name: 'invoice.field.invoice_message', fieldType: 'text', + printable: false, }, termsConditions: { name: 'invoice.field.terms_conditions', fieldType: 'text', + printable: false, }, entries: { name: 'invoice.field.entries', @@ -249,6 +265,7 @@ export default { delivered: { name: 'invoice.field.delivered', fieldType: 'boolean', + printable: false, }, }, }; diff --git a/packages/server/src/models/SaleReceipt.Settings.ts b/packages/server/src/models/SaleReceipt.Settings.ts index 3fecd0480..286f61ab2 100644 --- a/packages/server/src/models/SaleReceipt.Settings.ts +++ b/packages/server/src/models/SaleReceipt.Settings.ts @@ -98,6 +98,7 @@ export default { }, receiptDate: { name: 'receipt.field.receipt_date', + accessor: 'formattedReceiptDate', type: 'date', }, receiptNumber: { @@ -114,10 +115,12 @@ export default { name: 'receipt.field.receipt_message', column: 'receipt_message', type: 'text', + printable: false, }, statement: { name: 'receipt.field.statement', type: 'text', + printable: false, }, status: { name: 'receipt.field.status', @@ -127,6 +130,7 @@ export default { { key: 'closed', label: 'receipt.field.status.closed' }, ], exportable: true, + printable: false, }, entries: { name: 'Entries', @@ -148,6 +152,7 @@ export default { }, description: { name: 'Item Description', + printable: false, }, amount: { name: 'Item Amount', @@ -158,6 +163,7 @@ export default { createdAt: { name: 'receipt.field.created_at', type: 'date', + printable: false, }, }, fields2: { diff --git a/packages/server/src/models/Vendor.Settings.ts b/packages/server/src/models/Vendor.Settings.ts index 7681dfa10..cf007f278 100644 --- a/packages/server/src/models/Vendor.Settings.ts +++ b/packages/server/src/models/Vendor.Settings.ts @@ -131,21 +131,26 @@ export default { openingBalance: { name: 'vendor.field.opening_balance', type: 'number', + printable: false }, openingBalanceAt: { name: 'vendor.field.opening_balance_at', type: 'date', + printable: false }, currencyCode: { name: 'vendor.field.currency', type: 'text', + printable: false }, status: { name: 'vendor.field.status', + printable: false }, note: { name: 'vendor.field.note', type: 'text', + printable: false }, // Billing Address billingAddress1: { @@ -153,42 +158,49 @@ export default { column: 'billing_address1', type: 'text', exportable: true, + printable: false }, billingAddress2: { name: 'Billing Address 2', column: 'billing_address2', type: 'text', exportable: true, + printable: false }, billingAddressCity: { name: 'Billing Address City', column: 'billing_address_city', type: 'text', exportable: true, + printable: false }, billingAddressCountry: { name: 'Billing Address Country', column: 'billing_address_country', type: 'text', exportable: true, + printable: false }, billingAddressPostcode: { name: 'Billing Address Postcode', column: 'billing_address_postcode', type: 'text', exportable: true, + printable: false }, billingAddressState: { name: 'Billing Address State', column: 'billing_address_state', type: 'text', exportable: true, + printable: false }, billingAddressPhone: { name: 'Billing Address Phone', column: 'billing_address_phone', type: 'text', exportable: true, + printable: false }, // Shipping Address shippingAddress1: { @@ -196,47 +208,55 @@ export default { column: 'shipping_address1', type: 'text', exportable: true, + printable: false }, shippingAddress2: { name: 'Shipping Address 2', column: 'shipping_address2', type: 'text', exportable: true, + printable: false }, shippingAddressCity: { name: 'Shipping Address City', column: 'shipping_address_city', type: 'text', exportable: true, + printable: false }, shippingAddressCountry: { name: 'Shipping Address Country', column: 'shipping_address_country', type: 'text', exportable: true, + printable: false }, shippingAddressPostcode: { name: 'Shipping Address Postcode', column: 'shipping_address_postcode', type: 'text', exportable: true, + printable: false }, shippingAddressState: { name: 'Shipping Address State', column: 'shipping_address_state', type: 'text', exportable: true, + printable: false }, shippingAddressPhone: { name: 'Shipping Address Phone', column: 'shipping_address_phone', type: 'text', exportable: true, + printable: false }, createdAt: { name: 'vendor.field.created_at', type: 'date', exportable: true, + printable: false }, }, fields2: { diff --git a/packages/server/src/services/Export/ExportService.ts b/packages/server/src/services/Export/ExportService.ts index c8bb124fe..eec34c959 100644 --- a/packages/server/src/services/Export/ExportService.ts +++ b/packages/server/src/services/Export/ExportService.ts @@ -40,20 +40,22 @@ export class ExportResourceService { const data = await this.getExportableData(tenantId, resource); const transformed = this.transformExportedData(tenantId, resource, data); - const exportableColumns = this.getExportableColumns(resourceMeta); // Returns the csv, xlsx format. if (format === ExportFormat.Csv || format === ExportFormat.Xlsx) { + const exportableColumns = this.getExportableColumns(resourceMeta); const workbook = this.createWorkbook(transformed, exportableColumns); return this.exportWorkbook(workbook, format); // Returns the pdf format. } else if (format === ExportFormat.Pdf) { + const printableColumns = this.getPrintableColumns(resourceMeta); + return this.exportPdf.pdf( tenantId, - exportableColumns, + printableColumns, transformed, - 'Accounts' + resourceMeta?.print?.pageTitle ); } } @@ -146,6 +148,32 @@ export class ExportResourceService { return processColumns(resourceMeta.columns); } + private getPrintableColumns(resourceMeta: IModelMeta) { + const processColumns = ( + columns: { [key: string]: IModelMetaColumn }, + parent = '' + ) => { + return Object.entries(columns) + .filter(([_, value]) => value.printable !== false) + .flatMap(([key, value]) => { + if (value.type === 'collection' && value.collectionOf === 'object') { + return processColumns(value.columns, key); + } else { + const group = parent; + return [ + { + name: value.name, + type: value.type || 'text', + accessor: value.accessor || key, + group, + }, + ]; + } + }); + }; + return processColumns(resourceMeta.columns); + } + /** * Creates a workbook from the provided data and columns. * @param {any[]} data - The data to be included in the workbook. @@ -157,7 +185,6 @@ export class ExportResourceService { const worksheetData = data.map((item) => exportableColumns.map((col) => get(item, getDataAccessor(col))) ); - worksheetData.unshift(exportableColumns.map((col) => col.name)); const worksheet = xlsx.utils.aoa_to_sheet(worksheetData); diff --git a/packages/server/src/services/Export/utils.ts b/packages/server/src/services/Export/utils.ts index 94b395096..10ed4654c 100644 --- a/packages/server/src/services/Export/utils.ts +++ b/packages/server/src/services/Export/utils.ts @@ -35,7 +35,10 @@ export const getDataAccessor = (col: any) => { export const mapPdfRows = (columns: any, data: Record) => { return data.map((item) => { const cells = columns.map((column) => { - return { key: column.accessor, value: get(item, column.accessor) }; + return { + key: column.accessor, + value: get(item, getDataAccessor(column)), + }; }); return { cells, classNames: '' }; });