expiry-using-payment #79

Merged
dhanshas merged 2 commits from expiry-using-payment into dev 2025-08-05 06:16:48 +00:00
Showing only changes of commit b2bb8891c3 - Show all commits

View File

@ -13,7 +13,7 @@ interface MembershipData {
subscription?: { subscription?: {
name: string; name: string;
frequency: string; frequency: string;
assignedAt: admin.firestore.Timestamp; assignedAt?: admin.firestore.Timestamp;
}; };
} }
@ -23,6 +23,17 @@ interface ClientFields {
"last-name"?: string; "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( export const checkExpiredMemberships = onSchedule(
{ {
schedule: "0 8,14,20 * * *", schedule: "0 8,14,20 * * *",
@ -72,11 +83,28 @@ async function findExpiredMemberships(): Promise<
const expired: Array<{ id: string; data: MembershipData }> = []; const expired: Array<{ id: string; data: MembershipData }> = [];
snapshot.docs.forEach((doc) => { const batchSize = 10;
const data = doc.data() as MembershipData; const docs = snapshot.docs;
const isExpired = checkIfMembershipExpired(data);
if (isExpired) expired.push({ id: doc.id, data }); 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; return expired;
} catch (error) { } 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 { try {
if ( if (!data.subscription || !data.subscription.frequency) {
!data.subscription ||
!data.subscription.frequency ||
!data.subscription.assignedAt
) {
logger.warn( 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; return false;
} }
const startDate = ( let startDate: Date;
data.subscription.assignedAt as admin.firestore.Timestamp
).toDate(); if (data.subscription.assignedAt) {
const expiryDate = calculateExpiryDate( startDate = (data.subscription.assignedAt as admin.firestore.Timestamp).toDate();
startDate, } else {
data.subscription.frequency 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(); 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) { } catch (error) {
logger.error(`Error checking expiry for membership ${data.id}:`, error); logger.error(`Error checking expiry for membership ${membershipId}:`, error);
return false; 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 { function calculateExpiryDate(startDate: Date, frequency: string): Date {
const expiry = new Date(startDate); const expiry = new Date(startDate);
switch (frequency.toLowerCase()) { switch (frequency.toLowerCase()) {
@ -135,6 +213,30 @@ function calculateExpiryDate(startDate: Date, frequency: string): Date {
return expiry; 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( async function processExpiredMembership(
membershipId: string, membershipId: string,
membershipData: MembershipData membershipData: MembershipData
@ -174,14 +276,31 @@ async function sendPlanExpiredNotification(
return; return;
} }
const expiryDate = membershipData.subscription?.assignedAt?.toDate(); let expiryDate: Date | undefined;
const formattedDate = expiryDate let formattedDate = "Unknown Date";
? expiryDate.toLocaleDateString("en-US", {
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", year: "numeric",
month: "long", month: "long",
day: "numeric", day: "numeric",
}) });
: "Unknown Date"; }
}
await app await app
.firestore() .firestore()
@ -203,9 +322,9 @@ async function sendPlanExpiredNotification(
membershipId, membershipId,
gymName, gymName,
formattedExpiryDate: formattedDate, formattedExpiryDate: formattedDate,
expiryDate: expiryDate: expiryDate
membershipData.subscription?.assignedAt || ? admin.firestore.Timestamp.fromDate(expiryDate)
admin.firestore.Timestamp.fromDate(new Date()), : admin.firestore.Timestamp.fromDate(new Date()),
}, },
}); });