2025-08-27 10:54:05 +09:00
|
|
|
import { Body, Controller, Get, Param, Post, UseGuards, Request, BadRequestException } from "@nestjs/common";
|
2025-08-27 20:01:46 +09:00
|
|
|
import { OrderOrchestrator } from "./services/order-orchestrator.service";
|
2025-08-23 17:24:37 +09:00
|
|
|
import { ApiBearerAuth, ApiOperation, ApiParam, ApiResponse, ApiTags } from "@nestjs/swagger";
|
|
|
|
|
import { JwtAuthGuard } from "../auth/guards/jwt-auth.guard";
|
|
|
|
|
import { RequestWithUser } from "../auth/auth.types";
|
2025-08-27 10:54:05 +09:00
|
|
|
import { Logger } from "nestjs-pino";
|
2025-08-27 20:01:46 +09:00
|
|
|
import { IsString, IsArray, IsIn, IsNotEmpty, IsOptional, IsObject, ValidateNested } from "class-validator";
|
|
|
|
|
import { Type } from "class-transformer";
|
|
|
|
|
|
|
|
|
|
class OrderConfigurations {
|
|
|
|
|
// Activation (All order types)
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsIn(["Immediate", "Scheduled"])
|
|
|
|
|
activationType?: "Immediate" | "Scheduled";
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
scheduledAt?: string;
|
|
|
|
|
|
|
|
|
|
// Internet specific
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsIn(["IPoE-BYOR", "IPoE-HGW", "PPPoE"])
|
|
|
|
|
accessMode?: "IPoE-BYOR" | "IPoE-HGW" | "PPPoE";
|
|
|
|
|
|
|
|
|
|
// SIM specific
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsIn(["eSIM", "Physical SIM"])
|
|
|
|
|
simType?: "eSIM" | "Physical SIM";
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
eid?: string; // Required for eSIM
|
|
|
|
|
|
|
|
|
|
// MNP/Porting
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
isMnp?: string; // "true" | "false"
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
mnpNumber?: string;
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
mnpExpiry?: string;
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
mnpPhone?: string;
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
mvnoAccountNumber?: string;
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
portingLastName?: string;
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
portingFirstName?: string;
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
portingLastNameKatakana?: string;
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
portingFirstNameKatakana?: string;
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsIn(["Male", "Female", "Corporate/Other"])
|
|
|
|
|
portingGender?: "Male" | "Female" | "Corporate/Other";
|
|
|
|
|
|
|
|
|
|
@IsOptional()
|
|
|
|
|
@IsString()
|
|
|
|
|
portingDateOfBirth?: string;
|
|
|
|
|
|
|
|
|
|
// VPN region is inferred from product VPN_Region__c field, no user input needed
|
|
|
|
|
}
|
2025-08-23 17:24:37 +09:00
|
|
|
|
2025-08-27 10:54:05 +09:00
|
|
|
class CreateOrderBody {
|
|
|
|
|
@IsString()
|
|
|
|
|
@IsNotEmpty()
|
2025-08-27 20:01:46 +09:00
|
|
|
@IsIn(["Internet", "SIM", "VPN", "Other"])
|
|
|
|
|
orderType: "Internet" | "SIM" | "VPN" | "Other";
|
|
|
|
|
|
|
|
|
|
@IsArray()
|
|
|
|
|
@IsString({ each: true })
|
|
|
|
|
@IsNotEmpty({ each: true })
|
|
|
|
|
skus: string[];
|
2025-08-27 10:54:05 +09:00
|
|
|
|
2025-08-27 20:01:46 +09:00
|
|
|
@IsOptional()
|
2025-08-27 10:54:05 +09:00
|
|
|
@IsObject()
|
2025-08-27 20:01:46 +09:00
|
|
|
@ValidateNested()
|
|
|
|
|
@Type(() => OrderConfigurations)
|
|
|
|
|
configurations?: OrderConfigurations;
|
2025-08-23 17:24:37 +09:00
|
|
|
}
|
2025-08-20 18:02:50 +09:00
|
|
|
|
2025-08-21 15:24:40 +09:00
|
|
|
@ApiTags("orders")
|
|
|
|
|
@Controller("orders")
|
2025-08-20 18:02:50 +09:00
|
|
|
export class OrdersController {
|
2025-08-27 10:54:05 +09:00
|
|
|
constructor(
|
2025-08-27 20:01:46 +09:00
|
|
|
private orderOrchestrator: OrderOrchestrator,
|
2025-08-27 10:54:05 +09:00
|
|
|
private readonly logger: Logger
|
|
|
|
|
) {}
|
2025-08-20 18:02:50 +09:00
|
|
|
|
2025-08-23 17:24:37 +09:00
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiBearerAuth()
|
|
|
|
|
@Post()
|
2025-08-27 20:01:46 +09:00
|
|
|
@ApiOperation({ summary: "Create Salesforce Order using clean architecture" })
|
2025-08-23 17:24:37 +09:00
|
|
|
@ApiResponse({ status: 201 })
|
|
|
|
|
async create(@Request() req: RequestWithUser, @Body() body: CreateOrderBody) {
|
2025-08-27 10:54:05 +09:00
|
|
|
this.logger.log({
|
|
|
|
|
body,
|
|
|
|
|
userId: req.user?.id,
|
2025-08-27 20:01:46 +09:00
|
|
|
orderType: body?.orderType,
|
|
|
|
|
skus: body?.skus,
|
|
|
|
|
configurations: body?.configurations
|
2025-08-27 10:54:05 +09:00
|
|
|
}, "Order creation request received");
|
|
|
|
|
|
2025-08-27 20:01:46 +09:00
|
|
|
try {
|
|
|
|
|
return await this.orderOrchestrator.createOrder(req.user.id, body);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
this.logger.error({
|
|
|
|
|
error: error instanceof Error ? error.message : String(error),
|
|
|
|
|
body,
|
|
|
|
|
userId: req.user?.id
|
|
|
|
|
}, "Order creation failed");
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiBearerAuth()
|
|
|
|
|
@Get("user")
|
|
|
|
|
@ApiOperation({ summary: "Get user's orders" })
|
|
|
|
|
async getUserOrders(@Request() req: RequestWithUser) {
|
|
|
|
|
return this.orderOrchestrator.getOrdersForUser(req.user.id);
|
2025-08-23 17:24:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiBearerAuth()
|
|
|
|
|
@Get(":sfOrderId")
|
|
|
|
|
@ApiOperation({ summary: "Get order summary/status" })
|
|
|
|
|
@ApiParam({ name: "sfOrderId", type: String })
|
|
|
|
|
async get(@Request() req: RequestWithUser, @Param("sfOrderId") sfOrderId: string) {
|
2025-08-27 20:01:46 +09:00
|
|
|
return this.orderOrchestrator.getOrder(sfOrderId);
|
2025-08-23 17:24:37 +09:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
|
|
|
@ApiBearerAuth()
|
|
|
|
|
@Post(":sfOrderId/provision")
|
|
|
|
|
@ApiOperation({ summary: "Trigger provisioning for an approved order" })
|
|
|
|
|
@ApiParam({ name: "sfOrderId", type: String })
|
|
|
|
|
async provision(@Request() req: RequestWithUser, @Param("sfOrderId") sfOrderId: string) {
|
2025-08-27 20:01:46 +09:00
|
|
|
return this.orderOrchestrator.provisionOrder(req.user.id, sfOrderId);
|
2025-08-23 17:24:37 +09:00
|
|
|
}
|
2025-08-20 18:02:50 +09:00
|
|
|
}
|