mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 15:20:34 +00:00
refactor(nestjs): attachments and s3 modules
This commit is contained in:
@@ -77,6 +77,8 @@ import { PaymentServicesModule } from '../PaymentServices/PaymentServices.module
|
|||||||
import { AuthModule } from '../Auth/Auth.module';
|
import { AuthModule } from '../Auth/Auth.module';
|
||||||
import { TenancyModule } from '../Tenancy/Tenancy.module';
|
import { TenancyModule } from '../Tenancy/Tenancy.module';
|
||||||
import { LoopsModule } from '../Loops/Loops.module';
|
import { LoopsModule } from '../Loops/Loops.module';
|
||||||
|
import { AttachmentsModule } from '../Attachments/Attachment.module';
|
||||||
|
import { S3Module } from '../S3/S3.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -188,7 +190,9 @@ import { LoopsModule } from '../Loops/Loops.module';
|
|||||||
OrganizationModule,
|
OrganizationModule,
|
||||||
TenantDBManagerModule,
|
TenantDBManagerModule,
|
||||||
PaymentServicesModule,
|
PaymentServicesModule,
|
||||||
LoopsModule
|
LoopsModule,
|
||||||
|
AttachmentsModule,
|
||||||
|
S3Module
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [
|
providers: [
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { S3Module } from "../S3/S3.module";
|
||||||
|
import { DeleteAttachment } from "./DeleteAttachment";
|
||||||
|
import { GetAttachment } from "./GetAttachment";
|
||||||
|
import { getAttachmentPresignedUrl } from "./GetAttachmentPresignedUrl";
|
||||||
|
import { LinkAttachment } from "./LinkAttachment";
|
||||||
|
import { UnlinkAttachment } from "./UnlinkAttachment";
|
||||||
|
import { ValidateAttachments } from "./ValidateAttachments";
|
||||||
|
import { AttachmentsOnBillPayments } from "./events/AttachmentsOnPaymentsMade";
|
||||||
|
import { AttachmentsOnBills } from "./events/AttachmentsOnBills";
|
||||||
|
import { AttachmentsOnCreditNote } from "./events/AttachmentsOnCreditNote";
|
||||||
|
import { AttachmentsOnExpenses } from "./events/AttachmentsOnExpenses";
|
||||||
|
import { AttachmentsOnPaymentsReceived } from "./events/AttachmentsOnPaymentsReceived";
|
||||||
|
import { AttachmentsOnManualJournals } from "./events/AttachmentsOnManualJournals";
|
||||||
|
import { AttachmentsOnVendorCredits } from "./events/AttachmentsOnVendorCredits";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [S3Module],
|
||||||
|
providers: [
|
||||||
|
DeleteAttachment,
|
||||||
|
GetAttachment,
|
||||||
|
getAttachmentPresignedUrl,
|
||||||
|
LinkAttachment,
|
||||||
|
UnlinkAttachment,
|
||||||
|
ValidateAttachments,
|
||||||
|
AttachmentsOnBillPayments,
|
||||||
|
AttachmentsOnBills,
|
||||||
|
AttachmentsOnCreditNote,
|
||||||
|
AttachmentsOnExpenses,
|
||||||
|
AttachmentsOnPaymentsReceived,
|
||||||
|
AttachmentsOnManualJournals,
|
||||||
|
AttachmentsOnVendorCredits
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class AttachmentsModule {}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { Transformer } from "../Transformer/Transformer";
|
||||||
|
|
||||||
|
export class AttachmentTransformer extends Transformer{
|
||||||
|
/**
|
||||||
|
* Exclude attributes.
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
public excludeAttributes = (): string[] => {
|
||||||
|
return ['id', 'createdAt'];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Includeded attributes.
|
||||||
|
* @returns {string[]}
|
||||||
|
*/
|
||||||
|
public includeAttributes = (): string[] => {
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { UploadDocument } from './UploadDocument';
|
||||||
|
import { DeleteAttachment } from './DeleteAttachment';
|
||||||
|
import { GetAttachment } from './GetAttachment';
|
||||||
|
import { LinkAttachment } from './LinkAttachment';
|
||||||
|
import { UnlinkAttachment } from './UnlinkAttachment';
|
||||||
|
import { getAttachmentPresignedUrl } from './GetAttachmentPresignedUrl';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AttachmentsApplication {
|
||||||
|
constructor(
|
||||||
|
private readonly uploadDocumentService: UploadDocument,
|
||||||
|
private readonly deleteDocumentService: DeleteAttachment,
|
||||||
|
private readonly getDocumentService: GetAttachment,
|
||||||
|
private readonly linkDocumentService: LinkAttachment,
|
||||||
|
private readonly unlinkDocumentService: UnlinkAttachment,
|
||||||
|
private readonly getPresignedUrlService: getAttachmentPresignedUrl,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the metadata of uploaded document to S3 on database.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {} file
|
||||||
|
* @returns {Promise<Document>}
|
||||||
|
*/
|
||||||
|
public upload(file: any) {
|
||||||
|
return this.uploadDocumentService.upload(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the give file attachment file key.
|
||||||
|
* @param {string} documentKey
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
public delete(documentKey: string) {
|
||||||
|
return this.deleteDocumentService.delete(documentKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the document data.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} documentKey
|
||||||
|
*/
|
||||||
|
public get(documentKey: string) {
|
||||||
|
return this.getDocumentService.getAttachment(documentKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links the given document to resource model.
|
||||||
|
* @param {string} filekey
|
||||||
|
* @param {string} modelRef
|
||||||
|
* @param {number} modelId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public link(filekey: string, modelRef: string, modelId: number) {
|
||||||
|
return this.linkDocumentService.link(filekey, modelRef, modelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlinks the given document from resource model.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} filekey
|
||||||
|
* @param {string} modelRef
|
||||||
|
* @param {number} modelId
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public unlink(filekey: string, modelRef: string, modelId: number) {
|
||||||
|
return this.unlinkDocumentService.unlink(filekey, modelRef, modelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the presigned url of the given attachment key.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} key
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
public getPresignedUrl(key: string): Promise<string> {
|
||||||
|
return this.getPresignedUrlService.getPresignedUrl(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { UnitOfWork } from '../Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { S3_CLIENT } from '../S3/S3.module';
|
||||||
|
import { DocumentModel } from './models/Document.model';
|
||||||
|
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||||
|
import { DocumentLinkModel } from './models/DocumentLink.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DeleteAttachment {
|
||||||
|
constructor(
|
||||||
|
private readonly uow: UnitOfWork,
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
|
||||||
|
@Inject(S3_CLIENT)
|
||||||
|
private readonly s3Client: S3Client,
|
||||||
|
|
||||||
|
@Inject(Document.name)
|
||||||
|
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
||||||
|
|
||||||
|
@Inject(DocumentLinkModel.name)
|
||||||
|
private readonly documentLinkModel: TenantModelProxy<typeof DocumentLinkModel>
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the give file attachment file key.
|
||||||
|
* @param {string} filekey
|
||||||
|
*/
|
||||||
|
async delete(filekey: string): Promise<void> {
|
||||||
|
const params = {
|
||||||
|
Bucket: this.configService.get('s3.bucket'),
|
||||||
|
Key: filekey,
|
||||||
|
};
|
||||||
|
await this.s3Client.send(new DeleteObjectCommand(params));
|
||||||
|
|
||||||
|
const foundDocument = await this.documentModel().query()
|
||||||
|
.findOne('key', filekey)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
await this.uow.withTransaction(async (trx: Knex.Transaction) => {
|
||||||
|
// Delete all document links
|
||||||
|
await this.documentLinkModel().query(trx)
|
||||||
|
.where('documentId', foundDocument.id)
|
||||||
|
.delete();
|
||||||
|
|
||||||
|
// Delete thedocument.
|
||||||
|
await this.documentModel().query(trx).findById(foundDocument.id).delete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { S3_CLIENT } from '../S3/S3.module';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class GetAttachment {
|
||||||
|
constructor(
|
||||||
|
private readonly configService: ConfigService,
|
||||||
|
|
||||||
|
@Inject(S3_CLIENT)
|
||||||
|
private readonly s3: S3Client
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Retrieves data of the given document key.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} filekey
|
||||||
|
*/
|
||||||
|
async getAttachment(filekey: string) {
|
||||||
|
const params = {
|
||||||
|
Bucket: this.configService.get('s3.bucket'),
|
||||||
|
Key: filekey,
|
||||||
|
};
|
||||||
|
const data = await this.s3.send(new GetObjectCommand(params));
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { GetObjectCommand } from '@aws-sdk/client-s3';
|
||||||
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||||
|
import { DocumentModel } from './models/Document.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class getAttachmentPresignedUrl {
|
||||||
|
constructor(
|
||||||
|
private readonly documentModel: TenantModelProxy<typeof DocumentModel>
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the presigned url of the given attachment key with the original filename.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} key
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
async getPresignedUrl(tenantId: number, key: string) {
|
||||||
|
const foundDocument = await this.documentModel().query().findOne({ key });
|
||||||
|
|
||||||
|
let ResponseContentDisposition = 'attachment';
|
||||||
|
if (foundDocument && foundDocument.originName) {
|
||||||
|
ResponseContentDisposition += `; filename="${foundDocument.originName}"`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const command = new GetObjectCommand({
|
||||||
|
Bucket: config.s3.bucket,
|
||||||
|
Key: key,
|
||||||
|
ResponseContentDisposition,
|
||||||
|
});
|
||||||
|
const signedUrl = await getSignedUrl(s3, command, { expiresIn: 300 });
|
||||||
|
|
||||||
|
return signedUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import bluebird from 'bluebird';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import {
|
||||||
|
validateLinkModelEntryExists,
|
||||||
|
validateLinkModelExists,
|
||||||
|
} from './Attachments/_utils';
|
||||||
|
import HasTenancyService from '../Tenancy/TenancyService';
|
||||||
|
import { ServiceError } from '@/exceptions';
|
||||||
|
import { ERRORS } from './constants';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class LinkAttachment {
|
||||||
|
@Inject()
|
||||||
|
private tenancy: HasTenancyService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links the given file key to the given model type and id.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} filekey
|
||||||
|
* @param {string} modelRef
|
||||||
|
* @param {number} modelId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async link(
|
||||||
|
tenantId: number,
|
||||||
|
filekey: string,
|
||||||
|
modelRef: string,
|
||||||
|
modelId: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
) {
|
||||||
|
const { DocumentLink, Document, ...models } = this.tenancy.models(tenantId);
|
||||||
|
const LinkModel = models[modelRef];
|
||||||
|
validateLinkModelExists(LinkModel);
|
||||||
|
|
||||||
|
const foundFile = await Document.query(trx)
|
||||||
|
.findOne('key', filekey)
|
||||||
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
const foundLinkModel = await LinkModel.query(trx).findById(modelId);
|
||||||
|
validateLinkModelEntryExists(foundLinkModel);
|
||||||
|
|
||||||
|
const foundLinks = await DocumentLink.query(trx)
|
||||||
|
.where('modelRef', modelRef)
|
||||||
|
.where('modelId', modelId)
|
||||||
|
.where('documentId', foundFile.id);
|
||||||
|
|
||||||
|
if (foundLinks.length > 0) {
|
||||||
|
throw new ServiceError(ERRORS.DOCUMENT_LINK_ALREADY_LINKED);
|
||||||
|
}
|
||||||
|
await DocumentLink.query(trx).insert({
|
||||||
|
modelRef,
|
||||||
|
modelId,
|
||||||
|
documentId: foundFile.id,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links the given file keys to the given model type and id.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string[]} filekeys
|
||||||
|
* @param {string} modelRef
|
||||||
|
* @param {number} modelId
|
||||||
|
* @param {Knex.Transaction} trx
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async bulkLink(
|
||||||
|
tenantId: number,
|
||||||
|
filekeys: string[],
|
||||||
|
modelRef: string,
|
||||||
|
modelId: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
) {
|
||||||
|
return bluebird.each(filekeys, async (fieldKey: string) => {
|
||||||
|
try {
|
||||||
|
await this.link(tenantId, fieldKey, modelRef, modelId, trx);
|
||||||
|
} catch {
|
||||||
|
// Ignore catching exceptions in bulk action.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { NextFunction, Request, Response } from 'express';
|
||||||
|
import multer from 'multer';
|
||||||
|
import type { Multer } from 'multer';
|
||||||
|
import multerS3 from 'multer-s3';
|
||||||
|
import { s3 } from '@/lib/S3/S3';
|
||||||
|
import { Service } from 'typedi';
|
||||||
|
import config from '@/config';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentUploadPipeline {
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* @param req The HTTP request object.
|
||||||
|
* @param res The HTTP response object.
|
||||||
|
* @param next The callback to pass control to the next middleware function.
|
||||||
|
*/
|
||||||
|
public validateS3Configured(req: Request, res: Response, next: NextFunction) {
|
||||||
|
if (
|
||||||
|
!config.s3.region ||
|
||||||
|
!config.s3.accessKeyId ||
|
||||||
|
!config.s3.secretAccessKey
|
||||||
|
) {
|
||||||
|
const missingKeys = [];
|
||||||
|
if (!config.s3.region) missingKeys.push('region');
|
||||||
|
if (!config.s3.accessKeyId) missingKeys.push('accessKeyId');
|
||||||
|
if (!config.s3.secretAccessKey) missingKeys.push('secretAccessKey');
|
||||||
|
const missing = missingKeys.join(', ');
|
||||||
|
|
||||||
|
throw new Error(`S3 configuration error: Missing ${missing}`);
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Express middleware for uploading attachments to an S3 bucket.
|
||||||
|
* It utilizes the multer middleware for handling multipart/form-data, specifically for file uploads.
|
||||||
|
*/
|
||||||
|
public uploadPipeline(): Multer {
|
||||||
|
return multer({
|
||||||
|
storage: multerS3({
|
||||||
|
s3,
|
||||||
|
bucket: config.s3.bucket,
|
||||||
|
contentType: multerS3.AUTO_CONTENT_TYPE,
|
||||||
|
metadata: function (req, file, cb) {
|
||||||
|
cb(null, { fieldName: file.fieldname });
|
||||||
|
},
|
||||||
|
key: function (req, file, cb) {
|
||||||
|
cb(null, Date.now().toString());
|
||||||
|
},
|
||||||
|
acl: function(req, file, cb) {
|
||||||
|
// Conditionally set file to public or private based on isPublic flag
|
||||||
|
const aclValue = true ? 'public-read' : 'private';
|
||||||
|
cb(null, aclValue); // Set ACL based on the isPublic flag
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
122
packages/server-nest/src/modules/Attachments/UnlinkAttachment.ts
Normal file
122
packages/server-nest/src/modules/Attachments/UnlinkAttachment.ts
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import bluebird from 'bluebird';
|
||||||
|
import {
|
||||||
|
validateLinkModelEntryExists,
|
||||||
|
validateLinkModelExists,
|
||||||
|
} from './_utils';
|
||||||
|
import { Knex } from 'knex';
|
||||||
|
import { difference } from 'lodash';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UnlinkAttachment {
|
||||||
|
/**
|
||||||
|
* Unlink the attachments from the model entry.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} filekey
|
||||||
|
* @param {string} modelRef
|
||||||
|
* @param {number} modelId
|
||||||
|
*/
|
||||||
|
async unlink(
|
||||||
|
tenantId: number,
|
||||||
|
filekey: string,
|
||||||
|
modelRef: string,
|
||||||
|
modelId: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
): Promise<void> {
|
||||||
|
const { DocumentLink, Document, ...models } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const LinkModel = models[modelRef];
|
||||||
|
validateLinkModelExists(LinkModel);
|
||||||
|
|
||||||
|
const foundLinkModel = await LinkModel.query(trx).findById(modelId);
|
||||||
|
validateLinkModelEntryExists(foundLinkModel);
|
||||||
|
|
||||||
|
const document = await Document.query(trx).findOne('key', filekey);
|
||||||
|
|
||||||
|
// Delete the document link.
|
||||||
|
await DocumentLink.query(trx)
|
||||||
|
.where('modelRef', modelRef)
|
||||||
|
.where('modelId', modelId)
|
||||||
|
.where('documentId', document.id)
|
||||||
|
.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bulk unlink the attachments from the model entry.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} fieldkey
|
||||||
|
* @param {string} modelRef
|
||||||
|
* @param {number} modelId
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async bulkUnlink(
|
||||||
|
tenantId: number,
|
||||||
|
filekeys: string[],
|
||||||
|
modelRef: string,
|
||||||
|
modelId: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
): Promise<void> {
|
||||||
|
await bluebird.each(filekeys, (fieldKey: string) => {
|
||||||
|
try {
|
||||||
|
this.unlink(tenantId, fieldKey, modelRef, modelId, trx);
|
||||||
|
} catch {
|
||||||
|
// Ignore catching exceptions on bulk action.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all the unpresented keys of the given model type and id.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string[]} presentedKeys
|
||||||
|
* @param {string} modelRef
|
||||||
|
* @param {number} modelId
|
||||||
|
* @param {Knex.Transaction} trx
|
||||||
|
*/
|
||||||
|
async unlinkUnpresentedKeys(
|
||||||
|
tenantId: number,
|
||||||
|
presentedKeys: string[],
|
||||||
|
modelRef: string,
|
||||||
|
modelId: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
): Promise<void> {
|
||||||
|
const { DocumentLink } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
const modelLinks = await DocumentLink.query(trx)
|
||||||
|
.where('modelRef', modelRef)
|
||||||
|
.where('modelId', modelId)
|
||||||
|
.withGraphFetched('document');
|
||||||
|
|
||||||
|
const modelLinkKeys = modelLinks.map((link) => link.document.key);
|
||||||
|
const unpresentedKeys = difference(modelLinkKeys, presentedKeys);
|
||||||
|
|
||||||
|
await this.bulkUnlink(tenantId, unpresentedKeys, modelRef, modelId, trx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments of the given model type and id.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string} modelRef
|
||||||
|
* @param {number} modelId
|
||||||
|
* @param {Knex.Transaction} trx
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async unlinkAllModelKeys(
|
||||||
|
tenantId: number,
|
||||||
|
modelRef: string,
|
||||||
|
modelId: number,
|
||||||
|
trx?: Knex.Transaction
|
||||||
|
): Promise<void> {
|
||||||
|
const { DocumentLink } = this.tenancy.models(tenantId);
|
||||||
|
|
||||||
|
// Get all the keys of the modelRef and modelId.
|
||||||
|
const modelLinks = await DocumentLink.query(trx)
|
||||||
|
.where('modelRef', modelRef)
|
||||||
|
.where('modelId', modelId)
|
||||||
|
.withGraphFetched('document');
|
||||||
|
|
||||||
|
const modelLinkKeys = modelLinks.map((link) => link.document.key);
|
||||||
|
|
||||||
|
await this.bulkUnlink(tenantId, modelLinkKeys, modelRef, modelId, trx);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { TenancyDatabaseModule } from '../Tenancy/TenancyDB/TenancyDB.module';
|
||||||
|
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||||
|
import { DocumentModel } from './models/Document.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UploadDocument {
|
||||||
|
constructor(
|
||||||
|
@Inject(DocumentModel.name)
|
||||||
|
private readonly documentModel: TenantModelProxy<typeof DocumentModel>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts the document metadata.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {} file
|
||||||
|
* @returns {}
|
||||||
|
*/
|
||||||
|
async upload(file: any) {
|
||||||
|
const insertedDocument = await this.documentModel().query().insert({
|
||||||
|
key: file.key,
|
||||||
|
mimeType: file.mimetype,
|
||||||
|
size: file.size,
|
||||||
|
originName: file.originalname,
|
||||||
|
});
|
||||||
|
return insertedDocument;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { castArray, difference } from 'lodash';
|
||||||
|
import HasTenancyService from '../Tenancy/TenancyService';
|
||||||
|
import { ServiceError } from '@/exceptions';
|
||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||||
|
import { DocumentModel } from './models/Document.model';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class ValidateAttachments {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly documentModel: TenantModelProxy<typeof DocumentModel>
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Validates the given file keys existance.
|
||||||
|
* @param {number} tenantId
|
||||||
|
* @param {string|string[]} key
|
||||||
|
*/
|
||||||
|
async validate(tenantId: number, key: string | string[]) {
|
||||||
|
const keys = castArray(key);
|
||||||
|
const documents = await this.documentModel().query().whereIn('key', key);
|
||||||
|
const documentKeys = documents.map((document) => document.key);
|
||||||
|
|
||||||
|
const notFoundKeys = difference(keys, documentKeys);
|
||||||
|
|
||||||
|
if (notFoundKeys.length > 0) {
|
||||||
|
throw new ServiceError('DOCUMENT_KEYS_INVALID');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
packages/server-nest/src/modules/Attachments/_utils.ts
Normal file
14
packages/server-nest/src/modules/Attachments/_utils.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { ServiceError } from '@/exceptions';
|
||||||
|
import { ERRORS } from './constants';
|
||||||
|
|
||||||
|
export const validateLinkModelExists = (LinkModel) => {
|
||||||
|
if (!LinkModel) {
|
||||||
|
throw new ServiceError(ERRORS.DOCUMENT_LINK_REF_INVALID);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const validateLinkModelEntryExists = (foundLinkModel) => {
|
||||||
|
if (!foundLinkModel) {
|
||||||
|
throw new ServiceError(ERRORS.DOCUMENT_LINK_ID_INVALID);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export enum ERRORS {
|
||||||
|
DOCUMENT_LINK_REF_INVALID = 'DOCUMENT_LINK_REF_INVALID',
|
||||||
|
DOCUMENT_LINK_ID_INVALID = 'DOCUMENT_LINK_ID_INVALID',
|
||||||
|
DOCUMENT_LINK_ALREADY_LINKED = 'DOCUMENT_LINK_ALREADY_LINKED'
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
IBIllEventDeletedPayload,
|
||||||
|
IBillCreatedPayload,
|
||||||
|
IBillCreatingPayload,
|
||||||
|
IBillEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentsOnBills {
|
||||||
|
@Inject()
|
||||||
|
private linkAttachmentService: LinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unlinkAttachmentService: UnlinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private validateDocuments: ValidateAttachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.bill.onCreating,
|
||||||
|
this.validateAttachmentsOnBillCreate.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.bill.onCreated,
|
||||||
|
this.handleAttachmentsOnBillCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.bill.onEdited,
|
||||||
|
this.handleUnlinkUnpresentedKeysOnBillEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.bill.onEdited,
|
||||||
|
this.handleLinkPresentedKeysOnBillEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.bill.onDeleting,
|
||||||
|
this.handleUnlinkAttachmentsOnBillDeleted.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the attachment keys on creating bill.
|
||||||
|
* @param {ISaleInvoiceCreatingPaylaod}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateAttachmentsOnBillCreate({
|
||||||
|
billDTO,
|
||||||
|
tenantId,
|
||||||
|
}: IBillCreatingPayload): Promise<void> {
|
||||||
|
if (isEmpty(billDTO.attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const documentKeys = billDTO?.attachments?.map((a) => a.key);
|
||||||
|
|
||||||
|
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking the attachments of the created bill.
|
||||||
|
* @param {ISaleInvoiceCreatedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleAttachmentsOnBillCreated({
|
||||||
|
tenantId,
|
||||||
|
bill,
|
||||||
|
billDTO,
|
||||||
|
trx,
|
||||||
|
}: IBillCreatedPayload): Promise<void> {
|
||||||
|
if (isEmpty(billDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'Bill',
|
||||||
|
bill.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unlinking all the unpresented keys of the edited bill.
|
||||||
|
* @param {IBillEditedPayload}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkUnpresentedKeysOnBillEdited({
|
||||||
|
tenantId,
|
||||||
|
billDTO,
|
||||||
|
bill,
|
||||||
|
trx
|
||||||
|
}: IBillEditedPayload) {
|
||||||
|
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'Bill',
|
||||||
|
bill.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking all the presented keys of the edited bill.
|
||||||
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleLinkPresentedKeysOnBillEdited({
|
||||||
|
tenantId,
|
||||||
|
billDTO,
|
||||||
|
oldBill,
|
||||||
|
trx,
|
||||||
|
}: IBillEditedPayload) {
|
||||||
|
if (isEmpty(billDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = billDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'Bill',
|
||||||
|
oldBill.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments once the bill deleted.
|
||||||
|
* @param {ISaleInvoiceDeletedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkAttachmentsOnBillDeleted({
|
||||||
|
tenantId,
|
||||||
|
oldBill,
|
||||||
|
trx,
|
||||||
|
}: IBIllEventDeletedPayload) {
|
||||||
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
|
tenantId,
|
||||||
|
'Bill',
|
||||||
|
oldBill.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
ICreditNoteCreatedPayload,
|
||||||
|
ICreditNoteCreatingPayload,
|
||||||
|
ICreditNoteDeletingPayload,
|
||||||
|
ICreditNoteEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentsOnCreditNote {
|
||||||
|
@Inject()
|
||||||
|
private linkAttachmentService: LinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unlinkAttachmentService: UnlinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private validateDocuments: ValidateAttachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onCreating,
|
||||||
|
this.validateAttachmentsOnCreditNoteCreate.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onCreated,
|
||||||
|
this.handleAttachmentsOnCreditNoteCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onEdited,
|
||||||
|
this.handleUnlinkUnpresentedKeysOnCreditNoteEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onEdited,
|
||||||
|
this.handleLinkPresentedKeysOnCreditNoteEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.creditNote.onDeleting,
|
||||||
|
this.handleUnlinkAttachmentsOnCreditNoteDeleted.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the attachment keys on creating credit note.
|
||||||
|
* @param {ICreditNoteCreatingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateAttachmentsOnCreditNoteCreate({
|
||||||
|
creditNoteDTO,
|
||||||
|
tenantId,
|
||||||
|
}: ICreditNoteCreatingPayload): Promise<void> {
|
||||||
|
if (isEmpty(creditNoteDTO.attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const documentKeys = creditNoteDTO?.attachments?.map((a) => a.key);
|
||||||
|
|
||||||
|
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking the attachments of the created credit note.
|
||||||
|
* @param {ICreditNoteCreatedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleAttachmentsOnCreditNoteCreated({
|
||||||
|
tenantId,
|
||||||
|
creditNote,
|
||||||
|
creditNoteDTO,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteCreatedPayload): Promise<void> {
|
||||||
|
if (isEmpty(creditNoteDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = creditNoteDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'CreditNote',
|
||||||
|
creditNote.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unlinking all the unpresented keys of the edited credit note.
|
||||||
|
* @param {ICreditNoteEditedPayload}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkUnpresentedKeysOnCreditNoteEdited({
|
||||||
|
tenantId,
|
||||||
|
creditNoteEditDTO,
|
||||||
|
oldCreditNote,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteEditedPayload) {
|
||||||
|
const keys = creditNoteEditDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'CreditNote',
|
||||||
|
oldCreditNote.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking all the presented keys of the edited credit note.
|
||||||
|
* @param {ICreditNoteEditedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleLinkPresentedKeysOnCreditNoteEdited({
|
||||||
|
tenantId,
|
||||||
|
creditNoteEditDTO,
|
||||||
|
oldCreditNote,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteEditedPayload) {
|
||||||
|
if (isEmpty(creditNoteEditDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = creditNoteEditDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'CreditNote',
|
||||||
|
oldCreditNote.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments once the credit note deleted.
|
||||||
|
* @param {ICreditNoteDeletingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkAttachmentsOnCreditNoteDeleted({
|
||||||
|
tenantId,
|
||||||
|
oldCreditNote,
|
||||||
|
trx,
|
||||||
|
}: ICreditNoteDeletingPayload) {
|
||||||
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
|
tenantId,
|
||||||
|
'CreditNote',
|
||||||
|
oldCreditNote.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
IExpenseCreatedPayload,
|
||||||
|
IExpenseCreatingPayload,
|
||||||
|
IExpenseDeletingPayload,
|
||||||
|
IExpenseEventEditPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentsOnExpenses {
|
||||||
|
@Inject()
|
||||||
|
private linkAttachmentService: LinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unlinkAttachmentService: UnlinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private validateDocuments: ValidateAttachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.expenses.onCreating,
|
||||||
|
this.validateAttachmentsOnExpenseCreate.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.expenses.onCreated,
|
||||||
|
this.handleAttachmentsOnExpenseCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.expenses.onEdited,
|
||||||
|
this.handleUnlinkUnpresentedKeysOnExpenseEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.expenses.onEdited,
|
||||||
|
this.handleLinkPresentedKeysOnExpenseEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.expenses.onDeleting,
|
||||||
|
this.handleUnlinkAttachmentsOnExpenseDeleted.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the attachment keys on creating expense.
|
||||||
|
* @param {ISaleInvoiceCreatingPaylaod}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateAttachmentsOnExpenseCreate({
|
||||||
|
expenseDTO,
|
||||||
|
tenantId,
|
||||||
|
}: IExpenseCreatingPayload): Promise<void> {
|
||||||
|
if (isEmpty(expenseDTO.attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const documentKeys = expenseDTO?.attachments?.map((a) => a.key);
|
||||||
|
|
||||||
|
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking the attachments of the created expense.
|
||||||
|
* @param {ISaleInvoiceCreatedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleAttachmentsOnExpenseCreated({
|
||||||
|
tenantId,
|
||||||
|
expenseDTO,
|
||||||
|
expense,
|
||||||
|
trx,
|
||||||
|
}: IExpenseCreatedPayload): Promise<void> {
|
||||||
|
if (isEmpty(expenseDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'Expense',
|
||||||
|
expense.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unlinking all the unpresented keys of the edited expense.
|
||||||
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkUnpresentedKeysOnExpenseEdited({
|
||||||
|
tenantId,
|
||||||
|
expenseDTO,
|
||||||
|
expense,
|
||||||
|
trx,
|
||||||
|
}: IExpenseEventEditPayload) {
|
||||||
|
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'Expense',
|
||||||
|
expense.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking all the presented keys of the edited expense.
|
||||||
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleLinkPresentedKeysOnExpenseEdited({
|
||||||
|
tenantId,
|
||||||
|
expenseDTO,
|
||||||
|
oldExpense,
|
||||||
|
trx,
|
||||||
|
}: IExpenseEventEditPayload) {
|
||||||
|
if (isEmpty(expenseDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = expenseDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'Expense',
|
||||||
|
oldExpense.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments once the expense deleted.
|
||||||
|
* @param {ISaleInvoiceDeletedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkAttachmentsOnExpenseDeleted({
|
||||||
|
tenantId,
|
||||||
|
oldExpense,
|
||||||
|
trx,
|
||||||
|
}: IExpenseDeletingPayload) {
|
||||||
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
|
tenantId,
|
||||||
|
'Expense',
|
||||||
|
oldExpense.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
IManualJournalCreatingPayload,
|
||||||
|
IManualJournalEventCreatedPayload,
|
||||||
|
IManualJournalEventDeletedPayload,
|
||||||
|
IManualJournalEventEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentsOnManualJournals {
|
||||||
|
@Inject()
|
||||||
|
private linkAttachmentService: LinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unlinkAttachmentService: UnlinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private validateDocuments: ValidateAttachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.manualJournals.onCreating,
|
||||||
|
this.validateAttachmentsOnManualJournalCreate.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.manualJournals.onCreated,
|
||||||
|
this.handleAttachmentsOnManualJournalCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.manualJournals.onEdited,
|
||||||
|
this.handleUnlinkUnpresentedKeysOnManualJournalEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.manualJournals.onEdited,
|
||||||
|
this.handleLinkPresentedKeysOnManualJournalEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.manualJournals.onDeleting,
|
||||||
|
this.handleUnlinkAttachmentsOnManualJournalDeleted.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the attachment keys on creating manual journal.
|
||||||
|
* @param {IManualJournalCreatingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateAttachmentsOnManualJournalCreate({
|
||||||
|
manualJournalDTO,
|
||||||
|
tenantId,
|
||||||
|
}: IManualJournalCreatingPayload): Promise<void> {
|
||||||
|
if (isEmpty(manualJournalDTO.attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const documentKeys = manualJournalDTO?.attachments?.map((a) => a.key);
|
||||||
|
|
||||||
|
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking the attachments of the created manual journal.
|
||||||
|
* @param {IManualJournalEventCreatedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleAttachmentsOnManualJournalCreated({
|
||||||
|
tenantId,
|
||||||
|
manualJournalDTO,
|
||||||
|
manualJournal,
|
||||||
|
trx,
|
||||||
|
}: IManualJournalEventCreatedPayload): Promise<void> {
|
||||||
|
if (isEmpty(manualJournalDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = manualJournalDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'ManualJournal',
|
||||||
|
manualJournal.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unlinking all the unpresented keys of the edited manual journal.
|
||||||
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkUnpresentedKeysOnManualJournalEdited({
|
||||||
|
tenantId,
|
||||||
|
manualJournalDTO,
|
||||||
|
manualJournal,
|
||||||
|
trx
|
||||||
|
}: IManualJournalEventEditedPayload) {
|
||||||
|
const keys = manualJournalDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'SaleInvoice',
|
||||||
|
manualJournal.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking all the presented keys of the edited manual journal.
|
||||||
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleLinkPresentedKeysOnManualJournalEdited({
|
||||||
|
tenantId,
|
||||||
|
manualJournalDTO,
|
||||||
|
oldManualJournal,
|
||||||
|
trx,
|
||||||
|
}: IManualJournalEventEditedPayload) {
|
||||||
|
if (isEmpty(manualJournalDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = manualJournalDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'ManualJournal',
|
||||||
|
oldManualJournal.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments once the manual journal deleted.
|
||||||
|
* @param {ISaleInvoiceDeletedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkAttachmentsOnManualJournalDeleted({
|
||||||
|
tenantId,
|
||||||
|
oldManualJournal,
|
||||||
|
trx,
|
||||||
|
}: IManualJournalEventDeletedPayload) {
|
||||||
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
|
tenantId,
|
||||||
|
'SaleInvoice',
|
||||||
|
oldManualJournal.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
IBillPaymentCreatingPayload,
|
||||||
|
IBillPaymentDeletingPayload,
|
||||||
|
IBillPaymentEventCreatedPayload,
|
||||||
|
IBillPaymentEventEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentsOnBillPayments {
|
||||||
|
@Inject()
|
||||||
|
private linkAttachmentService: LinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unlinkAttachmentService: UnlinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private validateDocuments: ValidateAttachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.billPayment.onCreating,
|
||||||
|
this.validateAttachmentsOnBillPaymentCreate.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.billPayment.onCreated,
|
||||||
|
this.handleAttachmentsOnBillPaymentCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.billPayment.onEdited,
|
||||||
|
this.handleUnlinkUnpresentedKeysOnBillPaymentEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.billPayment.onEdited,
|
||||||
|
this.handleLinkPresentedKeysOnBillPaymentEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.billPayment.onDeleting,
|
||||||
|
this.handleUnlinkAttachmentsOnBillPaymentDeleted.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the attachment keys on creating bill payment.
|
||||||
|
* @param {IBillPaymentCreatingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateAttachmentsOnBillPaymentCreate({
|
||||||
|
billPaymentDTO,
|
||||||
|
tenantId,
|
||||||
|
}: IBillPaymentCreatingPayload): Promise<void> {
|
||||||
|
if (isEmpty(billPaymentDTO.attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const documentKeys = billPaymentDTO?.attachments?.map((a) => a.key);
|
||||||
|
|
||||||
|
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking the attachments of the created bill payment.
|
||||||
|
* @param {IBillPaymentEventCreatedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleAttachmentsOnBillPaymentCreated({
|
||||||
|
tenantId,
|
||||||
|
billPaymentDTO,
|
||||||
|
billPayment,
|
||||||
|
trx,
|
||||||
|
}: IBillPaymentEventCreatedPayload): Promise<void> {
|
||||||
|
if (isEmpty(billPaymentDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = billPaymentDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'BillPayment',
|
||||||
|
billPayment.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unlinking all the unpresented keys of the edited bill payment.
|
||||||
|
* @param {IBillPaymentEventEditedPayload}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkUnpresentedKeysOnBillPaymentEdited({
|
||||||
|
tenantId,
|
||||||
|
billPaymentDTO,
|
||||||
|
oldBillPayment,
|
||||||
|
trx,
|
||||||
|
}: IBillPaymentEventEditedPayload) {
|
||||||
|
const keys = billPaymentDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'BillPayment',
|
||||||
|
oldBillPayment.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking all the presented keys of the edited bill payment.
|
||||||
|
* @param {IBillPaymentEventEditedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleLinkPresentedKeysOnBillPaymentEdited({
|
||||||
|
tenantId,
|
||||||
|
billPaymentDTO,
|
||||||
|
oldBillPayment,
|
||||||
|
trx,
|
||||||
|
}: IBillPaymentEventEditedPayload) {
|
||||||
|
if (isEmpty(billPaymentDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = billPaymentDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'BillPayment',
|
||||||
|
oldBillPayment.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments once the bill payment deleted.
|
||||||
|
* @param {IBillPaymentDeletingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkAttachmentsOnBillPaymentDeleted({
|
||||||
|
tenantId,
|
||||||
|
oldBillPayment,
|
||||||
|
trx,
|
||||||
|
}: IBillPaymentDeletingPayload) {
|
||||||
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
|
tenantId,
|
||||||
|
'BillPayment',
|
||||||
|
oldBillPayment.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
IPaymentReceivedCreatedPayload,
|
||||||
|
IPaymentReceivedCreatingPayload,
|
||||||
|
IPaymentReceivedDeletingPayload,
|
||||||
|
IPaymentReceivedEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentsOnPaymentsReceived {
|
||||||
|
@Inject()
|
||||||
|
private linkAttachmentService: LinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unlinkAttachmentService: UnlinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private validateDocuments: ValidateAttachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.paymentReceive.onCreating,
|
||||||
|
this.validateAttachmentsOnPaymentCreate.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.paymentReceive.onCreated,
|
||||||
|
this.handleAttachmentsOnPaymentCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.paymentReceive.onEdited,
|
||||||
|
this.handleUnlinkUnpresentedKeysOnPaymentEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.paymentReceive.onEdited,
|
||||||
|
this.handleLinkPresentedKeysOnPaymentEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.paymentReceive.onDeleting,
|
||||||
|
this.handleUnlinkAttachmentsOnPaymentDelete.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the attachment keys on creating payment.
|
||||||
|
* @param {IPaymentReceivedCreatingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateAttachmentsOnPaymentCreate({
|
||||||
|
paymentReceiveDTO,
|
||||||
|
tenantId,
|
||||||
|
}: IPaymentReceivedCreatingPayload): Promise<void> {
|
||||||
|
if (isEmpty(paymentReceiveDTO.attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const documentKeys = paymentReceiveDTO?.attachments?.map((a) => a.key);
|
||||||
|
|
||||||
|
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking the attachments of the created payment.
|
||||||
|
* @param {IPaymentReceivedCreatedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleAttachmentsOnPaymentCreated({
|
||||||
|
tenantId,
|
||||||
|
paymentReceiveDTO,
|
||||||
|
paymentReceive,
|
||||||
|
trx,
|
||||||
|
}: IPaymentReceivedCreatedPayload): Promise<void> {
|
||||||
|
if (isEmpty(paymentReceiveDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = paymentReceiveDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'PaymentReceive',
|
||||||
|
paymentReceive.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unlinking all the unpresented keys of the edited payment.
|
||||||
|
* @param {IPaymentReceivedEditedPayload}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkUnpresentedKeysOnPaymentEdited({
|
||||||
|
tenantId,
|
||||||
|
paymentReceiveDTO,
|
||||||
|
oldPaymentReceive,
|
||||||
|
trx,
|
||||||
|
}: IPaymentReceivedEditedPayload) {
|
||||||
|
const keys = paymentReceiveDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'PaymentReceive',
|
||||||
|
oldPaymentReceive.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking all the presented keys of the edited payment.
|
||||||
|
* @param {IPaymentReceivedEditedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleLinkPresentedKeysOnPaymentEdited({
|
||||||
|
tenantId,
|
||||||
|
paymentReceiveDTO,
|
||||||
|
oldPaymentReceive,
|
||||||
|
trx,
|
||||||
|
}: IPaymentReceivedEditedPayload) {
|
||||||
|
if (isEmpty(paymentReceiveDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = paymentReceiveDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'PaymentReceive',
|
||||||
|
oldPaymentReceive.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments once the payment deleted.
|
||||||
|
* @param {ISaleInvoiceDeletedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkAttachmentsOnPaymentDelete({
|
||||||
|
tenantId,
|
||||||
|
oldPaymentReceive,
|
||||||
|
trx,
|
||||||
|
}: IPaymentReceivedDeletingPayload) {
|
||||||
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
|
tenantId,
|
||||||
|
'PaymentReceive',
|
||||||
|
oldPaymentReceive.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
ISaleEstimateCreatedPayload,
|
||||||
|
ISaleEstimateCreatingPayload,
|
||||||
|
ISaleEstimateDeletingPayload,
|
||||||
|
ISaleEstimateEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentsOnSaleEstimates {
|
||||||
|
@Inject()
|
||||||
|
private linkAttachmentService: LinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unlinkAttachmentService: UnlinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private validateDocuments: ValidateAttachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleEstimate.onCreating,
|
||||||
|
this.validateAttachmentsOnSaleEstimateCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleEstimate.onCreated,
|
||||||
|
this.handleAttachmentsOnSaleEstimateCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleEstimate.onEdited,
|
||||||
|
this.handleUnlinkUnpresentedKeysOnSaleEstimateEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleEstimate.onEdited,
|
||||||
|
this.handleLinkPresentedKeysOnSaleEstimateEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleEstimate.onDeleting,
|
||||||
|
this.handleUnlinkAttachmentsOnSaleEstimateDelete.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the attachment keys on creating sale estimate.
|
||||||
|
* @param {ISaleEstimateCreatingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateAttachmentsOnSaleEstimateCreated({
|
||||||
|
estimateDTO,
|
||||||
|
tenantId,
|
||||||
|
}: ISaleEstimateCreatingPayload): Promise<void> {
|
||||||
|
if (isEmpty(estimateDTO.attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const documentKeys = estimateDTO?.attachments?.map((a) => a.key);
|
||||||
|
|
||||||
|
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking the attachments of the created sale estimate.
|
||||||
|
* @param {ISaleEstimateCreatedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleAttachmentsOnSaleEstimateCreated({
|
||||||
|
tenantId,
|
||||||
|
saleEstimateDTO,
|
||||||
|
saleEstimate,
|
||||||
|
trx,
|
||||||
|
}: ISaleEstimateCreatedPayload): Promise<void> {
|
||||||
|
if (isEmpty(saleEstimateDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = saleEstimateDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'SaleEstimate',
|
||||||
|
saleEstimate.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unlinking all the unpresented keys of the edited sale estimate.
|
||||||
|
* @param {ISaleEstimateEditedPayload}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkUnpresentedKeysOnSaleEstimateEdited({
|
||||||
|
tenantId,
|
||||||
|
estimateDTO,
|
||||||
|
oldSaleEstimate,
|
||||||
|
trx
|
||||||
|
}: ISaleEstimateEditedPayload) {
|
||||||
|
const keys = estimateDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
|
||||||
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'SaleEstimate',
|
||||||
|
oldSaleEstimate.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking all the presented keys of the edited sale estimate.
|
||||||
|
* @param {ISaleEstimateEditedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleLinkPresentedKeysOnSaleEstimateEdited({
|
||||||
|
tenantId,
|
||||||
|
estimateDTO,
|
||||||
|
oldSaleEstimate,
|
||||||
|
trx,
|
||||||
|
}: ISaleEstimateEditedPayload) {
|
||||||
|
if (isEmpty(estimateDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = estimateDTO.attachments?.map((attachment) => attachment.key);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'SaleEstimate',
|
||||||
|
oldSaleEstimate.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments once the estimate deleted.
|
||||||
|
* @param {ISaleEstimateDeletingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkAttachmentsOnSaleEstimateDelete({
|
||||||
|
tenantId,
|
||||||
|
oldSaleEstimate,
|
||||||
|
trx,
|
||||||
|
}: ISaleEstimateDeletingPayload) {
|
||||||
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
|
tenantId,
|
||||||
|
'SaleEstimate',
|
||||||
|
oldSaleEstimate.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
ISaleInvoiceCreatedPayload,
|
||||||
|
ISaleInvoiceCreatingPaylaod,
|
||||||
|
ISaleInvoiceDeletingPayload,
|
||||||
|
ISaleInvoiceEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentsOnSaleInvoiceCreated {
|
||||||
|
@Inject()
|
||||||
|
private linkAttachmentService: LinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unlinkAttachmentService: UnlinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private validateDocuments: ValidateAttachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleInvoice.onCreating,
|
||||||
|
this.validateAttachmentsOnSaleInvoiceCreate.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleInvoice.onCreated,
|
||||||
|
this.handleAttachmentsOnSaleInvoiceCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleInvoice.onEdited,
|
||||||
|
this.handleUnlinkUnpresentedKeysOnInvoiceEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleInvoice.onEdited,
|
||||||
|
this.handleLinkPresentedKeysOnInvoiceEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleInvoice.onDeleting,
|
||||||
|
this.handleUnlinkAttachmentsOnInvoiceDeleted.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the attachment keys on creating sale invoice.
|
||||||
|
* @param {ISaleInvoiceCreatingPaylaod}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateAttachmentsOnSaleInvoiceCreate({
|
||||||
|
saleInvoiceDTO,
|
||||||
|
tenantId,
|
||||||
|
}: ISaleInvoiceCreatingPaylaod): Promise<void> {
|
||||||
|
if (isEmpty(saleInvoiceDTO.attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const documentKeys = saleInvoiceDTO?.attachments?.map((a) => a.key);
|
||||||
|
|
||||||
|
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking the attachments of the created sale invoice.
|
||||||
|
* @param {ISaleInvoiceCreatedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleAttachmentsOnSaleInvoiceCreated({
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceDTO,
|
||||||
|
saleInvoice,
|
||||||
|
trx,
|
||||||
|
}: ISaleInvoiceCreatedPayload): Promise<void> {
|
||||||
|
if (isEmpty(saleInvoiceDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = saleInvoiceDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'SaleInvoice',
|
||||||
|
saleInvoice.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unlinking all the unpresented keys of the edited sale invoice.
|
||||||
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkUnpresentedKeysOnInvoiceEdited({
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceDTO,
|
||||||
|
saleInvoice,
|
||||||
|
trx,
|
||||||
|
}: ISaleInvoiceEditedPayload) {
|
||||||
|
// if (isEmpty(saleInvoiceDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = saleInvoiceDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'SaleInvoice',
|
||||||
|
saleInvoice.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking all the presented keys of the edited sale invoice.
|
||||||
|
* @param {ISaleInvoiceEditedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleLinkPresentedKeysOnInvoiceEdited({
|
||||||
|
tenantId,
|
||||||
|
saleInvoiceDTO,
|
||||||
|
oldSaleInvoice,
|
||||||
|
trx,
|
||||||
|
}: ISaleInvoiceEditedPayload) {
|
||||||
|
if (isEmpty(saleInvoiceDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = saleInvoiceDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'SaleInvoice',
|
||||||
|
oldSaleInvoice.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments once the invoice deleted.
|
||||||
|
* @param {ISaleInvoiceDeletedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkAttachmentsOnInvoiceDeleted({
|
||||||
|
tenantId,
|
||||||
|
oldSaleInvoice,
|
||||||
|
trx,
|
||||||
|
}: ISaleInvoiceDeletingPayload) {
|
||||||
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
|
tenantId,
|
||||||
|
'SaleInvoice',
|
||||||
|
oldSaleInvoice.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
ISaleReceiptCreatedPayload,
|
||||||
|
ISaleReceiptCreatingPayload,
|
||||||
|
ISaleReceiptDeletingPayload,
|
||||||
|
ISaleReceiptEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentsOnSaleReceipt {
|
||||||
|
@Inject()
|
||||||
|
private linkAttachmentService: LinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unlinkAttachmentService: UnlinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private validateDocuments: ValidateAttachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleReceipt.onCreating,
|
||||||
|
this.validateAttachmentsOnSaleInvoiceCreate.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleReceipt.onCreated,
|
||||||
|
this.handleAttachmentsOnSaleInvoiceCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleReceipt.onEdited,
|
||||||
|
this.handleUnlinkUnpresentedKeysOnInvoiceEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleReceipt.onEdited,
|
||||||
|
this.handleLinkPresentedKeysOnInvoiceEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.saleReceipt.onDeleting,
|
||||||
|
this.handleUnlinkAttachmentsOnReceiptDeleted.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the attachment keys on creating sale receipt.
|
||||||
|
* @param {ISaleReceiptCreatingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateAttachmentsOnSaleInvoiceCreate({
|
||||||
|
saleReceiptDTO,
|
||||||
|
tenantId,
|
||||||
|
}: ISaleReceiptCreatingPayload): Promise<void> {
|
||||||
|
if (isEmpty(saleReceiptDTO.attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const documentKeys = saleReceiptDTO?.attachments?.map((a) => a.key);
|
||||||
|
|
||||||
|
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking the attachments of the created sale receipt.
|
||||||
|
* @param {ISaleReceiptCreatedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleAttachmentsOnSaleInvoiceCreated({
|
||||||
|
tenantId,
|
||||||
|
saleReceiptDTO,
|
||||||
|
saleReceipt,
|
||||||
|
trx,
|
||||||
|
}: ISaleReceiptCreatedPayload): Promise<void> {
|
||||||
|
if (isEmpty(saleReceiptDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = saleReceiptDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'SaleReceipt',
|
||||||
|
saleReceipt.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unlinking all the unpresented keys of the edited sale receipt.
|
||||||
|
* @param {ISaleReceiptEditedPayload}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkUnpresentedKeysOnInvoiceEdited({
|
||||||
|
tenantId,
|
||||||
|
saleReceiptDTO,
|
||||||
|
saleReceipt,
|
||||||
|
trx,
|
||||||
|
}: ISaleReceiptEditedPayload) {
|
||||||
|
const keys = saleReceiptDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'SaleReceipt',
|
||||||
|
saleReceipt.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking all the presented keys of the edited sale receipt.
|
||||||
|
* @param {ISaleReceiptEditedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleLinkPresentedKeysOnInvoiceEdited({
|
||||||
|
tenantId,
|
||||||
|
saleReceiptDTO,
|
||||||
|
oldSaleReceipt,
|
||||||
|
trx,
|
||||||
|
}: ISaleReceiptEditedPayload) {
|
||||||
|
if (isEmpty(saleReceiptDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = saleReceiptDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'SaleReceipt',
|
||||||
|
oldSaleReceipt.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments once the receipt deleted.
|
||||||
|
* @param {ISaleReceiptDeletingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkAttachmentsOnReceiptDeleted({
|
||||||
|
tenantId,
|
||||||
|
oldSaleReceipt,
|
||||||
|
trx,
|
||||||
|
}: ISaleReceiptDeletingPayload) {
|
||||||
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
|
tenantId,
|
||||||
|
'SaleReceipt',
|
||||||
|
oldSaleReceipt.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
import { Inject, Service } from 'typedi';
|
||||||
|
import { isEmpty } from 'lodash';
|
||||||
|
import {
|
||||||
|
IVendorCreditCreatedPayload,
|
||||||
|
IVendorCreditCreatingPayload,
|
||||||
|
IVendorCreditDeletingPayload,
|
||||||
|
IVendorCreditEditedPayload,
|
||||||
|
} from '@/interfaces';
|
||||||
|
import events from '@/subscribers/events';
|
||||||
|
import { LinkAttachment } from '../LinkAttachment';
|
||||||
|
import { ValidateAttachments } from '../ValidateAttachments';
|
||||||
|
import { UnlinkAttachment } from '../UnlinkAttachment';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class AttachmentsOnVendorCredits {
|
||||||
|
@Inject()
|
||||||
|
private linkAttachmentService: LinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private unlinkAttachmentService: UnlinkAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private validateDocuments: ValidateAttachments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method.
|
||||||
|
*/
|
||||||
|
public attach(bus) {
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onCreating,
|
||||||
|
this.validateAttachmentsOnVendorCreditCreate.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onCreated,
|
||||||
|
this.handleAttachmentsOnVendorCreditCreated.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onEdited,
|
||||||
|
this.handleUnlinkUnpresentedKeysOnVendorCreditEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onEdited,
|
||||||
|
this.handleLinkPresentedKeysOnVendorCreditEdited.bind(this)
|
||||||
|
);
|
||||||
|
bus.subscribe(
|
||||||
|
events.vendorCredit.onDeleting,
|
||||||
|
this.handleUnlinkAttachmentsOnVendorCreditDeleted.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates the attachment keys on creating vendor credit.
|
||||||
|
* @param {IVendorCreditCreatingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async validateAttachmentsOnVendorCreditCreate({
|
||||||
|
vendorCreditCreateDTO,
|
||||||
|
tenantId,
|
||||||
|
}: IVendorCreditCreatingPayload): Promise<void> {
|
||||||
|
if (isEmpty(vendorCreditCreateDTO.attachments)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const documentKeys = vendorCreditCreateDTO?.attachments?.map((a) => a.key);
|
||||||
|
|
||||||
|
await this.validateDocuments.validate(tenantId, documentKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking the attachments of the created vendor credit.
|
||||||
|
* @param {IVendorCreditCreatedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleAttachmentsOnVendorCreditCreated({
|
||||||
|
tenantId,
|
||||||
|
vendorCreditCreateDTO,
|
||||||
|
vendorCredit,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditCreatedPayload): Promise<void> {
|
||||||
|
if (isEmpty(vendorCreditCreateDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = vendorCreditCreateDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'VendorCredit',
|
||||||
|
vendorCredit.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles unlinking all the unpresented keys of the edited vendor credit.
|
||||||
|
* @param {IVendorCreditEditedPayload}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkUnpresentedKeysOnVendorCreditEdited({
|
||||||
|
tenantId,
|
||||||
|
vendorCreditDTO,
|
||||||
|
oldVendorCredit,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditEditedPayload) {
|
||||||
|
const keys = vendorCreditDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.unlinkAttachmentService.unlinkUnpresentedKeys(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'VendorCredit',
|
||||||
|
oldVendorCredit.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles linking all the presented keys of the edited vendor credit.
|
||||||
|
* @param {IVendorCreditEditedPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleLinkPresentedKeysOnVendorCreditEdited({
|
||||||
|
tenantId,
|
||||||
|
vendorCreditDTO,
|
||||||
|
oldVendorCredit,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditEditedPayload) {
|
||||||
|
if (isEmpty(vendorCreditDTO.attachments)) return;
|
||||||
|
|
||||||
|
const keys = vendorCreditDTO.attachments?.map(
|
||||||
|
(attachment) => attachment.key
|
||||||
|
);
|
||||||
|
await this.linkAttachmentService.bulkLink(
|
||||||
|
tenantId,
|
||||||
|
keys,
|
||||||
|
'VendorCredit',
|
||||||
|
oldVendorCredit.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlink all attachments once the vendor credit deleted.
|
||||||
|
* @param {IVendorCreditDeletingPayload}
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
private async handleUnlinkAttachmentsOnVendorCreditDeleted({
|
||||||
|
tenantId,
|
||||||
|
oldVendorCredit,
|
||||||
|
trx,
|
||||||
|
}: IVendorCreditDeletingPayload) {
|
||||||
|
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||||
|
tenantId,
|
||||||
|
'VendorCredit',
|
||||||
|
oldVendorCredit.id,
|
||||||
|
trx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
|
||||||
|
export class DocumentModel extends TenantBaseModel{
|
||||||
|
originName!: string;
|
||||||
|
size!: number;
|
||||||
|
mimeType!: string;
|
||||||
|
key!: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table name
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'documents';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model timestamps.
|
||||||
|
*/
|
||||||
|
get timestamps() {
|
||||||
|
return ['createdAt', 'updatedAt'];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { TenantBaseModel } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { Model, mixin } from 'objection';
|
||||||
|
|
||||||
|
export class DocumentLinkModel extends TenantBaseModel {
|
||||||
|
/**
|
||||||
|
* Table name
|
||||||
|
*/
|
||||||
|
static get tableName() {
|
||||||
|
return 'document_links';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model timestamps.
|
||||||
|
*/
|
||||||
|
get timestamps() {
|
||||||
|
return ['createdAt', 'updatedAt'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relationship mapping.
|
||||||
|
*/
|
||||||
|
static get relationMappings() {
|
||||||
|
const { DocumentModel } = require('./Document');
|
||||||
|
|
||||||
|
return {
|
||||||
|
/**
|
||||||
|
* Sale invoice associated entries.
|
||||||
|
*/
|
||||||
|
document: {
|
||||||
|
relation: Model.HasOneRelation,
|
||||||
|
modelClass: DocumentModel,
|
||||||
|
join: {
|
||||||
|
from: 'document_links.documentId',
|
||||||
|
to: 'documents.id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
9
packages/server-nest/src/modules/Attachments/utils.ts
Normal file
9
packages/server-nest/src/modules/Attachments/utils.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import config from '@/config';
|
||||||
|
|
||||||
|
export const getUploadedObjectUri = (objectKey: string) => {
|
||||||
|
return new URL(
|
||||||
|
path.join(config.s3.bucket, objectKey),
|
||||||
|
config.s3.endpoint
|
||||||
|
).toString();
|
||||||
|
};
|
||||||
29
packages/server-nest/src/modules/S3/S3.module.ts
Normal file
29
packages/server-nest/src/modules/S3/S3.module.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { S3Client } from '@aws-sdk/client-s3';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
|
export const S3_CLIENT = 'S3_CLIENT';
|
||||||
|
|
||||||
|
const services = [
|
||||||
|
{
|
||||||
|
provide: S3_CLIENT,
|
||||||
|
useFactory: (configService: ConfigService) => {
|
||||||
|
const config = configService.get('s3');
|
||||||
|
|
||||||
|
return new S3Client({
|
||||||
|
region: config.region,
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: config.accessKeyId,
|
||||||
|
secretAccessKey: config.secretAccessKey,
|
||||||
|
},
|
||||||
|
endpoint: config.endpoint,
|
||||||
|
forcePathStyle: config.forcePathStyle,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
@Module({
|
||||||
|
providers: [...services],
|
||||||
|
exports: [...services],
|
||||||
|
})
|
||||||
|
export class S3Module {}
|
||||||
Reference in New Issue
Block a user