Files
bigcapital/packages/server/src/services/Import/ImportFileMapping.ts
2024-03-27 04:01:01 +02:00

146 lines
3.6 KiB
TypeScript

import { fromPairs } from 'lodash';
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
import {
ImportDateFormats,
ImportFileMapPOJO,
ImportMappingAttr,
} from './interfaces';
import ResourceService from '../Resource/ResourceService';
import { ServiceError } from '@/exceptions';
import { ERRORS } from './_utils';
@Service()
export class ImportFileMapping {
@Inject()
private tenancy: HasTenancyService;
@Inject()
private resource: ResourceService;
/**
* Mapping the excel sheet columns with resource columns.
* @param {number} tenantId
* @param {number} importId
* @param {ImportMappingAttr} maps
*/
public async mapping(
tenantId: number,
importId: number,
maps: ImportMappingAttr[]
): Promise<ImportFileMapPOJO> {
const { Import } = this.tenancy.models(tenantId);
const importFile = await Import.query()
.findOne('filename', importId)
.throwIfNotFound();
// Invalidate the from/to map attributes.
this.validateMapsAttrs(tenantId, importFile, maps);
// Validate the diplicated relations of map attrs.
this.validateDuplicatedMapAttrs(maps);
// Validate the date format mapping.
this.validateDateFormatMapping(tenantId, importFile.resource, maps);
const mappingStringified = JSON.stringify(maps);
await Import.query().findById(importFile.id).patch({
mapping: mappingStringified,
});
return {
import: {
importId: importFile.importId,
resource: importFile.resource,
},
};
}
/**
* Validate the mapping attributes.
* @param {number} tenantId -
* @param {} importFile -
* @param {ImportMappingAttr[]} maps
* @throws {ServiceError(ERRORS.INVALID_MAP_ATTRS)}
*/
private validateMapsAttrs(
tenantId: number,
importFile: any,
maps: ImportMappingAttr[]
) {
const fields = this.resource.getResourceImportableFields(
tenantId,
importFile.resource
);
const columnsMap = fromPairs(
importFile.columnsParsed.map((field) => [field, ''])
);
const invalid = [];
maps.forEach((map) => {
if (
'undefined' === typeof fields[map.to] ||
'undefined' === typeof columnsMap[map.from]
) {
invalid.push(map);
}
});
if (invalid.length > 0) {
throw new ServiceError(ERRORS.INVALID_MAP_ATTRS);
}
}
/**
* Validate the map attrs relation should be one-to-one relation only.
* @param {ImportMappingAttr[]} maps
*/
private validateDuplicatedMapAttrs(maps: ImportMappingAttr[]) {
const fromMap = {};
const toMap = {};
maps.forEach((map) => {
if (fromMap[map.from]) {
throw new ServiceError(ERRORS.DUPLICATED_FROM_MAP_ATTR);
} else {
fromMap[map.from] = true;
}
if (toMap[map.to]) {
throw new ServiceError(ERRORS.DUPLICATED_TO_MAP_ATTR);
} else {
toMap[map.to] = true;
}
});
}
/**
* Validates the date format mapping.
* @param {number} tenantId
* @param {string} resource
* @param {ImportMappingAttr[]} maps
*/
private validateDateFormatMapping(
tenantId: number,
resource: string,
maps: ImportMappingAttr[]
) {
const fields = this.resource.getResourceImportableFields(
tenantId,
resource
);
maps.forEach((map) => {
if (
typeof fields[map.to] !== 'undefined' &&
fields[map.to].fieldType === 'date'
) {
if (
typeof map.dateFormat !== 'undefined' &&
ImportDateFormats.indexOf(map.dateFormat) === -1
) {
throw new ServiceError(ERRORS.INVALID_MAP_DATE_FORMAT);
}
}
});
}
}