feat(miniapp): carry overdue billing and admin role flows

This commit is contained in:
2026-03-23 15:44:55 +04:00
parent ee8c53d89b
commit 5af14e101e
44 changed files with 2965 additions and 329 deletions

View File

@@ -23,6 +23,7 @@
"0019_faithful_madame_masque.sql": "38711341799b04a7c47fcc64fd19faf5b26e6f183d6a4c01d492b9929cd63641",
"0020_natural_mauler.sql": "a80a4a0196a3b4931040850089346d1bc99b34a5afca77d6d62478ee4b8902c1",
"0020_silver_payments.sql": "9686235c75453f1eaa016f2f4ab7fce8fe964c76a4e3515987a2b9f90bd7b1ad",
"0021_sharp_payer.sql": "973596e154382984ba7769979ea58298b6d93c5139540854be01e8b283ddb4f1"
"0021_sharp_payer.sql": "973596e154382984ba7769979ea58298b6d93c5139540854be01e8b283ddb4f1",
"0022_carry_purchase_history.sql": "f031c9736e43e71eec3263a323332c29de9324c6409db034b0760051c8a9f074"
}
}

View File

@@ -0,0 +1,34 @@
ALTER TABLE "purchase_messages"
ADD COLUMN "cycle_id" uuid REFERENCES "billing_cycles"("id") ON DELETE SET NULL;
--> statement-breakpoint
CREATE INDEX "purchase_messages_cycle_idx" ON "purchase_messages" USING btree ("cycle_id");
--> statement-breakpoint
CREATE TABLE "payment_purchase_allocations" (
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
"payment_record_id" uuid NOT NULL,
"purchase_id" uuid NOT NULL,
"member_id" uuid NOT NULL,
"amount_minor" bigint NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL
);
--> statement-breakpoint
ALTER TABLE "payment_purchase_allocations"
ADD CONSTRAINT "payment_purchase_allocations_payment_record_id_payment_records_id_fk"
FOREIGN KEY ("payment_record_id") REFERENCES "public"."payment_records"("id")
ON DELETE cascade ON UPDATE no action;
--> statement-breakpoint
ALTER TABLE "payment_purchase_allocations"
ADD CONSTRAINT "payment_purchase_allocations_purchase_id_purchase_messages_id_fk"
FOREIGN KEY ("purchase_id") REFERENCES "public"."purchase_messages"("id")
ON DELETE cascade ON UPDATE no action;
--> statement-breakpoint
ALTER TABLE "payment_purchase_allocations"
ADD CONSTRAINT "payment_purchase_allocations_member_id_members_id_fk"
FOREIGN KEY ("member_id") REFERENCES "public"."members"("id")
ON DELETE cascade ON UPDATE no action;
--> statement-breakpoint
CREATE INDEX "payment_purchase_allocations_payment_idx"
ON "payment_purchase_allocations" USING btree ("payment_record_id");
--> statement-breakpoint
CREATE INDEX "payment_purchase_allocations_purchase_member_idx"
ON "payment_purchase_allocations" USING btree ("purchase_id","member_id");

View File

@@ -155,6 +155,13 @@
"when": 1774200000000,
"tag": "0021_sharp_payer",
"breakpoints": true
},
{
"idx": 22,
"version": "7",
"when": 1774205000000,
"tag": "0022_carry_purchase_history",
"breakpoints": true
}
]
}

View File

@@ -414,6 +414,9 @@ export const purchaseMessages = pgTable(
householdId: uuid('household_id')
.notNull()
.references(() => households.id, { onDelete: 'cascade' }),
cycleId: uuid('cycle_id').references(() => billingCycles.id, {
onDelete: 'set null'
}),
senderMemberId: uuid('sender_member_id').references(() => members.id, {
onDelete: 'set null'
}),
@@ -444,6 +447,7 @@ export const purchaseMessages = pgTable(
table.householdId,
table.telegramThreadId
),
cycleIdx: index('purchase_messages_cycle_idx').on(table.cycleId),
senderIdx: index('purchase_messages_sender_idx').on(table.senderTelegramUserId),
tgMessageUnique: uniqueIndex('purchase_messages_household_tg_message_unique').on(
table.householdId,
@@ -662,6 +666,31 @@ export const paymentRecords = pgTable(
})
)
export const paymentPurchaseAllocations = pgTable(
'payment_purchase_allocations',
{
id: uuid('id').defaultRandom().primaryKey(),
paymentRecordId: uuid('payment_record_id')
.notNull()
.references(() => paymentRecords.id, { onDelete: 'cascade' }),
purchaseId: uuid('purchase_id')
.notNull()
.references(() => purchaseMessages.id, { onDelete: 'cascade' }),
memberId: uuid('member_id')
.notNull()
.references(() => members.id, { onDelete: 'cascade' }),
amountMinor: bigint('amount_minor', { mode: 'bigint' }).notNull(),
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull()
},
(table) => ({
paymentIdx: index('payment_purchase_allocations_payment_idx').on(table.paymentRecordId),
purchaseMemberIdx: index('payment_purchase_allocations_purchase_member_idx').on(
table.purchaseId,
table.memberId
)
})
)
export const settlements = pgTable(
'settlements',
{
@@ -732,4 +761,5 @@ export type TopicMessage = typeof topicMessages.$inferSelect
export type AnonymousMessage = typeof anonymousMessages.$inferSelect
export type PaymentConfirmation = typeof paymentConfirmations.$inferSelect
export type PaymentRecord = typeof paymentRecords.$inferSelect
export type PaymentPurchaseAllocation = typeof paymentPurchaseAllocations.$inferSelect
export type Settlement = typeof settlements.$inferSelect