工单和发票
酒厂银行账户
This commit is contained in:
@@ -170,6 +170,23 @@ export class CreateTicketDto {
|
||||
extraJson?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
export class AdminCreateTicketDto {
|
||||
@IsString()
|
||||
@IsIn(['REFUND', 'RESHIPMENT', 'ALERT', 'DAMAGE_RETURN', 'RETURN_REFUND'])
|
||||
ticketType: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
orderNo: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
remark?: string;
|
||||
|
||||
@IsOptional()
|
||||
evidenceUrls?: string[];
|
||||
}
|
||||
|
||||
export class UpdateTicketStatusDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
|
||||
@@ -2,8 +2,14 @@ import { Body, Controller, Get, Param, Post, Query, UseGuards } from '@nestjs/co
|
||||
import { HqAuthGuard } from '../../common/guards/hq-auth.guard';
|
||||
import { AuthUser } from '../../common/guards/jwt-auth.guard';
|
||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||
import { HqOperation } from '../../common/hq-operation/hq-operation.decorator';
|
||||
import { HqOperationAction } from '../../common/hq-operation/hq-operation.constants';
|
||||
import { TradeService } from '../trade/trade.service';
|
||||
import { IssueInvoiceDto, RejectInvoiceDto } from '../trade/dto/after-sale.dto';
|
||||
import {
|
||||
AdminCreateInvoiceDto,
|
||||
IssueInvoiceDto,
|
||||
RejectInvoiceDto,
|
||||
} from '../trade/dto/after-sale.dto';
|
||||
|
||||
@Controller('admin/invoices')
|
||||
@UseGuards(HqAuthGuard)
|
||||
@@ -23,12 +29,29 @@ export class AdminInvoicesController {
|
||||
});
|
||||
}
|
||||
|
||||
@Post()
|
||||
@HqOperation({
|
||||
action: HqOperationAction.INVOICE_CREATE,
|
||||
refType: 'INVOICE',
|
||||
batch: true,
|
||||
includeBody: true,
|
||||
})
|
||||
create(@Body() body: AdminCreateInvoiceDto) {
|
||||
return this.tradeService.adminCreateInvoice(body);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
detail(@Param('id') id: string) {
|
||||
return this.tradeService.adminGetInvoice(BigInt(id));
|
||||
}
|
||||
|
||||
@Post(':id/issue')
|
||||
@HqOperation({
|
||||
action: HqOperationAction.INVOICE_ISSUE,
|
||||
refType: 'INVOICE',
|
||||
refIdParam: 'id',
|
||||
includeBody: true,
|
||||
})
|
||||
issue(
|
||||
@CurrentUser() user: AuthUser,
|
||||
@Param('id') id: string,
|
||||
@@ -38,6 +61,12 @@ export class AdminInvoicesController {
|
||||
}
|
||||
|
||||
@Post(':id/reject')
|
||||
@HqOperation({
|
||||
action: HqOperationAction.INVOICE_REJECT,
|
||||
refType: 'INVOICE',
|
||||
refIdParam: 'id',
|
||||
includeBody: true,
|
||||
})
|
||||
reject(
|
||||
@CurrentUser() user: AuthUser,
|
||||
@Param('id') id: string,
|
||||
|
||||
@@ -8,6 +8,7 @@ import { HqOperation } from '../../common/hq-operation/hq-operation.decorator';
|
||||
import { HqOperationAction } from '../../common/hq-operation/hq-operation.constants';
|
||||
import { AdminTicketsService } from './admin-tickets.service';
|
||||
import { TicketListQueryDto } from '../common/dto/common-query.dto';
|
||||
import { AdminCreateTicketDto } from '../common/dto/common-mutate.dto';
|
||||
|
||||
@Controller('admin/tickets')
|
||||
@UseGuards(HqAuthGuard)
|
||||
@@ -19,6 +20,17 @@ export class AdminTicketsController {
|
||||
return this.service.list(query);
|
||||
}
|
||||
|
||||
@Post()
|
||||
@HqOperation({
|
||||
action: HqOperationAction.TICKET_CREATE,
|
||||
refType: 'TICKET',
|
||||
batch: true,
|
||||
includeBody: true,
|
||||
})
|
||||
create(@Body() body: AdminCreateTicketDto) {
|
||||
return this.service.createByHq(body);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
detail(@Param('id') id: string) {
|
||||
return this.service.detail(BigInt(id));
|
||||
|
||||
@@ -40,6 +40,43 @@ export class AdminTicketsService {
|
||||
return this.ticketService.detail(id);
|
||||
}
|
||||
|
||||
async createByHq(body: {
|
||||
ticketType: string;
|
||||
orderNo: string;
|
||||
remark?: string;
|
||||
evidenceUrls?: string[];
|
||||
}) {
|
||||
const orderNo = body.orderNo?.trim();
|
||||
if (!orderNo) throw new BadRequestException('请填写订单号');
|
||||
const order = await this.prisma.order.findFirst({ where: { orderNo } });
|
||||
if (!order) throw new NotFoundException('订单不存在');
|
||||
if (order.status === 'PENDING_PAY' || order.status === 'CANCELLED') {
|
||||
throw new BadRequestException('当前订单不可创建工单');
|
||||
}
|
||||
if (['REFUNDING', 'REFUNDED'].includes(order.status) && body.ticketType !== 'ALERT') {
|
||||
throw new BadRequestException('订单已在退款流程中');
|
||||
}
|
||||
|
||||
const pending = await this.prisma.commonTicket.findFirst({
|
||||
where: {
|
||||
ticketType: body.ticketType as never,
|
||||
refType: 'ORDER',
|
||||
refId: order.id,
|
||||
status: { in: ['PENDING', 'OPEN'] },
|
||||
},
|
||||
});
|
||||
if (pending) throw new BadRequestException('该类型工单已在处理中');
|
||||
|
||||
const evidenceUrls = (body.evidenceUrls ?? []).filter((u) => typeof u === 'string' && u.trim());
|
||||
return this.ticketService.create({
|
||||
ticketType: body.ticketType,
|
||||
refType: 'ORDER',
|
||||
refId: order.id.toString(),
|
||||
remark: body.remark ?? '',
|
||||
extraJson: evidenceUrls.length ? { evidenceUrls } : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
private parseExtra(raw: unknown): TicketCollabExtra {
|
||||
if (!raw || typeof raw !== 'object') return {};
|
||||
return raw as TicketCollabExtra;
|
||||
|
||||
@@ -64,6 +64,13 @@ export class CreateInvoiceDto {
|
||||
remark?: string;
|
||||
}
|
||||
|
||||
export class AdminCreateInvoiceDto extends CreateInvoiceDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@MaxLength(32)
|
||||
orderNo: string;
|
||||
}
|
||||
|
||||
export class IssueInvoiceDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
|
||||
@@ -754,6 +754,27 @@ export class TradeService {
|
||||
return days;
|
||||
}
|
||||
|
||||
async adminCreateInvoice(
|
||||
body: {
|
||||
orderNo: string;
|
||||
titleType: string;
|
||||
invoiceKind: string;
|
||||
titleName: string;
|
||||
taxNo?: string;
|
||||
addressPhone?: string;
|
||||
bankAccount?: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
remark?: string;
|
||||
},
|
||||
) {
|
||||
const orderNo = body.orderNo?.trim();
|
||||
if (!orderNo) throw new BadRequestException('请填写订单号');
|
||||
const order = await this.prisma.order.findFirst({ where: { orderNo } });
|
||||
if (!order) throw new NotFoundException('订单不存在');
|
||||
return this.createInvoice(order.userId, order.id, body);
|
||||
}
|
||||
|
||||
async adminListInvoices(query: { status?: string; page?: number; pageSize?: number }) {
|
||||
const page = query.page ?? 1;
|
||||
const pageSize = query.pageSize ?? 20;
|
||||
|
||||
Reference in New Issue
Block a user