Merged PR 641: ライセンス割当完了通知 [U-108] の実装
## 概要 [Task3305: ライセンス割当完了通知 [U-108] の実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3305) - ライセンス割り当ての完了通知メール送信機能を追加しました。 - テストでメール送信しないようSendGridのメソッドを上書きする処理を追加しました。 ## レビューポイント - テンプレートの適用内容に不自然な点はないでしょうか? - テストでのメソッドの上書きは適切でしょうか? ## UIの変更 - なし ## 動作確認状況 - ローカルで確認
This commit is contained in:
parent
48ff009d39
commit
9e1bc8944f
@ -97,6 +97,41 @@ export const overrideSendgridService = <TService>(
|
||||
customerMail: string,
|
||||
customerAccountName: string,
|
||||
) => Promise<void>;
|
||||
sendMailWithU105?: (
|
||||
context: Context,
|
||||
customerMails: string[],
|
||||
customerAccountName: string,
|
||||
lisenceCount: number,
|
||||
poNumber: string,
|
||||
dealerEmails: string[],
|
||||
dealerAccountName: string,
|
||||
) => Promise<void>;
|
||||
sendMailWithU106?: (
|
||||
context: Context,
|
||||
customerMails: string[],
|
||||
customerAccountName: string,
|
||||
lisenceCount: number,
|
||||
poNumber: string,
|
||||
dealerEmails: string[],
|
||||
dealerAccountName: string,
|
||||
) => Promise<void>;
|
||||
sendMailWithU107?: (
|
||||
context: Context,
|
||||
customerMails: string[],
|
||||
customerAccountName: string,
|
||||
lisenceCount: number,
|
||||
poNumber: string,
|
||||
dealerEmails: string[],
|
||||
dealerAccountName: string,
|
||||
) => Promise<void>;
|
||||
sendMailWithU108?: (
|
||||
context: Context,
|
||||
userName: string,
|
||||
userMail: string,
|
||||
customerAdminMails: string[],
|
||||
customerAccountName: string,
|
||||
dealerAccountName: string,
|
||||
) => Promise<void>;
|
||||
},
|
||||
): void => {
|
||||
// テストコードでのみ許される強引な方法でprivateメンバ変数の参照を取得
|
||||
@ -131,6 +166,58 @@ export const overrideSendgridService = <TService>(
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
if (overrides.sendMailWithU105) {
|
||||
Object.defineProperty(obj, obj.sendMailWithU105.name, {
|
||||
value: overrides.sendMailWithU105,
|
||||
writable: true,
|
||||
});
|
||||
} else {
|
||||
Object.defineProperty(obj, obj.sendMailWithU105.name, {
|
||||
value: async () => {
|
||||
return;
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
if (overrides.sendMailWithU106) {
|
||||
Object.defineProperty(obj, obj.sendMailWithU106.name, {
|
||||
value: overrides.sendMailWithU106,
|
||||
writable: true,
|
||||
});
|
||||
} else {
|
||||
Object.defineProperty(obj, obj.sendMailWithU106.name, {
|
||||
value: async () => {
|
||||
return;
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
if (overrides.sendMailWithU107) {
|
||||
Object.defineProperty(obj, obj.sendMailWithU107.name, {
|
||||
value: overrides.sendMailWithU107,
|
||||
writable: true,
|
||||
});
|
||||
} else {
|
||||
Object.defineProperty(obj, obj.sendMailWithU107.name, {
|
||||
value: async () => {
|
||||
return;
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
if (overrides.sendMailWithU108) {
|
||||
Object.defineProperty(obj, obj.sendMailWithU108.name, {
|
||||
value: overrides.sendMailWithU108,
|
||||
writable: true,
|
||||
});
|
||||
} else {
|
||||
Object.defineProperty(obj, obj.sendMailWithU108.name, {
|
||||
value: async () => {
|
||||
return;
|
||||
},
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (overrides.createMailContentFromEmailConfirm) {
|
||||
Object.defineProperty(obj, obj.createMailContentFromEmailConfirm.name, {
|
||||
|
||||
@ -2273,6 +2273,12 @@ describe('issueLicense', () => {
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU107: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
const now = new Date();
|
||||
// 親と子アカウントを作成する
|
||||
const { id: parentAccountId } = (
|
||||
@ -2368,6 +2374,11 @@ describe('issueLicense', () => {
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU107: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
const now = new Date();
|
||||
// 親と子アカウントを作成する
|
||||
const { id: parentAccountId } = (
|
||||
@ -6125,6 +6136,11 @@ describe('deleteAccountAndData', () => {
|
||||
const module = await makeTestingModule(source);
|
||||
if (!module) fail();
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU107: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
// 第一~第四階層のアカウント作成
|
||||
const {
|
||||
tier1Accounts: tier1Accounts,
|
||||
@ -6232,6 +6248,11 @@ describe('deleteAccountAndData', () => {
|
||||
const licensesB = await getLicenses(source, tier5AccountsB.account.id);
|
||||
|
||||
const usersService = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(usersService, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
// アカウントAのライセンスを割り当てる
|
||||
await usersService.allocateLicense(
|
||||
context,
|
||||
|
||||
@ -41,6 +41,7 @@ import {
|
||||
makeTestSimpleAccount,
|
||||
makeTestUser,
|
||||
} from '../../common/test/utility';
|
||||
import { overrideSendgridService } from '../../common/test/overrides';
|
||||
|
||||
describe('DBテスト', () => {
|
||||
let source: DataSource | null = null;
|
||||
@ -330,6 +331,11 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
const expiry_date = new NewAllocatedLicenseExpirationDate();
|
||||
|
||||
@ -401,6 +407,11 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
await service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
@ -478,7 +489,11 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
const expiry_date = new NewAllocatedLicenseExpirationDate();
|
||||
|
||||
await service.allocateLicense(
|
||||
@ -584,6 +599,11 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
await service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
@ -648,6 +668,11 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
await service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
@ -712,6 +737,11 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
await service.allocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
@ -756,6 +786,11 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
service.allocateLicense(
|
||||
@ -808,6 +843,11 @@ describe('ライセンス割り当て', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
service.allocateLicense(
|
||||
@ -885,6 +925,11 @@ describe('ライセンス割り当て解除', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
await service.deallocateLicense(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
userId,
|
||||
@ -975,6 +1020,11 @@ describe('ライセンス割り当て解除', () => {
|
||||
);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU108: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
await expect(
|
||||
service.deallocateLicense(makeContext('trackingId', 'requestId'), userId),
|
||||
).rejects.toEqual(
|
||||
@ -1031,6 +1081,11 @@ describe('ライセンス注文キャンセル', () => {
|
||||
);
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU106: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
await service.cancelOrder(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
tier2Accounts[0].users[0].external_id,
|
||||
@ -1066,6 +1121,11 @@ describe('ライセンス注文キャンセル', () => {
|
||||
);
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU106: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
await expect(
|
||||
service.cancelOrder(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
@ -1097,6 +1157,11 @@ describe('ライセンス注文キャンセル', () => {
|
||||
);
|
||||
|
||||
const service = module.get<LicensesService>(LicensesService);
|
||||
overrideSendgridService(service, {
|
||||
sendMailWithU106: async () => {
|
||||
return;
|
||||
},
|
||||
});
|
||||
await expect(
|
||||
service.cancelOrder(
|
||||
makeContext('trackingId', 'requestId'),
|
||||
|
||||
@ -1005,9 +1005,8 @@ export class UsersService {
|
||||
);
|
||||
|
||||
try {
|
||||
const accountId = (
|
||||
await this.usersRepository.findUserById(context, userId)
|
||||
).account_id;
|
||||
const { external_id: externalId, account_id: accountId } =
|
||||
await this.usersRepository.findUserById(context, userId);
|
||||
|
||||
await this.licensesRepository.allocateLicense(
|
||||
context,
|
||||
@ -1015,6 +1014,47 @@ export class UsersService {
|
||||
newLicenseId,
|
||||
accountId,
|
||||
);
|
||||
|
||||
// メール送信処理
|
||||
try {
|
||||
const { parent_account_id: dealerId } =
|
||||
await this.accountsRepository.findAccountById(context, accountId);
|
||||
|
||||
if (dealerId == null) {
|
||||
throw new Error(`dealer is null. account_id=${accountId}`);
|
||||
}
|
||||
|
||||
const { company_name: dealerName } =
|
||||
await this.accountsRepository.findAccountById(context, dealerId);
|
||||
|
||||
const { companyName, adminEmails } = await this.getAccountInformation(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
|
||||
const adb2cUser = await this.adB2cService.getUser(context, externalId);
|
||||
const { displayName, emailAddress } =
|
||||
getUserNameAndMailAddress(adb2cUser);
|
||||
|
||||
if (emailAddress == null) {
|
||||
throw new Error(`emailAddress is null. externalId=${externalId}`);
|
||||
}
|
||||
|
||||
// 管理者に割り当てた場合にはTOに管理者のメールアドレスを設定するので、CCには管理者のメールアドレスを設定しない
|
||||
const ccAdminEmails = adminEmails.filter((x) => x !== emailAddress);
|
||||
|
||||
await this.sendgridService.sendMailWithU108(
|
||||
context,
|
||||
displayName,
|
||||
emailAddress,
|
||||
ccAdminEmails,
|
||||
companyName,
|
||||
dealerName,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
// メール送信に関する例外はログだけ出して握りつぶす
|
||||
}
|
||||
} catch (e) {
|
||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
@ -1197,4 +1237,51 @@ export class UsersService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* アカウントIDを指定して、アカウント情報と管理者情報を取得する
|
||||
* @param context
|
||||
* @param accountId 対象アカウントID
|
||||
* @returns 企業名/管理者メールアドレス
|
||||
*/
|
||||
private async getAccountInformation(
|
||||
context: Context,
|
||||
accountId: number,
|
||||
): Promise<{
|
||||
companyName: string;
|
||||
adminEmails: string[];
|
||||
}> {
|
||||
// アカウントIDから企業名を取得する
|
||||
const { company_name } = await this.accountsRepository.findAccountById(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
|
||||
// 管理者一覧を取得
|
||||
const admins = await this.usersRepository.findAdminUsers(
|
||||
context,
|
||||
accountId,
|
||||
);
|
||||
const adminExternalIDs = admins.map((x) => x.external_id);
|
||||
|
||||
// ADB2Cから管理者IDを元にメールアドレスを取得する
|
||||
const usersInfo = await this.adB2cService.getUsers(
|
||||
context,
|
||||
adminExternalIDs,
|
||||
);
|
||||
|
||||
// 生のAzure AD B2Cのユーザー情報からメールアドレスを抽出する
|
||||
const adminEmails = usersInfo.map((x) => {
|
||||
const { emailAddress } = getUserNameAndMailAddress(x);
|
||||
if (emailAddress == null) {
|
||||
throw new Error('dealer admin email-address is not found');
|
||||
}
|
||||
return emailAddress;
|
||||
});
|
||||
|
||||
return {
|
||||
companyName: company_name,
|
||||
adminEmails: adminEmails,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,6 +12,8 @@ import {
|
||||
LICENSE_QUANTITY,
|
||||
PO_NUMBER,
|
||||
TOP_URL,
|
||||
USER_EMAIL,
|
||||
USER_NAME,
|
||||
} from '../../templates/constants';
|
||||
|
||||
@Injectable()
|
||||
@ -30,6 +32,8 @@ export class SendGridService {
|
||||
private readonly templateU106Text: string;
|
||||
private readonly templateU107Html: string;
|
||||
private readonly templateU107Text: string;
|
||||
private readonly templateU108Html: string;
|
||||
private readonly templateU108Text: string;
|
||||
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
this.appDomain = this.configService.getOrThrow<string>('APP_DOMAIN');
|
||||
@ -86,6 +90,15 @@ export class SendGridService {
|
||||
path.resolve(__dirname, `../../templates/template_U_107.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
this.templateU108Html = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_108.html`),
|
||||
'utf-8',
|
||||
);
|
||||
this.templateU108Text = readFileSync(
|
||||
path.resolve(__dirname, `../../templates/template_U_108.txt`),
|
||||
'utf-8',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -395,6 +408,61 @@ export class SendGridService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* U-108のテンプレートを使用したメールを送信する
|
||||
* @param context
|
||||
* @param userName ライセンス割り当てされたユーザーの名前
|
||||
* @param userMail ライセンス割り当てされたユーザーのメールアドレス
|
||||
* @param customerAdminMails ライセンス割り当てされたユーザーの所属するアカウントの管理者(primary/secondary)のメールアドレス
|
||||
* @param customerAccountName ライセンス割り当てされたユーザーの所属するアカウントの名前
|
||||
* @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名)
|
||||
* @returns mail with u108
|
||||
*/
|
||||
async sendMailWithU108(
|
||||
context: Context,
|
||||
userName: string,
|
||||
userMail: string,
|
||||
customerAdminMails: string[],
|
||||
customerAccountName: string,
|
||||
dealerAccountName: string,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU108.name}`,
|
||||
);
|
||||
try {
|
||||
const subject = 'License Assigned Notification [U-108]';
|
||||
|
||||
// メールの本文を作成する
|
||||
const html = this.templateU108Html
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(USER_NAME, userName)
|
||||
.replaceAll(USER_EMAIL, userMail)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
const text = this.templateU108Text
|
||||
.replaceAll(CUSTOMER_NAME, customerAccountName)
|
||||
.replaceAll(DEALER_NAME, dealerAccountName)
|
||||
.replaceAll(USER_NAME, userName)
|
||||
.replaceAll(USER_EMAIL, userMail)
|
||||
.replaceAll(TOP_URL, this.appDomain);
|
||||
|
||||
// メールを送信する
|
||||
this.sendMail(
|
||||
context,
|
||||
[userMail],
|
||||
customerAdminMails,
|
||||
this.mailFrom,
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU108.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* メールを送信する
|
||||
* @param context
|
||||
|
||||
@ -3,3 +3,5 @@ export const DEALER_NAME = '$DEALER_NAME$';
|
||||
export const LICENSE_QUANTITY = '$LICENSE_QUANTITY$';
|
||||
export const PO_NUMBER = '$PO_NUMBER$';
|
||||
export const TOP_URL = '$TOP_URL$';
|
||||
export const USER_NAME = '$USER_NAME$';
|
||||
export const USER_EMAIL = '$USER_EMAIL$';
|
||||
|
||||
80
dictation_server/src/templates/template_U_108.html
Normal file
80
dictation_server/src/templates/template_U_108.html
Normal file
@ -0,0 +1,80 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>License Assigned Notification [U-108]</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h3><English></h3>
|
||||
<p>Dear $CUSTOMER_NAME$,</p>
|
||||
<p>
|
||||
Please be informed that a license has been assigned to the following
|
||||
user.<br />
|
||||
- User Name: $USER_NAME$<br />
|
||||
- Email: $USER_EMAIL$
|
||||
</p>
|
||||
<p>
|
||||
Please log in to ODMS Cloud to verify the license expiration date.<br />
|
||||
URL: $TOP_URL$
|
||||
</p>
|
||||
<p>
|
||||
If you need support regarding ODMS Cloud, please contact $DEALER_NAME$.
|
||||
</p>
|
||||
<p>
|
||||
If you have received this e-mail in error, please delete this e-mail
|
||||
from your system.<br />
|
||||
This is an automatically generated e-mail and this mailbox is not
|
||||
monitored. Please do not reply.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3><Deutsch></h3>
|
||||
<p>Sehr geehrte(r) $CUSTOMER_NAME$,</p>
|
||||
<p>
|
||||
Bitte beachten Sie, dass dem folgenden Benutzer eine Lizenz zugewiesen
|
||||
wurde.<br />
|
||||
- Nutzername: $USER_NAME$<br />
|
||||
- Email: $USER_EMAIL$
|
||||
</p>
|
||||
<p>
|
||||
Bitte melden Sie sich bei ODMS Cloud an, um das Ablaufdatum der Lizenz
|
||||
zu überprüfen.<br />
|
||||
URL: $TOP_URL$
|
||||
</p>
|
||||
<p>
|
||||
Wenn Sie Unterstützung bezüglich ODMS Cloud benötigen, wenden Sie sich
|
||||
bitte an $DEALER_NAME$.
|
||||
</p>
|
||||
<p>
|
||||
Wenn Sie diese E-Mail fälschlicherweise erhalten haben, löschen Sie
|
||||
diese E-Mail bitte aus Ihrem System.<br />
|
||||
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird
|
||||
nicht überwacht. Bitte nicht antworten.
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<h3><Français></h3>
|
||||
<p>Chère/Cher $CUSTOMER_NAME$,</p>
|
||||
<p>
|
||||
Veuillez être informé qu'une licence a été attribuée à l'utilisateur
|
||||
suivant.<br />
|
||||
- Nom d'utilisateur: $USER_NAME$<br />
|
||||
- Email: $USER_EMAIL$
|
||||
</p>
|
||||
<p>
|
||||
Veuillez vous connecter à ODMS Cloud pour vérifier la date d'expiration
|
||||
de la licence.<br />
|
||||
URL: $TOP_URL$
|
||||
</p>
|
||||
<p>
|
||||
Si vous avez besoin d'assistance concernant ODMS Cloud, veuillez
|
||||
contacter $DEALER_NAME$.
|
||||
</p>
|
||||
<p>
|
||||
Si vous avez reçu cet e-mail par erreur, veuillez supprimer cet e-mail
|
||||
de votre système.<br />
|
||||
Il s'agit d'un e-mail généré automatiquement et cette boîte aux lettres
|
||||
n'est pas surveillée. Merci de ne pas répondre.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
47
dictation_server/src/templates/template_U_108.txt
Normal file
47
dictation_server/src/templates/template_U_108.txt
Normal file
@ -0,0 +1,47 @@
|
||||
<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 fälschlicherweise erhalten haben, löschen Sie diese E-Mail bitte aus Ihrem System.
|
||||
Dies ist eine automatisch generierte E-Mail und dieses Postfach wird nicht überwacht. Bitte nicht antworten.
|
||||
|
||||
<Français>
|
||||
|
||||
Chère/Cher $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.
|
||||
Loading…
x
Reference in New Issue
Block a user