- 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.
206 lines
5.9 KiB
TypeScript
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);
|
|
}
|
|
}
|