diff --git a/packages/server/resources/locales/en.json b/packages/server/resources/locales/en.json index 7ba5673b9..ba4c158b8 100644 --- a/packages/server/resources/locales/en.json +++ b/packages/server/resources/locales/en.json @@ -432,6 +432,7 @@ "vendor.field.created_at": "Created at", "vendor.field.balance": "Balance", "vendor.field.status": "Status", + "vendor.field.note": "Note", "vendor.field.currency": "Currency", "vendor.field.status.active": "Active", "vendor.field.status.inactive": "Inactive", diff --git a/packages/server/src/interfaces/Model.ts b/packages/server/src/interfaces/Model.ts index 3fc4e2e6c..93bb1f7fc 100644 --- a/packages/server/src/interfaces/Model.ts +++ b/packages/server/src/interfaces/Model.ts @@ -126,9 +126,10 @@ export interface IModelMeta { defaultFilterField: string; defaultSort: IModelMetaDefaultSort; - importable?: boolean; exportable?: boolean; + exportFlattenOn?: string; + importable?: boolean; importAggregator?: string; importAggregateOn?: string; importAggregateBy?: string; @@ -174,4 +175,11 @@ interface IModelMetaColumnText { type: 'text;'; } -export type IModelMetaColumn = ImodelMetaColumnMeta & IModelMetaColumnText; +interface IModelMetaColumnCollection { + type: 'collection'; + collectionOf: 'object'; + columns: { [key: string]: ImodelMetaColumnMeta & IModelMetaColumnText }; +} + +export type IModelMetaColumn = ImodelMetaColumnMeta & + (IModelMetaColumnText | IModelMetaColumnCollection); diff --git a/packages/server/src/models/Bill.Settings.ts b/packages/server/src/models/Bill.Settings.ts index 1c2f681ed..890a9635a 100644 --- a/packages/server/src/models/Bill.Settings.ts +++ b/packages/server/src/models/Bill.Settings.ts @@ -5,6 +5,7 @@ export default { sortField: 'bill_date', }, importable: true, + exportFlattenOn: 'entries', exportable: true, importAggregator: 'group', importAggregateOn: 'entries', @@ -121,7 +122,7 @@ export default { }, paidAmount: { name: 'Paid Amount', - accessor: 'formattedPaymentAmount' + accessor: 'formattedPaymentAmount', }, note: { name: 'Note', @@ -131,6 +132,33 @@ export default { name: 'Open', type: 'boolean', }, + entries: { + name: 'Entries', + accessor: 'entries', + type: 'collection', + collectionOf: 'object', + columns: { + itemName: { + name: 'Item Name', + accessor: 'item.name', + }, + rate: { + name: 'Item Rate', + accessor: 'rateFormatted', + }, + quantity: { + name: 'Item Quantity', + accessor: 'quantityFormatted', + }, + description: { + name: 'Item Description', + }, + amount: { + name: 'Item Amount', + accessor: 'totalFormatted', + }, + }, + }, }, fields2: { billNumber: { diff --git a/packages/server/src/models/CreditNote.Meta.ts b/packages/server/src/models/CreditNote.Meta.ts index 3a2633359..5da0c1d9e 100644 --- a/packages/server/src/models/CreditNote.Meta.ts +++ b/packages/server/src/models/CreditNote.Meta.ts @@ -13,10 +13,13 @@ export default { sortField: 'name', }, exportable: true, + exportFlattenOn: 'entries', + importable: true, importAggregator: 'group', importAggregateOn: 'entries', importAggregateBy: 'creditNoteNumber', + fields: { customer: { name: 'credit_note.field.customer', @@ -116,6 +119,32 @@ export default { name: 'Open', type: 'boolean', }, + entries: { + name: 'Entries', + type: 'collection', + collectionOf: 'object', + columns: { + itemName: { + name: 'Item Name', + accessor: 'item.name', + }, + rate: { + name: 'Item Rate', + accessor: 'rateFormatted', + }, + quantity: { + name: 'Item Quantity', + accessor: 'quantityFormatted', + }, + description: { + name: 'Item Description', + }, + amount: { + name: 'Item Amount', + accessor: 'totalFormatted', + }, + }, + }, }, fields2: { customerId: { diff --git a/packages/server/src/models/Customer.Settings.ts b/packages/server/src/models/Customer.Settings.ts index 08605c891..71f631032 100644 --- a/packages/server/src/models/Customer.Settings.ts +++ b/packages/server/src/models/Customer.Settings.ts @@ -95,157 +95,132 @@ export default { firstName: { name: 'vendor.field.first_name', type: 'text', - exportable: true, }, lastName: { name: 'vendor.field.last_name', type: 'text', - exportable: true, }, displayName: { name: 'vendor.field.display_name', type: 'text', - exportable: true, }, email: { name: 'vendor.field.email', type: 'text', - exportable: true, }, workPhone: { name: 'vendor.field.work_phone', type: 'text', - exportable: true, }, personalPhone: { - name: 'vendor.field.personal_pone', + name: 'vendor.field.personal_phone', type: 'text', - exportable: true, }, companyName: { name: 'vendor.field.company_name', type: 'text', - exportable: true, }, website: { name: 'vendor.field.website', type: 'text', - exportable: true, - }, - createdAt: { - name: 'vendor.field.created_at', - type: 'date', - exportable: true, }, balance: { name: 'vendor.field.balance', type: 'number', - exportable: true, }, openingBalance: { name: 'vendor.field.opening_balance', type: 'number', - exportable: true, }, openingBalanceAt: { name: 'vendor.field.opening_balance_at', type: 'date', - exportable: true, }, currencyCode: { name: 'vendor.field.currency', type: 'text', - exportable: true, }, status: { name: 'vendor.field.status', - exportable: true, + }, + note: { + name: 'vendor.field.note', }, // Billing Address billingAddress1: { name: 'Billing Address 1', column: 'billing_address1', type: 'text', - exportable: true, }, billingAddress2: { name: 'Billing Address 2', column: 'billing_address2', type: 'text', - exportable: true, }, billingAddressCity: { name: 'Billing Address City', column: 'billing_address_city', type: 'text', - exportable: true, }, billingAddressCountry: { name: 'Billing Address Country', column: 'billing_address_country', type: 'text', - exportable: true, }, billingAddressPostcode: { name: 'Billing Address Postcode', column: 'billing_address_postcode', type: 'text', - exportable: true, }, billingAddressState: { name: 'Billing Address State', column: 'billing_address_state', type: 'text', - exportable: true, }, billingAddressPhone: { name: 'Billing Address Phone', column: 'billing_address_phone', type: 'text', - exportable: true, }, // Shipping Address shippingAddress1: { name: 'Shipping Address 1', column: 'shipping_address1', type: 'text', - exportable: true, }, shippingAddress2: { name: 'Shipping Address 2', column: 'shipping_address2', type: 'text', - exportable: true, }, shippingAddressCity: { name: 'Shipping Address City', column: 'shipping_address_city', type: 'text', - exportable: true, }, shippingAddressCountry: { name: 'Shipping Address Country', column: 'shipping_address_country', type: 'text', - exportable: true, }, shippingAddressPostcode: { name: 'Shipping Address Postcode', column: 'shipping_address_postcode', type: 'text', - exportable: true, }, shippingAddressPhone: { name: 'Shipping Address Phone', column: 'shipping_address_phone', type: 'text', - exportable: true, }, shippingAddressState: { name: 'Shipping Address State', column: 'shipping_address_state', type: 'text', - exportable: true, + }, + createdAt: { + name: 'vendor.field.created_at', + type: 'date', }, }, fields2: { diff --git a/packages/server/src/models/Expense.Settings.ts b/packages/server/src/models/Expense.Settings.ts index a071b49f3..12c539782 100644 --- a/packages/server/src/models/Expense.Settings.ts +++ b/packages/server/src/models/Expense.Settings.ts @@ -8,6 +8,7 @@ export default { sortField: 'name', }, importable: true, + exportFlattenOn: 'categories', exportable: true, fields: { payment_date: { @@ -66,42 +67,50 @@ export default { paymentReceive: { name: 'expense.field.payment_account', type: 'text', - exportable: true, + accessor: 'paymentAccount.name' }, referenceNo: { name: 'expense.field.reference_no', type: 'text', - exportable: true, }, paymentDate: { name: 'expense.field.payment_date', type: 'date', - exportable: true, }, currencyCode: { name: 'expense.field.currency_code', type: 'text', - exportable: true, }, exchangeRate: { name: 'expense.field.exchange_rate', type: 'number', - exportable: true, }, description: { name: 'expense.field.description', type: 'text', - exportable: true, }, categories: { name: 'expense.field.categories', type: 'collection', - exportable: true, + collectionOf: 'object', + columns: { + expenseAccount: { + name: 'expense.field.expense_account', + accessor: 'expenseAccount.name', + }, + amount: { + name: 'expense.field.amount', + accessor: 'amountFormatted', + }, + description: { + name: 'expense.field.line_description', + type: 'text', + }, + }, }, publish: { name: 'expense.field.publish', type: 'boolean', - exportable: true, }, }, fields2: { diff --git a/packages/server/src/models/ManualJournal.Settings.ts b/packages/server/src/models/ManualJournal.Settings.ts index 9349744ee..db2712220 100644 --- a/packages/server/src/models/ManualJournal.Settings.ts +++ b/packages/server/src/models/ManualJournal.Settings.ts @@ -5,6 +5,8 @@ export default { sortField: 'name', }, importable: true, + exportFlattenOn: 'entries', + exportable: true, importAggregator: 'group', importAggregateOn: 'entries', @@ -61,42 +63,70 @@ export default { date: { name: 'manual_journal.field.date', type: 'date', - exportable: true, }, journalNumber: { name: 'manual_journal.field.journal_number', type: 'text', - exportable: true, }, reference: { name: 'manual_journal.field.reference', type: 'text', - exportable: true, }, journalType: { name: 'manual_journal.field.journal_type', type: 'text', - exportable: true, + }, + amount: { + name: 'Amount', + accessor: 'formattedAmount', }, currencyCode: { name: 'manual_journal.field.currency', type: 'text', - exportable: true, }, - exchange_rate: { + exchangeRate: { name: 'manual_journal.field.exchange_rate', type: 'number', - exportable: true, }, description: { name: 'manual_journal.field.description', type: 'text', - exportable: true, }, - publish: { - name: 'Publish', - type: 'boolean', - exportable: true, + entries: { + name: 'Entries', + type: 'collection', + collectionOf: 'object', + columns: { + credit: { + name: 'Credit', + type: 'text', + }, + debit: { + name: 'Debit', + type: 'text', + }, + account: { + name: 'Account', + accessor: 'account.name', + }, + contact: { + name: 'Contact', + accessor: 'contact.displayName', + }, + note: { + name: 'Note', + }, + }, + publish: { + name: 'Publish', + type: 'boolean', + }, + publishedAt: { + name: 'Published At', + }, + }, + createdAt: { + name: 'Created At', }, }, fields2: { diff --git a/packages/server/src/models/SaleEstimate.Settings.ts b/packages/server/src/models/SaleEstimate.Settings.ts index 378fa73fa..a9577b4f4 100644 --- a/packages/server/src/models/SaleEstimate.Settings.ts +++ b/packages/server/src/models/SaleEstimate.Settings.ts @@ -5,6 +5,8 @@ export default { sortField: 'estimate_date', }, exportable: true, + exportFlattenOn: 'entries', + importable: true, importAggregator: 'group', importAggregateOn: 'entries', @@ -131,6 +133,33 @@ export default { type: 'boolean', exportable: true, }, + entries: { + name: 'Entries', + accessor: 'entries', + type: 'collection', + collectionOf: 'object', + columns: { + itemName: { + name: 'Item Name', + accessor: 'item.name', + }, + rate: { + name: 'Item Rate', + accessor: 'rateFormatted', + }, + quantity: { + name: 'Item Quantity', + accessor: 'quantityFormatted', + }, + description: { + name: 'Item Description', + }, + amount: { + name: 'Item Amount', + accessor: 'totalFormatted', + }, + }, + }, }, fields2: { customerId: { @@ -191,7 +220,7 @@ export default { relationModel: 'Item', relationImportMatch: ['name', 'code'], required: true, - importHint: "Matches the item name or code." + importHint: 'Matches the item name or code.', }, rate: { name: 'invoice.field.rate', diff --git a/packages/server/src/models/SaleInvoice.Settings.ts b/packages/server/src/models/SaleInvoice.Settings.ts index d632af617..24728522e 100644 --- a/packages/server/src/models/SaleInvoice.Settings.ts +++ b/packages/server/src/models/SaleInvoice.Settings.ts @@ -5,6 +5,8 @@ export default { sortField: 'created_at', }, exportable: true, + exportFlattenOn: 'entries', + importable: true, importAggregator: 'group', importAggregateOn: 'entries', @@ -125,7 +127,7 @@ export default { }, paidAmount: { name: 'Paid Amount', - accessor: 'paymentAmountFormatted' + accessor: 'paymentAmountFormatted', }, dueAmount: { name: 'Due Amount', @@ -143,6 +145,33 @@ export default { name: 'invoice.field.delivered', type: 'boolean', }, + entries: { + name: 'Entries', + accessor: 'entries', + type: 'collection', + collectionOf: 'object', + columns: { + itemName: { + name: 'Item Name', + accessor: 'item.name', + }, + rate: { + name: 'Item Rate', + accessor: 'rateFormatted', + }, + quantity: { + name: 'Item Quantity', + accessor: 'quantityFormatted', + }, + description: { + name: 'Item Description', + }, + amount: { + name: 'Item Amount', + accessor: 'totalFormatted', + }, + }, + }, }, fields2: { invoiceDate: { diff --git a/packages/server/src/models/SaleReceipt.Settings.ts b/packages/server/src/models/SaleReceipt.Settings.ts index ce26bbfff..3fecd0480 100644 --- a/packages/server/src/models/SaleReceipt.Settings.ts +++ b/packages/server/src/models/SaleReceipt.Settings.ts @@ -5,6 +5,8 @@ export default { sortField: 'created_at', }, exportable: true, + exportFlattenOn: 'entries', + importable: true, importAggregator: 'group', importAggregateOn: 'entries', @@ -117,10 +119,6 @@ export default { name: 'receipt.field.statement', type: 'text', }, - createdAt: { - name: 'receipt.field.created_at', - type: 'date', - }, status: { name: 'receipt.field.status', type: 'enumeration', @@ -130,6 +128,37 @@ export default { ], exportable: true, }, + entries: { + name: 'Entries', + accessor: 'entries', + type: 'collection', + collectionOf: 'object', + columns: { + itemName: { + name: 'Item Name', + accessor: 'item.name', + }, + rate: { + name: 'Item Rate', + accessor: 'rateFormatted', + }, + quantity: { + name: 'Item Quantity', + accessor: 'quantityFormatted', + }, + description: { + name: 'Item Description', + }, + amount: { + name: 'Item Amount', + accessor: 'totalFormatted', + }, + }, + }, + createdAt: { + name: 'receipt.field.created_at', + type: 'date', + }, }, fields2: { receiptDate: { diff --git a/packages/server/src/models/Vendor.Settings.ts b/packages/server/src/models/Vendor.Settings.ts index 2fa93bf6d..7681dfa10 100644 --- a/packages/server/src/models/Vendor.Settings.ts +++ b/packages/server/src/models/Vendor.Settings.ts @@ -95,71 +95,57 @@ export default { firstName: { name: 'vendor.field.first_name', type: 'text', - exportable: true, }, lastName: { name: 'vendor.field.last_name', type: 'text', - exportable: true, }, displayName: { name: 'vendor.field.display_name', type: 'text', - exportable: true, }, email: { name: 'vendor.field.email', type: 'text', - exportable: true, }, workPhone: { name: 'vendor.field.work_phone', type: 'text', - exportable: true, }, personalPhone: { name: 'vendor.field.personal_phone', type: 'text', - exportable: true, }, companyName: { name: 'vendor.field.company_name', type: 'text', - exportable: true, }, website: { name: 'vendor.field.website', type: 'text', - exportable: true, - }, - createdAt: { - name: 'vendor.field.created_at', - type: 'date', - exportable: true, }, balance: { name: 'vendor.field.balance', type: 'number', - exportable: true, }, openingBalance: { name: 'vendor.field.opening_balance', type: 'number', - exportable: true, }, openingBalanceAt: { name: 'vendor.field.opening_balance_at', type: 'date', - exportable: true, }, currencyCode: { name: 'vendor.field.currency', type: 'text', - exportable: true, }, status: { name: 'vendor.field.status', - exportable: true, + }, + note: { + name: 'vendor.field.note', + type: 'text', }, // Billing Address billingAddress1: { @@ -246,7 +232,12 @@ export default { column: 'shipping_address_phone', type: 'text', exportable: true, - } + }, + createdAt: { + name: 'vendor.field.created_at', + type: 'date', + exportable: true, + }, }, fields2: { firstName: { diff --git a/packages/server/src/models/VendorCredit.Meta.ts b/packages/server/src/models/VendorCredit.Meta.ts index d1ce500b7..b57cc275c 100644 --- a/packages/server/src/models/VendorCredit.Meta.ts +++ b/packages/server/src/models/VendorCredit.Meta.ts @@ -13,10 +13,13 @@ export default { sortField: 'name', }, exportable: true, + exportFlattenOn: 'entries', + importable: true, importAggregator: 'group', importAggregateOn: 'entries', importAggregateBy: 'vendorCreditNumber', + fields: { vendor: { name: 'vendor_credit.field.vendor', @@ -99,6 +102,22 @@ export default { name: 'Vendor Credit Date', type: 'date', }, + amount: { + name: 'Amount', + accessor: 'formattedAmount', + }, + creditRemaining: { + name: 'Credits Remaining', + accessor: 'formattedCreditsRemaining', + }, + refundedAmount: { + name: 'Refunded Amount', + accessor: 'refundedAmount', + }, + invoicedAmount: { + name: 'Invoiced Amount', + accessor: 'formattedInvoicedAmount', + }, note: { name: 'Note', type: 'text', @@ -107,6 +126,32 @@ export default { name: 'Open', type: 'boolean', }, + entries: { + name: 'Entries', + type: 'collection', + collectionOf: 'object', + columns: { + itemName: { + name: 'Item Name', + accessor: 'item.name', + }, + rate: { + name: 'Item Rate', + accessor: 'rateFormatted', + }, + quantity: { + name: 'Item Quantity', + accessor: 'quantityFormatted', + }, + description: { + name: 'Item Description', + }, + amount: { + name: 'Item Amount', + accessor: 'totalFormatted', + }, + }, + }, }, fields2: { vendorId: { diff --git a/packages/server/src/services/CreditNotes/ListCreditNotes.ts b/packages/server/src/services/CreditNotes/ListCreditNotes.ts index 498d3d74d..11ec2f7fa 100644 --- a/packages/server/src/services/CreditNotes/ListCreditNotes.ts +++ b/packages/server/src/services/CreditNotes/ListCreditNotes.ts @@ -45,7 +45,7 @@ export default class ListCreditNotes extends BaseCreditNotes { ); const { results, pagination } = await CreditNote.query() .onBuild((builder) => { - builder.withGraphFetched('entries'); + builder.withGraphFetched('entries.item'); builder.withGraphFetched('customer'); dynamicFilter.buildQuery()(builder); }) diff --git a/packages/server/src/services/Export/ExportService.ts b/packages/server/src/services/Export/ExportService.ts index 111b5587a..c9c3dc432 100644 --- a/packages/server/src/services/Export/ExportService.ts +++ b/packages/server/src/services/Export/ExportService.ts @@ -1,12 +1,14 @@ import { Inject, Service } from 'typedi'; import xlsx from 'xlsx'; +import * as R from 'ramda'; import { get } from 'lodash'; import { sanitizeResourceName } from '../Import/_utils'; import ResourceService from '../Resource/ResourceService'; import { ExportableResources } from './ExportResources'; import { ServiceError } from '@/exceptions'; import { Errors } from './common'; -import { IModelMeta } from '@/interfaces'; +import { IModelMeta, IModelMetaColumn } from '@/interfaces'; +import { flatDataCollections, getDataAccessor } from './utils'; @Service() export class ExportResourceService { @@ -22,16 +24,16 @@ export class ExportResourceService { * @param {string} resourceName - Resource name. * @param {string} format - File format. */ - async export(tenantId: number, resourceName: string, format: string = 'csv') { + public async export(tenantId: number, resourceName: string, format: string = 'csv') { const resource = sanitizeResourceName(resourceName); const resourceMeta = this.getResourceMeta(tenantId, resource); this.validateResourceMeta(resourceMeta); const data = await this.getExportableData(tenantId, resource); + const transformed = this.transformExportedData(tenantId, resource, data); const exportableColumns = this.getExportableColumns(resourceMeta); - - const workbook = this.createWorkbook(data, exportableColumns); + const workbook = this.createWorkbook(transformed, exportableColumns); return this.exportWorkbook(workbook, format); } @@ -57,6 +59,29 @@ export class ExportResourceService { } } + /** + * Transforms the exported data based on the resource metadata. + * If the resource metadata specifies a flattening attribute (`exportFlattenOn`), + * the data will be flattened based on this attribute using the `flatDataCollections` utility function. + * + * @param {number} tenantId - The tenant identifier. + * @param {string} resource - The name of the resource. + * @param {Array>} data - The original data to be transformed. + * @returns {Array>} - The transformed data. + */ + private transformExportedData( + tenantId: number, + resource: string, + data: Array> + ): Array> { + const resourceMeta = this.getResourceMeta(tenantId, resource); + + return R.when>, Array>>( + R.always(Boolean(resourceMeta.exportFlattenOn)), + (data) => flatDataCollections(data, resourceMeta.exportFlattenOn), + data + ); + } /** * Fetches exportable data for a given resource. * @param {number} tenantId - The tenant identifier. @@ -75,13 +100,29 @@ export class ExportResourceService { * @returns An array of exportable columns. */ private getExportableColumns(resourceMeta: IModelMeta) { - return Object.entries(resourceMeta.columns) - .filter(([_, value]) => value.exportable !== false) - .map(([key, value]) => ({ - name: value.name, - type: value.type, - accessor: value.accessor || key, - })); + const processColumns = ( + columns: { [key: string]: IModelMetaColumn }, + parent = '' + ) => { + return Object.entries(columns) + .filter(([_, value]) => value.exportable !== 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); } /** @@ -93,12 +134,14 @@ export class ExportResourceService { private createWorkbook(data: any[], exportableColumns: any[]) { const workbook = xlsx.utils.book_new(); const worksheetData = data.map((item) => - exportableColumns.map((col) => get(item, col.accessor)) + exportableColumns.map((col) => get(item, getDataAccessor(col))) ); + worksheetData.unshift(exportableColumns.map((col) => col.name)); const worksheet = xlsx.utils.aoa_to_sheet(worksheetData); xlsx.utils.book_append_sheet(workbook, worksheet, 'Exported Data'); + return workbook; } diff --git a/packages/server/src/services/Export/utils.ts b/packages/server/src/services/Export/utils.ts new file mode 100644 index 000000000..e1436d8ab --- /dev/null +++ b/packages/server/src/services/Export/utils.ts @@ -0,0 +1,27 @@ +import { flatMap } from 'lodash'; +/** + * Flattens the data based on a specified attribute. + * @param data - The data to be flattened. + * @param flattenAttr - The attribute to be flattened. + * @returns - The flattened data. + */ +export const flatDataCollections = ( + data: Record, + flattenAttr: string +): Record[] => { + return flatMap(data, (item) => + item[flattenAttr].map((entry) => ({ + ...item, + [flattenAttr]: entry, + })) + ); +}; + +/** + * Gets the data accessor for a given column. + * @param col - The column to get the data accessor for. + * @returns - The data accessor. + */ +export const getDataAccessor = (col: any) => { + return col.group ? `${col.group}.${col.accessor}` : col.accessor; +}; diff --git a/packages/server/src/services/Purchases/Bills/GetBills.ts b/packages/server/src/services/Purchases/Bills/GetBills.ts index 1ea19797d..73abc55da 100644 --- a/packages/server/src/services/Purchases/Bills/GetBills.ts +++ b/packages/server/src/services/Purchases/Bills/GetBills.ts @@ -49,6 +49,7 @@ export class GetBills { const { results, pagination } = await Bill.query() .onBuild((builder) => { builder.withGraphFetched('vendor'); + builder.withGraphFetched('entries.item'); dynamicFilter.buildQuery()(builder); }) .pagination(filter.page - 1, filter.pageSize); diff --git a/packages/server/src/services/Purchases/VendorCredits/VendorCreditTransformer.ts b/packages/server/src/services/Purchases/VendorCredits/VendorCreditTransformer.ts index be1431ac2..282b6f08e 100644 --- a/packages/server/src/services/Purchases/VendorCredits/VendorCreditTransformer.ts +++ b/packages/server/src/services/Purchases/VendorCredits/VendorCreditTransformer.ts @@ -14,6 +14,7 @@ export class VendorCreditTransformer extends Transformer { 'formattedSubtotal', 'formattedVendorCreditDate', 'formattedCreditsRemaining', + 'formattedInvoicedAmount', 'entries', ]; }; @@ -58,6 +59,17 @@ export class VendorCreditTransformer extends Transformer { }); }; + /** + * Retrieves the formatted invoiced amount. + * @param credit + * @returns {string} + */ + protected formattedInvoicedAmount = (credit) => { + return formatNumber(credit.invoicedAmount, { + currencyCode: credit.currencyCode, + }); + }; + /** * Retrieves the entries of the bill. * @param {IVendorCredit} vendorCredit diff --git a/packages/server/src/services/Resource/ResourceService.ts b/packages/server/src/services/Resource/ResourceService.ts index 61914157a..3e0ffbafe 100644 --- a/packages/server/src/services/Resource/ResourceService.ts +++ b/packages/server/src/services/Resource/ResourceService.ts @@ -105,7 +105,11 @@ export default class ResourceService { const $enumerationType = (field) => field.fieldType === 'enumeration' ? field : undefined; - const $hasFields = (field) => 'undefined' !== typeof field.fields ? field : undefined; + const $hasFields = (field) => + 'undefined' !== typeof field.fields ? field : undefined; + + const $hasColumns = (column) => + 'undefined' !== typeof column.columns ? column : undefined; const naviagations = [ ['fields', qim.$each, 'name'], @@ -114,6 +118,7 @@ export default class ResourceService { ['fields2', qim.$each, $enumerationType, 'options', qim.$each, 'label'], ['fields2', qim.$each, $hasFields, 'fields', qim.$each, 'name'], ['columns', qim.$each, 'name'], + ['columns', qim.$each, $hasColumns, 'columns', qim.$each, 'name'], ]; return this.i18nService.i18nApply(naviagations, meta, tenantId); } diff --git a/packages/server/src/services/Sales/Estimates/GetSaleEstimates.ts b/packages/server/src/services/Sales/Estimates/GetSaleEstimates.ts index e0b53adb3..bba1db943 100644 --- a/packages/server/src/services/Sales/Estimates/GetSaleEstimates.ts +++ b/packages/server/src/services/Sales/Estimates/GetSaleEstimates.ts @@ -51,6 +51,7 @@ export class GetSaleEstimates { .onBuild((builder) => { builder.withGraphFetched('customer'); builder.withGraphFetched('entries'); + builder.withGraphFetched('entries.item'); dynamicFilter.buildQuery()(builder); }) .pagination(filter.page - 1, filter.pageSize); diff --git a/packages/server/src/services/Sales/Invoices/GetSaleInvoices.ts b/packages/server/src/services/Sales/Invoices/GetSaleInvoices.ts index b1d9b93db..569aceccb 100644 --- a/packages/server/src/services/Sales/Invoices/GetSaleInvoices.ts +++ b/packages/server/src/services/Sales/Invoices/GetSaleInvoices.ts @@ -49,7 +49,7 @@ export class GetSaleInvoices { ); const { results, pagination } = await SaleInvoice.query() .onBuild((builder) => { - builder.withGraphFetched('entries'); + builder.withGraphFetched('entries.item'); builder.withGraphFetched('customer'); dynamicFilter.buildQuery()(builder); }) diff --git a/packages/server/src/services/Sales/Receipts/GetSaleReceipts.ts b/packages/server/src/services/Sales/Receipts/GetSaleReceipts.ts index 24150e349..1916c5e75 100644 --- a/packages/server/src/services/Sales/Receipts/GetSaleReceipts.ts +++ b/packages/server/src/services/Sales/Receipts/GetSaleReceipts.ts @@ -11,6 +11,9 @@ import { SaleReceiptTransformer } from './SaleReceiptTransformer'; import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; import DynamicListingService from '@/services/DynamicListing/DynamicListService'; +interface GetSaleReceiptsSettings { + fetchEntriesGraph?: boolean; +} @Service() export class GetSaleReceipts { @Inject() @@ -50,7 +53,7 @@ export class GetSaleReceipts { .onBuild((builder) => { builder.withGraphFetched('depositAccount'); builder.withGraphFetched('customer'); - builder.withGraphFetched('entries'); + builder.withGraphFetched('entries.item'); dynamicFilter.buildQuery()(builder); })