From 75dc6ec15d859277d17e97f2f0be1088c47d63f4 Mon Sep 17 00:00:00 2001 From: barsa Date: Thu, 25 Dec 2025 16:01:02 +0900 Subject: [PATCH] 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. --- .github/workflows/deploy.yml | 2 - .github/workflows/pr-checks.yml | 2 - .github/workflows/security.yml | 4 -- .lintstagedrc.json | 2 +- .../opportunity-resolution.service.ts | 43 ++++++++++------- .../services/salesforce-account.service.ts | 48 +++++-------------- .../services/internet-cancellation.service.ts | 7 ++- 7 files changed, 44 insertions(+), 64 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 535e0be9..c3941a56 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -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 diff --git a/.github/workflows/pr-checks.yml b/.github/workflows/pr-checks.yml index 01286ba2..f83adcc9 100644 --- a/.github/workflows/pr-checks.yml +++ b/.github/workflows/pr-checks.yml @@ -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 diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 3559a8ce..eaadb1bc 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -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: | diff --git a/.lintstagedrc.json b/.lintstagedrc.json index b92ee90c..23248968 100644 --- a/.lintstagedrc.json +++ b/.lintstagedrc.json @@ -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"] } diff --git a/apps/bff/src/integrations/salesforce/services/opportunity-resolution.service.ts b/apps/bff/src/integrations/salesforce/services/opportunity-resolution.service.ts index defc2ac3..efcad846 100644 --- a/apps/bff/src/integrations/salesforce/services/opportunity-resolution.service.ts +++ b/apps/bff/src/integrations/salesforce/services/opportunity-resolution.service.ts @@ -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 }; } /** 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 83bb7057..1ef17bfe 100644 --- a/apps/bff/src/integrations/salesforce/services/salesforce-account.service.ts +++ b/apps/bff/src/integrations/salesforce/services/salesforce-account.service.ts @@ -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 { - 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 // ============================================================================ diff --git a/apps/bff/src/modules/subscriptions/internet-management/services/internet-cancellation.service.ts b/apps/bff/src/modules/subscriptions/internet-management/services/internet-cancellation.service.ts index abed5d52..a1de84dc 100644 --- a/apps/bff/src/modules/subscriptions/internet-management/services/internet-cancellation.service.ts +++ b/apps/bff/src/modules/subscriptions/internet-management/services/internet-cancellation.service.ts @@ -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,