feat: implement billing module for tenant subscription management and super-admin payment processing
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Post, Query, UseGuards } from '@nestjs/common';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
import { TenantWalletService } from './tenant-wallet.service';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||
@@ -39,4 +39,35 @@ export class TenantWalletController {
|
||||
currency || 'JOD',
|
||||
);
|
||||
}
|
||||
|
||||
/** كشف حساب تفصيلي للدفتر. book يمكن أن يكون 'revenue' أو 'pending' */
|
||||
@Get('statement')
|
||||
statement(
|
||||
@CurrentUser() user: AuthUser,
|
||||
@Query('book') book: 'revenue' | 'pending',
|
||||
@Query('currency') currency?: string,
|
||||
@Query('limit') limit?: string,
|
||||
@Query('offset') offset?: string,
|
||||
) {
|
||||
return this.wallet.statement(
|
||||
book || 'revenue',
|
||||
user.tenantId,
|
||||
currency || 'JOD',
|
||||
limit ? parseInt(limit, 10) : 100,
|
||||
offset ? parseInt(offset, 10) : 0,
|
||||
);
|
||||
}
|
||||
|
||||
/** سحب أرباح المستأجر (من دفتر الإيرادات حصراً) */
|
||||
@Post('withdraw')
|
||||
withdraw(
|
||||
@CurrentUser() user: AuthUser,
|
||||
@Body() body: { amount: number; currency?: string },
|
||||
) {
|
||||
return this.wallet.withdraw(
|
||||
user.tenantId,
|
||||
Number(body.amount),
|
||||
body.currency || 'JOD',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +142,43 @@ export class TenantWalletService {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* كشف الحساب التفصيلي للدفتر.
|
||||
*/
|
||||
async statement(book: Book, tenantId: string, currency = 'JOD', limit = 100, offset = 0) {
|
||||
const repo = this.repoFor(book);
|
||||
const [rows, total] = await repo.findAndCount({
|
||||
where: { tenant_id: tenantId, currency } as any,
|
||||
order: { created_at: 'DESC' },
|
||||
take: limit,
|
||||
skip: offset,
|
||||
});
|
||||
return { data: rows, total, limit, offset };
|
||||
}
|
||||
|
||||
/**
|
||||
* سحب أرباح المستأجر (من دفتر الإيرادات حصراً).
|
||||
*/
|
||||
async withdraw(tenantId: string, amount: number, currency = 'JOD') {
|
||||
if (amount <= 0) throw new BadRequestException('Amount must be positive');
|
||||
|
||||
const currentBalance = await this.balance('revenue', tenantId, currency);
|
||||
if (currentBalance < amount) {
|
||||
throw new BadRequestException('Insufficient revenue balance');
|
||||
}
|
||||
|
||||
const ref = `PAYOUT-${Date.now()}`;
|
||||
const entry = await this.post('revenue', {
|
||||
tenantId,
|
||||
amount: -amount, // سالب = سحب
|
||||
currency,
|
||||
reason: 'payout', // Should exist or fall under general
|
||||
ref,
|
||||
});
|
||||
|
||||
return { success: true, ref, withdrawn: amount, remaining: currentBalance - amount };
|
||||
}
|
||||
|
||||
/** اختصارات مقروءة عند نقطة الاستدعاء — تمنع تمرير الدفتر الخطأ سهواً. */
|
||||
creditRevenue(e: PostEntry, em?: EntityManager) {
|
||||
return this.post('revenue', e, em);
|
||||
|
||||
Reference in New Issue
Block a user