Refactor Linting Configuration and Update GitHub Workflows

- Updated linting configuration to use 'eslint --fix' for automatic code formatting on staged files.
- Removed explicit version specification for pnpm in GitHub workflows to allow for flexibility in version management.
- Enhanced opportunity resolution service with locking mechanism to prevent race conditions during opportunity creation.
- Simplified Salesforce account service by querying the auto-generated account number instead of generating it manually.
- Improved cancellation date handling in internet cancellation service to avoid timezone issues.
This commit is contained in:
barsa 2025-12-25 16:01:02 +09:00
parent a688121a16
commit 75dc6ec15d
7 changed files with 44 additions and 64 deletions

View File

@ -23,8 +23,6 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 10.25.0
- name: Setup Node.js
uses: actions/setup-node@v4

View File

@ -22,8 +22,6 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "10.25.0"
- name: Get pnpm store directory
id: pnpm-cache

View File

@ -30,8 +30,6 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "10.25.0"
- name: Get pnpm store directory
id: pnpm-cache
@ -139,8 +137,6 @@ jobs:
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: "10.25.0"
- name: Check for outdated dependencies
run: |

View File

@ -1,4 +1,4 @@
{
"*.{ts,tsx,js,jsx}": ["eslint --no-warn-ignored", "prettier -w"],
"*.{ts,tsx,js,jsx}": ["eslint --fix --no-warn-ignored", "prettier -w"],
"*.{json,md,yml,yaml,css,scss}": ["prettier -w"]
}

View File

@ -43,26 +43,33 @@ export class OpportunityResolutionService {
wasCreated: boolean;
}> {
const safeAccountId = assertSalesforceId(accountId, "accountId");
const lockKey = `opportunity:eligibility:${safeAccountId}:Internet`;
const existing = await this.opportunities.findOpenOpportunityForAccount(
safeAccountId,
OPPORTUNITY_PRODUCT_TYPE.INTERNET,
{ stages: OPPORTUNITY_MATCH_STAGES_INTERNET_ELIGIBILITY }
return this.lockService.withLock(
lockKey,
async () => {
const existing = await this.opportunities.findOpenOpportunityForAccount(
safeAccountId,
OPPORTUNITY_PRODUCT_TYPE.INTERNET,
{ stages: OPPORTUNITY_MATCH_STAGES_INTERNET_ELIGIBILITY }
);
if (existing) {
return { opportunityId: existing, wasCreated: false };
}
const created = await this.opportunities.createOpportunity({
accountId: safeAccountId,
productType: OPPORTUNITY_PRODUCT_TYPE.INTERNET,
stage: OPPORTUNITY_STAGE.INTRODUCTION,
source: OPPORTUNITY_SOURCE.INTERNET_ELIGIBILITY,
applicationStage: APPLICATION_STAGE.INTRO_1,
});
return { opportunityId: created, wasCreated: true };
},
{ ttlMs: 10_000 }
);
if (existing) {
return { opportunityId: existing, wasCreated: false };
}
const created = await this.opportunities.createOpportunity({
accountId: safeAccountId,
productType: OPPORTUNITY_PRODUCT_TYPE.INTERNET,
stage: OPPORTUNITY_STAGE.INTRODUCTION,
source: OPPORTUNITY_SOURCE.INTERNET_ELIGIBILITY,
applicationStage: APPLICATION_STAGE.INTRO_1,
});
return { opportunityId: created, wasCreated: true };
}
/**

View File

@ -175,12 +175,8 @@ export class SalesforceAccountService {
): Promise<{ accountId: string; accountNumber: string }> {
this.logger.log("Creating new Salesforce Account", { email: data.email });
// Generate unique account number (SF_Account_No__c)
const accountNumber = await this.generateAccountNumber();
const accountPayload = {
Name: `${data.firstName} ${data.lastName}`,
SF_Account_No__c: accountNumber,
BillingStreet:
data.address.address1 + (data.address.address2 ? `\n${data.address.address2}` : ""),
BillingCity: data.address.city,
@ -207,6 +203,18 @@ export class SalesforceAccountService {
const accountId = result.id as string;
// Query back the auto-generated account number
const accountRecord = (await this.connection.query(
`SELECT SF_Account_No__c FROM Account WHERE Id = '${this.safeSoql(accountId)}'`,
{ label: "checkout:getCreatedAccountNumber" }
)) as SalesforceResponse<{ SF_Account_No__c: string }>;
const accountNumber = accountRecord.records[0]?.SF_Account_No__c || "";
if (!accountNumber) {
this.logger.warn("Account number not found for newly created account", { accountId });
}
this.logger.log("Salesforce Account created", {
accountId,
accountNumber,
@ -273,38 +281,6 @@ export class SalesforceAccountService {
}
}
/**
* Generate a unique customer number for new accounts
* Format: PNNNNNNN (P = Portal, 7 digits)
*/
private async generateAccountNumber(): Promise<string> {
try {
// Query for max existing portal account number
const result = (await this.connection.query(
`SELECT SF_Account_No__c FROM Account WHERE SF_Account_No__c LIKE 'P%' ORDER BY SF_Account_No__c DESC LIMIT 1`,
{ label: "checkout:getMaxAccountNumber" }
)) as SalesforceResponse<{ SF_Account_No__c: string }>;
let nextNumber = 1000001; // Start from P1000001
if (result.totalSize > 0 && result.records[0]?.SF_Account_No__c) {
const lastNumber = result.records[0].SF_Account_No__c;
const numPart = parseInt(lastNumber.substring(1), 10);
if (!isNaN(numPart)) {
nextNumber = numPart + 1;
}
}
return `P${nextNumber}`;
} catch (error) {
this.logger.error("Failed to generate account number, using timestamp fallback", {
error: getErrorMessage(error),
});
// Fallback: use timestamp to ensure uniqueness
return `P${Date.now().toString().slice(-7)}`;
}
}
// ============================================================================
// Portal Field Update Methods
// ============================================================================

View File

@ -194,7 +194,12 @@ export class InternetCancellationService {
// Cancellation date is end of selected month
const lastDayOfMonth = new Date(year, month, 0);
const cancellationDate = lastDayOfMonth.toISOString().split("T")[0];
// Use local date components to avoid timezone shifts when converting to string
const cancellationDate = [
lastDayOfMonth.getFullYear(),
String(lastDayOfMonth.getMonth() + 1).padStart(2, "0"),
String(lastDayOfMonth.getDate()).padStart(2, "0"),
].join("-");
this.logger.log("Processing Internet cancellation request", {
userId,