mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 20:30:33 +00:00
refactor(nestjs): attachments module
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
"@casl/ability": "^5.4.3",
|
||||
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
||||
"@liaoliaots/nestjs-redis": "^10.0.0",
|
||||
"@types/multer": "^1.4.11",
|
||||
"@nestjs/bull": "^10.2.1",
|
||||
"@nestjs/bullmq": "^10.2.2",
|
||||
"@nestjs/cache-manager": "^2.2.2",
|
||||
@@ -69,11 +70,14 @@
|
||||
"lodash": "^4.17.21",
|
||||
"lru-cache": "^6.0.0",
|
||||
"mathjs": "^9.4.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"moment": "^2.30.1",
|
||||
"moment-range": "^4.0.2",
|
||||
"moment-timezone": "^0.5.43",
|
||||
"mysql": "^2.18.1",
|
||||
"mysql2": "^3.11.3",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"multer-s3": "^3.0.1",
|
||||
"nestjs-cls": "^5.2.0",
|
||||
"nestjs-i18n": "^10.4.9",
|
||||
"nestjs-redis": "^1.3.3",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export const MULTER_MODULE_OPTIONS = 'MULTER_MODULE_OPTIONS';
|
||||
@@ -0,0 +1,24 @@
|
||||
import type { Multer } from 'multer';
|
||||
import * as multerS3 from 'multer-s3';
|
||||
|
||||
export const multerExceptions = {
|
||||
// from https://github.com/expressjs/multer/blob/master/lib/multer-error.js
|
||||
LIMIT_PART_COUNT: 'Too many parts',
|
||||
LIMIT_FILE_SIZE: 'File too large',
|
||||
LIMIT_FILE_COUNT: 'Too many files',
|
||||
LIMIT_FIELD_KEY: 'Field name too long',
|
||||
LIMIT_FIELD_VALUE: 'Field value too long',
|
||||
LIMIT_FIELD_COUNT: 'Too many fields',
|
||||
LIMIT_UNEXPECTED_FILE: 'Unexpected field',
|
||||
MISSING_FIELD_NAME: 'Field name missing',
|
||||
};
|
||||
|
||||
export const busboyExceptions = {
|
||||
// from https://github.com/mscdex/busboy/blob/master/lib/types/multipart.js
|
||||
MULTIPART_BOUNDARY_NOT_FOUND: 'Multipart: Boundary not found',
|
||||
MULTIPART_MALFORMED_PART_HEADER: 'Malformed part header',
|
||||
MULTIPART_UNEXPECTED_END_OF_FORM: 'Unexpected end of form',
|
||||
MULTIPART_UNEXPECTED_END_OF_FILE: 'Unexpected end of file',
|
||||
};
|
||||
|
||||
|
||||
38
packages/server-nest/src/common/constants/multer.utils.ts
Normal file
38
packages/server-nest/src/common/constants/multer.utils.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
BadRequestException,
|
||||
HttpException,
|
||||
PayloadTooLargeException,
|
||||
} from '@nestjs/common';
|
||||
import { multerExceptions, busboyExceptions } from './multer.constants';
|
||||
|
||||
// Multer may add in a 'field' property to the error
|
||||
// https://github.com/expressjs/multer/blob/aa42bea6ac7d0cb8fcb279b15a7278cda805dc63/lib/multer-error.js#L19
|
||||
export function transformException(
|
||||
error: (Error & { field?: string }) | undefined,
|
||||
) {
|
||||
if (!error || error instanceof HttpException) {
|
||||
return error;
|
||||
}
|
||||
switch (error.message) {
|
||||
case multerExceptions.LIMIT_FILE_SIZE:
|
||||
return new PayloadTooLargeException(error.message);
|
||||
case multerExceptions.LIMIT_FILE_COUNT:
|
||||
case multerExceptions.LIMIT_FIELD_KEY:
|
||||
case multerExceptions.LIMIT_FIELD_VALUE:
|
||||
case multerExceptions.LIMIT_FIELD_COUNT:
|
||||
case multerExceptions.LIMIT_UNEXPECTED_FILE:
|
||||
case multerExceptions.LIMIT_PART_COUNT:
|
||||
case multerExceptions.MISSING_FIELD_NAME:
|
||||
if (error.field) {
|
||||
return new BadRequestException(`${error.message} - ${error.field}`);
|
||||
}
|
||||
return new BadRequestException(error.message);
|
||||
case busboyExceptions.MULTIPART_BOUNDARY_NOT_FOUND:
|
||||
return new BadRequestException(error.message);
|
||||
case busboyExceptions.MULTIPART_MALFORMED_PART_HEADER:
|
||||
case busboyExceptions.MULTIPART_UNEXPECTED_END_OF_FORM:
|
||||
case busboyExceptions.MULTIPART_UNEXPECTED_END_OF_FILE:
|
||||
return new BadRequestException(`Multipart: ${error.message}`);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
Inject,
|
||||
mixin,
|
||||
NestInterceptor,
|
||||
Optional,
|
||||
Type,
|
||||
} from '@nestjs/common';
|
||||
import * as multer from 'multer';
|
||||
import { Observable } from 'rxjs';
|
||||
import { MULTER_MODULE_OPTIONS } from '../constants/files.constants';
|
||||
import { transformException } from '../constants/multer.utils';
|
||||
import { MulterModuleOptions } from '@nestjs/platform-express';
|
||||
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
|
||||
|
||||
type MulterInstance = any;
|
||||
/**
|
||||
* @param {string} fieldName
|
||||
* @param {Function|MulterOptions} localOptions - Function that receives controller instance or MulterOptions object
|
||||
*/
|
||||
export function FileInterceptor(
|
||||
fieldName: string,
|
||||
localOptions?: ((instance: any) => MulterOptions) | MulterOptions,
|
||||
): Type<NestInterceptor> {
|
||||
class MixinInterceptor implements NestInterceptor {
|
||||
protected multer: MulterInstance;
|
||||
|
||||
constructor(
|
||||
@Optional()
|
||||
@Inject(MULTER_MODULE_OPTIONS)
|
||||
options: (() => MulterModuleOptions | MulterModuleOptions) = () => ({}),
|
||||
) {
|
||||
const resolvedOptions = typeof localOptions === 'function'
|
||||
? localOptions(this)
|
||||
: localOptions;
|
||||
|
||||
this.multer = (multer as any)({
|
||||
...(typeof options === 'function' ? options() : options),
|
||||
...resolvedOptions,
|
||||
});
|
||||
}
|
||||
|
||||
async intercept(
|
||||
context: ExecutionContext,
|
||||
next: CallHandler,
|
||||
): Promise<Observable<any>> {
|
||||
const ctx = context.switchToHttp();
|
||||
|
||||
await new Promise<void>((resolve, reject) =>
|
||||
this.multer.single(fieldName)(
|
||||
ctx.getRequest(),
|
||||
ctx.getResponse(),
|
||||
(err: any) => {
|
||||
if (err) {
|
||||
const error = transformException(err);
|
||||
return reject(error);
|
||||
}
|
||||
resolve();
|
||||
},
|
||||
),
|
||||
);
|
||||
return next.handle();
|
||||
}
|
||||
}
|
||||
|
||||
const Interceptor = mixin(MixinInterceptor);
|
||||
|
||||
return Interceptor;
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { S3Module } from "../S3/S3.module";
|
||||
import * as multerS3 from 'multer-s3';
|
||||
import { S3_CLIENT, S3Module } from "../S3/S3.module";
|
||||
import { DeleteAttachment } from "./DeleteAttachment";
|
||||
import { GetAttachment } from "./GetAttachment";
|
||||
import { getAttachmentPresignedUrl } from "./GetAttachmentPresignedUrl";
|
||||
@@ -13,11 +14,26 @@ import { AttachmentsOnExpenses } from "./events/AttachmentsOnExpenses";
|
||||
import { AttachmentsOnPaymentsReceived } from "./events/AttachmentsOnPaymentsReceived";
|
||||
import { AttachmentsOnManualJournals } from "./events/AttachmentsOnManualJournals";
|
||||
import { AttachmentsOnVendorCredits } from "./events/AttachmentsOnVendorCredits";
|
||||
import { AttachmentsOnSaleInvoiceCreated } from "./events/AttachmentsOnSaleInvoice";
|
||||
import { AttachmentsController } from "./Attachments.controller";
|
||||
import { RegisterTenancyModel } from "../Tenancy/TenancyModels/Tenancy.module";
|
||||
import { DocumentModel } from "./models/Document.model";
|
||||
import { DocumentLinkModel } from "./models/DocumentLink.model";
|
||||
import { AttachmentsApplication } from "./AttachmentsApplication";
|
||||
import { UploadDocument } from "./UploadDocument";
|
||||
import { AttachmentUploadPipeline } from "./S3UploadPipeline";
|
||||
import { MULTER_MODULE_OPTIONS } from "@/common/constants/files.constants";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { S3Client } from "@aws-sdk/client-s3";
|
||||
|
||||
|
||||
const models = [
|
||||
RegisterTenancyModel(DocumentModel),
|
||||
RegisterTenancyModel(DocumentLinkModel),
|
||||
];
|
||||
|
||||
@Module({
|
||||
imports: [S3Module],
|
||||
imports: [S3Module, ...models],
|
||||
controllers: [AttachmentsController],
|
||||
providers: [
|
||||
DeleteAttachment,
|
||||
GetAttachment,
|
||||
@@ -31,7 +47,34 @@ import { AttachmentsOnVendorCredits } from "./events/AttachmentsOnVendorCredits"
|
||||
AttachmentsOnExpenses,
|
||||
AttachmentsOnPaymentsReceived,
|
||||
AttachmentsOnManualJournals,
|
||||
AttachmentsOnVendorCredits
|
||||
AttachmentsOnVendorCredits,
|
||||
AttachmentsOnSaleInvoiceCreated,
|
||||
AttachmentsApplication,
|
||||
UploadDocument,
|
||||
AttachmentUploadPipeline,
|
||||
{
|
||||
provide: MULTER_MODULE_OPTIONS,
|
||||
inject: [ConfigService, S3_CLIENT],
|
||||
useFactory: (configService: ConfigService, s3: S3Client) => ({
|
||||
storage: multerS3({
|
||||
s3,
|
||||
bucket: configService.get('s3.bucket'),
|
||||
contentType: multerS3.AUTO_CONTENT_TYPE,
|
||||
metadata: function (req, file, cb) {
|
||||
cb(null, { fieldName: file.fieldname });
|
||||
},
|
||||
key: function (req, file, cb) {
|
||||
cb(null, Date.now().toString());
|
||||
},
|
||||
acl: function(req, file, cb) {
|
||||
// Conditionally set file to public or private based on isPublic flag
|
||||
const aclValue = true ? 'public-read' : 'private';
|
||||
// Set ACL based on the isPublic flag
|
||||
cb(null, aclValue);
|
||||
}
|
||||
}),
|
||||
})
|
||||
}
|
||||
]
|
||||
})
|
||||
export class AttachmentsModule {}
|
||||
@@ -0,0 +1,202 @@
|
||||
import mime from 'mime-types';
|
||||
import { Response, NextFunction, Request } from 'express';
|
||||
import {
|
||||
ApiBody,
|
||||
ApiConsumes,
|
||||
ApiOperation,
|
||||
ApiParam,
|
||||
ApiResponse,
|
||||
ApiTags,
|
||||
} from '@nestjs/swagger';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
Res,
|
||||
UnauthorizedException,
|
||||
UploadedFile,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
LinkAttachmentDto,
|
||||
UnlinkAttachmentDto,
|
||||
UploadAttachmentDto,
|
||||
} from './dtos/Attachment.dto';
|
||||
import { AttachmentsApplication } from './AttachmentsApplication';
|
||||
import { AttachmentUploadPipeline } from './S3UploadPipeline';
|
||||
import { FileInterceptor } from '@/common/interceptors/file.interceptor';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@ApiTags('Attachments')
|
||||
@Controller('/attachments')
|
||||
export class AttachmentsController {
|
||||
/**
|
||||
* @param {AttachmentsApplication} attachmentsApplication - Attachments application.
|
||||
* @param uploadPipelineService
|
||||
*/
|
||||
constructor(
|
||||
private readonly attachmentsApplication: AttachmentsApplication,
|
||||
private readonly uploadPipelineService: AttachmentUploadPipeline,
|
||||
private readonly configService: ConfigService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Uploads the attachments to S3 and store the file metadata to DB.
|
||||
*/
|
||||
@Post()
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
@ApiConsumes('multipart/form-data')
|
||||
@ApiOperation({ summary: 'Upload attachment to S3' })
|
||||
@ApiBody({ description: 'Upload attachment', type: UploadAttachmentDto })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'The document has been uploaded successfully',
|
||||
})
|
||||
@ApiResponse({
|
||||
status: 401,
|
||||
description: 'Unauthorized - File upload failed',
|
||||
})
|
||||
async uploadAttachment(
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
): Promise<Response | void> {
|
||||
if (!file) {
|
||||
throw new UnauthorizedException({
|
||||
errorType: 'FILE_UPLOAD_FAILED',
|
||||
message: 'Now file uploaded.',
|
||||
});
|
||||
}
|
||||
const data = await this.attachmentsApplication.upload(file);
|
||||
|
||||
return res.status(200).send({
|
||||
status: 200,
|
||||
message: 'The document has uploaded successfully.',
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the given attachment key.
|
||||
*/
|
||||
@Get('/:id')
|
||||
@ApiOperation({ summary: 'Get attachment by ID' })
|
||||
@ApiParam({ name: 'id', description: 'Attachment ID' })
|
||||
@ApiResponse({ status: 200, description: 'Returns the attachment file' })
|
||||
async getAttachment(
|
||||
@Res() res: Response,
|
||||
@Param('id') documentId: string,
|
||||
): Promise<Response | void> {
|
||||
const data = await this.attachmentsApplication.get(documentId);
|
||||
|
||||
const byte = await data.Body.transformToByteArray();
|
||||
const extension = mime.extension(data.ContentType);
|
||||
const buffer = Buffer.from(byte);
|
||||
|
||||
res.set('Content-Disposition', `filename="${documentId}.${extension}"`);
|
||||
res.set('Content-Type', data.ContentType);
|
||||
res.send(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given document key.
|
||||
*/
|
||||
@Delete('/:id')
|
||||
@ApiOperation({ summary: 'Delete attachment by ID' })
|
||||
@ApiParam({ name: 'id', description: 'Attachment ID' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'The document has been deleted successfully',
|
||||
})
|
||||
async deleteAttachment(
|
||||
@Res() res: Response,
|
||||
@Param('id') documentId: string,
|
||||
): Promise<Response | void> {
|
||||
await this.attachmentsApplication.delete(documentId);
|
||||
|
||||
return res.status(200).send({
|
||||
status: 200,
|
||||
message: 'The document has been delete successfully.',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Links the given document key.
|
||||
*/
|
||||
@Post('/:id/link')
|
||||
@ApiOperation({ summary: 'Link attachment to a model' })
|
||||
@ApiParam({ name: 'id', description: 'Attachment ID' })
|
||||
@ApiBody({ type: LinkAttachmentDto })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'The document has been linked successfully',
|
||||
})
|
||||
async linkDocument(
|
||||
@Body() linkDocumentDto: LinkAttachmentDto,
|
||||
@Param('id') documentId: string,
|
||||
@Res() res: Response,
|
||||
): Promise<Response | void> {
|
||||
await this.attachmentsApplication.link(
|
||||
documentId,
|
||||
linkDocumentDto.modelRef,
|
||||
linkDocumentDto.modelId,
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
status: 200,
|
||||
message: 'The document has been linked successfully.',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Links the given document key.
|
||||
*/
|
||||
@Post('/:id/unlink')
|
||||
@ApiOperation({ summary: 'Unlink attachment from a model' })
|
||||
@ApiParam({ name: 'id', description: 'Attachment ID' })
|
||||
@ApiBody({ type: UnlinkAttachmentDto })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'The document has been unlinked successfully',
|
||||
})
|
||||
async unlinkDocument(
|
||||
@Body() unlinkDto: UnlinkAttachmentDto,
|
||||
@Param('id') documentId: string,
|
||||
@Res() res: Response,
|
||||
): Promise<Response | void> {
|
||||
await this.attachmentsApplication.link(
|
||||
documentId,
|
||||
unlinkDto.modelRef,
|
||||
unlinkDto.modelId,
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
status: 200,
|
||||
message: 'The document has been linked successfully.',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreives the presigned url of the given attachment key.
|
||||
*/
|
||||
@Get('/:id/presigned-url')
|
||||
@ApiOperation({ summary: 'Get presigned URL for attachment' })
|
||||
@ApiParam({ name: 'id', description: 'Attachment ID' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Returns the presigned URL for the attachment',
|
||||
})
|
||||
async getAttachmentPresignedUrl(
|
||||
@Param('id') documentKey: string,
|
||||
res: Response,
|
||||
next: NextFunction,
|
||||
): Promise<Response | void> {
|
||||
const presignedUrl =
|
||||
await this.attachmentsApplication.getPresignedUrl(documentKey);
|
||||
|
||||
return res.status(200).send({ presignedUrl });
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ export class AttachmentsApplication {
|
||||
|
||||
/**
|
||||
* Saves the metadata of uploaded document to S3 on database.
|
||||
* @param {number} tenantId
|
||||
* @param {} file
|
||||
* @returns {Promise<Document>}
|
||||
*/
|
||||
@@ -38,7 +37,6 @@ export class AttachmentsApplication {
|
||||
|
||||
/**
|
||||
* Retrieves the document data.
|
||||
* @param {number} tenantId
|
||||
* @param {string} documentKey
|
||||
*/
|
||||
public get(documentKey: string) {
|
||||
@@ -58,7 +56,6 @@ export class AttachmentsApplication {
|
||||
|
||||
/**
|
||||
* Unlinks the given document from resource model.
|
||||
* @param {number} tenantId
|
||||
* @param {string} filekey
|
||||
* @param {string} modelRef
|
||||
* @param {number} modelId
|
||||
@@ -70,7 +67,6 @@ export class AttachmentsApplication {
|
||||
|
||||
/**
|
||||
* Retrieves the presigned url of the given attachment key.
|
||||
* @param {number} tenantId
|
||||
* @param {string} key
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
||||
import { S3_CLIENT } from '../S3/S3.module';
|
||||
import { DocumentModel } from './models/Document.model';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
@@ -17,14 +17,14 @@ export class DeleteAttachment {
|
||||
@Inject(S3_CLIENT)
|
||||
private readonly s3Client: S3Client,
|
||||
|
||||
@Inject(Document.name)
|
||||
@Inject(DocumentModel.name)
|
||||
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
||||
|
||||
@Inject(DocumentLinkModel.name)
|
||||
private readonly documentLinkModel: TenantModelProxy<typeof DocumentLinkModel>
|
||||
) {
|
||||
|
||||
}
|
||||
private readonly documentLinkModel: TenantModelProxy<
|
||||
typeof DocumentLinkModel
|
||||
>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Deletes the give file attachment file key.
|
||||
@@ -37,13 +37,15 @@ export class DeleteAttachment {
|
||||
};
|
||||
await this.s3Client.send(new DeleteObjectCommand(params));
|
||||
|
||||
const foundDocument = await this.documentModel().query()
|
||||
const foundDocument = await this.documentModel()
|
||||
.query()
|
||||
.findOne('key', filekey)
|
||||
.throwIfNotFound();
|
||||
|
||||
await this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||
// Delete all document links
|
||||
await this.documentLinkModel().query(trx)
|
||||
await this.documentLinkModel()
|
||||
.query(trx)
|
||||
.where('documentId', foundDocument.id)
|
||||
.delete();
|
||||
|
||||
|
||||
@@ -9,13 +9,11 @@ export class GetAttachment {
|
||||
private readonly configService: ConfigService,
|
||||
|
||||
@Inject(S3_CLIENT)
|
||||
private readonly s3: S3Client
|
||||
) {
|
||||
|
||||
}
|
||||
private readonly s3: S3Client,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves data of the given document key.
|
||||
* @param {number} tenantId
|
||||
* @param {string} filekey
|
||||
*/
|
||||
async getAttachment(filekey: string) {
|
||||
|
||||
@@ -1,35 +1,42 @@
|
||||
import { GetObjectCommand } from '@aws-sdk/client-s3';
|
||||
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
||||
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
import { DocumentModel } from './models/Document.model';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { S3_CLIENT } from '../S3/S3.module';
|
||||
|
||||
@Injectable()
|
||||
export class getAttachmentPresignedUrl {
|
||||
constructor(
|
||||
private readonly documentModel: TenantModelProxy<typeof DocumentModel>
|
||||
private readonly configService: ConfigService,
|
||||
|
||||
@Inject(DocumentModel.name)
|
||||
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
||||
|
||||
@Inject(S3_CLIENT)
|
||||
private readonly s3Client: S3Client,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Retrieves the presigned url of the given attachment key with the original filename.
|
||||
* @param {number} tenantId
|
||||
* @param {string} key
|
||||
* @param {string} key -
|
||||
* @returns {string}
|
||||
*/
|
||||
async getPresignedUrl(tenantId: number, key: string) {
|
||||
async getPresignedUrl(key: string) {
|
||||
const foundDocument = await this.documentModel().query().findOne({ key });
|
||||
const config = this.configService.get('s3');
|
||||
|
||||
let ResponseContentDisposition = 'attachment';
|
||||
if (foundDocument && foundDocument.originName) {
|
||||
ResponseContentDisposition += `; filename="${foundDocument.originName}"`;
|
||||
}
|
||||
|
||||
const command = new GetObjectCommand({
|
||||
Bucket: config.s3.bucket,
|
||||
Bucket: config.bucket,
|
||||
Key: key,
|
||||
ResponseContentDisposition,
|
||||
});
|
||||
const signedUrl = await getSignedUrl(s3, command, { expiresIn: 300 });
|
||||
const signedUrl = await getSignedUrl(this.s3Client, command, { expiresIn: 300 });
|
||||
|
||||
return signedUrl;
|
||||
}
|
||||
|
||||
@@ -1,46 +1,59 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import bluebird from 'bluebird';
|
||||
import { Knex } from 'knex';
|
||||
import {
|
||||
validateLinkModelEntryExists,
|
||||
validateLinkModelExists,
|
||||
} from './Attachments/_utils';
|
||||
import HasTenancyService from '../Tenancy/TenancyService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
} from './_utils';
|
||||
import { ERRORS } from './constants';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
import { DocumentLink } from '../ChromiumlyTenancy/models/DocumentLink';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { ServiceError } from '../Items/ServiceError';
|
||||
import { getAttachableModelsMap } from './decorators/InjectAttachable.decorator';
|
||||
import { DocumentModel } from './models/Document.model';
|
||||
import { SaleInvoice } from '../SaleInvoices/models/SaleInvoice';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class LinkAttachment {
|
||||
@Inject()
|
||||
private tenancy: HasTenancyService;
|
||||
constructor(
|
||||
private moduleRef: ModuleRef,
|
||||
|
||||
@Inject(DocumentLink.name)
|
||||
private readonly documentLinkModel: TenantModelProxy<typeof DocumentLink>,
|
||||
|
||||
@Inject(DocumentModel.name)
|
||||
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Links the given file key to the given model type and id.
|
||||
* @param {number} tenantId
|
||||
* @param {string} filekey
|
||||
* @param {string} modelRef
|
||||
* @param {number} modelId
|
||||
* @param {string} filekey - File key.
|
||||
* @param {string} modelRef - Model reference.
|
||||
* @param {number} modelId - Model id.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async link(
|
||||
tenantId: number,
|
||||
filekey: string,
|
||||
modelRef: string,
|
||||
modelId: number,
|
||||
trx?: Knex.Transaction
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
const { DocumentLink, Document, ...models } = this.tenancy.models(tenantId);
|
||||
const LinkModel = models[modelRef];
|
||||
validateLinkModelExists(LinkModel);
|
||||
const attachmentsAttachableModels = getAttachableModelsMap();
|
||||
const attachableModel = attachmentsAttachableModels.get(modelRef);
|
||||
|
||||
const foundFile = await Document.query(trx)
|
||||
validateLinkModelExists(attachableModel);
|
||||
|
||||
const LinkModel = this.moduleRef.get(modelRef, { strict: false });
|
||||
const foundFile = await this.documentModel()
|
||||
.query(trx)
|
||||
.findOne('key', filekey)
|
||||
.throwIfNotFound();
|
||||
|
||||
const foundLinkModel = await LinkModel.query(trx).findById(modelId);
|
||||
const foundLinkModel = await LinkModel().query(trx).findById(modelId);
|
||||
validateLinkModelEntryExists(foundLinkModel);
|
||||
|
||||
const foundLinks = await DocumentLink.query(trx)
|
||||
const foundLinks = await this.documentLinkModel().query(trx)
|
||||
.where('modelRef', modelRef)
|
||||
.where('modelId', modelId)
|
||||
.where('documentId', foundFile.id);
|
||||
@@ -48,7 +61,7 @@ export class LinkAttachment {
|
||||
if (foundLinks.length > 0) {
|
||||
throw new ServiceError(ERRORS.DOCUMENT_LINK_ALREADY_LINKED);
|
||||
}
|
||||
await DocumentLink.query(trx).insert({
|
||||
await this.documentLinkModel().query(trx).insert({
|
||||
modelRef,
|
||||
modelId,
|
||||
documentId: foundFile.id,
|
||||
@@ -57,23 +70,21 @@ export class LinkAttachment {
|
||||
|
||||
/**
|
||||
* Links the given file keys to the given model type and id.
|
||||
* @param {number} tenantId
|
||||
* @param {string[]} filekeys
|
||||
* @param {string} modelRef
|
||||
* @param {number} modelId
|
||||
* @param {Knex.Transaction} trx
|
||||
* @param {string[]} filekeys - File keys.
|
||||
* @param {string} modelRef - Model reference.
|
||||
* @param {number} modelId - Model id.
|
||||
* @param {Knex.Transaction} trx - Knex transaction.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async bulkLink(
|
||||
tenantId: number,
|
||||
filekeys: string[],
|
||||
modelRef: string,
|
||||
modelId: number,
|
||||
trx?: Knex.Transaction
|
||||
trx?: Knex.Transaction,
|
||||
) {
|
||||
return bluebird.each(filekeys, async (fieldKey: string) => {
|
||||
try {
|
||||
await this.link(tenantId, fieldKey, modelRef, modelId, trx);
|
||||
await this.link(fieldKey, modelRef, modelId, trx);
|
||||
} catch {
|
||||
// Ignore catching exceptions in bulk action.
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import multer from 'multer';
|
||||
import type { Multer } from 'multer';
|
||||
import multerS3 from 'multer-s3';
|
||||
import { s3 } from '@/lib/S3/S3';
|
||||
import { Service } from 'typedi';
|
||||
import config from '@/config';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentUploadPipeline {
|
||||
constructor(
|
||||
private readonly configService: ConfigService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Middleware to ensure that S3 configuration is properly set before proceeding.
|
||||
* This function checks if the necessary S3 configuration keys are present and throws an error if any are missing.
|
||||
@@ -16,44 +16,21 @@ export class AttachmentUploadPipeline {
|
||||
* @param next The callback to pass control to the next middleware function.
|
||||
*/
|
||||
public validateS3Configured(req: Request, res: Response, next: NextFunction) {
|
||||
const config = this.configService.get('s3');
|
||||
|
||||
if (
|
||||
!config.s3.region ||
|
||||
!config.s3.accessKeyId ||
|
||||
!config.s3.secretAccessKey
|
||||
!config.region ||
|
||||
!config.accessKeyId ||
|
||||
!config.secretAccessKey
|
||||
) {
|
||||
const missingKeys = [];
|
||||
if (!config.s3.region) missingKeys.push('region');
|
||||
if (!config.s3.accessKeyId) missingKeys.push('accessKeyId');
|
||||
if (!config.s3.secretAccessKey) missingKeys.push('secretAccessKey');
|
||||
if (!config.region) missingKeys.push('region');
|
||||
if (!config.accessKeyId) missingKeys.push('accessKeyId');
|
||||
if (!config.secretAccessKey) missingKeys.push('secretAccessKey');
|
||||
const missing = missingKeys.join(', ');
|
||||
|
||||
throw new Error(`S3 configuration error: Missing ${missing}`);
|
||||
}
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Express middleware for uploading attachments to an S3 bucket.
|
||||
* It utilizes the multer middleware for handling multipart/form-data, specifically for file uploads.
|
||||
*/
|
||||
public uploadPipeline(): Multer {
|
||||
return multer({
|
||||
storage: multerS3({
|
||||
s3,
|
||||
bucket: config.s3.bucket,
|
||||
contentType: multerS3.AUTO_CONTENT_TYPE,
|
||||
metadata: function (req, file, cb) {
|
||||
cb(null, { fieldName: file.fieldname });
|
||||
},
|
||||
key: function (req, file, cb) {
|
||||
cb(null, Date.now().toString());
|
||||
},
|
||||
acl: function(req, file, cb) {
|
||||
// Conditionally set file to public or private based on isPublic flag
|
||||
const aclValue = true ? 'public-read' : 'private';
|
||||
cb(null, aclValue); // Set ACL based on the isPublic flag
|
||||
}
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,56 @@
|
||||
import bluebird from 'bluebird';
|
||||
import { difference } from 'lodash';
|
||||
import {
|
||||
validateLinkModelEntryExists,
|
||||
validateLinkModelExists,
|
||||
} from './_utils';
|
||||
import { Knex } from 'knex';
|
||||
import { difference } from 'lodash';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DocumentLinkModel } from './models/DocumentLink.model';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
import { DocumentModel } from './models/Document.model';
|
||||
import { getAttachableModelsMap } from './decorators/InjectAttachable.decorator';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
|
||||
@Injectable()
|
||||
export class UnlinkAttachment {
|
||||
constructor(
|
||||
private moduleRef: ModuleRef,
|
||||
|
||||
@Inject(DocumentModel.name)
|
||||
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
||||
|
||||
@Inject(DocumentLinkModel.name)
|
||||
private readonly documentLinkModel: TenantModelProxy<
|
||||
typeof DocumentLinkModel
|
||||
>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Unlink the attachments from the model entry.
|
||||
* @param {number} tenantId
|
||||
* @param {string} filekey
|
||||
* @param {string} modelRef
|
||||
* @param {number} modelId
|
||||
* @param {string} filekey - File key.
|
||||
* @param {string} modelRef - Model reference.
|
||||
* @param {number} modelId - Model id.
|
||||
*/
|
||||
async unlink(
|
||||
tenantId: number,
|
||||
filekey: string,
|
||||
modelRef: string,
|
||||
modelId: number,
|
||||
trx?: Knex.Transaction
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<void> {
|
||||
const { DocumentLink, Document, ...models } = this.tenancy.models(tenantId);
|
||||
|
||||
const LinkModel = models[modelRef];
|
||||
validateLinkModelExists(LinkModel);
|
||||
const attachmentsAttachableModels = getAttachableModelsMap();
|
||||
const attachableModel = attachmentsAttachableModels.get(modelRef);
|
||||
|
||||
validateLinkModelExists(attachableModel);
|
||||
|
||||
const LinkModel = this.moduleRef.get(modelRef, { strict: false });
|
||||
const foundLinkModel = await LinkModel.query(trx).findById(modelId);
|
||||
validateLinkModelEntryExists(foundLinkModel);
|
||||
|
||||
const document = await Document.query(trx).findOne('key', filekey);
|
||||
const document = await this.documentModel().query(trx).findOne('key', filekey);
|
||||
|
||||
// Delete the document link.
|
||||
await DocumentLink.query(trx)
|
||||
await this.documentLinkModel().query(trx)
|
||||
.where('modelRef', modelRef)
|
||||
.where('modelId', modelId)
|
||||
.where('documentId', document.id)
|
||||
@@ -43,22 +59,20 @@ export class UnlinkAttachment {
|
||||
|
||||
/**
|
||||
* Bulk unlink the attachments from the model entry.
|
||||
* @param {number} tenantId
|
||||
* @param {string} fieldkey
|
||||
* @param {string} modelRef
|
||||
* @param {number} modelId
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async bulkUnlink(
|
||||
tenantId: number,
|
||||
filekeys: string[],
|
||||
modelRef: string,
|
||||
modelId: number,
|
||||
trx?: Knex.Transaction
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<void> {
|
||||
await bluebird.each(filekeys, (fieldKey: string) => {
|
||||
try {
|
||||
this.unlink(tenantId, fieldKey, modelRef, modelId, trx);
|
||||
this.unlink(fieldKey, modelRef, modelId, trx);
|
||||
} catch {
|
||||
// Ignore catching exceptions on bulk action.
|
||||
}
|
||||
@@ -74,15 +88,13 @@ export class UnlinkAttachment {
|
||||
* @param {Knex.Transaction} trx
|
||||
*/
|
||||
async unlinkUnpresentedKeys(
|
||||
tenantId: number,
|
||||
presentedKeys: string[],
|
||||
modelRef: string,
|
||||
modelId: number,
|
||||
trx?: Knex.Transaction
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<void> {
|
||||
const { DocumentLink } = this.tenancy.models(tenantId);
|
||||
|
||||
const modelLinks = await DocumentLink.query(trx)
|
||||
const modelLinks = await this.documentLinkModel()
|
||||
.query(trx)
|
||||
.where('modelRef', modelRef)
|
||||
.where('modelId', modelId)
|
||||
.withGraphFetched('document');
|
||||
@@ -90,33 +102,30 @@ export class UnlinkAttachment {
|
||||
const modelLinkKeys = modelLinks.map((link) => link.document.key);
|
||||
const unpresentedKeys = difference(modelLinkKeys, presentedKeys);
|
||||
|
||||
await this.bulkUnlink(tenantId, unpresentedKeys, modelRef, modelId, trx);
|
||||
await this.bulkUnlink(unpresentedKeys, modelRef, modelId, trx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink all attachments of the given model type and id.
|
||||
* @param {number} tenantId
|
||||
* @param {string} modelRef
|
||||
* @param {number} modelId
|
||||
* @param {Knex.Transaction} trx
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async unlinkAllModelKeys(
|
||||
tenantId: number,
|
||||
modelRef: string,
|
||||
modelId: number,
|
||||
trx?: Knex.Transaction
|
||||
trx?: Knex.Transaction,
|
||||
): Promise<void> {
|
||||
const { DocumentLink } = this.tenancy.models(tenantId);
|
||||
|
||||
// Get all the keys of the modelRef and modelId.
|
||||
const modelLinks = await DocumentLink.query(trx)
|
||||
const modelLinks = await this.documentLinkModel()
|
||||
.query(trx)
|
||||
.where('modelRef', modelRef)
|
||||
.where('modelId', modelId)
|
||||
.withGraphFetched('document');
|
||||
|
||||
const modelLinkKeys = modelLinks.map((link) => link.document.key);
|
||||
|
||||
await this.bulkUnlink(tenantId, modelLinkKeys, modelRef, modelId, trx);
|
||||
await this.bulkUnlink(modelLinkKeys, modelRef, modelId, trx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
import { castArray, difference } from 'lodash';
|
||||
import HasTenancyService from '../Tenancy/TenancyService';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||
import { DocumentModel } from './models/Document.model';
|
||||
import { ServiceError } from '../Items/ServiceError';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class ValidateAttachments {
|
||||
|
||||
constructor(
|
||||
private readonly documentModel: TenantModelProxy<typeof DocumentModel>
|
||||
) {
|
||||
@Inject(DocumentModel.name)
|
||||
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
||||
) {}
|
||||
|
||||
}
|
||||
/**
|
||||
* Validates the given file keys existance.
|
||||
* @param {number} tenantId
|
||||
* @param {string|string[]} key
|
||||
*/
|
||||
async validate(tenantId: number, key: string | string[]) {
|
||||
async validate(key: string | string[]) {
|
||||
const keys = castArray(key);
|
||||
const documents = await this.documentModel().query().whereIn('key', key);
|
||||
const documentKeys = documents.map((document) => document.key);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { ServiceError } from '../Items/ServiceError';
|
||||
import { ERRORS } from './constants';
|
||||
|
||||
export const validateLinkModelExists = (LinkModel) => {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
// Array to store attachable model names
|
||||
const attachableModelsMap: Map<string, boolean> = new Map();
|
||||
|
||||
/**
|
||||
* Decorator that marks a class as attachable and adds its name to the attachable models array.
|
||||
* This is used to track which models can have attachments.
|
||||
*/
|
||||
export function InjectAttachable() {
|
||||
return function (target: any) {
|
||||
// Add the model name to the attachable models array
|
||||
attachableModelsMap.set(target.name, true);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all attachable model names
|
||||
*/
|
||||
export function getAttachableModelsMap() {
|
||||
return attachableModelsMap;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { IsNotEmpty, IsString } from "class-validator";
|
||||
|
||||
|
||||
@@ -6,3 +7,27 @@ export class AttachmentLinkDto {
|
||||
@IsNotEmpty()
|
||||
key: string;
|
||||
}
|
||||
|
||||
|
||||
export class UnlinkAttachmentDto {
|
||||
@IsNotEmpty()
|
||||
modelRef: string;
|
||||
|
||||
|
||||
@IsNotEmpty()
|
||||
modelId: number;
|
||||
}
|
||||
|
||||
export class LinkAttachmentDto {
|
||||
@IsNotEmpty()
|
||||
modelRef: string;
|
||||
|
||||
|
||||
@IsNotEmpty()
|
||||
modelId: number;
|
||||
}
|
||||
|
||||
export class UploadAttachmentDto {
|
||||
@ApiProperty({ type: 'string', format: 'binary' })
|
||||
file: any;
|
||||
}
|
||||
@@ -1,68 +1,45 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
IBIllEventDeletedPayload,
|
||||
IBillCreatedPayload,
|
||||
IBillCreatingPayload,
|
||||
IBillEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
} from '@/modules/Bills/Bills.types';
|
||||
import { ValidateAttachments } from '../ValidateAttachments';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentsOnBills {
|
||||
@Inject()
|
||||
private linkAttachmentService: LinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private unlinkAttachmentService: UnlinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private validateDocuments: ValidateAttachments;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {LinkAttachment} linkAttachmentService
|
||||
* @param {UnlinkAttachment} unlinkAttachmentService
|
||||
* @param {ValidateAttachments} validateDocuments
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.bill.onCreating,
|
||||
this.validateAttachmentsOnBillCreate.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onCreated,
|
||||
this.handleAttachmentsOnBillCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onEdited,
|
||||
this.handleUnlinkUnpresentedKeysOnBillEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onEdited,
|
||||
this.handleLinkPresentedKeysOnBillEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.bill.onDeleting,
|
||||
this.handleUnlinkAttachmentsOnBillDeleted.bind(this)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly linkAttachmentService: LinkAttachment,
|
||||
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||
private readonly validateDocuments: ValidateAttachments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the attachment keys on creating bill.
|
||||
* @param {ISaleInvoiceCreatingPaylaod}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async validateAttachmentsOnBillCreate({
|
||||
@OnEvent(events.bill.onCreating)
|
||||
async validateAttachmentsOnBillCreate({
|
||||
billDTO,
|
||||
tenantId,
|
||||
}: IBillCreatingPayload): Promise<void> {
|
||||
if (isEmpty(billDTO.attachments)) {
|
||||
return;
|
||||
}
|
||||
const documentKeys = billDTO?.attachments?.map((a) => a.key);
|
||||
|
||||
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||
await this.validateDocuments.validate(documentKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +47,8 @@ export class AttachmentsOnBills {
|
||||
* @param {ISaleInvoiceCreatedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleAttachmentsOnBillCreated({
|
||||
tenantId,
|
||||
@OnEvent(events.bill.onCreated)
|
||||
async handleAttachmentsOnBillCreated({
|
||||
bill,
|
||||
billDTO,
|
||||
trx,
|
||||
@@ -79,32 +56,27 @@ export class AttachmentsOnBills {
|
||||
if (isEmpty(billDTO.attachments)) return;
|
||||
|
||||
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'Bill',
|
||||
bill.id,
|
||||
trx
|
||||
);
|
||||
|
||||
await this.linkAttachmentService.bulkLink(keys, 'Bill', bill.id, trx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles unlinking all the unpresented keys of the edited bill.
|
||||
* @param {IBillEditedPayload}
|
||||
*/
|
||||
private async handleUnlinkUnpresentedKeysOnBillEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.bill.onEdited)
|
||||
async handleUnlinkUnpresentedKeysOnBillEdited({
|
||||
billDTO,
|
||||
bill,
|
||||
trx
|
||||
trx,
|
||||
}: IBillEditedPayload) {
|
||||
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
||||
|
||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||
tenantId,
|
||||
keys,
|
||||
'Bill',
|
||||
bill.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -113,8 +85,8 @@ export class AttachmentsOnBills {
|
||||
* @param {ISaleInvoiceEditedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleLinkPresentedKeysOnBillEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.bill.onEdited)
|
||||
async handleLinkPresentedKeysOnBillEdited({
|
||||
billDTO,
|
||||
oldBill,
|
||||
trx,
|
||||
@@ -122,13 +94,8 @@ export class AttachmentsOnBills {
|
||||
if (isEmpty(billDTO.attachments)) return;
|
||||
|
||||
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'Bill',
|
||||
oldBill.id,
|
||||
trx
|
||||
);
|
||||
|
||||
await this.linkAttachmentService.bulkLink(keys, 'Bill', oldBill.id, trx);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,16 +103,15 @@ export class AttachmentsOnBills {
|
||||
* @param {ISaleInvoiceDeletedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnBillDeleted({
|
||||
tenantId,
|
||||
@OnEvent(events.bill.onDeleting)
|
||||
async handleUnlinkAttachmentsOnBillDeleted({
|
||||
oldBill,
|
||||
trx,
|
||||
}: IBIllEventDeletedPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'Bill',
|
||||
oldBill.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,45 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
ICreditNoteCreatedPayload,
|
||||
ICreditNoteCreatingPayload,
|
||||
ICreditNoteDeletingPayload,
|
||||
ICreditNoteEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
} from '@/modules/CreditNotes/types/CreditNotes.types';
|
||||
import { ValidateAttachments } from '../ValidateAttachments';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentsOnCreditNote {
|
||||
@Inject()
|
||||
private linkAttachmentService: LinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private unlinkAttachmentService: UnlinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private validateDocuments: ValidateAttachments;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
* @param {LinkAttachment} linkAttachmentService -
|
||||
* @param {UnlinkAttachment} unlinkAttachmentService -
|
||||
* @param {ValidateAttachments} validateDocuments -
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.creditNote.onCreating,
|
||||
this.validateAttachmentsOnCreditNoteCreate.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.creditNote.onCreated,
|
||||
this.handleAttachmentsOnCreditNoteCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.creditNote.onEdited,
|
||||
this.handleUnlinkUnpresentedKeysOnCreditNoteEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.creditNote.onEdited,
|
||||
this.handleLinkPresentedKeysOnCreditNoteEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.creditNote.onDeleting,
|
||||
this.handleUnlinkAttachmentsOnCreditNoteDeleted.bind(this)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly linkAttachmentService: LinkAttachment,
|
||||
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||
private readonly validateDocuments: ValidateAttachments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the attachment keys on creating credit note.
|
||||
* @param {ICreditNoteCreatingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async validateAttachmentsOnCreditNoteCreate({
|
||||
@OnEvent(events.creditNote.onCreating)
|
||||
async validateAttachmentsOnCreditNoteCreate({
|
||||
creditNoteDTO,
|
||||
tenantId,
|
||||
}: ICreditNoteCreatingPayload): Promise<void> {
|
||||
if (isEmpty(creditNoteDTO.attachments)) {
|
||||
return;
|
||||
}
|
||||
const documentKeys = creditNoteDTO?.attachments?.map((a) => a.key);
|
||||
|
||||
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||
await this.validateDocuments.validate(documentKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +47,8 @@ export class AttachmentsOnCreditNote {
|
||||
* @param {ICreditNoteCreatedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleAttachmentsOnCreditNoteCreated({
|
||||
tenantId,
|
||||
@OnEvent(events.creditNote.onCreated)
|
||||
async handleAttachmentsOnCreditNoteCreated({
|
||||
creditNote,
|
||||
creditNoteDTO,
|
||||
trx,
|
||||
@@ -79,12 +56,12 @@ export class AttachmentsOnCreditNote {
|
||||
if (isEmpty(creditNoteDTO.attachments)) return;
|
||||
|
||||
const keys = creditNoteDTO.attachments?.map((attachment) => attachment.key);
|
||||
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'CreditNote',
|
||||
creditNote.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -92,21 +69,20 @@ export class AttachmentsOnCreditNote {
|
||||
* Handles unlinking all the unpresented keys of the edited credit note.
|
||||
* @param {ICreditNoteEditedPayload}
|
||||
*/
|
||||
private async handleUnlinkUnpresentedKeysOnCreditNoteEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.creditNote.onEdited)
|
||||
async handleUnlinkUnpresentedKeysOnCreditNoteEdited({
|
||||
creditNoteEditDTO,
|
||||
oldCreditNote,
|
||||
trx,
|
||||
}: ICreditNoteEditedPayload) {
|
||||
const keys = creditNoteEditDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||
tenantId,
|
||||
keys,
|
||||
'CreditNote',
|
||||
oldCreditNote.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -115,8 +91,8 @@ export class AttachmentsOnCreditNote {
|
||||
* @param {ICreditNoteEditedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleLinkPresentedKeysOnCreditNoteEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.creditNote.onEdited)
|
||||
async handleLinkPresentedKeysOnCreditNoteEdited({
|
||||
creditNoteEditDTO,
|
||||
oldCreditNote,
|
||||
trx,
|
||||
@@ -124,14 +100,13 @@ export class AttachmentsOnCreditNote {
|
||||
if (isEmpty(creditNoteEditDTO.attachments)) return;
|
||||
|
||||
const keys = creditNoteEditDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'CreditNote',
|
||||
oldCreditNote.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -140,16 +115,15 @@ export class AttachmentsOnCreditNote {
|
||||
* @param {ICreditNoteDeletingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnCreditNoteDeleted({
|
||||
tenantId,
|
||||
@OnEvent(events.creditNote.onDeleting)
|
||||
async handleUnlinkAttachmentsOnCreditNoteDeleted({
|
||||
oldCreditNote,
|
||||
trx,
|
||||
}: ICreditNoteDeletingPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'CreditNote',
|
||||
oldCreditNote.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,40 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
IExpenseCreatedPayload,
|
||||
IExpenseCreatingPayload,
|
||||
IExpenseDeletingPayload,
|
||||
IExpenseEventEditPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
} from '@/modules/Expenses/Expenses.types';
|
||||
import { ValidateAttachments } from '../ValidateAttachments';
|
||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentsOnExpenses {
|
||||
@Inject()
|
||||
private linkAttachmentService: LinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private unlinkAttachmentService: UnlinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private validateDocuments: ValidateAttachments;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.expenses.onCreating,
|
||||
this.validateAttachmentsOnExpenseCreate.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.expenses.onCreated,
|
||||
this.handleAttachmentsOnExpenseCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.expenses.onEdited,
|
||||
this.handleUnlinkUnpresentedKeysOnExpenseEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.expenses.onEdited,
|
||||
this.handleLinkPresentedKeysOnExpenseEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.expenses.onDeleting,
|
||||
this.handleUnlinkAttachmentsOnExpenseDeleted.bind(this)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly linkAttachmentService: LinkAttachment,
|
||||
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||
private readonly validateDocuments: ValidateAttachments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the attachment keys on creating expense.
|
||||
* @param {ISaleInvoiceCreatingPaylaod}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async validateAttachmentsOnExpenseCreate({
|
||||
@OnEvent(events.expenses.onCreating)
|
||||
async validateAttachmentsOnExpenseCreate({
|
||||
expenseDTO,
|
||||
tenantId,
|
||||
}: IExpenseCreatingPayload): Promise<void> {
|
||||
if (isEmpty(expenseDTO.attachments)) {
|
||||
return;
|
||||
}
|
||||
const documentKeys = expenseDTO?.attachments?.map((a) => a.key);
|
||||
|
||||
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||
await this.validateDocuments.validate(documentKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +42,8 @@ export class AttachmentsOnExpenses {
|
||||
* @param {ISaleInvoiceCreatedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleAttachmentsOnExpenseCreated({
|
||||
tenantId,
|
||||
@OnEvent(events.expenses.onCreated)
|
||||
async handleAttachmentsOnExpenseCreated({
|
||||
expenseDTO,
|
||||
expense,
|
||||
trx,
|
||||
@@ -79,12 +51,12 @@ export class AttachmentsOnExpenses {
|
||||
if (isEmpty(expenseDTO.attachments)) return;
|
||||
|
||||
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
||||
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'Expense',
|
||||
expense.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -92,19 +64,18 @@ export class AttachmentsOnExpenses {
|
||||
* Handles unlinking all the unpresented keys of the edited expense.
|
||||
* @param {ISaleInvoiceEditedPayload}
|
||||
*/
|
||||
private async handleUnlinkUnpresentedKeysOnExpenseEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.expenses.onEdited)
|
||||
async handleUnlinkUnpresentedKeysOnExpenseEdited({
|
||||
expenseDTO,
|
||||
expense,
|
||||
trx,
|
||||
}: IExpenseEventEditPayload) {
|
||||
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||
tenantId,
|
||||
keys,
|
||||
'Expense',
|
||||
expense.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -113,8 +84,8 @@ export class AttachmentsOnExpenses {
|
||||
* @param {ISaleInvoiceEditedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleLinkPresentedKeysOnExpenseEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.expenses.onEdited)
|
||||
async handleLinkPresentedKeysOnExpenseEdited({
|
||||
expenseDTO,
|
||||
oldExpense,
|
||||
trx,
|
||||
@@ -123,11 +94,10 @@ export class AttachmentsOnExpenses {
|
||||
|
||||
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'Expense',
|
||||
oldExpense.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -136,16 +106,15 @@ export class AttachmentsOnExpenses {
|
||||
* @param {ISaleInvoiceDeletedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnExpenseDeleted({
|
||||
tenantId,
|
||||
@OnEvent(events.expenses.onDeleting)
|
||||
async handleUnlinkAttachmentsOnExpenseDeleted({
|
||||
oldExpense,
|
||||
trx,
|
||||
}: IExpenseDeletingPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'Expense',
|
||||
oldExpense.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,40 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
IManualJournalCreatingPayload,
|
||||
IManualJournalEventCreatedPayload,
|
||||
IManualJournalEventDeletedPayload,
|
||||
IManualJournalEventEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
} from '@/modules/ManualJournals/types/ManualJournals.types';
|
||||
import { ValidateAttachments } from '../ValidateAttachments';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentsOnManualJournals {
|
||||
@Inject()
|
||||
private linkAttachmentService: LinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private unlinkAttachmentService: UnlinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private validateDocuments: ValidateAttachments;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.manualJournals.onCreating,
|
||||
this.validateAttachmentsOnManualJournalCreate.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.manualJournals.onCreated,
|
||||
this.handleAttachmentsOnManualJournalCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.manualJournals.onEdited,
|
||||
this.handleUnlinkUnpresentedKeysOnManualJournalEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.manualJournals.onEdited,
|
||||
this.handleLinkPresentedKeysOnManualJournalEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.manualJournals.onDeleting,
|
||||
this.handleUnlinkAttachmentsOnManualJournalDeleted.bind(this)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly linkAttachmentService: LinkAttachment,
|
||||
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||
private readonly validateDocuments: ValidateAttachments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the attachment keys on creating manual journal.
|
||||
* @param {IManualJournalCreatingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async validateAttachmentsOnManualJournalCreate({
|
||||
@OnEvent(events.manualJournals.onCreating)
|
||||
async validateAttachmentsOnManualJournalCreate({
|
||||
manualJournalDTO,
|
||||
tenantId,
|
||||
}: IManualJournalCreatingPayload): Promise<void> {
|
||||
if (isEmpty(manualJournalDTO.attachments)) {
|
||||
return;
|
||||
}
|
||||
const documentKeys = manualJournalDTO?.attachments?.map((a) => a.key);
|
||||
|
||||
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||
await this.validateDocuments.validate(documentKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +42,8 @@ export class AttachmentsOnManualJournals {
|
||||
* @param {IManualJournalEventCreatedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleAttachmentsOnManualJournalCreated({
|
||||
tenantId,
|
||||
@OnEvent(events.manualJournals.onCreated)
|
||||
async handleAttachmentsOnManualJournalCreated({
|
||||
manualJournalDTO,
|
||||
manualJournal,
|
||||
trx,
|
||||
@@ -79,14 +51,13 @@ export class AttachmentsOnManualJournals {
|
||||
if (isEmpty(manualJournalDTO.attachments)) return;
|
||||
|
||||
const keys = manualJournalDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'ManualJournal',
|
||||
manualJournal.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,21 +65,20 @@ export class AttachmentsOnManualJournals {
|
||||
* Handles unlinking all the unpresented keys of the edited manual journal.
|
||||
* @param {ISaleInvoiceEditedPayload}
|
||||
*/
|
||||
private async handleUnlinkUnpresentedKeysOnManualJournalEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.manualJournals.onEdited)
|
||||
async handleUnlinkUnpresentedKeysOnManualJournalEdited({
|
||||
manualJournalDTO,
|
||||
manualJournal,
|
||||
trx
|
||||
trx,
|
||||
}: IManualJournalEventEditedPayload) {
|
||||
const keys = manualJournalDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||
tenantId,
|
||||
keys,
|
||||
'SaleInvoice',
|
||||
manualJournal.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -117,8 +87,8 @@ export class AttachmentsOnManualJournals {
|
||||
* @param {ISaleInvoiceEditedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleLinkPresentedKeysOnManualJournalEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.manualJournals.onEdited)
|
||||
async handleLinkPresentedKeysOnManualJournalEdited({
|
||||
manualJournalDTO,
|
||||
oldManualJournal,
|
||||
trx,
|
||||
@@ -126,14 +96,13 @@ export class AttachmentsOnManualJournals {
|
||||
if (isEmpty(manualJournalDTO.attachments)) return;
|
||||
|
||||
const keys = manualJournalDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'ManualJournal',
|
||||
oldManualJournal.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -142,16 +111,15 @@ export class AttachmentsOnManualJournals {
|
||||
* @param {ISaleInvoiceDeletedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnManualJournalDeleted({
|
||||
tenantId,
|
||||
@OnEvent(events.manualJournals.onDeleting)
|
||||
async handleUnlinkAttachmentsOnManualJournalDeleted({
|
||||
oldManualJournal,
|
||||
trx,
|
||||
}: IManualJournalEventDeletedPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'SaleInvoice',
|
||||
oldManualJournal.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,40 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
IBillPaymentCreatingPayload,
|
||||
IBillPaymentDeletingPayload,
|
||||
IBillPaymentEventCreatedPayload,
|
||||
IBillPaymentEventEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
} from '@/modules/BillPayments/types/BillPayments.types';
|
||||
import { ValidateAttachments } from '../ValidateAttachments';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||
import { events } from '@/common/events/events';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentsOnBillPayments {
|
||||
@Inject()
|
||||
private linkAttachmentService: LinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private unlinkAttachmentService: UnlinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private validateDocuments: ValidateAttachments;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.billPayment.onCreating,
|
||||
this.validateAttachmentsOnBillPaymentCreate.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.billPayment.onCreated,
|
||||
this.handleAttachmentsOnBillPaymentCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.billPayment.onEdited,
|
||||
this.handleUnlinkUnpresentedKeysOnBillPaymentEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.billPayment.onEdited,
|
||||
this.handleLinkPresentedKeysOnBillPaymentEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.billPayment.onDeleting,
|
||||
this.handleUnlinkAttachmentsOnBillPaymentDeleted.bind(this)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly linkAttachmentService: LinkAttachment,
|
||||
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||
private readonly validateDocuments: ValidateAttachments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the attachment keys on creating bill payment.
|
||||
* @param {IBillPaymentCreatingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async validateAttachmentsOnBillPaymentCreate({
|
||||
@OnEvent(events.billPayment.onCreating)
|
||||
async validateAttachmentsOnBillPaymentCreate({
|
||||
billPaymentDTO,
|
||||
tenantId,
|
||||
}: IBillPaymentCreatingPayload): Promise<void> {
|
||||
if (isEmpty(billPaymentDTO.attachments)) {
|
||||
return;
|
||||
}
|
||||
const documentKeys = billPaymentDTO?.attachments?.map((a) => a.key);
|
||||
|
||||
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||
await this.validateDocuments.validate(documentKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +42,8 @@ export class AttachmentsOnBillPayments {
|
||||
* @param {IBillPaymentEventCreatedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleAttachmentsOnBillPaymentCreated({
|
||||
tenantId,
|
||||
@OnEvent(events.billPayment.onCreated)
|
||||
async handleAttachmentsOnBillPaymentCreated({
|
||||
billPaymentDTO,
|
||||
billPayment,
|
||||
trx,
|
||||
@@ -79,14 +51,13 @@ export class AttachmentsOnBillPayments {
|
||||
if (isEmpty(billPaymentDTO.attachments)) return;
|
||||
|
||||
const keys = billPaymentDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'BillPayment',
|
||||
billPayment.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,21 +65,20 @@ export class AttachmentsOnBillPayments {
|
||||
* Handles unlinking all the unpresented keys of the edited bill payment.
|
||||
* @param {IBillPaymentEventEditedPayload}
|
||||
*/
|
||||
private async handleUnlinkUnpresentedKeysOnBillPaymentEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.billPayment.onEdited)
|
||||
async handleUnlinkUnpresentedKeysOnBillPaymentEdited({
|
||||
billPaymentDTO,
|
||||
oldBillPayment,
|
||||
trx,
|
||||
}: IBillPaymentEventEditedPayload) {
|
||||
const keys = billPaymentDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||
tenantId,
|
||||
keys,
|
||||
'BillPayment',
|
||||
oldBillPayment.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -117,8 +87,8 @@ export class AttachmentsOnBillPayments {
|
||||
* @param {IBillPaymentEventEditedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleLinkPresentedKeysOnBillPaymentEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.billPayment.onEdited)
|
||||
async handleLinkPresentedKeysOnBillPaymentEdited({
|
||||
billPaymentDTO,
|
||||
oldBillPayment,
|
||||
trx,
|
||||
@@ -126,14 +96,13 @@ export class AttachmentsOnBillPayments {
|
||||
if (isEmpty(billPaymentDTO.attachments)) return;
|
||||
|
||||
const keys = billPaymentDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'BillPayment',
|
||||
oldBillPayment.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -142,16 +111,15 @@ export class AttachmentsOnBillPayments {
|
||||
* @param {IBillPaymentDeletingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnBillPaymentDeleted({
|
||||
tenantId,
|
||||
@OnEvent(events.billPayment.onDeleting)
|
||||
async handleUnlinkAttachmentsOnBillPaymentDeleted({
|
||||
oldBillPayment,
|
||||
trx,
|
||||
}: IBillPaymentDeletingPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'BillPayment',
|
||||
oldBillPayment.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,40 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
IPaymentReceivedCreatedPayload,
|
||||
IPaymentReceivedCreatingPayload,
|
||||
IPaymentReceivedDeletingPayload,
|
||||
IPaymentReceivedEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
} from '@/modules/PaymentReceived/types/PaymentReceived.types';
|
||||
import { ValidateAttachments } from '../ValidateAttachments';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentsOnPaymentsReceived {
|
||||
@Inject()
|
||||
private linkAttachmentService: LinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private unlinkAttachmentService: UnlinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private validateDocuments: ValidateAttachments;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.paymentReceive.onCreating,
|
||||
this.validateAttachmentsOnPaymentCreate.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.paymentReceive.onCreated,
|
||||
this.handleAttachmentsOnPaymentCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.paymentReceive.onEdited,
|
||||
this.handleUnlinkUnpresentedKeysOnPaymentEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.paymentReceive.onEdited,
|
||||
this.handleLinkPresentedKeysOnPaymentEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.paymentReceive.onDeleting,
|
||||
this.handleUnlinkAttachmentsOnPaymentDelete.bind(this)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly linkAttachmentService: LinkAttachment,
|
||||
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||
private readonly validateDocuments: ValidateAttachments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the attachment keys on creating payment.
|
||||
* @param {IPaymentReceivedCreatingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async validateAttachmentsOnPaymentCreate({
|
||||
@OnEvent(events.paymentReceive.onCreating)
|
||||
async validateAttachmentsOnPaymentCreate({
|
||||
paymentReceiveDTO,
|
||||
tenantId,
|
||||
}: IPaymentReceivedCreatingPayload): Promise<void> {
|
||||
if (isEmpty(paymentReceiveDTO.attachments)) {
|
||||
return;
|
||||
}
|
||||
const documentKeys = paymentReceiveDTO?.attachments?.map((a) => a.key);
|
||||
|
||||
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||
await this.validateDocuments.validate(documentKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +42,8 @@ export class AttachmentsOnPaymentsReceived {
|
||||
* @param {IPaymentReceivedCreatedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleAttachmentsOnPaymentCreated({
|
||||
tenantId,
|
||||
@OnEvent(events.paymentReceive.onCreated)
|
||||
async handleAttachmentsOnPaymentCreated({
|
||||
paymentReceiveDTO,
|
||||
paymentReceive,
|
||||
trx,
|
||||
@@ -79,14 +51,13 @@ export class AttachmentsOnPaymentsReceived {
|
||||
if (isEmpty(paymentReceiveDTO.attachments)) return;
|
||||
|
||||
const keys = paymentReceiveDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'PaymentReceive',
|
||||
paymentReceive.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,21 +65,20 @@ export class AttachmentsOnPaymentsReceived {
|
||||
* Handles unlinking all the unpresented keys of the edited payment.
|
||||
* @param {IPaymentReceivedEditedPayload}
|
||||
*/
|
||||
@OnEvent(events.paymentReceive.onEdited)
|
||||
private async handleUnlinkUnpresentedKeysOnPaymentEdited({
|
||||
tenantId,
|
||||
paymentReceiveDTO,
|
||||
oldPaymentReceive,
|
||||
trx,
|
||||
}: IPaymentReceivedEditedPayload) {
|
||||
const keys = paymentReceiveDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||
tenantId,
|
||||
keys,
|
||||
'PaymentReceive',
|
||||
oldPaymentReceive.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -117,8 +87,8 @@ export class AttachmentsOnPaymentsReceived {
|
||||
* @param {IPaymentReceivedEditedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleLinkPresentedKeysOnPaymentEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.paymentReceive.onEdited)
|
||||
async handleLinkPresentedKeysOnPaymentEdited({
|
||||
paymentReceiveDTO,
|
||||
oldPaymentReceive,
|
||||
trx,
|
||||
@@ -126,14 +96,13 @@ export class AttachmentsOnPaymentsReceived {
|
||||
if (isEmpty(paymentReceiveDTO.attachments)) return;
|
||||
|
||||
const keys = paymentReceiveDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'PaymentReceive',
|
||||
oldPaymentReceive.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -142,16 +111,15 @@ export class AttachmentsOnPaymentsReceived {
|
||||
* @param {ISaleInvoiceDeletedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnPaymentDelete({
|
||||
tenantId,
|
||||
@OnEvent(events.paymentReceive.onDeleting)
|
||||
async handleUnlinkAttachmentsOnPaymentDelete({
|
||||
oldPaymentReceive,
|
||||
trx,
|
||||
}: IPaymentReceivedDeletingPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'PaymentReceive',
|
||||
oldPaymentReceive.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,40 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
ISaleEstimateCreatedPayload,
|
||||
ISaleEstimateCreatingPayload,
|
||||
ISaleEstimateDeletingPayload,
|
||||
ISaleEstimateEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
} from '@/modules/SaleEstimates/types/SaleEstimates.types';
|
||||
import { ValidateAttachments } from '../ValidateAttachments';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentsOnSaleEstimates {
|
||||
@Inject()
|
||||
private linkAttachmentService: LinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private unlinkAttachmentService: UnlinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private validateDocuments: ValidateAttachments;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.saleEstimate.onCreating,
|
||||
this.validateAttachmentsOnSaleEstimateCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleEstimate.onCreated,
|
||||
this.handleAttachmentsOnSaleEstimateCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleEstimate.onEdited,
|
||||
this.handleUnlinkUnpresentedKeysOnSaleEstimateEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleEstimate.onEdited,
|
||||
this.handleLinkPresentedKeysOnSaleEstimateEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleEstimate.onDeleting,
|
||||
this.handleUnlinkAttachmentsOnSaleEstimateDelete.bind(this)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly linkAttachmentService: LinkAttachment,
|
||||
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||
private readonly validateDocuments: ValidateAttachments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the attachment keys on creating sale estimate.
|
||||
* @param {ISaleEstimateCreatingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async validateAttachmentsOnSaleEstimateCreated({
|
||||
@OnEvent(events.saleEstimate.onCreating)
|
||||
async validateAttachmentsOnSaleEstimateCreated({
|
||||
estimateDTO,
|
||||
tenantId,
|
||||
}: ISaleEstimateCreatingPayload): Promise<void> {
|
||||
if (isEmpty(estimateDTO.attachments)) {
|
||||
return;
|
||||
}
|
||||
const documentKeys = estimateDTO?.attachments?.map((a) => a.key);
|
||||
|
||||
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||
await this.validateDocuments.validate(documentKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +42,8 @@ export class AttachmentsOnSaleEstimates {
|
||||
* @param {ISaleEstimateCreatedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleAttachmentsOnSaleEstimateCreated({
|
||||
tenantId,
|
||||
@OnEvent(events.saleEstimate.onCreated)
|
||||
async handleAttachmentsOnSaleEstimateCreated({
|
||||
saleEstimateDTO,
|
||||
saleEstimate,
|
||||
trx,
|
||||
@@ -79,14 +51,13 @@ export class AttachmentsOnSaleEstimates {
|
||||
if (isEmpty(saleEstimateDTO.attachments)) return;
|
||||
|
||||
const keys = saleEstimateDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'SaleEstimate',
|
||||
saleEstimate.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,20 +65,19 @@ export class AttachmentsOnSaleEstimates {
|
||||
* Handles unlinking all the unpresented keys of the edited sale estimate.
|
||||
* @param {ISaleEstimateEditedPayload}
|
||||
*/
|
||||
private async handleUnlinkUnpresentedKeysOnSaleEstimateEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.saleEstimate.onEdited)
|
||||
async handleUnlinkUnpresentedKeysOnSaleEstimateEdited({
|
||||
estimateDTO,
|
||||
oldSaleEstimate,
|
||||
trx
|
||||
trx,
|
||||
}: ISaleEstimateEditedPayload) {
|
||||
const keys = estimateDTO.attachments?.map((attachment) => attachment.key);
|
||||
|
||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||
tenantId,
|
||||
keys,
|
||||
'SaleEstimate',
|
||||
oldSaleEstimate.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -116,8 +86,8 @@ export class AttachmentsOnSaleEstimates {
|
||||
* @param {ISaleEstimateEditedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleLinkPresentedKeysOnSaleEstimateEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.saleEstimate.onEdited)
|
||||
async handleLinkPresentedKeysOnSaleEstimateEdited({
|
||||
estimateDTO,
|
||||
oldSaleEstimate,
|
||||
trx,
|
||||
@@ -125,12 +95,12 @@ export class AttachmentsOnSaleEstimates {
|
||||
if (isEmpty(estimateDTO.attachments)) return;
|
||||
|
||||
const keys = estimateDTO.attachments?.map((attachment) => attachment.key);
|
||||
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'SaleEstimate',
|
||||
oldSaleEstimate.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -139,16 +109,15 @@ export class AttachmentsOnSaleEstimates {
|
||||
* @param {ISaleEstimateDeletingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnSaleEstimateDelete({
|
||||
tenantId,
|
||||
@OnEvent(events.saleEstimate.onDeleting)
|
||||
async handleUnlinkAttachmentsOnSaleEstimateDelete({
|
||||
oldSaleEstimate,
|
||||
trx,
|
||||
}: ISaleEstimateDeletingPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'SaleEstimate',
|
||||
oldSaleEstimate.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,40 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import {
|
||||
ISaleInvoiceCreatedPayload,
|
||||
ISaleInvoiceCreatingPaylaod,
|
||||
ISaleInvoiceDeletingPayload,
|
||||
ISaleInvoiceEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
} from '@/modules/SaleInvoices/SaleInvoice.types';
|
||||
import { ValidateAttachments } from '../ValidateAttachments';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentsOnSaleInvoiceCreated {
|
||||
@Inject()
|
||||
private linkAttachmentService: LinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private unlinkAttachmentService: UnlinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private validateDocuments: ValidateAttachments;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onCreating,
|
||||
this.validateAttachmentsOnSaleInvoiceCreate.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onCreated,
|
||||
this.handleAttachmentsOnSaleInvoiceCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onEdited,
|
||||
this.handleUnlinkUnpresentedKeysOnInvoiceEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onEdited,
|
||||
this.handleLinkPresentedKeysOnInvoiceEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onDeleting,
|
||||
this.handleUnlinkAttachmentsOnInvoiceDeleted.bind(this)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly linkAttachmentService: LinkAttachment,
|
||||
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||
private readonly validateDocuments: ValidateAttachments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the attachment keys on creating sale invoice.
|
||||
* @param {ISaleInvoiceCreatingPaylaod}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async validateAttachmentsOnSaleInvoiceCreate({
|
||||
@OnEvent(events.saleInvoice.onCreating)
|
||||
async validateAttachmentsOnSaleInvoiceCreate({
|
||||
saleInvoiceDTO,
|
||||
tenantId,
|
||||
}: ISaleInvoiceCreatingPaylaod): Promise<void> {
|
||||
if (isEmpty(saleInvoiceDTO.attachments)) {
|
||||
return;
|
||||
}
|
||||
const documentKeys = saleInvoiceDTO?.attachments?.map((a) => a.key);
|
||||
|
||||
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||
await this.validateDocuments.validate(documentKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +42,8 @@ export class AttachmentsOnSaleInvoiceCreated {
|
||||
* @param {ISaleInvoiceCreatedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleAttachmentsOnSaleInvoiceCreated({
|
||||
tenantId,
|
||||
@OnEvent(events.saleInvoice.onCreated)
|
||||
async handleAttachmentsOnSaleInvoiceCreated({
|
||||
saleInvoiceDTO,
|
||||
saleInvoice,
|
||||
trx,
|
||||
@@ -79,14 +51,13 @@ export class AttachmentsOnSaleInvoiceCreated {
|
||||
if (isEmpty(saleInvoiceDTO.attachments)) return;
|
||||
|
||||
const keys = saleInvoiceDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'SaleInvoice',
|
||||
saleInvoice.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,8 +65,8 @@ export class AttachmentsOnSaleInvoiceCreated {
|
||||
* Handles unlinking all the unpresented keys of the edited sale invoice.
|
||||
* @param {ISaleInvoiceEditedPayload}
|
||||
*/
|
||||
private async handleUnlinkUnpresentedKeysOnInvoiceEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.saleInvoice.onEdited)
|
||||
async handleUnlinkUnpresentedKeysOnInvoiceEdited({
|
||||
saleInvoiceDTO,
|
||||
saleInvoice,
|
||||
trx,
|
||||
@@ -103,14 +74,13 @@ export class AttachmentsOnSaleInvoiceCreated {
|
||||
// if (isEmpty(saleInvoiceDTO.attachments)) return;
|
||||
|
||||
const keys = saleInvoiceDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||
tenantId,
|
||||
keys,
|
||||
'SaleInvoice',
|
||||
saleInvoice.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -119,8 +89,8 @@ export class AttachmentsOnSaleInvoiceCreated {
|
||||
* @param {ISaleInvoiceEditedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleLinkPresentedKeysOnInvoiceEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.saleInvoice.onEdited)
|
||||
async handleLinkPresentedKeysOnInvoiceEdited({
|
||||
saleInvoiceDTO,
|
||||
oldSaleInvoice,
|
||||
trx,
|
||||
@@ -128,14 +98,13 @@ export class AttachmentsOnSaleInvoiceCreated {
|
||||
if (isEmpty(saleInvoiceDTO.attachments)) return;
|
||||
|
||||
const keys = saleInvoiceDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'SaleInvoice',
|
||||
oldSaleInvoice.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -144,16 +113,15 @@ export class AttachmentsOnSaleInvoiceCreated {
|
||||
* @param {ISaleInvoiceDeletedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnInvoiceDeleted({
|
||||
tenantId,
|
||||
@OnEvent(events.saleInvoice.onDeleting)
|
||||
async handleUnlinkAttachmentsOnInvoiceDeleted({
|
||||
oldSaleInvoice,
|
||||
trx,
|
||||
}: ISaleInvoiceDeletingPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'SaleInvoice',
|
||||
oldSaleInvoice.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,40 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import {
|
||||
ISaleReceiptCreatedPayload,
|
||||
ISaleReceiptCreatingPayload,
|
||||
ISaleReceiptDeletingPayload,
|
||||
ISaleReceiptEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
} from '../../SaleReceipts/types/SaleReceipts.types';
|
||||
import { ValidateAttachments } from '../ValidateAttachments';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentsOnSaleReceipt {
|
||||
@Inject()
|
||||
private linkAttachmentService: LinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private unlinkAttachmentService: UnlinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private validateDocuments: ValidateAttachments;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.saleReceipt.onCreating,
|
||||
this.validateAttachmentsOnSaleInvoiceCreate.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleReceipt.onCreated,
|
||||
this.handleAttachmentsOnSaleInvoiceCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleReceipt.onEdited,
|
||||
this.handleUnlinkUnpresentedKeysOnInvoiceEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleReceipt.onEdited,
|
||||
this.handleLinkPresentedKeysOnInvoiceEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleReceipt.onDeleting,
|
||||
this.handleUnlinkAttachmentsOnReceiptDeleted.bind(this)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly linkAttachmentService: LinkAttachment,
|
||||
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||
private readonly validateDocuments: ValidateAttachments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the attachment keys on creating sale receipt.
|
||||
* @param {ISaleReceiptCreatingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async validateAttachmentsOnSaleInvoiceCreate({
|
||||
@OnEvent(events.saleReceipt.onCreating)
|
||||
async validateAttachmentsOnSaleInvoiceCreate({
|
||||
saleReceiptDTO,
|
||||
tenantId,
|
||||
}: ISaleReceiptCreatingPayload): Promise<void> {
|
||||
if (isEmpty(saleReceiptDTO.attachments)) {
|
||||
return;
|
||||
}
|
||||
const documentKeys = saleReceiptDTO?.attachments?.map((a) => a.key);
|
||||
|
||||
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||
await this.validateDocuments.validate(documentKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +42,8 @@ export class AttachmentsOnSaleReceipt {
|
||||
* @param {ISaleReceiptCreatedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleAttachmentsOnSaleInvoiceCreated({
|
||||
tenantId,
|
||||
@OnEvent(events.saleReceipt.onCreated)
|
||||
async handleAttachmentsOnSaleInvoiceCreated({
|
||||
saleReceiptDTO,
|
||||
saleReceipt,
|
||||
trx,
|
||||
@@ -79,14 +51,13 @@ export class AttachmentsOnSaleReceipt {
|
||||
if (isEmpty(saleReceiptDTO.attachments)) return;
|
||||
|
||||
const keys = saleReceiptDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'SaleReceipt',
|
||||
saleReceipt.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,21 +65,20 @@ export class AttachmentsOnSaleReceipt {
|
||||
* Handles unlinking all the unpresented keys of the edited sale receipt.
|
||||
* @param {ISaleReceiptEditedPayload}
|
||||
*/
|
||||
private async handleUnlinkUnpresentedKeysOnInvoiceEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.saleReceipt.onEdited)
|
||||
async handleUnlinkUnpresentedKeysOnInvoiceEdited({
|
||||
saleReceiptDTO,
|
||||
saleReceipt,
|
||||
trx,
|
||||
}: ISaleReceiptEditedPayload) {
|
||||
const keys = saleReceiptDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||
tenantId,
|
||||
keys,
|
||||
'SaleReceipt',
|
||||
saleReceipt.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -117,8 +87,8 @@ export class AttachmentsOnSaleReceipt {
|
||||
* @param {ISaleReceiptEditedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleLinkPresentedKeysOnInvoiceEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.saleReceipt.onEdited)
|
||||
async handleLinkPresentedKeysOnInvoiceEdited({
|
||||
saleReceiptDTO,
|
||||
oldSaleReceipt,
|
||||
trx,
|
||||
@@ -126,14 +96,13 @@ export class AttachmentsOnSaleReceipt {
|
||||
if (isEmpty(saleReceiptDTO.attachments)) return;
|
||||
|
||||
const keys = saleReceiptDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'SaleReceipt',
|
||||
oldSaleReceipt.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -142,16 +111,15 @@ export class AttachmentsOnSaleReceipt {
|
||||
* @param {ISaleReceiptDeletingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnReceiptDeleted({
|
||||
tenantId,
|
||||
@OnEvent(events.saleReceipt.onDeleting)
|
||||
async handleUnlinkAttachmentsOnReceiptDeleted({
|
||||
oldSaleReceipt,
|
||||
trx,
|
||||
}: ISaleReceiptDeletingPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'SaleReceipt',
|
||||
oldSaleReceipt.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,68 +1,40 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { isEmpty } from 'lodash';
|
||||
import {
|
||||
IVendorCreditCreatedPayload,
|
||||
IVendorCreditCreatingPayload,
|
||||
IVendorCreditDeletingPayload,
|
||||
IVendorCreditEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
} from '../../VendorCredit/types/VendorCredit.types';
|
||||
import { ValidateAttachments } from '../ValidateAttachments';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||
import { LinkAttachment } from '../LinkAttachment';
|
||||
import { events } from '@/common/events/events';
|
||||
|
||||
@Service()
|
||||
@Injectable()
|
||||
export class AttachmentsOnVendorCredits {
|
||||
@Inject()
|
||||
private linkAttachmentService: LinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private unlinkAttachmentService: UnlinkAttachment;
|
||||
|
||||
@Inject()
|
||||
private validateDocuments: ValidateAttachments;
|
||||
|
||||
/**
|
||||
* Constructor method.
|
||||
*/
|
||||
public attach(bus) {
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onCreating,
|
||||
this.validateAttachmentsOnVendorCreditCreate.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onCreated,
|
||||
this.handleAttachmentsOnVendorCreditCreated.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onEdited,
|
||||
this.handleUnlinkUnpresentedKeysOnVendorCreditEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onEdited,
|
||||
this.handleLinkPresentedKeysOnVendorCreditEdited.bind(this)
|
||||
);
|
||||
bus.subscribe(
|
||||
events.vendorCredit.onDeleting,
|
||||
this.handleUnlinkAttachmentsOnVendorCreditDeleted.bind(this)
|
||||
);
|
||||
}
|
||||
constructor(
|
||||
private readonly linkAttachmentService: LinkAttachment,
|
||||
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||
private readonly validateDocuments: ValidateAttachments,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Validates the attachment keys on creating vendor credit.
|
||||
* @param {IVendorCreditCreatingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async validateAttachmentsOnVendorCreditCreate({
|
||||
@OnEvent(events.vendorCredit.onCreating)
|
||||
async validateAttachmentsOnVendorCreditCreate({
|
||||
vendorCreditCreateDTO,
|
||||
tenantId,
|
||||
}: IVendorCreditCreatingPayload): Promise<void> {
|
||||
if (isEmpty(vendorCreditCreateDTO.attachments)) {
|
||||
return;
|
||||
}
|
||||
const documentKeys = vendorCreditCreateDTO?.attachments?.map((a) => a.key);
|
||||
|
||||
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||
await this.validateDocuments.validate(documentKeys);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -70,8 +42,8 @@ export class AttachmentsOnVendorCredits {
|
||||
* @param {IVendorCreditCreatedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleAttachmentsOnVendorCreditCreated({
|
||||
tenantId,
|
||||
@OnEvent(events.vendorCredit.onCreated)
|
||||
async handleAttachmentsOnVendorCreditCreated({
|
||||
vendorCreditCreateDTO,
|
||||
vendorCredit,
|
||||
trx,
|
||||
@@ -79,14 +51,13 @@ export class AttachmentsOnVendorCredits {
|
||||
if (isEmpty(vendorCreditCreateDTO.attachments)) return;
|
||||
|
||||
const keys = vendorCreditCreateDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'VendorCredit',
|
||||
vendorCredit.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -94,21 +65,20 @@ export class AttachmentsOnVendorCredits {
|
||||
* Handles unlinking all the unpresented keys of the edited vendor credit.
|
||||
* @param {IVendorCreditEditedPayload}
|
||||
*/
|
||||
private async handleUnlinkUnpresentedKeysOnVendorCreditEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.vendorCredit.onEdited)
|
||||
async handleUnlinkUnpresentedKeysOnVendorCreditEdited({
|
||||
vendorCreditDTO,
|
||||
oldVendorCredit,
|
||||
trx,
|
||||
}: IVendorCreditEditedPayload) {
|
||||
const keys = vendorCreditDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||
tenantId,
|
||||
keys,
|
||||
'VendorCredit',
|
||||
oldVendorCredit.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -117,8 +87,8 @@ export class AttachmentsOnVendorCredits {
|
||||
* @param {IVendorCreditEditedPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleLinkPresentedKeysOnVendorCreditEdited({
|
||||
tenantId,
|
||||
@OnEvent(events.vendorCredit.onEdited)
|
||||
async handleLinkPresentedKeysOnVendorCreditEdited({
|
||||
vendorCreditDTO,
|
||||
oldVendorCredit,
|
||||
trx,
|
||||
@@ -126,14 +96,13 @@ export class AttachmentsOnVendorCredits {
|
||||
if (isEmpty(vendorCreditDTO.attachments)) return;
|
||||
|
||||
const keys = vendorCreditDTO.attachments?.map(
|
||||
(attachment) => attachment.key
|
||||
(attachment) => attachment.key,
|
||||
);
|
||||
await this.linkAttachmentService.bulkLink(
|
||||
tenantId,
|
||||
keys,
|
||||
'VendorCredit',
|
||||
oldVendorCredit.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -142,16 +111,15 @@ export class AttachmentsOnVendorCredits {
|
||||
* @param {IVendorCreditDeletingPayload}
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnVendorCreditDeleted({
|
||||
tenantId,
|
||||
@OnEvent(events.vendorCredit.onDeleting)
|
||||
async handleUnlinkAttachmentsOnVendorCreditDeleted({
|
||||
oldVendorCredit,
|
||||
trx,
|
||||
}: IVendorCreditDeletingPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'VendorCredit',
|
||||
oldVendorCredit.id,
|
||||
trx
|
||||
trx,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
import { Model, mixin } from 'objection';
|
||||
import { DocumentModel } from './Document.model';
|
||||
|
||||
export class DocumentLinkModel extends TenantBaseModel {
|
||||
document!: DocumentModel;
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import path from 'path';
|
||||
import config from '@/config';
|
||||
// import config from '@/config';
|
||||
|
||||
export const getUploadedObjectUri = (objectKey: string) => {
|
||||
return new URL(
|
||||
path.join(config.s3.bucket, objectKey),
|
||||
config.s3.endpoint
|
||||
).toString();
|
||||
return '';
|
||||
// return new URL(
|
||||
// path.join(config.s3.bucket, objectKey),
|
||||
// config.s3.endpoint
|
||||
// ).toString();
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { BaseModel } from '@/models/Model';
|
||||
|
||||
export class DocumentLink extends BaseModel{
|
||||
public modelRef: string;
|
||||
public modelId: string;
|
||||
public modelId: number;
|
||||
public documentId: number;
|
||||
public expiresAt?: Date;
|
||||
|
||||
|
||||
@@ -4,11 +4,6 @@ import * as moment from 'moment';
|
||||
import * as composeAsync from 'async/compose';
|
||||
import * as R from 'ramda';
|
||||
import { ERRORS } from '../constants';
|
||||
import {
|
||||
ICreditNoteEditDTO,
|
||||
ICreditNoteEntryNewDTO,
|
||||
ICreditNoteNewDTO,
|
||||
} from '../types/CreditNotes.types';
|
||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
|
||||
import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform';
|
||||
|
||||
@@ -1,43 +1,9 @@
|
||||
import { Knex } from 'knex';
|
||||
import { CreditNote } from '../models/CreditNote';
|
||||
import { RefundCreditNote } from '../../CreditNoteRefunds/models/RefundCreditNote';
|
||||
import { AttachmentLinkDTO } from '@/modules/Attachments/Attachments.types';
|
||||
import { IItemEntryDTO } from '@/modules/TransactionItemEntry/ItemEntry.types';
|
||||
import { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
|
||||
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
|
||||
import { ILedgerEntry } from '@/modules/Ledger/types/Ledger.types';
|
||||
import { EditCreditNoteDto } from '../dtos/CreditNote.dto';
|
||||
|
||||
export interface ICreditNoteEntryNewDTO extends IItemEntryDTO {}
|
||||
|
||||
export interface ICreditNoteNewDTO {
|
||||
customerId: number;
|
||||
exchangeRate?: number;
|
||||
creditNoteDate: Date;
|
||||
creditNoteNumber: string;
|
||||
note: string;
|
||||
open: boolean;
|
||||
entries: ICreditNoteEntryNewDTO[];
|
||||
branchId?: number;
|
||||
warehouseId?: number;
|
||||
attachments?: AttachmentLinkDTO[];
|
||||
discount?: number;
|
||||
// discountType?: DiscountType;
|
||||
adjustment?: number;
|
||||
}
|
||||
|
||||
export interface ICreditNoteEditDTO {
|
||||
customerId: number;
|
||||
exchangeRate?: number;
|
||||
creditNoteDate: Date;
|
||||
creditNoteNumber: string;
|
||||
note: string;
|
||||
open: boolean;
|
||||
entries: ICreditNoteEntryNewDTO[];
|
||||
branchId?: number;
|
||||
warehouseId?: number;
|
||||
attachments?: AttachmentLinkDTO[];
|
||||
}
|
||||
import { CreateCreditNoteDto, EditCreditNoteDto } from '../dtos/CreditNote.dto';
|
||||
|
||||
export enum CreditNoteAction {
|
||||
Create = 'Create',
|
||||
@@ -74,13 +40,13 @@ export interface ICreditNoteEditedPayload {
|
||||
}
|
||||
|
||||
export interface ICreditNoteCreatedPayload {
|
||||
creditNoteDTO: ICreditNoteNewDTO;
|
||||
creditNoteDTO: CreateCreditNoteDto;
|
||||
creditNote: CreditNote;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface ICreditNoteCreatingPayload {
|
||||
creditNoteDTO: ICreditNoteNewDTO;
|
||||
creditNoteDTO: CreateCreditNoteDto;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ import { Knex } from 'knex';
|
||||
import { Expense } from './models/Expense.model';
|
||||
import { SystemUser } from '../System/models/SystemUser';
|
||||
import { IFilterRole } from '../DynamicListing/DynamicFilter/DynamicFilter.types';
|
||||
import { CreateExpenseDto, EditExpenseDto } from './dtos/Expense.dto';
|
||||
import { CreateExpense } from './commands/CreateExpense.service';
|
||||
|
||||
export interface IPaginationMeta {
|
||||
total: number;
|
||||
@@ -19,59 +21,28 @@ export interface IExpensesFilter {
|
||||
filterQuery?: (query: any) => void;
|
||||
}
|
||||
|
||||
export interface IExpenseCommonDTO {
|
||||
currencyCode: string;
|
||||
exchangeRate?: number;
|
||||
description?: string;
|
||||
paymentAccountId: number;
|
||||
peyeeId?: number;
|
||||
referenceNo?: string;
|
||||
publish: boolean;
|
||||
userId: number;
|
||||
paymentDate: Date;
|
||||
payeeId: number;
|
||||
categories: IExpenseCategoryDTO[];
|
||||
|
||||
branchId?: number;
|
||||
// attachments?: AttachmentLinkDTO[];
|
||||
}
|
||||
|
||||
export interface IExpenseCreateDTO extends IExpenseCommonDTO {}
|
||||
export interface IExpenseEditDTO extends IExpenseCommonDTO {}
|
||||
|
||||
export interface IExpenseCategoryDTO {
|
||||
id?: number;
|
||||
expenseAccountId: number;
|
||||
index: number;
|
||||
amount: number;
|
||||
description?: string;
|
||||
expenseId: number;
|
||||
landedCost?: boolean;
|
||||
projectId?: number;
|
||||
}
|
||||
|
||||
export interface IExpenseCreatingPayload {
|
||||
trx: Knex.Transaction;
|
||||
expenseDTO: IExpenseCreateDTO;
|
||||
expenseDTO: CreateExpenseDto;
|
||||
}
|
||||
|
||||
export interface IExpenseEventEditingPayload {
|
||||
oldExpense: Expense;
|
||||
expenseDTO: IExpenseEditDTO;
|
||||
expenseDTO: EditExpenseDto;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface IExpenseCreatedPayload {
|
||||
expenseId: number;
|
||||
expense: Expense;
|
||||
expenseDTO: IExpenseCreateDTO;
|
||||
expenseDTO: CreateExpenseDto;
|
||||
trx?: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface IExpenseEventEditPayload {
|
||||
expenseId: number;
|
||||
expense: Expense;
|
||||
expenseDTO: IExpenseEditDTO;
|
||||
expenseDTO: EditExpenseDto;
|
||||
authorizedUser: SystemUser;
|
||||
oldExpense: Expense;
|
||||
trx: Knex.Transaction;
|
||||
|
||||
@@ -7,6 +7,7 @@ export const S3_CLIENT = 'S3_CLIENT';
|
||||
const services = [
|
||||
{
|
||||
provide: S3_CLIENT,
|
||||
inject: [ConfigService],
|
||||
useFactory: (configService: ConfigService) => {
|
||||
const config = configService.get('s3');
|
||||
|
||||
|
||||
@@ -29,6 +29,12 @@ class PaymentMethodDto {
|
||||
enable: boolean;
|
||||
}
|
||||
|
||||
|
||||
class AttachmentDto {
|
||||
@IsString()
|
||||
key: string;
|
||||
}
|
||||
|
||||
class CommandSaleInvoiceDto {
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
@@ -186,6 +192,16 @@ class CommandSaleInvoiceDto {
|
||||
example: 1,
|
||||
})
|
||||
fromEstimateId?: number;
|
||||
|
||||
@IsOptional()
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => AttachmentDto)
|
||||
@ApiProperty({
|
||||
description: 'The attachments of the sale receipt',
|
||||
example: [{ key: '123456' }],
|
||||
})
|
||||
attachments?: AttachmentDto[];
|
||||
}
|
||||
|
||||
export class CreateSaleInvoiceDto extends CommandSaleInvoiceDto {}
|
||||
|
||||
@@ -13,7 +13,9 @@ import { ISearchRole } from '@/modules/DynamicListing/DynamicFilter/DynamicFilte
|
||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||
import { PaymentIntegrationTransactionLink } from '../SaleInvoice.types';
|
||||
import { TransactionPaymentServiceEntry } from '@/modules/PaymentServices/models/TransactionPaymentServiceEntry.model';
|
||||
import { InjectAttachable } from '@/modules/Attachments/decorators/InjectAttachable.decorator';
|
||||
|
||||
@InjectAttachable()
|
||||
export class SaleInvoice extends TenantBaseModel{
|
||||
public taxAmountWithheld: number;
|
||||
public balance: number;
|
||||
|
||||
@@ -3,7 +3,6 @@ import { Knex } from 'knex';
|
||||
import {
|
||||
ISaleReceiptCreatedPayload,
|
||||
ISaleReceiptCreatingPayload,
|
||||
ISaleReceiptDTO,
|
||||
} from '../types/SaleReceipts.types';
|
||||
import { SaleReceiptDTOTransformer } from './SaleReceiptDTOTransformer.service';
|
||||
import { SaleReceiptValidators } from './SaleReceiptValidators.service';
|
||||
|
||||
@@ -1,39 +1,21 @@
|
||||
import { Knex } from 'knex';
|
||||
// import { IItemEntry } from './ItemEntry';
|
||||
// import { CommonMailOptions, CommonMailOptionsDTO } from '../SaleInvoices/types/Mailable';
|
||||
import { AttachmentLinkDTO } from '../../Attachments/Attachments.types';
|
||||
import { SaleReceipt } from '../models/SaleReceipt';
|
||||
import { CommonMailOptionsDTO } from '@/modules/MailNotification/MailNotification.types';
|
||||
import { CommonMailOptions } from '@/modules/MailNotification/MailNotification.types';
|
||||
import { TenantJobPayload } from '@/interfaces/Tenant';
|
||||
import { CreateSaleReceiptDto, EditSaleReceiptDto } from '../dtos/SaleReceipt.dto';
|
||||
|
||||
export interface ISalesReceiptsFilter {
|
||||
filterQuery?: (query: any) => void;
|
||||
}
|
||||
|
||||
export interface ISaleReceiptDTO {
|
||||
customerId: number;
|
||||
exchangeRate?: number;
|
||||
depositAccountId: number;
|
||||
receiptDate: Date;
|
||||
sendToEmail: string;
|
||||
referenceNo?: string;
|
||||
receiptNumber?: string;
|
||||
receiptMessage: string;
|
||||
statement: string;
|
||||
closed: boolean;
|
||||
entries: any[];
|
||||
branchId?: number;
|
||||
attachments?: AttachmentLinkDTO[];
|
||||
}
|
||||
|
||||
export interface ISaleReceiptSmsDetails {
|
||||
customerName: string;
|
||||
customerPhoneNumber: string;
|
||||
smsMessage: string;
|
||||
}
|
||||
export interface ISaleReceiptCreatingPayload {
|
||||
saleReceiptDTO: ISaleReceiptDTO;
|
||||
saleReceiptDTO: CreateSaleReceiptDto;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
@@ -41,21 +23,20 @@ export interface ISaleReceiptCreatedPayload {
|
||||
// tenantId: number;
|
||||
saleReceipt: SaleReceipt;
|
||||
saleReceiptId: number;
|
||||
saleReceiptDTO: ISaleReceiptDTO;
|
||||
saleReceiptDTO: CreateSaleReceiptDto;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface ISaleReceiptEditedPayload {
|
||||
oldSaleReceipt: SaleReceipt;
|
||||
saleReceipt: SaleReceipt;
|
||||
// saleReceiptId: number;
|
||||
saleReceiptDTO: ISaleReceiptDTO;
|
||||
saleReceiptDTO: EditSaleReceiptDto;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface ISaleReceiptEditingPayload {
|
||||
oldSaleReceipt: SaleReceipt;
|
||||
saleReceiptDTO: ISaleReceiptDTO;
|
||||
saleReceiptDTO: EditSaleReceiptDto;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
export interface ISaleReceiptEventClosedPayload {
|
||||
|
||||
@@ -15,7 +15,7 @@ export class AttachmentUploadPipeline {
|
||||
* @param res The HTTP response object.
|
||||
* @param next The callback to pass control to the next middleware function.
|
||||
*/
|
||||
public validateS3Configured(req: Request, res: Response, next: NextFunction) {
|
||||
validateS3Configured(req: Request, res: Response, next: NextFunction) {
|
||||
if (
|
||||
!config.s3.region ||
|
||||
!config.s3.accessKeyId ||
|
||||
@@ -42,6 +42,7 @@ export class AttachmentUploadPipeline {
|
||||
s3,
|
||||
bucket: config.s3.bucket,
|
||||
contentType: multerS3.AUTO_CONTENT_TYPE,
|
||||
|
||||
metadata: function (req, file, cb) {
|
||||
cb(null, { fieldName: file.fieldname });
|
||||
},
|
||||
|
||||
15
pnpm-lock.yaml
generated
15
pnpm-lock.yaml
generated
@@ -547,6 +547,9 @@ importers:
|
||||
'@supercharge/promise-pool':
|
||||
specifier: ^3.2.0
|
||||
version: 3.2.0
|
||||
'@types/multer':
|
||||
specifier: ^1.4.11
|
||||
version: 1.4.11
|
||||
'@types/nodemailer':
|
||||
specifier: ^6.4.17
|
||||
version: 6.4.17
|
||||
@@ -631,6 +634,9 @@ importers:
|
||||
mathjs:
|
||||
specifier: ^9.4.0
|
||||
version: 9.5.2
|
||||
mime-types:
|
||||
specifier: ^2.1.35
|
||||
version: 2.1.35
|
||||
moment:
|
||||
specifier: ^2.30.1
|
||||
version: 2.30.1
|
||||
@@ -640,6 +646,12 @@ importers:
|
||||
moment-timezone:
|
||||
specifier: ^0.5.43
|
||||
version: 0.5.45
|
||||
multer:
|
||||
specifier: 1.4.5-lts.1
|
||||
version: 1.4.5-lts.1
|
||||
multer-s3:
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1(@aws-sdk/client-s3@3.583.0)
|
||||
mysql:
|
||||
specifier: ^2.18.1
|
||||
version: 2.18.1
|
||||
@@ -12460,7 +12472,6 @@ packages:
|
||||
resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==}
|
||||
dependencies:
|
||||
'@types/express': 4.17.21
|
||||
dev: true
|
||||
|
||||
/@types/node-fetch@2.6.11:
|
||||
resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
|
||||
@@ -12653,7 +12664,7 @@ packages:
|
||||
/@types/serve-index@1.9.4:
|
||||
resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==}
|
||||
dependencies:
|
||||
'@types/express': 4.17.21
|
||||
'@types/express': 5.0.0
|
||||
dev: false
|
||||
|
||||
/@types/serve-static@1.15.7:
|
||||
|
||||
Reference in New Issue
Block a user