diff --git a/apps/bff/src/integrations/salesforce/services/salesforce-account.service.ts b/apps/bff/src/integrations/salesforce/services/salesforce-account.service.ts index cc2bb1ae..1a445f7b 100644 --- a/apps/bff/src/integrations/salesforce/services/salesforce-account.service.ts +++ b/apps/bff/src/integrations/salesforce/services/salesforce-account.service.ts @@ -177,7 +177,7 @@ export class SalesforceAccountService { const accountPayload: Record = { Name: `${data.firstName} ${data.lastName}`, - Phone: data.phone, + // Phone is stored on Contact, not Account // Portal tracking fields [this.portalStatusField]: "Active", [this.portalSourceField]: "Portal Checkout", @@ -236,12 +236,24 @@ export class SalesforceAccountService { email: data.email, }); + // Map gender values to Salesforce picklist values + const mapGenderToSalesforce = (gender: "male" | "female" | "other"): string => { + const mapping: Record<"male" | "female" | "other", string> = { + male: "男性", + female: "女性", + other: "Other", + }; + return mapping[gender]; + }; + const contactPayload: Record = { AccountId: data.accountId, FirstName: data.firstName, LastName: data.lastName, Email: data.email, - Phone: data.phone, + MobilePhone: data.phone, + Sex__c: mapGenderToSalesforce(data.gender), + Birthdate: data.dateOfBirth, // Salesforce expects YYYY-MM-DD format }; try { @@ -350,4 +362,6 @@ export interface CreateSalesforceContactRequest { lastName: string; email: string; phone: string; + gender: "male" | "female" | "other"; + dateOfBirth: string; // YYYY-MM-DD format } diff --git a/apps/bff/src/modules/auth/infra/workflows/signup-workflow.service.ts b/apps/bff/src/modules/auth/infra/workflows/signup-workflow.service.ts index 6bbc9d5f..0221a916 100644 --- a/apps/bff/src/modules/auth/infra/workflows/signup-workflow.service.ts +++ b/apps/bff/src/modules/auth/infra/workflows/signup-workflow.service.ts @@ -35,7 +35,7 @@ import { * * Orchestrates the signup process by coordinating: * - SignupValidationService: Validates customer numbers and preflight checks - * - SignupAccountResolverService: Resolves Salesforce accounts + * - SalesforceAccountService: Creates new Salesforce accounts and contacts * - SignupWhmcsService: Creates WHMCS clients * - SignupUserCreationService: Creates portal users with ID mappings */ @@ -210,38 +210,16 @@ export class SignupWorkflowService { } /** - * Resolve Salesforce account for signup - * Either finds existing account by customer number or creates new one + * Create a new Salesforce account for signup + * Always creates a new account - no lookup by customer number */ private async resolveSalesforceAccount(signupData: SignupRequest): Promise<{ accountSnapshot: SignupAccountSnapshot; customerNumberForWhmcs: string | null; }> { - const { email, firstName, lastName, phone, address, sfNumber } = signupData; - const normalizedCustomerNumber = this.accountResolver.normalizeCustomerNumber(sfNumber); + const { email, firstName, lastName, phone, address, gender, dateOfBirth } = signupData; - if (normalizedCustomerNumber) { - // Existing customer - find account by customer number - const resolved = await this.accountResolver.getAccountSnapshot(normalizedCustomerNumber); - if (!resolved) { - throw new BadRequestException( - `Salesforce account not found for Customer Number: ${normalizedCustomerNumber}` - ); - } - - if (resolved.WH_Account__c && resolved.WH_Account__c.trim() !== "") { - throw new ConflictException( - "You already have an account. Please use the login page to access your existing account." - ); - } - - return { - accountSnapshot: resolved, - customerNumberForWhmcs: normalizedCustomerNumber, - }; - } - - // New customer - create Salesforce account + // Check if account already exists by email const normalizedEmail = email.toLowerCase().trim(); const existingAccount = await this.salesforceAccountService.findByEmail(normalizedEmail); if (existingAccount) { @@ -266,6 +244,15 @@ export class SignupWorkflowService { throw new BadRequestException("Phone number is required for account creation"); } + if (!gender) { + throw new BadRequestException("Gender is required for account creation"); + } + + if (!dateOfBirth) { + throw new BadRequestException("Date of birth is required for account creation"); + } + + // Create new Salesforce account const created = await this.salesforceAccountService.createAccount({ firstName, lastName, @@ -280,6 +267,8 @@ export class SignupWorkflowService { lastName, email: normalizedEmail, phone, + gender, + dateOfBirth, // Address not added to Salesforce during signup }); diff --git a/apps/bff/src/modules/auth/infra/workflows/signup/signup-account-resolver.service.ts b/apps/bff/src/modules/auth/infra/workflows/signup/signup-account-resolver.service.ts index 09e8f6b8..093fd5e5 100644 --- a/apps/bff/src/modules/auth/infra/workflows/signup/signup-account-resolver.service.ts +++ b/apps/bff/src/modules/auth/infra/workflows/signup/signup-account-resolver.service.ts @@ -28,7 +28,8 @@ export class SignupAccountResolverService { async resolveOrCreate( signupData: SignupRequest ): Promise<{ snapshot: SignupAccountSnapshot; customerNumber: string | null }> { - const { sfNumber, email, firstName, lastName, phone, address } = signupData; + const { sfNumber, email, firstName, lastName, phone, address, gender, dateOfBirth } = + signupData; const normalizedCustomerNumber = this.normalizeCustomerNumber(sfNumber); if (normalizedCustomerNumber) { @@ -73,6 +74,14 @@ export class SignupAccountResolverService { throw new BadRequestException("Phone number is required for account creation"); } + if (!gender) { + throw new BadRequestException("Gender is required for account creation"); + } + + if (!dateOfBirth) { + throw new BadRequestException("Date of birth is required for account creation"); + } + const created = await this.salesforceAccountService.createAccount({ firstName, lastName, @@ -86,6 +95,8 @@ export class SignupAccountResolverService { lastName, email: normalizedEmail, phone, + gender, + dateOfBirth, }); const snapshot: SignupAccountSnapshot = { diff --git a/packages/domain/customer/contract.ts b/packages/domain/customer/contract.ts index a3503601..1216f659 100644 --- a/packages/domain/customer/contract.ts +++ b/packages/domain/customer/contract.ts @@ -42,6 +42,23 @@ export interface SalesforceAccountRecord { [key: string]: unknown; } +/** + * Salesforce contact record structure + * Raw structure from Salesforce API + */ +export interface SalesforceContactRecord { + Id: string; + AccountId?: string | null; + FirstName?: string | null; + LastName?: string | null; + Email?: string | null; + MobilePhone?: string | null; + Phone?: string | null; + Sex__c?: string | null; + Birthdate?: string | null; // YYYY-MM-DD format + [key: string]: unknown; +} + // ============================================================================ // Re-export Types from Schema (Schema-First Approach) // ============================================================================ diff --git a/packages/domain/customer/providers/index.ts b/packages/domain/customer/providers/index.ts index 8d66b4a1..ed015f22 100644 --- a/packages/domain/customer/providers/index.ts +++ b/packages/domain/customer/providers/index.ts @@ -10,7 +10,11 @@ export * from "./portal/index.js"; export * from "./whmcs/index.js"; // Provider-specific integration types (BFF-only convenience re-exports) -export type { SalesforceAccountFieldMap, SalesforceAccountRecord } from "../contract.js"; +export type { + SalesforceAccountFieldMap, + SalesforceAccountRecord, + SalesforceContactRecord, +} from "../contract.js"; export type { WhmcsAddClientParams, WhmcsValidateLoginParams,