Changes updated
This commit is contained in:
parent
5d47a78baa
commit
b2bb8891c3
@ -13,7 +13,7 @@ interface MembershipData {
|
||||
subscription?: {
|
||||
name: string;
|
||||
frequency: string;
|
||||
assignedAt: admin.firestore.Timestamp;
|
||||
assignedAt?: admin.firestore.Timestamp;
|
||||
};
|
||||
}
|
||||
|
||||
@ -23,6 +23,17 @@ interface ClientFields {
|
||||
"last-name"?: string;
|
||||
}
|
||||
|
||||
interface PaymentData {
|
||||
id: string;
|
||||
date: string;
|
||||
amount: number;
|
||||
paymentMethod: string;
|
||||
referenceNumber: string;
|
||||
dateTimestamp: Date;
|
||||
createdAt: Date;
|
||||
discount?: number;
|
||||
}
|
||||
|
||||
export const checkExpiredMemberships = onSchedule(
|
||||
{
|
||||
schedule: "0 8,14,20 * * *",
|
||||
@ -72,11 +83,28 @@ async function findExpiredMemberships(): Promise<
|
||||
|
||||
const expired: Array<{ id: string; data: MembershipData }> = [];
|
||||
|
||||
snapshot.docs.forEach((doc) => {
|
||||
const data = doc.data() as MembershipData;
|
||||
const isExpired = checkIfMembershipExpired(data);
|
||||
if (isExpired) expired.push({ id: doc.id, data });
|
||||
});
|
||||
const batchSize = 10;
|
||||
const docs = snapshot.docs;
|
||||
|
||||
for (let i = 0; i < docs.length; i += batchSize) {
|
||||
const batch = docs.slice(i, i + batchSize);
|
||||
const batchResults = await Promise.allSettled(
|
||||
batch.map(async (doc) => {
|
||||
const data = doc.data() as MembershipData;
|
||||
const isExpired = await checkIfMembershipExpired(doc.id, data);
|
||||
if (isExpired) {
|
||||
return { id: doc.id, data };
|
||||
}
|
||||
return null;
|
||||
})
|
||||
);
|
||||
|
||||
batchResults.forEach((result) => {
|
||||
if (result.status === "fulfilled" && result.value) {
|
||||
expired.push(result.value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return expired;
|
||||
} catch (error) {
|
||||
@ -85,35 +113,85 @@ async function findExpiredMemberships(): Promise<
|
||||
}
|
||||
}
|
||||
|
||||
function checkIfMembershipExpired(data: MembershipData): boolean {
|
||||
async function checkIfMembershipExpired(membershipId: string, data: MembershipData): Promise<boolean> {
|
||||
try {
|
||||
if (
|
||||
!data.subscription ||
|
||||
!data.subscription.frequency ||
|
||||
!data.subscription.assignedAt
|
||||
) {
|
||||
if (!data.subscription || !data.subscription.frequency) {
|
||||
logger.warn(
|
||||
`Skipping expiry check for membership ${data.id} with missing subscription data.`
|
||||
`Skipping expiry check for membership ${membershipId} with missing subscription data.`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
const startDate = (
|
||||
data.subscription.assignedAt as admin.firestore.Timestamp
|
||||
).toDate();
|
||||
const expiryDate = calculateExpiryDate(
|
||||
startDate,
|
||||
data.subscription.frequency
|
||||
);
|
||||
let startDate: Date;
|
||||
|
||||
if (data.subscription.assignedAt) {
|
||||
startDate = (data.subscription.assignedAt as admin.firestore.Timestamp).toDate();
|
||||
} else {
|
||||
logger.info(`Using payment logic for membership ${membershipId} as assignedAt is missing`);
|
||||
|
||||
const payments = await getPaymentsForMembership(membershipId);
|
||||
if (payments.length === 0) {
|
||||
logger.warn(`No payments found for membership ${membershipId}, cannot determine expiry`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const latestPayment = payments[0];
|
||||
startDate = latestPayment.dateTimestamp;
|
||||
|
||||
logger.info(`Using latest payment date ${startDate.toISOString()} for membership ${membershipId}`);
|
||||
}
|
||||
|
||||
const expiryDate = calculateExpiryDate(startDate, data.subscription.frequency);
|
||||
const now = new Date();
|
||||
|
||||
return now > expiryDate;
|
||||
const isExpired = now > expiryDate;
|
||||
|
||||
if (isExpired) {
|
||||
logger.info(`Membership ${membershipId} expired on ${expiryDate.toISOString()}`);
|
||||
}
|
||||
|
||||
return isExpired;
|
||||
} catch (error) {
|
||||
logger.error(`Error checking expiry for membership ${data.id}:`, error);
|
||||
logger.error(`Error checking expiry for membership ${membershipId}:`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function getPaymentsForMembership(membershipId: string): Promise<PaymentData[]> {
|
||||
try {
|
||||
const docSnapshot = await app
|
||||
.firestore()
|
||||
.collection("membershipPayments")
|
||||
.doc(membershipId)
|
||||
.get();
|
||||
|
||||
if (!docSnapshot.exists) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const data = docSnapshot.data();
|
||||
const paymentsData = data?.payments || [];
|
||||
|
||||
const payments: PaymentData[] = paymentsData.map((payment: any) => ({
|
||||
id: payment.id,
|
||||
date: payment.date,
|
||||
amount: payment.amount,
|
||||
paymentMethod: payment.paymentMethod,
|
||||
referenceNumber: payment.referenceNumber,
|
||||
dateTimestamp: payment.dateTimestamp.toDate ? payment.dateTimestamp.toDate() : new Date(payment.dateTimestamp),
|
||||
createdAt: payment.createdAt.toDate ? payment.createdAt.toDate() : new Date(payment.createdAt),
|
||||
discount: payment.discount
|
||||
}));
|
||||
|
||||
payments.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
||||
|
||||
return payments;
|
||||
} catch (error) {
|
||||
logger.error(`Error getting payments for membership ${membershipId}:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function calculateExpiryDate(startDate: Date, frequency: string): Date {
|
||||
const expiry = new Date(startDate);
|
||||
switch (frequency.toLowerCase()) {
|
||||
@ -135,6 +213,30 @@ function calculateExpiryDate(startDate: Date, frequency: string): Date {
|
||||
return expiry;
|
||||
}
|
||||
|
||||
function calculateRenewalDateFromPayment(subscription: any, paymentDate: Date): Date {
|
||||
const renewalDate = new Date(paymentDate);
|
||||
const frequency = subscription.frequency || 'Monthly';
|
||||
|
||||
switch (frequency.toLowerCase()) {
|
||||
case 'monthly':
|
||||
renewalDate.setMonth(renewalDate.getMonth() + 1);
|
||||
break;
|
||||
case 'quarterly':
|
||||
renewalDate.setMonth(renewalDate.getMonth() + 3);
|
||||
break;
|
||||
case 'half-yearly':
|
||||
renewalDate.setMonth(renewalDate.getMonth() + 6);
|
||||
break;
|
||||
case 'yearly':
|
||||
renewalDate.setFullYear(renewalDate.getFullYear() + 1);
|
||||
break;
|
||||
default:
|
||||
renewalDate.setMonth(renewalDate.getMonth() + 1);
|
||||
}
|
||||
|
||||
return renewalDate;
|
||||
}
|
||||
|
||||
async function processExpiredMembership(
|
||||
membershipId: string,
|
||||
membershipData: MembershipData
|
||||
@ -174,14 +276,31 @@ async function sendPlanExpiredNotification(
|
||||
return;
|
||||
}
|
||||
|
||||
const expiryDate = membershipData.subscription?.assignedAt?.toDate();
|
||||
const formattedDate = expiryDate
|
||||
? expiryDate.toLocaleDateString("en-US", {
|
||||
let expiryDate: Date | undefined;
|
||||
let formattedDate = "Unknown Date";
|
||||
|
||||
if (membershipData.subscription?.assignedAt) {
|
||||
expiryDate = membershipData.subscription.assignedAt.toDate();
|
||||
formattedDate = expiryDate.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
});
|
||||
} else {
|
||||
const payments = await getPaymentsForMembership(membershipId);
|
||||
if (payments.length > 0) {
|
||||
const latestPayment = payments[0];
|
||||
expiryDate = calculateRenewalDateFromPayment(
|
||||
membershipData.subscription,
|
||||
latestPayment.dateTimestamp
|
||||
);
|
||||
formattedDate = expiryDate.toLocaleDateString("en-US", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
: "Unknown Date";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
await app
|
||||
.firestore()
|
||||
@ -203,9 +322,9 @@ async function sendPlanExpiredNotification(
|
||||
membershipId,
|
||||
gymName,
|
||||
formattedExpiryDate: formattedDate,
|
||||
expiryDate:
|
||||
membershipData.subscription?.assignedAt ||
|
||||
admin.firestore.Timestamp.fromDate(new Date()),
|
||||
expiryDate: expiryDate
|
||||
? admin.firestore.Timestamp.fromDate(expiryDate)
|
||||
: admin.firestore.Timestamp.fromDate(new Date()),
|
||||
},
|
||||
});
|
||||
|
||||
@ -260,4 +379,4 @@ async function getGymName(gymId: string): Promise<string> {
|
||||
logger.error(`Error getting gym name for gym ${gymId}:`, error);
|
||||
return "Unknown Gym";
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user