Files
trackgo-be/src/modules/sales/sales-invoices/sales-invoices-write.controller.ts
T
shancheas 51db4f4a4d Refactor privilege management to support hierarchical privilege keys
- Updated privilege key structure to use a 3- or 4-part dotted hierarchy (e.g., `GROUP.PARENT.MODULE`).
- Modified the `RequirePrivilege` decorator to accept multiple keys, allowing for OR logic in privilege checks.
- Enhanced `PrivilegesGuard` to validate against multiple privilege keys, improving access control logic.
- Created migration scripts to update existing privilege keys in the database to the new format.
- Updated related services, controllers, and tests to accommodate the new privilege key structure and validation logic.
2026-09-01 13:14:58 +07:00

206 lines
5.9 KiB
TypeScript

import {
BadRequestException,
Body,
Controller,
Delete,
HttpCode,
Param,
ParseUUIDPipe,
Patch,
Post,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import {
ApiBearerAuth,
ApiBody,
ApiConsumes,
ApiCreatedResponse,
ApiForbiddenResponse,
ApiNoContentResponse,
ApiNotFoundResponse,
ApiOkResponse,
ApiOperation,
ApiTags,
ApiUnauthorizedResponse,
} from '@nestjs/swagger';
import { CurrentUser } from '../../../common/decorators/current-user.decorator';
import { RequirePrivilege } from '../../../common/decorators/require-privilege.decorator';
import { BEARER_AUTH_NAME } from '../../../common/swagger/setup-swagger';
import { isAllowedCsvUpload } from '../shared/sales-fields';
import { SALES_INVOICE_PRIVILEGE_KEYS } from './sales-invoices-read.controller';
import { SalesInvoicesService } from './sales-invoices.service';
import {
BulkIdsDto,
BulkStatusDto,
CreateSalesInvoiceDto,
SalesInvoiceDto,
UpdateSalesInvoiceDto,
UpdateSalesInvoiceStatusDto,
} from './dto/sales-invoice.dto';
@ApiTags('sales-invoices')
@ApiBearerAuth(BEARER_AUTH_NAME)
@Controller('sales-invoices')
export class SalesInvoicesWriteController {
constructor(private readonly salesInvoicesService: SalesInvoicesService) {}
@Post('import')
@RequirePrivilege(SALES_INVOICE_PRIVILEGE_KEYS, 'import')
@UseInterceptors(
FileInterceptor('file', {
limits: { fileSize: 1_048_576 },
fileFilter: (_req, file, cb) => {
if (!isAllowedCsvUpload(file)) {
cb(new BadRequestException('Only CSV files are allowed'), false);
return;
}
cb(null, true);
},
}),
)
@ApiConsumes('multipart/form-data')
@ApiBody({
schema: {
type: 'object',
properties: {
file: { type: 'string', format: 'binary' },
},
required: ['file'],
},
})
@ApiOperation({ summary: 'Import sales invoices from CSV' })
@ApiOkResponse({
schema: { properties: { imported: { type: 'number' } } },
})
@ApiUnauthorizedResponse()
@ApiForbiddenResponse()
importCsv(
@UploadedFile() file: { buffer?: Buffer } | undefined,
@CurrentUser('id') userId: string,
): Promise<{ imported: number }> {
const csv = file?.buffer?.toString('utf8') ?? '';
return this.salesInvoicesService.importCsv(csv, userId);
}
@Post('bulk-delete')
@HttpCode(200)
@RequirePrivilege(SALES_INVOICE_PRIVILEGE_KEYS, 'delete')
@ApiOperation({ summary: 'Bulk delete sales invoices' })
@ApiOkResponse({
schema: { properties: { deleted: { type: 'number' } } },
})
@ApiUnauthorizedResponse()
@ApiForbiddenResponse()
bulkDelete(@Body() dto: BulkIdsDto): Promise<{ deleted: number }> {
return this.salesInvoicesService.bulkDelete(dto.ids);
}
@Post('bulk-status')
@HttpCode(200)
@RequirePrivilege(SALES_INVOICE_PRIVILEGE_KEYS, 'update')
@ApiOperation({ summary: 'Bulk update sales invoice status' })
@ApiOkResponse({
schema: { properties: { updated: { type: 'number' } } },
})
@ApiUnauthorizedResponse()
@ApiForbiddenResponse()
bulkStatus(
@Body() dto: BulkStatusDto,
@CurrentUser('id') userId: string,
): Promise<{ updated: number }> {
return this.salesInvoicesService.bulkUpdateStatus(
dto.ids,
dto.status,
userId,
);
}
@Post()
@RequirePrivilege(SALES_INVOICE_PRIVILEGE_KEYS, 'create')
@ApiOperation({ summary: 'Create sales invoice' })
@ApiCreatedResponse({ type: SalesInvoiceDto })
@ApiUnauthorizedResponse()
@ApiForbiddenResponse()
create(
@Body() dto: CreateSalesInvoiceDto,
@CurrentUser('id') userId: string,
): Promise<SalesInvoiceDto> {
return this.salesInvoicesService.create({
code: dto.code,
salesOrderId: dto.salesOrderId,
packingSlipId: dto.packingSlipId,
date: dto.date,
salesPersonId: dto.salesPersonId,
branchId: dto.branchId,
divisionId: dto.divisionId,
customerId: dto.customerId,
address: dto.address,
latitude: dto.latitude,
longitude: dto.longitude,
notes: dto.notes,
products: dto.products,
status: dto.status,
userId,
});
}
@Patch(':id/status')
@RequirePrivilege(SALES_INVOICE_PRIVILEGE_KEYS, 'update')
@ApiOperation({ summary: 'Update sales invoice status' })
@ApiOkResponse({ type: SalesInvoiceDto })
@ApiNotFoundResponse()
@ApiUnauthorizedResponse()
@ApiForbiddenResponse()
updateStatus(
@Param('id', ParseUUIDPipe) id: string,
@Body() dto: UpdateSalesInvoiceStatusDto,
@CurrentUser('id') userId: string,
): Promise<SalesInvoiceDto> {
return this.salesInvoicesService.updateStatus(id, dto.status, userId);
}
@Patch(':id')
@RequirePrivilege(SALES_INVOICE_PRIVILEGE_KEYS, 'update')
@ApiOperation({ summary: 'Update sales invoice (not status)' })
@ApiOkResponse({ type: SalesInvoiceDto })
@ApiNotFoundResponse()
@ApiUnauthorizedResponse()
@ApiForbiddenResponse()
update(
@Param('id', ParseUUIDPipe) id: string,
@Body() dto: UpdateSalesInvoiceDto,
@CurrentUser('id') userId: string,
): Promise<SalesInvoiceDto> {
return this.salesInvoicesService.update(id, {
code: dto.code,
salesOrderId: dto.salesOrderId,
packingSlipId: dto.packingSlipId,
date: dto.date,
salesPersonId: dto.salesPersonId,
branchId: dto.branchId,
divisionId: dto.divisionId,
customerId: dto.customerId,
address: dto.address,
latitude: dto.latitude,
longitude: dto.longitude,
notes: dto.notes,
products: dto.products,
userId,
});
}
@Delete(':id')
@HttpCode(204)
@RequirePrivilege(SALES_INVOICE_PRIVILEGE_KEYS, 'delete')
@ApiOperation({ summary: 'Delete sales invoice' })
@ApiNoContentResponse()
@ApiNotFoundResponse()
@ApiUnauthorizedResponse()
@ApiForbiddenResponse()
async delete(@Param('id', ParseUUIDPipe) id: string): Promise<void> {
await this.salesInvoicesService.delete(id);
}
}