(projectId: string, startDate: Date, endDate: Date)
| 49 | }); |
| 50 | |
| 51 | export async function createInvoice(projectId: string, startDate: Date, endDate: Date) { |
| 52 | return await kysely.transaction().execute(async (tx) => { |
| 53 | const existingInvoice = await tx |
| 54 | .selectFrom("Invoice") |
| 55 | .where("projectId", "=", projectId) |
| 56 | .where("createdAt", ">=", toUTC(new Date()).startOf("month").toDate()) |
| 57 | .where("billingPeriod", "=", getPreviousMonthWithYearString()) |
| 58 | .executeTakeFirst(); |
| 59 | |
| 60 | if (existingInvoice) return; |
| 61 | // 1. Create empty invoice |
| 62 | let invoice = await tx |
| 63 | .insertInto("Invoice") |
| 64 | .values({ |
| 65 | id: uuidv4(), |
| 66 | projectId: projectId, |
| 67 | amount: 0, |
| 68 | slug: generateInvoiceSlug(), |
| 69 | billingPeriod: getPreviousMonthWithYearString(), |
| 70 | }) |
| 71 | .returning([ |
| 72 | "id", |
| 73 | "createdAt", |
| 74 | "slug", |
| 75 | "amount", |
| 76 | "billingPeriod", |
| 77 | "status", |
| 78 | "description", |
| 79 | "projectId", |
| 80 | ]) |
| 81 | .executeTakeFirst(); |
| 82 | |
| 83 | if (!invoice) throw new Error("No data available for invoice generation"); |
| 84 | |
| 85 | // 2. Associate stats with invoice |
| 86 | await tx |
| 87 | .updateTable("UsageLog") |
| 88 | .set({ |
| 89 | invoiceId: invoice.id, |
| 90 | }) |
| 91 | .where("projectId", "=", projectId) |
| 92 | .where("createdAt", ">=", startDate) |
| 93 | .where("createdAt", "<=", endDate) |
| 94 | .where("invoiceId", "is", null) |
| 95 | .execute(); |
| 96 | |
| 97 | // 3. Calculate usage stats |
| 98 | const stats = await getStats( |
| 99 | tx.selectFrom("UsageLog as ul").where("ul.invoiceId", "=", invoice.id), |
| 100 | ); |
| 101 | |
| 102 | // 4. Calculate available credits |
| 103 | const creditsAvailable = await tx |
| 104 | .selectFrom("CreditAdjustment as ca") |
| 105 | .where("ca.projectId", "=", projectId) |
| 106 | .where("ca.createdAt", "<=", endDate) |
| 107 | .select(({ fn }) => fn.sum(sql<number>`amount`).as("amount")) |
| 108 | .executeTakeFirst(); |
no test coverage detected