mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
feat: inactive associated Stripe payment link on invoice deleting
This commit is contained in:
@@ -68,6 +68,7 @@ import { StripeIntegrationController } from './controllers/StripeIntegration/Str
|
||||
import { ShareLinkController } from './controllers/ShareLink/ShareLinkController';
|
||||
import { PublicSharableLinkController } from './controllers/ShareLink/PublicSharableLinkController';
|
||||
import { PdfTemplatesController } from './controllers/PdfTemplates/PdfTemplatesController';
|
||||
import { PaymentServicesController } from './controllers/PaymentServices/PaymentServicesController';
|
||||
|
||||
export default () => {
|
||||
const app = Router();
|
||||
@@ -160,7 +161,10 @@ export default () => {
|
||||
'/pdf-templates',
|
||||
Container.get(PdfTemplatesController).router()
|
||||
);
|
||||
|
||||
dashboard.use(
|
||||
'/payment-services',
|
||||
Container.get(PaymentServicesController).router()
|
||||
);
|
||||
dashboard.use('/', Container.get(ProjectTasksController).router());
|
||||
dashboard.use('/', Container.get(ProjectTimesController).router());
|
||||
dashboard.use('/', Container.get(WarehousesItemController).router());
|
||||
|
||||
@@ -23,6 +23,16 @@ export interface PaymentIntegrationTransactionLinkEventPayload {
|
||||
trx?: Knex.Transaction
|
||||
}
|
||||
|
||||
export interface PaymentIntegrationTransactionLinkDeleteEventPayload {
|
||||
tenantId: number;
|
||||
enable: true;
|
||||
paymentIntegrationId: number;
|
||||
referenceType: string;
|
||||
referenceId: number;
|
||||
oldSaleInvoiceId: number;
|
||||
trx?: Knex.Transaction
|
||||
}
|
||||
|
||||
export interface ISaleInvoice {
|
||||
id: number;
|
||||
amount: number;
|
||||
@@ -156,9 +166,15 @@ export interface ISaleInvoiceEditingPayload {
|
||||
|
||||
export interface ISaleInvoiceDeletePayload {
|
||||
tenantId: number;
|
||||
saleInvoice: ISaleInvoice;
|
||||
oldSaleInvoice: ISaleInvoice;
|
||||
saleInvoiceId: number;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface ISaleInvoiceDeletingPayload {
|
||||
tenantId: number;
|
||||
oldSaleInvoice: ISaleInvoice;
|
||||
saleInvoiceId: number;
|
||||
trx: Knex.Transaction;
|
||||
}
|
||||
|
||||
export interface ISaleInvoiceDeletedPayload {
|
||||
|
||||
@@ -30,6 +30,17 @@ export class TransactionPaymentServiceEntry extends TenantModel {
|
||||
* Relationship mapping.
|
||||
*/
|
||||
static get relationMappings() {
|
||||
return {};
|
||||
const { PaymentIntegration } = require('./PaymentIntegration');
|
||||
|
||||
return {
|
||||
paymentIntegration: {
|
||||
relation: TenantModel.BelongsToOneRelation,
|
||||
modelClass: PaymentIntegration,
|
||||
join: {
|
||||
from: 'transactions_payment_methods.paymentIntegrationId',
|
||||
to: 'payment_integrations.id',
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { isEmpty } from 'lodash';
|
||||
import {
|
||||
ISaleInvoiceCreatedPayload,
|
||||
ISaleInvoiceCreatingPaylaod,
|
||||
ISaleInvoiceDeletePayload,
|
||||
ISaleInvoiceDeletingPayload,
|
||||
ISaleInvoiceEditedPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
@@ -146,13 +146,13 @@ export class AttachmentsOnSaleInvoiceCreated {
|
||||
*/
|
||||
private async handleUnlinkAttachmentsOnInvoiceDeleted({
|
||||
tenantId,
|
||||
saleInvoice,
|
||||
oldSaleInvoice,
|
||||
trx,
|
||||
}: ISaleInvoiceDeletePayload) {
|
||||
}: ISaleInvoiceDeletingPayload) {
|
||||
await this.unlinkAttachmentService.unlinkAllModelKeys(
|
||||
tenantId,
|
||||
'SaleInvoice',
|
||||
saleInvoice.id,
|
||||
oldSaleInvoice.id,
|
||||
trx
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
ISystemUser,
|
||||
ISaleInvoiceDeletePayload,
|
||||
ISaleInvoiceDeletedPayload,
|
||||
ISaleInvoiceDeletingPayload,
|
||||
} from '@/interfaces';
|
||||
import events from '@/subscribers/events';
|
||||
import UnitOfWork from '@/services/UnitOfWork';
|
||||
@@ -82,10 +83,10 @@ export class DeleteSaleInvoice {
|
||||
) {
|
||||
const { saleInvoiceRepository } = this.tenancy.repositories(tenantId);
|
||||
|
||||
const saleInvoice = await saleInvoiceRepository.findOneById(
|
||||
saleInvoiceId,
|
||||
'entries'
|
||||
);
|
||||
const saleInvoice = await saleInvoiceRepository.findOneById(saleInvoiceId, [
|
||||
'entries',
|
||||
'paymentMethods',
|
||||
]);
|
||||
if (!saleInvoice) {
|
||||
throw new ServiceError(ERRORS.SALE_INVOICE_NOT_FOUND);
|
||||
}
|
||||
@@ -118,15 +119,22 @@ export class DeleteSaleInvoice {
|
||||
// Validate the sale invoice has applied to credit note transaction.
|
||||
await this.validateInvoiceHasNoAppliedToCredit(tenantId, saleInvoiceId);
|
||||
|
||||
// Triggers `onSaleInvoiceDelete` event.
|
||||
await this.eventPublisher.emitAsync(events.saleInvoice.onDelete, {
|
||||
tenantId,
|
||||
oldSaleInvoice,
|
||||
saleInvoiceId,
|
||||
} as ISaleInvoiceDeletePayload);
|
||||
|
||||
// Deletes sale invoice transaction and associate transactions with UOW env.
|
||||
return this.uow.withTransaction(tenantId, async (trx: Knex.Transaction) => {
|
||||
// Triggers `onSaleInvoiceDelete` event.
|
||||
// Triggers `onSaleInvoiceDeleting` event.
|
||||
await this.eventPublisher.emitAsync(events.saleInvoice.onDeleting, {
|
||||
tenantId,
|
||||
saleInvoice: oldSaleInvoice,
|
||||
oldSaleInvoice,
|
||||
saleInvoiceId,
|
||||
trx,
|
||||
} as ISaleInvoiceDeletePayload);
|
||||
} as ISaleInvoiceDeletingPayload);
|
||||
|
||||
// Unlink the converted sale estimates from the given sale invoice.
|
||||
await this.unlockEstimateFromInvoice.unlinkConvertedEstimateFromInvoice(
|
||||
|
||||
@@ -3,7 +3,9 @@ import { omit } from 'lodash';
|
||||
import events from '@/subscribers/events';
|
||||
import {
|
||||
ISaleInvoiceCreatedPayload,
|
||||
ISaleInvoiceDeletingPayload,
|
||||
PaymentIntegrationTransactionLink,
|
||||
PaymentIntegrationTransactionLinkDeleteEventPayload,
|
||||
PaymentIntegrationTransactionLinkEventPayload,
|
||||
} from '@/interfaces';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
@@ -21,6 +23,10 @@ export class InvoicePaymentIntegrationSubscriber {
|
||||
events.saleInvoice.onCreated,
|
||||
this.handleCreatePaymentIntegrationEvents
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onDeleting,
|
||||
this.handleCreatePaymentIntegrationEventsOnDeleteInvoice
|
||||
);
|
||||
return bus;
|
||||
};
|
||||
|
||||
@@ -54,4 +60,34 @@ export class InvoicePaymentIntegrationSubscriber {
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ISaleInvoiceDeletingPayload} payload
|
||||
*/
|
||||
private handleCreatePaymentIntegrationEventsOnDeleteInvoice = ({
|
||||
tenantId,
|
||||
oldSaleInvoice,
|
||||
trx,
|
||||
}: ISaleInvoiceDeletingPayload) => {
|
||||
const paymentMethods =
|
||||
oldSaleInvoice.paymentMethods?.filter((method) => method.enable) || [];
|
||||
|
||||
paymentMethods.map(
|
||||
async (paymentMethod: PaymentIntegrationTransactionLink) => {
|
||||
const payload = {
|
||||
...omit(paymentMethod, ['id']),
|
||||
tenantId,
|
||||
oldSaleInvoiceId: oldSaleInvoice.id,
|
||||
trx,
|
||||
} as PaymentIntegrationTransactionLinkDeleteEventPayload;
|
||||
|
||||
// Triggers `onPaymentIntegrationDeleteLink` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.paymentIntegrationLink.onPaymentIntegrationDeleteLink,
|
||||
payload
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import HasTenancyService from '../Tenancy/TenancyService';
|
||||
import { StripePaymentService } from './StripePaymentService';
|
||||
import { Knex } from 'knex';
|
||||
import { EventPublisher } from '@/lib/EventPublisher/EventPublisher';
|
||||
import events from '@/subscribers/events';
|
||||
|
||||
@Service()
|
||||
export class DeleteStripePaymentLinkInvoice {
|
||||
@@ -11,27 +13,53 @@ export class DeleteStripePaymentLinkInvoice {
|
||||
@Inject()
|
||||
private stripePayment: StripePaymentService;
|
||||
|
||||
@Inject()
|
||||
private eventPublisher: EventPublisher;
|
||||
|
||||
/**
|
||||
* Deletes the Stripe payment link associates to the given sale invoice.
|
||||
* @param {number} tenantId
|
||||
* @param {number} invoiceId
|
||||
* @param {number} tenantId -
|
||||
* @param {number} invoiceId -
|
||||
* @param {Knex.Transaction} knex -
|
||||
*/
|
||||
async deletePaymentLink(
|
||||
async deleteInvoicePaymentLink(
|
||||
tenantId: number,
|
||||
invoiceId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> {
|
||||
const { SaleInvoice } = this.tenancy.models(tenantId);
|
||||
const invoice = await SaleInvoice.query().findById(invoiceId);
|
||||
|
||||
const stripeAcocunt = { stripeAccount: 'acct_1Px3dSPjeOqFxnPw' };
|
||||
const invoice = await SaleInvoice.query(trx)
|
||||
.findById(invoiceId)
|
||||
.withGraphFetched('paymentMethods.paymentIntegration')
|
||||
.throwIfNotFound();
|
||||
|
||||
// It will be only one Stripe payment method associated to the invoice.
|
||||
const stripePaymentMethod = invoice.paymentMethods?.find(
|
||||
(method) => method.paymentIntegration?.service === 'Stripe'
|
||||
);
|
||||
const stripeAccountId = stripePaymentMethod?.paymentIntegration?.accountId;
|
||||
const paymentIntegrationId = stripePaymentMethod?.paymentIntegration?.id;
|
||||
|
||||
if (invoice.stripePlinkId && stripeAccountId) {
|
||||
const stripeAcocunt = { stripeAccount: stripeAccountId };
|
||||
const stripePlinkId = invoice.stripePlinkId;
|
||||
|
||||
if (invoice.stripePlinkId) {
|
||||
await this.stripePayment.stripe.paymentLinks.update(
|
||||
invoice.stripePlinkId,
|
||||
stripePlinkId,
|
||||
{ active: false },
|
||||
stripeAcocunt
|
||||
);
|
||||
// Triggers `onStripePaymentLinkInactivated` event.
|
||||
await this.eventPublisher.emitAsync(
|
||||
events.stripeIntegration.onPaymentLinkInactivated,
|
||||
{
|
||||
tenantId,
|
||||
saleInvoiceId: invoiceId,
|
||||
paymentIntegrationId,
|
||||
stripePlinkId,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { Knex } from 'knex';
|
||||
import { Inject } from 'typedi';
|
||||
import { CreateStripeAccountService } from '@/api/controllers/StripeIntegration/CreateStripeAccountService';
|
||||
import { CreateStripeAccountDTO } from '@/api/controllers/StripeIntegration/types';
|
||||
import { SaleInvoiceStripePaymentLink } from './SaleInvoiceStripePaymentLink';
|
||||
import { DeleteStripePaymentLinkInvoice } from './DeleteStripePaymentLinkInvoice';
|
||||
|
||||
export class StripePaymentApplication {
|
||||
@Inject()
|
||||
private createStripeAccountService: CreateStripeAccountService;
|
||||
|
||||
@Inject()
|
||||
private saleInvoiceStripePaymentLinkService: SaleInvoiceStripePaymentLink;
|
||||
|
||||
@Inject()
|
||||
private deleteStripePaymentLinkInvoice: DeleteStripePaymentLinkInvoice;
|
||||
|
||||
/**
|
||||
* Creates a new Stripe account for Bigcapital.
|
||||
* @param {number} tenantId
|
||||
* @param {number} createStripeAccountDTO
|
||||
*/
|
||||
public createStripeAccount(
|
||||
tenantId: number,
|
||||
createStripeAccountDTO: CreateStripeAccountDTO
|
||||
) {
|
||||
return this.createStripeAccountService.createStripeAccount(
|
||||
tenantId,
|
||||
createStripeAccountDTO
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Stripe payment link for the given sale invoice.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} stripeIntegrationId - Stripe integration id.
|
||||
* @param {ISaleInvoice} saleInvoice - Sale invoice id.
|
||||
* @returns {Promise<string>}
|
||||
*/
|
||||
public createSaleInvoicePaymentLink(
|
||||
tenantId: number,
|
||||
stripeIntegrationId: number,
|
||||
invoiceId: number
|
||||
) {
|
||||
return this.saleInvoiceStripePaymentLinkService.createPaymentLink(
|
||||
tenantId,
|
||||
stripeIntegrationId,
|
||||
invoiceId
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the Stripe payment link associated with the given sale invoice.
|
||||
* @param {number} tenantId - Tenant id.
|
||||
* @param {number} invoiceId - Sale invoice id.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public deleteInvoicePaymentLink(
|
||||
tenantId: number,
|
||||
invoiceId: number,
|
||||
trx?: Knex.Transaction
|
||||
): Promise<void> {
|
||||
return this.deleteStripePaymentLinkInvoice.deleteInvoicePaymentLink(
|
||||
tenantId,
|
||||
invoiceId,
|
||||
trx
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { EventSubscriber } from '@/lib/EventPublisher/EventPublisher';
|
||||
import {
|
||||
ISaleInvoiceDeletedPayload,
|
||||
PaymentIntegrationTransactionLinkDeleteEventPayload,
|
||||
PaymentIntegrationTransactionLinkEventPayload,
|
||||
} from '@/interfaces';
|
||||
import { SaleInvoiceStripePaymentLink } from '../SaleInvoiceStripePaymentLink';
|
||||
@@ -25,10 +25,10 @@ export class CreatePaymentLinkOnInvoiceCreated extends EventSubscriber {
|
||||
events.paymentIntegrationLink.onPaymentIntegrationLink,
|
||||
this.handleCreatePaymentLinkOnIntegrationLink
|
||||
);
|
||||
// bus.subscribe(
|
||||
// events.saleInvoice.onDeleted,
|
||||
// this.handleDeletePaymentLinkOnInvoiceDeleted
|
||||
// );
|
||||
bus.subscribe(
|
||||
events.paymentIntegrationLink.onPaymentIntegrationDeleteLink,
|
||||
this.handleDeletePaymentLinkOnIntegrationLinkDelete
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,13 +59,15 @@ export class CreatePaymentLinkOnInvoiceCreated extends EventSubscriber {
|
||||
* Deletes the Stripe payment link once the associated invoice deleted.
|
||||
* @param {ISaleInvoiceDeletedPayload}
|
||||
*/
|
||||
private handleDeletePaymentLinkOnInvoiceDeleted = async ({
|
||||
saleInvoiceId,
|
||||
private handleDeletePaymentLinkOnIntegrationLinkDelete = async ({
|
||||
oldSaleInvoiceId,
|
||||
tenantId,
|
||||
}: ISaleInvoiceDeletedPayload) => {
|
||||
await this.deleteStripePaymentLinkInvoice.deletePaymentLink(
|
||||
trx,
|
||||
}: PaymentIntegrationTransactionLinkDeleteEventPayload) => {
|
||||
await this.deleteStripePaymentLinkInvoice.deleteInvoicePaymentLink(
|
||||
tenantId,
|
||||
saleInvoiceId
|
||||
oldSaleInvoiceId,
|
||||
trx
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export default class SalesTransactionLockingGuardSubscriber {
|
||||
this.transactionLockinGuardOnInvoiceWritingoffCanceling
|
||||
);
|
||||
bus.subscribe(
|
||||
events.saleInvoice.onDeleting,
|
||||
events.saleInvoice.onDelete,
|
||||
this.transactionLockingGuardOnInvoiceDeleting
|
||||
);
|
||||
|
||||
@@ -176,15 +176,15 @@ export default class SalesTransactionLockingGuardSubscriber {
|
||||
* @param {ISaleInvoiceDeletePayload} payload
|
||||
*/
|
||||
private transactionLockingGuardOnInvoiceDeleting = async ({
|
||||
saleInvoice,
|
||||
oldSaleInvoice,
|
||||
tenantId,
|
||||
}: ISaleInvoiceDeletePayload) => {
|
||||
// Can't continue if the old invoice not published.
|
||||
if (!saleInvoice.isDelivered) return;
|
||||
if (!oldSaleInvoice.isDelivered) return;
|
||||
|
||||
await this.salesLockingGuard.transactionLockingGuard(
|
||||
tenantId,
|
||||
saleInvoice.invoiceDate
|
||||
oldSaleInvoice.invoiceDate
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -705,7 +705,8 @@ export default {
|
||||
|
||||
// Payment methods integrations
|
||||
paymentIntegrationLink: {
|
||||
onPaymentIntegrationLink: 'onPaymentIntegrationLink'
|
||||
onPaymentIntegrationLink: 'onPaymentIntegrationLink',
|
||||
onPaymentIntegrationDeleteLink: 'onPaymentIntegrationDeleteLink'
|
||||
},
|
||||
|
||||
// Stripe Payment Integration
|
||||
@@ -714,5 +715,6 @@ export default {
|
||||
onAccountDeleted: 'onStripeIntegrationAccountDeleted',
|
||||
|
||||
onPaymentLinkCreated: 'onStripePaymentLinkCreated',
|
||||
onPaymentLinkInactivated: 'onStripePaymentLinkInactivated'
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user