mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-18 22:00:31 +00:00
refactor(nestjs): attachments module
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
"@casl/ability": "^5.4.3",
|
"@casl/ability": "^5.4.3",
|
||||||
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
||||||
"@liaoliaots/nestjs-redis": "^10.0.0",
|
"@liaoliaots/nestjs-redis": "^10.0.0",
|
||||||
|
"@types/multer": "^1.4.11",
|
||||||
"@nestjs/bull": "^10.2.1",
|
"@nestjs/bull": "^10.2.1",
|
||||||
"@nestjs/bullmq": "^10.2.2",
|
"@nestjs/bullmq": "^10.2.2",
|
||||||
"@nestjs/cache-manager": "^2.2.2",
|
"@nestjs/cache-manager": "^2.2.2",
|
||||||
@@ -69,11 +70,14 @@
|
|||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lru-cache": "^6.0.0",
|
"lru-cache": "^6.0.0",
|
||||||
"mathjs": "^9.4.0",
|
"mathjs": "^9.4.0",
|
||||||
|
"mime-types": "^2.1.35",
|
||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"moment-range": "^4.0.2",
|
"moment-range": "^4.0.2",
|
||||||
"moment-timezone": "^0.5.43",
|
"moment-timezone": "^0.5.43",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"mysql2": "^3.11.3",
|
"mysql2": "^3.11.3",
|
||||||
|
"multer": "1.4.5-lts.1",
|
||||||
|
"multer-s3": "^3.0.1",
|
||||||
"nestjs-cls": "^5.2.0",
|
"nestjs-cls": "^5.2.0",
|
||||||
"nestjs-i18n": "^10.4.9",
|
"nestjs-i18n": "^10.4.9",
|
||||||
"nestjs-redis": "^1.3.3",
|
"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 { 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 { DeleteAttachment } from "./DeleteAttachment";
|
||||||
import { GetAttachment } from "./GetAttachment";
|
import { GetAttachment } from "./GetAttachment";
|
||||||
import { getAttachmentPresignedUrl } from "./GetAttachmentPresignedUrl";
|
import { getAttachmentPresignedUrl } from "./GetAttachmentPresignedUrl";
|
||||||
@@ -13,11 +14,26 @@ import { AttachmentsOnExpenses } from "./events/AttachmentsOnExpenses";
|
|||||||
import { AttachmentsOnPaymentsReceived } from "./events/AttachmentsOnPaymentsReceived";
|
import { AttachmentsOnPaymentsReceived } from "./events/AttachmentsOnPaymentsReceived";
|
||||||
import { AttachmentsOnManualJournals } from "./events/AttachmentsOnManualJournals";
|
import { AttachmentsOnManualJournals } from "./events/AttachmentsOnManualJournals";
|
||||||
import { AttachmentsOnVendorCredits } from "./events/AttachmentsOnVendorCredits";
|
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({
|
@Module({
|
||||||
imports: [S3Module],
|
imports: [S3Module, ...models],
|
||||||
|
controllers: [AttachmentsController],
|
||||||
providers: [
|
providers: [
|
||||||
DeleteAttachment,
|
DeleteAttachment,
|
||||||
GetAttachment,
|
GetAttachment,
|
||||||
@@ -31,7 +47,34 @@ import { AttachmentsOnVendorCredits } from "./events/AttachmentsOnVendorCredits"
|
|||||||
AttachmentsOnExpenses,
|
AttachmentsOnExpenses,
|
||||||
AttachmentsOnPaymentsReceived,
|
AttachmentsOnPaymentsReceived,
|
||||||
AttachmentsOnManualJournals,
|
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 {}
|
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.
|
* Saves the metadata of uploaded document to S3 on database.
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {} file
|
* @param {} file
|
||||||
* @returns {Promise<Document>}
|
* @returns {Promise<Document>}
|
||||||
*/
|
*/
|
||||||
@@ -38,7 +37,6 @@ export class AttachmentsApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the document data.
|
* Retrieves the document data.
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {string} documentKey
|
* @param {string} documentKey
|
||||||
*/
|
*/
|
||||||
public get(documentKey: string) {
|
public get(documentKey: string) {
|
||||||
@@ -58,7 +56,6 @@ export class AttachmentsApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Unlinks the given document from resource model.
|
* Unlinks the given document from resource model.
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {string} filekey
|
* @param {string} filekey
|
||||||
* @param {string} modelRef
|
* @param {string} modelRef
|
||||||
* @param {number} modelId
|
* @param {number} modelId
|
||||||
@@ -70,7 +67,6 @@ export class AttachmentsApplication {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the presigned url of the given attachment key.
|
* Retrieves the presigned url of the given attachment key.
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
import { S3_CLIENT } from '../S3/S3.module';
|
import { S3_CLIENT } from '../S3/S3.module';
|
||||||
import { DocumentModel } from './models/Document.model';
|
import { DocumentModel } from './models/Document.model';
|
||||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||||
@@ -17,14 +17,14 @@ export class DeleteAttachment {
|
|||||||
@Inject(S3_CLIENT)
|
@Inject(S3_CLIENT)
|
||||||
private readonly s3Client: S3Client,
|
private readonly s3Client: S3Client,
|
||||||
|
|
||||||
@Inject(Document.name)
|
@Inject(DocumentModel.name)
|
||||||
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
||||||
|
|
||||||
@Inject(DocumentLinkModel.name)
|
@Inject(DocumentLinkModel.name)
|
||||||
private readonly documentLinkModel: TenantModelProxy<typeof DocumentLinkModel>
|
private readonly documentLinkModel: TenantModelProxy<
|
||||||
) {
|
typeof DocumentLinkModel
|
||||||
|
>,
|
||||||
}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes the give file attachment file key.
|
* Deletes the give file attachment file key.
|
||||||
@@ -37,13 +37,15 @@ export class DeleteAttachment {
|
|||||||
};
|
};
|
||||||
await this.s3Client.send(new DeleteObjectCommand(params));
|
await this.s3Client.send(new DeleteObjectCommand(params));
|
||||||
|
|
||||||
const foundDocument = await this.documentModel().query()
|
const foundDocument = await this.documentModel()
|
||||||
|
.query()
|
||||||
.findOne('key', filekey)
|
.findOne('key', filekey)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
await this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
await this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
// Delete all document links
|
// Delete all document links
|
||||||
await this.documentLinkModel().query(trx)
|
await this.documentLinkModel()
|
||||||
|
.query(trx)
|
||||||
.where('documentId', foundDocument.id)
|
.where('documentId', foundDocument.id)
|
||||||
.delete();
|
.delete();
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,11 @@ export class GetAttachment {
|
|||||||
private readonly configService: ConfigService,
|
private readonly configService: ConfigService,
|
||||||
|
|
||||||
@Inject(S3_CLIENT)
|
@Inject(S3_CLIENT)
|
||||||
private readonly s3: S3Client
|
private readonly s3: S3Client,
|
||||||
) {
|
) {}
|
||||||
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Retrieves data of the given document key.
|
* Retrieves data of the given document key.
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {string} filekey
|
* @param {string} filekey
|
||||||
*/
|
*/
|
||||||
async getAttachment(filekey: string) {
|
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 { 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 { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||||
import { DocumentModel } from './models/Document.model';
|
import { DocumentModel } from './models/Document.model';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { S3_CLIENT } from '../S3/S3.module';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class getAttachmentPresignedUrl {
|
export class getAttachmentPresignedUrl {
|
||||||
constructor(
|
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.
|
* Retrieves the presigned url of the given attachment key with the original filename.
|
||||||
* @param {number} tenantId
|
* @param {string} key -
|
||||||
* @param {string} key
|
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
async getPresignedUrl(tenantId: number, key: string) {
|
async getPresignedUrl(key: string) {
|
||||||
const foundDocument = await this.documentModel().query().findOne({ key });
|
const foundDocument = await this.documentModel().query().findOne({ key });
|
||||||
|
const config = this.configService.get('s3');
|
||||||
|
|
||||||
let ResponseContentDisposition = 'attachment';
|
let ResponseContentDisposition = 'attachment';
|
||||||
if (foundDocument && foundDocument.originName) {
|
if (foundDocument && foundDocument.originName) {
|
||||||
ResponseContentDisposition += `; filename="${foundDocument.originName}"`;
|
ResponseContentDisposition += `; filename="${foundDocument.originName}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const command = new GetObjectCommand({
|
const command = new GetObjectCommand({
|
||||||
Bucket: config.s3.bucket,
|
Bucket: config.bucket,
|
||||||
Key: key,
|
Key: key,
|
||||||
ResponseContentDisposition,
|
ResponseContentDisposition,
|
||||||
});
|
});
|
||||||
const signedUrl = await getSignedUrl(s3, command, { expiresIn: 300 });
|
const signedUrl = await getSignedUrl(this.s3Client, command, { expiresIn: 300 });
|
||||||
|
|
||||||
return signedUrl;
|
return signedUrl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,59 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { ModuleRef } from '@nestjs/core';
|
||||||
import bluebird from 'bluebird';
|
import bluebird from 'bluebird';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import {
|
import {
|
||||||
validateLinkModelEntryExists,
|
validateLinkModelEntryExists,
|
||||||
validateLinkModelExists,
|
validateLinkModelExists,
|
||||||
} from './Attachments/_utils';
|
} from './_utils';
|
||||||
import HasTenancyService from '../Tenancy/TenancyService';
|
|
||||||
import { ServiceError } from '@/exceptions';
|
|
||||||
import { ERRORS } from './constants';
|
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 {
|
export class LinkAttachment {
|
||||||
@Inject()
|
constructor(
|
||||||
private tenancy: HasTenancyService;
|
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.
|
* Links the given file key to the given model type and id.
|
||||||
* @param {number} tenantId
|
* @param {string} filekey - File key.
|
||||||
* @param {string} filekey
|
* @param {string} modelRef - Model reference.
|
||||||
* @param {string} modelRef
|
* @param {number} modelId - Model id.
|
||||||
* @param {number} modelId
|
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async link(
|
async link(
|
||||||
tenantId: number,
|
|
||||||
filekey: string,
|
filekey: string,
|
||||||
modelRef: string,
|
modelRef: string,
|
||||||
modelId: number,
|
modelId: number,
|
||||||
trx?: Knex.Transaction
|
trx?: Knex.Transaction,
|
||||||
) {
|
) {
|
||||||
const { DocumentLink, Document, ...models } = this.tenancy.models(tenantId);
|
const attachmentsAttachableModels = getAttachableModelsMap();
|
||||||
const LinkModel = models[modelRef];
|
const attachableModel = attachmentsAttachableModels.get(modelRef);
|
||||||
validateLinkModelExists(LinkModel);
|
|
||||||
|
|
||||||
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)
|
.findOne('key', filekey)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
const foundLinkModel = await LinkModel.query(trx).findById(modelId);
|
const foundLinkModel = await LinkModel().query(trx).findById(modelId);
|
||||||
validateLinkModelEntryExists(foundLinkModel);
|
validateLinkModelEntryExists(foundLinkModel);
|
||||||
|
|
||||||
const foundLinks = await DocumentLink.query(trx)
|
const foundLinks = await this.documentLinkModel().query(trx)
|
||||||
.where('modelRef', modelRef)
|
.where('modelRef', modelRef)
|
||||||
.where('modelId', modelId)
|
.where('modelId', modelId)
|
||||||
.where('documentId', foundFile.id);
|
.where('documentId', foundFile.id);
|
||||||
@@ -48,7 +61,7 @@ export class LinkAttachment {
|
|||||||
if (foundLinks.length > 0) {
|
if (foundLinks.length > 0) {
|
||||||
throw new ServiceError(ERRORS.DOCUMENT_LINK_ALREADY_LINKED);
|
throw new ServiceError(ERRORS.DOCUMENT_LINK_ALREADY_LINKED);
|
||||||
}
|
}
|
||||||
await DocumentLink.query(trx).insert({
|
await this.documentLinkModel().query(trx).insert({
|
||||||
modelRef,
|
modelRef,
|
||||||
modelId,
|
modelId,
|
||||||
documentId: foundFile.id,
|
documentId: foundFile.id,
|
||||||
@@ -57,23 +70,21 @@ export class LinkAttachment {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Links the given file keys to the given model type and id.
|
* Links the given file keys to the given model type and id.
|
||||||
* @param {number} tenantId
|
* @param {string[]} filekeys - File keys.
|
||||||
* @param {string[]} filekeys
|
* @param {string} modelRef - Model reference.
|
||||||
* @param {string} modelRef
|
* @param {number} modelId - Model id.
|
||||||
* @param {number} modelId
|
* @param {Knex.Transaction} trx - Knex transaction.
|
||||||
* @param {Knex.Transaction} trx
|
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async bulkLink(
|
async bulkLink(
|
||||||
tenantId: number,
|
|
||||||
filekeys: string[],
|
filekeys: string[],
|
||||||
modelRef: string,
|
modelRef: string,
|
||||||
modelId: number,
|
modelId: number,
|
||||||
trx?: Knex.Transaction
|
trx?: Knex.Transaction,
|
||||||
) {
|
) {
|
||||||
return bluebird.each(filekeys, async (fieldKey: string) => {
|
return bluebird.each(filekeys, async (fieldKey: string) => {
|
||||||
try {
|
try {
|
||||||
await this.link(tenantId, fieldKey, modelRef, modelId, trx);
|
await this.link(fieldKey, modelRef, modelId, trx);
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore catching exceptions in bulk action.
|
// Ignore catching exceptions in bulk action.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { NextFunction, Request, Response } from 'express';
|
import { NextFunction, Request, Response } from 'express';
|
||||||
import multer from 'multer';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import type { Multer } from 'multer';
|
import { Injectable } from '@nestjs/common';
|
||||||
import multerS3 from 'multer-s3';
|
|
||||||
import { s3 } from '@/lib/S3/S3';
|
|
||||||
import { Service } from 'typedi';
|
|
||||||
import config from '@/config';
|
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class AttachmentUploadPipeline {
|
export class AttachmentUploadPipeline {
|
||||||
|
constructor(
|
||||||
|
private readonly configService: ConfigService
|
||||||
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Middleware to ensure that S3 configuration is properly set before proceeding.
|
* 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.
|
* 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.
|
* @param next The callback to pass control to the next middleware function.
|
||||||
*/
|
*/
|
||||||
public validateS3Configured(req: Request, res: Response, next: NextFunction) {
|
public validateS3Configured(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const config = this.configService.get('s3');
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!config.s3.region ||
|
!config.region ||
|
||||||
!config.s3.accessKeyId ||
|
!config.accessKeyId ||
|
||||||
!config.s3.secretAccessKey
|
!config.secretAccessKey
|
||||||
) {
|
) {
|
||||||
const missingKeys = [];
|
const missingKeys = [];
|
||||||
if (!config.s3.region) missingKeys.push('region');
|
if (!config.region) missingKeys.push('region');
|
||||||
if (!config.s3.accessKeyId) missingKeys.push('accessKeyId');
|
if (!config.accessKeyId) missingKeys.push('accessKeyId');
|
||||||
if (!config.s3.secretAccessKey) missingKeys.push('secretAccessKey');
|
if (!config.secretAccessKey) missingKeys.push('secretAccessKey');
|
||||||
const missing = missingKeys.join(', ');
|
const missing = missingKeys.join(', ');
|
||||||
|
|
||||||
throw new Error(`S3 configuration error: Missing ${missing}`);
|
throw new Error(`S3 configuration error: Missing ${missing}`);
|
||||||
}
|
}
|
||||||
next();
|
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 bluebird from 'bluebird';
|
||||||
|
import { difference } from 'lodash';
|
||||||
import {
|
import {
|
||||||
validateLinkModelEntryExists,
|
validateLinkModelEntryExists,
|
||||||
validateLinkModelExists,
|
validateLinkModelExists,
|
||||||
} from './_utils';
|
} from './_utils';
|
||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { difference } from 'lodash';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { 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()
|
@Injectable()
|
||||||
export class UnlinkAttachment {
|
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.
|
* Unlink the attachments from the model entry.
|
||||||
* @param {number} tenantId
|
* @param {string} filekey - File key.
|
||||||
* @param {string} filekey
|
* @param {string} modelRef - Model reference.
|
||||||
* @param {string} modelRef
|
* @param {number} modelId - Model id.
|
||||||
* @param {number} modelId
|
|
||||||
*/
|
*/
|
||||||
async unlink(
|
async unlink(
|
||||||
tenantId: number,
|
|
||||||
filekey: string,
|
filekey: string,
|
||||||
modelRef: string,
|
modelRef: string,
|
||||||
modelId: number,
|
modelId: number,
|
||||||
trx?: Knex.Transaction
|
trx?: Knex.Transaction,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { DocumentLink, Document, ...models } = this.tenancy.models(tenantId);
|
const attachmentsAttachableModels = getAttachableModelsMap();
|
||||||
|
const attachableModel = attachmentsAttachableModels.get(modelRef);
|
||||||
const LinkModel = models[modelRef];
|
|
||||||
validateLinkModelExists(LinkModel);
|
|
||||||
|
|
||||||
|
validateLinkModelExists(attachableModel);
|
||||||
|
|
||||||
|
const LinkModel = this.moduleRef.get(modelRef, { strict: false });
|
||||||
const foundLinkModel = await LinkModel.query(trx).findById(modelId);
|
const foundLinkModel = await LinkModel.query(trx).findById(modelId);
|
||||||
validateLinkModelEntryExists(foundLinkModel);
|
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.
|
// Delete the document link.
|
||||||
await DocumentLink.query(trx)
|
await this.documentLinkModel().query(trx)
|
||||||
.where('modelRef', modelRef)
|
.where('modelRef', modelRef)
|
||||||
.where('modelId', modelId)
|
.where('modelId', modelId)
|
||||||
.where('documentId', document.id)
|
.where('documentId', document.id)
|
||||||
@@ -43,22 +59,20 @@ export class UnlinkAttachment {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Bulk unlink the attachments from the model entry.
|
* Bulk unlink the attachments from the model entry.
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {string} fieldkey
|
* @param {string} fieldkey
|
||||||
* @param {string} modelRef
|
* @param {string} modelRef
|
||||||
* @param {number} modelId
|
* @param {number} modelId
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async bulkUnlink(
|
async bulkUnlink(
|
||||||
tenantId: number,
|
|
||||||
filekeys: string[],
|
filekeys: string[],
|
||||||
modelRef: string,
|
modelRef: string,
|
||||||
modelId: number,
|
modelId: number,
|
||||||
trx?: Knex.Transaction
|
trx?: Knex.Transaction,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await bluebird.each(filekeys, (fieldKey: string) => {
|
await bluebird.each(filekeys, (fieldKey: string) => {
|
||||||
try {
|
try {
|
||||||
this.unlink(tenantId, fieldKey, modelRef, modelId, trx);
|
this.unlink(fieldKey, modelRef, modelId, trx);
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore catching exceptions on bulk action.
|
// Ignore catching exceptions on bulk action.
|
||||||
}
|
}
|
||||||
@@ -74,15 +88,13 @@ export class UnlinkAttachment {
|
|||||||
* @param {Knex.Transaction} trx
|
* @param {Knex.Transaction} trx
|
||||||
*/
|
*/
|
||||||
async unlinkUnpresentedKeys(
|
async unlinkUnpresentedKeys(
|
||||||
tenantId: number,
|
|
||||||
presentedKeys: string[],
|
presentedKeys: string[],
|
||||||
modelRef: string,
|
modelRef: string,
|
||||||
modelId: number,
|
modelId: number,
|
||||||
trx?: Knex.Transaction
|
trx?: Knex.Transaction,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { DocumentLink } = this.tenancy.models(tenantId);
|
const modelLinks = await this.documentLinkModel()
|
||||||
|
.query(trx)
|
||||||
const modelLinks = await DocumentLink.query(trx)
|
|
||||||
.where('modelRef', modelRef)
|
.where('modelRef', modelRef)
|
||||||
.where('modelId', modelId)
|
.where('modelId', modelId)
|
||||||
.withGraphFetched('document');
|
.withGraphFetched('document');
|
||||||
@@ -90,33 +102,30 @@ export class UnlinkAttachment {
|
|||||||
const modelLinkKeys = modelLinks.map((link) => link.document.key);
|
const modelLinkKeys = modelLinks.map((link) => link.document.key);
|
||||||
const unpresentedKeys = difference(modelLinkKeys, presentedKeys);
|
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.
|
* Unlink all attachments of the given model type and id.
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {string} modelRef
|
* @param {string} modelRef
|
||||||
* @param {number} modelId
|
* @param {number} modelId
|
||||||
* @param {Knex.Transaction} trx
|
* @param {Knex.Transaction} trx
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async unlinkAllModelKeys(
|
async unlinkAllModelKeys(
|
||||||
tenantId: number,
|
|
||||||
modelRef: string,
|
modelRef: string,
|
||||||
modelId: number,
|
modelId: number,
|
||||||
trx?: Knex.Transaction
|
trx?: Knex.Transaction,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const { DocumentLink } = this.tenancy.models(tenantId);
|
|
||||||
|
|
||||||
// Get all the keys of the modelRef and modelId.
|
// 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('modelRef', modelRef)
|
||||||
.where('modelId', modelId)
|
.where('modelId', modelId)
|
||||||
.withGraphFetched('document');
|
.withGraphFetched('document');
|
||||||
|
|
||||||
const modelLinkKeys = modelLinks.map((link) => link.document.key);
|
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 { castArray, difference } from 'lodash';
|
||||||
import HasTenancyService from '../Tenancy/TenancyService';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { ServiceError } from '@/exceptions';
|
|
||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||||
import { DocumentModel } from './models/Document.model';
|
import { DocumentModel } from './models/Document.model';
|
||||||
|
import { ServiceError } from '../Items/ServiceError';
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class ValidateAttachments {
|
export class ValidateAttachments {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly documentModel: TenantModelProxy<typeof DocumentModel>
|
@Inject(DocumentModel.name)
|
||||||
) {
|
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
||||||
|
) {}
|
||||||
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Validates the given file keys existance.
|
* Validates the given file keys existance.
|
||||||
* @param {number} tenantId
|
|
||||||
* @param {string|string[]} key
|
* @param {string|string[]} key
|
||||||
*/
|
*/
|
||||||
async validate(tenantId: number, key: string | string[]) {
|
async validate(key: string | string[]) {
|
||||||
const keys = castArray(key);
|
const keys = castArray(key);
|
||||||
const documents = await this.documentModel().query().whereIn('key', key);
|
const documents = await this.documentModel().query().whereIn('key', key);
|
||||||
const documentKeys = documents.map((document) => document.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';
|
import { ERRORS } from './constants';
|
||||||
|
|
||||||
export const validateLinkModelExists = (LinkModel) => {
|
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";
|
import { IsNotEmpty, IsString } from "class-validator";
|
||||||
|
|
||||||
|
|
||||||
@@ -6,3 +7,27 @@ export class AttachmentLinkDto {
|
|||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
key: string;
|
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 { isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
IBIllEventDeletedPayload,
|
IBIllEventDeletedPayload,
|
||||||
IBillCreatedPayload,
|
IBillCreatedPayload,
|
||||||
IBillCreatingPayload,
|
IBillCreatingPayload,
|
||||||
IBillEditedPayload,
|
IBillEditedPayload,
|
||||||
} from '@/interfaces';
|
} from '@/modules/Bills/Bills.types';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { LinkAttachment } from '../LinkAttachment';
|
|
||||||
import { ValidateAttachments } from '../ValidateAttachments';
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class AttachmentsOnBills {
|
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) {
|
constructor(
|
||||||
bus.subscribe(
|
private readonly linkAttachmentService: LinkAttachment,
|
||||||
events.bill.onCreating,
|
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||||
this.validateAttachmentsOnBillCreate.bind(this)
|
private readonly validateDocuments: ValidateAttachments,
|
||||||
);
|
) {}
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the attachment keys on creating bill.
|
* Validates the attachment keys on creating bill.
|
||||||
* @param {ISaleInvoiceCreatingPaylaod}
|
* @param {ISaleInvoiceCreatingPaylaod}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async validateAttachmentsOnBillCreate({
|
@OnEvent(events.bill.onCreating)
|
||||||
|
async validateAttachmentsOnBillCreate({
|
||||||
billDTO,
|
billDTO,
|
||||||
tenantId,
|
|
||||||
}: IBillCreatingPayload): Promise<void> {
|
}: IBillCreatingPayload): Promise<void> {
|
||||||
if (isEmpty(billDTO.attachments)) {
|
if (isEmpty(billDTO.attachments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const documentKeys = billDTO?.attachments?.map((a) => a.key);
|
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}
|
* @param {ISaleInvoiceCreatedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleAttachmentsOnBillCreated({
|
@OnEvent(events.bill.onCreated)
|
||||||
tenantId,
|
async handleAttachmentsOnBillCreated({
|
||||||
bill,
|
bill,
|
||||||
billDTO,
|
billDTO,
|
||||||
trx,
|
trx,
|
||||||
@@ -79,32 +56,27 @@ export class AttachmentsOnBills {
|
|||||||
if (isEmpty(billDTO.attachments)) return;
|
if (isEmpty(billDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
||||||
await this.linkAttachmentService.bulkLink(
|
|
||||||
tenantId,
|
await this.linkAttachmentService.bulkLink(keys, 'Bill', bill.id, trx);
|
||||||
keys,
|
|
||||||
'Bill',
|
|
||||||
bill.id,
|
|
||||||
trx
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles unlinking all the unpresented keys of the edited bill.
|
* Handles unlinking all the unpresented keys of the edited bill.
|
||||||
* @param {IBillEditedPayload}
|
* @param {IBillEditedPayload}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkUnpresentedKeysOnBillEdited({
|
@OnEvent(events.bill.onEdited)
|
||||||
tenantId,
|
async handleUnlinkUnpresentedKeysOnBillEdited({
|
||||||
billDTO,
|
billDTO,
|
||||||
bill,
|
bill,
|
||||||
trx
|
trx,
|
||||||
}: IBillEditedPayload) {
|
}: IBillEditedPayload) {
|
||||||
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
|
||||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'Bill',
|
'Bill',
|
||||||
bill.id,
|
bill.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,8 +85,8 @@ export class AttachmentsOnBills {
|
|||||||
* @param {ISaleInvoiceEditedPayload}
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleLinkPresentedKeysOnBillEdited({
|
@OnEvent(events.bill.onEdited)
|
||||||
tenantId,
|
async handleLinkPresentedKeysOnBillEdited({
|
||||||
billDTO,
|
billDTO,
|
||||||
oldBill,
|
oldBill,
|
||||||
trx,
|
trx,
|
||||||
@@ -122,13 +94,8 @@ export class AttachmentsOnBills {
|
|||||||
if (isEmpty(billDTO.attachments)) return;
|
if (isEmpty(billDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
||||||
await this.linkAttachmentService.bulkLink(
|
|
||||||
tenantId,
|
await this.linkAttachmentService.bulkLink(keys, 'Bill', oldBill.id, trx);
|
||||||
keys,
|
|
||||||
'Bill',
|
|
||||||
oldBill.id,
|
|
||||||
trx
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,16 +103,15 @@ export class AttachmentsOnBills {
|
|||||||
* @param {ISaleInvoiceDeletedPayload}
|
* @param {ISaleInvoiceDeletedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkAttachmentsOnBillDeleted({
|
@OnEvent(events.bill.onDeleting)
|
||||||
tenantId,
|
async handleUnlinkAttachmentsOnBillDeleted({
|
||||||
oldBill,
|
oldBill,
|
||||||
trx,
|
trx,
|
||||||
}: IBIllEventDeletedPayload) {
|
}: IBIllEventDeletedPayload) {
|
||||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
tenantId,
|
|
||||||
'Bill',
|
'Bill',
|
||||||
oldBill.id,
|
oldBill.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,45 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
ICreditNoteCreatedPayload,
|
ICreditNoteCreatedPayload,
|
||||||
ICreditNoteCreatingPayload,
|
ICreditNoteCreatingPayload,
|
||||||
ICreditNoteDeletingPayload,
|
ICreditNoteDeletingPayload,
|
||||||
ICreditNoteEditedPayload,
|
ICreditNoteEditedPayload,
|
||||||
} from '@/interfaces';
|
} from '@/modules/CreditNotes/types/CreditNotes.types';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { LinkAttachment } from '../LinkAttachment';
|
|
||||||
import { ValidateAttachments } from '../ValidateAttachments';
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class AttachmentsOnCreditNote {
|
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) {
|
constructor(
|
||||||
bus.subscribe(
|
private readonly linkAttachmentService: LinkAttachment,
|
||||||
events.creditNote.onCreating,
|
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||||
this.validateAttachmentsOnCreditNoteCreate.bind(this)
|
private readonly validateDocuments: ValidateAttachments,
|
||||||
);
|
) {}
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the attachment keys on creating credit note.
|
* Validates the attachment keys on creating credit note.
|
||||||
* @param {ICreditNoteCreatingPayload}
|
* @param {ICreditNoteCreatingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async validateAttachmentsOnCreditNoteCreate({
|
@OnEvent(events.creditNote.onCreating)
|
||||||
|
async validateAttachmentsOnCreditNoteCreate({
|
||||||
creditNoteDTO,
|
creditNoteDTO,
|
||||||
tenantId,
|
|
||||||
}: ICreditNoteCreatingPayload): Promise<void> {
|
}: ICreditNoteCreatingPayload): Promise<void> {
|
||||||
if (isEmpty(creditNoteDTO.attachments)) {
|
if (isEmpty(creditNoteDTO.attachments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const documentKeys = creditNoteDTO?.attachments?.map((a) => a.key);
|
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}
|
* @param {ICreditNoteCreatedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleAttachmentsOnCreditNoteCreated({
|
@OnEvent(events.creditNote.onCreated)
|
||||||
tenantId,
|
async handleAttachmentsOnCreditNoteCreated({
|
||||||
creditNote,
|
creditNote,
|
||||||
creditNoteDTO,
|
creditNoteDTO,
|
||||||
trx,
|
trx,
|
||||||
@@ -79,12 +56,12 @@ export class AttachmentsOnCreditNote {
|
|||||||
if (isEmpty(creditNoteDTO.attachments)) return;
|
if (isEmpty(creditNoteDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = creditNoteDTO.attachments?.map((attachment) => attachment.key);
|
const keys = creditNoteDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'CreditNote',
|
'CreditNote',
|
||||||
creditNote.id,
|
creditNote.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,21 +69,20 @@ export class AttachmentsOnCreditNote {
|
|||||||
* Handles unlinking all the unpresented keys of the edited credit note.
|
* Handles unlinking all the unpresented keys of the edited credit note.
|
||||||
* @param {ICreditNoteEditedPayload}
|
* @param {ICreditNoteEditedPayload}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkUnpresentedKeysOnCreditNoteEdited({
|
@OnEvent(events.creditNote.onEdited)
|
||||||
tenantId,
|
async handleUnlinkUnpresentedKeysOnCreditNoteEdited({
|
||||||
creditNoteEditDTO,
|
creditNoteEditDTO,
|
||||||
oldCreditNote,
|
oldCreditNote,
|
||||||
trx,
|
trx,
|
||||||
}: ICreditNoteEditedPayload) {
|
}: ICreditNoteEditedPayload) {
|
||||||
const keys = creditNoteEditDTO.attachments?.map(
|
const keys = creditNoteEditDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'CreditNote',
|
'CreditNote',
|
||||||
oldCreditNote.id,
|
oldCreditNote.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,8 +91,8 @@ export class AttachmentsOnCreditNote {
|
|||||||
* @param {ICreditNoteEditedPayload}
|
* @param {ICreditNoteEditedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleLinkPresentedKeysOnCreditNoteEdited({
|
@OnEvent(events.creditNote.onEdited)
|
||||||
tenantId,
|
async handleLinkPresentedKeysOnCreditNoteEdited({
|
||||||
creditNoteEditDTO,
|
creditNoteEditDTO,
|
||||||
oldCreditNote,
|
oldCreditNote,
|
||||||
trx,
|
trx,
|
||||||
@@ -124,14 +100,13 @@ export class AttachmentsOnCreditNote {
|
|||||||
if (isEmpty(creditNoteEditDTO.attachments)) return;
|
if (isEmpty(creditNoteEditDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = creditNoteEditDTO.attachments?.map(
|
const keys = creditNoteEditDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'CreditNote',
|
'CreditNote',
|
||||||
oldCreditNote.id,
|
oldCreditNote.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,16 +115,15 @@ export class AttachmentsOnCreditNote {
|
|||||||
* @param {ICreditNoteDeletingPayload}
|
* @param {ICreditNoteDeletingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkAttachmentsOnCreditNoteDeleted({
|
@OnEvent(events.creditNote.onDeleting)
|
||||||
tenantId,
|
async handleUnlinkAttachmentsOnCreditNoteDeleted({
|
||||||
oldCreditNote,
|
oldCreditNote,
|
||||||
trx,
|
trx,
|
||||||
}: ICreditNoteDeletingPayload) {
|
}: ICreditNoteDeletingPayload) {
|
||||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
tenantId,
|
|
||||||
'CreditNote',
|
'CreditNote',
|
||||||
oldCreditNote.id,
|
oldCreditNote.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,40 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
IExpenseCreatedPayload,
|
IExpenseCreatedPayload,
|
||||||
IExpenseCreatingPayload,
|
IExpenseCreatingPayload,
|
||||||
IExpenseDeletingPayload,
|
IExpenseDeletingPayload,
|
||||||
IExpenseEventEditPayload,
|
IExpenseEventEditPayload,
|
||||||
} from '@/interfaces';
|
} from '@/modules/Expenses/Expenses.types';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { LinkAttachment } from '../LinkAttachment';
|
|
||||||
import { ValidateAttachments } from '../ValidateAttachments';
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class AttachmentsOnExpenses {
|
export class AttachmentsOnExpenses {
|
||||||
@Inject()
|
constructor(
|
||||||
private linkAttachmentService: LinkAttachment;
|
private readonly linkAttachmentService: LinkAttachment,
|
||||||
|
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||||
@Inject()
|
private readonly validateDocuments: ValidateAttachments,
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the attachment keys on creating expense.
|
* Validates the attachment keys on creating expense.
|
||||||
* @param {ISaleInvoiceCreatingPaylaod}
|
* @param {ISaleInvoiceCreatingPaylaod}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async validateAttachmentsOnExpenseCreate({
|
@OnEvent(events.expenses.onCreating)
|
||||||
|
async validateAttachmentsOnExpenseCreate({
|
||||||
expenseDTO,
|
expenseDTO,
|
||||||
tenantId,
|
|
||||||
}: IExpenseCreatingPayload): Promise<void> {
|
}: IExpenseCreatingPayload): Promise<void> {
|
||||||
if (isEmpty(expenseDTO.attachments)) {
|
if (isEmpty(expenseDTO.attachments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const documentKeys = expenseDTO?.attachments?.map((a) => a.key);
|
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}
|
* @param {ISaleInvoiceCreatedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleAttachmentsOnExpenseCreated({
|
@OnEvent(events.expenses.onCreated)
|
||||||
tenantId,
|
async handleAttachmentsOnExpenseCreated({
|
||||||
expenseDTO,
|
expenseDTO,
|
||||||
expense,
|
expense,
|
||||||
trx,
|
trx,
|
||||||
@@ -79,12 +51,12 @@ export class AttachmentsOnExpenses {
|
|||||||
if (isEmpty(expenseDTO.attachments)) return;
|
if (isEmpty(expenseDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'Expense',
|
'Expense',
|
||||||
expense.id,
|
expense.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,19 +64,18 @@ export class AttachmentsOnExpenses {
|
|||||||
* Handles unlinking all the unpresented keys of the edited expense.
|
* Handles unlinking all the unpresented keys of the edited expense.
|
||||||
* @param {ISaleInvoiceEditedPayload}
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkUnpresentedKeysOnExpenseEdited({
|
@OnEvent(events.expenses.onEdited)
|
||||||
tenantId,
|
async handleUnlinkUnpresentedKeysOnExpenseEdited({
|
||||||
expenseDTO,
|
expenseDTO,
|
||||||
expense,
|
expense,
|
||||||
trx,
|
trx,
|
||||||
}: IExpenseEventEditPayload) {
|
}: IExpenseEventEditPayload) {
|
||||||
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
||||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'Expense',
|
'Expense',
|
||||||
expense.id,
|
expense.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,8 +84,8 @@ export class AttachmentsOnExpenses {
|
|||||||
* @param {ISaleInvoiceEditedPayload}
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleLinkPresentedKeysOnExpenseEdited({
|
@OnEvent(events.expenses.onEdited)
|
||||||
tenantId,
|
async handleLinkPresentedKeysOnExpenseEdited({
|
||||||
expenseDTO,
|
expenseDTO,
|
||||||
oldExpense,
|
oldExpense,
|
||||||
trx,
|
trx,
|
||||||
@@ -123,11 +94,10 @@ export class AttachmentsOnExpenses {
|
|||||||
|
|
||||||
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'Expense',
|
'Expense',
|
||||||
oldExpense.id,
|
oldExpense.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,16 +106,15 @@ export class AttachmentsOnExpenses {
|
|||||||
* @param {ISaleInvoiceDeletedPayload}
|
* @param {ISaleInvoiceDeletedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkAttachmentsOnExpenseDeleted({
|
@OnEvent(events.expenses.onDeleting)
|
||||||
tenantId,
|
async handleUnlinkAttachmentsOnExpenseDeleted({
|
||||||
oldExpense,
|
oldExpense,
|
||||||
trx,
|
trx,
|
||||||
}: IExpenseDeletingPayload) {
|
}: IExpenseDeletingPayload) {
|
||||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
tenantId,
|
|
||||||
'Expense',
|
'Expense',
|
||||||
oldExpense.id,
|
oldExpense.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,40 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
IManualJournalCreatingPayload,
|
IManualJournalCreatingPayload,
|
||||||
IManualJournalEventCreatedPayload,
|
IManualJournalEventCreatedPayload,
|
||||||
IManualJournalEventDeletedPayload,
|
IManualJournalEventDeletedPayload,
|
||||||
IManualJournalEventEditedPayload,
|
IManualJournalEventEditedPayload,
|
||||||
} from '@/interfaces';
|
} from '@/modules/ManualJournals/types/ManualJournals.types';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { LinkAttachment } from '../LinkAttachment';
|
|
||||||
import { ValidateAttachments } from '../ValidateAttachments';
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class AttachmentsOnManualJournals {
|
export class AttachmentsOnManualJournals {
|
||||||
@Inject()
|
constructor(
|
||||||
private linkAttachmentService: LinkAttachment;
|
private readonly linkAttachmentService: LinkAttachment,
|
||||||
|
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||||
@Inject()
|
private readonly validateDocuments: ValidateAttachments,
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the attachment keys on creating manual journal.
|
* Validates the attachment keys on creating manual journal.
|
||||||
* @param {IManualJournalCreatingPayload}
|
* @param {IManualJournalCreatingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async validateAttachmentsOnManualJournalCreate({
|
@OnEvent(events.manualJournals.onCreating)
|
||||||
|
async validateAttachmentsOnManualJournalCreate({
|
||||||
manualJournalDTO,
|
manualJournalDTO,
|
||||||
tenantId,
|
|
||||||
}: IManualJournalCreatingPayload): Promise<void> {
|
}: IManualJournalCreatingPayload): Promise<void> {
|
||||||
if (isEmpty(manualJournalDTO.attachments)) {
|
if (isEmpty(manualJournalDTO.attachments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const documentKeys = manualJournalDTO?.attachments?.map((a) => a.key);
|
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}
|
* @param {IManualJournalEventCreatedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleAttachmentsOnManualJournalCreated({
|
@OnEvent(events.manualJournals.onCreated)
|
||||||
tenantId,
|
async handleAttachmentsOnManualJournalCreated({
|
||||||
manualJournalDTO,
|
manualJournalDTO,
|
||||||
manualJournal,
|
manualJournal,
|
||||||
trx,
|
trx,
|
||||||
@@ -79,14 +51,13 @@ export class AttachmentsOnManualJournals {
|
|||||||
if (isEmpty(manualJournalDTO.attachments)) return;
|
if (isEmpty(manualJournalDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = manualJournalDTO.attachments?.map(
|
const keys = manualJournalDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'ManualJournal',
|
'ManualJournal',
|
||||||
manualJournal.id,
|
manualJournal.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,21 +65,20 @@ export class AttachmentsOnManualJournals {
|
|||||||
* Handles unlinking all the unpresented keys of the edited manual journal.
|
* Handles unlinking all the unpresented keys of the edited manual journal.
|
||||||
* @param {ISaleInvoiceEditedPayload}
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkUnpresentedKeysOnManualJournalEdited({
|
@OnEvent(events.manualJournals.onEdited)
|
||||||
tenantId,
|
async handleUnlinkUnpresentedKeysOnManualJournalEdited({
|
||||||
manualJournalDTO,
|
manualJournalDTO,
|
||||||
manualJournal,
|
manualJournal,
|
||||||
trx
|
trx,
|
||||||
}: IManualJournalEventEditedPayload) {
|
}: IManualJournalEventEditedPayload) {
|
||||||
const keys = manualJournalDTO.attachments?.map(
|
const keys = manualJournalDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'SaleInvoice',
|
'SaleInvoice',
|
||||||
manualJournal.id,
|
manualJournal.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,8 +87,8 @@ export class AttachmentsOnManualJournals {
|
|||||||
* @param {ISaleInvoiceEditedPayload}
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleLinkPresentedKeysOnManualJournalEdited({
|
@OnEvent(events.manualJournals.onEdited)
|
||||||
tenantId,
|
async handleLinkPresentedKeysOnManualJournalEdited({
|
||||||
manualJournalDTO,
|
manualJournalDTO,
|
||||||
oldManualJournal,
|
oldManualJournal,
|
||||||
trx,
|
trx,
|
||||||
@@ -126,14 +96,13 @@ export class AttachmentsOnManualJournals {
|
|||||||
if (isEmpty(manualJournalDTO.attachments)) return;
|
if (isEmpty(manualJournalDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = manualJournalDTO.attachments?.map(
|
const keys = manualJournalDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'ManualJournal',
|
'ManualJournal',
|
||||||
oldManualJournal.id,
|
oldManualJournal.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,16 +111,15 @@ export class AttachmentsOnManualJournals {
|
|||||||
* @param {ISaleInvoiceDeletedPayload}
|
* @param {ISaleInvoiceDeletedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkAttachmentsOnManualJournalDeleted({
|
@OnEvent(events.manualJournals.onDeleting)
|
||||||
tenantId,
|
async handleUnlinkAttachmentsOnManualJournalDeleted({
|
||||||
oldManualJournal,
|
oldManualJournal,
|
||||||
trx,
|
trx,
|
||||||
}: IManualJournalEventDeletedPayload) {
|
}: IManualJournalEventDeletedPayload) {
|
||||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
tenantId,
|
|
||||||
'SaleInvoice',
|
'SaleInvoice',
|
||||||
oldManualJournal.id,
|
oldManualJournal.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,40 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
IBillPaymentCreatingPayload,
|
IBillPaymentCreatingPayload,
|
||||||
IBillPaymentDeletingPayload,
|
IBillPaymentDeletingPayload,
|
||||||
IBillPaymentEventCreatedPayload,
|
IBillPaymentEventCreatedPayload,
|
||||||
IBillPaymentEventEditedPayload,
|
IBillPaymentEventEditedPayload,
|
||||||
} from '@/interfaces';
|
} from '@/modules/BillPayments/types/BillPayments.types';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { LinkAttachment } from '../LinkAttachment';
|
|
||||||
import { ValidateAttachments } from '../ValidateAttachments';
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class AttachmentsOnBillPayments {
|
export class AttachmentsOnBillPayments {
|
||||||
@Inject()
|
constructor(
|
||||||
private linkAttachmentService: LinkAttachment;
|
private readonly linkAttachmentService: LinkAttachment,
|
||||||
|
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||||
@Inject()
|
private readonly validateDocuments: ValidateAttachments,
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the attachment keys on creating bill payment.
|
* Validates the attachment keys on creating bill payment.
|
||||||
* @param {IBillPaymentCreatingPayload}
|
* @param {IBillPaymentCreatingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async validateAttachmentsOnBillPaymentCreate({
|
@OnEvent(events.billPayment.onCreating)
|
||||||
|
async validateAttachmentsOnBillPaymentCreate({
|
||||||
billPaymentDTO,
|
billPaymentDTO,
|
||||||
tenantId,
|
|
||||||
}: IBillPaymentCreatingPayload): Promise<void> {
|
}: IBillPaymentCreatingPayload): Promise<void> {
|
||||||
if (isEmpty(billPaymentDTO.attachments)) {
|
if (isEmpty(billPaymentDTO.attachments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const documentKeys = billPaymentDTO?.attachments?.map((a) => a.key);
|
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}
|
* @param {IBillPaymentEventCreatedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleAttachmentsOnBillPaymentCreated({
|
@OnEvent(events.billPayment.onCreated)
|
||||||
tenantId,
|
async handleAttachmentsOnBillPaymentCreated({
|
||||||
billPaymentDTO,
|
billPaymentDTO,
|
||||||
billPayment,
|
billPayment,
|
||||||
trx,
|
trx,
|
||||||
@@ -79,14 +51,13 @@ export class AttachmentsOnBillPayments {
|
|||||||
if (isEmpty(billPaymentDTO.attachments)) return;
|
if (isEmpty(billPaymentDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = billPaymentDTO.attachments?.map(
|
const keys = billPaymentDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'BillPayment',
|
'BillPayment',
|
||||||
billPayment.id,
|
billPayment.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,21 +65,20 @@ export class AttachmentsOnBillPayments {
|
|||||||
* Handles unlinking all the unpresented keys of the edited bill payment.
|
* Handles unlinking all the unpresented keys of the edited bill payment.
|
||||||
* @param {IBillPaymentEventEditedPayload}
|
* @param {IBillPaymentEventEditedPayload}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkUnpresentedKeysOnBillPaymentEdited({
|
@OnEvent(events.billPayment.onEdited)
|
||||||
tenantId,
|
async handleUnlinkUnpresentedKeysOnBillPaymentEdited({
|
||||||
billPaymentDTO,
|
billPaymentDTO,
|
||||||
oldBillPayment,
|
oldBillPayment,
|
||||||
trx,
|
trx,
|
||||||
}: IBillPaymentEventEditedPayload) {
|
}: IBillPaymentEventEditedPayload) {
|
||||||
const keys = billPaymentDTO.attachments?.map(
|
const keys = billPaymentDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'BillPayment',
|
'BillPayment',
|
||||||
oldBillPayment.id,
|
oldBillPayment.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,8 +87,8 @@ export class AttachmentsOnBillPayments {
|
|||||||
* @param {IBillPaymentEventEditedPayload}
|
* @param {IBillPaymentEventEditedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleLinkPresentedKeysOnBillPaymentEdited({
|
@OnEvent(events.billPayment.onEdited)
|
||||||
tenantId,
|
async handleLinkPresentedKeysOnBillPaymentEdited({
|
||||||
billPaymentDTO,
|
billPaymentDTO,
|
||||||
oldBillPayment,
|
oldBillPayment,
|
||||||
trx,
|
trx,
|
||||||
@@ -126,14 +96,13 @@ export class AttachmentsOnBillPayments {
|
|||||||
if (isEmpty(billPaymentDTO.attachments)) return;
|
if (isEmpty(billPaymentDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = billPaymentDTO.attachments?.map(
|
const keys = billPaymentDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'BillPayment',
|
'BillPayment',
|
||||||
oldBillPayment.id,
|
oldBillPayment.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,16 +111,15 @@ export class AttachmentsOnBillPayments {
|
|||||||
* @param {IBillPaymentDeletingPayload}
|
* @param {IBillPaymentDeletingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkAttachmentsOnBillPaymentDeleted({
|
@OnEvent(events.billPayment.onDeleting)
|
||||||
tenantId,
|
async handleUnlinkAttachmentsOnBillPaymentDeleted({
|
||||||
oldBillPayment,
|
oldBillPayment,
|
||||||
trx,
|
trx,
|
||||||
}: IBillPaymentDeletingPayload) {
|
}: IBillPaymentDeletingPayload) {
|
||||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
tenantId,
|
|
||||||
'BillPayment',
|
'BillPayment',
|
||||||
oldBillPayment.id,
|
oldBillPayment.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,40 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
IPaymentReceivedCreatedPayload,
|
IPaymentReceivedCreatedPayload,
|
||||||
IPaymentReceivedCreatingPayload,
|
IPaymentReceivedCreatingPayload,
|
||||||
IPaymentReceivedDeletingPayload,
|
IPaymentReceivedDeletingPayload,
|
||||||
IPaymentReceivedEditedPayload,
|
IPaymentReceivedEditedPayload,
|
||||||
} from '@/interfaces';
|
} from '@/modules/PaymentReceived/types/PaymentReceived.types';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { LinkAttachment } from '../LinkAttachment';
|
|
||||||
import { ValidateAttachments } from '../ValidateAttachments';
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class AttachmentsOnPaymentsReceived {
|
export class AttachmentsOnPaymentsReceived {
|
||||||
@Inject()
|
constructor(
|
||||||
private linkAttachmentService: LinkAttachment;
|
private readonly linkAttachmentService: LinkAttachment,
|
||||||
|
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||||
@Inject()
|
private readonly validateDocuments: ValidateAttachments,
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the attachment keys on creating payment.
|
* Validates the attachment keys on creating payment.
|
||||||
* @param {IPaymentReceivedCreatingPayload}
|
* @param {IPaymentReceivedCreatingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async validateAttachmentsOnPaymentCreate({
|
@OnEvent(events.paymentReceive.onCreating)
|
||||||
|
async validateAttachmentsOnPaymentCreate({
|
||||||
paymentReceiveDTO,
|
paymentReceiveDTO,
|
||||||
tenantId,
|
|
||||||
}: IPaymentReceivedCreatingPayload): Promise<void> {
|
}: IPaymentReceivedCreatingPayload): Promise<void> {
|
||||||
if (isEmpty(paymentReceiveDTO.attachments)) {
|
if (isEmpty(paymentReceiveDTO.attachments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const documentKeys = paymentReceiveDTO?.attachments?.map((a) => a.key);
|
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}
|
* @param {IPaymentReceivedCreatedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleAttachmentsOnPaymentCreated({
|
@OnEvent(events.paymentReceive.onCreated)
|
||||||
tenantId,
|
async handleAttachmentsOnPaymentCreated({
|
||||||
paymentReceiveDTO,
|
paymentReceiveDTO,
|
||||||
paymentReceive,
|
paymentReceive,
|
||||||
trx,
|
trx,
|
||||||
@@ -79,14 +51,13 @@ export class AttachmentsOnPaymentsReceived {
|
|||||||
if (isEmpty(paymentReceiveDTO.attachments)) return;
|
if (isEmpty(paymentReceiveDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = paymentReceiveDTO.attachments?.map(
|
const keys = paymentReceiveDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'PaymentReceive',
|
'PaymentReceive',
|
||||||
paymentReceive.id,
|
paymentReceive.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,21 +65,20 @@ export class AttachmentsOnPaymentsReceived {
|
|||||||
* Handles unlinking all the unpresented keys of the edited payment.
|
* Handles unlinking all the unpresented keys of the edited payment.
|
||||||
* @param {IPaymentReceivedEditedPayload}
|
* @param {IPaymentReceivedEditedPayload}
|
||||||
*/
|
*/
|
||||||
|
@OnEvent(events.paymentReceive.onEdited)
|
||||||
private async handleUnlinkUnpresentedKeysOnPaymentEdited({
|
private async handleUnlinkUnpresentedKeysOnPaymentEdited({
|
||||||
tenantId,
|
|
||||||
paymentReceiveDTO,
|
paymentReceiveDTO,
|
||||||
oldPaymentReceive,
|
oldPaymentReceive,
|
||||||
trx,
|
trx,
|
||||||
}: IPaymentReceivedEditedPayload) {
|
}: IPaymentReceivedEditedPayload) {
|
||||||
const keys = paymentReceiveDTO.attachments?.map(
|
const keys = paymentReceiveDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'PaymentReceive',
|
'PaymentReceive',
|
||||||
oldPaymentReceive.id,
|
oldPaymentReceive.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,8 +87,8 @@ export class AttachmentsOnPaymentsReceived {
|
|||||||
* @param {IPaymentReceivedEditedPayload}
|
* @param {IPaymentReceivedEditedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleLinkPresentedKeysOnPaymentEdited({
|
@OnEvent(events.paymentReceive.onEdited)
|
||||||
tenantId,
|
async handleLinkPresentedKeysOnPaymentEdited({
|
||||||
paymentReceiveDTO,
|
paymentReceiveDTO,
|
||||||
oldPaymentReceive,
|
oldPaymentReceive,
|
||||||
trx,
|
trx,
|
||||||
@@ -126,14 +96,13 @@ export class AttachmentsOnPaymentsReceived {
|
|||||||
if (isEmpty(paymentReceiveDTO.attachments)) return;
|
if (isEmpty(paymentReceiveDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = paymentReceiveDTO.attachments?.map(
|
const keys = paymentReceiveDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'PaymentReceive',
|
'PaymentReceive',
|
||||||
oldPaymentReceive.id,
|
oldPaymentReceive.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,16 +111,15 @@ export class AttachmentsOnPaymentsReceived {
|
|||||||
* @param {ISaleInvoiceDeletedPayload}
|
* @param {ISaleInvoiceDeletedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkAttachmentsOnPaymentDelete({
|
@OnEvent(events.paymentReceive.onDeleting)
|
||||||
tenantId,
|
async handleUnlinkAttachmentsOnPaymentDelete({
|
||||||
oldPaymentReceive,
|
oldPaymentReceive,
|
||||||
trx,
|
trx,
|
||||||
}: IPaymentReceivedDeletingPayload) {
|
}: IPaymentReceivedDeletingPayload) {
|
||||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
tenantId,
|
|
||||||
'PaymentReceive',
|
'PaymentReceive',
|
||||||
oldPaymentReceive.id,
|
oldPaymentReceive.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,40 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
ISaleEstimateCreatedPayload,
|
ISaleEstimateCreatedPayload,
|
||||||
ISaleEstimateCreatingPayload,
|
ISaleEstimateCreatingPayload,
|
||||||
ISaleEstimateDeletingPayload,
|
ISaleEstimateDeletingPayload,
|
||||||
ISaleEstimateEditedPayload,
|
ISaleEstimateEditedPayload,
|
||||||
} from '@/interfaces';
|
} from '@/modules/SaleEstimates/types/SaleEstimates.types';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { LinkAttachment } from '../LinkAttachment';
|
|
||||||
import { ValidateAttachments } from '../ValidateAttachments';
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
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 {
|
export class AttachmentsOnSaleEstimates {
|
||||||
@Inject()
|
constructor(
|
||||||
private linkAttachmentService: LinkAttachment;
|
private readonly linkAttachmentService: LinkAttachment,
|
||||||
|
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||||
@Inject()
|
private readonly validateDocuments: ValidateAttachments,
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the attachment keys on creating sale estimate.
|
* Validates the attachment keys on creating sale estimate.
|
||||||
* @param {ISaleEstimateCreatingPayload}
|
* @param {ISaleEstimateCreatingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async validateAttachmentsOnSaleEstimateCreated({
|
@OnEvent(events.saleEstimate.onCreating)
|
||||||
|
async validateAttachmentsOnSaleEstimateCreated({
|
||||||
estimateDTO,
|
estimateDTO,
|
||||||
tenantId,
|
|
||||||
}: ISaleEstimateCreatingPayload): Promise<void> {
|
}: ISaleEstimateCreatingPayload): Promise<void> {
|
||||||
if (isEmpty(estimateDTO.attachments)) {
|
if (isEmpty(estimateDTO.attachments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const documentKeys = estimateDTO?.attachments?.map((a) => a.key);
|
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}
|
* @param {ISaleEstimateCreatedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleAttachmentsOnSaleEstimateCreated({
|
@OnEvent(events.saleEstimate.onCreated)
|
||||||
tenantId,
|
async handleAttachmentsOnSaleEstimateCreated({
|
||||||
saleEstimateDTO,
|
saleEstimateDTO,
|
||||||
saleEstimate,
|
saleEstimate,
|
||||||
trx,
|
trx,
|
||||||
@@ -79,14 +51,13 @@ export class AttachmentsOnSaleEstimates {
|
|||||||
if (isEmpty(saleEstimateDTO.attachments)) return;
|
if (isEmpty(saleEstimateDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = saleEstimateDTO.attachments?.map(
|
const keys = saleEstimateDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'SaleEstimate',
|
'SaleEstimate',
|
||||||
saleEstimate.id,
|
saleEstimate.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,20 +65,19 @@ export class AttachmentsOnSaleEstimates {
|
|||||||
* Handles unlinking all the unpresented keys of the edited sale estimate.
|
* Handles unlinking all the unpresented keys of the edited sale estimate.
|
||||||
* @param {ISaleEstimateEditedPayload}
|
* @param {ISaleEstimateEditedPayload}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkUnpresentedKeysOnSaleEstimateEdited({
|
@OnEvent(events.saleEstimate.onEdited)
|
||||||
tenantId,
|
async handleUnlinkUnpresentedKeysOnSaleEstimateEdited({
|
||||||
estimateDTO,
|
estimateDTO,
|
||||||
oldSaleEstimate,
|
oldSaleEstimate,
|
||||||
trx
|
trx,
|
||||||
}: ISaleEstimateEditedPayload) {
|
}: ISaleEstimateEditedPayload) {
|
||||||
const keys = estimateDTO.attachments?.map((attachment) => attachment.key);
|
const keys = estimateDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
|
||||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'SaleEstimate',
|
'SaleEstimate',
|
||||||
oldSaleEstimate.id,
|
oldSaleEstimate.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,8 +86,8 @@ export class AttachmentsOnSaleEstimates {
|
|||||||
* @param {ISaleEstimateEditedPayload}
|
* @param {ISaleEstimateEditedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleLinkPresentedKeysOnSaleEstimateEdited({
|
@OnEvent(events.saleEstimate.onEdited)
|
||||||
tenantId,
|
async handleLinkPresentedKeysOnSaleEstimateEdited({
|
||||||
estimateDTO,
|
estimateDTO,
|
||||||
oldSaleEstimate,
|
oldSaleEstimate,
|
||||||
trx,
|
trx,
|
||||||
@@ -125,12 +95,12 @@ export class AttachmentsOnSaleEstimates {
|
|||||||
if (isEmpty(estimateDTO.attachments)) return;
|
if (isEmpty(estimateDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = estimateDTO.attachments?.map((attachment) => attachment.key);
|
const keys = estimateDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'SaleEstimate',
|
'SaleEstimate',
|
||||||
oldSaleEstimate.id,
|
oldSaleEstimate.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,16 +109,15 @@ export class AttachmentsOnSaleEstimates {
|
|||||||
* @param {ISaleEstimateDeletingPayload}
|
* @param {ISaleEstimateDeletingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkAttachmentsOnSaleEstimateDelete({
|
@OnEvent(events.saleEstimate.onDeleting)
|
||||||
tenantId,
|
async handleUnlinkAttachmentsOnSaleEstimateDelete({
|
||||||
oldSaleEstimate,
|
oldSaleEstimate,
|
||||||
trx,
|
trx,
|
||||||
}: ISaleEstimateDeletingPayload) {
|
}: ISaleEstimateDeletingPayload) {
|
||||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
tenantId,
|
|
||||||
'SaleEstimate',
|
'SaleEstimate',
|
||||||
oldSaleEstimate.id,
|
oldSaleEstimate.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,40 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
import {
|
import {
|
||||||
ISaleInvoiceCreatedPayload,
|
ISaleInvoiceCreatedPayload,
|
||||||
ISaleInvoiceCreatingPaylaod,
|
ISaleInvoiceCreatingPaylaod,
|
||||||
ISaleInvoiceDeletingPayload,
|
ISaleInvoiceDeletingPayload,
|
||||||
ISaleInvoiceEditedPayload,
|
ISaleInvoiceEditedPayload,
|
||||||
} from '@/interfaces';
|
} from '@/modules/SaleInvoices/SaleInvoice.types';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { LinkAttachment } from '../LinkAttachment';
|
|
||||||
import { ValidateAttachments } from '../ValidateAttachments';
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class AttachmentsOnSaleInvoiceCreated {
|
export class AttachmentsOnSaleInvoiceCreated {
|
||||||
@Inject()
|
constructor(
|
||||||
private linkAttachmentService: LinkAttachment;
|
private readonly linkAttachmentService: LinkAttachment,
|
||||||
|
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||||
@Inject()
|
private readonly validateDocuments: ValidateAttachments,
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the attachment keys on creating sale invoice.
|
* Validates the attachment keys on creating sale invoice.
|
||||||
* @param {ISaleInvoiceCreatingPaylaod}
|
* @param {ISaleInvoiceCreatingPaylaod}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async validateAttachmentsOnSaleInvoiceCreate({
|
@OnEvent(events.saleInvoice.onCreating)
|
||||||
|
async validateAttachmentsOnSaleInvoiceCreate({
|
||||||
saleInvoiceDTO,
|
saleInvoiceDTO,
|
||||||
tenantId,
|
|
||||||
}: ISaleInvoiceCreatingPaylaod): Promise<void> {
|
}: ISaleInvoiceCreatingPaylaod): Promise<void> {
|
||||||
if (isEmpty(saleInvoiceDTO.attachments)) {
|
if (isEmpty(saleInvoiceDTO.attachments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const documentKeys = saleInvoiceDTO?.attachments?.map((a) => a.key);
|
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}
|
* @param {ISaleInvoiceCreatedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleAttachmentsOnSaleInvoiceCreated({
|
@OnEvent(events.saleInvoice.onCreated)
|
||||||
tenantId,
|
async handleAttachmentsOnSaleInvoiceCreated({
|
||||||
saleInvoiceDTO,
|
saleInvoiceDTO,
|
||||||
saleInvoice,
|
saleInvoice,
|
||||||
trx,
|
trx,
|
||||||
@@ -79,14 +51,13 @@ export class AttachmentsOnSaleInvoiceCreated {
|
|||||||
if (isEmpty(saleInvoiceDTO.attachments)) return;
|
if (isEmpty(saleInvoiceDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = saleInvoiceDTO.attachments?.map(
|
const keys = saleInvoiceDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'SaleInvoice',
|
'SaleInvoice',
|
||||||
saleInvoice.id,
|
saleInvoice.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,8 +65,8 @@ export class AttachmentsOnSaleInvoiceCreated {
|
|||||||
* Handles unlinking all the unpresented keys of the edited sale invoice.
|
* Handles unlinking all the unpresented keys of the edited sale invoice.
|
||||||
* @param {ISaleInvoiceEditedPayload}
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkUnpresentedKeysOnInvoiceEdited({
|
@OnEvent(events.saleInvoice.onEdited)
|
||||||
tenantId,
|
async handleUnlinkUnpresentedKeysOnInvoiceEdited({
|
||||||
saleInvoiceDTO,
|
saleInvoiceDTO,
|
||||||
saleInvoice,
|
saleInvoice,
|
||||||
trx,
|
trx,
|
||||||
@@ -103,14 +74,13 @@ export class AttachmentsOnSaleInvoiceCreated {
|
|||||||
// if (isEmpty(saleInvoiceDTO.attachments)) return;
|
// if (isEmpty(saleInvoiceDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = saleInvoiceDTO.attachments?.map(
|
const keys = saleInvoiceDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'SaleInvoice',
|
'SaleInvoice',
|
||||||
saleInvoice.id,
|
saleInvoice.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,8 +89,8 @@ export class AttachmentsOnSaleInvoiceCreated {
|
|||||||
* @param {ISaleInvoiceEditedPayload}
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleLinkPresentedKeysOnInvoiceEdited({
|
@OnEvent(events.saleInvoice.onEdited)
|
||||||
tenantId,
|
async handleLinkPresentedKeysOnInvoiceEdited({
|
||||||
saleInvoiceDTO,
|
saleInvoiceDTO,
|
||||||
oldSaleInvoice,
|
oldSaleInvoice,
|
||||||
trx,
|
trx,
|
||||||
@@ -128,14 +98,13 @@ export class AttachmentsOnSaleInvoiceCreated {
|
|||||||
if (isEmpty(saleInvoiceDTO.attachments)) return;
|
if (isEmpty(saleInvoiceDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = saleInvoiceDTO.attachments?.map(
|
const keys = saleInvoiceDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'SaleInvoice',
|
'SaleInvoice',
|
||||||
oldSaleInvoice.id,
|
oldSaleInvoice.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,16 +113,15 @@ export class AttachmentsOnSaleInvoiceCreated {
|
|||||||
* @param {ISaleInvoiceDeletedPayload}
|
* @param {ISaleInvoiceDeletedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkAttachmentsOnInvoiceDeleted({
|
@OnEvent(events.saleInvoice.onDeleting)
|
||||||
tenantId,
|
async handleUnlinkAttachmentsOnInvoiceDeleted({
|
||||||
oldSaleInvoice,
|
oldSaleInvoice,
|
||||||
trx,
|
trx,
|
||||||
}: ISaleInvoiceDeletingPayload) {
|
}: ISaleInvoiceDeletingPayload) {
|
||||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
tenantId,
|
|
||||||
'SaleInvoice',
|
'SaleInvoice',
|
||||||
oldSaleInvoice.id,
|
oldSaleInvoice.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,40 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
import {
|
import {
|
||||||
ISaleReceiptCreatedPayload,
|
ISaleReceiptCreatedPayload,
|
||||||
ISaleReceiptCreatingPayload,
|
ISaleReceiptCreatingPayload,
|
||||||
ISaleReceiptDeletingPayload,
|
ISaleReceiptDeletingPayload,
|
||||||
ISaleReceiptEditedPayload,
|
ISaleReceiptEditedPayload,
|
||||||
} from '@/interfaces';
|
} from '../../SaleReceipts/types/SaleReceipts.types';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { LinkAttachment } from '../LinkAttachment';
|
|
||||||
import { ValidateAttachments } from '../ValidateAttachments';
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class AttachmentsOnSaleReceipt {
|
export class AttachmentsOnSaleReceipt {
|
||||||
@Inject()
|
constructor(
|
||||||
private linkAttachmentService: LinkAttachment;
|
private readonly linkAttachmentService: LinkAttachment,
|
||||||
|
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||||
@Inject()
|
private readonly validateDocuments: ValidateAttachments,
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the attachment keys on creating sale receipt.
|
* Validates the attachment keys on creating sale receipt.
|
||||||
* @param {ISaleReceiptCreatingPayload}
|
* @param {ISaleReceiptCreatingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async validateAttachmentsOnSaleInvoiceCreate({
|
@OnEvent(events.saleReceipt.onCreating)
|
||||||
|
async validateAttachmentsOnSaleInvoiceCreate({
|
||||||
saleReceiptDTO,
|
saleReceiptDTO,
|
||||||
tenantId,
|
|
||||||
}: ISaleReceiptCreatingPayload): Promise<void> {
|
}: ISaleReceiptCreatingPayload): Promise<void> {
|
||||||
if (isEmpty(saleReceiptDTO.attachments)) {
|
if (isEmpty(saleReceiptDTO.attachments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const documentKeys = saleReceiptDTO?.attachments?.map((a) => a.key);
|
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}
|
* @param {ISaleReceiptCreatedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleAttachmentsOnSaleInvoiceCreated({
|
@OnEvent(events.saleReceipt.onCreated)
|
||||||
tenantId,
|
async handleAttachmentsOnSaleInvoiceCreated({
|
||||||
saleReceiptDTO,
|
saleReceiptDTO,
|
||||||
saleReceipt,
|
saleReceipt,
|
||||||
trx,
|
trx,
|
||||||
@@ -79,14 +51,13 @@ export class AttachmentsOnSaleReceipt {
|
|||||||
if (isEmpty(saleReceiptDTO.attachments)) return;
|
if (isEmpty(saleReceiptDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = saleReceiptDTO.attachments?.map(
|
const keys = saleReceiptDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'SaleReceipt',
|
'SaleReceipt',
|
||||||
saleReceipt.id,
|
saleReceipt.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,21 +65,20 @@ export class AttachmentsOnSaleReceipt {
|
|||||||
* Handles unlinking all the unpresented keys of the edited sale receipt.
|
* Handles unlinking all the unpresented keys of the edited sale receipt.
|
||||||
* @param {ISaleReceiptEditedPayload}
|
* @param {ISaleReceiptEditedPayload}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkUnpresentedKeysOnInvoiceEdited({
|
@OnEvent(events.saleReceipt.onEdited)
|
||||||
tenantId,
|
async handleUnlinkUnpresentedKeysOnInvoiceEdited({
|
||||||
saleReceiptDTO,
|
saleReceiptDTO,
|
||||||
saleReceipt,
|
saleReceipt,
|
||||||
trx,
|
trx,
|
||||||
}: ISaleReceiptEditedPayload) {
|
}: ISaleReceiptEditedPayload) {
|
||||||
const keys = saleReceiptDTO.attachments?.map(
|
const keys = saleReceiptDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'SaleReceipt',
|
'SaleReceipt',
|
||||||
saleReceipt.id,
|
saleReceipt.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,8 +87,8 @@ export class AttachmentsOnSaleReceipt {
|
|||||||
* @param {ISaleReceiptEditedPayload}
|
* @param {ISaleReceiptEditedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleLinkPresentedKeysOnInvoiceEdited({
|
@OnEvent(events.saleReceipt.onEdited)
|
||||||
tenantId,
|
async handleLinkPresentedKeysOnInvoiceEdited({
|
||||||
saleReceiptDTO,
|
saleReceiptDTO,
|
||||||
oldSaleReceipt,
|
oldSaleReceipt,
|
||||||
trx,
|
trx,
|
||||||
@@ -126,14 +96,13 @@ export class AttachmentsOnSaleReceipt {
|
|||||||
if (isEmpty(saleReceiptDTO.attachments)) return;
|
if (isEmpty(saleReceiptDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = saleReceiptDTO.attachments?.map(
|
const keys = saleReceiptDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'SaleReceipt',
|
'SaleReceipt',
|
||||||
oldSaleReceipt.id,
|
oldSaleReceipt.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,16 +111,15 @@ export class AttachmentsOnSaleReceipt {
|
|||||||
* @param {ISaleReceiptDeletingPayload}
|
* @param {ISaleReceiptDeletingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkAttachmentsOnReceiptDeleted({
|
@OnEvent(events.saleReceipt.onDeleting)
|
||||||
tenantId,
|
async handleUnlinkAttachmentsOnReceiptDeleted({
|
||||||
oldSaleReceipt,
|
oldSaleReceipt,
|
||||||
trx,
|
trx,
|
||||||
}: ISaleReceiptDeletingPayload) {
|
}: ISaleReceiptDeletingPayload) {
|
||||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
tenantId,
|
|
||||||
'SaleReceipt',
|
'SaleReceipt',
|
||||||
oldSaleReceipt.id,
|
oldSaleReceipt.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,40 @@
|
|||||||
import { Inject, Service } from 'typedi';
|
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
IVendorCreditCreatedPayload,
|
IVendorCreditCreatedPayload,
|
||||||
IVendorCreditCreatingPayload,
|
IVendorCreditCreatingPayload,
|
||||||
IVendorCreditDeletingPayload,
|
IVendorCreditDeletingPayload,
|
||||||
IVendorCreditEditedPayload,
|
IVendorCreditEditedPayload,
|
||||||
} from '@/interfaces';
|
} from '../../VendorCredit/types/VendorCredit.types';
|
||||||
import events from '@/subscribers/events';
|
|
||||||
import { LinkAttachment } from '../LinkAttachment';
|
|
||||||
import { ValidateAttachments } from '../ValidateAttachments';
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { OnEvent } from '@nestjs/event-emitter';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
import { UnlinkAttachment } from '../UnlinkAttachment';
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { events } from '@/common/events/events';
|
||||||
|
|
||||||
@Service()
|
@Injectable()
|
||||||
export class AttachmentsOnVendorCredits {
|
export class AttachmentsOnVendorCredits {
|
||||||
@Inject()
|
constructor(
|
||||||
private linkAttachmentService: LinkAttachment;
|
private readonly linkAttachmentService: LinkAttachment,
|
||||||
|
private readonly unlinkAttachmentService: UnlinkAttachment,
|
||||||
@Inject()
|
private readonly validateDocuments: ValidateAttachments,
|
||||||
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)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the attachment keys on creating vendor credit.
|
* Validates the attachment keys on creating vendor credit.
|
||||||
* @param {IVendorCreditCreatingPayload}
|
* @param {IVendorCreditCreatingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async validateAttachmentsOnVendorCreditCreate({
|
@OnEvent(events.vendorCredit.onCreating)
|
||||||
|
async validateAttachmentsOnVendorCreditCreate({
|
||||||
vendorCreditCreateDTO,
|
vendorCreditCreateDTO,
|
||||||
tenantId,
|
|
||||||
}: IVendorCreditCreatingPayload): Promise<void> {
|
}: IVendorCreditCreatingPayload): Promise<void> {
|
||||||
if (isEmpty(vendorCreditCreateDTO.attachments)) {
|
if (isEmpty(vendorCreditCreateDTO.attachments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const documentKeys = vendorCreditCreateDTO?.attachments?.map((a) => a.key);
|
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}
|
* @param {IVendorCreditCreatedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleAttachmentsOnVendorCreditCreated({
|
@OnEvent(events.vendorCredit.onCreated)
|
||||||
tenantId,
|
async handleAttachmentsOnVendorCreditCreated({
|
||||||
vendorCreditCreateDTO,
|
vendorCreditCreateDTO,
|
||||||
vendorCredit,
|
vendorCredit,
|
||||||
trx,
|
trx,
|
||||||
@@ -79,14 +51,13 @@ export class AttachmentsOnVendorCredits {
|
|||||||
if (isEmpty(vendorCreditCreateDTO.attachments)) return;
|
if (isEmpty(vendorCreditCreateDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = vendorCreditCreateDTO.attachments?.map(
|
const keys = vendorCreditCreateDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'VendorCredit',
|
'VendorCredit',
|
||||||
vendorCredit.id,
|
vendorCredit.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,21 +65,20 @@ export class AttachmentsOnVendorCredits {
|
|||||||
* Handles unlinking all the unpresented keys of the edited vendor credit.
|
* Handles unlinking all the unpresented keys of the edited vendor credit.
|
||||||
* @param {IVendorCreditEditedPayload}
|
* @param {IVendorCreditEditedPayload}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkUnpresentedKeysOnVendorCreditEdited({
|
@OnEvent(events.vendorCredit.onEdited)
|
||||||
tenantId,
|
async handleUnlinkUnpresentedKeysOnVendorCreditEdited({
|
||||||
vendorCreditDTO,
|
vendorCreditDTO,
|
||||||
oldVendorCredit,
|
oldVendorCredit,
|
||||||
trx,
|
trx,
|
||||||
}: IVendorCreditEditedPayload) {
|
}: IVendorCreditEditedPayload) {
|
||||||
const keys = vendorCreditDTO.attachments?.map(
|
const keys = vendorCreditDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'VendorCredit',
|
'VendorCredit',
|
||||||
oldVendorCredit.id,
|
oldVendorCredit.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,8 +87,8 @@ export class AttachmentsOnVendorCredits {
|
|||||||
* @param {IVendorCreditEditedPayload}
|
* @param {IVendorCreditEditedPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleLinkPresentedKeysOnVendorCreditEdited({
|
@OnEvent(events.vendorCredit.onEdited)
|
||||||
tenantId,
|
async handleLinkPresentedKeysOnVendorCreditEdited({
|
||||||
vendorCreditDTO,
|
vendorCreditDTO,
|
||||||
oldVendorCredit,
|
oldVendorCredit,
|
||||||
trx,
|
trx,
|
||||||
@@ -126,14 +96,13 @@ export class AttachmentsOnVendorCredits {
|
|||||||
if (isEmpty(vendorCreditDTO.attachments)) return;
|
if (isEmpty(vendorCreditDTO.attachments)) return;
|
||||||
|
|
||||||
const keys = vendorCreditDTO.attachments?.map(
|
const keys = vendorCreditDTO.attachments?.map(
|
||||||
(attachment) => attachment.key
|
(attachment) => attachment.key,
|
||||||
);
|
);
|
||||||
await this.linkAttachmentService.bulkLink(
|
await this.linkAttachmentService.bulkLink(
|
||||||
tenantId,
|
|
||||||
keys,
|
keys,
|
||||||
'VendorCredit',
|
'VendorCredit',
|
||||||
oldVendorCredit.id,
|
oldVendorCredit.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,16 +111,15 @@ export class AttachmentsOnVendorCredits {
|
|||||||
* @param {IVendorCreditDeletingPayload}
|
* @param {IVendorCreditDeletingPayload}
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
private async handleUnlinkAttachmentsOnVendorCreditDeleted({
|
@OnEvent(events.vendorCredit.onDeleting)
|
||||||
tenantId,
|
async handleUnlinkAttachmentsOnVendorCreditDeleted({
|
||||||
oldVendorCredit,
|
oldVendorCredit,
|
||||||
trx,
|
trx,
|
||||||
}: IVendorCreditDeletingPayload) {
|
}: IVendorCreditDeletingPayload) {
|
||||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
tenantId,
|
|
||||||
'VendorCredit',
|
'VendorCredit',
|
||||||
oldVendorCredit.id,
|
oldVendorCredit.id,
|
||||||
trx
|
trx,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||||
import { Model, mixin } from 'objection';
|
import { Model, mixin } from 'objection';
|
||||||
|
import { DocumentModel } from './Document.model';
|
||||||
|
|
||||||
export class DocumentLinkModel extends TenantBaseModel {
|
export class DocumentLinkModel extends TenantBaseModel {
|
||||||
|
document!: DocumentModel;
|
||||||
/**
|
/**
|
||||||
* Table name
|
* Table name
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import config from '@/config';
|
// import config from '@/config';
|
||||||
|
|
||||||
export const getUploadedObjectUri = (objectKey: string) => {
|
export const getUploadedObjectUri = (objectKey: string) => {
|
||||||
return new URL(
|
return '';
|
||||||
path.join(config.s3.bucket, objectKey),
|
// return new URL(
|
||||||
config.s3.endpoint
|
// path.join(config.s3.bucket, objectKey),
|
||||||
).toString();
|
// config.s3.endpoint
|
||||||
|
// ).toString();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { BaseModel } from '@/models/Model';
|
|||||||
|
|
||||||
export class DocumentLink extends BaseModel{
|
export class DocumentLink extends BaseModel{
|
||||||
public modelRef: string;
|
public modelRef: string;
|
||||||
public modelId: string;
|
public modelId: number;
|
||||||
public documentId: number;
|
public documentId: number;
|
||||||
public expiresAt?: Date;
|
public expiresAt?: Date;
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,6 @@ import * as moment from 'moment';
|
|||||||
import * as composeAsync from 'async/compose';
|
import * as composeAsync from 'async/compose';
|
||||||
import * as R from 'ramda';
|
import * as R from 'ramda';
|
||||||
import { ERRORS } from '../constants';
|
import { ERRORS } from '../constants';
|
||||||
import {
|
|
||||||
ICreditNoteEditDTO,
|
|
||||||
ICreditNoteEntryNewDTO,
|
|
||||||
ICreditNoteNewDTO,
|
|
||||||
} from '../types/CreditNotes.types';
|
|
||||||
import { ServiceError } from '@/modules/Items/ServiceError';
|
import { ServiceError } from '@/modules/Items/ServiceError';
|
||||||
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
|
import { ItemsEntriesService } from '@/modules/Items/ItemsEntries.service';
|
||||||
import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform';
|
import { BranchTransactionDTOTransformer } from '@/modules/Branches/integrations/BranchTransactionDTOTransform';
|
||||||
|
|||||||
@@ -1,43 +1,9 @@
|
|||||||
import { Knex } from 'knex';
|
import { Knex } from 'knex';
|
||||||
import { CreditNote } from '../models/CreditNote';
|
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 { IFilterMeta, IPaginationMeta } from '@/interfaces/Model';
|
||||||
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
|
import { IDynamicListFilter } from '@/modules/DynamicListing/DynamicFilter/DynamicFilter.types';
|
||||||
import { ILedgerEntry } from '@/modules/Ledger/types/Ledger.types';
|
import { ILedgerEntry } from '@/modules/Ledger/types/Ledger.types';
|
||||||
import { EditCreditNoteDto } from '../dtos/CreditNote.dto';
|
import { CreateCreditNoteDto, 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[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum CreditNoteAction {
|
export enum CreditNoteAction {
|
||||||
Create = 'Create',
|
Create = 'Create',
|
||||||
@@ -74,13 +40,13 @@ export interface ICreditNoteEditedPayload {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ICreditNoteCreatedPayload {
|
export interface ICreditNoteCreatedPayload {
|
||||||
creditNoteDTO: ICreditNoteNewDTO;
|
creditNoteDTO: CreateCreditNoteDto;
|
||||||
creditNote: CreditNote;
|
creditNote: CreditNote;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICreditNoteCreatingPayload {
|
export interface ICreditNoteCreatingPayload {
|
||||||
creditNoteDTO: ICreditNoteNewDTO;
|
creditNoteDTO: CreateCreditNoteDto;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import { Knex } from 'knex';
|
|||||||
import { Expense } from './models/Expense.model';
|
import { Expense } from './models/Expense.model';
|
||||||
import { SystemUser } from '../System/models/SystemUser';
|
import { SystemUser } from '../System/models/SystemUser';
|
||||||
import { IFilterRole } from '../DynamicListing/DynamicFilter/DynamicFilter.types';
|
import { IFilterRole } from '../DynamicListing/DynamicFilter/DynamicFilter.types';
|
||||||
|
import { CreateExpenseDto, EditExpenseDto } from './dtos/Expense.dto';
|
||||||
|
import { CreateExpense } from './commands/CreateExpense.service';
|
||||||
|
|
||||||
export interface IPaginationMeta {
|
export interface IPaginationMeta {
|
||||||
total: number;
|
total: number;
|
||||||
@@ -19,59 +21,28 @@ export interface IExpensesFilter {
|
|||||||
filterQuery?: (query: any) => void;
|
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 {
|
export interface IExpenseCreatingPayload {
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
expenseDTO: IExpenseCreateDTO;
|
expenseDTO: CreateExpenseDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExpenseEventEditingPayload {
|
export interface IExpenseEventEditingPayload {
|
||||||
oldExpense: Expense;
|
oldExpense: Expense;
|
||||||
expenseDTO: IExpenseEditDTO;
|
expenseDTO: EditExpenseDto;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExpenseCreatedPayload {
|
export interface IExpenseCreatedPayload {
|
||||||
expenseId: number;
|
expenseId: number;
|
||||||
expense: Expense;
|
expense: Expense;
|
||||||
expenseDTO: IExpenseCreateDTO;
|
expenseDTO: CreateExpenseDto;
|
||||||
trx?: Knex.Transaction;
|
trx?: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExpenseEventEditPayload {
|
export interface IExpenseEventEditPayload {
|
||||||
expenseId: number;
|
expenseId: number;
|
||||||
expense: Expense;
|
expense: Expense;
|
||||||
expenseDTO: IExpenseEditDTO;
|
expenseDTO: EditExpenseDto;
|
||||||
authorizedUser: SystemUser;
|
authorizedUser: SystemUser;
|
||||||
oldExpense: Expense;
|
oldExpense: Expense;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export const S3_CLIENT = 'S3_CLIENT';
|
|||||||
const services = [
|
const services = [
|
||||||
{
|
{
|
||||||
provide: S3_CLIENT,
|
provide: S3_CLIENT,
|
||||||
|
inject: [ConfigService],
|
||||||
useFactory: (configService: ConfigService) => {
|
useFactory: (configService: ConfigService) => {
|
||||||
const config = configService.get('s3');
|
const config = configService.get('s3');
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ class PaymentMethodDto {
|
|||||||
enable: boolean;
|
enable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class AttachmentDto {
|
||||||
|
@IsString()
|
||||||
|
key: string;
|
||||||
|
}
|
||||||
|
|
||||||
class CommandSaleInvoiceDto {
|
class CommandSaleInvoiceDto {
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
@@ -186,6 +192,16 @@ class CommandSaleInvoiceDto {
|
|||||||
example: 1,
|
example: 1,
|
||||||
})
|
})
|
||||||
fromEstimateId?: number;
|
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 {}
|
export class CreateSaleInvoiceDto extends CommandSaleInvoiceDto {}
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ import { ISearchRole } from '@/modules/DynamicListing/DynamicFilter/DynamicFilte
|
|||||||
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||||
import { PaymentIntegrationTransactionLink } from '../SaleInvoice.types';
|
import { PaymentIntegrationTransactionLink } from '../SaleInvoice.types';
|
||||||
import { TransactionPaymentServiceEntry } from '@/modules/PaymentServices/models/TransactionPaymentServiceEntry.model';
|
import { TransactionPaymentServiceEntry } from '@/modules/PaymentServices/models/TransactionPaymentServiceEntry.model';
|
||||||
|
import { InjectAttachable } from '@/modules/Attachments/decorators/InjectAttachable.decorator';
|
||||||
|
|
||||||
|
@InjectAttachable()
|
||||||
export class SaleInvoice extends TenantBaseModel{
|
export class SaleInvoice extends TenantBaseModel{
|
||||||
public taxAmountWithheld: number;
|
public taxAmountWithheld: number;
|
||||||
public balance: number;
|
public balance: number;
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { Knex } from 'knex';
|
|||||||
import {
|
import {
|
||||||
ISaleReceiptCreatedPayload,
|
ISaleReceiptCreatedPayload,
|
||||||
ISaleReceiptCreatingPayload,
|
ISaleReceiptCreatingPayload,
|
||||||
ISaleReceiptDTO,
|
|
||||||
} from '../types/SaleReceipts.types';
|
} from '../types/SaleReceipts.types';
|
||||||
import { SaleReceiptDTOTransformer } from './SaleReceiptDTOTransformer.service';
|
import { SaleReceiptDTOTransformer } from './SaleReceiptDTOTransformer.service';
|
||||||
import { SaleReceiptValidators } from './SaleReceiptValidators.service';
|
import { SaleReceiptValidators } from './SaleReceiptValidators.service';
|
||||||
|
|||||||
@@ -1,39 +1,21 @@
|
|||||||
import { Knex } from 'knex';
|
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 { SaleReceipt } from '../models/SaleReceipt';
|
||||||
import { CommonMailOptionsDTO } from '@/modules/MailNotification/MailNotification.types';
|
import { CommonMailOptionsDTO } from '@/modules/MailNotification/MailNotification.types';
|
||||||
import { CommonMailOptions } from '@/modules/MailNotification/MailNotification.types';
|
import { CommonMailOptions } from '@/modules/MailNotification/MailNotification.types';
|
||||||
import { TenantJobPayload } from '@/interfaces/Tenant';
|
import { TenantJobPayload } from '@/interfaces/Tenant';
|
||||||
|
import { CreateSaleReceiptDto, EditSaleReceiptDto } from '../dtos/SaleReceipt.dto';
|
||||||
|
|
||||||
export interface ISalesReceiptsFilter {
|
export interface ISalesReceiptsFilter {
|
||||||
filterQuery?: (query: any) => void;
|
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 {
|
export interface ISaleReceiptSmsDetails {
|
||||||
customerName: string;
|
customerName: string;
|
||||||
customerPhoneNumber: string;
|
customerPhoneNumber: string;
|
||||||
smsMessage: string;
|
smsMessage: string;
|
||||||
}
|
}
|
||||||
export interface ISaleReceiptCreatingPayload {
|
export interface ISaleReceiptCreatingPayload {
|
||||||
saleReceiptDTO: ISaleReceiptDTO;
|
saleReceiptDTO: CreateSaleReceiptDto;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,21 +23,20 @@ export interface ISaleReceiptCreatedPayload {
|
|||||||
// tenantId: number;
|
// tenantId: number;
|
||||||
saleReceipt: SaleReceipt;
|
saleReceipt: SaleReceipt;
|
||||||
saleReceiptId: number;
|
saleReceiptId: number;
|
||||||
saleReceiptDTO: ISaleReceiptDTO;
|
saleReceiptDTO: CreateSaleReceiptDto;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISaleReceiptEditedPayload {
|
export interface ISaleReceiptEditedPayload {
|
||||||
oldSaleReceipt: SaleReceipt;
|
oldSaleReceipt: SaleReceipt;
|
||||||
saleReceipt: SaleReceipt;
|
saleReceipt: SaleReceipt;
|
||||||
// saleReceiptId: number;
|
saleReceiptDTO: EditSaleReceiptDto;
|
||||||
saleReceiptDTO: ISaleReceiptDTO;
|
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ISaleReceiptEditingPayload {
|
export interface ISaleReceiptEditingPayload {
|
||||||
oldSaleReceipt: SaleReceipt;
|
oldSaleReceipt: SaleReceipt;
|
||||||
saleReceiptDTO: ISaleReceiptDTO;
|
saleReceiptDTO: EditSaleReceiptDto;
|
||||||
trx: Knex.Transaction;
|
trx: Knex.Transaction;
|
||||||
}
|
}
|
||||||
export interface ISaleReceiptEventClosedPayload {
|
export interface ISaleReceiptEventClosedPayload {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export class AttachmentUploadPipeline {
|
|||||||
* @param res The HTTP response object.
|
* @param res The HTTP response object.
|
||||||
* @param next The callback to pass control to the next middleware function.
|
* @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 (
|
if (
|
||||||
!config.s3.region ||
|
!config.s3.region ||
|
||||||
!config.s3.accessKeyId ||
|
!config.s3.accessKeyId ||
|
||||||
@@ -42,6 +42,7 @@ export class AttachmentUploadPipeline {
|
|||||||
s3,
|
s3,
|
||||||
bucket: config.s3.bucket,
|
bucket: config.s3.bucket,
|
||||||
contentType: multerS3.AUTO_CONTENT_TYPE,
|
contentType: multerS3.AUTO_CONTENT_TYPE,
|
||||||
|
|
||||||
metadata: function (req, file, cb) {
|
metadata: function (req, file, cb) {
|
||||||
cb(null, { fieldName: file.fieldname });
|
cb(null, { fieldName: file.fieldname });
|
||||||
},
|
},
|
||||||
|
|||||||
15
pnpm-lock.yaml
generated
15
pnpm-lock.yaml
generated
@@ -547,6 +547,9 @@ importers:
|
|||||||
'@supercharge/promise-pool':
|
'@supercharge/promise-pool':
|
||||||
specifier: ^3.2.0
|
specifier: ^3.2.0
|
||||||
version: 3.2.0
|
version: 3.2.0
|
||||||
|
'@types/multer':
|
||||||
|
specifier: ^1.4.11
|
||||||
|
version: 1.4.11
|
||||||
'@types/nodemailer':
|
'@types/nodemailer':
|
||||||
specifier: ^6.4.17
|
specifier: ^6.4.17
|
||||||
version: 6.4.17
|
version: 6.4.17
|
||||||
@@ -631,6 +634,9 @@ importers:
|
|||||||
mathjs:
|
mathjs:
|
||||||
specifier: ^9.4.0
|
specifier: ^9.4.0
|
||||||
version: 9.5.2
|
version: 9.5.2
|
||||||
|
mime-types:
|
||||||
|
specifier: ^2.1.35
|
||||||
|
version: 2.1.35
|
||||||
moment:
|
moment:
|
||||||
specifier: ^2.30.1
|
specifier: ^2.30.1
|
||||||
version: 2.30.1
|
version: 2.30.1
|
||||||
@@ -640,6 +646,12 @@ importers:
|
|||||||
moment-timezone:
|
moment-timezone:
|
||||||
specifier: ^0.5.43
|
specifier: ^0.5.43
|
||||||
version: 0.5.45
|
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:
|
mysql:
|
||||||
specifier: ^2.18.1
|
specifier: ^2.18.1
|
||||||
version: 2.18.1
|
version: 2.18.1
|
||||||
@@ -12460,7 +12472,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==}
|
resolution: {integrity: sha512-svK240gr6LVWvv3YGyhLlA+6LRRWA4mnGIU7RcNmgjBYFl6665wcXrRfxGp5tEPVHUNm5FMcmq7too9bxCwX/w==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/express': 4.17.21
|
'@types/express': 4.17.21
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/node-fetch@2.6.11:
|
/@types/node-fetch@2.6.11:
|
||||||
resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
|
resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==}
|
||||||
@@ -12653,7 +12664,7 @@ packages:
|
|||||||
/@types/serve-index@1.9.4:
|
/@types/serve-index@1.9.4:
|
||||||
resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==}
|
resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/express': 4.17.21
|
'@types/express': 5.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@types/serve-static@1.15.7:
|
/@types/serve-static@1.15.7:
|
||||||
|
|||||||
Reference in New Issue
Block a user