diff --git a/dictation_client/.env.staging b/dictation_client/.env.staging
index b4beb04..6474e1b 100644
--- a/dictation_client/.env.staging
+++ b/dictation_client/.env.staging
@@ -1,5 +1,5 @@
VITE_STAGE=staging
-VITE_B2C_CLIENTID=5d8f0db9-4506-41d6-a5bb-5ec39f6eba8d
-VITE_B2C_AUTHORITY=https://adb2codmsstg.b2clogin.com/adb2codmsstg.onmicrosoft.com/b2c_1_signin_stg
-VITE_B2C_KNOWNAUTHORITIES=adb2codmsstg.b2clogin.com
+VITE_B2C_CLIENTID=6ddb8ca0-c39e-4eba-a3c1-d18ea289a315
+VITE_B2C_AUTHORITY=https://adb2codmsstaging.b2clogin.com/adb2codmsstaging.onmicrosoft.com/b2c_1_signin_staging
+VITE_B2C_KNOWNAUTHORITIES=adb2codmsstaging.b2clogin.com
VITE_DESK_TOP_APP_SCHEME=odms-desktopapp
\ No newline at end of file
diff --git a/dictation_function/package.json b/dictation_function/package.json
index 0be5119..1f1524a 100644
--- a/dictation_function/package.json
+++ b/dictation_function/package.json
@@ -9,7 +9,8 @@
"clean": "rimraf dist",
"prestart": "npm run clean && npm run build",
"start": "func start",
- "test": "sql-migrate up -config=/app/dictation_server/db/dbconfig.yml -env=test && jest -w 1",
+ "test": "tsc --noEmit && sql-migrate up -config=/app/dictation_server/db/dbconfig.yml -env=test && jest -w 1",
+ "typecheck": "tsc --noEmit",
"codegen": "sh codegen.sh"
},
"dependencies": {
diff --git a/dictation_function/src/constants/index.ts b/dictation_function/src/constants/index.ts
index ca6be1e..6c8c0e8 100644
--- a/dictation_function/src/constants/index.ts
+++ b/dictation_function/src/constants/index.ts
@@ -408,3 +408,13 @@ export const LICENSE_COUNT_ANALYSIS_FRONT_STRING = "LicenseAggregated";
* @const {string}
*/
export const LICENSE_COUNT_ANALYSIS_CONTAINER_NAME = "analysis-licenses";
+
+/**
+ * メールの置換文字列
+ * @const {string}
+ */
+export const CUSTOMER_NAME = "$CUSTOMER_NAME$";
+export const DEALER_NAME = "$DEALER_NAME$";
+export const TOP_URL = "$TOP_URL$";
+export const USER_NAME = "$USER_NAME$";
+export const USER_EMAIL = "$USER_EMAIL$";
diff --git a/dictation_function/src/functions/importUsers.ts b/dictation_function/src/functions/importUsers.ts
index 38716b9..0a34802 100644
--- a/dictation_function/src/functions/importUsers.ts
+++ b/dictation_function/src/functions/importUsers.ts
@@ -18,6 +18,16 @@ import { sign, getJwtKey } from "../common/jwt";
import { AccessToken, SystemAccessToken } from "../common/jwt/types";
import { isImportJson, isStageJson } from "../blobstorage/types/guards";
import https from "https";
+import globalAxios from "axios";
+
+// すべてのリクエストのヘッダーにX-Requested-Withを追加
+globalAxios.interceptors.request.use((config) => {
+ // headersがあれば追加、なければ新規作成
+ config.headers = config.headers || {};
+ // X-Requested-Withを追加
+ config.headers["X-Requested-With"] = "XMLHttpRequest";
+ return config;
+});
export async function importUsersProcessing(
context: InvocationContext,
diff --git a/dictation_function/src/functions/licenseAlert.ts b/dictation_function/src/functions/licenseAlert.ts
index d3204db..d1348fa 100644
--- a/dictation_function/src/functions/licenseAlert.ts
+++ b/dictation_function/src/functions/licenseAlert.ts
@@ -387,7 +387,9 @@ async function sendAlertMail(
// メールを送信
try {
await sendgrid.sendMail(
- targetAccount.primaryAdminEmail,
+ context,
+ [targetAccount.primaryAdminEmail],
+ [],
mailFrom,
subject,
text,
@@ -447,7 +449,9 @@ async function sendAlertMail(
// メールを送信
try {
await sendgrid.sendMail(
- targetAccount.secondaryAdminEmail,
+ context,
+ [targetAccount.secondaryAdminEmail],
+ [],
mailFrom,
subject,
text,
@@ -506,7 +510,9 @@ async function sendAlertMail(
// メールを送信
try {
await sendgrid.sendMail(
- targetAccount.primaryAdminEmail,
+ context,
+ [targetAccount.primaryAdminEmail],
+ [],
mailFrom,
subject,
text,
@@ -564,7 +570,9 @@ async function sendAlertMail(
// メールを送信
try {
await sendgrid.sendMail(
- targetAccount.secondaryAdminEmail,
+ context,
+ [targetAccount.secondaryAdminEmail],
+ [],
mailFrom,
subject,
text,
diff --git a/dictation_function/src/functions/licenseAutoAllocation.ts b/dictation_function/src/functions/licenseAutoAllocation.ts
index 9313a6a..8b1b053 100644
--- a/dictation_function/src/functions/licenseAutoAllocation.ts
+++ b/dictation_function/src/functions/licenseAutoAllocation.ts
@@ -1,14 +1,20 @@
import { app, InvocationContext, Timer } from "@azure/functions";
-import { Between, DataSource, In, MoreThan, Repository } from "typeorm";
+import { Between, DataSource, In, IsNull, MoreThan, Repository } from "typeorm";
import { User } from "../entity/user.entity";
import { Account } from "../entity/account.entity";
import { License, LicenseAllocationHistory } from "../entity/license.entity";
import * as dotenv from "dotenv";
import {
+ ADB2C_SIGN_IN_TYPE,
+ CUSTOMER_NAME,
+ DEALER_NAME,
LICENSE_ALLOCATED_STATUS,
LICENSE_TYPE,
SWITCH_FROM_TYPE,
TIERS,
+ TOP_URL,
+ USER_EMAIL,
+ USER_NAME,
USER_ROLES,
} from "../constants";
import {
@@ -17,10 +23,19 @@ import {
NewAllocatedLicenseExpirationDate,
} from "../common/types/types";
import { initializeDataSource } from "../database/initializeDataSource";
+import { readFileSync } from "fs";
+import path from "path";
+import { SendGridService } from "../sendgrid/sendgrid";
+import { AdB2cService } from "../adb2c/adb2c";
+import { RedisClient } from "redis";
+import { createRedisClient } from "../redis/redis";
export async function licenseAutoAllocationProcessing(
context: InvocationContext,
datasource: DataSource,
+ redisClient: RedisClient,
+ sendGrid: SendGridService,
+ adb2c: AdB2cService,
dateToTrigger?: Date
): Promise {
try {
@@ -33,6 +48,7 @@ export async function licenseAutoAllocationProcessing(
currentDateZeroTime = new DateWithZeroTime(dateToTrigger);
currentDateEndTime = new DateWithDayEndTime(dateToTrigger);
}
+
// 自動更新対象の候補となるアカウントを取得
const accountRepository = datasource.getRepository(Account);
const targetAccounts = await accountRepository.find({
@@ -56,6 +72,9 @@ export async function licenseAutoAllocationProcessing(
await allocateLicense(
context,
datasource,
+ redisClient,
+ sendGrid,
+ adb2c,
autoAllocationList,
currentDateZeroTime,
currentDateEndTime
@@ -79,7 +98,25 @@ export async function licenseAutoAllocation(
dotenv.config({ path: ".env" });
dotenv.config({ path: ".env.local", override: true });
const datasource = await initializeDataSource(context);
- await licenseAutoAllocationProcessing(context, datasource);
+ let redisClient: RedisClient;
+ try {
+ // redis接続
+ redisClient = createRedisClient();
+ } catch (e) {
+ context.log("redis client create failed.");
+ context.error(e);
+ throw e;
+ }
+ const sendGrid = new SendGridService();
+ const adb2c = new AdB2cService();
+
+ await licenseAutoAllocationProcessing(
+ context,
+ datasource,
+ redisClient,
+ sendGrid,
+ adb2c
+ );
} catch (e) {
context.log("licenseAutoAllocation failed.");
context.error(e);
@@ -199,24 +236,45 @@ export async function getAutoAllocatableLicense(
try {
context.log("[IN]getAutoAllocatableLicense");
// 割り当て可能なライセンスを取得
- const license = await licenseRepository.findOne({
- where: {
- account_id: accountId,
- status: In([
- LICENSE_ALLOCATED_STATUS.REUSABLE,
- LICENSE_ALLOCATED_STATUS.UNALLOCATED,
- ]),
- expiry_date: MoreThan(currentDateEndTime) || null,
- },
- order: {
- expiry_date: "ASC",
- },
+ const license = await licenseRepository.find({
+ where: [
+ {
+ account_id: accountId,
+ status: In([
+ LICENSE_ALLOCATED_STATUS.REUSABLE,
+ LICENSE_ALLOCATED_STATUS.UNALLOCATED,
+ ]),
+ expiry_date: MoreThan(currentDateEndTime),
+ },
+ {
+ account_id: accountId,
+ status: In([
+ LICENSE_ALLOCATED_STATUS.REUSABLE,
+ LICENSE_ALLOCATED_STATUS.UNALLOCATED,
+ ]),
+ expiry_date: IsNull(),
+ },
+ ],
});
- if (!license) {
+ if (license.length === 0) {
// 割り当て可能なライセンスが存在しない場合でもエラーとはしたくないので、undifinedを返却する
return undefined;
}
- return license;
+ // ライセンスをソートする
+ // 有効期限が近いものから割り当てるため、expiry_dateがnullのものは最後にする
+ const sortedLicense = license.sort((a, b) => {
+ if (a.expiry_date && b.expiry_date) {
+ return a.expiry_date.getTime() - b.expiry_date.getTime();
+ } else if (a.expiry_date && !b.expiry_date) {
+ return -1;
+ } else if (!a.expiry_date && b.expiry_date) {
+ return 1;
+ } else {
+ return 0;
+ }
+ });
+ // 有効期限が近いライセンスを返却する
+ return sortedLicense[0];
} catch (e) {
context.error(e);
context.log("getAutoAllocatableLicense failed.");
@@ -237,17 +295,23 @@ export async function getAutoAllocatableLicense(
export async function allocateLicense(
context: InvocationContext,
datasource: DataSource,
+ redisClient: RedisClient,
+ sendGrid: SendGridService,
+ adb2c: AdB2cService,
autoAllocationList: autoAllocationList,
currentDateZeroTime: DateWithZeroTime,
currentDateEndTime: DateWithDayEndTime
): Promise {
+ context.log("[IN]allocateLicense");
try {
- context.log("[IN]allocateLicense");
-
- // 自動更新対象ユーザーにライセンスを割り当てる
+ // 割り当て可能なライセンスが存在するかどうかのフラグ
let hasAllocatebleLicense = true;
+ // ユーザーに割り当てられているライセンスが自動更新対象であるかどうかのフラグ
+ let hasAutoRenewLicense = true;
for (const userId of autoAllocationList.userIds) {
await datasource.transaction(async (entityManager) => {
+ // フラグの初期化
+ hasAutoRenewLicense = true;
const licenseRepository = entityManager.getRepository(License);
const licenseAllocationHistoryRepo = entityManager.getRepository(
LicenseAllocationHistory
@@ -276,6 +340,7 @@ export async function allocateLicense(
});
if (!allocatedLicense) {
context.log(`skip auto allocation. userID:${userId}`);
+ hasAutoRenewLicense = false;
return;
}
@@ -349,17 +414,276 @@ export async function allocateLicense(
if (!hasAllocatebleLicense) {
break;
}
+
+ // ユーザーに割り当てられているライセンスが自動更新対象であるかどうかのフラグがfalseの場合、次のユーザーへ
+ if (!hasAutoRenewLicense) {
+ continue;
+ }
+
+ try {
+ //メール送信に必要な情報をDBから取得
+ const userRepository = datasource.getRepository(User);
+ const accountRepository = datasource.getRepository(Account);
+
+ // ライセンスを割り当てたユーザーとアカウントの情報を取得
+ const user = await userRepository.findOne({
+ where: { id: userId },
+ });
+ if (!user) {
+ throw new Error(`Target user not found. ${userId}`);
+ }
+ const account = await accountRepository.findOne({
+ where: { id: autoAllocationList.accountId },
+ relations: {
+ primaryAdminUser: true,
+ secondaryAdminUser: true,
+ },
+ });
+ if (!account) {
+ throw new Error(
+ `Target account not found. ${autoAllocationList.accountId}`
+ );
+ }
+ // アカウントのプライマリー管理者が存在しない場合はエラー
+ if (!account.primaryAdminUser) {
+ throw new Error(
+ `Primary admin user not found. accountID: ${account.id}`
+ );
+ }
+ // 親アカウントが存在する場合は取得
+ let parentAccount: Account | null = null;
+ if (account.parent_account_id) {
+ parentAccount = await accountRepository.findOne({
+ where: { id: account.parent_account_id },
+ });
+ if (!parentAccount) {
+ throw new Error(
+ `Parent account not found. accountID: ${account.parent_account_id}`
+ );
+ }
+ }
+
+ // アカウントの管理者とライセンスを割り当てたユーザーのメールアドレス取得に必要な外部IDを抽出
+ const externalIds: string[] = [];
+ externalIds.push(user.external_id);
+ externalIds.push(account.primaryAdminUser.external_id);
+ // セカンダリ管理者が存在する場合はセカンダリ管理者の外部IDも抽出
+ if (account.secondaryAdminUser) {
+ externalIds.push(account.secondaryAdminUser.external_id);
+ }
+ const adb2cUsers = await getMailAddressAndDisplayNameList(
+ context,
+ redisClient,
+ adb2c,
+ externalIds
+ );
+ // ライセンス割り当てされたユーザーの名前を取得
+ const userName = adb2cUsers.find(
+ (adb2cUser) => adb2cUser.externalId === user.external_id
+ )?.displayName;
+ if (!userName) {
+ throw new Error(
+ `Target ADb2Cuser name not found. externalId=${user.external_id}`
+ );
+ }
+ // ライセンス割り当てされたユーザーのメールアドレスを取得
+ const userMail = adb2cUsers.find(
+ (adb2cUser) => adb2cUser.externalId === user.external_id
+ )?.mailAddress;
+ if (!userMail) {
+ throw new Error(
+ `Target ADb2Cuser mail not found. externalId=${user.external_id}`
+ );
+ }
+ // アカウントのプライマリー管理者のメールアドレスを取得
+ const adminMails: string[] = [];
+ const primaryAdminMail = adb2cUsers.find(
+ (adb2cUser) =>
+ adb2cUser.externalId === account.primaryAdminUser?.external_id
+ )?.mailAddress;
+ if (!primaryAdminMail) {
+ throw new Error(
+ `Primary admin user mail not found. externalId=${account.primaryAdminUser.external_id}`
+ );
+ }
+ adminMails.push(primaryAdminMail);
+
+ // アカウントのセカンダリ管理者のメールアドレスを取得
+ const secondaryAdminMail = adb2cUsers.find(
+ (adb2cUser) =>
+ adb2cUser.externalId === account.secondaryAdminUser?.external_id
+ )?.mailAddress;
+ if (secondaryAdminMail) {
+ adminMails.push(secondaryAdminMail);
+ }
+
+ // メール送信
+ await sendMailWithU108(
+ context,
+ userName,
+ userMail,
+ adminMails,
+ account.company_name,
+ parentAccount ? parentAccount.company_name : null,
+ sendGrid
+ );
+ } catch (e) {
+ context.error(`error=${e}`);
+ // メール送信に関する例外はログだけ出して握りつぶす
+ }
}
} catch (e) {
// エラーが発生しても次のアカウントへの処理は継続させるため、例外をthrowせずにreturnだけする
- context.error(e);
context.log("allocateLicense failed.");
+ context.error(e);
return;
} finally {
context.log("[OUT]allocateLicense");
}
}
+// adb2cから指定した外部IDのユーザー情報を取得する
+export async function getMailAddressAndDisplayNameList(
+ context: InvocationContext,
+ redisClient: RedisClient,
+ adb2c: AdB2cService,
+ externalIds: string[]
+): Promise<
+ {
+ externalId: string;
+ displayName: string;
+ mailAddress: string;
+ }[]
+> {
+ context.log("[IN]getUsers");
+ try {
+ const users = [] as {
+ externalId: string;
+ displayName: string;
+ mailAddress: string;
+ }[];
+ // 外部IDからADB2Cユーザー情報を取得
+ const adb2cUsers = await adb2c.getUsers(context, redisClient, externalIds);
+ for (const externalId of externalIds) {
+ const adb2cUser = adb2cUsers.find((user) => user.id === externalId);
+ if (!adb2cUser) {
+ throw new Error(`ADB2C user not found. externalId=${externalId}`);
+ }
+ const mailAddress = adb2cUser.identities?.find(
+ (identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS
+ )?.issuerAssignedId;
+ if (!mailAddress) {
+ throw new Error(`ADB2C user mail not found. externalId=${externalId}`);
+ }
+ users.push({
+ externalId: externalId,
+ displayName: adb2cUser.displayName,
+ mailAddress: mailAddress,
+ });
+ }
+ return users;
+ } catch (e) {
+ context.error(e);
+ context.log("getUsers failed.");
+ throw e;
+ } finally {
+ context.log("[OUT]getUsers");
+ }
+}
+
+/**
+ * U-108のテンプレートを使用したメールを送信する
+ * @param context
+ * @param userName ライセンス割り当てされたユーザーの名前
+ * @param userMail ライセンス割り当てされたユーザーのメールアドレス
+ * @param customerAdminMails ライセンス割り当てされたユーザーの所属するアカウントの管理者(primary/secondary)のメールアドレス
+ * @param customerAccountName ライセンス割り当てされたユーザーの所属するアカウントの名前
+ * @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名)
+ * @returns mail with u108
+ */
+export async function sendMailWithU108(
+ context: InvocationContext,
+ userName: string,
+ userMail: string,
+ customerAdminMails: string[],
+ customerAccountName: string,
+ dealerAccountName: string | null,
+ sendGrid: SendGridService
+): Promise {
+ context.log("[IN] sendMailWithU108");
+ try {
+ const subject = "License Assigned Notification [U-108]";
+ const domain = process.env.APP_DOMAIN;
+ if (!domain) {
+ throw new Error("APP_DOMAIN is not defined.");
+ }
+ const mailFrom = process.env.MAIL_FROM;
+ if (!mailFrom) {
+ throw new Error("MAIL_FROM is not defined.");
+ }
+ const url = new URL(domain).href;
+
+ let html: string;
+ let text: string;
+
+ if (dealerAccountName === null) {
+ const templateU108NoParentHtml = readFileSync(
+ path.resolve(__dirname, `../templates/template_U_108_no_parent.html`),
+ "utf-8"
+ );
+ const templateU108NoParentText = readFileSync(
+ path.resolve(__dirname, `../templates/template_U_108_no_parent.txt`),
+ "utf-8"
+ );
+ html = templateU108NoParentHtml
+ .replaceAll(CUSTOMER_NAME, customerAccountName)
+ .replaceAll(USER_NAME, userName)
+ .replaceAll(USER_EMAIL, userMail)
+ .replaceAll(TOP_URL, url);
+ text = templateU108NoParentText
+ .replaceAll(CUSTOMER_NAME, customerAccountName)
+ .replaceAll(USER_NAME, userName)
+ .replaceAll(USER_EMAIL, userMail)
+ .replaceAll(TOP_URL, url);
+ } else {
+ const templateU108Html = readFileSync(
+ path.resolve(__dirname, `../templates/template_U_108.html`),
+ "utf-8"
+ );
+ const templateU108Text = readFileSync(
+ path.resolve(__dirname, `../templates/template_U_108.txt`),
+ "utf-8"
+ );
+ html = templateU108Html
+ .replaceAll(CUSTOMER_NAME, customerAccountName)
+ .replaceAll(DEALER_NAME, dealerAccountName)
+ .replaceAll(USER_NAME, userName)
+ .replaceAll(USER_EMAIL, userMail)
+ .replaceAll(TOP_URL, url);
+ text = templateU108Text
+ .replaceAll(CUSTOMER_NAME, customerAccountName)
+ .replaceAll(DEALER_NAME, dealerAccountName)
+ .replaceAll(USER_NAME, userName)
+ .replaceAll(USER_EMAIL, userMail)
+ .replaceAll(TOP_URL, url);
+ }
+ const ccAddress = customerAdminMails.includes(userMail) ? [] : [userMail];
+
+ // メールを送信する
+ await sendGrid.sendMail(
+ context,
+ customerAdminMails,
+ ccAddress,
+ mailFrom,
+ subject,
+ text,
+ html
+ );
+ } finally {
+ context.log(`[OUT] sendMailWithU108`);
+ }
+}
+
app.timer("licenseAutoAllocation", {
schedule: "0 0 16 * * *",
handler: licenseAutoAllocation,
diff --git a/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts b/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts
index f01542e..c4b4514 100644
--- a/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts
+++ b/dictation_function/src/functions/licenseAutoAllocationManualRetry.ts
@@ -9,6 +9,10 @@ import { licenseAutoAllocationProcessing } from "./licenseAutoAllocation";
import * as dotenv from "dotenv";
import { HTTP_METHODS, HTTP_STATUS_CODES } from "../constants";
import { initializeDataSource } from "../database/initializeDataSource";
+import { RedisClient } from "redis";
+import { createRedisClient } from "../redis/redis";
+import { AdB2cService } from "../adb2c/adb2c";
+import { SendGridService } from "../sendgrid/sendgrid";
export async function licenseAutoAllocationManualRetry(
req: HttpRequest,
@@ -38,14 +42,35 @@ export async function licenseAutoAllocationManualRetry(
dotenv.config({ path: ".env" });
dotenv.config({ path: ".env.local", override: true });
const datasource = await initializeDataSource(context);
- await licenseAutoAllocationProcessing(context, datasource, dateToTrigger);
+ let redisClient: RedisClient;
+ try {
+ // redis接続
+ redisClient = createRedisClient();
+ } catch (e) {
+ context.log("redis client create failed.");
+ context.error(e);
+ throw e;
+ }
+ const sendGrid = new SendGridService();
+ const adb2c = new AdB2cService();
+
+ await licenseAutoAllocationProcessing(
+ context,
+ datasource,
+ redisClient,
+ sendGrid,
+ adb2c,
+ dateToTrigger
+ );
context.log("Automatic license allocation has been triggered.");
return {
status: HTTP_STATUS_CODES.OK,
body: "Automatic license allocation has been triggered.",
};
} else {
- context.log(`Please use the POST method. Requested method = [${req.method}]`);
+ context.log(
+ `Please use the POST method. Requested method = [${req.method}]`
+ );
return {
status: HTTP_STATUS_CODES.BAD_REQUEST,
body: `Please use the POST method. method = [${req.method}]`,
diff --git a/dictation_function/src/sendgrid/sendgrid.ts b/dictation_function/src/sendgrid/sendgrid.ts
index cb90de1..ef8e049 100644
--- a/dictation_function/src/sendgrid/sendgrid.ts
+++ b/dictation_function/src/sendgrid/sendgrid.ts
@@ -1,3 +1,4 @@
+import { InvocationContext } from "@azure/functions";
import sendgrid from "@sendgrid/mail";
import { error } from "console";
@@ -9,9 +10,11 @@ export class SendGridService {
sendgrid.setApiKey(process.env.SENDGRID_API_KEY);
}
- /**
+ /**
* メールを送信する
+ * @param context
* @param to
+ * @param cc
* @param from
* @param subject
* @param text
@@ -19,28 +22,40 @@ export class SendGridService {
* @returns mail
*/
async sendMail(
- to: string,
+ context: InvocationContext,
+ to: string[],
+ cc: string[],
from: string,
subject: string,
text: string,
- html: string
+ html: string,
): Promise {
+ context.log(`[IN] ${this.sendMail.name}`);
try {
const res = await sendgrid
.send({
from: {
email: from,
},
- to: {
- email: to,
- },
+ to: to.map((v) => ({ email: v })),
+ cc: cc.map((v) => ({ email: v })),
subject: subject,
text: text,
html: html,
})
.then((v) => v[0]);
+ context.log(
+ ` status code: ${
+ res.statusCode
+ } body: ${JSON.stringify(res.body)}`,
+ );
} catch (e) {
+ context.warn(`send mail faild.`);
+ context.warn(`${this.sendMail.name} error=${e}`);
throw e;
+ } finally {
+ context.log(`[OUT] ${this.sendMail.name}`);
}
}
}
+
diff --git a/dictation_function/src/templates/template_U_108.html b/dictation_function/src/templates/template_U_108.html
new file mode 100644
index 0000000..0ba001c
--- /dev/null
+++ b/dictation_function/src/templates/template_U_108.html
@@ -0,0 +1,81 @@
+
+
+ License Assigned Notification [U-108]
+
+
+
+
<English>
+
Dear $CUSTOMER_NAME$,
+
+ Please be informed that a license has been assigned to the following
+ user.
+ - User Name: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+
+ Please log in to ODMS Cloud to verify the license expiration date.
+ URL: $TOP_URL$
+
+
+ If you need support regarding ODMS Cloud, please contact $DEALER_NAME$.
+
+
+ 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) $CUSTOMER_NAME$,
+
+ Bitte beachten Sie, dass dem folgenden Benutzer eine Lizenz zugewiesen
+ wurde.
+ - Nutzername: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+
+ Bitte melden Sie sich bei ODMS Cloud an, um das Ablaufdatum der Lizenz
+ zu überprüfen.
+ URL: $TOP_URL$
+
+
+ Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich
+ bitte an $DEALER_NAME$.
+
+
+ Wenn Sie diese E-Mail irrtümlich erhalten haben, löschen Sie diese
+ E-Mail bitte aus Ihrem System.
+ Dies ist eine automatisch generierte
+ E-Mail und diese Mailbox wird nicht überwacht. Bitte antworten Sie
+ nicht.
+
+
+
+
<Français>
+
Chère/Cher $CUSTOMER_NAME$,
+
+ Veuillez être informé qu'une licence a été attribuée à l'utilisateur
+ suivant.
+ - Nom d'utilisateur: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+
+ Veuillez vous connecter à ODMS Cloud pour vérifier la date d'expiration
+ de la licence.
+ URL: $TOP_URL$
+
+
+ Si vous avez besoin d'assistance concernant ODMS Cloud, veuillez
+ contacter $DEALER_NAME$.
+
+
+ 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.
+
+
+
+
diff --git a/dictation_function/src/templates/template_U_108.txt b/dictation_function/src/templates/template_U_108.txt
new file mode 100644
index 0000000..3615550
--- /dev/null
+++ b/dictation_function/src/templates/template_U_108.txt
@@ -0,0 +1,47 @@
+
+
+Dear $CUSTOMER_NAME$,
+
+Please be informed that a license has been assigned to the following user.
+ - User Name: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+Please log in to ODMS Cloud to verify the license expiration date.
+URL: $TOP_URL$
+
+If you need support regarding ODMS Cloud, please contact $DEALER_NAME$.
+
+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) $CUSTOMER_NAME$,
+
+Bitte beachten Sie, dass dem folgenden Benutzer eine Lizenz zugewiesen wurde.
+ - Nutzername: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+Bitte melden Sie sich bei ODMS Cloud an, um das Ablaufdatum der Lizenz zu überprüfen.
+URL: $TOP_URL$
+
+Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich bitte an $DEALER_NAME$.
+
+Wenn Sie diese E-Mail irrtümlich erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System.
+Dies ist eine automatisch generierte E-Mail und diese Mailbox wird nicht überwacht. Bitte antworten Sie nicht.
+
+
+
+Chère/Cher $CUSTOMER_NAME$,
+
+Veuillez être informé qu'une licence a été attribuée à l'utilisateur suivant.
+ - Nom d'utilisateur: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+Veuillez vous connecter à ODMS Cloud pour vérifier la date d'expiration de la licence.
+URL: $TOP_URL$
+
+Si vous avez besoin d'assistance concernant ODMS Cloud, veuillez contacter $DEALER_NAME$.
+
+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_function/src/templates/template_U_108_no_parent.html b/dictation_function/src/templates/template_U_108_no_parent.html
new file mode 100644
index 0000000..6f93225
--- /dev/null
+++ b/dictation_function/src/templates/template_U_108_no_parent.html
@@ -0,0 +1,70 @@
+
+
+ License Assigned Notification [U-108]
+
+
+
+
<English>
+
Dear $CUSTOMER_NAME$,
+
+ Please be informed that a license has been assigned to the following
+ user.
+ - User Name: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+
+ Please log in to ODMS Cloud to verify the license expiration date.
+ URL: $TOP_URL$
+
+
+ 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) $CUSTOMER_NAME$,
+
+ Bitte beachten Sie, dass dem folgenden Benutzer eine Lizenz zugewiesen
+ wurde.
+ - Nutzername: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+
+ Bitte melden Sie sich bei ODMS Cloud an, um das Ablaufdatum der Lizenz
+ zu überprüfen.
+ URL: $TOP_URL$
+
+
+ Wenn Sie diese E-Mail irrtümlich erhalten haben, löschen Sie diese
+ E-Mail bitte aus Ihrem System.
+ Dies ist eine automatisch generierte
+ E-Mail und diese Mailbox wird nicht überwacht. Bitte antworten Sie
+ nicht.
+
+
+
+
<Français>
+
Chère/Cher $CUSTOMER_NAME$,
+
+ Veuillez être informé qu'une licence a été attribuée à l'utilisateur
+ suivant.
+ - Nom d'utilisateur: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+
+ Veuillez vous connecter à ODMS Cloud pour vérifier la date d'expiration
+ de la licence.
+ URL: $TOP_URL$
+
+
+ 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.
+
+
+
+
diff --git a/dictation_function/src/templates/template_U_108_no_parent.txt b/dictation_function/src/templates/template_U_108_no_parent.txt
new file mode 100644
index 0000000..8c07856
--- /dev/null
+++ b/dictation_function/src/templates/template_U_108_no_parent.txt
@@ -0,0 +1,41 @@
+
+
+Dear $CUSTOMER_NAME$,
+
+Please be informed that a license has been assigned to the following user.
+ - User Name: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+Please log in to ODMS Cloud to verify the license expiration date.
+URL: $TOP_URL$
+
+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) $CUSTOMER_NAME$,
+
+Bitte beachten Sie, dass dem folgenden Benutzer eine Lizenz zugewiesen wurde.
+ - Nutzername: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+Bitte melden Sie sich bei ODMS Cloud an, um das Ablaufdatum der Lizenz zu überprüfen.
+URL: $TOP_URL$
+
+Wenn Sie diese E-Mail irrtümlich erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System.
+Dies ist eine automatisch generierte E-Mail und diese Mailbox wird nicht überwacht. Bitte antworten Sie nicht.
+
+
+
+Chère/Cher $CUSTOMER_NAME$,
+
+Veuillez être informé qu'une licence a été attribuée à l'utilisateur suivant.
+ - Nom d'utilisateur: $USER_NAME$
+ - Email: $USER_EMAIL$
+
+Veuillez vous connecter à ODMS Cloud pour vérifier la date d'expiration de la licence.
+URL: $TOP_URL$
+
+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_function/src/test/common/adb2c.mock.ts b/dictation_function/src/test/common/adb2c.mock.ts
new file mode 100644
index 0000000..aa0a59b
--- /dev/null
+++ b/dictation_function/src/test/common/adb2c.mock.ts
@@ -0,0 +1,88 @@
+import { InvocationContext } from "@azure/functions";
+import { AdB2cUser } from "../../adb2c/types/types";
+import { ADB2C_SIGN_IN_TYPE } from "../../constants";
+import { RedisClient } from "redis-mock";
+
+// テスト用adb2c
+export class AdB2cServiceMock {
+ /**
+ * Azure AD B2Cからユーザ情報を取得する
+ * @param externalIds 外部ユーザーID
+ * @returns ユーザ情報
+ */
+ async getUsers(
+ context: InvocationContext,
+ redisClient: RedisClient,
+ externalIds: string[]
+ ): Promise {
+ const AdB2cMockUsers: AdB2cUser[] = [
+ {
+ id: "external_id1",
+ displayName: "test1",
+ identities: [
+ {
+ signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
+ issuer: "issuer",
+ issuerAssignedId: "test1@mail.com",
+ },
+ ],
+ },
+ {
+ id: "external_id2",
+ displayName: "test2",
+ identities: [
+ {
+ signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
+ issuer: "issuer",
+ issuerAssignedId: "test2@mail.com",
+ },
+ ],
+ },
+ {
+ id: "external_id3",
+ displayName: "test3",
+ identities: [
+ {
+ signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
+ issuer: "issuer",
+ issuerAssignedId: "test3@mail.com",
+ },
+ ],
+ },
+ {
+ id: "external_id4",
+ displayName: "test4",
+ identities: [
+ {
+ signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
+ issuer: "issuer",
+ issuerAssignedId: "test4@mail.com",
+ },
+ ],
+ },
+ {
+ id: "external_id5",
+ displayName: "test5",
+ identities: [
+ {
+ signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
+ issuer: "issuer",
+ issuerAssignedId: "test5@mail.com",
+ },
+ ],
+ },
+ {
+ id: "external_id6",
+ displayName: "test6",
+ identities: [
+ {
+ signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
+ issuer: "issuer",
+ issuerAssignedId: "test6@mail.com",
+ },
+ ],
+ },
+ ];
+ return AdB2cMockUsers;
+ }
+}
diff --git a/dictation_function/src/test/common/sendGrid.mock.ts b/dictation_function/src/test/common/sendGrid.mock.ts
new file mode 100644
index 0000000..7abfc30
--- /dev/null
+++ b/dictation_function/src/test/common/sendGrid.mock.ts
@@ -0,0 +1,24 @@
+import { InvocationContext } from "@azure/functions";
+
+export class SendGridServiceMock {
+ /**
+ * メールを送信する
+ * @param to
+ * @param from
+ * @param subject
+ * @param text
+ * @param html
+ * @returns mail
+ */
+ async sendMail(
+ context: InvocationContext,
+ to: string[],
+ cc: string[],
+ from: string,
+ subject: string,
+ text: string,
+ html: string
+ ): Promise {
+ return;
+ }
+}
diff --git a/dictation_function/src/test/common/utility.ts b/dictation_function/src/test/common/utility.ts
index 2417f04..fe13688 100644
--- a/dictation_function/src/test/common/utility.ts
+++ b/dictation_function/src/test/common/utility.ts
@@ -233,6 +233,59 @@ export const createLicense = async (
});
identifiers.pop() as License;
};
+export const createAndAllocateLicense = async (
+ datasource: DataSource,
+ licenseId: number,
+ expiry_date: Date | null,
+ accountId: number,
+ type: string,
+ status: string,
+ allocated_user_id: number | null,
+ order_id: number | null,
+ deleted_at: Date | null,
+ delete_order_id: number | null,
+ created_at?: Date
+): Promise => {
+ const { identifiers } = await datasource.getRepository(License).insert({
+ id: licenseId,
+ expiry_date: expiry_date,
+ account_id: accountId,
+ type: type,
+ status: status,
+ allocated_user_id: allocated_user_id,
+ order_id: order_id,
+ deleted_at: deleted_at,
+ delete_order_id: delete_order_id,
+ created_by: "test_runner",
+ created_at: created_at ? created_at : new Date(),
+ updated_by: "updater",
+ updated_at: new Date(),
+ });
+ identifiers.pop() as License;
+ // 割り当てるユーザーがいない場合は履歴を作成しない
+ if (!allocated_user_id) {
+ return;
+ }
+ // switch_from_typeを作成
+ // typeが"CARD"の場合は"CARD","TRIAL"の場合は"TRIAL","NORMAL"の場合は"NONE"を設定
+
+ const switch_from_type =
+ type === "CARD" ? "CARD" : type === "TRIAL" ? "TRIAL" : "NONE";
+
+ // ライセンスの割り当て履歴を作成
+ await datasource.getRepository(LicenseAllocationHistory).insert({
+ license_id: licenseId,
+ account_id: accountId,
+ user_id: allocated_user_id ?? -1,
+ is_allocated: true,
+ switch_from_type: switch_from_type,
+ executed_at: new Date(),
+ created_by: "test_runner",
+ created_at: new Date(),
+ updated_by: "updater",
+ updated_at: new Date(),
+ });
+};
export const createLicenseAllocationHistory = async (
datasource: DataSource,
diff --git a/dictation_function/src/test/licenseAlert.spec.ts b/dictation_function/src/test/licenseAlert.spec.ts
index a8e65a0..b30ba01 100644
--- a/dictation_function/src/test/licenseAlert.spec.ts
+++ b/dictation_function/src/test/licenseAlert.spec.ts
@@ -1,6 +1,6 @@
import { DataSource } from "typeorm";
import { licenseAlertProcessing } from "../functions/licenseAlert";
-import { makeTestAccount, createLicense } from "./common/utility";
+import { makeTestAccount, createAndAllocateLicense } from "./common/utility";
import * as dotenv from "dotenv";
import {
DateWithDayEndTime,
@@ -8,13 +8,13 @@ import {
ExpirationThresholdDate,
NewTrialLicenseExpirationDate,
} from "../common/types/types";
-import { AdB2cUser } from "../adb2c/types/types";
-import { ADB2C_SIGN_IN_TYPE } from "../constants";
import { SendGridService } from "../sendgrid/sendgrid";
import { AdB2cService } from "../adb2c/adb2c";
import { InvocationContext } from "@azure/functions";
-import { RedisClient, createClient } from "redis-mock";
+import { createClient } from "redis-mock";
import { promisify } from "util";
+import { SendGridServiceMock } from "./common/sendGrid.mock";
+import { AdB2cServiceMock } from "./common/adb2c.mock";
describe("licenseAlert", () => {
dotenv.config({ path: ".env" });
@@ -56,7 +56,7 @@ describe("licenseAlert", () => {
{ tier: 5 },
{ external_id: "external_id1", accepted_dpa_version: null }
);
- await createLicense(
+ await createAndAllocateLicense(
source,
1,
expiringSoonDate,
@@ -103,7 +103,7 @@ describe("licenseAlert", () => {
accepted_dpa_version: null,
}
);
- await createLicense(
+ await createAndAllocateLicense(
source,
1,
expiringSoonDate,
@@ -147,7 +147,7 @@ describe("licenseAlert", () => {
{ tier: 5 },
{ external_id: "external_id3", accepted_dpa_version: null }
);
- await createLicense(
+ await createAndAllocateLicense(
source,
1,
expiringSoonDate,
@@ -159,7 +159,7 @@ describe("licenseAlert", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
2,
expiryDate,
@@ -206,7 +206,7 @@ describe("licenseAlert", () => {
accepted_dpa_version: null,
}
);
- await createLicense(
+ await createAndAllocateLicense(
source,
1,
expiringSoonDate,
@@ -254,7 +254,7 @@ describe("licenseAlert", () => {
accepted_privacy_notice_version: null,
}
);
- await createLicense(
+ await createAndAllocateLicense(
source,
1,
expiringSoonDate,
@@ -278,97 +278,6 @@ describe("licenseAlert", () => {
});
});
-// テスト用sendgrid
-export class SendGridServiceMock {
- /**
- * メールを送信する
- * @param to
- * @param from
- * @param subject
- * @param text
- * @param html
- * @returns mail
- */
- async sendMail(
- to: string,
- from: string,
- subject: string,
- text: string,
- html: string
- ): Promise {
- return;
- }
-}
-// テスト用adb2c
-export class AdB2cServiceMock {
- /**
- * Azure AD B2Cからユーザ情報を取得する
- * @param externalIds 外部ユーザーID
- * @returns ユーザ情報
- */
- async getUsers(
- context: InvocationContext,
- redisClient: RedisClient,
- externalIds: string[]
- ): Promise {
- const AdB2cMockUsers: AdB2cUser[] = [
- {
- id: "external_id1",
- displayName: "test1",
- identities: [
- {
- signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
- issuer: "issuer",
- issuerAssignedId: "test1@mail.com",
- },
- ],
- },
- {
- id: "external_id2",
- displayName: "test2",
- identities: [
- {
- signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
- issuer: "issuer",
- issuerAssignedId: "test2@mail.com",
- },
- ],
- },
- {
- id: "external_id3",
- displayName: "test3",
- identities: [
- {
- signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
- issuer: "issuer",
- issuerAssignedId: "test3@mail.com",
- },
- ],
- },
- {
- id: "external_id4",
- displayName: "test4",
- identities: [
- {
- signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
- issuer: "issuer",
- issuerAssignedId: "test4@mail.com",
- },
- ],
- },
- {
- id: "external_id5",
- displayName: "test5",
- identities: [
- {
- signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
- issuer: "issuer",
- issuerAssignedId: "test5@mail.com",
- },
- ],
- },
- ];
- return AdB2cMockUsers;
- }
-}
+
+
diff --git a/dictation_function/src/test/licenseAutoAllocation.spec.ts b/dictation_function/src/test/licenseAutoAllocation.spec.ts
index 5f1261b..5e2cced 100644
--- a/dictation_function/src/test/licenseAutoAllocation.spec.ts
+++ b/dictation_function/src/test/licenseAutoAllocation.spec.ts
@@ -8,18 +8,24 @@ import {
import { DateWithDayEndTime } from "../common/types/types";
import {
makeTestAccount,
- createLicense,
+ createAndAllocateLicense,
makeTestUser,
selectLicenseByAllocatedUser,
selectLicenseAllocationHistory,
} from "./common/utility";
import * as dotenv from "dotenv";
import { InvocationContext } from "@azure/functions";
+import { AdB2cService } from "../adb2c/adb2c";
+import { SendGridService } from "../sendgrid/sendgrid";
+import { SendGridServiceMock } from "./common/sendGrid.mock";
+import { AdB2cServiceMock } from "./common/adb2c.mock";
+import { createClient } from "redis-mock";
describe("licenseAutoAllocation", () => {
dotenv.config({ path: ".env" });
dotenv.config({ path: ".env.test", override: true });
let source: DataSource | null = null;
+ const redisClient = createClient();
beforeEach(async () => {
source = new DataSource({
type: "sqlite",
@@ -40,33 +46,39 @@ describe("licenseAutoAllocation", () => {
it("有効期限が本日のライセンスが自動更新されること", async () => {
if (!source) fail();
const context = new InvocationContext();
-
+ const sendgridMock = new SendGridServiceMock() as SendGridService;
+ const adb2cMock = new AdB2cServiceMock() as AdB2cService;
+ // 呼び出し回数でテスト成否を判定
+ const spySend = jest.spyOn(sendgridMock, "sendMail");
const currentDateEndTime = new DateWithDayEndTime();
// アカウント
const account1 = await makeTestAccount(
source,
{ tier: 5 },
- { role: `${USER_ROLES.NONE}` }
+ { role: `${USER_ROLES.NONE}`, external_id: "external_id1" }
);
const account2 = await makeTestAccount(
source,
{ tier: 5 },
- { role: `${USER_ROLES.NONE}` }
+ { role: `${USER_ROLES.NONE}`, external_id: "external_id6" }
);
// 更新対象のユーザー(3role分)
const user1 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.NONE}`,
+ external_id: "external_id2",
});
const user2 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.AUTHOR}`,
+ external_id: "external_id3",
});
const user3 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.TYPIST}`,
+ external_id: "external_id4",
});
// 更新対象ではないユーザー(まだ有効期限が残っている)
@@ -90,10 +102,11 @@ describe("licenseAutoAllocation", () => {
const user7 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.NONE}`,
+ external_id: "external_id5",
});
// 割り当て済みで有効期限が本日のライセンス
- await createLicense(
+ await createAndAllocateLicense(
source,
1,
currentDateEndTime,
@@ -105,7 +118,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
2,
currentDateEndTime,
@@ -117,7 +130,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
3,
currentDateEndTime,
@@ -129,7 +142,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
20,
currentDateEndTime,
@@ -141,7 +154,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
5,
currentDateEndTime,
@@ -153,7 +166,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
6,
currentDateEndTime,
@@ -165,7 +178,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
7,
currentDateEndTime,
@@ -183,7 +196,7 @@ describe("licenseAutoAllocation", () => {
nextDate.setDate(nextDate.getDate() + 1);
nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
nextDate.setMilliseconds(0);
- await createLicense(
+ await createAndAllocateLicense(
source,
4,
nextDate,
@@ -204,7 +217,7 @@ describe("licenseAutoAllocation", () => {
date.setDate(date.getDate() + i);
date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
date.setMilliseconds(0);
- await createLicense(
+ await createAndAllocateLicense(
source,
i + 100,
date,
@@ -222,7 +235,7 @@ describe("licenseAutoAllocation", () => {
date.setDate(date.getDate() + 30);
date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
date.setMilliseconds(0);
- await createLicense(
+ await createAndAllocateLicense(
source,
200,
date,
@@ -235,7 +248,13 @@ describe("licenseAutoAllocation", () => {
null
);
- await licenseAutoAllocationProcessing(context, source);
+ await licenseAutoAllocationProcessing(
+ context,
+ source,
+ redisClient,
+ sendgridMock,
+ adb2cMock
+ );
const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
@@ -277,11 +296,349 @@ describe("licenseAutoAllocation", () => {
expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe(
account1.account.id
);
+ expect(
+ licenseAllocationHistory.licenseAllocationHistory?.switch_from_type
+ ).toBe("CARD");
+ // メール送信が行われていることを確認
+ expect(spySend).toHaveBeenCalledTimes(4);
+ });
+
+ it("新規ライセンスがある場合でも、有効期限が本日のライセンスが自動更新されること", async () => {
+ if (!source) fail();
+ const context = new InvocationContext();
+ const sendgridMock = new SendGridServiceMock() as SendGridService;
+ const adb2cMock = new AdB2cServiceMock() as AdB2cService;
+ // 呼び出し回数でテスト成否を判定
+ const spySend = jest.spyOn(sendgridMock, "sendMail");
+
+ const currentDateEndTime = new DateWithDayEndTime();
+
+ // アカウント
+ const account1 = await makeTestAccount(
+ source,
+ { tier: 5 },
+ { role: `${USER_ROLES.NONE}`, external_id: "external_id1" }
+ );
+ const account2 = await makeTestAccount(
+ source,
+ { tier: 5 },
+ { role: `${USER_ROLES.NONE}`, external_id: "external_id6" }
+ );
+
+ // 更新対象のユーザー(3role分)
+ const testNoneUser1 = await makeTestUser(source, {
+ account_id: account1.account.id,
+ role: `${USER_ROLES.NONE}`,
+ external_id: "external_id2",
+ });
+ const testAuthorUser2 = await makeTestUser(source, {
+ account_id: account1.account.id,
+ role: `${USER_ROLES.AUTHOR}`,
+ external_id: "external_id3",
+ });
+ const testTypistUser3 = await makeTestUser(source, {
+ account_id: account1.account.id,
+ role: `${USER_ROLES.TYPIST}`,
+ external_id: "external_id4",
+ });
+
+ // 更新対象ではないユーザー(まだ有効期限が残っている)
+ const testNoneUser4ExpirationRemain = await makeTestUser(source, {
+ account_id: account1.account.id,
+ role: `${USER_ROLES.NONE}`,
+ });
+
+ // 更新対象ではないユーザー(auto_renewがfalse)
+ const testNoneUser5AutoRenewFalse = await makeTestUser(source, {
+ account_id: account1.account.id,
+ role: `${USER_ROLES.NONE}`,
+ auto_renew: false,
+ });
+ // 更新対象のユーザー(Author二人目)
+ const testAuthorUser6 = await makeTestUser(source, {
+ account_id: account1.account.id,
+ role: `${USER_ROLES.AUTHOR}`,
+ external_id: "external_id5",
+ });
+ // 更新対象のユーザー(ただしライセンスが足りない)
+ const testNoneUser7lackofLicenses = await makeTestUser(source, {
+ account_id: account1.account.id,
+ role: `${USER_ROLES.NONE}`,
+ });
+
+ // 割り当て済みで有効期限が本日のライセンス
+ await createAndAllocateLicense(
+ source,
+ 1,
+ currentDateEndTime,
+ account1.account.id,
+ LICENSE_TYPE.CARD,
+ LICENSE_ALLOCATED_STATUS.ALLOCATED,
+ testNoneUser1.id,
+ null,
+ null,
+ null
+ );
+ await createAndAllocateLicense(
+ source,
+ 2,
+ currentDateEndTime,
+ account1.account.id,
+ LICENSE_TYPE.NORMAL,
+ LICENSE_ALLOCATED_STATUS.ALLOCATED,
+ testAuthorUser2.id,
+ null,
+ null,
+ null
+ );
+ await createAndAllocateLicense(
+ source,
+ 3,
+ currentDateEndTime,
+ account1.account.id,
+ LICENSE_TYPE.TRIAL,
+ LICENSE_ALLOCATED_STATUS.ALLOCATED,
+ testTypistUser3.id,
+ null,
+ null,
+ null
+ );
+ await createAndAllocateLicense(
+ source,
+ 20,
+ currentDateEndTime,
+ account2.account.id,
+ LICENSE_TYPE.CARD,
+ LICENSE_ALLOCATED_STATUS.ALLOCATED,
+ account2.admin.id,
+ null,
+ null,
+ null
+ );
+ await createAndAllocateLicense(
+ source,
+ 5,
+ currentDateEndTime,
+ account1.account.id,
+ LICENSE_TYPE.CARD,
+ LICENSE_ALLOCATED_STATUS.ALLOCATED,
+ testNoneUser5AutoRenewFalse.id,
+ null,
+ null,
+ null
+ );
+ await createAndAllocateLicense(
+ source,
+ 6,
+ currentDateEndTime,
+ account1.account.id,
+ LICENSE_TYPE.CARD,
+ LICENSE_ALLOCATED_STATUS.ALLOCATED,
+ testAuthorUser6.id,
+ null,
+ null,
+ null
+ );
+ await createAndAllocateLicense(
+ source,
+ 7,
+ currentDateEndTime,
+ account1.account.id,
+ LICENSE_TYPE.CARD,
+ LICENSE_ALLOCATED_STATUS.ALLOCATED,
+ testNoneUser7lackofLicenses.id,
+ null,
+ null,
+ null
+ );
+
+ // 割り当て済みの更新対象ではないライセンス
+ const nextDate = new Date();
+ nextDate.setDate(nextDate.getDate() + 1);
+ nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
+ nextDate.setMilliseconds(0);
+ await createAndAllocateLicense(
+ source,
+ 4,
+ nextDate,
+ account1.account.id,
+ LICENSE_TYPE.CARD,
+ LICENSE_ALLOCATED_STATUS.ALLOCATED,
+ testNoneUser4ExpirationRemain.id,
+ null,
+ null,
+ null
+ );
+
+ // 有効期限が先のライセンスを作成
+ // idが100のものは有効期限が当日なので自動割り当て対象外
+ // idが101のものから割り当てられる
+ for (let i = 0; i < 4; i++) {
+ const date = new Date();
+ date.setDate(date.getDate() + i);
+ date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
+ date.setMilliseconds(0);
+ await createAndAllocateLicense(
+ source,
+ i + 100,
+ date,
+ account1.account.id,
+ LICENSE_TYPE.TRIAL,
+ LICENSE_ALLOCATED_STATUS.UNALLOCATED,
+ null,
+ null,
+ null,
+ null
+ );
+ }
+ // account1用の有効期限が設定されていないライセンスを作成
+ await createAndAllocateLicense(
+ source,
+ 99,
+ null,
+ account1.account.id,
+ LICENSE_TYPE.NORMAL,
+ LICENSE_ALLOCATED_STATUS.REUSABLE,
+ null,
+ null,
+ null,
+ null
+ );
+ // account2用の有効期限が設定されていないライセンスを作成
+ await createAndAllocateLicense(
+ source,
+ 200,
+ null,
+ account2.account.id,
+ LICENSE_TYPE.CARD,
+ LICENSE_ALLOCATED_STATUS.REUSABLE,
+ null,
+ null,
+ null,
+ null
+ );
+
+ await licenseAutoAllocationProcessing(
+ context,
+ source,
+ redisClient,
+ sendgridMock,
+ adb2cMock
+ );
+ const testNoneUser1Allocated = await selectLicenseByAllocatedUser(
+ source,
+ testNoneUser1.id
+ );
+ const testAuthorUser2Allocated = await selectLicenseByAllocatedUser(
+ source,
+ testAuthorUser2.id
+ );
+ const testTypistUser3Allocated = await selectLicenseByAllocatedUser(
+ source,
+ testTypistUser3.id
+ );
+ const testNoneUser4ExpirationRemainAllocated =
+ await selectLicenseByAllocatedUser(
+ source,
+ testNoneUser4ExpirationRemain.id
+ );
+ const testNoneUser5AutoRenewFalseAllocated =
+ await selectLicenseByAllocatedUser(
+ source,
+ testNoneUser5AutoRenewFalse.id
+ );
+ const testAuthorUser6Allocated = await selectLicenseByAllocatedUser(
+ source,
+ testAuthorUser6.id
+ );
+ const testNoneUser7lackofLicensesAllocated =
+ await selectLicenseByAllocatedUser(
+ source,
+ testNoneUser7lackofLicenses.id
+ );
+ const admin2Allocated = await selectLicenseByAllocatedUser(
+ source,
+ account2.admin.id
+ );
+ const testNoneUser1LicenseAllocationHistory =
+ await selectLicenseAllocationHistory(source, testNoneUser1.id, 99);
+ const testAuthorUser2LicenseAllocationHistory =
+ await selectLicenseAllocationHistory(source, testAuthorUser2.id, 101);
+ const testTypistUser3LicenseAllocationHistory =
+ await selectLicenseAllocationHistory(source, testTypistUser3.id, 103);
+
+ // Author、Typist、Noneの優先順位で割り当てられていることを確認
+ // 複数Authorがいる場合、それぞれに割り当てられていることを確認
+ expect(testAuthorUser2Allocated.license?.id).toBe(101);
+ expect(testAuthorUser6Allocated.license?.id).toBe(102);
+ expect(testTypistUser3Allocated.license?.id).toBe(103);
+ expect(testNoneUser1Allocated.license?.id).toBe(99);
+ // 有効期限がまだあるので、ライセンスが更新されていないことを確認
+ expect(testNoneUser4ExpirationRemainAllocated.license?.id).toBe(4);
+ // auto_renewがfalseなので、ライセンスが更新されていないことを確認
+ expect(testNoneUser5AutoRenewFalseAllocated.license?.id).toBe(5);
+ // ライセンスが足りない場合、ライセンスが更新されていないことを確認
+ expect(testNoneUser7lackofLicensesAllocated.license?.id).toBe(7);
+ // 複数アカウント分の処理が正常に行われていることの確認
+ expect(admin2Allocated.license?.id).toBe(200);
+
+ // ライセンス割り当て履歴テーブルが更新されていることを確認
+ expect(
+ testNoneUser1LicenseAllocationHistory.licenseAllocationHistory?.user_id
+ ).toBe(testNoneUser1.id);
+ expect(
+ testNoneUser1LicenseAllocationHistory.licenseAllocationHistory
+ ?.is_allocated
+ ).toBe(true);
+ expect(
+ testNoneUser1LicenseAllocationHistory.licenseAllocationHistory?.account_id
+ ).toBe(account1.account.id);
+ expect(
+ testNoneUser1LicenseAllocationHistory.licenseAllocationHistory
+ ?.switch_from_type
+ ).toBe("CARD");
+ expect(
+ testAuthorUser2LicenseAllocationHistory.licenseAllocationHistory?.user_id
+ ).toBe(testAuthorUser2.id);
+ expect(
+ testAuthorUser2LicenseAllocationHistory.licenseAllocationHistory
+ ?.is_allocated
+ ).toBe(true);
+ expect(
+ testAuthorUser2LicenseAllocationHistory.licenseAllocationHistory
+ ?.account_id
+ ).toBe(account1.account.id);
+ expect(
+ testAuthorUser2LicenseAllocationHistory.licenseAllocationHistory
+ ?.switch_from_type
+ ).toBe("NONE");
+ expect(
+ testTypistUser3LicenseAllocationHistory.licenseAllocationHistory?.user_id
+ ).toBe(testTypistUser3.id);
+ expect(
+ testTypistUser3LicenseAllocationHistory.licenseAllocationHistory
+ ?.is_allocated
+ ).toBe(true);
+ expect(
+ testTypistUser3LicenseAllocationHistory.licenseAllocationHistory
+ ?.account_id
+ ).toBe(account1.account.id);
+ expect(
+ testTypistUser3LicenseAllocationHistory.licenseAllocationHistory
+ ?.switch_from_type
+ ).toBe("TRIAL");
+ // メール送信が行われていることを確認
+ expect(spySend).toHaveBeenCalledTimes(5);
});
it("有効期限が指定日のライセンスが自動更新されること(リトライ用)", async () => {
if (!source) fail();
const context = new InvocationContext();
+ const sendgridMock = new SendGridServiceMock() as SendGridService;
+ const adb2cMock = new AdB2cServiceMock() as AdB2cService;
+ // 呼び出し回数でテスト成否を判定
+ const spySend = jest.spyOn(sendgridMock, "sendMail");
+
// 2023/11/22の日付を作成
const date1122 = new Date(2023, 10, 22, 23, 59, 59);
const currentDateEndTime = new DateWithDayEndTime(date1122);
@@ -289,26 +646,29 @@ describe("licenseAutoAllocation", () => {
const account1 = await makeTestAccount(
source,
{ tier: 5 },
- { role: `${USER_ROLES.NONE}` }
+ { role: `${USER_ROLES.NONE}`, external_id: "external_id1" }
);
const account2 = await makeTestAccount(
source,
{ tier: 5 },
- { role: `${USER_ROLES.NONE}` }
+ { role: `${USER_ROLES.NONE}`, external_id: "external_id6" }
);
// 更新対象のユーザー(3role分)
const user1 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.NONE}`,
+ external_id: "external_id2",
});
const user2 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.AUTHOR}`,
+ external_id: "external_id3",
});
const user3 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.TYPIST}`,
+ external_id: "external_id4",
});
// 更新対象ではないユーザー(まだ有効期限が残っている)
@@ -325,7 +685,7 @@ describe("licenseAutoAllocation", () => {
});
// 割り当て済みで有効期限が11/22のライセンス
- await createLicense(
+ await createAndAllocateLicense(
source,
1,
currentDateEndTime,
@@ -337,7 +697,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
2,
currentDateEndTime,
@@ -349,7 +709,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
3,
currentDateEndTime,
@@ -361,7 +721,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
20,
currentDateEndTime,
@@ -373,7 +733,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
5,
currentDateEndTime,
@@ -390,7 +750,7 @@ describe("licenseAutoAllocation", () => {
nextDate.setDate(date1122.getDate() + 1);
nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
nextDate.setMilliseconds(0);
- await createLicense(
+ await createAndAllocateLicense(
source,
4,
nextDate,
@@ -409,7 +769,7 @@ describe("licenseAutoAllocation", () => {
// 2023/11/22の日付を作成
const date = new Date(2023, 10, 22, 23, 59, 59);
date.setDate(date.getDate() + i);
- await createLicense(
+ await createAndAllocateLicense(
source,
i + 100,
date,
@@ -427,7 +787,7 @@ describe("licenseAutoAllocation", () => {
dateMarch31.setMonth(12);
dateMarch31.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
dateMarch31.setMilliseconds(0);
- await createLicense(
+ await createAndAllocateLicense(
source,
200,
dateMarch31,
@@ -439,7 +799,14 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await licenseAutoAllocationProcessing(context, source, date1122);
+ await licenseAutoAllocationProcessing(
+ context,
+ source,
+ redisClient,
+ sendgridMock,
+ adb2cMock,
+ date1122
+ );
const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
@@ -475,36 +842,50 @@ describe("licenseAutoAllocation", () => {
expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe(
account1.account.id
);
+ // メール送信が行われていることを確認
+ expect(spySend).toHaveBeenCalledTimes(4);
});
- it("新たに割り当てられるライセンスが存在しないため、ライセンスが自動更新されない(エラーではない)", async () => {
+ it("新たに割り当てられるライセンスが存在しないアカウントは、ライセンスが自動更新されない(エラーではない)", async () => {
if (!source) fail();
const context = new InvocationContext();
+ const sendgridMock = new SendGridServiceMock() as SendGridService;
+ const adb2cMock = new AdB2cServiceMock() as AdB2cService;
+ // 呼び出し回数でテスト成否を判定
+ const spySend = jest.spyOn(sendgridMock, "sendMail");
const currentDateEndTime = new DateWithDayEndTime();
- // アカウント
+ // アカウントと管理者
const account1 = await makeTestAccount(
source,
{ tier: 5 },
- { role: `${USER_ROLES.NONE}` }
+ { role: `${USER_ROLES.NONE}`, external_id: "external_id1" }
+ );
+ const account2 = await makeTestAccount(
+ source,
+ { tier: 5 },
+ { role: `${USER_ROLES.NONE}`, external_id: "external_id5" }
);
- // 更新対象のユーザー(3role分)
+ // account1の更新対象のユーザー(3role分)
const user1 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.NONE}`,
+ external_id: "external_id2",
});
const user2 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.AUTHOR}`,
+ external_id: "external_id3",
});
const user3 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.TYPIST}`,
+ external_id: "external_id4",
});
// 割り当て済みで有効期限が本日のライセンス
- await createLicense(
+ await createAndAllocateLicense(
source,
1,
currentDateEndTime,
@@ -516,7 +897,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
2,
currentDateEndTime,
@@ -528,7 +909,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
3,
currentDateEndTime,
@@ -540,20 +921,67 @@ describe("licenseAutoAllocation", () => {
null,
null
);
+ await createAndAllocateLicense(
+ source,
+ 4,
+ currentDateEndTime,
+ account2.account.id,
+ LICENSE_TYPE.TRIAL,
+ LICENSE_ALLOCATED_STATUS.ALLOCATED,
+ account2.admin.id,
+ null,
+ null,
+ null
+ );
- await licenseAutoAllocationProcessing(context, source);
+ // account2の有効期限が先の未割当ライセンスを作成
+ const nextDate = new Date();
+ nextDate.setDate(nextDate.getDate() + 1);
+ nextDate.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
+ nextDate.setMilliseconds(0);
+ await createAndAllocateLicense(
+ source,
+ 100,
+ nextDate,
+ account2.account.id,
+ LICENSE_TYPE.NORMAL,
+ LICENSE_ALLOCATED_STATUS.UNALLOCATED,
+ null,
+ null,
+ null,
+ null
+ );
+
+ await licenseAutoAllocationProcessing(
+ context,
+ source,
+ redisClient,
+ sendgridMock,
+ adb2cMock
+ );
const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
+ const account2AdminAllocated = await selectLicenseByAllocatedUser(
+ source,
+ account2.admin.id
+ );
// ライセンスが更新されていないことを確認
expect(user1Allocated.license?.id).toBe(1);
expect(user2Allocated.license?.id).toBe(2);
expect(user3Allocated.license?.id).toBe(3);
+ expect(account2AdminAllocated.license?.id).toBe(100);
+ // メール送信が行われていないことを確認
+ expect(spySend).toHaveBeenCalledTimes(1);
});
it("tier4のアカウントのため、ライセンスが自動更新されない", async () => {
if (!source) fail();
const context = new InvocationContext();
+ const sendgridMock = new SendGridServiceMock() as SendGridService;
+ const adb2cMock = new AdB2cServiceMock() as AdB2cService;
+ // 呼び出し回数でテスト成否を判定
+ const spySend = jest.spyOn(sendgridMock, "sendMail");
const currentDateEndTime = new DateWithDayEndTime();
@@ -561,25 +989,28 @@ describe("licenseAutoAllocation", () => {
const account1 = await makeTestAccount(
source,
{ tier: 4 },
- { role: `${USER_ROLES.NONE}` }
+ { role: `${USER_ROLES.NONE}`, external_id: "external_id1" }
);
// 更新対象のユーザー(3role分)
const user1 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.NONE}`,
+ external_id: "external_id2",
});
const user2 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.AUTHOR}`,
+ external_id: "external_id3",
});
const user3 = await makeTestUser(source, {
account_id: account1.account.id,
role: `${USER_ROLES.TYPIST}`,
+ external_id: "external_id4",
});
// 割り当て済みで有効期限が本日のライセンス
- await createLicense(
+ await createAndAllocateLicense(
source,
1,
currentDateEndTime,
@@ -591,7 +1022,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
2,
currentDateEndTime,
@@ -603,7 +1034,7 @@ describe("licenseAutoAllocation", () => {
null,
null
);
- await createLicense(
+ await createAndAllocateLicense(
source,
3,
currentDateEndTime,
@@ -624,7 +1055,7 @@ describe("licenseAutoAllocation", () => {
date.setDate(date.getDate() + i);
date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
date.setMilliseconds(0);
- await createLicense(
+ await createAndAllocateLicense(
source,
i + 100,
date,
@@ -638,7 +1069,13 @@ describe("licenseAutoAllocation", () => {
);
}
- await licenseAutoAllocationProcessing(context, source);
+ await licenseAutoAllocationProcessing(
+ context,
+ source,
+ redisClient,
+ sendgridMock,
+ adb2cMock
+ );
const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
const user2Allocated = await selectLicenseByAllocatedUser(source, user2.id);
const user3Allocated = await selectLicenseByAllocatedUser(source, user3.id);
@@ -646,5 +1083,99 @@ describe("licenseAutoAllocation", () => {
expect(user1Allocated.license?.id).toBe(1);
expect(user2Allocated.license?.id).toBe(2);
expect(user3Allocated.license?.id).toBe(3);
+ // メール送信が行われていないことを確認
+ expect(spySend).toHaveBeenCalledTimes(0);
+ });
+
+ it("メール送信に失敗しても、有効期限が本日のライセンスが自動更新されること", async () => {
+ if (!source) fail();
+ const context = new InvocationContext();
+ const sendgridMock = new SendGridServiceMock() as SendGridService;
+ const adb2cMock = new AdB2cServiceMock() as AdB2cService;
+ // 呼び出し回数でテスト成否を判定
+ const spySend = jest.spyOn(sendgridMock, "sendMail");
+ const currentDateEndTime = new DateWithDayEndTime();
+
+ // アカウント
+ const account1 = await makeTestAccount(
+ source,
+ { tier: 5 },
+ { role: `${USER_ROLES.NONE}`, external_id: "external_id1" }
+ );
+
+ // 更新対象のユーザー(3role分)
+ const user1 = await makeTestUser(source, {
+ account_id: account1.account.id,
+ role: `${USER_ROLES.NONE}`,
+ external_id: "external_id7", // メール送信失敗用
+ });
+ // 割り当て済みで有効期限が本日のライセンス
+ await createAndAllocateLicense(
+ source,
+ 1,
+ currentDateEndTime,
+ account1.account.id,
+ LICENSE_TYPE.CARD,
+ LICENSE_ALLOCATED_STATUS.ALLOCATED,
+ user1.id,
+ null,
+ null,
+ null
+ );
+
+ // 有効期限が先の未割当ライセンスを作成
+ // idが100のものは有効期限が当日なので自動割り当て対象外
+ // idが101のものから割り当てられる
+ for (let i = 0; i < 5; i++) {
+ const date = new Date();
+ date.setDate(date.getDate() + i);
+ date.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
+ date.setMilliseconds(0);
+ await createAndAllocateLicense(
+ source,
+ i + 100,
+ date,
+ account1.account.id,
+ LICENSE_TYPE.CARD,
+ LICENSE_ALLOCATED_STATUS.UNALLOCATED,
+ null,
+ null,
+ null,
+ null
+ );
+ }
+
+ await licenseAutoAllocationProcessing(
+ context,
+ source,
+ redisClient,
+ sendgridMock,
+ adb2cMock
+ );
+ const user1Allocated = await selectLicenseByAllocatedUser(source, user1.id);
+
+ const licenseAllocationHistory = await selectLicenseAllocationHistory(
+ source,
+ user1.id,
+ 101
+ );
+ // 割り当てられていることを確認
+ expect(user1Allocated.license?.id).toBe(101);
+
+ // ライセンス割り当て履歴テーブルが更新されていることを確認
+ expect(licenseAllocationHistory.licenseAllocationHistory?.user_id).toBe(
+ user1.id
+ );
+ expect(
+ licenseAllocationHistory.licenseAllocationHistory?.is_allocated
+ ).toBe(true);
+ expect(licenseAllocationHistory.licenseAllocationHistory?.account_id).toBe(
+ account1.account.id
+ );
+ expect(
+ licenseAllocationHistory.licenseAllocationHistory?.switch_from_type
+ ).toBe("CARD");
+ // メール送信が行われていないことを確認
+ expect(spySend).toHaveBeenCalledTimes(0);
});
});
diff --git a/dictation_function/tsconfig.json b/dictation_function/tsconfig.json
index dd9c7a4..a541177 100644
--- a/dictation_function/tsconfig.json
+++ b/dictation_function/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"module": "commonjs",
- "target": "es6",
+ "target": "ES2021",
"outDir": "dist",
"rootDir": ".",
"sourceMap": true,
diff --git a/dictation_server/.env.local.example b/dictation_server/.env.local.example
index 78c2af2..31c4508 100644
--- a/dictation_server/.env.local.example
+++ b/dictation_server/.env.local.example
@@ -36,4 +36,5 @@ EMAIL_CONFIRM_LIFETIME=86400
REDIS_HOST=redis-cache
REDIS_PORT=6379
REDIS_PASSWORD=omdsredispass
-ADB2C_CACHE_TTL=86400
\ No newline at end of file
+ADB2C_CACHE_TTL=86400
+DEALER_ACCOUNT_ID_HIDDEN_LIST=1,2,3,4
\ No newline at end of file
diff --git a/dictation_server/.env.test b/dictation_server/.env.test
index 07d483e..c6730fb 100644
--- a/dictation_server/.env.test
+++ b/dictation_server/.env.test
@@ -37,4 +37,5 @@ REDIS_HOST=redis-cache
REDIS_PORT=6379
REDIS_PASSWORD=omdsredispass
ADB2C_CACHE_TTL=86400
-TEMPLATE_ROOT=dist
\ No newline at end of file
+TEMPLATE_ROOT=dist
+DEALER_ACCOUNT_ID_HIDDEN_LIST=50,99
\ No newline at end of file
diff --git a/dictation_server/src/common/validators/env.validator.ts b/dictation_server/src/common/validators/env.validator.ts
index bd931d3..a676908 100644
--- a/dictation_server/src/common/validators/env.validator.ts
+++ b/dictation_server/src/common/validators/env.validator.ts
@@ -36,6 +36,10 @@ export class EnvValidator {
@IsString()
DB_PASSWORD: string;
+ @IsOptional()
+ @IsString()
+ DEALER_ACCOUNT_ID_HIDDEN_LIST: string;
+
// .env.local
@IsOptional()
@IsString()
diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts
index 190d524..60ca795 100644
--- a/dictation_server/src/features/accounts/accounts.service.spec.ts
+++ b/dictation_server/src/features/accounts/accounts.service.spec.ts
@@ -2818,6 +2818,35 @@ describe('getDealers', () => {
],
});
});
+
+ it('非表示指定されたDealer以外のDealerを取得できる', async () => {
+ if (!source) fail();
+ const module = await makeTestingModule(source);
+ if (!module) fail();
+ // 100件のDealerを作成し、country,id,company_nameを取得する
+ const dealers: { country: string; id: number; name: string }[] = [];
+ for (let i = 0; i < 100; i++) {
+ const { id, company_name, country } = (
+ await makeTestAccount(source, {
+ parent_account_id: i,
+ tier: TIERS.TIER4,
+ country: 'JP',
+ company_name: `DEALER_${i}`,
+ })
+ ).account;
+ dealers.push({ id, name: company_name, country });
+ }
+ const service = module.get(AccountsService);
+ const context = makeContext(`uuidv4`, 'requestId');
+ const result = await service.getDealers(context);
+ // idが50と99のDealerを非表示にする
+
+ expect(result.dealers.length).toBe(98);
+ expect(result).toEqual({
+ dealers: dealers.filter((dealer) => dealer.id !== 50 && dealer.id !== 99),
+ });
+ });
+
it('0件でもDealerを取得できる', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
@@ -7454,6 +7483,7 @@ describe('deleteAccountAndData', () => {
expect(expectUserIds).toStrictEqual(userArchiveIds);
});
});
+
describe('getAccountInfoMinimalAccess', () => {
let source: DataSource | null = null;
beforeAll(async () => {
@@ -9890,7 +9920,9 @@ describe('updatePartnerInfo', () => {
subject: string,
text: string,
html: string,
- ) => {},
+ ) => {
+ // empty
+ },
});
overrideAdB2cService(service, {
getUser: async (context, externalId) => {
@@ -9977,7 +10009,9 @@ describe('updatePartnerInfo', () => {
subject: string,
text: string,
html: string,
- ) => {},
+ ) => {
+ // empty
+ },
});
overrideAdB2cService(service, {
getUser: async (context, externalId) => {
diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts
index a69eb2a..75b53e9 100644
--- a/dictation_server/src/features/accounts/accounts.service.ts
+++ b/dictation_server/src/features/accounts/accounts.service.ts
@@ -82,9 +82,13 @@ import {
WorktypeIdNotFoundError,
} from '../../repositories/worktypes/errors/types';
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
+import { ConfigService } from '@nestjs/config';
@Injectable()
export class AccountsService {
+ //プロダクト バックログ項目 4077: [保守]本番環境動作確認用のDealerアカウントを表示しないようにする の対応
+ private readonly dealerAccountIdHiddenList: number[] = [];
+ private readonly logger = new Logger(AccountsService.name);
constructor(
private readonly accountRepository: AccountsRepositoryService,
private readonly licensesRepository: LicensesRepositoryService,
@@ -94,8 +98,27 @@ export class AccountsService {
private readonly adB2cService: AdB2cService,
private readonly sendgridService: SendGridService,
private readonly blobStorageService: BlobstorageService,
- ) {}
- private readonly logger = new Logger(AccountsService.name);
+ private readonly configService: ConfigService,
+ ) {
+ const dealerAccountIdList = this.configService.get(
+ 'DEALER_ACCOUNT_ID_HIDDEN_LIST',
+ );
+ // ディーラーアカウントIDリストを数値配列に変換する
+ // 変換できない場合はエラーをスローする
+ // 存在しない場合や空文字列の場合は空の配列を返す
+ if (dealerAccountIdList) {
+ this.dealerAccountIdHiddenList = dealerAccountIdList
+ .split(',')
+ .map((x) => {
+ const id = parseInt(x, 10);
+ if (isNaN(id)) {
+ throw new Error('DEALER_ACCOUNT_ID_HIDDEN_LIST is invalid');
+ }
+ return id;
+ });
+ }
+ }
+
/**
* 第五階層用のライセンス情報を取得する
* @param accountId
@@ -1192,9 +1215,26 @@ export class AccountsService {
const dealerAccounts = await this.accountRepository.findDealerAccounts(
context,
);
+ // プロダクト バックログ項目 4077: [保守]本番環境動作確認用のDealerアカウントを表示しないようにする の対応
+ // this.dealerAccountIdHiddenListに含まれるアカウント(動作確認用のアカウント)を除外する。
+ // 除外したアカウントをlogに出力する
+ const filteredDealerAccounts = dealerAccounts.filter((dealerAccount) => {
+ const isHidden = this.dealerAccountIdHiddenList.includes(
+ dealerAccount.id,
+ );
+ if (isHidden) {
+ this.logger.log(
+ `[${context.getTrackingId()}] hidden dealer account: ${
+ dealerAccount.id
+ }`,
+ );
+ }
+ return !isHidden;
+ });
+ // レスポンス用の型に変換
const dealers: GetDealersResponse = {
- dealers: dealerAccounts.map((dealerAccount): Dealer => {
+ dealers: filteredDealerAccounts.map((dealerAccount): Dealer => {
return {
id: dealerAccount.id,
name: dealerAccount.company_name,
diff --git a/dictation_server/src/features/users/users.service.ts b/dictation_server/src/features/users/users.service.ts
index 957dde1..6896e98 100644
--- a/dictation_server/src/features/users/users.service.ts
+++ b/dictation_server/src/features/users/users.service.ts
@@ -63,7 +63,6 @@ import {
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
import { AccountsRepositoryService } from '../../repositories/accounts/accounts.repository.service';
-import { Account } from '../../repositories/accounts/entity/account.entity';
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
@Injectable()
@@ -195,7 +194,6 @@ export class UsersService {
);
//DBよりアクセス者の所属するアカウントIDを取得する
let adminUser: EntityUser;
- let account: Account | null;
try {
adminUser = await this.usersRepository.findUserByExternalId(
context,
@@ -210,7 +208,7 @@ export class UsersService {
}
const accountId = adminUser.account_id;
- account = adminUser.account;
+ const account = adminUser.account;
//authorIdが重複していないかチェックする
if (authorId) {
diff --git a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts
index b399326..7432e72 100644
--- a/dictation_server/src/gateways/sendgrid/sendgrid.service.ts
+++ b/dictation_server/src/gateways/sendgrid/sendgrid.service.ts
@@ -20,10 +20,19 @@ import {
TYPIST_NAME,
VERIFY_LINK,
TEMPORARY_PASSWORD,
- EMAIL_DUPLICATION,
- AUTHOR_ID_DUPLICATION,
- UNEXPECTED_ERROR,
+ EMAIL_DUPLICATION_EN,
+ AUTHOR_ID_DUPLICATION_EN,
+ UNEXPECTED_ERROR_EN,
+ EMAIL_DUPLICATION_DE,
+ AUTHOR_ID_DUPLICATION_DE,
+ UNEXPECTED_ERROR_DE,
+ EMAIL_DUPLICATION_FR,
+ AUTHOR_ID_DUPLICATION_FR,
+ UNEXPECTED_ERROR_FR,
REQUEST_TIME,
+ NO_ERROR_MESSAGE_EN,
+ NO_ERROR_MESSAGE_DE,
+ NO_ERROR_MESSAGE_FR,
} from '../../templates/constants';
import { URL } from 'node:url';
@@ -1387,18 +1396,37 @@ export class SendGridService {
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU122.name}`,
);
try {
- const duplicateEmailsMsg =
- duplicateEmails.length === 0
- ? 'エラーはありません'
- : duplicateEmails.map((x) => `L${x}`).join(', ');
- const duplicateAuthorIdsMsg =
- duplicateAuthorIds.length === 0
- ? 'エラーはありません'
- : duplicateAuthorIds.map((x) => `L${x}`).join(', ');
- const otherErrorsMsg =
- otherErrors.length === 0
- ? 'エラーはありません'
- : otherErrors.map((x) => `L${x}`).join(', ');
+ let duplicateEmailsMsgEn = NO_ERROR_MESSAGE_EN;
+ let duplicateAuthorIdsMsgEn = NO_ERROR_MESSAGE_EN;
+ let otherErrorsMsgEn = NO_ERROR_MESSAGE_EN;
+ let duplicateEmailsMsgDe = NO_ERROR_MESSAGE_DE;
+ let duplicateAuthorIdsMsgDe = NO_ERROR_MESSAGE_DE;
+ let otherErrorsMsgDe = NO_ERROR_MESSAGE_DE;
+ let duplicateEmailsMsgFr = NO_ERROR_MESSAGE_FR;
+ let duplicateAuthorIdsMsgFr = NO_ERROR_MESSAGE_FR;
+ let otherErrorsMsgFr = NO_ERROR_MESSAGE_FR;
+
+ if (duplicateEmails.length !== 0) {
+ duplicateEmailsMsgEn = duplicateEmails.map((x) => `L${x}`).join(', ');
+ duplicateEmailsMsgDe = duplicateEmails.map((x) => `L${x}`).join(', ');
+ duplicateEmailsMsgFr = duplicateEmails.map((x) => `L${x}`).join(', ');
+ }
+ if (duplicateAuthorIds.length !== 0) {
+ duplicateAuthorIdsMsgEn = duplicateAuthorIds
+ .map((x) => `L${x}`)
+ .join(', ');
+ duplicateAuthorIdsMsgDe = duplicateAuthorIds
+ .map((x) => `L${x}`)
+ .join(', ');
+ duplicateAuthorIdsMsgFr = duplicateAuthorIds
+ .map((x) => `L${x}`)
+ .join(', ');
+ }
+ if (otherErrors.length !== 0) {
+ otherErrorsMsgEn = otherErrors.map((x) => `L${x}`).join(', ');
+ otherErrorsMsgDe = otherErrors.map((x) => `L${x}`).join(', ');
+ otherErrorsMsgFr = otherErrors.map((x) => `L${x}`).join(', ');
+ }
const subject = 'User Bulk Registration Failed Notification [U-122]';
@@ -1408,27 +1436,51 @@ export class SendGridService {
if (!dealerAccountName) {
html = this.templateU122NoParentHtml
.replaceAll(CUSTOMER_NAME, customerAccountName)
- .replaceAll(EMAIL_DUPLICATION, duplicateEmailsMsg)
- .replaceAll(AUTHOR_ID_DUPLICATION, duplicateAuthorIdsMsg)
- .replaceAll(UNEXPECTED_ERROR, otherErrorsMsg);
+ .replaceAll(EMAIL_DUPLICATION_EN, duplicateEmailsMsgEn)
+ .replaceAll(EMAIL_DUPLICATION_DE, duplicateEmailsMsgDe)
+ .replaceAll(EMAIL_DUPLICATION_FR, duplicateEmailsMsgFr)
+ .replaceAll(AUTHOR_ID_DUPLICATION_EN, duplicateAuthorIdsMsgEn)
+ .replaceAll(AUTHOR_ID_DUPLICATION_DE, duplicateAuthorIdsMsgDe)
+ .replaceAll(AUTHOR_ID_DUPLICATION_FR, duplicateAuthorIdsMsgFr)
+ .replaceAll(UNEXPECTED_ERROR_EN, otherErrorsMsgEn)
+ .replaceAll(UNEXPECTED_ERROR_DE, otherErrorsMsgDe)
+ .replaceAll(UNEXPECTED_ERROR_FR, otherErrorsMsgFr);
text = this.templateU122NoParentText
.replaceAll(CUSTOMER_NAME, customerAccountName)
- .replaceAll(EMAIL_DUPLICATION, duplicateEmailsMsg)
- .replaceAll(AUTHOR_ID_DUPLICATION, duplicateAuthorIdsMsg)
- .replaceAll(UNEXPECTED_ERROR, otherErrorsMsg);
+ .replaceAll(EMAIL_DUPLICATION_EN, duplicateEmailsMsgEn)
+ .replaceAll(EMAIL_DUPLICATION_DE, duplicateEmailsMsgDe)
+ .replaceAll(EMAIL_DUPLICATION_FR, duplicateEmailsMsgFr)
+ .replaceAll(AUTHOR_ID_DUPLICATION_EN, duplicateAuthorIdsMsgEn)
+ .replaceAll(AUTHOR_ID_DUPLICATION_DE, duplicateAuthorIdsMsgDe)
+ .replaceAll(AUTHOR_ID_DUPLICATION_FR, duplicateAuthorIdsMsgFr)
+ .replaceAll(UNEXPECTED_ERROR_EN, otherErrorsMsgEn)
+ .replaceAll(UNEXPECTED_ERROR_DE, otherErrorsMsgDe)
+ .replaceAll(UNEXPECTED_ERROR_FR, otherErrorsMsgFr);
} else {
html = this.templateU122Html
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(DEALER_NAME, dealerAccountName)
- .replaceAll(EMAIL_DUPLICATION, duplicateEmailsMsg)
- .replaceAll(AUTHOR_ID_DUPLICATION, duplicateAuthorIdsMsg)
- .replaceAll(UNEXPECTED_ERROR, otherErrorsMsg);
+ .replaceAll(EMAIL_DUPLICATION_EN, duplicateEmailsMsgEn)
+ .replaceAll(EMAIL_DUPLICATION_DE, duplicateEmailsMsgDe)
+ .replaceAll(EMAIL_DUPLICATION_FR, duplicateEmailsMsgFr)
+ .replaceAll(AUTHOR_ID_DUPLICATION_EN, duplicateAuthorIdsMsgEn)
+ .replaceAll(AUTHOR_ID_DUPLICATION_DE, duplicateAuthorIdsMsgDe)
+ .replaceAll(AUTHOR_ID_DUPLICATION_FR, duplicateAuthorIdsMsgFr)
+ .replaceAll(UNEXPECTED_ERROR_EN, otherErrorsMsgEn)
+ .replaceAll(UNEXPECTED_ERROR_DE, otherErrorsMsgDe)
+ .replaceAll(UNEXPECTED_ERROR_FR, otherErrorsMsgFr);
text = this.templateU122Text
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(DEALER_NAME, dealerAccountName)
- .replaceAll(EMAIL_DUPLICATION, duplicateEmailsMsg)
- .replaceAll(AUTHOR_ID_DUPLICATION, duplicateAuthorIdsMsg)
- .replaceAll(UNEXPECTED_ERROR, otherErrorsMsg);
+ .replaceAll(EMAIL_DUPLICATION_EN, duplicateEmailsMsgEn)
+ .replaceAll(EMAIL_DUPLICATION_DE, duplicateEmailsMsgDe)
+ .replaceAll(EMAIL_DUPLICATION_FR, duplicateEmailsMsgFr)
+ .replaceAll(AUTHOR_ID_DUPLICATION_EN, duplicateAuthorIdsMsgEn)
+ .replaceAll(AUTHOR_ID_DUPLICATION_DE, duplicateAuthorIdsMsgDe)
+ .replaceAll(AUTHOR_ID_DUPLICATION_FR, duplicateAuthorIdsMsgFr)
+ .replaceAll(UNEXPECTED_ERROR_EN, otherErrorsMsgEn)
+ .replaceAll(UNEXPECTED_ERROR_DE, otherErrorsMsgDe)
+ .replaceAll(UNEXPECTED_ERROR_FR, otherErrorsMsgFr);
}
// メールを送信する
diff --git a/dictation_server/src/templates/constants.ts b/dictation_server/src/templates/constants.ts
index d6650a6..47ebb6d 100644
--- a/dictation_server/src/templates/constants.ts
+++ b/dictation_server/src/templates/constants.ts
@@ -12,6 +12,18 @@ export const FILE_NAME = '$FILE_NAME$';
export const TYPIST_NAME = '$TYPIST_NAME$';
export const TEMPORARY_PASSWORD = '$TEMPORARY_PASSWORD$';
export const REQUEST_TIME = '$REQUEST_TIME$';
-export const EMAIL_DUPLICATION = `$EMAIL_DUPLICATION$`;
-export const AUTHOR_ID_DUPLICATION = `$AUTHOR_ID_DUPLICATION$`;
-export const UNEXPECTED_ERROR = `$UNEXPECTED_ERROR$`;
+// 言語ごとに変更
+export const EMAIL_DUPLICATION_EN = `$EMAIL_DUPLICATION_EN$`;
+export const AUTHOR_ID_DUPLICATION_EN = `$AUTHOR_ID_DUPLICATION_EN$`;
+export const UNEXPECTED_ERROR_EN = `$UNEXPECTED_ERROR_EN$`;
+export const EMAIL_DUPLICATION_DE = `$EMAIL_DUPLICATION_DE$`;
+export const AUTHOR_ID_DUPLICATION_DE = `$AUTHOR_ID_DUPLICATION_DE$`;
+export const UNEXPECTED_ERROR_DE = `$UNEXPECTED_ERROR_DE$`;
+export const EMAIL_DUPLICATION_FR = `$EMAIL_DUPLICATION_FR$`;
+export const AUTHOR_ID_DUPLICATION_FR = `$AUTHOR_ID_DUPLICATION_FR$`;
+export const UNEXPECTED_ERROR_FR = `$UNEXPECTED_ERROR_FR$`;
+
+// 言語ごとに当てはまる値
+export const NO_ERROR_MESSAGE_EN = 'No errors';
+export const NO_ERROR_MESSAGE_DE = 'Keine Fehler';
+export const NO_ERROR_MESSAGE_FR = 'Aucune erreur';
diff --git a/dictation_server/src/templates/template_U_122.html b/dictation_server/src/templates/template_U_122.html
index f33c4c3..b456eb5 100644
--- a/dictation_server/src/templates/template_U_122.html
+++ b/dictation_server/src/templates/template_U_122.html
@@ -18,11 +18,11 @@
1. The e-mail address in the line below has already been registered or is a duplicate of an e-mail address in
another
line.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_EN$
2. The Author ID in the line below is already registered or is a duplicate of an Author ID in another line.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_EN$
* E-mail address and Author ID that have already been registered cannot be registered again.
@@ -35,7 +35,7 @@
3. An unexpected error occurred during user registration on the following line. If it does not succeed after
trying
again, please contact your dealer.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_EN$
If you need support regarding the ODMS Cloud, please contact $DEALER_NAME$.
@@ -59,12 +59,12 @@
1. Die E-Mail-Adresse in der Zeile unten ist bereits registriert oder ist ein Duplikat einer E-Mail-Adresse in
einer
anderen Zeile.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_DE$
2. Die Author-ID in der Zeile darunter ist bereits registriert oder ein Duplikat einer AuthorID in einer anderen
Zeile.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_DE$
* E-Mail-Adresse und Autoren-ID, die bereits registriert wurden, können nicht erneut registriert werden.
@@ -78,7 +78,7 @@
3. Bei der Benutzerregistrierung ist in der folgenden Zeile ein unerwarteter Fehler aufgetreten. Sollte es auch
nach
einem erneuten Versuch nicht erfolgreich sein, wenden Sie sich bitte an Ihren Händler.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_DE$
Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich bitte an $DEALER_NAME$.
@@ -101,13 +101,13 @@
1. L'adresse e-mail dans la ligne ci-dessous a déjà été enregistrée ou est un double d'une adresse e-mail dans une
autre ligne.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_FR$
2. L'Identifiant Auteur dans la ligne ci-dessous est déjà enregistré ou est un double d'un Identifiant Auteur dans
une
autre ligne.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_FR$
* L'adresse e-mail et l'Identifiant Auteur déjà enregistrés ne peuvent pas être enregistrés à nouveau.
@@ -121,7 +121,7 @@
3. Une erreur inattendue s'est produite lors de l'enregistrement de l'utilisateur sur la ligne suivante. Si cela
ne
fonctionne pas après une nouvelle tentative, veuillez contacter votre revendeur.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_FR$
Si vous avez besoin d'assistance concernant ODMS Cloud, veuillez contacter $DEALER_NAME$.
diff --git a/dictation_server/src/templates/template_U_122.txt b/dictation_server/src/templates/template_U_122.txt
index 6729c16..59315df 100644
--- a/dictation_server/src/templates/template_U_122.txt
+++ b/dictation_server/src/templates/template_U_122.txt
@@ -5,16 +5,16 @@ Dear $CUSTOMER_NAME$,
Bulk user registration using the CSV file has failed. The cause and location of the error is shown in 1, 2, and 3 below. ( L = Line )
1. The e-mail address in the line below has already been registered or is a duplicate of an e-mail address in another line.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_EN$
2. The Author ID in the line below is already registered or is a duplicate of an Author ID in another line.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_EN$
* E-mail address and Author ID that have already been registered cannot be registered again.
* Rows without errors have been successfully registered. Therefore, if you use the same CSV file and register the user that has been successfully registered, a duplicate error will occur. Please create a CSV file containing only the lines where the error occurred, or manually register them one by one.
3. An unexpected error occurred during user registration on the following line. If it does not succeed after trying again, please contact your dealer.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_EN$
If you need support regarding the ODMS Cloud, please contact $DEALER_NAME$.
@@ -28,16 +28,16 @@ Sehr geehrte(r) $CUSTOMER_NAME$,
Die Massenregistrierung von Benutzern mithilfe der CSV-Datei ist fehlgeschlagen. Die Ursache und der Ort des Fehlers werden in 1, 2 und 3 unten angezeigt. (L = Linie)
1. Die E-Mail-Adresse in der Zeile unten ist bereits registriert oder ist ein Duplikat einer E-Mail-Adresse in einer anderen Zeile.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_DE$
2. Die Author-ID in der Zeile darunter ist bereits registriert oder ein Duplikat einer AuthorID in einer anderen Zeile.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_DE$
* E-Mail-Adresse und Autoren-ID, die bereits registriert wurden, können nicht erneut registriert werden.
* Zeilen ohne Fehler wurden erfolgreich registriert. Wenn Sie daher dieselbe CSV-Datei verwenden und den erfolgreich registrierten Benutzer registrieren, tritt ein doppelter Fehler auf. Bitte erstellen Sie eine CSV-Datei, die nur die Zeilen enthält, in denen der Fehler aufgetreten ist, oder registrieren Sie sie einzeln manuell.
3. Bei der Benutzerregistrierung ist in der folgenden Zeile ein unerwarteter Fehler aufgetreten. Sollte es auch nach einem erneuten Versuch nicht erfolgreich sein, wenden Sie sich bitte an Ihren Händler.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_DE$
Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich bitte an $DEALER_NAME$.
@@ -51,16 +51,16 @@ Chère/Cher $CUSTOMER_NAME$,
L'enregistrement groupé des utilisateurs à l'aide du fichier CSV a échoué. La cause et l'emplacement de l'erreur sont indiqués aux points 1, 2 et 3 ci-dessous. ( L = Ligne )
1. L'adresse e-mail dans la ligne ci-dessous a déjà été enregistrée ou est un double d'une adresse e-mail dans une autre ligne.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_FR$
2. L'Identifiant Auteur dans la ligne ci-dessous est déjà enregistré ou est un double d'un Identifiant Auteur dans une autre ligne.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_FR$
* L'adresse e-mail et l'Identifiant Auteur déjà enregistrés ne peuvent pas être enregistrés à nouveau.
* Les lignes sans erreurs ont été enregistrées avec succès. Par conséquent, si vous utilisez le même fichier CSV et enregistrez l'utilisateur qui a été enregistré avec succès, une erreur en double se produira. Veuillez créer un fichier CSV contenant uniquement les lignes où l'erreur s'est produite, ou enregistrez-les manuellement une par une.
3. Une erreur inattendue s'est produite lors de l'enregistrement de l'utilisateur sur la ligne suivante. Si cela ne fonctionne pas après une nouvelle tentative, veuillez contacter votre revendeur.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_FR$
Si vous avez besoin d'assistance concernant ODMS Cloud, veuillez contacter $DEALER_NAME$.
diff --git a/dictation_server/src/templates/template_U_122_no_parent.html b/dictation_server/src/templates/template_U_122_no_parent.html
index bd9a2af..1b3903a 100644
--- a/dictation_server/src/templates/template_U_122_no_parent.html
+++ b/dictation_server/src/templates/template_U_122_no_parent.html
@@ -18,11 +18,11 @@
1. The e-mail address in the line below has already been registered or is a duplicate of an e-mail address in
another
line.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_EN$
2. The Author ID in the line below is already registered or is a duplicate of an Author ID in another line.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_EN$
* E-mail address and Author ID that have already been registered cannot be registered again.
@@ -35,7 +35,7 @@
3. An unexpected error occurred during user registration on the following line. If it does not succeed after
trying
again, please contact your dealer.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_EN$
If you received this e-mail in error, please delete this e-mail from your system.
@@ -56,12 +56,12 @@
1. Die E-Mail-Adresse in der Zeile unten ist bereits registriert oder ist ein Duplikat einer E-Mail-Adresse in
einer
anderen Zeile.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_DE$
2. Die Author-ID in der Zeile darunter ist bereits registriert oder ein Duplikat einer AuthorID in einer anderen
Zeile.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_DE$
* E-Mail-Adresse und Autoren-ID, die bereits registriert wurden, können nicht erneut registriert werden.
@@ -75,7 +75,7 @@
3. Bei der Benutzerregistrierung ist in der folgenden Zeile ein unerwarteter Fehler aufgetreten. Sollte es auch
nach
einem erneuten Versuch nicht erfolgreich sein, wenden Sie sich bitte an Ihren Händler.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_DE$
Wenn Sie diese E-Mail irrtümlich erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System.
@@ -95,13 +95,13 @@
1. L'adresse e-mail dans la ligne ci-dessous a déjà été enregistrée ou est un double d'une adresse e-mail dans une
autre ligne.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_FR$
2. L'Identifiant Auteur dans la ligne ci-dessous est déjà enregistré ou est un double d'un Identifiant Auteur dans
une
autre ligne.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_FR$
* L'adresse e-mail et l'Identifiant Auteur déjà enregistrés ne peuvent pas être enregistrés à nouveau.
@@ -115,7 +115,7 @@
3. Une erreur inattendue s'est produite lors de l'enregistrement de l'utilisateur sur la ligne suivante. Si cela
ne
fonctionne pas après une nouvelle tentative, veuillez contacter votre revendeur.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_FR$
Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail de votre système.
diff --git a/dictation_server/src/templates/template_U_122_no_parent.txt b/dictation_server/src/templates/template_U_122_no_parent.txt
index f91ba7f..f9b3461 100644
--- a/dictation_server/src/templates/template_U_122_no_parent.txt
+++ b/dictation_server/src/templates/template_U_122_no_parent.txt
@@ -5,16 +5,16 @@ Dear $CUSTOMER_NAME$,
Bulk user registration using the CSV file has failed. The cause and location of the error is shown in 1, 2, and 3 below. ( L = Line )
1. The e-mail address in the line below has already been registered or is a duplicate of an e-mail address in another line.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_EN$
2. The Author ID in the line below is already registered or is a duplicate of an Author ID in another line.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_EN$
* E-mail address and Author ID that have already been registered cannot be registered again.
* Rows without errors have been successfully registered. Therefore, if you use the same CSV file and register the user that has been successfully registered, a duplicate error will occur. Please create a CSV file containing only the lines where the error occurred, or manually register them one by one.
3. An unexpected error occurred during user registration on the following line. If it does not succeed after trying again, please contact your dealer.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_EN$
If you 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.
@@ -26,16 +26,16 @@ Sehr geehrte(r) $CUSTOMER_NAME$,
Die Massenregistrierung von Benutzern mithilfe der CSV-Datei ist fehlgeschlagen. Die Ursache und der Ort des Fehlers werden in 1, 2 und 3 unten angezeigt. (L = Linie)
1. Die E-Mail-Adresse in der Zeile unten ist bereits registriert oder ist ein Duplikat einer E-Mail-Adresse in einer anderen Zeile.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_DE$
2. Die Author-ID in der Zeile darunter ist bereits registriert oder ein Duplikat einer AuthorID in einer anderen Zeile.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_DE$
* E-Mail-Adresse und Autoren-ID, die bereits registriert wurden, können nicht erneut registriert werden.
* Zeilen ohne Fehler wurden erfolgreich registriert. Wenn Sie daher dieselbe CSV-Datei verwenden und den erfolgreich registrierten Benutzer registrieren, tritt ein doppelter Fehler auf. Bitte erstellen Sie eine CSV-Datei, die nur die Zeilen enthält, in denen der Fehler aufgetreten ist, oder registrieren Sie sie einzeln manuell.
3. Bei der Benutzerregistrierung ist in der folgenden Zeile ein unerwarteter Fehler aufgetreten. Sollte es auch nach einem erneuten Versuch nicht erfolgreich sein, wenden Sie sich bitte an Ihren Händler.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_DE$
Wenn Sie diese E-Mail irrtümlich erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System.
Dies ist eine automatisch generierte E-Mail und diese Mailbox wird nicht überwacht. Bitte antworten Sie nicht.
@@ -47,16 +47,16 @@ Chère/Cher $CUSTOMER_NAME$,
L'enregistrement groupé des utilisateurs à l'aide du fichier CSV a échoué. La cause et l'emplacement de l'erreur sont indiqués aux points 1, 2 et 3 ci-dessous. ( L = Ligne )
1. L'adresse e-mail dans la ligne ci-dessous a déjà été enregistrée ou est un double d'une adresse e-mail dans une autre ligne.
- $EMAIL_DUPLICATION$
+ $EMAIL_DUPLICATION_FR$
2. L'Identifiant Auteur dans la ligne ci-dessous est déjà enregistré ou est un double d'un Identifiant Auteur dans une autre ligne.
- $AUTHOR_ID_DUPLICATION$
+ $AUTHOR_ID_DUPLICATION_FR$
* L'adresse e-mail et l'Identifiant Auteur déjà enregistrés ne peuvent pas être enregistrés à nouveau.
* Les lignes sans erreurs ont été enregistrées avec succès. Par conséquent, si vous utilisez le même fichier CSV et enregistrez l'utilisateur qui a été enregistré avec succès, une erreur en double se produira. Veuillez créer un fichier CSV contenant uniquement les lignes où l'erreur s'est produite, ou enregistrez-les manuellement une par une.
3. Une erreur inattendue s'est produite lors de l'enregistrement de l'utilisateur sur la ligne suivante. Si cela ne fonctionne pas après une nouvelle tentative, veuillez contacter votre revendeur.
- $UNEXPECTED_ERROR$
+ $UNEXPECTED_ERROR_FR$
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