feat: add student wallet utility billing

This commit is contained in:
2026-07-14 20:39:32 +08:00
parent b480070e69
commit c75a08affe
29 changed files with 986 additions and 548 deletions

View File

@@ -33,12 +33,24 @@ export class Bill {
@Column({ name: 'total_amount', type: 'decimal', precision: 10, scale: 2, default: 0 })
totalAmount: number;
@Column({ name: 'deposit_deducted_amount', type: 'decimal', precision: 10, scale: 2, default: 0 })
depositDeductedAmount: number;
@Column({ type: 'varchar', length: 30, default: 'batch' })
source: 'batch' | 'student_utility';
@Column({ type: 'varchar', length: 20, default: 'draft' })
@Column({ name: 'paid_amount', type: 'decimal', precision: 10, scale: 2, default: 0 })
paidAmount: number;
@Column({ name: 'outstanding_amount', type: 'decimal', precision: 10, scale: 2, default: 0 })
outstandingAmount: number;
@Column({ type: 'varchar', length: 20, default: 'unpaid' })
status: string;
@Column({ name: 'cancelled_at', type: 'datetime', nullable: true })
cancelledAt: Date | null;
@Column({ name: 'cancel_reason', type: 'varchar', length: 300, nullable: true })
cancelReason: string | null;
@CreateDateColumn({ name: 'generated_at' })
generatedAt: Date;

View File

@@ -35,3 +35,6 @@ export { ResultArchive } from './result-archive.entity';
export { ArchiveAttachment } from './archive-attachment.entity';
export { StudentDingMapping } from './student-ding-mapping.entity';
export { AiConfig } from '../ai-config/ai-config.entity';
export * from './student-wallet.entity';
export * from './wallet-transaction.entity';

View File

@@ -34,6 +34,9 @@ export class PersonalExpense {
@Column({ name: 'recorded_by', nullable: true })
recordedBy: number;
@Column({ name: 'bill_id', type: 'integer', nullable: true })
billId: number | null;
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;

View File

@@ -0,0 +1,32 @@
import {
Column,
CreateDateColumn,
Entity,
JoinColumn,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
import { Student } from './student.entity';
@Entity('student_wallets')
export class StudentWallet {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'student_id', type: 'integer', unique: true })
studentId: number;
@Column({ type: 'decimal', precision: 12, scale: 2, default: 0 })
balance: number;
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
@UpdateDateColumn({ name: 'updated_at' })
updatedAt: Date;
@OneToOne(() => Student, { onDelete: 'CASCADE' })
@JoinColumn({ name: 'student_id' })
student: Student;
}

View File

@@ -0,0 +1,32 @@
import { Column, CreateDateColumn, Entity, Index, PrimaryGeneratedColumn } from 'typeorm';
@Entity('wallet_transactions')
@Index(['studentId', 'createdAt'])
export class WalletTransaction {
@PrimaryGeneratedColumn()
id: number;
@Column({ name: 'student_id', type: 'integer' })
studentId: number;
@Column({ name: 'bill_id', type: 'integer', nullable: true })
billId: number | null;
@Column({ type: 'varchar', length: 30 })
type: 'recharge' | 'adjustment' | 'bill_payment' | 'bill_refund';
@Column({ type: 'decimal', precision: 12, scale: 2 })
amount: number;
@Column({ name: 'balance_after', type: 'decimal', precision: 12, scale: 2 })
balanceAfter: number;
@Column({ type: 'varchar', length: 300, nullable: true })
description: string | null;
@Column({ name: 'recorded_by', type: 'integer', nullable: true })
recordedBy: number | null;
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
}