From 1bc6618a6df94b9f1aba94bae1b2578c8696002f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=AF=E6=9C=AC=20=E9=96=8B?= Date: Wed, 20 Dec 2023 07:54:47 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=20643:=20=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=82=BB=E3=83=B3=E3=82=B9=E5=BC=95=E3=81=8D=E6=88=BB=E3=81=97?= =?UTF-8?q?=E5=AE=8C=E4=BA=86=E9=80=9A=E7=9F=A5=20[U-109]=C2=A0=E3=81=AE?= =?UTF-8?q?=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task3306: ライセンス引き戻し完了通知 [U-109] の実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3306) - ライセンス引き戻し完了のメール送信機能を追加しました。 - テストでメール送信しないようSendGridのメソッドを上書きする処理を追加しました。 ## レビューポイント - テンプレートの適用内容に不自然な点はないか - アカウントのFromとToとCCに関わる部分で認識違いはないか - `orderedAccountId` という引数には注文したアカウント=下位階層のアカウントが入るという理解であっているか等 ## UIの変更 - なし ## 動作確認状況 - npm run testは通過 --- dictation_client/.vscode/settings.json | 4 +- dictation_server/.vscode/settings.json | 2 +- .../accounts/accounts.service.spec.ts | 6 ++ .../src/features/accounts/accounts.service.ts | 43 ++++++++++-- .../src/gateways/sendgrid/sendgrid.service.ts | 66 +++++++++++++++++++ .../accounts/accounts.repository.service.ts | 7 +- .../src/templates/template_U_109.html | 52 +++++++++++++++ .../src/templates/template_U_109.txt | 35 ++++++++++ 8 files changed, 204 insertions(+), 11 deletions(-) create mode 100644 dictation_server/src/templates/template_U_109.html create mode 100644 dictation_server/src/templates/template_U_109.txt diff --git a/dictation_client/.vscode/settings.json b/dictation_client/.vscode/settings.json index 4ec4dc1..2f24e82 100644 --- a/dictation_client/.vscode/settings.json +++ b/dictation_client/.vscode/settings.json @@ -27,8 +27,8 @@ "debug.javascript.usePreview": false, "editor.copyWithSyntaxHighlighting": false, "editor.codeActionsOnSave": { - "source.fixAll.eslint": true, - "source.fixAll.stylelint": true + "source.fixAll.eslint": "explicit", + "source.fixAll.stylelint": "explicit" }, "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, diff --git a/dictation_server/.vscode/settings.json b/dictation_server/.vscode/settings.json index 1f6f864..8edcbd3 100644 --- a/dictation_server/.vscode/settings.json +++ b/dictation_server/.vscode/settings.json @@ -1,7 +1,7 @@ { "terminal.integrated.shell.linux": "/bin/bash", "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" }, "eslint.format.enable": false, "[javascript]": { diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index d96397b..cfaacab 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -5112,6 +5112,7 @@ describe('ライセンス発行キャンセル', () => { ); const service = module.get(AccountsService); + overrideSendgridService(service, {}); await service.cancelIssue( makeContext('trackingId', 'requestId'), tier1Accounts[0].users[0].external_id, @@ -5175,6 +5176,7 @@ describe('ライセンス発行キャンセル', () => { ); const service = module.get(AccountsService); + overrideSendgridService(service, {}); await service.cancelIssue( makeContext('trackingId', 'requestId'), tier2Accounts[0].users[0].external_id, @@ -5212,6 +5214,7 @@ describe('ライセンス発行キャンセル', () => { }); const poNumber = 'CANCEL_TEST'; const service = module.get(AccountsService); + overrideSendgridService(service, {}); await expect( service.cancelIssue( makeContext('trackingId', 'requestId'), @@ -5258,6 +5261,7 @@ describe('ライセンス発行キャンセル', () => { null, ); const service = module.get(AccountsService); + overrideSendgridService(service, {}); await expect( service.cancelIssue( makeContext('trackingId', 'requestId'), @@ -5304,6 +5308,7 @@ describe('ライセンス発行キャンセル', () => { null, ); const service = module.get(AccountsService); + overrideSendgridService(service, {}); await expect( service.cancelIssue( makeContext('trackingId', 'requestId'), @@ -5351,6 +5356,7 @@ describe('ライセンス発行キャンセル', () => { null, ); const service = module.get(AccountsService); + overrideSendgridService(service, {}); await expect( service.cancelIssue( makeContext('trackingId', 'requestId'), diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts index 76dfede..a123d98 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -1419,11 +1419,42 @@ export class AccountsService { try { // 発行キャンセル処理 - await this.accountRepository.cancelIssue( - context, - orderedAccountId, - poNumber, - ); + const { canceledIssueLicenseOrderId } = + await this.accountRepository.cancelIssue( + context, + orderedAccountId, + poNumber, + ); + try { + // 発行キャンセルされ、発行済状態から注文中状態に戻った注文を取得する + const order = await this.licensesRepository.getLicenseOrder( + context, + orderedAccountId, + poNumber, + canceledIssueLicenseOrderId, + ); + if (order == null) { + throw new Error('order not found.'); + } + const { quantity, from_account_id, to_account_id } = order; + const customer = await this.getAccountInformation( + context, + from_account_id, + ); + const dealer = await this.getAccountInformation(context, to_account_id); + await this.sendgridService.sendMailWithU109( + context, + dealer.adminEmails, + dealer.companyName, + quantity, + poNumber, + customer.adminEmails, + customer.companyName, + ); + } catch (e) { + this.logger.error(`[${context.getTrackingId()}] error=${e}`); + // メール送信の例外はログだけ出して握りつぶす + } } catch (e) { this.logger.error(`[${context.getTrackingId()}] error=${e}`); switch (e.constructor) { @@ -2326,7 +2357,7 @@ export class AccountsService { const adminEmails = usersInfo.map((x) => { const { emailAddress } = getUserNameAndMailAddress(x); if (emailAddress == null) { - throw new Error('dealer admin email-address is not found'); + throw new Error(`admin email-address is not found. id=${x.id}`); } return emailAddress; }); diff --git a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts index 289b14a..70dc088 100644 --- a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts +++ b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts @@ -34,6 +34,8 @@ export class SendGridService { private readonly templateU107Text: string; private readonly templateU108Html: string; private readonly templateU108Text: string; + private readonly templateU109Html: string; + private readonly templateU109Text: string; constructor(private readonly configService: ConfigService) { this.appDomain = this.configService.getOrThrow('APP_DOMAIN'); @@ -99,6 +101,15 @@ export class SendGridService { path.resolve(__dirname, `../../templates/template_U_108.txt`), 'utf-8', ); + + this.templateU109Html = readFileSync( + path.resolve(__dirname, `../../templates/template_U_109.html`), + 'utf-8', + ); + this.templateU109Text = readFileSync( + path.resolve(__dirname, `../../templates/template_U_109.txt`), + 'utf-8', + ); } } @@ -408,6 +419,61 @@ export class SendGridService { } } + /** + * U-109のテンプレートを使用したメールを送信する + * @param context context + * @param dealerEmails ライセンス発行をキャンセルした上位アカウントの管理者(primary/secondary)のメールアドレス + * @param dealerAccountName ライセンス発行をキャンセルした上位アカウントの会社名 + * @param lisenceCount ライセンス発行をキャンセルした対象の注文の内容(ライセンス数) + * @param poNumber ライセンス発行をキャンセルした対象の注文の内容(PO番号) + * @param customerMails ライセンス発行をキャンセルされたアカウントの管理者(primary/secondary)のメールアドレス + * @param customerAccountName ライセンス発行をキャンセルされたアカウントの会社名 + * @returns + */ + async sendMailWithU109( + context: Context, + dealerEmails: string[], + dealerAccountName: string, + lisenceCount: number, + poNumber: string, + customerMails: string[], + customerAccountName: string, + ): Promise { + this.logger.log( + `[IN] [${context.getTrackingId()}] ${this.sendMailWithU109.name}`, + ); + try { + const subject = 'License Returned Notification [U-109]'; + + // メールの本文を作成する + const html = this.templateU109Html + .replaceAll(CUSTOMER_NAME, customerAccountName) + .replaceAll(DEALER_NAME, dealerAccountName) + .replaceAll(PO_NUMBER, poNumber) + .replaceAll(LICENSE_QUANTITY, `${lisenceCount}`); + const text = this.templateU109Text + .replaceAll(CUSTOMER_NAME, customerAccountName) + .replaceAll(DEALER_NAME, dealerAccountName) + .replaceAll(PO_NUMBER, poNumber) + .replaceAll(LICENSE_QUANTITY, `${lisenceCount}`); + + // メールを送信する + this.sendMail( + context, + dealerEmails, + customerMails, + this.mailFrom, + subject, + text, + html, + ); + } finally { + this.logger.log( + `[OUT] [${context.getTrackingId()}] ${this.sendMailWithU109.name}`, + ); + } + } + /** * U-108のテンプレートを使用したメールを送信する * @param context diff --git a/dictation_server/src/repositories/accounts/accounts.repository.service.ts b/dictation_server/src/repositories/accounts/accounts.repository.service.ts index 872e22a..745adb6 100644 --- a/dictation_server/src/repositories/accounts/accounts.repository.service.ts +++ b/dictation_server/src/repositories/accounts/accounts.repository.service.ts @@ -774,13 +774,14 @@ export class AccountsRepositoryService { * 注文元アカウントIDとPOナンバーに紐づくライセンス発行をキャンセルする * @param orderedAccountId:キャンセルしたい発行の注文元アカウントID * @param poNumber:POナンバー + * @returns { canceledIssueLicenseOrderId } : キャンセルされたライセンス発行に紐づく注文ID */ async cancelIssue( context: Context, orderedAccountId: number, poNumber: string, - ): Promise { - await this.dataSource.transaction(async (entityManager) => { + ): Promise<{ canceledIssueLicenseOrderId: number }> { + return await this.dataSource.transaction(async (entityManager) => { const orderRepo = entityManager.getRepository(LicenseOrder); // キャンセル対象の発行を取得 @@ -862,6 +863,8 @@ export class AccountsRepositoryService { this.isCommentOut, context, ); + + return { canceledIssueLicenseOrderId: targetOrder.id }; }); } diff --git a/dictation_server/src/templates/template_U_109.html b/dictation_server/src/templates/template_U_109.html new file mode 100644 index 0000000..5b0c746 --- /dev/null +++ b/dictation_server/src/templates/template_U_109.html @@ -0,0 +1,52 @@ + + + + License Returned Notification [U-109] + + + +
+

<English>

+

Dear $DEALER_NAME$,

+

+ Please be informed that the licenses issued with the following contents has been returned from your customer and placed back into your License inventory.
+ - Company Name: $CUSTOMER_NAME$
+ - Number of canceled licenses: $LICENSE_QUANTITY$
+ - PO Number: $PO_NUMBER$ +

+

+ If you have received this e-mail in error, please delete this e-mail from your system.
+ This is an automatically generated e-mail and this mailbox is not monitored. Please do not reply. +

+
+
+

<Deutsch>

+

Sehr geehrte(r) $DEALER_NAME$,

+

+ Bitte beachten Sie, dass die ausgestellten Lizenzen mit den folgenden Inhalten von Ihrem Kunden zurückgegeben und wieder in Ihren Lizenzbestand aufgenommen wurden.
+ - Name der Firma: $CUSTOMER_NAME$
+ - Anzahl der gekündigten Lizenzen: $LICENSE_QUANTITY$
+ - Bestellnummer: $PO_NUMBER$ +

+

+ Wenn Sie diese E-Mail fälschlicherweise erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System.
+ Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten. +

+
+
+

<Français>

+

Chère/Cher $DEALER_NAME$,

+

+ Veuillez noter que les licences émises avec le contenu suivant ont été retournées par votre client et replacées dans votre inventaire de licences.
+ - Nom de l'entreprise: $CUSTOMER_NAME$
+ - Nombre de licences annulées: $LICENSE_QUANTITY$
+ - Numéro de bon de commande $PO_NUMBER$ +

+

+ Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail de votre système.
+ Il s'agit d'un e-mail généré automatiquement et cette boîte aux lettres n'est pas surveillée. Merci de ne pas répondre. +

+
+ + + \ No newline at end of file diff --git a/dictation_server/src/templates/template_U_109.txt b/dictation_server/src/templates/template_U_109.txt new file mode 100644 index 0000000..e895071 --- /dev/null +++ b/dictation_server/src/templates/template_U_109.txt @@ -0,0 +1,35 @@ + + +Dear $DEALER_NAME$, + +Please be informed that the licenses issued with the following contents has been returned from your customer and placed back into your License inventory. + - Company Name: $CUSTOMER_NAME$ + - Number of canceled licenses: $LICENSE_QUANTITY$ + - PO Number: $PO_NUMBER$ + +If you have received this e-mail in error, please delete this e-mail from your system. +This is an automatically generated e-mail and this mailbox is not monitored. Please do not reply. + + + +Sehr geehrte(r) $DEALER_NAME$, + +Bitte beachten Sie, dass die ausgestellten Lizenzen mit den folgenden Inhalten von Ihrem Kunden zurückgegeben und wieder in Ihren Lizenzbestand aufgenommen wurden. + - Name der Firma: $CUSTOMER_NAME$ + - Anzahl der gekündigten Lizenzen: $LICENSE_QUANTITY$ + - Bestellnummer: $PO_NUMBER$ + +Wenn Sie diese E-Mail fälschlicherweise erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System. +Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten. + + + +Chère/Cher $DEALER_NAME$, + +Veuillez noter que les licences émises avec le contenu suivant ont été retournées par votre client et replacées dans votre inventaire de licences. + - Nom de l'entreprise: $CUSTOMER_NAME$ + - Nombre de licences annulées: $LICENSE_QUANTITY$ + - Numéro de bon de commande $PO_NUMBER$ + +Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail de votre système. +Il s'agit d'un e-mail généré automatiquement et cette boîte aux lettres n'est pas surveillée. Merci de ne pas répondre. \ No newline at end of file