From 079491823dbc6d3da7bcd2692d25f6c6f4ca4b4b Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Tue, 9 Apr 2024 22:00:04 +0200 Subject: [PATCH] feat: add hints to import fields --- packages/server/src/models/Bill.Settings.ts | 1 + .../server/src/models/BillPayment.Settings.ts | 3 + packages/server/src/models/CreditNote.Meta.ts | 1 + .../server/src/models/Expense.Settings.ts | 2 + packages/server/src/models/Item.Settings.ts | 93 +++++----------- .../src/models/PaymentReceive.Settings.ts | 3 + .../src/models/SaleEstimate.Settings.ts | 1 + .../server/src/models/SaleInvoice.Settings.ts | 1 + .../server/src/models/SaleReceipt.Settings.ts | 1 + .../UncategorizedCashflowTransaction.meta.ts | 6 +- .../server/src/models/VendorCredit.Meta.ts | 1 + .../server/src/services/Cashflow/constants.ts | 44 ++++---- .../src/services/Import/ImportFileProcess.ts | 15 ++- .../server/src/services/Import/interfaces.ts | 1 + .../src/services/Items/ItemsImportable.ts | 3 +- .../server/src/services/Items/constants.ts | 84 +++++++++++++- packages/webapp/src/hooks/query/import.ts | 104 +++++++++++++++++- 17 files changed, 261 insertions(+), 103 deletions(-) diff --git a/packages/server/src/models/Bill.Settings.ts b/packages/server/src/models/Bill.Settings.ts index 3d07d68d7..2842ff926 100644 --- a/packages/server/src/models/Bill.Settings.ts +++ b/packages/server/src/models/Bill.Settings.ts @@ -132,6 +132,7 @@ export default { relationModel: 'Item', relationImportMatch: ['name', 'code'], required: true, + importHint: "Matches the item name or code." }, rate: { name: 'Rate', diff --git a/packages/server/src/models/BillPayment.Settings.ts b/packages/server/src/models/BillPayment.Settings.ts index 15abe6866..1f2369f21 100644 --- a/packages/server/src/models/BillPayment.Settings.ts +++ b/packages/server/src/models/BillPayment.Settings.ts @@ -84,6 +84,7 @@ export default { name: 'bill_payment.field.payment_number', fieldType: 'text', unique: true, + importHint: "The payment number should be unique." }, paymentAccountId: { name: 'bill_payment.field.payment_account', @@ -91,6 +92,7 @@ export default { relationModel: 'Account', relationImportMatch: ['name', 'code'], required: true, + importHint: "Matches the account name or code." }, exchangeRate: { name: 'bill_payment.field.exchange_rate', @@ -118,6 +120,7 @@ export default { relationModel: 'Bill', relationImportMatch: 'billNumber', required: true, + importHint: "Matches the bill number." }, paymentAmount: { name: 'bill_payment.field.entries.payment_amount', diff --git a/packages/server/src/models/CreditNote.Meta.ts b/packages/server/src/models/CreditNote.Meta.ts index 16f9c13a7..f6317528b 100644 --- a/packages/server/src/models/CreditNote.Meta.ts +++ b/packages/server/src/models/CreditNote.Meta.ts @@ -130,6 +130,7 @@ export default { relationModel: 'Item', relationImportMatch: ['name', 'code'], required: true, + importHint: 'Matches the item name or code.', }, rate: { name: 'Rate', diff --git a/packages/server/src/models/Expense.Settings.ts b/packages/server/src/models/Expense.Settings.ts index 75b395a79..c1d670526 100644 --- a/packages/server/src/models/Expense.Settings.ts +++ b/packages/server/src/models/Expense.Settings.ts @@ -68,6 +68,7 @@ export default { relationModel: 'Account', relationImportMatch: ['name', 'code'], required: true, + importHint: "Matches the account name or code." }, referenceNo: { name: 'expense.field.reference_no', @@ -101,6 +102,7 @@ export default { relationModel: 'Account', relationImportMatch: ['name', 'code'], required: true, + importHint: "Matches the account name or code." }, amount: { name: 'expense.field.amount', diff --git a/packages/server/src/models/Item.Settings.ts b/packages/server/src/models/Item.Settings.ts index 7447ff3d3..640c271ea 100644 --- a/packages/server/src/models/Item.Settings.ts +++ b/packages/server/src/models/Item.Settings.ts @@ -124,117 +124,82 @@ export default { fields2: { type: { name: 'item.field.type', - column: 'type', fieldType: 'enumeration', options: [ { key: 'inventory', label: 'item.field.type.inventory' }, { key: 'service', label: 'item.field.type.service' }, { key: 'non-inventory', label: 'item.field.type.non-inventory' }, ], + required: true, }, name: { name: 'item.field.name', - column: 'name', fieldType: 'text', + required: true, }, code: { name: 'item.field.code', - column: 'code', fieldType: 'text', }, sellable: { name: 'item.field.sellable', - column: 'sellable', fieldType: 'boolean', }, purchasable: { name: 'item.field.purchasable', - column: 'purchasable', fieldType: 'boolean', }, - sell_price: { - name: 'item.field.cost_price', - column: 'sell_price', + sellPrice: { + name: 'item.field.sell_price', fieldType: 'number', }, cost_price: { + name: 'item.field.cost_price', + fieldType: 'number', + }, + costAccount: { name: 'item.field.cost_account', - column: 'cost_price', - fieldType: 'number', + fieldType: 'relation', + relationModel: 'Account', + relationImportMatch: ['name', 'code'], + importHint: 'Matches the account name or code.', }, - cost_account: { + sellAccount: { name: 'item.field.sell_account', - column: 'cost_account_id', fieldType: 'relation', - - relationType: 'enumeration', - relationKey: 'costAccount', - - relationEntityLabel: 'name', - relationEntityKey: 'slug', + relationModel: 'Account', + relationImportMatch: ['name', 'code'], + importHint: 'Matches the account name or code.', }, - sell_account: { - name: 'item.field.sell_description', - column: 'sell_account_id', - fieldType: 'relation', - - relationType: 'enumeration', - relationKey: 'sellAccount', - - relationEntityLabel: 'name', - relationEntityKey: 'slug', - }, - inventory_account: { + inventoryAccount: { name: 'item.field.inventory_account', - column: 'inventory_account_id', - - relationType: 'enumeration', - relationKey: 'inventoryAccount', - - relationEntityLabel: 'name', - relationEntityKey: 'slug', + fieldType: 'relation', + relationModel: 'Account', + relationImportMatch: ['name', 'code'], + importHint: 'Matches the account name or code.', }, - sell_description: { - name: 'Sell description', - column: 'sell_description', + sellDescription: { + name: 'Sell Description', fieldType: 'text', }, - purchase_description: { - name: 'Purchase description', - column: 'purchase_description', + purchaseDescription: { + name: 'Purchase Description', fieldType: 'text', }, - quantity_on_hand: { - name: 'item.field.quantity_on_hand', - column: 'quantity_on_hand', - fieldType: 'number', - }, note: { name: 'item.field.note', - column: 'note', fieldType: 'text', }, category: { name: 'item.field.category', - column: 'category_id', - - relationType: 'enumeration', - relationKey: 'category', - - relationEntityLabel: 'name', - relationEntityKey: 'id', + fieldType: 'relation', + relationModel: 'ItemCategory', + relationImportMatch: ['name'], + importHint: "Matches the category name." }, active: { name: 'item.field.active', - column: 'active', fieldType: 'boolean', - filterable: false, - }, - created_at: { - name: 'item.field.created_at', - column: 'created_at', - columnType: 'date', - fieldType: 'date', }, }, }; diff --git a/packages/server/src/models/PaymentReceive.Settings.ts b/packages/server/src/models/PaymentReceive.Settings.ts index e44478ed6..e96e1c7b2 100644 --- a/packages/server/src/models/PaymentReceive.Settings.ts +++ b/packages/server/src/models/PaymentReceive.Settings.ts @@ -84,10 +84,12 @@ export default { relationModel: 'Account', relationImportMatch: ['name', 'code'], required: true, + importHint: "Matches the account name or code." }, paymentReceiveNo: { name: 'payment_receive.field.payment_receive_no', fieldType: 'text', + importHint: "The payment number should be unique." }, statement: { name: 'payment_receive.field.statement', @@ -106,6 +108,7 @@ export default { relationModel: 'SaleInvoice', relationImportMatch: 'invoiceNo', required: true, + importHint: "Matches the invoice number." }, paymentAmount: { name: 'payment_receive.field.entries.payment_amount', diff --git a/packages/server/src/models/SaleEstimate.Settings.ts b/packages/server/src/models/SaleEstimate.Settings.ts index 5d967d2b8..f92735f34 100644 --- a/packages/server/src/models/SaleEstimate.Settings.ts +++ b/packages/server/src/models/SaleEstimate.Settings.ts @@ -132,6 +132,7 @@ export default { relationModel: 'Item', relationImportMatch: ['name', 'code'], required: true, + 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 0bdab53a2..c48befbcb 100644 --- a/packages/server/src/models/SaleInvoice.Settings.ts +++ b/packages/server/src/models/SaleInvoice.Settings.ts @@ -142,6 +142,7 @@ export default { relationModel: 'Item', relationImportMatch: ['name', 'code'], required: true, + importHint: "Matches the item name or code." }, rate: { name: 'invoice.field.rate', diff --git a/packages/server/src/models/SaleReceipt.Settings.ts b/packages/server/src/models/SaleReceipt.Settings.ts index 43fd54837..2b2fe6fa2 100644 --- a/packages/server/src/models/SaleReceipt.Settings.ts +++ b/packages/server/src/models/SaleReceipt.Settings.ts @@ -126,6 +126,7 @@ export default { relationModel: 'Item', relationImportMatch: ['name', 'code'], required: true, + importHint: "Matches the item name or code." }, rate: { name: 'invoice.field.rate', diff --git a/packages/server/src/models/UncategorizedCashflowTransaction.meta.ts b/packages/server/src/models/UncategorizedCashflowTransaction.meta.ts index 2a6529c0d..d5b55d8fb 100644 --- a/packages/server/src/models/UncategorizedCashflowTransaction.meta.ts +++ b/packages/server/src/models/UncategorizedCashflowTransaction.meta.ts @@ -53,23 +53,19 @@ export default { }, payee: { name: 'Payee', - column: 'payee', fieldType: 'text', }, description: { name: 'Description', - column: 'description', fieldType: 'text', }, referenceNo: { name: 'Reference No.', - column: 'reference_no', fieldType: 'text', }, amount: { name: 'Amount', - column: 'Amount', - fieldType: 'numeric', + fieldType: 'number', required: true, }, }, diff --git a/packages/server/src/models/VendorCredit.Meta.ts b/packages/server/src/models/VendorCredit.Meta.ts index 9ef1496dd..86a3aa103 100644 --- a/packages/server/src/models/VendorCredit.Meta.ts +++ b/packages/server/src/models/VendorCredit.Meta.ts @@ -122,6 +122,7 @@ export default { relationModel: 'Item', relationImportMatch: ['name', 'code'], required: true, + importHint: "Matches the item name or code." }, rate: { name: 'Rate', diff --git a/packages/server/src/services/Cashflow/constants.ts b/packages/server/src/services/Cashflow/constants.ts index c77c45f69..528bd8e48 100644 --- a/packages/server/src/services/Cashflow/constants.ts +++ b/packages/server/src/services/Cashflow/constants.ts @@ -79,27 +79,25 @@ export interface ICashflowTransactionTypeMeta { } export const BankTransactionsSampleData = [ - [ - { - Amount: '6,410.19', - Date: '2024-03-26', - Payee: 'MacGyver and Sons', - 'Reference No.': 'REF-1', - Description: 'Commodi quo labore.', - }, - { - Amount: '8,914.17', - Date: '2024-01-05', - Payee: 'Eichmann - Bergnaum', - 'Reference No.': 'REF-1', - Description: 'Quia enim et.', - }, - { - Amount: '6,200.88', - Date: '2024-02-17', - Payee: 'Luettgen, Mraz and Legros', - 'Reference No.': 'REF-1', - Description: 'Occaecati consequuntur cum impedit illo.', - }, - ], + { + Amount: '6,410.19', + Date: '2024-03-26', + Payee: 'MacGyver and Sons', + 'Reference No.': 'REF-1', + Description: 'Commodi quo labore.', + }, + { + Amount: '8,914.17', + Date: '2024-01-05', + Payee: 'Eichmann - Bergnaum', + 'Reference No.': 'REF-1', + Description: 'Quia enim et.', + }, + { + Amount: '6,200.88', + Date: '2024-02-17', + Payee: 'Luettgen, Mraz and Legros', + 'Reference No.': 'REF-1', + Description: 'Occaecati consequuntur cum impedit illo.', + }, ]; diff --git a/packages/server/src/services/Import/ImportFileProcess.ts b/packages/server/src/services/Import/ImportFileProcess.ts index 8eebccede..7d0548dc9 100644 --- a/packages/server/src/services/Import/ImportFileProcess.ts +++ b/packages/server/src/services/Import/ImportFileProcess.ts @@ -2,7 +2,12 @@ import { Inject, Service } from 'typedi'; import { chain } from 'lodash'; import { Knex } from 'knex'; import { ServiceError } from '@/exceptions'; -import { ERRORS, getSheetColumns, getUnmappedSheetColumns, readImportFile } from './_utils'; +import { + ERRORS, + getSheetColumns, + getUnmappedSheetColumns, + readImportFile, +} from './_utils'; import { ImportFileCommon } from './ImportFileCommon'; import { ImportFileDataTransformer } from './ImportFileDataTransformer'; import ResourceService from '../Resource/ResourceService'; @@ -49,10 +54,9 @@ export class ImportFileProcess { const sheetData = this.importCommon.parseXlsxSheet(buffer); const header = getSheetColumns(sheetData); - const resourceFields = this.resource.getResourceFields2( - tenantId, - importFile.resource - ); + const resource = importFile.resource; + const resourceFields = this.resource.getResourceFields2(tenantId, resource); + // Runs the importing operation with ability to return errors that will happen. const [successedImport, failedImport, allData] = await this.uow.withTransaction( @@ -91,6 +95,7 @@ export class ImportFileProcess { const skippedCount = errorsCount; return { + resource, createdCount, skippedCount, totalCount, diff --git a/packages/server/src/services/Import/interfaces.ts b/packages/server/src/services/Import/interfaces.ts index 3da238e5f..65d53ff59 100644 --- a/packages/server/src/services/Import/interfaces.ts +++ b/packages/server/src/services/Import/interfaces.ts @@ -44,6 +44,7 @@ export interface ImportFileMapPOJO { } export interface ImportFilePreviewPOJO { + resource: string; createdCount: number; skippedCount: number; totalCount: number; diff --git a/packages/server/src/services/Items/ItemsImportable.ts b/packages/server/src/services/Items/ItemsImportable.ts index c618d8353..bcce0e089 100644 --- a/packages/server/src/services/Items/ItemsImportable.ts +++ b/packages/server/src/services/Items/ItemsImportable.ts @@ -3,6 +3,7 @@ import { Knex } from 'knex'; import { Importable } from '@/services/Import/Importable'; import { IItemCreateDTO } from '@/interfaces'; import { CreateItem } from './CreateItem'; +import { ItemsSampleData } from './constants'; @Service() export class ItemsImportable extends Importable { @@ -28,6 +29,6 @@ export class ItemsImportable extends Importable { * Retrieves the sample data of customers used to download sample sheet. */ public sampleData(): any[] { - return []; + return ItemsSampleData; } } diff --git a/packages/server/src/services/Items/constants.ts b/packages/server/src/services/Items/constants.ts index 1acbc70c6..3d571c527 100644 --- a/packages/server/src/services/Items/constants.ts +++ b/packages/server/src/services/Items/constants.ts @@ -1,4 +1,3 @@ - export const ERRORS = { NOT_FOUND: 'NOT_FOUND', ITEMS_NOT_FOUND: 'ITEMS_NOT_FOUND', @@ -19,7 +18,8 @@ export const ERRORS = { ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT: 'ITEM_HAS_ASSOCIATED_INVENTORY_ADJUSTMENT', ITEM_CANNOT_CHANGE_INVENTORY_TYPE: 'ITEM_CANNOT_CHANGE_INVENTORY_TYPE', - TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS: 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS', + TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS: + 'TYPE_CANNOT_CHANGE_WITH_ITEM_HAS_TRANSACTIONS', INVENTORY_ACCOUNT_CANNOT_MODIFIED: 'INVENTORY_ACCOUNT_CANNOT_MODIFIED', ITEM_HAS_ASSOCIATED_TRANSACTIONS: 'ITEM_HAS_ASSOCIATED_TRANSACTIONS', @@ -53,8 +53,84 @@ export const DEFAULT_VIEWS = [ slug: 'non-inventory', rolesLogicExpression: '1', roles: [ - { index: 1, fieldKey: 'type', comparator: 'equals', value: 'non-inventory' }, + { + index: 1, + fieldKey: 'type', + comparator: 'equals', + value: 'non-inventory', + }, ], columns: DEFAULT_VIEW_COLUMNS, }, -] \ No newline at end of file +]; + +export const ItemsSampleData = [ + { + 'Item Type': 'Inventory', + 'Item Name': 'Hettinger, Schumm and Bartoletti', + 'Item Code': '1000', + Sellable: 'T', + Purchasable: 'T', + 'Cost Price': '10000', + 'Sell Price': '1000', + 'Cost Account': 'Cost of Goods Sold', + 'Sell Account': 'Other Income', + 'Inventory Account': 'Inventory Asset', + 'Sell Description': 'Description ....', + 'Purchase Description': 'Description ....', + Category: 'sdafasdfsadf', + Note: 'At dolor est non tempore et quisquam.', + Active: 'TRUE', + }, + { + 'Item Type': 'Inventory', + 'Item Name': 'Schmitt Group', + 'Item Code': '1001', + Sellable: 'T', + Purchasable: 'T', + 'Cost Price': '10000', + 'Sell Price': '1000', + 'Cost Account': 'Cost of Goods Sold', + 'Sell Account': 'Other Income', + 'Inventory Account': 'Inventory Asset', + 'Sell Description': 'Description ....', + 'Purchase Description': 'Description ....', + Category: 'sdafasdfsadf', + Note: 'Id perspiciatis at adipisci minus accusamus dolor iure dolore.', + Active: 'TRUE', + }, + { + 'Item Type': 'Inventory', + 'Item Name': 'Marks - Carroll', + 'Item Code': '1002', + Sellable: 'T', + Purchasable: 'T', + 'Cost Price': '10000', + 'Sell Price': '1000', + 'Cost Account': 'Cost of Goods Sold', + 'Sell Account': 'Other Income', + 'Inventory Account': 'Inventory Asset', + 'Sell Description': 'Description ....', + 'Purchase Description': 'Description ....', + Category: 'sdafasdfsadf', + Note: 'Odio odio minus similique.', + Active: 'TRUE', + }, + { + 'Item Type': 'Inventory', + 'Item Name': 'VonRueden, Ruecker and Hettinger', + 'Item Code': '1003', + Sellable: 'T', + Purchasable: 'T', + 'Cost Price': '10000', + 'Sell Price': '1000', + 'Cost Account': 'Cost of Goods Sold', + 'Sell Account': 'Other Income', + 'Inventory Account': 'Inventory Asset', + 'Sell Description': 'Description ....', + 'Purchase Description': 'Description ....', + Category: 'sdafasdfsadf', + Note: 'Quibusdam dolores illo.', + Active: 'TRUE', + }, +]; diff --git a/packages/webapp/src/hooks/query/import.ts b/packages/webapp/src/hooks/query/import.ts index 965a32556..369dfbd6d 100644 --- a/packages/webapp/src/hooks/query/import.ts +++ b/packages/webapp/src/hooks/query/import.ts @@ -1,8 +1,14 @@ // @ts-nocheck -import { useMutation, useQuery, useQueryClient } from 'react-query'; +import { + QueryClient, + useMutation, + useQuery, + useQueryClient, +} from 'react-query'; import useApiRequest from '../useRequest'; import { transformToCamelCase } from '@/utils'; import { downloadFile, useDownloadFile } from '../useDownloadFile'; +import T from './types'; const QueryKeys = { ImportPreview: 'ImportPreview', @@ -75,6 +81,7 @@ export function useImportFileProcess(props = {}) { { onSuccess: (res, id) => { // Invalidate queries. + invalidateResourcesOnImport(queryClient, res.data.resource); }, ...props, }, @@ -117,3 +124,98 @@ export const useSampleSheetImport = () => { }, ); }; + +/** + * Invalidates resources cached queries based on the given resource name, + * @param queryClient + * @param resource + */ +const invalidateResourcesOnImport = ( + queryClient: QueryClient, + resource: string, +) => { + debugger; + switch (resource) { + case 'Item': + queryClient.invalidateQueries(T.ITEMS); + queryClient.invalidateQueries(T.ITEM); + break; + + case 'ItemCategory': + queryClient.invalidateQueries(T.ITEMS_CATEGORIES); + break; + + case 'Bill': + queryClient.invalidateQueries(T.BILLS); + queryClient.invalidateQueries(T.BILL); + queryClient.invalidateQueries(T.ITEMS_ASSOCIATED_WITH_BILLS); + break; + + case 'SaleInvoice': + queryClient.invalidateQueries(T.SALE_INVOICE); + queryClient.invalidateQueries(T.SALE_INVOICES); + queryClient.invalidateQueries(T.ITEM_ASSOCIATED_WITH_INVOICES); + break; + + case 'SaleEstimate': + queryClient.invalidateQueries(T.SALE_ESTIMATE); + queryClient.invalidateQueries(T.SALE_ESTIMATES); + queryClient.invalidateQueries(T.ITEM_ASSOCIATED_WITH_ESTIMATES); + break; + + case 'SaleReceipt': + queryClient.invalidateQueries(T.SALE_RECEIPT); + queryClient.invalidateQueries(T.SALE_RECEIPTS); + queryClient.invalidateQueries(T.ITEM_ASSOCIATED_WITH_RECEIPTS); + break; + + case 'CreditNote': + queryClient.invalidateQueries(T.CREDIT_NOTE); + queryClient.invalidateQueries(T.CREDIT_NOTES); + break; + + case 'VendorCredit': + queryClient.invalidateQueries(T.VENDOR_CREDIT); + queryClient.invalidateQueries(T.VENDOR_CREDITS); + break; + + case 'PaymentReceive': + queryClient.invalidateQueries(T.PAYMENT_RECEIVE); + queryClient.invalidateQueries(T.PAYMENT_RECEIVES); + break; + + case 'BillPayment': + queryClient.invalidateQueries(T.BILLS_PAYMENT_TRANSACTIONS); + break; + + case 'Customer': + queryClient.invalidateQueries(T.CUSTOMERS); + queryClient.invalidateQueries(T.CUSTOMER); + break; + + case 'Vendor': + queryClient.invalidateQueries(T.VENDOR); + queryClient.invalidateQueries(T.VENDORS); + break; + + case 'Expense': + queryClient.invalidateQueries(T.EXPENSE); + queryClient.invalidateQueries(T.EXPENSES); + break; + + case 'ManualJournal': + queryClient.invalidateQueries(T.MANUAL_JOURNAL); + queryClient.invalidateQueries(T.MANUAL_JOURNALS); + break; + + case 'UncategorizedCashflowTransaction': + queryClient.invalidateQueries(T.CASH_FLOW_TRANSACTIONS); + queryClient.invalidateQueries(T.CASH_FLOW_TRANSACTIONS); + queryClient.invalidateQueries(T.CASHFLOW_ACCOUNT_TRANSACTIONS_INFINITY); + queryClient.invalidateQueries( + T.CASHFLOW_ACCOUNT_UNCATEGORIZED_TRANSACTIONS_INFINITY, + ); + queryClient.invalidateQueries(T.CASHFLOW_UNCAATEGORIZED_TRANSACTION); + break; + } +};