mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 06:10:31 +00:00
fix: invoice generate sharable link
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
|
import { Knex } from 'knex';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
import { UnitOfWork } from '@/modules/Tenancy/TenancyDB/UnitOfWork.service';
|
||||||
import { CommandItemCategoryValidatorService } from './CommandItemCategoryValidator.service';
|
import { CommandItemCategoryValidatorService } from './CommandItemCategoryValidator.service';
|
||||||
import { ItemCategory } from '../models/ItemCategory.model';
|
import { ItemCategory } from '../models/ItemCategory.model';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
|
||||||
import { Knex } from 'knex';
|
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
||||||
import { events } from '@/common/events/events';
|
import { events } from '@/common/events/events';
|
||||||
import { IItemCategoryDeletedPayload } from '../ItemCategory.interfaces';
|
import { IItemCategoryDeletedPayload } from '../ItemCategory.interfaces';
|
||||||
import { Item } from '@/modules/Items/models/Item';
|
import { Item } from '@/modules/Items/models/Item';
|
||||||
@@ -46,7 +46,10 @@ export class DeleteItemCategoryService {
|
|||||||
await this.unassociateItemsWithCategories(itemCategoryId, trx);
|
await this.unassociateItemsWithCategories(itemCategoryId, trx);
|
||||||
|
|
||||||
// Delete item category.
|
// Delete item category.
|
||||||
await ItemCategory.query(trx).findById(itemCategoryId).delete();
|
await this.itemCategoryModel()
|
||||||
|
.query(trx)
|
||||||
|
.findById(itemCategoryId)
|
||||||
|
.delete();
|
||||||
|
|
||||||
// Triggers `onItemCategoryDeleted` event.
|
// Triggers `onItemCategoryDeleted` event.
|
||||||
await this.eventEmitter.emitAsync(events.itemCategory.onDeleted, {
|
await this.eventEmitter.emitAsync(events.itemCategory.onDeleted, {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
|
import { ClsService } from 'nestjs-cls';
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
import { TenantModelProxy } from '../System/models/TenantBaseModel';
|
||||||
import { SaleInvoice } from '../SaleInvoices/models/SaleInvoice';
|
import { SaleInvoice } from '../SaleInvoices/models/SaleInvoice';
|
||||||
@@ -6,8 +7,6 @@ import { TransformerInjectable } from '../Transformer/TransformerInjectable.serv
|
|||||||
import { PaymentLink } from './models/PaymentLink';
|
import { PaymentLink } from './models/PaymentLink';
|
||||||
import { ServiceError } from '../Items/ServiceError';
|
import { ServiceError } from '../Items/ServiceError';
|
||||||
import { GetInvoicePaymentLinkMetaTransformer } from '../SaleInvoices/queries/GetInvoicePaymentLink.transformer';
|
import { GetInvoicePaymentLinkMetaTransformer } from '../SaleInvoices/queries/GetInvoicePaymentLink.transformer';
|
||||||
import { ClsService } from 'nestjs-cls';
|
|
||||||
import { TenancyContext } from '../Tenancy/TenancyContext.service';
|
|
||||||
import { TenantModel } from '../System/models/TenantModel';
|
import { TenantModel } from '../System/models/TenantModel';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
@@ -15,7 +14,6 @@ export class GetInvoicePaymentLinkMetadata {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly transformer: TransformerInjectable,
|
private readonly transformer: TransformerInjectable,
|
||||||
private readonly clsService: ClsService,
|
private readonly clsService: ClsService,
|
||||||
private readonly tenancyContext: TenancyContext,
|
|
||||||
|
|
||||||
@Inject(SaleInvoice.name)
|
@Inject(SaleInvoice.name)
|
||||||
private readonly saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
private readonly saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||||
|
|||||||
@@ -25,10 +25,7 @@ export class PaymentLinksController {
|
|||||||
schema: {
|
schema: {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
properties: {
|
properties: {
|
||||||
data: {
|
data: { type: 'object', description: 'Payment link metadata' },
|
||||||
type: 'object',
|
|
||||||
description: 'Payment link metadata',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
CreateSaleInvoiceDto,
|
CreateSaleInvoiceDto,
|
||||||
EditSaleInvoiceDto,
|
EditSaleInvoiceDto,
|
||||||
} from './dtos/SaleInvoice.dto';
|
} from './dtos/SaleInvoice.dto';
|
||||||
|
import { GenerateShareLink } from './commands/GenerateInvoicePaymentLink.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SaleInvoiceApplication {
|
export class SaleInvoiceApplication {
|
||||||
@@ -39,6 +40,7 @@ export class SaleInvoiceApplication {
|
|||||||
private getSaleInvoiceStateService: GetSaleInvoiceState,
|
private getSaleInvoiceStateService: GetSaleInvoiceState,
|
||||||
private sendSaleInvoiceMailService: SendSaleInvoiceMail,
|
private sendSaleInvoiceMailService: SendSaleInvoiceMail,
|
||||||
private getSaleInvoiceMailStateService: GetSaleInvoiceMailState,
|
private getSaleInvoiceMailStateService: GetSaleInvoiceMailState,
|
||||||
|
private generateShareLinkService: GenerateShareLink,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -202,4 +204,23 @@ export class SaleInvoiceApplication {
|
|||||||
saleInvoiceid,
|
saleInvoiceid,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the given sale invoice sharable link.
|
||||||
|
* @param {number} saleInvoiceId
|
||||||
|
* @param {string} publicity
|
||||||
|
* @param {string} expiryTime
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public generateSaleInvoiceSharableLink(
|
||||||
|
saleInvoiceId: number,
|
||||||
|
publicity: string = 'private',
|
||||||
|
expiryTime: string = '',
|
||||||
|
) {
|
||||||
|
return this.generateShareLinkService.generatePaymentLink(
|
||||||
|
saleInvoiceId,
|
||||||
|
publicity,
|
||||||
|
expiryTime,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,12 +38,14 @@ import { AcceptType } from '@/constants/accept-type';
|
|||||||
import { SaleInvoiceResponseDto } from './dtos/SaleInvoiceResponse.dto';
|
import { SaleInvoiceResponseDto } from './dtos/SaleInvoiceResponse.dto';
|
||||||
import { PaginatedResponseDto } from '@/common/dtos/PaginatedResults.dto';
|
import { PaginatedResponseDto } from '@/common/dtos/PaginatedResults.dto';
|
||||||
import { SaleInvoiceStateResponseDto } from './dtos/SaleInvoiceState.dto';
|
import { SaleInvoiceStateResponseDto } from './dtos/SaleInvoiceState.dto';
|
||||||
|
import { GenerateSaleInvoiceSharableLinkResponseDto } from './dtos/generateSaleInvoiceSharableLinkResponse.dto';
|
||||||
|
|
||||||
@Controller('sale-invoices')
|
@Controller('sale-invoices')
|
||||||
@ApiTags('Sale Invoices')
|
@ApiTags('Sale Invoices')
|
||||||
@ApiExtraModels(SaleInvoiceResponseDto)
|
@ApiExtraModels(SaleInvoiceResponseDto)
|
||||||
@ApiExtraModels(PaginatedResponseDto)
|
@ApiExtraModels(PaginatedResponseDto)
|
||||||
@ApiExtraModels(SaleInvoiceStateResponseDto)
|
@ApiExtraModels(SaleInvoiceStateResponseDto)
|
||||||
|
@ApiExtraModels(GenerateSaleInvoiceSharableLinkResponseDto)
|
||||||
@ApiHeader({
|
@ApiHeader({
|
||||||
name: 'organization-id',
|
name: 'organization-id',
|
||||||
description: 'The organization id',
|
description: 'The organization id',
|
||||||
@@ -318,4 +320,25 @@ export class SaleInvoicesController {
|
|||||||
): Promise<SaleInvoiceMailState> {
|
): Promise<SaleInvoiceMailState> {
|
||||||
return this.saleInvoiceApplication.getSaleInvoiceMailState(id);
|
return this.saleInvoiceApplication.getSaleInvoiceMailState(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post(':id/generate-link')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Generate sharable sale invoice link (private or public)',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: 201,
|
||||||
|
description: 'The link has been generated successfully.',
|
||||||
|
schema: {
|
||||||
|
$ref: getSchemaPath(GenerateSaleInvoiceSharableLinkResponseDto),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@ApiParam({
|
||||||
|
name: 'id',
|
||||||
|
required: true,
|
||||||
|
type: Number,
|
||||||
|
description: 'The sale invoice id',
|
||||||
|
})
|
||||||
|
generateSaleInvoiceSharableLink(@Param('id', ParseIntPipe) id: number) {
|
||||||
|
return this.saleInvoiceApplication.generateSaleInvoiceSharableLink(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { events } from '@/common/events/events';
|
|||||||
import { PaymentLink } from '@/modules/PaymentLinks/models/PaymentLink';
|
import { PaymentLink } from '@/modules/PaymentLinks/models/PaymentLink';
|
||||||
import { SaleInvoice } from '../models/SaleInvoice';
|
import { SaleInvoice } from '../models/SaleInvoice';
|
||||||
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
import { TenantModelProxy } from '@/modules/System/models/TenantBaseModel';
|
||||||
|
import { TenancyContext } from '@/modules/Tenancy/TenancyContext.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class GenerateShareLink {
|
export class GenerateShareLink {
|
||||||
@@ -16,12 +17,13 @@ export class GenerateShareLink {
|
|||||||
private uow: UnitOfWork,
|
private uow: UnitOfWork,
|
||||||
private eventPublisher: EventEmitter2,
|
private eventPublisher: EventEmitter2,
|
||||||
private transformer: TransformerInjectable,
|
private transformer: TransformerInjectable,
|
||||||
|
private tenancyContext: TenancyContext,
|
||||||
|
|
||||||
@Inject(SaleInvoice.name)
|
@Inject(SaleInvoice.name)
|
||||||
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
private saleInvoiceModel: TenantModelProxy<typeof SaleInvoice>,
|
||||||
|
|
||||||
@Inject(PaymentLink.name)
|
@Inject(PaymentLink.name)
|
||||||
private paymentLinkModel: TenantModelProxy<typeof PaymentLink>,
|
private paymentLinkModel: typeof PaymentLink,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -39,10 +41,10 @@ export class GenerateShareLink {
|
|||||||
.query()
|
.query()
|
||||||
.findById(saleInvoiceId)
|
.findById(saleInvoiceId)
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
const tenant = await this.tenancyContext.getTenant();
|
||||||
|
|
||||||
// Generate unique uuid for sharable link.
|
// Generate unique uuid for sharable link.
|
||||||
const linkId = uuidv4() as string;
|
const linkId = uuidv4() as string;
|
||||||
|
|
||||||
const commonEventPayload = {
|
const commonEventPayload = {
|
||||||
saleInvoiceId,
|
saleInvoiceId,
|
||||||
publicity,
|
publicity,
|
||||||
@@ -54,11 +56,12 @@ export class GenerateShareLink {
|
|||||||
events.saleInvoice.onPublicLinkGenerating,
|
events.saleInvoice.onPublicLinkGenerating,
|
||||||
{ ...commonEventPayload, trx },
|
{ ...commonEventPayload, trx },
|
||||||
);
|
);
|
||||||
const paymentLink = await this.paymentLinkModel().query().insert({
|
const paymentLink = await this.paymentLinkModel.query().insert({
|
||||||
linkId,
|
linkId,
|
||||||
publicity,
|
publicity,
|
||||||
resourceId: foundInvoice.id,
|
resourceId: foundInvoice.id,
|
||||||
resourceType: 'SaleInvoice',
|
resourceType: 'SaleInvoice',
|
||||||
|
tenantId: tenant.id,
|
||||||
});
|
});
|
||||||
// Triggers `onPublicSharableLinkGenerated` event.
|
// Triggers `onPublicSharableLinkGenerated` event.
|
||||||
await this.eventPublisher.emitAsync(
|
await this.eventPublisher.emitAsync(
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class GenerateSaleInvoiceSharableLinkResponseDto {
|
||||||
|
@ApiProperty({
|
||||||
|
description: 'Sharable payment link for the sale invoice',
|
||||||
|
example:
|
||||||
|
'http://localhost:3000/payment/123e4567-e89b-12d3-a456-426614174000',
|
||||||
|
})
|
||||||
|
link: string;
|
||||||
|
}
|
||||||
@@ -29,7 +29,6 @@ import { SaleInvoice } from '@/modules/SaleInvoices/models/SaleInvoice';
|
|||||||
import { PaymentReceivedEntry } from '@/modules/PaymentReceived/models/PaymentReceivedEntry';
|
import { PaymentReceivedEntry } from '@/modules/PaymentReceived/models/PaymentReceivedEntry';
|
||||||
import { CreditNoteAppliedInvoice } from '@/modules/CreditNotesApplyInvoice/models/CreditNoteAppliedInvoice';
|
import { CreditNoteAppliedInvoice } from '@/modules/CreditNotesApplyInvoice/models/CreditNoteAppliedInvoice';
|
||||||
import { CreditNote } from '@/modules/CreditNotes/models/CreditNote';
|
import { CreditNote } from '@/modules/CreditNotes/models/CreditNote';
|
||||||
import { PaymentLink } from '@/modules/PaymentLinks/models/PaymentLink';
|
|
||||||
import { SaleReceipt } from '@/modules/SaleReceipts/models/SaleReceipt';
|
import { SaleReceipt } from '@/modules/SaleReceipts/models/SaleReceipt';
|
||||||
import { ManualJournal } from '@/modules/ManualJournals/models/ManualJournal';
|
import { ManualJournal } from '@/modules/ManualJournals/models/ManualJournal';
|
||||||
import { ManualJournalEntry } from '@/modules/ManualJournals/models/ManualJournalEntry';
|
import { ManualJournalEntry } from '@/modules/ManualJournals/models/ManualJournalEntry';
|
||||||
@@ -70,7 +69,6 @@ const models = [
|
|||||||
CreditNoteAppliedInvoice,
|
CreditNoteAppliedInvoice,
|
||||||
CreditNote,
|
CreditNote,
|
||||||
RefundCreditNote,
|
RefundCreditNote,
|
||||||
PaymentLink,
|
|
||||||
SaleReceipt,
|
SaleReceipt,
|
||||||
ManualJournal,
|
ManualJournal,
|
||||||
ManualJournalEntry,
|
ManualJournalEntry,
|
||||||
@@ -82,7 +80,6 @@ const models = [
|
|||||||
TenantUser,
|
TenantUser,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorator factory that registers a model with the tenancy system.
|
* Decorator factory that registers a model with the tenancy system.
|
||||||
* @param model The model class to register
|
* @param model The model class to register
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ export const SharePaymentLinkForm = ({
|
|||||||
generateShareLink(values)
|
generateShareLink(values)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
setUrl(res.link?.link);
|
setUrl(res.link);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ export function useCreatePaymentLink(
|
|||||||
return useMutation<CreatePaymentLinkResponse, Error, CreatePaymentLinkValues>(
|
return useMutation<CreatePaymentLinkResponse, Error, CreatePaymentLinkValues>(
|
||||||
(values) =>
|
(values) =>
|
||||||
apiRequest
|
apiRequest
|
||||||
.post('/payment-links/generate', transfromToSnakeCase(values))
|
.post(
|
||||||
|
`/sale-invoices/${values.transactionId}/generate-link`,
|
||||||
|
transfromToSnakeCase(values),
|
||||||
|
)
|
||||||
.then((res) => res.data),
|
.then((res) => res.data),
|
||||||
{
|
{
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
Reference in New Issue
Block a user