Enhance Salesforce Account and Contact Creation in Signup Workflow

- Updated SalesforceAccountService to map gender values to Salesforce picklist values and added date of birth handling for contact creation.
- Modified SignupWorkflowService to require gender and date of birth during account creation, ensuring complete data submission.
- Adjusted SignupAccountResolverService to include gender and date of birth in account creation logic, improving data integrity.
- Introduced SalesforceContactRecord interface in domain to standardize contact data structure, enhancing type safety and validation.
This commit is contained in:
barsa 2026-01-06 16:52:02 +09:00
parent 1a3032bbd3
commit a8580b0d61
5 changed files with 66 additions and 31 deletions

View File

@ -177,7 +177,7 @@ export class SalesforceAccountService {
const accountPayload: Record<string, unknown> = { const accountPayload: Record<string, unknown> = {
Name: `${data.firstName} ${data.lastName}`, Name: `${data.firstName} ${data.lastName}`,
Phone: data.phone, // Phone is stored on Contact, not Account
// Portal tracking fields // Portal tracking fields
[this.portalStatusField]: "Active", [this.portalStatusField]: "Active",
[this.portalSourceField]: "Portal Checkout", [this.portalSourceField]: "Portal Checkout",
@ -236,12 +236,24 @@ export class SalesforceAccountService {
email: data.email, 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<string, unknown> = { const contactPayload: Record<string, unknown> = {
AccountId: data.accountId, AccountId: data.accountId,
FirstName: data.firstName, FirstName: data.firstName,
LastName: data.lastName, LastName: data.lastName,
Email: data.email, Email: data.email,
Phone: data.phone, MobilePhone: data.phone,
Sex__c: mapGenderToSalesforce(data.gender),
Birthdate: data.dateOfBirth, // Salesforce expects YYYY-MM-DD format
}; };
try { try {
@ -350,4 +362,6 @@ export interface CreateSalesforceContactRequest {
lastName: string; lastName: string;
email: string; email: string;
phone: string; phone: string;
gender: "male" | "female" | "other";
dateOfBirth: string; // YYYY-MM-DD format
} }

View File

@ -35,7 +35,7 @@ import {
* *
* Orchestrates the signup process by coordinating: * Orchestrates the signup process by coordinating:
* - SignupValidationService: Validates customer numbers and preflight checks * - SignupValidationService: Validates customer numbers and preflight checks
* - SignupAccountResolverService: Resolves Salesforce accounts * - SalesforceAccountService: Creates new Salesforce accounts and contacts
* - SignupWhmcsService: Creates WHMCS clients * - SignupWhmcsService: Creates WHMCS clients
* - SignupUserCreationService: Creates portal users with ID mappings * - SignupUserCreationService: Creates portal users with ID mappings
*/ */
@ -210,38 +210,16 @@ export class SignupWorkflowService {
} }
/** /**
* Resolve Salesforce account for signup * Create a new Salesforce account for signup
* Either finds existing account by customer number or creates new one * Always creates a new account - no lookup by customer number
*/ */
private async resolveSalesforceAccount(signupData: SignupRequest): Promise<{ private async resolveSalesforceAccount(signupData: SignupRequest): Promise<{
accountSnapshot: SignupAccountSnapshot; accountSnapshot: SignupAccountSnapshot;
customerNumberForWhmcs: string | null; customerNumberForWhmcs: string | null;
}> { }> {
const { email, firstName, lastName, phone, address, sfNumber } = signupData; const { email, firstName, lastName, phone, address, gender, dateOfBirth } = signupData;
const normalizedCustomerNumber = this.accountResolver.normalizeCustomerNumber(sfNumber);
if (normalizedCustomerNumber) { // Check if account already exists by email
// 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
const normalizedEmail = email.toLowerCase().trim(); const normalizedEmail = email.toLowerCase().trim();
const existingAccount = await this.salesforceAccountService.findByEmail(normalizedEmail); const existingAccount = await this.salesforceAccountService.findByEmail(normalizedEmail);
if (existingAccount) { if (existingAccount) {
@ -266,6 +244,15 @@ export class SignupWorkflowService {
throw new BadRequestException("Phone number is required for account creation"); 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({ const created = await this.salesforceAccountService.createAccount({
firstName, firstName,
lastName, lastName,
@ -280,6 +267,8 @@ export class SignupWorkflowService {
lastName, lastName,
email: normalizedEmail, email: normalizedEmail,
phone, phone,
gender,
dateOfBirth,
// Address not added to Salesforce during signup // Address not added to Salesforce during signup
}); });

View File

@ -28,7 +28,8 @@ export class SignupAccountResolverService {
async resolveOrCreate( async resolveOrCreate(
signupData: SignupRequest signupData: SignupRequest
): Promise<{ snapshot: SignupAccountSnapshot; customerNumber: string | null }> { ): 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); const normalizedCustomerNumber = this.normalizeCustomerNumber(sfNumber);
if (normalizedCustomerNumber) { if (normalizedCustomerNumber) {
@ -73,6 +74,14 @@ export class SignupAccountResolverService {
throw new BadRequestException("Phone number is required for account creation"); 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({ const created = await this.salesforceAccountService.createAccount({
firstName, firstName,
lastName, lastName,
@ -86,6 +95,8 @@ export class SignupAccountResolverService {
lastName, lastName,
email: normalizedEmail, email: normalizedEmail,
phone, phone,
gender,
dateOfBirth,
}); });
const snapshot: SignupAccountSnapshot = { const snapshot: SignupAccountSnapshot = {

View File

@ -42,6 +42,23 @@ export interface SalesforceAccountRecord {
[key: string]: unknown; [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) // Re-export Types from Schema (Schema-First Approach)
// ============================================================================ // ============================================================================

View File

@ -10,7 +10,11 @@ export * from "./portal/index.js";
export * from "./whmcs/index.js"; export * from "./whmcs/index.js";
// Provider-specific integration types (BFF-only convenience re-exports) // 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 { export type {
WhmcsAddClientParams, WhmcsAddClientParams,
WhmcsValidateLoginParams, WhmcsValidateLoginParams,