feat: linking relation with id in importing

This commit is contained in:
Ahmed Bouhuolia
2024-04-01 01:13:31 +02:00
parent 22a016b56e
commit 74da28b464
21 changed files with 394 additions and 84 deletions

View File

@@ -245,25 +245,26 @@
"account.field.currency": "Currency", "account.field.currency": "Currency",
"account.field.balance": "Balance", "account.field.balance": "Balance",
"account.field.created_at": "Created at", "account.field.created_at": "Created at",
"item.field.type": "Item type", "item.field.type": "Item Type",
"item.field.type.inventory": "Inventory", "item.field.type.inventory": "Inventory",
"item.field.type.service": "Service", "item.field.type.service": "Service",
"item.field.type.non-inventory": "Non inventory", "item.field.type.non-inventory": "Non Inventory",
"item.field.name": "Name", "item.field.name": "Item Name",
"item.field.code": "Code", "item.field.code": "Item Code",
"item.field.sellable": "Sellable", "item.field.sellable": "Sellable",
"item.field.purchasable": "Purchasable", "item.field.purchasable": "Purchasable",
"item.field.cost_price": "Cost price", "item.field.cost_price": "Cost Price",
"item.field.cost_account": "Cost account", "item.field.sell_price": "Sell Price",
"item.field.sell_account": "Sell account", "item.field.cost_account": "Cost Account",
"item.field.sell_description": "Sell description", "item.field.sell_account": "Sell Account",
"item.field.inventory_account": "Inventory account", "item.field.sell_description": "Sell Description",
"item.field.purchase_description": "Purchase description", "item.field.inventory_account": "Inventory Account",
"item.field.quantity_on_hand": "Quantity on hand", "item.field.purchase_description": "Purchase Description",
"item.field.quantity_on_hand": "Quantity on Hand",
"item.field.note": "Note", "item.field.note": "Note",
"item.field.category": "Category", "item.field.category": "Category",
"item.field.active": "Active", "item.field.active": "Active",
"item.field.created_at": "Created at", "item.field.created_at": "Created At",
"item_category.field.name": "Name", "item_category.field.name": "Name",
"item_category.field.description": "Description", "item_category.field.description": "Description",
"item_category.field.count": "Count", "item_category.field.count": "Count",

View File

@@ -344,7 +344,7 @@ export default class ItemsController extends BaseController {
const filter = { const filter = {
sortOrder: 'DESC', sortOrder: 'DESC',
columnSortBy: 'created_at', columnSortBy: 'createdAt',
page: 1, page: 1,
pageSize: 12, pageSize: 12,
inactiveMode: false, inactiveMode: false,

View File

@@ -6,7 +6,7 @@ import ItemTransactionsController from './ItemsTransactions';
@Service() @Service()
export default class ItemsBaseController { export default class ItemsBaseController {
router() { public router() {
const router = Router(); const router = Router();
router.use('/', Container.get(ItemsController).router()); router.use('/', Container.get(ItemsController).router());

View File

@@ -32,12 +32,13 @@ export interface IModelMetaFieldCommon {
name: string; name: string;
column: string; column: string;
columnable?: boolean; columnable?: boolean;
fieldType: IModelColumnType;
customQuery?: Function; customQuery?: Function;
required?: boolean; required?: boolean;
importHint?: string; importHint?: string;
importableRelationLabel?: string;
order?: number; order?: number;
unique?: number; unique?: number;
dataTransferObjectKey?: string;
} }
export interface IModelMetaFieldText { export interface IModelMetaFieldText {

View File

@@ -16,45 +16,53 @@ export default {
{ key: 'non-inventory', label: 'item.field.type.non-inventory' }, { key: 'non-inventory', label: 'item.field.type.non-inventory' },
], ],
importable: true, importable: true,
required: true,
}, },
name: { name: {
name: 'item.field.name', name: 'item.field.name',
column: 'name', column: 'name',
fieldType: 'text', fieldType: 'text',
importable: true, importable: true,
required: true,
unique: true,
}, },
code: { code: {
name: 'item.field.code', name: 'item.field.code',
column: 'code', column: 'code',
fieldType: 'text', fieldType: 'text',
importable: true, importable: true,
}, },
sellable: { sellable: {
name: 'item.field.sellable', name: 'item.field.sellable',
column: 'sellable', column: 'sellable',
fieldType: 'boolean', fieldType: 'boolean',
importable: true, importable: true,
required: true,
}, },
purchasable: { purchasable: {
name: 'item.field.purchasable', name: 'item.field.purchasable',
column: 'purchasable', column: 'purchasable',
fieldType: 'boolean', fieldType: 'boolean',
importable: true, importable: true,
required: true,
}, },
sellPrice: { sellPrice: {
name: 'item.field.cost_price', name: 'item.field.sell_price',
column: 'sell_price', column: 'sell_price',
fieldType: 'number', fieldType: 'number',
importable: true, importable: true,
required: true,
}, },
costPrice: { costPrice: {
name: 'item.field.cost_account', name: 'item.field.cost_price',
column: 'cost_price', column: 'cost_price',
fieldType: 'number', fieldType: 'number',
importable: true, importable: true,
required: true,
}, },
costAccount: { costAccount: {
name: 'item.field.sell_account', name: 'item.field.cost_account',
column: 'cost_account_id', column: 'cost_account_id',
fieldType: 'relation', fieldType: 'relation',
@@ -64,10 +72,13 @@ export default {
relationEntityLabel: 'name', relationEntityLabel: 'name',
relationEntityKey: 'slug', relationEntityKey: 'slug',
dataTransferObjectKey: 'costAccountId',
importableRelationLabel: ['name', 'code'],
importable: true, importable: true,
required: true,
}, },
sellAccount: { sellAccount: {
name: 'item.field.sell_description', name: 'item.field.sell_account',
column: 'sell_account_id', column: 'sell_account_id',
fieldType: 'relation', fieldType: 'relation',
@@ -77,11 +88,15 @@ export default {
relationEntityLabel: 'name', relationEntityLabel: 'name',
relationEntityKey: 'slug', relationEntityKey: 'slug',
importableRelationLabel: ['name', 'code'],
importable: true, importable: true,
required: true,
}, },
inventoryAccount: { inventoryAccount: {
name: 'item.field.inventory_account', name: 'item.field.inventory_account',
column: 'inventory_account_id', column: 'inventory_account_id',
fieldType: 'relation',
relationType: 'enumeration', relationType: 'enumeration',
relationKey: 'inventoryAccount', relationKey: 'inventoryAccount',
@@ -89,7 +104,10 @@ export default {
relationEntityLabel: 'name', relationEntityLabel: 'name',
relationEntityKey: 'slug', relationEntityKey: 'slug',
importableRelationLabel: ['name', 'code'],
importable: true, importable: true,
required: true,
}, },
sellDescription: { sellDescription: {
name: 'Sell description', name: 'Sell description',
@@ -107,7 +125,6 @@ export default {
name: 'item.field.quantity_on_hand', name: 'item.field.quantity_on_hand',
column: 'quantity_on_hand', column: 'quantity_on_hand',
fieldType: 'number', fieldType: 'number',
importable: true,
}, },
note: { note: {
name: 'item.field.note', name: 'item.field.note',
@@ -118,12 +135,15 @@ export default {
category: { category: {
name: 'item.field.category', name: 'item.field.category',
column: 'category_id', column: 'category_id',
fieldType: 'relation',
relationType: 'enumeration', relationType: 'enumeration',
relationKey: 'category', relationKey: 'category',
relationEntityLabel: 'name', relationEntityLabel: 'name',
relationEntityKey: 'id', relationEntityKey: 'id',
importableRelationLabel: 'name',
importable: true, importable: true,
}, },
active: { active: {

View File

@@ -4,16 +4,19 @@ export default {
sortField: 'name', sortField: 'name',
sortOrder: 'DESC', sortOrder: 'DESC',
}, },
importable: true,
fields: { fields: {
name: { name: {
name: 'item_category.field.name', name: 'item_category.field.name',
column: 'name', column: 'name',
fieldType: 'text', fieldType: 'text',
importable: true,
}, },
description: { description: {
name: 'item_category.field.description', name: 'item_category.field.description',
column: 'description', column: 'description',
fieldType: 'text', fieldType: 'text',
importable: true,
}, },
count: { count: {
name: 'item_category.field.count', name: 'item_category.field.count',

View File

@@ -1,21 +1,32 @@
import { Service } from 'typedi'; import { Inject, Service } from 'typedi';
import * as R from 'ramda'; import * as R from 'ramda';
import { isUndefined, get, chain } from 'lodash'; import bluebird from 'bluebird';
import { isUndefined, get, chain, toArray, pickBy, castArray } from 'lodash';
import { ImportMappingAttr, ResourceMetaFieldsMap } from './interfaces'; import { ImportMappingAttr, ResourceMetaFieldsMap } from './interfaces';
import { parseBoolean } from '@/utils'; import { parseBoolean } from '@/utils';
import { trimObject } from './_utils'; import { trimObject } from './_utils';
import { Account, Item } from '@/models';
import ResourceService from '../Resource/ResourceService';
import { Knex } from 'knex';
const CurrencyParsingDTOs = 10;
@Service() @Service()
export class ImportFileDataTransformer { export class ImportFileDataTransformer {
@Inject()
private resource: ResourceService;
/** /**
* *
* @param {number} tenantId - * @param {number} tenantId -
* @param {} * @param {}
*/ */
public parseSheetData( public async parseSheetData(
tenantId: number,
importFile: any, importFile: any,
importableFields: any, importableFields: any,
data: Record<string, unknown>[] data: Record<string, unknown>[],
trx?: Knex.Transaction
) { ) {
// Sanitize the sheet data. // Sanitize the sheet data.
const sanitizedData = this.sanitizeSheetData(data); const sanitizedData = this.sanitizeSheetData(data);
@@ -25,10 +36,17 @@ export class ImportFileDataTransformer {
sanitizedData, sanitizedData,
importFile.mappingParsed importFile.mappingParsed
); );
const resourceModel = this.resource.getResourceModel(
tenantId,
importFile.resource
);
// Parse the mapped sheet values. // Parse the mapped sheet values.
const parsedValues = this.parseExcelValues(importableFields, mappedDTOs); return this.parseExcelValues(
importableFields,
return parsedValues; mappedDTOs,
resourceModel,
trx
);
} }
/** /**
@@ -67,35 +85,86 @@ export class ImportFileDataTransformer {
* @param {Record<string, any>} valueDTOS - * @param {Record<string, any>} valueDTOS -
* @returns {Record<string, any>} * @returns {Record<string, any>}
*/ */
public parseExcelValues( public async parseExcelValues(
fields: ResourceMetaFieldsMap, fields: ResourceMetaFieldsMap,
valueDTOs: Record<string, any>[] valueDTOs: Record<string, any>[],
): Record<string, any> { resourceModel: any,
const parser = (value, key) => { trx?: Knex.Transaction
): Promise<Record<string, any>> {
// Prases the given object value based on the field key type.
const parser = async (value, key) => {
let _value = value; let _value = value;
const field = fields[key];
// Parses the boolean value. // Parses the boolean value.
if (fields[key].fieldType === 'boolean') { if (fields[key].fieldType === 'boolean') {
_value = parseBoolean(value, false); _value = parseBoolean(value, false);
// Parses the enumeration value. // Parses the enumeration value.
} else if (fields[key].fieldType === 'enumeration') { } else if (field.fieldType === 'enumeration') {
const field = fields[key]; const field = fields[key];
const option = get(field, 'options', []).find( const option = get(field, 'options', []).find(
(option) => option.label === value (option) => option.label === value
); );
_value = get(option, 'key'); _value = get(option, 'key');
// Prases the numeric value. // Parses the numeric value.
} else if (fields[key].fieldType === 'number') { } else if (fields[key].fieldType === 'number') {
_value = parseFloat(value); _value = parseFloat(value);
// Parses the relation value.
} else if (field.fieldType === 'relation') {
const relationModel = resourceModel.relationMappings[field.relationKey];
const RelationModel = relationModel?.modelClass;
if (!relationModel || !RelationModel) {
throw new Error(`The relation model of ${key} field is not exist.`);
}
const relationQuery = RelationModel.query(trx);
const relationKeys = field?.importableRelationLabel
? castArray(field?.importableRelationLabel)
: castArray(field?.relationEntityLabel);
relationQuery.where(function () {
relationKeys.forEach((relationKey: string) => {
this.orWhereRaw('LOWER(??) = LOWER(?)', [relationKey, value]);
});
});
const result = await relationQuery.first();
_value = get(result, 'id');
} }
return _value; return _value;
}; };
return valueDTOs.map((DTO) => {
return chain(DTO) const parseKey = (key: string) => {
.pickBy((value, key) => !isUndefined(fields[key])) const field = fields[key];
.mapValues(parser) let _objectTransferObjectKey = key;
.value();
if (field.fieldType === 'relation') {
_objectTransferObjectKey = `${key}Id`;
}
return _objectTransferObjectKey;
};
const parseAsync = async (valueDTO) => {
// Remove the undefined fields.
const _valueDTO = pickBy(
valueDTO,
(value, key) => !isUndefined(fields[key])
);
const keys = Object.keys(_valueDTO);
// Map the object values.
return bluebird.reduce(
keys,
async (acc, key) => {
const parsedValue = await parser(_valueDTO[key], key);
const parsedKey = await parseKey(key);
acc[parsedKey] = parsedValue;
return acc;
},
{}
);
};
return bluebird.map(valueDTOs, parseAsync, {
concurrency: CurrencyParsingDTOs,
}); });
} }
} }

View File

@@ -57,19 +57,31 @@ export class ImportFileProcess {
tenantId, tenantId,
importFile.resource importFile.resource
); );
// Prases the sheet json data.
const parsedData = this.importParser.parseSheetData(
importFile,
importableFields,
sheetData
);
// Runs the importing operation with ability to return errors that will happen. // Runs the importing operation with ability to return errors that will happen.
const [successedImport, failedImport] = await this.uow.withTransaction( const [successedImport, failedImport, allData] =
tenantId, await this.uow.withTransaction(
(trx: Knex.Transaction) => tenantId,
this.importCommon.import(tenantId, importFile, parsedData, trx), async (trx: Knex.Transaction) => {
trx // Prases the sheet json data.
); const parsedData = await this.importParser.parseSheetData(
tenantId,
importFile,
importableFields,
sheetData,
trx
);
const [successedImport, failedImport] =
await this.importCommon.import(
tenantId,
importFile,
parsedData,
trx
);
return [successedImport, failedImport, parsedData];
},
trx
);
const mapping = importFile.mappingParsed; const mapping = importFile.mappingParsed;
const errors = chain(failedImport) const errors = chain(failedImport)
.map((oper) => oper.error) .map((oper) => oper.error)
@@ -77,7 +89,7 @@ export class ImportFileProcess {
.value(); .value();
const unmappedColumns = getUnmappedSheetColumns(header, mapping); const unmappedColumns = getUnmappedSheetColumns(header, mapping);
const totalCount = parsedData.length; const totalCount = allData.length;
const createdCount = successedImport.length; const createdCount = successedImport.length;
const errorsCount = failedImport.length; const errorsCount = failedImport.length;

View File

@@ -4,6 +4,8 @@ import { ImportableRegistry } from './ImportableRegistry';
import { UncategorizedTransactionsImportable } from '../Cashflow/UncategorizedTransactionsImportable'; import { UncategorizedTransactionsImportable } from '../Cashflow/UncategorizedTransactionsImportable';
import { CustomersImportable } from '../Contacts/Customers/CustomersImportable'; import { CustomersImportable } from '../Contacts/Customers/CustomersImportable';
import { VendorsImportable } from '../Contacts/Vendors/VendorsImportable'; import { VendorsImportable } from '../Contacts/Vendors/VendorsImportable';
import { ItemsImportable } from '../Items/ItemsImportable';
import { ItemCategoriesImportable } from '../ItemCategories/ItemCategoriesImportable';
@Service() @Service()
export class ImportableResources { export class ImportableResources {
@@ -24,6 +26,8 @@ export class ImportableResources {
}, },
{ resource: 'Customer', importable: CustomersImportable }, { resource: 'Customer', importable: CustomersImportable },
{ resource: 'Vendor', importable: VendorsImportable }, { resource: 'Vendor', importable: VendorsImportable },
{ resource: 'Item', importable: ItemsImportable },
{ resource: 'ItemCategory', importable: ItemCategoriesImportable },
]; ];
public get registry() { public get registry() {

View File

@@ -1,5 +1,12 @@
import * as Yup from 'yup'; import * as Yup from 'yup';
import { defaultTo, upperFirst, camelCase, first, isUndefined, pickBy } from 'lodash'; import {
defaultTo,
upperFirst,
camelCase,
first,
isUndefined,
pickBy,
} from 'lodash';
import pluralize from 'pluralize'; import pluralize from 'pluralize';
import { ResourceMetaFieldsMap } from './interfaces'; import { ResourceMetaFieldsMap } from './interfaces';
import { IModelMetaField } from '@/interfaces'; import { IModelMetaField } from '@/interfaces';
@@ -83,11 +90,25 @@ export const convertFieldsToYupValidation = (fields: ResourceMetaFieldsMap) => {
if (field.required) { if (field.required) {
fieldSchema = fieldSchema.required(); fieldSchema = fieldSchema.required();
} }
yupSchema[fieldName] = fieldSchema; const _fieldName = parseFieldName(fieldName, field);
yupSchema[_fieldName] = fieldSchema;
}); });
return Yup.object().shape(yupSchema); return Yup.object().shape(yupSchema);
}; };
const parseFieldName = (fieldName: string, field: IModelMetaField) => {
let _key = fieldName;
if (field.fieldType === 'relation') {
_key = `${fieldName}Id`;
}
if (field.dataTransferObjectKey) {
_key = field.dataTransferObjectKey;
}
return _key;
};
export const getUnmappedSheetColumns = (columns, mapping) => { export const getUnmappedSheetColumns = (columns, mapping) => {
return columns.filter( return columns.filter(
(column) => !mapping.some((map) => map.from === column) (column) => !mapping.some((map) => map.from === column)

View File

@@ -0,0 +1,38 @@
import { Inject, Service } from 'typedi';
import ItemCategoriesService from './ItemCategoriesService';
import { Importable } from '../Import/Importable';
import { Knex } from 'knex';
import { IItemCategoryOTD } from '@/interfaces';
import { ItemCategoriesSampleData } from './constants';
@Service()
export class ItemCategoriesImportable extends Importable {
@Inject()
private itemCategoriesService: ItemCategoriesService;
/**
* Importing to create new item category service.
* @param {number} tenantId
* @param {any} createDTO
* @param {Knex.Transaction} trx
*/
public async importable(
tenantId: number,
createDTO: IItemCategoryOTD,
trx?: Knex.Transaction
) {
await this.itemCategoriesService.newItemCategory(
tenantId,
createDTO,
{},
trx
);
}
/**
* Item categories sample data used to download sample sheet file.
*/
public sampleData(): any[] {
return ItemCategoriesSampleData;
}
}

View File

@@ -1,6 +1,6 @@
import { Inject } from 'typedi'; import { Inject } from 'typedi';
import * as R from 'ramda'; import * as R from 'ramda';
import Knex from 'knex'; import { Knex } from 'knex';
import { ServiceError } from '@/exceptions'; import { ServiceError } from '@/exceptions';
import { import {
IItemCategory, IItemCategory,
@@ -115,7 +115,8 @@ export default class ItemCategoriesService implements IItemCategoriesService {
public async newItemCategory( public async newItemCategory(
tenantId: number, tenantId: number,
itemCategoryOTD: IItemCategoryOTD, itemCategoryOTD: IItemCategoryOTD,
authorizedUser: ISystemUser authorizedUser: ISystemUser,
trx?: Knex.Transaction
): Promise<IItemCategory> { ): Promise<IItemCategory> {
const { ItemCategory } = this.tenancy.models(tenantId); const { ItemCategory } = this.tenancy.models(tenantId);
@@ -139,20 +140,24 @@ export default class ItemCategoriesService implements IItemCategoriesService {
authorizedUser authorizedUser
); );
// Creates item category under unit-of-work evnirement. // Creates item category under unit-of-work evnirement.
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => { return this.uow.withTransaction(
// Inserts the item category. tenantId,
const itemCategory = await ItemCategory.query(trx).insert({ async (trx: Knex.Transaction) => {
...itemCategoryObj, // Inserts the item category.
}); const itemCategory = await ItemCategory.query(trx).insert({
// Triggers `onItemCategoryCreated` event. ...itemCategoryObj,
await this.eventPublisher.emitAsync(events.itemCategory.onCreated, { });
itemCategory, // Triggers `onItemCategoryCreated` event.
tenantId, await this.eventPublisher.emitAsync(events.itemCategory.onCreated, {
trx, itemCategory,
} as IItemCategoryCreatedPayload); tenantId,
trx,
} as IItemCategoryCreatedPayload);
return itemCategory; return itemCategory;
}); },
trx
);
} }
/** /**
@@ -308,7 +313,7 @@ export default class ItemCategoriesService implements IItemCategoriesService {
} as IItemCategoryDeletedPayload); } as IItemCategoryDeletedPayload);
}); });
} }
/** /**
* Parses items categories filter DTO. * Parses items categories filter DTO.
* @param {} filterDTO * @param {} filterDTO

View File

@@ -11,3 +11,25 @@ export const ERRORS = {
INVENTORY_ACCOUNT_NOT_INVENTORY: 'INVENTORY_ACCOUNT_NOT_INVENTORY', INVENTORY_ACCOUNT_NOT_INVENTORY: 'INVENTORY_ACCOUNT_NOT_INVENTORY',
CATEGORY_HAVE_ITEMS: 'CATEGORY_HAVE_ITEMS', CATEGORY_HAVE_ITEMS: 'CATEGORY_HAVE_ITEMS',
}; };
export const ItemCategoriesSampleData = [
{
Name: 'Kassulke Group',
Description: 'Optio itaque eaque qui adipisci illo sed.',
},
{
Name: 'Crist, Mraz and Lueilwitz',
Description:
'Dolores veniam deserunt sed commodi error quia veritatis non.',
},
{
Name: 'Gutmann and Sons',
Description:
'Ratione aperiam voluptas rem adipisci assumenda eos neque veritatis tempora.',
},
{
Name: 'Reichel - Raynor',
Description:
'Necessitatibus repellendus placeat possimus dolores excepturi ut.',
},
];

View File

@@ -88,7 +88,11 @@ export class CreateItem {
* @param {IItemDTO} item * @param {IItemDTO} item
* @return {Promise<IItem>} * @return {Promise<IItem>}
*/ */
public async createItem(tenantId: number, itemDTO: IItemDTO): Promise<IItem> { public async createItem(
tenantId: number,
itemDTO: IItemDTO,
trx?: Knex.Transaction
): Promise<IItem> {
const { Item } = this.tenancy.models(tenantId); const { Item } = this.tenancy.models(tenantId);
// Authorize the item before creating. // Authorize the item before creating.
@@ -111,7 +115,8 @@ export class CreateItem {
} as IItemEventCreatedPayload); } as IItemEventCreatedPayload);
return item; return item;
} },
trx
); );
return item; return item;
} }

View File

@@ -35,7 +35,10 @@ export class ItemsValidators {
} }
}); });
if (foundItems.length > 0) { if (foundItems.length > 0) {
throw new ServiceError(ERRORS.ITEM_NAME_EXISTS); throw new ServiceError(
ERRORS.ITEM_NAME_EXISTS,
'The item name is already exist.'
);
} }
} }

View File

@@ -0,0 +1,34 @@
import { Inject, Service } from 'typedi';
import { Knex } from 'knex';
import { Importable } from '@/services/Import/Importable';
import { IItemCreateDTO } from '@/interfaces';
import { CreateItem } from './CreateItem';
@Service()
export class ItemsImportable extends Importable {
@Inject()
private createItemService: CreateItem;
/**
* Mapps the imported data to create a new item service.
* @param {number} tenantId
* @param {ICustomerNewDTO} createDTO
* @param {Knex.Transaction} trx
* @returns {Promise<void>}
*/
public async importable(
tenantId: number,
createDTO: IItemCreateDTO,
trx?: Knex.Transaction<any, any[]>
): Promise<void> {
console.log(createDTO, tenantId, 'XX');
await this.createItemService.createItem(tenantId, createDTO, trx);
}
/**
* Retrieves the sample data of customers used to download sample sheet.
*/
public sampleData(): any[] {
return [];
}
}

View File

@@ -94,6 +94,10 @@ function ItemsActionsBar({
const handleTableRowSizeChange = (size) => { const handleTableRowSizeChange = (size) => {
addSetting('items', 'tableSize', size); addSetting('items', 'tableSize', size);
}; };
// Handles the import button click.
const handleImportBtnClick = () => {
history.push('/items/import');
};
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
@@ -143,6 +147,7 @@ function ItemsActionsBar({
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="file-import-16" iconSize={16} />} icon={<Icon icon="file-import-16" iconSize={16} />}
onClick={handleImportBtnClick}
text={<T id={'import'} />} text={<T id={'import'} />}
/> />
<Button <Button

View File

@@ -0,0 +1,25 @@
// @ts-nocheck
import { DashboardInsider } from '@/components';
import { ImportView } from '../Import/ImportView';
import { useHistory } from 'react-router-dom';
export default function ItemsImportpage() {
const history = useHistory();
const handleImportSuccess = () => {
history.push('/items');
};
const handleCancelBtnClick = () => {
history.push('/items');
};
return (
<DashboardInsider name={'import-items'}>
<ImportView
resource={'items'}
onImportSuccess={handleImportSuccess}
onCancelClick={handleCancelBtnClick}
exampleTitle="Items Example"
/>
</DashboardInsider>
);
}

View File

@@ -0,0 +1,25 @@
// @ts-nocheck
import { DashboardInsider } from '@/components';
import { ImportView } from '../Import/ImportView';
import { useHistory } from 'react-router-dom';
export default function ItemCategoriesImport() {
const history = useHistory();
const handleImportSuccess = () => {
history.push('/items/categories');
};
const handleCancelBtnClick = () => {
history.push('/items/categories');
};
return (
<DashboardInsider name={'import-item-categories'}>
<ImportView
resource={'item_category'}
onImportSuccess={handleImportSuccess}
onCancelClick={handleCancelBtnClick}
exampleTitle="Item Categories Example"
/>
</DashboardInsider>
);
}

View File

@@ -12,7 +12,7 @@ import {
FormattedMessage as T, FormattedMessage as T,
AdvancedFilterPopover, AdvancedFilterPopover,
DashboardFilterButton, DashboardFilterButton,
DashboardActionsBar DashboardActionsBar,
} from '@/components'; } from '@/components';
import withItemCategories from './withItemCategories'; import withItemCategories from './withItemCategories';
@@ -22,6 +22,7 @@ import withAlertActions from '@/containers/Alert/withAlertActions';
import { compose } from '@/utils'; import { compose } from '@/utils';
import { useItemsCategoriesContext } from './ItemsCategoriesProvider'; import { useItemsCategoriesContext } from './ItemsCategoriesProvider';
import { useHistory } from 'react-router-dom';
/** /**
* Items categories actions bar. * Items categories actions bar.
@@ -41,11 +42,16 @@ function ItemsCategoryActionsBar({
openAlert, openAlert,
}) { }) {
const { fields } = useItemsCategoriesContext(); const { fields } = useItemsCategoriesContext();
const history = useHistory();
const onClickNewCategory = () => { const onClickNewCategory = () => {
openDialog('item-category-form', {}); openDialog('item-category-form', {});
}; };
const handleImportBtnClick = () => {
history.push('/item/categories/import');
};
// Handle the items categories bulk delete. // Handle the items categories bulk delete.
const handelBulkDelete = () => { const handelBulkDelete = () => {
openAlert('item-categories-bulk-delete', { openAlert('item-categories-bulk-delete', {
@@ -93,6 +99,7 @@ function ItemsCategoryActionsBar({
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="file-import-16" iconSize={16} />} icon={<Icon icon="file-import-16" iconSize={16} />}
text={<T id={'import'} />} text={<T id={'import'} />}
onClick={handleImportBtnClick}
/> />
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}

View File

@@ -63,6 +63,16 @@ export const getDashboardRoutes = () => [
defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL, defaultSearchResource: RESOURCES_TYPES.MANUAL_JOURNAL,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN], subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
{
path: `/item/categories/import`,
component: lazy(
() => import('@/containers/ItemsCategories/ItemCategoriesImport'),
),
backLink: true,
pageTitle: 'Item Categories Import',
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
defaultSearchResource: RESOURCES_TYPES.ITEM,
},
{ {
path: `/items/categories`, path: `/items/categories`,
component: lazy( component: lazy(
@@ -73,9 +83,16 @@ export const getDashboardRoutes = () => [
defaultSearchResource: RESOURCES_TYPES.ITEM, defaultSearchResource: RESOURCES_TYPES.ITEM,
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN], subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
}, },
// Items. // Items.
{
path: `/items/import`,
component: lazy(() => import('@/containers/Items/ItemsImportPage')),
backLink: true,
pageTitle: 'Items Import',
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
defaultSearchResource: RESOURCES_TYPES.CUSTOMER,
},
{ {
path: `/items/:id/edit`, path: `/items/:id/edit`,
component: lazy(() => import('@/containers/Items/ItemFormPage')), component: lazy(() => import('@/containers/Items/ItemFormPage')),
@@ -514,12 +531,7 @@ export const getDashboardRoutes = () => [
// Customers // Customers
{ {
path: `/customers/import`, path: `/customers/import`,
component: lazy( component: lazy(() => import('@/containers/Customers/CustomersImport')),
() =>
import(
'@/containers/Customers/CustomersImport'
),
),
backLink: true, backLink: true,
pageTitle: 'Customers Import', pageTitle: 'Customers Import',
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN], subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],
@@ -577,9 +589,7 @@ export const getDashboardRoutes = () => [
// Vendors // Vendors
{ {
path: `/vendors/import`, path: `/vendors/import`,
component: lazy( component: lazy(() => import('@/containers/Vendors/VendorsImport')),
() => import('@/containers/Vendors/VendorsImport'),
),
backLink: true, backLink: true,
pageTitle: 'Vendors Import', pageTitle: 'Vendors Import',
subscriptionActive: [SUBSCRIPTION_TYPE.MAIN], subscriptionActive: [SUBSCRIPTION_TYPE.MAIN],