Files
calendar/packages/features/bookings/lib/payment/processNoShowFeeOnCancellation.ts
Benny JooandGitHub ff38d6c7db refactor: Remove circular deps between @calcom/lib and @calcom/features [1] (#24399)
* add eslint config

* migrate autoLock to features

* migrate teamService to features

* migrate userCreationService

* migrate insights services to features

* migrate ProfileRepository

* update imports

* migrate filter segmen tests

* migrate filter segment repository

* migrate getBusyTimes

* migrate getLocaleFromRequest

* refactor csvUtils

* make filename clearer

* migrate getLuckyUser integration test

* migrate autoLock test to features

* wip

* refactors

* migrate useBookerUrl

* migrate more

* wip

* Migrate eventTypeRepository

* membership repository

* update imports

* update imports

* migrate

* move organization repository

* update imports

* update imports

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix tests

* fix type checks

* fix

* fix

* migrate

* update imports

* fix tests

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix

* fix
2025-10-13 12:01:02 -03:00

82 lines
2.6 KiB
TypeScript

import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-utils";
import logger from "@calcom/lib/logger";
import { MembershipRepository } from "@calcom/features/membership/repositories/MembershipRepository";
import type { Payment } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";
import { handleNoShowFee } from "./handleNoShowFee";
import { shouldChargeNoShowCancellationFee } from "./shouldChargeNoShowCancellationFee";
export const processNoShowFeeOnCancellation = async ({
booking,
payments,
cancelledByUserId,
}: {
booking: Parameters<typeof handleNoShowFee>[0]["booking"];
payments: Payment[];
cancelledByUserId?: number;
}) => {
const log = logger.getSubLogger({ prefix: ["processNoShowFeeOnCancellation"] });
// Skip no-show fee if the booking was cancelled by the organizer or team/org admin
if (cancelledByUserId && booking.userId === cancelledByUserId) {
log.info(
`Booking ${booking.uid} was cancelled by the organizer (${cancelledByUserId}), skipping no-show fee`
);
return;
}
// Skip no-show fee if the booking was cancelled by a team/org admin
if (cancelledByUserId && booking.eventType?.teamId) {
const membership = await MembershipRepository.findUniqueByUserIdAndTeamId({
userId: cancelledByUserId,
teamId: booking.eventType.teamId,
});
if (
membership &&
(membership.role === MembershipRole.OWNER || membership.role === MembershipRole.ADMIN)
) {
log.info(
`Booking ${booking.uid} was cancelled by team admin/owner (${cancelledByUserId}), skipping no-show fee`
);
return;
}
}
const paymentToCharge = payments.find(
(payment) => payment.paymentOption === "HOLD" && payment.success === false
);
if (!paymentToCharge) {
log.info(`No payment found to charge for booking ${booking.uid}`);
return;
}
// Parse the event type metadata
const eventTypeMetadata = eventTypeMetaDataSchemaWithTypedApps.parse(booking.eventType?.metadata ?? {});
// Determine if we need to charge the no-show fee
const shouldChargeNoShowFee = shouldChargeNoShowCancellationFee({
booking,
eventTypeMetadata,
payment: paymentToCharge,
});
if (!shouldChargeNoShowFee) {
log.info(`Date is not valid for no-show fee to charge for booking ${booking.uid}`);
return;
}
// Process charging the no show fee
try {
await handleNoShowFee({
booking,
payment: paymentToCharge,
});
} catch (error) {
log.error(`Error charging no-show fee for booking ${booking.uid}`, error);
throw new Error(`Failed to charge no-show fee with error ${error}`);
}
};