Merged PR 795: API実装(一括登録完了)

## 概要
[Task3763: API実装(一括登録完了)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3763)

- 一括登録完了APIとテストを実装しました。
  - メール文面は多言語対応がまだですのですべて日本語の文面にしています。

## レビューポイント
- 送信メールの内容は認識通りでしょうか?

## UIの変更
- なし
## 動作確認状況
- ローカルで確認
This commit is contained in:
makabe.t 2024-03-04 09:15:53 +00:00
parent 363f12f86f
commit 7ff563f644
13 changed files with 1053 additions and 2 deletions

View File

@ -1139,7 +1139,14 @@ export class UsersController {
const context = makeContext(systemName, requestId);
this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`);
// TODO: 処理を実装
const { accountId, filename, requestTime, errors } = body;
await this.usersService.multipleImportsComplate(
context,
accountId,
filename,
requestTime,
errors,
);
return {};
}

View File

@ -56,6 +56,8 @@ import {
import { truncateAllTable } from '../../common/test/init';
import { createTask } from '../files/test/utility';
import { createCheckoutPermissions } from '../tasks/test/utility';
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
import { MultipleImportErrors } from './types/types';
describe('UsersService.confirmUser', () => {
let source: DataSource | null = null;
@ -3653,3 +3655,188 @@ describe('UsersService.deleteUser', () => {
}
});
});
describe('UsersService.multipleImportsComplate', () => {
let source: DataSource | null = null;
beforeAll(async () => {
if (source == null) {
source = await (async () => {
const s = new DataSource({
type: 'mysql',
host: 'test_mysql_db',
port: 3306,
username: 'user',
password: 'password',
database: 'odms',
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
synchronize: false, // trueにすると自動的にmigrationが行われるため注意
});
return await s.initialize();
})();
}
});
beforeEach(async () => {
if (source) {
await truncateAllTable(source);
}
});
afterAll(async () => {
await source?.destroy();
source = null;
});
it('ユーザー一括登録完了メールを送信できる(成功)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const { account: dealer } = await makeTestAccount(source, {
company_name: 'dealerCompany',
tier: 4,
});
const { account, admin } = await makeTestAccount(source, {
tier: 5,
company_name: 'company',
parent_account_id: dealer.id,
});
const service = module.get<UsersService>(UsersService);
const context = makeContext(`uuidv4`, 'requestId');
overrideAdB2cService(service, {
getUsers: async () => {
return [
{
id: admin.external_id,
displayName: 'admin',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'admin@example.com',
},
],
},
];
},
});
overrideSendgridService(service, {});
// メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。
const spy = jest
.spyOn(service['sendgridService'], 'sendMailWithU121')
.mockImplementation();
const requestTime = Math.floor(new Date(2024, 2, 3).getTime() / 1000);
const filename = `U_20240303_000000_${admin.account_id}_${admin.id}.csv`;
const errors: MultipleImportErrors[] = [];
// ユーザー一括登録完了
await service.multipleImportsComplate(
context,
account.id,
filename,
requestTime,
errors,
);
// ADB2Cユーザー削除メソッドが呼ばれているか確認
expect(spy).toHaveBeenCalledWith(
context,
['admin@example.com'],
account.company_name,
dealer.company_name,
'2024.3.3',
filename,
);
});
it('ユーザー一括登録完了メールを送信できる(失敗)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const { account: dealer } = await makeTestAccount(source, {
company_name: 'dealerCompany',
tier: 4,
});
const { account, admin } = await makeTestAccount(source, {
tier: 5,
company_name: 'company',
parent_account_id: dealer.id,
});
const service = module.get<UsersService>(UsersService);
const context = makeContext(`uuidv4`, 'requestId');
overrideAdB2cService(service, {
getUsers: async () => {
return [
{
id: admin.external_id,
displayName: 'admin',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'admin@example.com',
},
],
},
];
},
});
overrideSendgridService(service, {});
// メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。
const spy = jest
.spyOn(service['sendgridService'], 'sendMailWithU122')
.mockImplementation();
const requestTime = Math.floor(new Date(2024, 2, 3).getTime() / 1000);
const filename = `U_20240303_000000_${admin.account_id}_${admin.id}`;
const errors: MultipleImportErrors[] = [
{
name: 'user1',
line: 1,
errorCode: 'E010301',
},
{
name: 'user2',
line: 2,
errorCode: 'E010302',
},
{
name: 'user2',
line: 3,
errorCode: 'E010302',
},
{
name: 'user3',
line: 4,
errorCode: 'E009999',
},
];
// ユーザー一括登録完了
await service.multipleImportsComplate(
context,
account.id,
filename,
requestTime,
errors,
);
// ADB2Cユーザー削除メソッドが呼ばれているか確認
expect(spy).toHaveBeenCalledWith(
context,
['admin@example.com'],
account.company_name,
dealer.company_name,
[1],
[2, 3],
[4],
);
});
});

View File

@ -23,7 +23,11 @@ import {
} from '../../repositories/users/entity/user.entity';
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
import { GetRelationsResponse, User } from './types/types';
import {
GetRelationsResponse,
MultipleImportErrors,
User,
} from './types/types';
import {
AdminDeleteFailedError,
AssignedWorkflowWithAuthorDeleteFailedError,
@ -1492,6 +1496,109 @@ export class UsersService {
);
}
}
/**
*
* @param context
* @param accountId
* @param fileName
* @param requestTime
* @param errors
* @returns imports complate
*/
async multipleImportsComplate(
context: Context,
accountId: number,
fileName: string,
requestTime: number,
errors: MultipleImportErrors[],
): Promise<void> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${
this.multipleImportsComplate.name
} | params: { accountId: ${accountId}, fileName: ${fileName}, requestTime: ${requestTime} };`,
);
try {
const account = await this.accountsRepository.findAccountById(
context,
accountId,
);
if (!account) {
throw new Error(`account not found. id=${accountId}`);
}
const dealerId = account.parent_account_id;
let dealerName: string | null = null;
if (dealerId !== null) {
const { company_name } = await this.accountsRepository.findAccountById(
context,
dealerId,
);
dealerName = company_name;
}
// アカウント情報を取得
const { companyName, adminEmails } = await this.getAccountInformation(
context,
accountId,
);
if (errors.length === 0) {
const requestTimeDate = new Date(requestTime * 1000);
// 完了メールを通知する
await this.sendgridService.sendMailWithU121(
context,
adminEmails,
companyName,
dealerName,
`${requestTimeDate.getFullYear()}.${
requestTimeDate.getMonth() + 1
}.${requestTimeDate.getDate()}`,
fileName,
);
} else {
const duplicateEmails: number[] = [];
const duplicateAuthorIds: number[] = [];
const otherErrors: number[] = [];
// エラーを仕分ける
for (const error of errors) {
switch (error.errorCode) {
// メールアドレス重複エラー
case 'E010301':
duplicateEmails.push(error.line);
break;
// AuthorID重複エラー
case 'E010302':
duplicateAuthorIds.push(error.line);
break;
// その他エラー
default:
otherErrors.push(error.line);
break;
}
}
// エラーメールを通知する
await this.sendgridService.sendMailWithU122(
context,
adminEmails,
companyName,
dealerName,
duplicateEmails,
duplicateAuthorIds,
otherErrors,
);
}
} catch (e) {
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
} finally {
this.logger.log(
`[OUT] [${context.getTrackingId()}] ${
this.multipleImportsComplate.name
}`,
);
}
}
/**
* IDを指定して

View File

@ -20,6 +20,10 @@ import {
TYPIST_NAME,
VERIFY_LINK,
TEMPORARY_PASSWORD,
EMAIL_DUPLICATION,
AUTHOR_ID_DUPLICATION,
UNEXPECTED_ERROR,
REQUEST_TIME,
} from '../../templates/constants';
import { URL } from 'node:url';
@ -71,6 +75,14 @@ export class SendGridService {
private readonly templateU119Text: string;
private readonly templateU119NoParentHtml: string;
private readonly templateU119NoParentText: string;
private readonly templateU121Html: string;
private readonly templateU121Text: string;
private readonly templateU121NoParentHtml: string;
private readonly templateU121NoParentText: string;
private readonly templateU122Html: string;
private readonly templateU122Text: string;
private readonly templateU122NoParentHtml: string;
private readonly templateU122NoParentText: string;
constructor(private readonly configService: ConfigService) {
this.appDomain = this.configService.getOrThrow<string>('APP_DOMAIN');
@ -255,6 +267,44 @@ export class SendGridService {
path.resolve(__dirname, `../../templates/template_U_119_no_parent.txt`),
'utf-8',
);
this.templateU121Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_121.html`),
'utf-8',
);
this.templateU121Text = readFileSync(
path.resolve(__dirname, `../../templates/template_U_121.txt`),
'utf-8',
);
this.templateU121NoParentHtml = readFileSync(
path.resolve(
__dirname,
`../../templates/template_U_121_no_parent.html`,
),
'utf-8',
);
this.templateU121NoParentText = readFileSync(
path.resolve(__dirname, `../../templates/template_U_121_no_parent.txt`),
'utf-8',
);
this.templateU122Html = readFileSync(
path.resolve(__dirname, `../../templates/template_U_122.html`),
'utf-8',
);
this.templateU122Text = readFileSync(
path.resolve(__dirname, `../../templates/template_U_122.txt`),
'utf-8',
);
this.templateU122NoParentHtml = readFileSync(
path.resolve(
__dirname,
`../../templates/template_U_122_no_parent.html`,
),
'utf-8',
);
this.templateU122NoParentText = readFileSync(
path.resolve(__dirname, `../../templates/template_U_122_no_parent.txt`),
'utf-8',
);
}
}
@ -1138,6 +1188,157 @@ export class SendGridService {
}
}
/**
* U-121使
* @param context
* @param customerAdminMails (primary/secondary)
* @param customerAccountName
* @param dealerAccountName
* @param requestTime
* @param fileName 使
* @returns mail with u121
*/
async sendMailWithU121(
context: Context,
customerAdminMails: string[],
customerAccountName: string,
dealerAccountName: string | null,
requestTime: string,
fileName: string,
): Promise<void> {
this.logger.log(
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU121.name}`,
);
try {
const subject = 'ユーザー一括登録 完了通知 [U-121]';
let html: string;
let text: string;
if (!dealerAccountName) {
html = this.templateU121NoParentHtml
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(REQUEST_TIME, requestTime)
.replaceAll(FILE_NAME, fileName);
text = this.templateU121NoParentText
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(REQUEST_TIME, requestTime)
.replaceAll(FILE_NAME, fileName);
} else {
html = this.templateU121Html
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(DEALER_NAME, dealerAccountName)
.replaceAll(REQUEST_TIME, requestTime)
.replaceAll(FILE_NAME, fileName);
text = this.templateU121Text
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(DEALER_NAME, dealerAccountName)
.replaceAll(REQUEST_TIME, requestTime)
.replaceAll(FILE_NAME, fileName);
}
// メールを送信する
await this.sendMail(
context,
customerAdminMails,
[],
this.mailFrom,
subject,
text,
html,
);
} finally {
this.logger.log(
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU121.name}`,
);
}
}
/**
* U-122使
* @param context
* @param customerAdminMails (primary/secondary)
* @param customerAccountName
* @param dealerAccountName
* @param duplicateEmails
* @param duplicateAuthorIds AuthorIdの重複エラーのある行番号
* @param otherErrors
* @returns mail with u122
*/
async sendMailWithU122(
context: Context,
customerAdminMails: string[],
customerAccountName: string,
dealerAccountName: string | null,
duplicateEmails: number[],
duplicateAuthorIds: number[],
otherErrors: number[],
): Promise<void> {
this.logger.log(
`[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(', ');
const subject = 'ユーザー一括登録 失敗通知 [U-122]';
let html: string;
let text: string;
if (!dealerAccountName) {
html = this.templateU122NoParentHtml
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(EMAIL_DUPLICATION, duplicateEmailsMsg)
.replaceAll(AUTHOR_ID_DUPLICATION, duplicateAuthorIdsMsg)
.replaceAll(UNEXPECTED_ERROR, otherErrorsMsg);
text = this.templateU122NoParentText
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(EMAIL_DUPLICATION, duplicateEmailsMsg)
.replaceAll(AUTHOR_ID_DUPLICATION, duplicateAuthorIdsMsg)
.replaceAll(UNEXPECTED_ERROR, otherErrorsMsg);
} 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);
text = this.templateU122Text
.replaceAll(CUSTOMER_NAME, customerAccountName)
.replaceAll(DEALER_NAME, dealerAccountName)
.replaceAll(EMAIL_DUPLICATION, duplicateEmailsMsg)
.replaceAll(AUTHOR_ID_DUPLICATION, duplicateAuthorIdsMsg)
.replaceAll(UNEXPECTED_ERROR, otherErrorsMsg);
}
// メールを送信する
await this.sendMail(
context,
customerAdminMails,
[],
this.mailFrom,
subject,
text,
html,
);
} finally {
this.logger.log(
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU122.name}`,
);
}
}
/**
*
* @param context

View File

@ -11,3 +11,8 @@ export const AUTHOR_NAME = '$AUTHOR_NAME$';
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$`;

View File

@ -0,0 +1,65 @@
<html>
<head>
<title>Storage Usage Exceeded Notification [U-119]</title>
</head>
<body>
<div>
<h3>&lt;English&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>
CSVファイルによるユーザー一括登録が完了しました。<br />
- リクエスト日時:$REQUEST_TIME$<br />
- SCVファイル名$FILE_NAME$
</p>
<p>
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$
にお問い合わせください。
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system.<br />
This is an automatically generated e-mail, please do not reply.
</p>
</div>
<div>
<h3>&lt;Deutsch&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>
CSVファイルによるユーザー一括登録が完了しました。<br />
- リクエスト日時:$REQUEST_TIME$<br />
- SCVファイル名$FILE_NAME$
</p>
<p>
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$
にお問い合わせください。
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system.<br />
This is an automatically generated e-mail, please do not reply.
</p>
</div>
<div>
<h3>&lt;Français&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>
CSVファイルによるユーザー一括登録が完了しました。<br />
- リクエスト日時:$REQUEST_TIME$<br />
- SCVファイル名$FILE_NAME$
</p>
<p>
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$
にお問い合わせください。
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system.<br />
This is an automatically generated e-mail, please do not reply.
</p>
</div>
</body>
</html>

View File

@ -0,0 +1,44 @@
<English>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録が完了しました。
- リクエスト日時:$REQUEST_TIME$
- SCVファイル名$FILE_NAME$
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$ にお問い合わせください。
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.
<Deutsch>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録が完了しました。
- リクエスト日時:$REQUEST_TIME$
- SCVファイル名$FILE_NAME$
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$ にお問い合わせください。
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.
<Français>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録が完了しました。
- リクエスト日時:$REQUEST_TIME$
- SCVファイル名$FILE_NAME$
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$ にお問い合わせください。
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.

View File

@ -0,0 +1,53 @@
<html>
<head>
<title>Storage Usage Exceeded Notification [U-119]</title>
</head>
<body>
<div>
<h3>&lt;English&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>
CSVファイルによるユーザー一括登録が完了しました。<br />
- リクエスト日時:$REQUEST_TIME$<br />
- SCVファイル名$FILE_NAME$
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system.<br />
This is an automatically generated e-mail, please do not reply.
</p>
</div>
<div>
<h3>&lt;Deutsch&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>
CSVファイルによるユーザー一括登録が完了しました。<br />
- リクエスト日時:$REQUEST_TIME$<br />
- SCVファイル名$FILE_NAME$
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system.<br />
This is an automatically generated e-mail, please do not reply.
</p>
</div>
<div>
<h3>&lt;Français&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>
CSVファイルによるユーザー一括登録が完了しました。<br />
- リクエスト日時:$REQUEST_TIME$<br />
- SCVファイル名$FILE_NAME$
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system.<br />
This is an automatically generated e-mail, please do not reply.
</p>
</div>
</body>
</html>

View File

@ -0,0 +1,38 @@
<English>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録が完了しました。
- リクエスト日時:$REQUEST_TIME$
- SCVファイル名$FILE_NAME$
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.
<Deutsch>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録が完了しました。
- リクエスト日時:$REQUEST_TIME$
- SCVファイル名$FILE_NAME$
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.
<Français>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録が完了しました。
- リクエスト日時:$REQUEST_TIME$
- SCVファイル名$FILE_NAME$
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.

View File

@ -0,0 +1,107 @@
<html>
<head>
<title>Storage Usage Exceeded Notification [U-119]</title>
</head>
<body>
<div>
<h3>&lt;English&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>CSVファイルによるユーザー一括登録に失敗しました。</p>
<p>
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail
addressと重複しています。<br />
$EMAIL_DUPLICATION$
</p>
<p>
2.
以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。<br />
$AUTHOR_ID_DUPLICATION$
</p>
<p>
*既に登録済みのE-mail address, Author IDを再登録することはできません。
</p>
<p>
3.
以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。<br />
$UNEXPECTED_ERROR$
</p>
<p>
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$
にお問い合わせください。
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system. This is an automatically generated e-mail, please do not
reply.
</p>
</div>
<div>
<h3>&lt;Deutsch&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>CSVファイルによるユーザー一括登録に失敗しました。</p>
<p>
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail
addressと重複しています。<br />
$EMAIL_DUPLICATION$
</p>
<p>
2.
以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。<br />
$AUTHOR_ID_DUPLICATION$
</p>
<p>
*既に登録済みのE-mail address, Author IDを再登録することはできません。
</p>
<p>
3.
以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。<br />
$UNEXPECTED_ERROR$
</p>
<p>
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$
にお問い合わせください。
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system. This is an automatically generated e-mail, please do not
reply.
</p>
</div>
<div>
<h3>&lt;Français&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>CSVファイルによるユーザー一括登録に失敗しました。</p>
<p>
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail
addressと重複しています。<br />
$EMAIL_DUPLICATION$
</p>
<p>
2.
以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。<br />
$AUTHOR_ID_DUPLICATION$
</p>
<p>
*既に登録済みのE-mail address, Author IDを再登録することはできません。
</p>
<p>
3.
以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。<br />
$UNEXPECTED_ERROR$
</p>
<p>
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$
にお問い合わせください。
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system. This is an automatically generated e-mail, please do not
reply.
</p>
</div>
</body>
</html>

View File

@ -0,0 +1,74 @@
<English>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録に失敗しました。
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail addressと重複しています。
$EMAIL_DUPLICATION$
2. 以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。
$AUTHOR_ID_DUPLICATION$
*既に登録済みのE-mail address, Author IDを再登録することはできません。
3. 以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。
   $UNEXPECTED_ERROR$
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$ にお問い合わせください。
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.
<Deutsch>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録に失敗しました。
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail addressと重複しています。
$EMAIL_DUPLICATION$
2. 以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。
$AUTHOR_ID_DUPLICATION$
*既に登録済みのE-mail address, Author IDを再登録することはできません。
3. 以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。
   $UNEXPECTED_ERROR$
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$ にお問い合わせください。
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.
<Français>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録に失敗しました。
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail addressと重複しています。
$EMAIL_DUPLICATION$
2. 以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。
$AUTHOR_ID_DUPLICATION$
*既に登録済みのE-mail address, Author IDを再登録することはできません。
3. 以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。
   $UNEXPECTED_ERROR$
ディーラーによるサポートが必要な場合は、 $DEALER_NAME$ にお問い合わせください。
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.

View File

@ -0,0 +1,95 @@
<html>
<head>
<title>Storage Usage Exceeded Notification [U-119]</title>
</head>
<body>
<div>
<h3>&lt;English&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>CSVファイルによるユーザー一括登録に失敗しました。</p>
<p>
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail
addressと重複しています。<br />
$EMAIL_DUPLICATION$
</p>
<p>
2.
以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。<br />
$AUTHOR_ID_DUPLICATION$
</p>
<p>
*既に登録済みのE-mail address, Author IDを再登録することはできません。
</p>
<p>
3.
以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。<br />
$UNEXPECTED_ERROR$
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system. This is an automatically generated e-mail, please do not
reply.
</p>
</div>
<div>
<h3>&lt;Deutsch&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>CSVファイルによるユーザー一括登録に失敗しました。</p>
<p>
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail
addressと重複しています。<br />
$EMAIL_DUPLICATION$
</p>
<p>
2.
以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。<br />
$AUTHOR_ID_DUPLICATION$
</p>
<p>
*既に登録済みのE-mail address, Author IDを再登録することはできません。
</p>
<p>
3.
以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。<br />
$UNEXPECTED_ERROR$
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system. This is an automatically generated e-mail, please do not
reply.
</p>
</div>
<div>
<h3>&lt;Français&gt;</h3>
<p>Dear $CUSTOMER_NAME$,</p>
<p>ODMS Cloudをご利用いただきありがとうございます。</p>
<p>CSVファイルによるユーザー一括登録に失敗しました。</p>
<p>
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail
addressと重複しています。<br />
$EMAIL_DUPLICATION$
</p>
<p>
2.
以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。<br />
$AUTHOR_ID_DUPLICATION$
</p>
<p>
*既に登録済みのE-mail address, Author IDを再登録することはできません。
</p>
<p>
3.
以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。<br />
$UNEXPECTED_ERROR$
</p>
<p>
If you received this e-mail in error, please delete this e-mail from
your system. This is an automatically generated e-mail, please do not
reply.
</p>
</div>
</body>
</html>

View File

@ -0,0 +1,68 @@
<English>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録に失敗しました。
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail addressと重複しています。
$EMAIL_DUPLICATION$
2. 以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。
$AUTHOR_ID_DUPLICATION$
*既に登録済みのE-mail address, Author IDを再登録することはできません。
3. 以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。
   $UNEXPECTED_ERROR$
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.
<Deutsch>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録に失敗しました。
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail addressと重複しています。
$EMAIL_DUPLICATION$
2. 以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。
$AUTHOR_ID_DUPLICATION$
*既に登録済みのE-mail address, Author IDを再登録することはできません。
3. 以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。
   $UNEXPECTED_ERROR$
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.
<Français>
Dear $CUSTOMER_NAME$,
ODMS Cloudをご利用いただきありがとうございます。
CSVファイルによるユーザー一括登録に失敗しました。
1. 以下の行にあるE-mail addressは既に登録済みか、別の行のE-mail addressと重複しています。
$EMAIL_DUPLICATION$
2. 以下の行にあるAuthorIDは既に登録済みか、別の行のAuthorIDと重複しています。
$AUTHOR_ID_DUPLICATION$
*既に登録済みのE-mail address, Author IDを再登録することはできません。
3. 以下の行のユーザー登録時に想定外のエラーが発生しました。時間をおいて再度実行しても成功しない場合はディーラーにお問い合わせください。
   $UNEXPECTED_ERROR$
If you received this e-mail in error, please delete this e-mail from your system.
This is an automatically generated e-mail, please do not reply.