refactor: import resource module to nestjs

This commit is contained in:
Ahmed Bouhuolia
2025-01-13 10:15:57 +02:00
parent 270b421a6c
commit 72818759a5
38 changed files with 479 additions and 424 deletions

View File

@@ -2,6 +2,7 @@ import { Knex } from 'knex';
import { IItemEntryDTO } from '../TransactionItemEntry/ItemEntry.types'; import { IItemEntryDTO } from '../TransactionItemEntry/ItemEntry.types';
import { AttachmentLinkDTO } from '../Attachments/Attachments.types'; import { AttachmentLinkDTO } from '../Attachments/Attachments.types';
import { Bill } from './models/Bill'; import { Bill } from './models/Bill';
import { IDynamicListFilter } from '../DynamicListing/DynamicFilter/DynamicFilter.types';
export interface IBillDTO { export interface IBillDTO {
vendorId: number; vendorId: number;
@@ -42,12 +43,12 @@ export interface IBillEditDTO {
attachments?: AttachmentLinkDTO[]; attachments?: AttachmentLinkDTO[];
} }
// export interface IBillsFilter extends IDynamicListFilterDTO { export interface IBillsFilter extends IDynamicListFilter {
// stringifiedFilterRoles?: string; stringifiedFilterRoles?: string;
// page: number; page: number;
// pageSize: number; pageSize: number;
// filterQuery?: (q: any) => void; filterQuery?: (q: any) => void;
// } }
export interface IBillCreatedPayload { export interface IBillCreatedPayload {
// tenantId: number; // tenantId: number;

View File

@@ -1,25 +1,26 @@
import { Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import * as R from 'ramda'; import * as R from 'ramda';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service'; import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service'; import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { Bill } from '../models/Bill'; import { Bill } from '../models/Bill';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { BillTransformer } from './Bill.transformer'; import { BillTransformer } from './Bill.transformer';
import { IBillsFilter } from '../Bills.types';
@Injectable() @Injectable()
export class GetBillsService { export class GetBillsService {
constructor( constructor(
private transformer: TransformerInjectable, private transformer: TransformerInjectable,
private dynamicListService: DynamicListService, private dynamicListService: DynamicListService,
@Inject(Bill.name) private billModel: typeof Bill,
) {} ) {}
/** /**
* Retrieve bills data table list. * Retrieve bills data table list.
* @param {IBillsFilter} billsFilter - * @param {IBillsFilter} billsFilter -
*/ */
public async getBills( public async getBills(filterDTO: IBillsFilter): Promise<{
filterDTO: IBillsFilter,
): Promise<{
bills: Bill; bills: Bill;
pagination: IPaginationMeta; pagination: IPaginationMeta;
filterMeta: IFilterMeta; filterMeta: IFilterMeta;
@@ -32,7 +33,8 @@ export class GetBillsService {
Bill, Bill,
filter, filter,
); );
const { results, pagination } = await Bill.query() const { results, pagination } = await this.billModel
.query()
.onBuild((builder) => { .onBuild((builder) => {
builder.withGraphFetched('vendor'); builder.withGraphFetched('vendor');
builder.withGraphFetched('entries.item'); builder.withGraphFetched('entries.item');

View File

@@ -7,7 +7,9 @@ import { GetCreditNotePdf } from './queries/GetCreditNotePdf.serivce';
import { import {
ICreditNoteEditDTO, ICreditNoteEditDTO,
ICreditNoteNewDTO, ICreditNoteNewDTO,
ICreditNotesQueryDTO,
} from './types/CreditNotes.types'; } from './types/CreditNotes.types';
import { GetCreditNotesService } from './queries/GetCreditNotes.service';
@Injectable() @Injectable()
export class CreditNoteApplication { export class CreditNoteApplication {
@@ -17,6 +19,7 @@ export class CreditNoteApplication {
private openCreditNoteService: OpenCreditNoteService, private openCreditNoteService: OpenCreditNoteService,
private deleteCreditNoteService: DeleteCreditNoteService, private deleteCreditNoteService: DeleteCreditNoteService,
private getCreditNotePdfService: GetCreditNotePdf, private getCreditNotePdfService: GetCreditNotePdf,
private getCreditNotesService: GetCreditNotesService,
) {} ) {}
/** /**
@@ -67,4 +70,13 @@ export class CreditNoteApplication {
getCreditNotePdf(creditNoteId: number) { getCreditNotePdf(creditNoteId: number) {
return this.getCreditNotePdfService.getCreditNotePdf(creditNoteId); return this.getCreditNotePdfService.getCreditNotePdf(creditNoteId);
} }
/**
* Retrieves the credit notes list.
* @param {ICreditNotesQueryDTO} creditNotesQuery
* @returns {Promise<GetCreditNotesResponse>}
*/
getCreditNotes(creditNotesQuery: ICreditNotesQueryDTO) {
return this.getCreditNotesService.getCreditNotesList(creditNotesQuery);
}
} }

View File

@@ -1,8 +1,18 @@
import { Body, Controller, Delete, Param, Post, Put } from '@nestjs/common'; import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
Query,
} from '@nestjs/common';
import { CreditNoteApplication } from './CreditNoteApplication.service'; import { CreditNoteApplication } from './CreditNoteApplication.service';
import { import {
ICreditNoteEditDTO, ICreditNoteEditDTO,
ICreditNoteNewDTO, ICreditNoteNewDTO,
ICreditNotesQueryDTO,
} from './types/CreditNotes.types'; } from './types/CreditNotes.types';
import { PublicRoute } from '../Auth/Jwt.guard'; import { PublicRoute } from '../Auth/Jwt.guard';
@@ -19,6 +29,11 @@ export class CreditNotesController {
return this.creditNoteApplication.createCreditNote(creditNoteDTO); return this.creditNoteApplication.createCreditNote(creditNoteDTO);
} }
@Get()
getCreditNotes(@Query() creditNotesQuery: ICreditNotesQueryDTO) {
return this.creditNoteApplication.getCreditNotes(creditNotesQuery);
}
@Put(':id') @Put(':id')
editCreditNote( editCreditNote(
@Param('id') creditNoteId: number, @Param('id') creditNoteId: number,

View File

@@ -1,68 +1,67 @@
// import { Service, Inject } from 'typedi'; import * as R from 'ramda';
// import * as R from 'ramda'; import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
// import { ICreditNotesQueryDTO } from '@/interfaces'; import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
// import DynamicListingService from '@/services/DynamicListing/DynamicListService'; import { ICreditNotesQueryDTO } from '../types/CreditNotes.types';
// import BaseCreditNotes from './commands/CommandCreditNoteDTOTransform.service'; import { CreditNote } from '../models/CreditNote';
// import { CreditNoteTransformer } from './queries/CreditNoteTransformer'; import { CreditNoteTransformer } from './CreditNoteTransformer';
// import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable'; import { Inject } from '@nestjs/common';
import { Injectable } from '@nestjs/common';
// @Service() @Injectable()
// export default class ListCreditNotes extends BaseCreditNotes { export class GetCreditNotesService {
// @Inject() constructor(
// private dynamicListService: DynamicListingService; private readonly dynamicListService: DynamicListService,
private readonly transformer: TransformerInjectable,
// @Inject() @Inject(CreditNote.name)
// private transformer: TransformerInjectable; private readonly creditNoteModel: typeof CreditNote,
) {}
// /** /**
// * Parses the sale invoice list filter DTO. * Parses the sale invoice list filter DTO.
// * @param filterDTO * @param filterDTO
// * @returns * @returns
// */ */
// private parseListFilterDTO = (filterDTO) => { private parseListFilterDTO = (filterDTO) => {
// return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO); return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
// }; };
// /** /**
// * Retrieves the paginated and filterable credit notes list. * Retrieves the paginated and filterable credit notes list.
// * @param {number} tenantId - * @param {number} tenantId -
// * @param {ICreditNotesQueryDTO} creditNotesQuery - * @param {ICreditNotesQueryDTO} creditNotesQuery -
// */ */
// public getCreditNotesList = async ( public async getCreditNotesList(
// tenantId: number, creditNotesQuery: ICreditNotesQueryDTO,
// creditNotesQuery: ICreditNotesQueryDTO ): Promise<GetCreditNotesResponse> {
// ) => { // Parses stringified filter roles.
// const { CreditNote } = this.tenancy.models(tenantId); const filter = this.parseListFilterDTO(creditNotesQuery);
// // Parses stringified filter roles. // Dynamic list service.
// const filter = this.parseListFilterDTO(creditNotesQuery); const dynamicFilter = await this.dynamicListService.dynamicList(
CreditNote,
filter,
);
const { results, pagination } = await this.creditNoteModel
.query()
.onBuild((builder) => {
builder.withGraphFetched('entries.item');
builder.withGraphFetched('customer');
dynamicFilter.buildQuery()(builder);
creditNotesQuery?.filterQuery && creditNotesQuery?.filterQuery(builder);
})
.pagination(filter.page - 1, filter.pageSize);
// // Dynamic list service. // Transforomes the credit notes to POJO.
// const dynamicFilter = await this.dynamicListService.dynamicList( const creditNotes = await this.transformer.transform(
// tenantId, results,
// CreditNote, new CreditNoteTransformer(),
// filter );
// );
// const { results, pagination } = await CreditNote.query()
// .onBuild((builder) => {
// builder.withGraphFetched('entries.item');
// builder.withGraphFetched('customer');
// dynamicFilter.buildQuery()(builder);
// creditNotesQuery?.filterQuery && creditNotesQuery?.filterQuery(builder);
// })
// .pagination(filter.page - 1, filter.pageSize);
// // Transforomes the credit notes to POJO. return {
// const creditNotes = await this.transformer.transform( creditNotes,
// tenantId, pagination,
// results, filterMeta: dynamicFilter.getResponseMeta(),
// new CreditNoteTransformer() };
// ); }
}
// return {
// creditNotes,
// pagination,
// filterMeta: dynamicFilter.getResponseMeta(),
// };
// };
// }

View File

@@ -11,7 +11,7 @@ import { CreditNoteInventoryTransactions } from '../commands/CreditNotesInventor
@Injectable() @Injectable()
export class CreditNoteInventoryTransactionsSubscriber { export class CreditNoteInventoryTransactionsSubscriber {
constructor( constructor(
private readonly inventoryTransactions: CreditNoteInventoryTransactions; private readonly inventoryTransactions: CreditNoteInventoryTransactions,
) {} ) {}
/** /**
@@ -30,9 +30,9 @@ export class CreditNoteInventoryTransactionsSubscriber {
await this.inventoryTransactions.createInventoryTransactions( await this.inventoryTransactions.createInventoryTransactions(
creditNote, creditNote,
trx trx,
); );
}; }
/** /**
* Rewrites inventory transactions once credit note edited. * Rewrites inventory transactions once credit note edited.
@@ -48,11 +48,11 @@ export class CreditNoteInventoryTransactionsSubscriber {
if (!creditNote.isOpen) return; if (!creditNote.isOpen) return;
await this.inventoryTransactions.editInventoryTransactions( await this.inventoryTransactions.editInventoryTransactions(
creditNoteId, creditNote.id,
creditNote, creditNote,
trx trx,
); );
}; }
/** /**
* Reverts inventory transactions once credit note deleted. * Reverts inventory transactions once credit note deleted.
@@ -67,8 +67,8 @@ export class CreditNoteInventoryTransactionsSubscriber {
if (!oldCreditNote.isOpen) return; if (!oldCreditNote.isOpen) return;
await this.inventoryTransactions.deleteInventoryTransactions( await this.inventoryTransactions.deleteInventoryTransactions(
creditNoteId, oldCreditNote.id,
trx trx,
); );
}; }
} }

View File

@@ -3,8 +3,11 @@ import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectab
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import * as R from 'ramda'; import * as R from 'ramda';
import { Customer } from '../models/Customer'; import { Customer } from '../models/Customer';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { CustomerTransfromer } from './CustomerTransformer'; import { CustomerTransfromer } from './CustomerTransformer';
import {
GetCustomersResponse,
ICustomersFilter,
} from '../types/Customers.types';
@Injectable() @Injectable()
export class GetCustomers { export class GetCustomers {
@@ -26,12 +29,11 @@ export class GetCustomers {
/** /**
* Retrieve customers paginated list. * Retrieve customers paginated list.
* @param {ICustomersFilter} filter - Cusotmers filter. * @param {ICustomersFilter} filter - Cusotmers filter.
* @returns {Promise<GetCustomersResponse>}
*/ */
public async getCustomersList(filterDTO: ICustomersFilter): Promise<{ public async getCustomersList(
customers: Customer[]; filterDTO: ICustomersFilter,
pagination: IPaginationMeta; ): Promise<GetCustomersResponse> {
filterMeta: IFilterMeta;
}> {
// Parses customers list filter DTO. // Parses customers list filter DTO.
const filter = this.parseCustomersListFilterDTO(filterDTO); const filter = this.parseCustomersListFilterDTO(filterDTO);

View File

@@ -1,6 +1,8 @@
import { Knex } from 'knex'; import { Knex } from 'knex';
import { Customer } from '../models/Customer'; import { Customer } from '../models/Customer';
import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types'; import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
// Customer Interfaces. // Customer Interfaces.
// ---------------------------------- // ----------------------------------
@@ -47,12 +49,17 @@ export interface ICustomerEditDTO extends IContactAddressDTO {
active?: boolean; active?: boolean;
} }
// export interface ICustomersFilter extends IDynamicListFilter { export interface ICustomersFilter extends IDynamicListFilter {
// stringifiedFilterRoles?: string; stringifiedFilterRoles?: string;
// page?: number; page?: number;
// pageSize?: number; pageSize?: number;
// } }
export interface GetCustomersResponse {
customers: Customer[];
pagination: IPaginationMeta;
filterMeta: IFilterMeta;
}
// Customer Events. // Customer Events.
// ---------------------------------- // ----------------------------------
export interface ICustomerEventCreatedPayload { export interface ICustomerEventCreatedPayload {

View File

@@ -1,3 +1,4 @@
// @ts-nocheck
import { OPERATION } from '@/libs/logic-evaluation/Parser'; import { OPERATION } from '@/libs/logic-evaluation/Parser';
export default class QueryParser { export default class QueryParser {

View File

@@ -1,7 +1,7 @@
import { Service } from 'typedi';
import { AsyncLocalStorage } from 'async_hooks'; import { AsyncLocalStorage } from 'async_hooks';
import { Injectable } from '@nestjs/common';
@Service() @Injectable()
export class ImportAls { export class ImportAls {
private als: AsyncLocalStorage<Map<string, any>>; private als: AsyncLocalStorage<Map<string, any>>;

View File

@@ -1,6 +1,5 @@
import bluebird from 'bluebird'; import bluebird from 'bluebird';
import * as R from 'ramda'; import * as R from 'ramda';
import { Inject, Service } from 'typedi';
import { first } from 'lodash'; import { first } from 'lodash';
import { ImportFileDataValidator } from './ImportFileDataValidator'; import { ImportFileDataValidator } from './ImportFileDataValidator';
import { Knex } from 'knex'; import { Knex } from 'knex';
@@ -10,40 +9,35 @@ import {
ImportOperSuccess, ImportOperSuccess,
ImportableContext, ImportableContext,
} from './interfaces'; } from './interfaces';
import { ServiceError } from '@/exceptions';
import { getUniqueImportableValue, trimObject } from './_utils'; import { getUniqueImportableValue, trimObject } from './_utils';
import { ImportableResources } from './ImportableResources'; import { ImportableResources } from './ImportableResources';
import ResourceService from '../Resource/ResourceService'; import { ResourceService } from '../Resource/ResourceService';
import { Import } from '@/system/models'; import { Import } from '@/system/models';
import { Injectable } from '@nestjs/common';
import { ServiceError } from '../Items/ServiceError';
@Service() @Injectable()
export class ImportFileCommon { export class ImportFileCommon {
@Inject() constructor(
private importFileValidator: ImportFileDataValidator; private readonly importFileValidator: ImportFileDataValidator,
private readonly importable: ImportableResources,
@Inject() private readonly resource: ResourceService,
private importable: ImportableResources; ) {}
@Inject()
private resource: ResourceService;
/** /**
* Imports the given parsed data to the resource storage through registered importable service. * Imports the given parsed data to the resource storage through registered importable service.
* @param {number} tenantId -
* @param {string} resourceName - Resource name. * @param {string} resourceName - Resource name.
* @param {Record<string, any>} parsedData - Parsed data. * @param {Record<string, any>} parsedData - Parsed data.
* @param {Knex.Transaction} trx - Knex transaction. * @param {Knex.Transaction} trx - Knex transaction.
* @returns {Promise<[ImportOperSuccess[], ImportOperError[]]>} * @returns {Promise<[ImportOperSuccess[], ImportOperError[]]>}
*/ */
public async import( public async import(
tenantId: number,
importFile: Import, importFile: Import,
parsedData: Record<string, any>[], parsedData: Record<string, any>[],
trx?: Knex.Transaction trx?: Knex.Transaction,
): Promise<[ImportOperSuccess[], ImportOperError[]]> { ): Promise<[ImportOperSuccess[], ImportOperError[]]> {
const resourceFields = this.resource.getResourceFields2( const resourceFields = this.resource.getResourceFields2(
tenantId, importFile.resource,
importFile.resource
); );
const ImportableRegistry = this.importable.registry; const ImportableRegistry = this.importable.registry;
const importable = ImportableRegistry.getImportable(importFile.resource); const importable = ImportableRegistry.getImportable(importFile.resource);
@@ -69,14 +63,14 @@ export class ImportFileCommon {
// Validate the DTO object before passing it to the service layer. // Validate the DTO object before passing it to the service layer.
await this.importFileValidator.validateData( await this.importFileValidator.validateData(
resourceFields, resourceFields,
transformedDTO transformedDTO,
); );
try { try {
// Run the importable function and listen to the errors. // Run the importable function and listen to the errors.
const data = await importable.importable( const data = await importable.importable(
tenantId, tenantId,
transformedDTO, transformedDTO,
trx trx,
); );
success.push({ index, data }); success.push({ index, data });
} catch (err) { } catch (err) {
@@ -117,7 +111,7 @@ export class ImportFileCommon {
*/ */
public async validateParamsSchema( public async validateParamsSchema(
resourceName: string, resourceName: string,
params: Record<string, any> params: Record<string, any>,
) { ) {
const ImportableRegistry = this.importable.registry; const ImportableRegistry = this.importable.registry;
const importable = ImportableRegistry.getImportable(resourceName); const importable = ImportableRegistry.getImportable(resourceName);
@@ -143,7 +137,7 @@ export class ImportFileCommon {
public async validateParams( public async validateParams(
tenantId: number, tenantId: number,
resourceName: string, resourceName: string,
params: Record<string, any> params: Record<string, any>,
) { ) {
const ImportableRegistry = this.importable.registry; const ImportableRegistry = this.importable.registry;
const importable = ImportableRegistry.getImportable(resourceName); const importable = ImportableRegistry.getImportable(resourceName);

View File

@@ -1,4 +1,3 @@
import { Inject, Service } from 'typedi';
import bluebird from 'bluebird'; import bluebird from 'bluebird';
import { isUndefined, pickBy, set } from 'lodash'; import { isUndefined, pickBy, set } from 'lodash';
import { Knex } from 'knex'; import { Knex } from 'knex';
@@ -11,17 +10,15 @@ import {
sanitizeSheetData, sanitizeSheetData,
getMapToPath, getMapToPath,
} from './_utils'; } from './_utils';
import ResourceService from '../Resource/ResourceService'; import { ResourceService } from '../Resource/ResourceService';
import HasTenancyService from '../Tenancy/TenancyService';
import { CurrencyParsingDTOs } from './_constants'; import { CurrencyParsingDTOs } from './_constants';
import { Injectable } from '@nestjs/common';
@Service() @Injectable()
export class ImportFileDataTransformer { export class ImportFileDataTransformer {
@Inject() constructor(
private resource: ResourceService; private readonly resource: ResourceService,
) {}
@Inject()
private tenancy: HasTenancyService;
/** /**
* Parses the given sheet data before passing to the service layer. * Parses the given sheet data before passing to the service layer.
@@ -30,7 +27,6 @@ export class ImportFileDataTransformer {
* @param {} * @param {}
*/ */
public async parseSheetData( public async parseSheetData(
tenantId: number,
importFile: any, importFile: any,
importableFields: ResourceMetaFieldsMap, importableFields: ResourceMetaFieldsMap,
data: Record<string, unknown>[], data: Record<string, unknown>[],
@@ -46,13 +42,11 @@ export class ImportFileDataTransformer {
); );
// Parse the mapped sheet values. // Parse the mapped sheet values.
const parsedValues = await this.parseExcelValues( const parsedValues = await this.parseExcelValues(
tenantId,
importableFields, importableFields,
mappedDTOs, mappedDTOs,
trx trx
); );
const aggregateValues = this.aggregateParsedValues( const aggregateValues = this.aggregateParsedValues(
tenantId,
importFile.resource, importFile.resource,
parsedValues parsedValues
); );
@@ -66,13 +60,12 @@ export class ImportFileDataTransformer {
* @param {Record<string, any>} parsedData * @param {Record<string, any>} parsedData
* @returns {Record<string, any>[]} * @returns {Record<string, any>[]}
*/ */
public aggregateParsedValues = ( public aggregateParsedValues(
tenantId: number,
resourceName: string, resourceName: string,
parsedData: Record<string, any>[] parsedData: Record<string, any>[]
): Record<string, any>[] => { ): Record<string, any>[] {
let _value = parsedData; let _value = parsedData;
const meta = this.resource.getResourceMeta(tenantId, resourceName); const meta = this.resource.getResourceMeta(resourceName);
if (meta.importAggregator === 'group') { if (meta.importAggregator === 'group') {
_value = aggregate( _value = aggregate(
@@ -113,7 +106,6 @@ export class ImportFileDataTransformer {
* @returns {Record<string, any>} * @returns {Record<string, any>}
*/ */
public async parseExcelValues( public async parseExcelValues(
tenantId: number,
fields: ResourceMetaFieldsMap, fields: ResourceMetaFieldsMap,
valueDTOs: Record<string, any>[], valueDTOs: Record<string, any>[],
trx?: Knex.Transaction trx?: Knex.Transaction

View File

@@ -1,10 +1,11 @@
import { Service } from 'typedi';
import { ImportInsertError, ResourceMetaFieldsMap } from './interfaces'; import { ImportInsertError, ResourceMetaFieldsMap } from './interfaces';
import { ERRORS, convertFieldsToYupValidation } from './_utils'; import { ERRORS, convertFieldsToYupValidation } from './_utils';
import { IModelMeta } from '@/interfaces'; import { Injectable } from '@nestjs/common';
import { ServiceError } from '@/exceptions'; import { IModelMeta } from '@/interfaces/Model';
import { ServiceError } from '../Items/ServiceError';
@Service() @Injectable()
export class ImportFileDataValidator { export class ImportFileDataValidator {
/** /**
* Validates the given resource is importable. * Validates the given resource is importable.

View File

@@ -1,19 +1,18 @@
import { Injectable } from '@nestjs/common';
import { fromPairs, isUndefined } from 'lodash'; import { fromPairs, isUndefined } from 'lodash';
import { Inject, Service } from 'typedi';
import { import {
ImportDateFormats, ImportDateFormats,
ImportFileMapPOJO, ImportFileMapPOJO,
ImportMappingAttr, ImportMappingAttr,
} from './interfaces'; } from './interfaces';
import ResourceService from '../Resource/ResourceService'; import { ResourceService } from '../Resource/ResourceService';
import { ServiceError } from '@/exceptions'; import { ServiceError } from '../Items/ServiceError';
import { ERRORS } from './_utils'; import { ERRORS } from './_utils';
import { Import } from '@/system/models'; import { Import } from './models/Import';
@Service() @Injectable()
export class ImportFileMapping { export class ImportFileMapping {
@Inject() constructor(private readonly resource: ResourceService) {}
private resource: ResourceService;
/** /**
* Mapping the excel sheet columns with resource columns. * Mapping the excel sheet columns with resource columns.
@@ -22,16 +21,15 @@ export class ImportFileMapping {
* @param {ImportMappingAttr} maps * @param {ImportMappingAttr} maps
*/ */
public async mapping( public async mapping(
tenantId: number,
importId: string, importId: string,
maps: ImportMappingAttr[] maps: ImportMappingAttr[],
): Promise<ImportFileMapPOJO> { ): Promise<ImportFileMapPOJO> {
const importFile = await Import.query() const importFile = await Import.query()
.findOne('filename', importId) .findOne('filename', importId)
.throwIfNotFound(); .throwIfNotFound();
// Invalidate the from/to map attributes. // Invalidate the from/to map attributes.
this.validateMapsAttrs(tenantId, importFile, maps); this.validateMapsAttrs(importFile, maps);
// @todo validate the required fields. // @todo validate the required fields.
@@ -39,7 +37,7 @@ export class ImportFileMapping {
this.validateDuplicatedMapAttrs(maps); this.validateDuplicatedMapAttrs(maps);
// Validate the date format mapping. // Validate the date format mapping.
this.validateDateFormatMapping(tenantId, importFile.resource, maps); this.validateDateFormatMapping(importFile.resource, maps);
const mappingStringified = JSON.stringify(maps); const mappingStringified = JSON.stringify(maps);
@@ -61,17 +59,10 @@ export class ImportFileMapping {
* @param {ImportMappingAttr[]} maps * @param {ImportMappingAttr[]} maps
* @throws {ServiceError(ERRORS.INVALID_MAP_ATTRS)} * @throws {ServiceError(ERRORS.INVALID_MAP_ATTRS)}
*/ */
private validateMapsAttrs( private validateMapsAttrs(importFile: any, maps: ImportMappingAttr[]) {
tenantId: number, const fields = this.resource.getResourceFields2(importFile.resource);
importFile: any,
maps: ImportMappingAttr[]
) {
const fields = this.resource.getResourceFields2(
tenantId,
importFile.resource
);
const columnsMap = fromPairs( const columnsMap = fromPairs(
importFile.columnsParsed.map((field) => [field, '']) importFile.columnsParsed.map((field) => [field, '']),
); );
const invalid = []; const invalid = [];
@@ -130,14 +121,10 @@ export class ImportFileMapping {
* @param {ImportMappingAttr[]} maps * @param {ImportMappingAttr[]} maps
*/ */
private validateDateFormatMapping( private validateDateFormatMapping(
tenantId: number,
resource: string, resource: string,
maps: ImportMappingAttr[] maps: ImportMappingAttr[],
) { ) {
const fields = this.resource.getResourceImportableFields( const fields = this.resource.getResourceImportableFields(resource);
tenantId,
resource
);
// @todo Validate date type of the nested fields. // @todo Validate date type of the nested fields.
maps.forEach((map) => { maps.forEach((map) => {
if ( if (

View File

@@ -1,16 +1,11 @@
import { Inject, Service } from 'typedi'; import { Import } from './models/Import';
import HasTenancyService from '../Tenancy/TenancyService';
import { TransformerInjectable } from '@/lib/Transformer/TransformerInjectable';
import { ImportFileMetaTransformer } from './ImportFileMetaTransformer'; import { ImportFileMetaTransformer } from './ImportFileMetaTransformer';
import { Import } from '@/system/models'; import { Injectable } from '@nestjs/common';
import { TransformerInjectable } from '../Transformer/TransformerInjectable.service';
@Service() @Injectable()
export class ImportFileMeta { export class ImportFileMeta {
@Inject() constructor(private readonly transformer: TransformerInjectable) {}
private tenancy: HasTenancyService;
@Inject()
private transformer: TransformerInjectable;
/** /**
* Retrieves the import meta of the given import model id. * Retrieves the import meta of the given import model id.
@@ -18,16 +13,15 @@ export class ImportFileMeta {
* @param {number} importId * @param {number} importId
* @returns {} * @returns {}
*/ */
async getImportMeta(tenantId: number, importId: string) { async getImportMeta(importId: string) {
const importFile = await Import.query() const importFile = await Import.query()
.where('tenantId', tenantId) .where('tenantId', tenantId)
.findOne('importId', importId); .findOne('importId', importId);
// Retrieves the transformed accounts collection. // Retrieves the transformed accounts collection.
return this.transformer.transform( return this.transformer.transform(
tenantId,
importFile, importFile,
new ImportFileMetaTransformer() new ImportFileMetaTransformer(),
); );
} }
} }

View File

@@ -1,4 +1,4 @@
import { Transformer } from '@/lib/Transformer/Transformer'; import { Transformer } from '../Transformer/Transformer';
export class ImportFileMetaTransformer extends Transformer { export class ImportFileMetaTransformer extends Transformer {
/** /**
@@ -11,7 +11,7 @@ export class ImportFileMetaTransformer extends Transformer {
public excludeAttributes = (): string[] => { public excludeAttributes = (): string[] => {
return ['id', 'filename', 'columns', 'mappingParsed', 'mapping']; return ['id', 'filename', 'columns', 'mappingParsed', 'mapping'];
} };
map(importFile) { map(importFile) {
return importFile.mappingParsed; return importFile.mappingParsed;

View File

@@ -1,49 +1,36 @@
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
import { ImportFilePreviewPOJO } from './interfaces'; import { ImportFilePreviewPOJO } from './interfaces';
import { ImportFileProcess } from './ImportFileProcess'; import { ImportFileProcess } from './ImportFileProcess';
import { ImportAls } from './ImportALS'; import { ImportAls } from './ImportALS';
import { Injectable } from '@nestjs/common';
@Service() @Injectable()
export class ImportFilePreview { export class ImportFilePreview {
@Inject() constructor(
private tenancy: HasTenancyService; private readonly importFile: ImportFileProcess,
private readonly importAls: ImportAls,
@Inject() ) {}
private importFile: ImportFileProcess;
@Inject()
private importAls: ImportAls;
/** /**
* Preview the imported file results before commiting the transactions. * Preview the imported file results before commiting the transactions.
* @param {number} tenantId -
* @param {string} importId - * @param {string} importId -
* @returns {Promise<ImportFilePreviewPOJO>} * @returns {Promise<ImportFilePreviewPOJO>}
*/ */
public async preview( public async preview(importId: string): Promise<ImportFilePreviewPOJO> {
tenantId: number,
importId: string
): Promise<ImportFilePreviewPOJO> {
return this.importAls.runPreview<Promise<ImportFilePreviewPOJO>>(() => return this.importAls.runPreview<Promise<ImportFilePreviewPOJO>>(() =>
this.previewAlsRun(tenantId, importId) this.previewAlsRun(importId),
); );
} }
/** /**
* Preview the imported file results before commiting the transactions. * Preview the imported file results before commiting the transactions.
* @param {number} tenantId
* @param {number} importId * @param {number} importId
* @returns {Promise<ImportFilePreviewPOJO>} * @returns {Promise<ImportFilePreviewPOJO>}
*/ */
public async previewAlsRun( public async previewAlsRun(importId: string): Promise<ImportFilePreviewPOJO> {
tenantId: number,
importId: string
): Promise<ImportFilePreviewPOJO> {
const knex = this.tenancy.knex(tenantId); const knex = this.tenancy.knex(tenantId);
const trx = await knex.transaction({ isolationLevel: 'read uncommitted' }); const trx = await knex.transaction({ isolationLevel: 'read uncommitted' });
const meta = await this.importFile.import(tenantId, importId, trx); const meta = await this.importFile.import(importId, trx);
// Rollback the successed transaction. // Rollback the successed transaction.
await trx.rollback(); await trx.rollback();

View File

@@ -1,29 +1,23 @@
import { Inject, Service } from 'typedi';
import { chain } from 'lodash'; import { chain } from 'lodash';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { ServiceError } from '@/exceptions';
import { ERRORS, getUnmappedSheetColumns, readImportFile } from './_utils'; import { ERRORS, getUnmappedSheetColumns, readImportFile } from './_utils';
import { ImportFileCommon } from './ImportFileCommon'; import { ImportFileCommon } from './ImportFileCommon';
import { ImportFileDataTransformer } from './ImportFileDataTransformer'; import { ImportFileDataTransformer } from './ImportFileDataTransformer';
import ResourceService from '../Resource/ResourceService';
import UnitOfWork from '../UnitOfWork';
import { ImportFilePreviewPOJO } from './interfaces'; import { ImportFilePreviewPOJO } from './interfaces';
import { Import } from '@/system/models';
import { parseSheetData } from './sheet_utils'; import { parseSheetData } from './sheet_utils';
import { Injectable } from '@nestjs/common';
import { ResourceService } from '../Resource/ResourceService';
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
import { ServiceError } from '../Items/ServiceError';
@Service() @Injectable()
export class ImportFileProcess { export class ImportFileProcess {
@Inject() constructor(
private resource: ResourceService; private readonly resource: ResourceService,
private readonly importCommon: ImportFileCommon,
@Inject() private readonly importParser: ImportFileDataTransformer,
private importCommon: ImportFileCommon; private readonly uow: UnitOfWork,
) {}
@Inject()
private importParser: ImportFileDataTransformer;
@Inject()
private uow: UnitOfWork;
/** /**
* Preview the imported file results before commiting the transactions. * Preview the imported file results before commiting the transactions.
@@ -32,9 +26,8 @@ export class ImportFileProcess {
* @returns {Promise<ImportFilePreviewPOJO>} * @returns {Promise<ImportFilePreviewPOJO>}
*/ */
public async import( public async import(
tenantId: number,
importId: string, importId: string,
trx?: Knex.Transaction trx?: Knex.Transaction,
): Promise<ImportFilePreviewPOJO> { ): Promise<ImportFilePreviewPOJO> {
const importFile = await Import.query() const importFile = await Import.query()
.findOne('importId', importId) .findOne('importId', importId)
@@ -51,7 +44,7 @@ export class ImportFileProcess {
const [sheetData, sheetColumns] = parseSheetData(buffer); const [sheetData, sheetColumns] = parseSheetData(buffer);
const resource = importFile.resource; const resource = importFile.resource;
const resourceFields = this.resource.getResourceFields2(tenantId, resource); const resourceFields = this.resource.getResourceFields2(resource);
// 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, allData] = const [successedImport, failedImport, allData] =
@@ -64,18 +57,18 @@ export class ImportFileProcess {
importFile, importFile,
resourceFields, resourceFields,
sheetData, sheetData,
trx trx,
); );
const [successedImport, failedImport] = const [successedImport, failedImport] =
await this.importCommon.import( await this.importCommon.import(
tenantId, tenantId,
importFile, importFile,
parsedData, parsedData,
trx trx,
); );
return [successedImport, failedImport, parsedData]; return [successedImport, failedImport, parsedData];
}, },
trx trx,
); );
const mapping = importFile.mappingParsed; const mapping = importFile.mappingParsed;
const errors = chain(failedImport) const errors = chain(failedImport)

View File

@@ -1,64 +1,46 @@
import { Inject, Service } from 'typedi';
import HasTenancyService from '../Tenancy/TenancyService';
import { ImportFilePreviewPOJO } from './interfaces'; import { ImportFilePreviewPOJO } from './interfaces';
import { ImportFileProcess } from './ImportFileProcess'; import { ImportFileProcess } from './ImportFileProcess';
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
import events from '@/subscribers/events';
import { IImportFileCommitedEventPayload } from '@/interfaces/Import';
import { ImportAls } from './ImportALS'; import { ImportAls } from './ImportALS';
import { Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { events } from '@/common/events/events';
@Service() @Injectable()
export class ImportFileProcessCommit { export class ImportFileProcessCommit {
@Inject() constructor(
private tenancy: HasTenancyService; private readonly importFile: ImportFileProcess,
private readonly importAls: ImportAls,
@Inject() private readonly eventEmitter: EventEmitter2,
private importFile: ImportFileProcess; ) {}
@Inject()
private importAls: ImportAls;
@Inject()
private eventPublisher: EventPublisher;
/** /**
* Commits the imported file under ALS. * Commits the imported file under ALS.
* @param {number} tenantId * @param {string} importId - The import ID.
* @param {string} importId
* @returns {Promise<ImportFilePreviewPOJO>} * @returns {Promise<ImportFilePreviewPOJO>}
*/ */
public commit( public commit(importId: string): Promise<ImportFilePreviewPOJO> {
tenantId: number,
importId: string
): Promise<ImportFilePreviewPOJO> {
return this.importAls.runCommit<Promise<ImportFilePreviewPOJO>>(() => return this.importAls.runCommit<Promise<ImportFilePreviewPOJO>>(() =>
this.commitAlsRun(tenantId, importId) this.commitAlsRun(importId),
); );
} }
/** /**
* Commits the imported file. * Commits the imported file.
* @param {number} tenantId * @param {number} importId - The import ID.
* @param {number} importId
* @returns {Promise<ImportFilePreviewPOJO>} * @returns {Promise<ImportFilePreviewPOJO>}
*/ */
public async commitAlsRun( public async commitAlsRun(importId: string): Promise<ImportFilePreviewPOJO> {
tenantId: number,
importId: string
): Promise<ImportFilePreviewPOJO> {
const knex = this.tenancy.knex(tenantId);
const trx = await knex.transaction({ isolationLevel: 'read uncommitted' }); const trx = await knex.transaction({ isolationLevel: 'read uncommitted' });
const meta = await this.importFile.import(tenantId, importId, trx); const meta = await this.importFile.import(importId, trx);
// Commit the successed transaction. // Commit the successed transaction.
await trx.commit(); await trx.commit();
// Triggers `onImportFileCommitted` event. // Triggers `onImportFileCommitted` event.
await this.eventPublisher.emitAsync(events.import.onImportCommitted, { await this.eventEmitter.emitAsync(events.import.onImportCommitted, {
meta, meta,
importId, importId,
tenantId,
} as IImportFileCommitedEventPayload); } as IImportFileCommitedEventPayload);
return meta; return meta;

View File

@@ -1,4 +1,3 @@
import { Inject, Service } from 'typedi';
import { import {
deleteImportFile, deleteImportFile,
getResourceColumns, getResourceColumns,
@@ -6,23 +5,21 @@ import {
sanitizeResourceName, sanitizeResourceName,
validateSheetEmpty, validateSheetEmpty,
} from './_utils'; } from './_utils';
import ResourceService from '../Resource/ResourceService'; import { ResourceService } from '../Resource/ResourceService';
import { ImportFileCommon } from './ImportFileCommon'; import { ImportFileCommon } from './ImportFileCommon';
import { ImportFileDataValidator } from './ImportFileDataValidator'; import { ImportFileDataValidator } from './ImportFileDataValidator';
import { ImportFileUploadPOJO } from './interfaces'; import { ImportFileUploadPOJO } from './interfaces';
import { Import } from '@/system/models'; import { Import } from '@/system/models';
import { parseSheetData } from './sheet_utils'; import { parseSheetData } from './sheet_utils';
import { Injectable } from '@nestjs/common';
@Service() @Injectable()
export class ImportFileUploadService { export class ImportFileUploadService {
@Inject() constructor(
private resourceService: ResourceService; private resourceService: ResourceService,
private importFileCommon: ImportFileCommon,
@Inject() private importValidator: ImportFileDataValidator,
private importFileCommon: ImportFileCommon; ) {}
@Inject()
private importValidator: ImportFileDataValidator;
/** /**
* Imports the specified file for the given resource. * Imports the specified file for the given resource.
@@ -34,17 +31,16 @@ export class ImportFileUploadService {
* @returns {Promise<ImportFileUploadPOJO>} * @returns {Promise<ImportFileUploadPOJO>}
*/ */
public async import( public async import(
tenantId: number,
resourceName: string, resourceName: string,
filename: string, filename: string,
params: Record<string, number | string> params: Record<string, number | string>,
): Promise<ImportFileUploadPOJO> { ): Promise<ImportFileUploadPOJO> {
try { try {
return await this.importUnhandled( return await this.importUnhandled(
tenantId, tenantId,
resourceName, resourceName,
filename, filename,
params params,
); );
} catch (err) { } catch (err) {
deleteImportFile(filename); deleteImportFile(filename);
@@ -61,16 +57,12 @@ export class ImportFileUploadService {
* @returns {Promise<ImportFileUploadPOJO>} * @returns {Promise<ImportFileUploadPOJO>}
*/ */
public async importUnhandled( public async importUnhandled(
tenantId: number,
resourceName: string, resourceName: string,
filename: string, filename: string,
params: Record<string, number | string> params: Record<string, number | string>,
): Promise<ImportFileUploadPOJO> { ): Promise<ImportFileUploadPOJO> {
const resource = sanitizeResourceName(resourceName); const resource = sanitizeResourceName(resourceName);
const resourceMeta = this.resourceService.getResourceMeta( const resourceMeta = this.resourceService.getResourceMeta(resource);
tenantId,
resource
);
// Throw service error if the resource does not support importing. // Throw service error if the resource does not support importing.
this.importValidator.validateResourceImportable(resourceMeta); this.importValidator.validateResourceImportable(resourceMeta);
@@ -107,7 +99,7 @@ export class ImportFileUploadService {
}); });
const resourceColumnsMap = this.resourceService.getResourceFields2( const resourceColumnsMap = this.resourceService.getResourceFields2(
tenantId, tenantId,
resource resource,
); );
const resourceColumns = getResourceColumns(resourceColumnsMap); const resourceColumns = getResourceColumns(resourceColumnsMap);

View File

@@ -1,10 +1,10 @@
import moment from 'moment'; import moment from 'moment';
import bluebird from 'bluebird'; import bluebird from 'bluebird';
import { Import } from '@/system/models';
import { deleteImportFile } from './_utils'; import { deleteImportFile } from './_utils';
import { Service } from 'typedi'; import { Injectable } from '@nestjs/common';
import { Import } from './models/Import';
@Service() @Injectable()
export class ImportDeleteExpiredFiles { export class ImportDeleteExpiredFiles {
/** /**
* Delete expired files. * Delete expired files.

View File

@@ -0,0 +1,25 @@
import { Module } from '@nestjs/common';
import { ImportResourceApplication } from './ImportResourceApplication';
import { ImportFileUploadService } from './ImportFileUpload';
import { ImportFileMapping } from './ImportFileMapping';
import { ImportFileProcess } from './ImportFileProcess';
import { ImportFilePreview } from './ImportFilePreview';
import { ImportFileProcessCommit } from './ImportFileProcessCommit';
import { ImportFileMeta } from './ImportFileMeta';
import { ImportSampleService } from './ImportSample';
@Module({
imports: [],
providers: [
ImportResourceApplication,
ImportFileUploadService,
ImportFileMapping,
ImportFileProcess,
ImportFilePreview,
ImportSampleService,
ImportFileMeta,
ImportFileProcessCommit,
],
exports: [ImportResourceApplication],
})
export class ImportResourceModule {}

View File

@@ -1,4 +1,3 @@
import { Inject } from 'typedi';
import { ImportFileUploadService } from './ImportFileUpload'; import { ImportFileUploadService } from './ImportFileUpload';
import { ImportFileMapping } from './ImportFileMapping'; import { ImportFileMapping } from './ImportFileMapping';
import { ImportMappingAttr } from './interfaces'; import { ImportMappingAttr } from './interfaces';
@@ -7,100 +6,75 @@ import { ImportFilePreview } from './ImportFilePreview';
import { ImportSampleService } from './ImportSample'; import { ImportSampleService } from './ImportSample';
import { ImportFileMeta } from './ImportFileMeta'; import { ImportFileMeta } from './ImportFileMeta';
import { ImportFileProcessCommit } from './ImportFileProcessCommit'; import { ImportFileProcessCommit } from './ImportFileProcessCommit';
import { Injectable } from '@nestjs/common';
@Inject() @Injectable()
export class ImportResourceApplication { export class ImportResourceApplication {
@Inject() constructor(
private importFileService: ImportFileUploadService; private readonly importFileService: ImportFileUploadService,
private readonly importMappingService: ImportFileMapping,
@Inject() private readonly importProcessService: ImportFileProcess,
private importMappingService: ImportFileMapping; private readonly ImportFilePreviewService: ImportFilePreview,
private readonly importSampleService: ImportSampleService,
@Inject() private readonly importMetaService: ImportFileMeta,
private importProcessService: ImportFileProcess; private readonly importProcessCommit: ImportFileProcessCommit,
) {}
@Inject()
private ImportFilePreviewService: ImportFilePreview;
@Inject()
private importSampleService: ImportSampleService;
@Inject()
private importMetaService: ImportFileMeta;
@Inject()
private importProcessCommit: ImportFileProcessCommit;
/** /**
* Reads the imported file and stores the import file meta under unqiue id. * Reads the imported file and stores the import file meta under unqiue id.
* @param {number} tenantId -
* @param {string} resource - Resource name. * @param {string} resource - Resource name.
* @param {string} fileName - File name. * @param {string} fileName - File name.
* @returns {Promise<ImportFileUploadPOJO>} * @returns {Promise<ImportFileUploadPOJO>}
*/ */
public async import( public async import(
tenantId: number,
resource: string, resource: string,
filename: string, filename: string,
params: Record<string, any> params: Record<string, any>,
) { ) {
return this.importFileService.import(tenantId, resource, filename, params); return this.importFileService.import(resource, filename, params);
} }
/** /**
* Mapping the excel sheet columns with resource columns. * Mapping the excel sheet columns with resource columns.
* @param {number} tenantId
* @param {number} importId - Import id. * @param {number} importId - Import id.
* @param {ImportMappingAttr} maps * @param {ImportMappingAttr} maps
*/ */
public async mapping( public async mapping(importId: string, maps: ImportMappingAttr[]) {
tenantId: number, return this.importMappingService.mapping(importId, maps);
importId: string,
maps: ImportMappingAttr[]
) {
return this.importMappingService.mapping(tenantId, importId, maps);
} }
/** /**
* Preview the mapped results before process importing. * Preview the mapped results before process importing.
* @param {number} tenantId
* @param {number} importId - Import id. * @param {number} importId - Import id.
* @returns {Promise<ImportFilePreviewPOJO>} * @returns {Promise<ImportFilePreviewPOJO>}
*/ */
public async preview(tenantId: number, importId: string) { public async preview(importId: string) {
return this.ImportFilePreviewService.preview(tenantId, importId); return this.ImportFilePreviewService.preview(importId);
} }
/** /**
* Process the import file sheet through service for creating entities. * Process the import file sheet through service for creating entities.
* @param {number} tenantId
* @param {number} importId * @param {number} importId
* @returns {Promise<ImportFilePreviewPOJO>} * @returns {Promise<ImportFilePreviewPOJO>}
*/ */
public async process(tenantId: number, importId: string) { public async process(importId: string) {
return this.importProcessCommit.commit(tenantId, importId); return this.importProcessCommit.commit(importId);
} }
/** /**
* Retrieves the import meta of the given import id. * Retrieves the import meta of the given import id.
* @param {number} tenantId -
* @param {string} importId - Import id. * @param {string} importId - Import id.
* @returns {} * @returns {}
*/ */
public importMeta(tenantId: number, importId: string) { public importMeta(importId: string) {
return this.importMetaService.getImportMeta(tenantId, importId); return this.importMetaService.getImportMeta(importId);
} }
/** /**
* Retrieves the csv/xlsx sample sheet of the given * Retrieves the csv/xlsx sample sheet of the given
* @param {number} tenantId
* @param {number} resource - Resource name. * @param {number} resource - Resource name.
*/ */
public sample( public sample(resource: string, format: 'csv' | 'xlsx' = 'csv') {
tenantId: number, return this.importSampleService.sample(resource, format);
resource: string,
format: 'csv' | 'xlsx' = 'csv'
) {
return this.importSampleService.sample(tenantId, resource, format);
} }
} }

View File

@@ -1,22 +1,21 @@
import XLSX from 'xlsx'; import XLSX from 'xlsx';
import { Inject, Service } from 'typedi';
import { ImportableResources } from './ImportableResources'; import { ImportableResources } from './ImportableResources';
import { sanitizeResourceName } from './_utils'; import { sanitizeResourceName } from './_utils';
import { Injectable } from '@nestjs/common';
@Service() @Injectable()
export class ImportSampleService { export class ImportSampleService {
@Inject() constructor(
private importable: ImportableResources; private readonly importable: ImportableResources,
) {}
/** /**
* Retrieves the sample sheet of the given resource. * Retrieves the sample sheet of the given resource.
* @param {number} tenantId
* @param {string} resource * @param {string} resource
* @param {string} format * @param {string} format
* @returns {Buffer | string} * @returns {Buffer | string}
*/ */
public sample( public sample(
tenantId: number,
resource: string, resource: string,
format: 'csv' | 'xlsx' format: 'csv' | 'xlsx'
): Buffer | string { ): Buffer | string {

View File

@@ -6,11 +6,16 @@ import {
Param, Param,
Post, Post,
Put, Put,
Query,
} from '@nestjs/common'; } from '@nestjs/common';
import { InventoryAdjustmentsApplicationService } from './InventoryAdjustmentsApplication.service'; import { InventoryAdjustmentsApplicationService } from './InventoryAdjustmentsApplication.service';
import { IQuickInventoryAdjustmentDTO } from './types/InventoryAdjustments.types'; import {
IInventoryAdjustmentsFilter,
IQuickInventoryAdjustmentDTO,
} from './types/InventoryAdjustments.types';
import { InventoryAdjustment } from './models/InventoryAdjustment'; import { InventoryAdjustment } from './models/InventoryAdjustment';
import { PublicRoute } from '../Auth/Jwt.guard'; import { PublicRoute } from '../Auth/Jwt.guard';
import { IPaginationMeta } from '@/interfaces/Model';
@Controller('inventory-adjustments') @Controller('inventory-adjustments')
@PublicRoute() @PublicRoute()
@@ -37,6 +42,18 @@ export class InventoryAdjustmentsController {
); );
} }
@Get()
public async getInventoryAdjustments(
@Query() filterDTO: IInventoryAdjustmentsFilter,
): Promise<{
inventoryAdjustments: InventoryAdjustment[];
pagination: IPaginationMeta;
}> {
return this.inventoryAdjustmentsApplicationService.getInventoryAdjustments(
filterDTO,
);
}
@Get(':id') @Get(':id')
public async getInventoryAdjustment( public async getInventoryAdjustment(
@Param('id') inventoryAdjustmentId: number, @Param('id') inventoryAdjustmentId: number,

View File

@@ -2,9 +2,14 @@ import { Injectable } from '@nestjs/common';
import { DeleteInventoryAdjustmentService } from './commands/DeleteInventoryAdjustment.service'; import { DeleteInventoryAdjustmentService } from './commands/DeleteInventoryAdjustment.service';
import { PublishInventoryAdjustmentService } from './commands/PublishInventoryAdjustment.service'; import { PublishInventoryAdjustmentService } from './commands/PublishInventoryAdjustment.service';
import { CreateQuickInventoryAdjustmentService } from './commands/CreateQuickInventoryAdjustment.service'; import { CreateQuickInventoryAdjustmentService } from './commands/CreateQuickInventoryAdjustment.service';
import { IQuickInventoryAdjustmentDTO } from './types/InventoryAdjustments.types'; import {
IInventoryAdjustmentsFilter,
IQuickInventoryAdjustmentDTO,
} from './types/InventoryAdjustments.types';
import { InventoryAdjustment } from './models/InventoryAdjustment'; import { InventoryAdjustment } from './models/InventoryAdjustment';
import { GetInventoryAdjustmentService } from './queries/GetInventoryAdjustment.service'; import { GetInventoryAdjustmentService } from './queries/GetInventoryAdjustment.service';
import { GetInventoryAdjustmentsService } from './queries/GetInventoryAdjustments.service';
import { IPaginationMeta } from '@/interfaces/Model';
@Injectable() @Injectable()
export class InventoryAdjustmentsApplicationService { export class InventoryAdjustmentsApplicationService {
@@ -13,6 +18,7 @@ export class InventoryAdjustmentsApplicationService {
private readonly deleteInventoryAdjustmentService: DeleteInventoryAdjustmentService, private readonly deleteInventoryAdjustmentService: DeleteInventoryAdjustmentService,
private readonly publishInventoryAdjustmentService: PublishInventoryAdjustmentService, private readonly publishInventoryAdjustmentService: PublishInventoryAdjustmentService,
private readonly getInventoryAdjustmentService: GetInventoryAdjustmentService, private readonly getInventoryAdjustmentService: GetInventoryAdjustmentService,
private readonly getInventoryAdjustmentsService: GetInventoryAdjustmentsService,
) {} ) {}
/** /**
@@ -63,4 +69,19 @@ export class InventoryAdjustmentsApplicationService {
inventoryAdjustmentId, inventoryAdjustmentId,
); );
} }
/**
* Retrieves the inventory adjustments paginated list.
* @param {IInventoryAdjustmentsFilter} adjustmentsFilter - Inventory adjustments filter.
*/
public async getInventoryAdjustments(
filterDTO: IInventoryAdjustmentsFilter,
): Promise<{
inventoryAdjustments: InventoryAdjustment[];
pagination: IPaginationMeta;
}> {
return this.getInventoryAdjustmentsService.getInventoryAdjustments(
filterDTO,
);
}
} }

View File

@@ -1,56 +1,65 @@
// import { InventoryAdjustmentTransformer } from "../InventoryAdjustmentTransformer"; import { Inject } from '@nestjs/common';
// import { InventoryAdjustment } from "../models/InventoryAdjustment"; import * as R from 'ramda';
// import { IInventoryAdjustmentsFilter } from "../types/InventoryAdjustments.types"; import { IPaginationMeta } from '@/interfaces/Model';
import { InventoryAdjustmentTransformer } from '../InventoryAdjustmentTransformer';
import { InventoryAdjustment } from '../models/InventoryAdjustment';
import { IInventoryAdjustmentsFilter } from '../types/InventoryAdjustments.types';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
// import { TransformerInjectable } from "@/modules/Transformer/TransformerInjectable.service"; export class GetInventoryAdjustmentsService {
constructor(
public readonly transformer: TransformerInjectable,
private readonly dynamicListService: DynamicListService,
// export class GetInventoryAdjustmentsService { @Inject(InventoryAdjustment.name)
private readonly inventoryAdjustmentModel: typeof InventoryAdjustment,
) {}
/**
* Retrieve the inventory adjustments paginated list.
* @param {number} tenantId
* @param {IInventoryAdjustmentsFilter} adjustmentsFilter
*/
public async getInventoryAdjustments(
filterDTO: IInventoryAdjustmentsFilter,
): Promise<{
inventoryAdjustments: InventoryAdjustment[];
pagination: IPaginationMeta;
}> {
// Parses inventory adjustments list filter DTO.
const filter = this.parseListFilterDTO(filterDTO);
// constructor( // Dynamic list service.
// public readonly transformer: TransformerInjectable, const dynamicFilter = await this.dynamicListService.dynamicList(
// private readonly inventoryAdjustmentModel: typeof InventoryAdjustment, InventoryAdjustment,
// ) { filter,
);
const { results, pagination } = await this.inventoryAdjustmentModel
.query()
.onBuild((query) => {
query.withGraphFetched('entries.item');
query.withGraphFetched('adjustmentAccount');
// } dynamicFilter.buildQuery()(query);
// /** })
// * Retrieve the inventory adjustments paginated list. .pagination(filter.page - 1, filter.pageSize);
// * @param {number} tenantId
// * @param {IInventoryAdjustmentsFilter} adjustmentsFilter
// */
// public async getInventoryAdjustments(
// tenantId: number,
// filterDTO: IInventoryAdjustmentsFilter,
// ): Promise<{
// inventoryAdjustments: IInventoryAdjustment[];
// pagination: IPaginationMeta;
// }> {
// // Parses inventory adjustments list filter DTO. // Retrieves the transformed inventory adjustments.
// const filter = this.parseListFilterDTO(filterDTO); const inventoryAdjustments = await this.transformer.transform(
results,
new InventoryAdjustmentTransformer(),
);
return {
inventoryAdjustments,
pagination,
};
}
// // Dynamic list service. /**
// const dynamicFilter = await this.dynamicListService.dynamicList( * Parses inventory adjustments list filter DTO.
// tenantId, * @param filterDTO -
// InventoryAdjustment, */
// filter, private parseListFilterDTO(filterDTO) {
// ); return R.compose(this.dynamicListService.parseStringifiedFilter)(filterDTO);
// const { results, pagination } = await this.inventoryAdjustmentModel.query() }
// .onBuild((query) => { }
// query.withGraphFetched('entries.item');
// query.withGraphFetched('adjustmentAccount');
// dynamicFilter.buildQuery()(query);
// })
// .pagination(filter.page - 1, filter.pageSize);
// // Retrieves the transformed inventory adjustments.
// const inventoryAdjustments = await this.transformer.transform(
// results,
// new InventoryAdjustmentTransformer(),
// );
// return {
// inventoryAdjustments,
// pagination,
// };
// }
// }

View File

@@ -1,5 +1,8 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { IItemCategoryOTD } from './ItemCategory.interfaces'; import {
IItemCategoriesFilter,
IItemCategoryOTD,
} from './ItemCategory.interfaces';
import { CreateItemCategoryService } from './commands/CreateItemCategory.service'; import { CreateItemCategoryService } from './commands/CreateItemCategory.service';
import { DeleteItemCategoryService } from './commands/DeleteItemCategory.service'; import { DeleteItemCategoryService } from './commands/DeleteItemCategory.service';
import { EditItemCategoryService } from './commands/EditItemCategory.service'; import { EditItemCategoryService } from './commands/EditItemCategory.service';
@@ -75,7 +78,7 @@ export class ItemCategoryApplication {
/** /**
* Retrieves the item categories list. * Retrieves the item categories list.
* @param {IItemCategoriesFilter} filterDTO - The item categories filter DTO. * @param {IItemCategoriesFilter} filterDTO - The item categories filter DTO.
* @returns {Promise<IItemCategory[]>} * @returns {Promise<GetItemCategoriesResponse>}
*/ */
public getItemCategories(filterDTO: IItemCategoriesFilter) { public getItemCategories(filterDTO: IItemCategoriesFilter) {
return this.getItemCategoriesService.getItemCategories(filterDTO); return this.getItemCategoriesService.getItemCategories(filterDTO);

View File

@@ -6,9 +6,14 @@ import {
Param, Param,
Post, Post,
Put, Put,
Query,
} from '@nestjs/common'; } from '@nestjs/common';
import { ItemCategoryApplication } from './ItemCategory.application'; import { ItemCategoryApplication } from './ItemCategory.application';
import { IItemCategoryOTD } from './ItemCategory.interfaces'; import {
GetItemCategoriesResponse,
IItemCategoriesFilter,
IItemCategoryOTD,
} from './ItemCategory.interfaces';
import { PublicRoute } from '../Auth/Jwt.guard'; import { PublicRoute } from '../Auth/Jwt.guard';
@Controller('item-categories') @Controller('item-categories')
@@ -29,6 +34,13 @@ export class ItemCategoryController {
); );
} }
@Get()
async getItemCategories(
@Query() filterDTO: IItemCategoriesFilter,
): Promise<GetItemCategoriesResponse> {
return this.itemCategoryApplication.getItemCategories(filterDTO);
}
@Put(':id') @Put(':id')
async editItemCategory( async editItemCategory(
@Param('id') id: number, @Param('id') id: number,

View File

@@ -2,7 +2,8 @@ import { Knex } from 'knex';
// import { IDynamicListFilterDTO } from './DynamicFilter'; // import { IDynamicListFilterDTO } from './DynamicFilter';
// import { ISystemUser } from './User'; // import { ISystemUser } from './User';
import { ItemCategory } from './models/ItemCategory.model'; import { ItemCategory } from './models/ItemCategory.model';
import { IDynamicListFilter } from '../DynamicListing/DynamicFilter/DynamicFilter.types';
import { IFilterMeta } from '@/interfaces/Model';
export interface IItemCategoryOTD { export interface IItemCategoryOTD {
name: string; name: string;
@@ -35,3 +36,13 @@ export interface IItemCategoryDeletedPayload {
itemCategoryId: number; itemCategoryId: number;
oldItemCategory: ItemCategory; oldItemCategory: ItemCategory;
} }
export interface IItemCategoriesFilter extends IDynamicListFilter {
stringifiedFilterRoles?: string;
filterQuery?: (trx: Knex.Transaction) => void;
}
export interface GetItemCategoriesResponse {
itemCategories: ItemCategory[];
filterMeta: IFilterMeta;
}

View File

@@ -2,6 +2,10 @@ import * as R from 'ramda';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service'; import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { ItemCategory } from '../models/ItemCategory.model'; import { ItemCategory } from '../models/ItemCategory.model';
import { Inject } from '@nestjs/common'; import { Inject } from '@nestjs/common';
import {
GetItemCategoriesResponse,
IItemCategoriesFilter,
} from '../ItemCategory.interfaces';
export class GetItemCategoriesService { export class GetItemCategoriesService {
constructor( constructor(
@@ -25,12 +29,12 @@ export class GetItemCategoriesService {
/** /**
* Retrieve item categories list. * Retrieve item categories list.
* @param {number} tenantId * @param {IItemCategoriesFilter} filterDTO
* @param filter * @returns {Promise<GetItemCategoriesResponse>}
*/ */
public async getItemCategories( public async getItemCategories(
filterDTO: IItemCategoriesFilter, filterDTO: IItemCategoriesFilter,
): Promise<{ itemCategories: ItemCategory[]; filterMeta: IFilterMeta }> { ): Promise<GetItemCategoriesResponse> {
// Parses list filter DTO. // Parses list filter DTO.
const filter = this.parsesListFilterDTO(filterDTO); const filter = this.parsesListFilterDTO(filterDTO);

View File

@@ -158,10 +158,7 @@ export class PaymentReceivesApplication {
* @param {PaymentReceive} paymentReceive * @param {PaymentReceive} paymentReceive
* @returns * @returns
*/ */
public getPaymentReceivePdf = ( public getPaymentReceivePdf = (paymentReceiveId: number) => {
tenantId: number,
paymentReceiveId: number,
) => {
return this.getPaymentReceivePdfService.getPaymentReceivePdf( return this.getPaymentReceivePdfService.getPaymentReceivePdf(
paymentReceiveId, paymentReceiveId,
); );

View File

@@ -5,6 +5,7 @@ import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectab
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service'; import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { PaymentReceived } from '../models/PaymentReceived'; import { PaymentReceived } from '../models/PaymentReceived';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model'; import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { IPaymentsReceivedFilter } from '../types/PaymentReceived.types';
@Injectable() @Injectable()
export class GetPaymentsReceivedService { export class GetPaymentsReceivedService {
@@ -18,7 +19,7 @@ export class GetPaymentsReceivedService {
/** /**
* Retrieve payment receives paginated and filterable list. * Retrieve payment receives paginated and filterable list.
* @param {IPaymentsReceivedFilter} paymentReceivesFilter * @param {IPaymentsReceivedFilter} filterDTO
*/ */
public async getPaymentReceives(filterDTO: IPaymentsReceivedFilter): Promise<{ public async getPaymentReceives(filterDTO: IPaymentsReceivedFilter): Promise<{
paymentReceives: PaymentReceived[]; paymentReceives: PaymentReceived[];

View File

@@ -1,6 +1,7 @@
import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types'; import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types';
import { Knex } from 'knex'; import { Knex } from 'knex';
import { PaymentReceived } from '../models/PaymentReceived'; import { PaymentReceived } from '../models/PaymentReceived';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
export interface IPaymentReceivedCreateDTO { export interface IPaymentReceivedCreateDTO {
customerId: number; customerId: number;
@@ -39,10 +40,10 @@ export interface IPaymentReceivedEntryDTO {
paymentAmount: number; paymentAmount: number;
} }
// export interface IPaymentsReceivedFilter extends IDynamicListFilterDTO { export interface IPaymentsReceivedFilter extends IDynamicListFilter {
// stringifiedFilterRoles?: string; stringifiedFilterRoles?: string;
// filterQuery?: (trx: Knex.Transaction) => void; filterQuery?: (trx: Knex.Transaction) => void;
// } }
export interface IPaymentReceivePageEntry { export interface IPaymentReceivePageEntry {
invoiceId: number; invoiceId: number;

View File

@@ -6,6 +6,7 @@ import {
Param, Param,
Post, Post,
Put, Put,
Query,
} from '@nestjs/common'; } from '@nestjs/common';
import { VendorCreditsApplicationService } from './VendorCreditsApplication.service'; import { VendorCreditsApplicationService } from './VendorCreditsApplication.service';
import { import {
@@ -31,6 +32,11 @@ export class VendorCreditsController {
return this.vendorCreditsApplication.openVendorCredit(vendorCreditId); return this.vendorCreditsApplication.openVendorCredit(vendorCreditId);
} }
@Get()
async getVendorCredits(@Query() filterDTO: IVendorCreditsFilter) {
return this.vendorCreditsApplication.getVendorCredits(filterDTO);
}
@Put(':id') @Put(':id')
async editVendorCredit( async editVendorCredit(
@Param('id') vendorCreditId: number, @Param('id') vendorCreditId: number,

View File

@@ -9,6 +9,7 @@ import {
IVendorEditDTO, IVendorEditDTO,
IVendorNewDTO, IVendorNewDTO,
IVendorOpeningBalanceEditDTO, IVendorOpeningBalanceEditDTO,
IVendorsFilter,
} from './types/Vendors.types'; } from './types/Vendors.types';
import { GetVendorsService } from './queries/GetVendors.service'; import { GetVendorsService } from './queries/GetVendors.service';
@@ -79,9 +80,9 @@ export class VendorsApplication {
/** /**
* Retrieves the vendors paginated list. * Retrieves the vendors paginated list.
* @param {IVendorsFilter} filterDTO * @param {IVendorsFilter} filterDTO
* @returns {Promise<{vendors: Vendor[], pagination: IPaginationMeta, filterMeta: IFilterMeta}>>} * @returns {Promise<{ vendors: Vendor[], pagination: IPaginationMeta, filterMeta: IFilterMeta }>>}
*/ */
public getVendors = (filterDTO: IVendorsFilter) => { public getVendors(filterDTO: IVendorsFilter) {
return this.getVendorsService.getVendorsList(filterDTO); return this.getVendorsService.getVendorsList(filterDTO);
}; };
} }

View File

@@ -1,13 +1,19 @@
import * as R from 'ramda'; import * as R from 'ramda';
import { Inject, Injectable } from '@nestjs/common';
import { Vendor } from '../models/Vendor'; import { Vendor } from '../models/Vendor';
import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service'; import { DynamicListService } from '@/modules/DynamicListing/DynamicList.service';
import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service'; import { TransformerInjectable } from '@/modules/Transformer/TransformerInjectable.service';
import { Inject, Injectable } from '@nestjs/common';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
import { VendorTransfromer } from './VendorTransformer'; import { VendorTransfromer } from './VendorTransformer';
import { GetVendorsResponse, IVendorsFilter } from '../types/Vendors.types';
@Injectable() @Injectable()
export class GetVendorsService { export class GetVendorsService {
/**
* Constructor method.
* @param {DynamicListService} dynamicListService
* @param {TransformerInjectable} transformer
* @param {typeof Vendor} vendorModel
*/
constructor( constructor(
private dynamicListService: DynamicListService, private dynamicListService: DynamicListService,
private transformer: TransformerInjectable, private transformer: TransformerInjectable,
@@ -18,12 +24,11 @@ export class GetVendorsService {
/** /**
* Retrieve vendors datatable list. * Retrieve vendors datatable list.
* @param {IVendorsFilter} vendorsFilter - Vendors filter. * @param {IVendorsFilter} vendorsFilter - Vendors filter.
* @returns {Promise<GetVendorsResponse>}
*/ */
public async getVendorsList(filterDTO: IVendorsFilter): Promise<{ public async getVendorsList(
vendors: Vendor[]; filterDTO: IVendorsFilter,
pagination: IPaginationMeta; ): Promise<GetVendorsResponse> {
filterMeta: IFilterMeta;
}> {
// Parses vendors list filter DTO. // Parses vendors list filter DTO.
const filter = this.parseVendorsListFilterDTO(filterDTO); const filter = this.parseVendorsListFilterDTO(filterDTO);

View File

@@ -3,6 +3,8 @@
import { Knex } from 'knex'; import { Knex } from 'knex';
import { Vendor } from '../models/Vendor'; import { Vendor } from '../models/Vendor';
import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types'; import { IContactAddressDTO } from '@/modules/Contacts/types/Contacts.types';
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
// ---------------------------------- // ----------------------------------
export interface IVendorNewDTO extends IContactAddressDTO { export interface IVendorNewDTO extends IContactAddressDTO {
@@ -43,11 +45,17 @@ export interface IVendorEditDTO extends IContactAddressDTO {
active?: boolean; active?: boolean;
} }
// export interface IVendorsFilter extends IDynamicListFilter { export interface IVendorsFilter extends IDynamicListFilter {
// stringifiedFilterRoles?: string; stringifiedFilterRoles?: string;
// page?: number; page?: number;
// pageSize?: number; pageSize?: number;
// } }
export interface GetVendorsResponse {
vendors: Vendor[];
pagination: IPaginationMeta;
filterMeta: IFilterMeta;
}
// Vendor Events. // Vendor Events.
// ---------------------------------- // ----------------------------------