138 lines
4.7 KiB
TypeScript
138 lines
4.7 KiB
TypeScript
/**
|
|
* ════════════════════════════════════════════════════════════
|
|
* مُصادَق (Musadaq) — Invoices Controller / متحكم الفواتير
|
|
* ════════════════════════════════════════════════════════════
|
|
* Handles HTTP requests related to invoice management.
|
|
* يتعامل مع طلبات HTTP المتعلقة بإدارة الفواتير.
|
|
* ════════════════════════════════════════════════════════════
|
|
*/
|
|
|
|
import {
|
|
Controller,
|
|
Get,
|
|
Post,
|
|
Patch,
|
|
Body,
|
|
Param,
|
|
UseGuards,
|
|
UploadedFile,
|
|
UseInterceptors,
|
|
ParseUUIDPipe,
|
|
Res,
|
|
} from '@nestjs/common';
|
|
import { Response } from 'express';
|
|
import { FileInterceptor } from '@nestjs/platform-express';
|
|
import { InvoicesService } from './invoice.service';
|
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
|
import { RolesGuard } from '../../common/guards/roles.guard';
|
|
import { Roles } from '../../common/decorators/roles.decorator';
|
|
import { UserRole } from '../users/enums/role.enum';
|
|
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
|
|
|
@Controller('invoices')
|
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
export class InvoicesController {
|
|
constructor(private invoicesService: InvoicesService) {}
|
|
|
|
/**
|
|
* قائمة جميع الفواتير للمكتب
|
|
* List all invoices for the entire accounting office (tenant)
|
|
*/
|
|
@Get()
|
|
async findAllByTenant(@CurrentUser() user: any) {
|
|
return this.invoicesService.findAllByTenant(user);
|
|
}
|
|
|
|
/**
|
|
* رفع فاتورة لشركة محددة
|
|
* Upload an invoice file for a specific company
|
|
*/
|
|
@Post('upload/:companyId')
|
|
@Roles(UserRole.ADMIN, UserRole.ACCOUNTANT, UserRole.SUPER_ADMIN)
|
|
@UseInterceptors(FileInterceptor('file'))
|
|
async upload(
|
|
@CurrentUser() user: any,
|
|
@Param('companyId', ParseUUIDPipe) companyId: string,
|
|
@UploadedFile() file: Express.Multer.File,
|
|
) {
|
|
return this.invoicesService.upload(user, companyId, file);
|
|
}
|
|
|
|
/**
|
|
* قائمة الفواتير لشركة محددة
|
|
* List all invoices for a specific company
|
|
*/
|
|
@Get('company/:companyId')
|
|
async findAll(
|
|
@CurrentUser() user: any,
|
|
@Param('companyId', ParseUUIDPipe) companyId: string,
|
|
) {
|
|
return this.invoicesService.findAll(user, companyId);
|
|
}
|
|
|
|
/**
|
|
* تفاصيل فاتورة محددة
|
|
* Get details of a specific invoice
|
|
*/
|
|
@Get(':id')
|
|
async findOne(
|
|
@CurrentUser() user: any,
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
) {
|
|
return this.invoicesService.findOne(user, id);
|
|
}
|
|
|
|
/**
|
|
* تحديث بيانات الفاتورة يدوياً
|
|
*/
|
|
/**
|
|
* إرسال الفاتورة إلى بوابة جو فوترة الحكومية
|
|
* Submit an invoice to the official JoFotara portal
|
|
*/
|
|
@Post(':id/submit')
|
|
@Roles(UserRole.ADMIN, UserRole.ACCOUNTANT, UserRole.SUPER_ADMIN)
|
|
async submit(@CurrentUser() user: any, @Param('id', ParseUUIDPipe) id: string) {
|
|
return this.invoicesService.submitToJoFotara(user, id);
|
|
}
|
|
|
|
/**
|
|
* حذف الفاتورة نهائياً
|
|
* Permanently delete an invoice
|
|
*/
|
|
@Post(':id/delete') // Using POST for delete to match frontend request style or use standard DELETE
|
|
@Roles(UserRole.ADMIN, UserRole.ACCOUNTANT, UserRole.SUPER_ADMIN)
|
|
async remove(@CurrentUser() user: any, @Param('id', ParseUUIDPipe) id: string) {
|
|
return this.invoicesService.remove(user, id);
|
|
}
|
|
|
|
/**
|
|
* الحصول على الملف الأصلي للفاتورة
|
|
* Download/Stream the original invoice file
|
|
*/
|
|
@Get(':id/file')
|
|
async getFile(
|
|
@CurrentUser() user: any,
|
|
@Param('id', ParseUUIDPipe) id: string,
|
|
@Res({ passthrough: true }) res: Response,
|
|
) {
|
|
const streamableFile = await this.invoicesService.getFile(user, id);
|
|
|
|
// We need to determine the content type to ensure it opens inline in the browser (iframe)
|
|
// We can fetch the invoice details first to get the extension.
|
|
const invoice = await this.invoicesService.findOne(user, id);
|
|
let mimeType = 'application/pdf'; // Default fallback
|
|
if (invoice.original_file_path) {
|
|
const ext = invoice.original_file_path.split('.').pop()?.toLowerCase();
|
|
if (ext === 'jpg' || ext === 'jpeg') mimeType = 'image/jpeg';
|
|
else if (ext === 'png') mimeType = 'image/png';
|
|
}
|
|
|
|
res.set({
|
|
'Content-Type': mimeType,
|
|
'Content-Disposition': 'inline', // This forces the browser to display it instead of downloading
|
|
});
|
|
|
|
return streamableFile;
|
|
}
|
|
}
|