Merged PR 643: ライセンス引き戻し完了通知 [U-109] の実装

## 概要
[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は通過
This commit is contained in:
湯本 開 2023-12-20 07:54:47 +00:00
parent 60bb8f9e20
commit 1bc6618a6d
8 changed files with 204 additions and 11 deletions

View File

@ -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,

View File

@ -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]": {

View File

@ -5112,6 +5112,7 @@ describe('ライセンス発行キャンセル', () => {
);
const service = module.get<AccountsService>(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>(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>(AccountsService);
overrideSendgridService(service, {});
await expect(
service.cancelIssue(
makeContext('trackingId', 'requestId'),
@ -5258,6 +5261,7 @@ describe('ライセンス発行キャンセル', () => {
null,
);
const service = module.get<AccountsService>(AccountsService);
overrideSendgridService(service, {});
await expect(
service.cancelIssue(
makeContext('trackingId', 'requestId'),
@ -5304,6 +5308,7 @@ describe('ライセンス発行キャンセル', () => {
null,
);
const service = module.get<AccountsService>(AccountsService);
overrideSendgridService(service, {});
await expect(
service.cancelIssue(
makeContext('trackingId', 'requestId'),
@ -5351,6 +5356,7 @@ describe('ライセンス発行キャンセル', () => {
null,
);
const service = module.get<AccountsService>(AccountsService);
overrideSendgridService(service, {});
await expect(
service.cancelIssue(
makeContext('trackingId', 'requestId'),

View File

@ -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;
});

View File

@ -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<string>('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<void> {
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

View File

@ -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<void> {
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 };
});
}

View File

@ -0,0 +1,52 @@
<html>
<head>
<title>License Returned Notification [U-109]</title>
</head>
<body>
<div>
<h3>&lt;English&gt;</h3>
<p>Dear $DEALER_NAME$,</p>
<p>
Please be informed that the licenses issued with the following contents has been returned from your customer and placed back into your License inventory.<br />
- Company Name: $CUSTOMER_NAME$<br />
- Number of canceled licenses: $LICENSE_QUANTITY$<br />
- PO Number: $PO_NUMBER$
</p>
<p>
If you have received this e-mail in error, please delete this e-mail from your system.<br />
This is an automatically generated e-mail and this mailbox is not monitored. Please do not reply.
</p>
</div>
<div>
<h3>&lt;Deutsch&gt;</h3>
<p>Sehr geehrte(r) $DEALER_NAME$,</p>
<p>
Bitte beachten Sie, dass die ausgestellten Lizenzen mit den folgenden Inhalten von Ihrem Kunden zurückgegeben und wieder in Ihren Lizenzbestand aufgenommen wurden.<br />
- Name der Firma: $CUSTOMER_NAME$<br />
- Anzahl der gekündigten Lizenzen: $LICENSE_QUANTITY$<br />
- Bestellnummer: $PO_NUMBER$
</p>
<p>
Wenn Sie diese E-Mail fälschlicherweise erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System.<br />
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten.
</p>
</div>
<div>
<h3>&lt;Français&gt;</h3>
<p>Chère/Cher $DEALER_NAME$,</p>
<p>
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.<br />
- Nom de l'entreprise: $CUSTOMER_NAME$<br />
- Nombre de licences annulées: $LICENSE_QUANTITY$<br />
- Numéro de bon de commande $PO_NUMBER$
</p>
<p>
Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail de votre système.<br />
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.
</p>
</div>
</body>
</html>

View File

@ -0,0 +1,35 @@
<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.