From ffd6eb4e684f2ddc9653635d720f78bf24143070 Mon Sep 17 00:00:00 2001 From: "makabe.t" Date: Thu, 9 May 2024 01:20:57 +0000 Subject: [PATCH 1/9] Merged PR 886: GET /tasks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task3980: GET /tasks](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3980) - `GET /tasks` のバリデータのUTを追加しました。 ## レビューポイント - テスト項目は適切でしょうか? ## UIの変更 - なし ## クエリの変更 - なし ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - 具体的にどのような確認をしたか - テストの修正のみなので影響なし --- .../features/tasks/tasks.controller.spec.ts | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/dictation_server/src/features/tasks/tasks.controller.spec.ts b/dictation_server/src/features/tasks/tasks.controller.spec.ts index 72b8ac5..c753ab4 100644 --- a/dictation_server/src/features/tasks/tasks.controller.spec.ts +++ b/dictation_server/src/features/tasks/tasks.controller.spec.ts @@ -2,6 +2,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { TasksController } from './tasks.controller'; import { TasksService } from './tasks.service'; import { ConfigModule } from '@nestjs/config'; +import { TasksRequest } from './types/types'; +import { validate } from 'class-validator'; +import { plainToClass } from 'class-transformer'; describe('TasksController', () => { let controller: TasksController; @@ -27,4 +30,89 @@ describe('TasksController', () => { it('should be defined', () => { expect(controller).toBeDefined(); }); + + describe('valdation getTasks', () => { + it('最低限の有効なリクエストが成功する', async () => { + const request = new TasksRequest(); + + const valdationObject = plainToClass(TasksRequest, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(0); + }); + it('設定可能な全てのパラメータが指定されている場合、リクエストが成功する', async () => { + const request = new TasksRequest(); + request.limit = 1; + request.offset = 1; + request.status = 'Uploaded'; + request.direction = 'ASC'; + request.paramName = 'JOB_NUMBER'; + + const valdationObject = plainToClass(TasksRequest, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(0); + }); + it('limitが0より小さい場合、リクエストが失敗する', async () => { + const request = new TasksRequest(); + request.limit = -1; + + const valdationObject = plainToClass(TasksRequest, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('limitが文字列の場合、リクエストが失敗する', async () => { + const request = { limit: 'test' }; + + const valdationObject = plainToClass(TasksRequest, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('offsetが0より小さい場合、リクエストが失敗する', async () => { + const request = new TasksRequest(); + request.offset = -1; + + const valdationObject = plainToClass(TasksRequest, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('offsetが文字列の場合、リクエストが失敗する', async () => { + const request = { offset: 'test' }; + + const valdationObject = plainToClass(TasksRequest, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('statusがタスクのステータス文字列以外の場合、リクエストが失敗する', async () => { + const request = new TasksRequest(); + request.status = 'test'; + + const valdationObject = plainToClass(TasksRequest, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('directionがASC,DESC以外の場合、リクエストが失敗する', async () => { + const request = new TasksRequest(); + request.direction = 'test'; + + const valdationObject = plainToClass(TasksRequest, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('paramNameがTASK_LIST_SORTABLE_ATTRIBUTES以外の場合、リクエストが失敗する', async () => { + const request = new TasksRequest(); + request.paramName = 'test'; + + const valdationObject = plainToClass(TasksRequest, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + }); }); From 68df7cd7281849f4247dbb3e9aaefe49b80ec9e4 Mon Sep 17 00:00:00 2001 From: "makabe.t" Date: Thu, 9 May 2024 05:28:03 +0000 Subject: [PATCH 2/9] Merged PR 887: POST /auth/token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task3981: POST /auth/token](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3981) - 元PBI or タスクへのリンク(内容・目的などはそちらにあるはず) - `POST /auth/token` のバリデータのUTを追加しました。 ## レビューポイント - テスト項目は適切でしょうか? ## UIの変更 - なし ## クエリの変更 - なし ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - 具体的にどのような確認をしたか - テストの修正のみなので影響なし --- .../src/features/auth/auth.controller.spec.ts | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/dictation_server/src/features/auth/auth.controller.spec.ts b/dictation_server/src/features/auth/auth.controller.spec.ts index a762b74..aa4aeae 100644 --- a/dictation_server/src/features/auth/auth.controller.spec.ts +++ b/dictation_server/src/features/auth/auth.controller.spec.ts @@ -6,6 +6,9 @@ import { makeDefaultAdB2cMockValue, } from './test/auth.service.mock'; import { ConfigModule } from '@nestjs/config'; +import { TokenRequest } from './types/types'; +import { plainToClass } from 'class-transformer'; +import { validate } from 'class-validator'; describe('AuthController', () => { let controller: AuthController; @@ -30,4 +33,57 @@ describe('AuthController', () => { it('should be defined', () => { expect(controller).toBeDefined(); }); + + describe('valdation token', () => { + it('最低限の有効なリクエストが成功する', async () => { + const request = new TokenRequest(); + request.idToken = 'test'; + request.type = 'web'; + + const valdationObject = plainToClass(TokenRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(0); + }); + it('idTokenが指定されていない場合、リクエストが失敗する', async () => { + const request = new TokenRequest(); + request.type = 'web'; + + const valdationObject = plainToClass(TokenRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('idTokenが空文字の場合、リクエストが失敗する', async () => { + const request = new TokenRequest(); + request.idToken = ''; + request.type = 'web'; + + const valdationObject = plainToClass(TokenRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('idTokenが文字列でない場合、リクエストが失敗する', async () => { + const request = { idToken: 1, type: 'web' }; + + const valdationObject = plainToClass(TokenRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('typeが指定されていない場合、リクエストが失敗する', async () => { + const request = new TokenRequest(); + request.idToken = 'test'; + + const valdationObject = plainToClass(TokenRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('typeがweb,mobile,desktop以外の場合、リクエストが失敗する', async () => { + const request = new TokenRequest(); + request.idToken = 'test'; + request.type = 'invalid'; + + const valdationObject = plainToClass(TokenRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + }); }); From 228e21ba783d16c36729682167f9ccbdd34fed9a Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Fri, 10 May 2024 03:56:48 +0000 Subject: [PATCH 3/9] =?UTF-8?q?Merged=20PR=20892:=20migration=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task4035: migration修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4035) - job_numberテーブル作成 - マイグレーションのコマンド修正 ## レビューポイント - インデックス・ユニーク制約・外部キー制約の認識は合っているか - マイグレーションのコマンドは基本的にccbで認識あっているか ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - マイグレーションファイル作成のみでほかに影響なし ## 補足 - 相談、参考資料などがあれば --- .../db/migrations/065-create_job_number.sql | 13 +++++++++++++ dictation_server/package.json | 6 +++--- 2 files changed, 16 insertions(+), 3 deletions(-) create mode 100644 dictation_server/db/migrations/065-create_job_number.sql diff --git a/dictation_server/db/migrations/065-create_job_number.sql b/dictation_server/db/migrations/065-create_job_number.sql new file mode 100644 index 0000000..3ed6b7d --- /dev/null +++ b/dictation_server/db/migrations/065-create_job_number.sql @@ -0,0 +1,13 @@ +-- +migrate Up +CREATE TABLE IF NOT EXISTS `job_number` ( + `id` BIGINT UNSIGNED AUTO_INCREMENT NOT NULL PRIMARY KEY COMMENT 'ID', + `account_id` BIGINT UNSIGNED NOT NULL COMMENT 'アカウントID', + `job_number` VARCHAR(10) NOT NULL COMMENT 'JOBナンバー', + `updated_at` TIMESTAMP DEFAULT now() COMMENT '更新時刻', + CONSTRAINT `unique_account_id` UNIQUE (`account_id`), + CONSTRAINT `fk_account_id` FOREIGN KEY (`account_id`) REFERENCES `accounts`(`id`), + INDEX `idx_account_id` (`account_id`) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci; + +-- +migrate Down +DROP TABLE `job_number`; \ No newline at end of file diff --git a/dictation_server/package.json b/dictation_server/package.json index 7ca9f5b..a8d50bd 100644 --- a/dictation_server/package.json +++ b/dictation_server/package.json @@ -24,9 +24,9 @@ "test:e2e": "jest --config ./test/jest-e2e.json", "og": "openapi-generator-cli", "openapi-format": "cat \"src/api/odms/openapi.json\" | jq -c . > \"src/api/odms/openapi.json\" && prettier --write \"src/api/odms/*.json\"", - "migrate:up": "sql-migrate up -config=/app/dictation_server/db/dbconfig.yml -env=local", - "migrate:down": "sql-migrate down -config=/app/dictation_server/db/dbconfig.yml -env=local", - "migrate:status": "sql-migrate status -config=/app/dictation_server/db/dbconfig.yml -env=local", + "migrate:up": "sql-migrate up -config=/app/dictation_server/db/dbconfig.yml -env=ccb", + "migrate:down": "sql-migrate down -config=/app/dictation_server/db/dbconfig.yml -env=ccb", + "migrate:status": "sql-migrate status -config=/app/dictation_server/db/dbconfig.yml -env=ccb", "migrate:up:test": "sql-migrate up -config=/app/dictation_server/db/dbconfig.yml -env=test" }, "dependencies": { From 35e2d626a0ff365f8dfff3cb96790635919bff80 Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Mon, 13 May 2024 05:04:16 +0000 Subject: [PATCH 4/9] =?UTF-8?q?Merged=20PR=20893:=20API=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=EF=BC=88upload-finished=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task4033: API修正(upload-finished)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4033) - JobNumberテーブルから取得したJOBNUMBERを使用してタスクを作成する。 - テスト追加 ## レビューポイント - テストケースは足りているか - JOBNUMBERの採番ロジックに誤りはないか ## クエリの変更 - Repositoryを変更し、クエリが変更された場合は変更内容を確認する - JobNumberテーブルからの取得と更新クエリを追加した - 既存のクエリを変更はしていない ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - 具体的にどのような確認をしたか - テストケースを修正し、既存テストがすべて通ることを確認 ## 補足 - 相談、参考資料などがあれば --- dictation_server/src/common/test/utility.ts | 25 + .../accounts/accounts.service.spec.ts | 2 +- .../src/features/files/files.service.spec.ts | 506 +++++++++++++++++- .../src/features/files/test/utility.ts | 13 + .../src/features/tasks/test/utility.ts | 9 - .../templates/templates.service.spec.ts | 4 +- .../job_number/entity/job_number.entity.ts | 25 + .../job_number.repository.module.ts | 11 + .../job_number.repository.service.ts | 7 + .../tasks/tasks.repository.service.ts | 34 +- 10 files changed, 611 insertions(+), 25 deletions(-) create mode 100644 dictation_server/src/repositories/job_number/entity/job_number.entity.ts create mode 100644 dictation_server/src/repositories/job_number/job_number.repository.module.ts create mode 100644 dictation_server/src/repositories/job_number/job_number.repository.service.ts diff --git a/dictation_server/src/common/test/utility.ts b/dictation_server/src/common/test/utility.ts index 28c8b0c..6046a4f 100644 --- a/dictation_server/src/common/test/utility.ts +++ b/dictation_server/src/common/test/utility.ts @@ -9,6 +9,8 @@ import { } from '../../constants'; import { License } from '../../repositories/licenses/entity/license.entity'; import { AccountArchive } from '../../repositories/accounts/entity/account_archive.entity'; +import { Task } from '../../repositories/tasks/entity/task.entity'; +import { JobNumber } from '../../repositories/job_number/entity/job_number.entity'; type InitialTestDBState = { tier1Accounts: { account: Account; users: User[] }[]; @@ -421,3 +423,26 @@ export const getLicenses = async ( }); return licenses; }; + +export const getTasks = async ( + datasource: DataSource, + account_id: number, +): Promise => { + const tasks = await datasource.getRepository(Task).find({ + where: { account_id: account_id }, + }); + return tasks; +}; + +// job_numberを取得する +export const getJobNumber = async ( + datasource: DataSource, + account_id: number, +): Promise => { + const jobNumber = await datasource.getRepository(JobNumber).findOne({ + where: { + account_id: account_id, + }, + }); + return jobNumber; +}; diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index 60ca795..99ed015 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -41,6 +41,7 @@ import { getLicenses, getUserArchive, getAccountArchive, + getTasks, } from '../../common/test/utility'; import { AccountsService } from './accounts.service'; import { Context, makeContext } from '../../common/log'; @@ -94,7 +95,6 @@ import { truncateAllTable } from '../../common/test/init'; import { createTask, getCheckoutPermissions, - getTasks, } from '../tasks/test/utility'; import { createCheckoutPermissions } from '../tasks/test/utility'; import { TestLogger } from '../../common/test/logger'; diff --git a/dictation_server/src/features/files/files.service.spec.ts b/dictation_server/src/features/files/files.service.spec.ts index 0539eed..01d2b1e 100644 --- a/dictation_server/src/features/files/files.service.spec.ts +++ b/dictation_server/src/features/files/files.service.spec.ts @@ -3,6 +3,7 @@ import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { makeBlobstorageServiceMockValue } from './test/files.service.mock'; import { DataSource } from 'typeorm'; import { + createJobNumber, createLicense, createTask, createUserGroupAndMember, @@ -13,6 +14,8 @@ import { import { FilesService } from './files.service'; import { makeContext } from '../../common/log'; import { + getJobNumber, + getTasks, makeHierarchicalAccounts, makeTestAccount, makeTestSimpleAccount, @@ -50,6 +53,7 @@ import { } from '../../constants'; import { truncateAllTable } from '../../common/test/init'; import { TestLogger } from '../../common/test/logger'; +import { TasksService } from '../tasks/tasks.service'; describe('publishUploadSas', () => { let source: DataSource | null = null; @@ -322,6 +326,10 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); // ワークフロータイピストを作成 await createWorkflowTypist(source, workflowId, typistUserId); + + // 初期値のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000000'); + const blobParam = makeBlobstorageServiceMockValue(); const notificationParam = makeDefaultNotificationhubServiceMockValue(); @@ -335,6 +343,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { const notificationHubService = module.get( NotificationhubService, ); + const result = await service.uploadFinished( makeContext('trackingId', 'requestId'), authorExternalId, @@ -404,7 +413,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { accountId, 'http://blob/url/file.zip', 'file.zip', - '01', + 'Uploaded', typistUserId, authorAuthorId ?? '', ); @@ -423,6 +432,10 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); // ワークフロータイピストを作成 await createWorkflowTypist(source, workflowId, typistUserId); + + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000001'); + const blobParam = makeBlobstorageServiceMockValue(); const notificationParam = makeDefaultNotificationhubServiceMockValue(); @@ -546,6 +559,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { userGroupId, // ルーティング先のユーザーグループIDを設定 ); + // 初期値のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000000'); + const blobParam = makeBlobstorageServiceMockValue(); const notificationParam = makeDefaultNotificationhubServiceMockValue(); @@ -668,6 +684,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { userGroupId, // ルーティング先のユーザーグループIDを設定 ); + // 初期値のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000000'); + const blobParam = makeBlobstorageServiceMockValue(); const notificationParam = makeDefaultNotificationhubServiceMockValue(); @@ -736,6 +755,10 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { role: 'author', author_id: 'AUTHOR_ID', }); + + // 初期値のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000000'); + const blobParam = makeBlobstorageServiceMockValue(); const notificationParam = makeDefaultNotificationhubServiceMockValue(); @@ -835,6 +858,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000003'); + const service = module.get(FilesService); const spy = jest .spyOn(service['sendGridService'], 'sendMail') @@ -918,6 +944,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000003'); + const service = module.get(FilesService); // メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。 const spy = jest @@ -1004,6 +1033,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000004'); + const service = module.get(FilesService); // メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。 const spy = jest @@ -1095,6 +1127,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000004'); + const service = module.get(FilesService); // メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。 const spy = jest @@ -1361,7 +1396,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { accountId, `http://blob/url/file_${i}.zip`, `file_${i}.zip`, - '01', + 'Uploaded', typistUserId, authorAuthorId ?? '', undefined, @@ -1381,6 +1416,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { false, ); + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000010'); + const module = await makeTestingModuleWithBlobAndNotification( source, blobParam, @@ -1487,7 +1525,7 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { accountId, `http://blob/url/file_${i}.zip`, `file_${i}.zip`, - '01', + 'Uploaded', typistUserId, authorAuthorId ?? '', undefined, @@ -1496,6 +1534,9 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000010'); + const module = await makeTestingModuleWithBlobAndNotification( source, blobParam, @@ -1621,6 +1662,8 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { 'Backup', false, ); + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000010'); const module = await makeTestingModuleWithBlobAndNotification( source, @@ -1747,6 +1790,8 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { 'Backup', false, ); + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '99999999'); const module = await makeTestingModuleWithBlobAndNotification( source, @@ -1844,6 +1889,10 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); // ワークフロータイピストを作成 await createWorkflowTypist(source, workflowId, typistUserId); + + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '99999999'); + const blobParam = makeBlobstorageServiceMockValue(); const notificationParam = makeDefaultNotificationhubServiceMockValue(); @@ -1915,6 +1964,457 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { expect(resultCheckoutPermission.length).toEqual(1); expect(resultCheckoutPermission[0].user_id).toEqual(typistUserId); }); + + it('タスク作成時に採番されるジョブナンバーが常に最新タスクのジョブナンバー + 1となる(最新タスクが削除されている場合)', async () => { + if (!source) fail(); + const { id: accountId } = await makeTestSimpleAccount(source); + const { + external_id: authorExternalId, + id: authorUserId, + author_id: authorAuthorId, + } = await makeTestUser(source, { + account_id: accountId, + external_id: 'author-user-external-id', + role: 'author', + author_id: 'AUTHOR_ID', + }); + const { id: typistUserId } = await makeTestUser(source, { + account_id: accountId, + external_id: 'typist-user-external-id', + role: 'typist', + author_id: undefined, + }); + // ワークタイプを作成 + const { id: worktypeId, custom_worktype_id } = await createWorktype( + source, + accountId, + 'worktypeId', + ); + // テンプレートファイルを作成 + const { id: templateFileId } = await createTemplateFile( + source, + accountId, + 'templateFile', + 'http://blob/url/templateFile.zip', + ); + // ワークフローを作成 + const { id: workflowId } = await createWorkflow( + source, + accountId, + authorUserId, + worktypeId, + templateFileId, + ); + // ワークフロータイピストを作成 + await createWorkflowTypist(source, workflowId, typistUserId); + const blobParam = makeBlobstorageServiceMockValue(); + const notificationParam = makeDefaultNotificationhubServiceMockValue(); + + // タスクを10件作成, ジョブナンバーは00000001から00000010まで + for (let i = 1; i <= 10; i++) { + await createTask( + source, + accountId, + `http://blob/url/file_${i}.zip`, + `file_${i}.zip`, + 'Uploaded', + typistUserId, + authorAuthorId ?? '', + undefined, + undefined, + i.toString().padStart(8, '0'), + ); + } + + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000010'); + + { + // 初期データ確認 + const tasks = await getTasks(source, accountId); + expect(tasks.length).toEqual(10); + const jobNumber = await getJobNumber(source, accountId); + expect(jobNumber?.job_number).toEqual('00000010'); + } + + const module = await makeTestingModuleWithBlobAndNotification( + source, + blobParam, + notificationParam, + ); + if (!module) fail(); + const service = module.get(FilesService); + const taskService = module.get(TasksService); + const notificationHubService = module.get( + NotificationhubService, + ); + + // 最新のジョブナンバーのタスクを取得 + const latestTask = await getTaskFromJobNumber(source, '00000010'); + await taskService.deleteTask( + makeContext('trackingId', 'requestId'), + authorExternalId, + latestTask?.audio_file_id ?? 0, // 最新タスクのaudioFileId + ); + + { + // タスク削除確認 + const tasks = await getTasks(source, accountId); + expect(tasks.length).toEqual(9); + // JobNumberが00000010のタスクが存在しないことを確認 + expect( + tasks.find((task) => task.job_number === '00000010'), + ).toBeUndefined(); + } + + const result = await service.uploadFinished( + makeContext('trackingId', 'requestId'), + authorExternalId, + 'http://blob/url/file.zip', + authorAuthorId ?? '', + 'file.zip', + '11:22:33', + '2023-05-26T11:22:33.444', + '2023-05-26T11:22:33.444', + '2023-05-26T11:22:33.444', + 256, + '01', + 'DS2', + 'comment', + custom_worktype_id, + optionItemList, + false, + ); + expect(result.jobNumber).toEqual('00000011'); + // job_numberテーブルが正しく更新されているか確認 + const jobNumber = await getJobNumber(source, accountId); + expect(jobNumber?.job_number).toEqual('00000011'); + + // 通知処理が想定通りの引数で呼ばれているか確認 + expect(notificationHubService.notify).toHaveBeenCalledWith( + makeContext('trackingId', 'requestId'), + [`user_${typistUserId}`], + { + authorId: 'AUTHOR_ID', + filename: 'file', + priority: 'High', + uploadedAt: '2023-05-26T11:22:33.444', + }, + ); + // 作成したタスクを取得 + const resultTask = await getTaskFromJobNumber(source, result.jobNumber); + // タスクのチェックアウト権限を取得 + const resultCheckoutPermission = await getCheckoutPermissions( + source, + resultTask?.id ?? 0, + ); + // タスクのテンプレートファイルIDを確認 + expect(resultTask?.template_file_id).toEqual(templateFileId); + // タスクのチェックアウト権限が想定通り(ワークフローで設定されている)のユーザーIDで作成されているか確認 + expect(resultCheckoutPermission.length).toEqual(1); + expect(resultCheckoutPermission[0].user_id).toEqual(typistUserId); + }); + it('タスク作成時に採番されるジョブナンバーが常に最新タスクのジョブナンバー + 1となる(途中のタスクが削除されている場合)', async () => { + if (!source) fail(); + const { id: accountId } = await makeTestSimpleAccount(source); + const { + external_id: authorExternalId, + id: authorUserId, + author_id: authorAuthorId, + } = await makeTestUser(source, { + account_id: accountId, + external_id: 'author-user-external-id', + role: 'author', + author_id: 'AUTHOR_ID', + }); + const { id: typistUserId } = await makeTestUser(source, { + account_id: accountId, + external_id: 'typist-user-external-id', + role: 'typist', + author_id: undefined, + }); + // ワークタイプを作成 + const { id: worktypeId, custom_worktype_id } = await createWorktype( + source, + accountId, + 'worktypeId', + ); + // テンプレートファイルを作成 + const { id: templateFileId } = await createTemplateFile( + source, + accountId, + 'templateFile', + 'http://blob/url/templateFile.zip', + ); + // ワークフローを作成 + const { id: workflowId } = await createWorkflow( + source, + accountId, + authorUserId, + worktypeId, + templateFileId, + ); + // ワークフロータイピストを作成 + await createWorkflowTypist(source, workflowId, typistUserId); + const blobParam = makeBlobstorageServiceMockValue(); + const notificationParam = makeDefaultNotificationhubServiceMockValue(); + + // タスクを10件作成, ジョブナンバーは00000001から00000010まで + for (let i = 1; i <= 10; i++) { + await createTask( + source, + accountId, + `http://blob/url/file_${i}.zip`, + `file_${i}.zip`, + 'Uploaded', + typistUserId, + authorAuthorId ?? '', + undefined, + undefined, + i.toString().padStart(8, '0'), + ); + } + + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '00000010'); + + { + // 初期データ確認 + const tasks = await getTasks(source, accountId); + expect(tasks.length).toEqual(10); + const jobNumber = await getJobNumber(source, accountId); + expect(jobNumber?.job_number).toEqual('00000010'); + } + + const module = await makeTestingModuleWithBlobAndNotification( + source, + blobParam, + notificationParam, + ); + if (!module) fail(); + const service = module.get(FilesService); + const taskService = module.get(TasksService); + const notificationHubService = module.get( + NotificationhubService, + ); + + // 途中のジョブナンバーのタスクを取得 + const latestTask = await getTaskFromJobNumber(source, '00000005'); + await taskService.deleteTask( + makeContext('trackingId', 'requestId'), + authorExternalId, + latestTask?.audio_file_id ?? 0, // 最新タスクのaudioFileId + ); + + { + // タスク削除確認 + const tasks = await getTasks(source, accountId); + expect(tasks.length).toEqual(9); + // JobNumberが00000005のタスクが存在しないことを確認 + expect( + tasks.find((task) => task.job_number === '00000005'), + ).toBeUndefined(); + } + + const result = await service.uploadFinished( + makeContext('trackingId', 'requestId'), + authorExternalId, + 'http://blob/url/file.zip', + authorAuthorId ?? '', + 'file.zip', + '11:22:33', + '2023-05-26T11:22:33.444', + '2023-05-26T11:22:33.444', + '2023-05-26T11:22:33.444', + 256, + '01', + 'DS2', + 'comment', + custom_worktype_id, + optionItemList, + false, + ); + expect(result.jobNumber).toEqual('00000011'); + // job_numberテーブルが正しく更新されているか確認 + const jobNumber = await getJobNumber(source, accountId); + expect(jobNumber?.job_number).toEqual('00000011'); + + // 通知処理が想定通りの引数で呼ばれているか確認 + expect(notificationHubService.notify).toHaveBeenCalledWith( + makeContext('trackingId', 'requestId'), + [`user_${typistUserId}`], + { + authorId: 'AUTHOR_ID', + filename: 'file', + priority: 'High', + uploadedAt: '2023-05-26T11:22:33.444', + }, + ); + // 作成したタスクを取得 + const resultTask = await getTaskFromJobNumber(source, result.jobNumber); + // タスクのチェックアウト権限を取得 + const resultCheckoutPermission = await getCheckoutPermissions( + source, + resultTask?.id ?? 0, + ); + // タスクのテンプレートファイルIDを確認 + expect(resultTask?.template_file_id).toEqual(templateFileId); + // タスクのチェックアウト権限が想定通り(ワークフローで設定されている)のユーザーIDで作成されているか確認 + expect(resultCheckoutPermission.length).toEqual(1); + expect(resultCheckoutPermission[0].user_id).toEqual(typistUserId); + }); + it('タスク作成時に採番されるジョブナンバーが常に最新タスクのジョブナンバー + 1となる(最新タスクのジョブナンバーが99999999で削除されている場合)', async () => { + if (!source) fail(); + const { id: accountId } = await makeTestSimpleAccount(source); + const { + external_id: authorExternalId, + id: authorUserId, + author_id: authorAuthorId, + } = await makeTestUser(source, { + account_id: accountId, + external_id: 'author-user-external-id', + role: 'author', + author_id: 'AUTHOR_ID', + }); + const { id: typistUserId } = await makeTestUser(source, { + account_id: accountId, + external_id: 'typist-user-external-id', + role: 'typist', + author_id: undefined, + }); + // ワークタイプを作成 + const { id: worktypeId, custom_worktype_id } = await createWorktype( + source, + accountId, + 'worktypeId', + ); + // テンプレートファイルを作成 + const { id: templateFileId } = await createTemplateFile( + source, + accountId, + 'templateFile', + 'http://blob/url/templateFile.zip', + ); + // ワークフローを作成 + const { id: workflowId } = await createWorkflow( + source, + accountId, + authorUserId, + worktypeId, + templateFileId, + ); + // ワークフロータイピストを作成 + await createWorkflowTypist(source, workflowId, typistUserId); + const blobParam = makeBlobstorageServiceMockValue(); + const notificationParam = makeDefaultNotificationhubServiceMockValue(); + + // タスクを10件作成, ジョブナンバーは99999989から99999999まで + for (let i = 99999989; i <= 99999999; i++) { + await createTask( + source, + accountId, + `http://blob/url/file_${i}.zip`, + `file_${i}.zip`, + 'Uploaded', + typistUserId, + authorAuthorId ?? '', + undefined, + undefined, + i.toString().padStart(8, '0'), + ); + } + + // 最新のジョブナンバーでjob_numberテーブルを作成 + await createJobNumber(source, accountId, '99999999'); + + { + // 初期データ確認 + const tasks = await getTasks(source, accountId); + expect(tasks.length).toEqual(11); + const jobNumber = await getJobNumber(source, accountId); + expect(jobNumber?.job_number).toEqual('99999999'); + } + + + + const module = await makeTestingModuleWithBlobAndNotification( + source, + blobParam, + notificationParam, + ); + if (!module) fail(); + const service = module.get(FilesService); + const taskService = module.get(TasksService); + const notificationHubService = module.get( + NotificationhubService, + ); + + // 最新のジョブナンバーのタスクを取得 + const latestTask = await getTaskFromJobNumber(source, '99999999'); + await taskService.deleteTask( + makeContext('trackingId', 'requestId'), + authorExternalId, + latestTask?.audio_file_id ?? 0, // 最新タスクのaudioFileId + ); + + { + // タスク削除確認 + const tasks = await getTasks(source, accountId); + expect(tasks.length).toEqual(10); + // JobNumberが99999999のタスクが存在しないことを確認 + expect( + tasks.find((task) => task.job_number === '99999999'), + ).toBeUndefined(); + } + + const result = await service.uploadFinished( + makeContext('trackingId', 'requestId'), + authorExternalId, + 'http://blob/url/file.zip', + authorAuthorId ?? '', + 'file.zip', + '11:22:33', + '2023-05-26T11:22:33.444', + '2023-05-26T11:22:33.444', + '2023-05-26T11:22:33.444', + 256, + '01', + 'DS2', + 'comment', + custom_worktype_id, + optionItemList, + false, + ); + expect(result.jobNumber).toEqual('00000001'); + // job_numberテーブルが正しく更新されているか確認 + // 最新タスクのジョブナンバーが99999999の時、新規作成されたタスクのジョブナンバーは00000001になる + const jobNumber = await getJobNumber(source, accountId); + expect(jobNumber?.job_number).toEqual('00000001'); + + // 通知処理が想定通りの引数で呼ばれているか確認 + expect(notificationHubService.notify).toHaveBeenCalledWith( + makeContext('trackingId', 'requestId'), + [`user_${typistUserId}`], + { + authorId: 'AUTHOR_ID', + filename: 'file', + priority: 'High', + uploadedAt: '2023-05-26T11:22:33.444', + }, + ); + // 作成したタスクを取得 + const resultTask = await getTaskFromJobNumber(source, result.jobNumber); + // タスクのチェックアウト権限を取得 + const resultCheckoutPermission = await getCheckoutPermissions( + source, + resultTask?.id ?? 0, + ); + // タスクのテンプレートファイルIDを確認 + expect(resultTask?.template_file_id).toEqual(templateFileId); + // タスクのチェックアウト権限が想定通り(ワークフローで設定されている)のユーザーIDで作成されているか確認 + expect(resultCheckoutPermission.length).toEqual(1); + expect(resultCheckoutPermission[0].user_id).toEqual(typistUserId); + }); }); describe('音声ファイルダウンロードURL取得', () => { diff --git a/dictation_server/src/features/files/test/utility.ts b/dictation_server/src/features/files/test/utility.ts index c001e6a..9239a1f 100644 --- a/dictation_server/src/features/files/test/utility.ts +++ b/dictation_server/src/features/files/test/utility.ts @@ -44,6 +44,7 @@ import { import { UserGroup } from '../../../repositories/user_groups/entity/user_group.entity'; import { UserGroupMember } from '../../../repositories/user_groups/entity/user_group_member.entity'; import { License } from '../../../repositories/licenses/entity/license.entity'; +import { JobNumber } from '../../../repositories/job_number/entity/job_number.entity'; export const createTask = async ( datasource: DataSource, @@ -109,6 +110,18 @@ export const createTask = async ( return { audioFileId: audioFile.id, taskId: task.id }; }; +// job_numberテーブルにレコードを作成する +export const createJobNumber = async ( + datasource: DataSource, + accountId: number, + jobNumber: string, +): Promise => { + await datasource.getRepository(JobNumber).insert({ + account_id: accountId, + job_number: jobNumber, + }); +}; + export const getTaskFromJobNumber = async ( datasource: DataSource, jobNumber: string, diff --git a/dictation_server/src/features/tasks/test/utility.ts b/dictation_server/src/features/tasks/test/utility.ts index 31928e0..676f1ad 100644 --- a/dictation_server/src/features/tasks/test/utility.ts +++ b/dictation_server/src/features/tasks/test/utility.ts @@ -263,15 +263,6 @@ export const getTask = async ( return task; }; -export const getTasks = async ( - datasource: DataSource, - account_id: number, -): Promise => { - const tasks = await datasource.getRepository(Task).find({ - where: { account_id: account_id }, - }); - return tasks; -}; export const getCheckoutPermissions = async ( datasource: DataSource, diff --git a/dictation_server/src/features/templates/templates.service.spec.ts b/dictation_server/src/features/templates/templates.service.spec.ts index 37c3813..442dc87 100644 --- a/dictation_server/src/features/templates/templates.service.spec.ts +++ b/dictation_server/src/features/templates/templates.service.spec.ts @@ -6,7 +6,7 @@ import { getTemplateFiles, updateTaskTemplateFile, } from './test/utility'; -import { makeTestAccount, makeTestUser } from '../../common/test/utility'; +import { getTasks, makeTestAccount, makeTestUser } from '../../common/test/utility'; import { makeContext } from '../../common/log'; import { TemplateFilesRepositoryService } from '../../repositories/template_files/template_files.repository.service'; import { HttpException, HttpStatus } from '@nestjs/common'; @@ -14,7 +14,7 @@ import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { truncateAllTable } from '../../common/test/init'; import { overrideBlobstorageService } from '../../common/test/overrides'; import { TASK_STATUS, USER_ROLES } from '../../constants'; -import { createTask, getTasks } from '../tasks/test/utility'; +import { createTask } from '../tasks/test/utility'; import { createWorkflow, createWorkflowTypist, diff --git a/dictation_server/src/repositories/job_number/entity/job_number.entity.ts b/dictation_server/src/repositories/job_number/entity/job_number.entity.ts new file mode 100644 index 0000000..ec2373e --- /dev/null +++ b/dictation_server/src/repositories/job_number/entity/job_number.entity.ts @@ -0,0 +1,25 @@ +import { + Entity, + Column, + PrimaryGeneratedColumn, + UpdateDateColumn, +} from 'typeorm'; +import { bigintTransformer } from '../../../common/entity'; + +@Entity({ name: 'job_number' }) +export class JobNumber { + @PrimaryGeneratedColumn() + id: number; + + @Column({ type: 'bigint', transformer: bigintTransformer }) + account_id: number; + + @Column() + job_number: string; + + @UpdateDateColumn({ + default: () => "datetime('now', 'localtime')", + type: 'datetime', + }) // defaultはSQLite用設定値.本番用は別途migrationで設定 + updated_at: Date; +} diff --git a/dictation_server/src/repositories/job_number/job_number.repository.module.ts b/dictation_server/src/repositories/job_number/job_number.repository.module.ts new file mode 100644 index 0000000..a39f9bd --- /dev/null +++ b/dictation_server/src/repositories/job_number/job_number.repository.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { JobNumber } from './entity/job_number.entity'; +import { JobNumberRepositoryService } from './job_number.repository.service'; + +@Module({ + imports: [TypeOrmModule.forFeature([JobNumber])], + providers: [JobNumberRepositoryService], + exports: [JobNumberRepositoryService], +}) +export class JobNumberRepositoryModule {} diff --git a/dictation_server/src/repositories/job_number/job_number.repository.service.ts b/dictation_server/src/repositories/job_number/job_number.repository.service.ts new file mode 100644 index 0000000..5790f00 --- /dev/null +++ b/dictation_server/src/repositories/job_number/job_number.repository.service.ts @@ -0,0 +1,7 @@ +import { Injectable } from '@nestjs/common'; +import { DataSource } from 'typeorm'; + +@Injectable() +export class JobNumberRepositoryService { + constructor(private dataSource: DataSource) {} +} diff --git a/dictation_server/src/repositories/tasks/tasks.repository.service.ts b/dictation_server/src/repositories/tasks/tasks.repository.service.ts index d5b2a37..1e30896 100644 --- a/dictation_server/src/repositories/tasks/tasks.repository.service.ts +++ b/dictation_server/src/repositories/tasks/tasks.repository.service.ts @@ -54,6 +54,7 @@ import { } from '../../common/repository'; import { Context } from '../../common/log'; import { UserNotFoundError } from '../users/errors/types'; +import { JobNumber } from '../job_number/entity/job_number.entity'; @Injectable() export class TasksRepositoryService { @@ -905,25 +906,29 @@ export class TasksRepositoryService { const taskRepo = entityManager.getRepository(Task); - // バグ 3954: [4/8リリース]タスクをすべてBackupした後、タスクを作成するとジョブナンバーが1から採番される(暫定対応) - // アカウント内で最新タスクのタスクを取得し、そのJOBナンバーをインクリメントして新しいタスクのJOBナンバーを設定する - const lastTask = await taskRepo.findOne({ + // バグ 3954: [4/8リリース]タスクをすべてBackupした後、タスクを作成するとジョブナンバーが1から採番される(恒久対応) + // job_numberテーブルから最新のJOBナンバーを取得 + const jobNumberRepo = entityManager.getRepository(JobNumber); + const currentJobNumberData = await jobNumberRepo.findOne({ where: { account_id: account_id }, - order: { created_at: 'DESC', job_number: 'DESC' }, comment: `${context.getTrackingId()}_${new Date().toUTCString()}`, lock: { mode: 'pessimistic_write' }, }); + // JOBナンバーが存在しない場合はエラー + // 運用上はあり得ないが、プログラム上発生しうるのでエラーとして処理 + if (!currentJobNumberData) { + throw new Error(`JobNumber not exists. account_id:${account_id}`); + } - let newJobNumber = '00000001'; - if (!lastTask) { - // 初回は00000001 - newJobNumber = '00000001'; - } else if (lastTask.job_number === '99999999') { + let newJobNumber: string = ''; + if (currentJobNumberData.job_number === '99999999') { // 末尾なら00000001に戻る newJobNumber = '00000001'; } else { // 最新のJOBナンバーをインクリメントして次の番号とする - newJobNumber = `${Number(lastTask.job_number) + 1}`.padStart(8, '0'); + newJobNumber = `${ + Number(currentJobNumberData.job_number) + 1 + }`.padStart(8, '0'); } task.job_number = newJobNumber; @@ -935,6 +940,15 @@ export class TasksRepositoryService { context, ); + // JobNumberを更新 + await updateEntity( + jobNumberRepo, + { account_id: account_id }, + { job_number: newJobNumber }, + this.isCommentOut, + context, + ); + const optionItems = paramOptionItems.map((x) => { return { audio_file_id: persisted.audio_file_id, From dfdc6a33ad6eb9f5efdd32ff70931a98505f4225 Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Tue, 14 May 2024 02:12:41 +0000 Subject: [PATCH 5/9] =?UTF-8?q?Merged=20PR=20894:=20API=E4=BF=AE=E6=AD=A3(?= =?UTF-8?q?=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E5=89=8A=E9=99=A4?= =?UTF-8?q?=E7=B3=BB)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task4034: API修正(アカウント削除系)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4034) - アカウント削除時にJobNumberテーブルのレコードも削除するように修正 - パートナー削除時にJobNumberテーブルのレコードも削除するように修正 - テスト修正 ## レビューポイント - テストケースに不足はないか - ジョブナンバーテーブルの削除順に問題はないか ## クエリの変更 - Repositoryを変更し、クエリが変更された場合は変更内容を確認する - JobNumberテーブルのレコードを削除する処理を追加した - 既存のクエリに影響はなし ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - 既存のテストが通ることを確認 - テストしていなかった観点(ソート条件も削除されているか等)も確認するように修正 ## 補足 - 相談、参考資料などがあれば --- dictation_server/src/common/test/modules.ts | 2 + dictation_server/src/common/test/utility.ts | 78 ++++++ .../accounts/accounts.service.spec.ts | 233 +++++++++++++++++- .../src/features/accounts/accounts.service.ts | 6 +- .../src/features/accounts/test/utility.ts | 2 +- .../src/features/auth/test/utility.ts | 2 + .../src/features/files/files.service.spec.ts | 21 +- .../src/features/files/test/utility.ts | 12 - .../src/features/tasks/tasks.service.spec.ts | 7 +- .../src/features/tasks/test/utility.ts | 1 - .../templates/templates.service.spec.ts | 6 +- .../src/features/users/test/utility.ts | 13 - .../accounts/accounts.repository.service.ts | 19 ++ 13 files changed, 346 insertions(+), 56 deletions(-) diff --git a/dictation_server/src/common/test/modules.ts b/dictation_server/src/common/test/modules.ts index 8d50139..149c8de 100644 --- a/dictation_server/src/common/test/modules.ts +++ b/dictation_server/src/common/test/modules.ts @@ -40,6 +40,7 @@ import { TermsModule } from '../../features/terms/terms.module'; import { CacheModule } from '@nestjs/common'; import { RedisModule } from '../../gateways/redis/redis.module'; import { RedisService } from '../../gateways/redis/redis.service'; +import { JobNumberRepositoryModule } from '../../repositories/job_number/job_number.repository.module'; export const makeTestingModule = async ( datasource: DataSource, @@ -79,6 +80,7 @@ export const makeTestingModule = async ( SortCriteriaRepositoryModule, WorktypesRepositoryModule, TermsRepositoryModule, + JobNumberRepositoryModule, RedisModule, CacheModule.register({ isGlobal: true, ttl: 86400 }), ], diff --git a/dictation_server/src/common/test/utility.ts b/dictation_server/src/common/test/utility.ts index 6046a4f..d784663 100644 --- a/dictation_server/src/common/test/utility.ts +++ b/dictation_server/src/common/test/utility.ts @@ -11,6 +11,7 @@ import { License } from '../../repositories/licenses/entity/license.entity'; import { AccountArchive } from '../../repositories/accounts/entity/account_archive.entity'; import { Task } from '../../repositories/tasks/entity/task.entity'; import { JobNumber } from '../../repositories/job_number/entity/job_number.entity'; +import { SortCriteria } from '../../repositories/sort_criteria/entity/sort_criteria.entity'; type InitialTestDBState = { tier1Accounts: { account: Account; users: User[] }[]; @@ -239,6 +240,11 @@ export const makeTestAccount = async ( if (!account || !admin) { throw new Error('Unexpected null'); } + // sort_criteriaテーブルにデータを追加 + await createSortCriteria(datasource, userId, 'JOB_NUMBER', 'ASC'); + + // job_numberテーブルにデータを追加 + await createJobNumber(datasource, accountId, '00000000'); return { account: account, @@ -325,6 +331,8 @@ export const makeTestUser = async ( if (!user) { throw new Error('Unexpected null'); } + // sort_criteriaテーブルにデータを追加 + await createSortCriteria(datasource, user.id, 'FILE_LENGTH', 'ASC'); return user; }; @@ -434,6 +442,33 @@ export const getTasks = async ( return tasks; }; +// job_numberテーブルにレコードを作成する +export const createJobNumber = async ( + datasource: DataSource, + accountId: number, + jobNumber: string, +): Promise => { + await datasource.getRepository(JobNumber).insert({ + account_id: accountId, + job_number: jobNumber, + }); +}; + + +// job_numberテーブルのレコードを更新する +export const updateJobNumber = async ( + datasource: DataSource, + accountId: number, + jobNumber: string, +): Promise => { + await datasource.getRepository(JobNumber).update( + { account_id: accountId }, + { + job_number: jobNumber, + }, + ); +}; + // job_numberを取得する export const getJobNumber = async ( datasource: DataSource, @@ -446,3 +481,46 @@ export const getJobNumber = async ( }); return jobNumber; }; + +// sort_criteriaを作成する +export const createSortCriteria = async ( + datasource: DataSource, + userId: number, + parameter: string, + direction: string, +): Promise => { + await datasource.getRepository(SortCriteria).insert({ + user_id: userId, + parameter: parameter, + direction: direction, + }); +}; + +// 指定したユーザーのsort_criteriaを更新する +export const updateSortCriteria = async ( + datasource: DataSource, + userId: number, + parameter: string, + direction: string, +): Promise => { + await datasource.getRepository(SortCriteria).update( + { user_id: userId }, + { + parameter: parameter, + direction: direction, + }, + ); +}; + +// 指定したユーザーのsort_criteriaを取得する +export const getSortCriteria = async ( + datasource: DataSource, + userId: number, +): Promise => { + const sortCriteria = await datasource.getRepository(SortCriteria).findOne({ + where: { + user_id: userId, + }, + }); + return sortCriteria; +}; diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index 99ed015..dcad6ac 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -21,7 +21,7 @@ import { createWorktype, getLicenseOrders, getOptionItems, - getSortCriteria, + getSortCriteriaList, getTypistGroup, getTypistGroupMember, getTypistGroupMembers, @@ -42,6 +42,8 @@ import { getUserArchive, getAccountArchive, getTasks, + getSortCriteria, + getJobNumber, } from '../../common/test/utility'; import { AccountsService } from './accounts.service'; import { Context, makeContext } from '../../common/log'; @@ -92,10 +94,7 @@ import { } from '../workflows/test/utility'; import { UsersService } from '../users/users.service'; import { truncateAllTable } from '../../common/test/init'; -import { - createTask, - getCheckoutPermissions, -} from '../tasks/test/utility'; +import { createTask, getCheckoutPermissions } from '../tasks/test/utility'; import { createCheckoutPermissions } from '../tasks/test/utility'; import { TestLogger } from '../../common/test/logger'; import { Account } from '../../repositories/accounts/entity/account.entity'; @@ -418,7 +417,7 @@ describe('createAccount', () => { expect(accounts.length).toBe(0); const users = await getUsers(source); expect(users.length).toBe(0); - const sortCriteria = await getSortCriteria(source); + const sortCriteria = await getSortCriteriaList(source); expect(sortCriteria.length).toBe(0); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( @@ -486,7 +485,7 @@ describe('createAccount', () => { expect(accounts.length).toBe(0); const users = await getUsers(source); expect(users.length).toBe(0); - const sortCriteria = await getSortCriteria(source); + const sortCriteria = await getSortCriteriaList(source); expect(sortCriteria.length).toBe(0); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( @@ -557,7 +556,7 @@ describe('createAccount', () => { expect(accounts.length).toBe(0); const users = await getUsers(source); expect(users.length).toBe(0); - const sortCriteria = await getSortCriteria(source); + const sortCriteria = await getSortCriteriaList(source); expect(sortCriteria.length).toBe(0); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( @@ -630,7 +629,7 @@ describe('createAccount', () => { expect(accounts.length).toBe(1); const users = await getUsers(source); expect(users.length).toBe(1); - const sortCriteria = await getSortCriteria(source); + const sortCriteria = await getSortCriteriaList(source); expect(sortCriteria.length).toBe(1); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( @@ -715,7 +714,7 @@ describe('createAccount', () => { expect(accounts.length).toBe(0); const users = await getUsers(source); expect(users.length).toBe(0); - const sortCriteria = await getSortCriteria(source); + const sortCriteria = await getSortCriteriaList(source); expect(sortCriteria.length).toBe(0); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( @@ -803,7 +802,7 @@ describe('createAccount', () => { expect(accounts.length).toBe(1); const users = await getUsers(source); expect(users.length).toBe(1); - const sortCriteria = await getSortCriteria(source); + const sortCriteria = await getSortCriteriaList(source); expect(sortCriteria.length).toBe(1); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith( @@ -7227,6 +7226,27 @@ describe('deleteAccountAndData', () => { ); expect(LicenseAllocationHistoryRecordA.length).toBe(0); + // 第五階層のアカウントAの管理者ユーザーが持つsortCriteriaが削除されていること + const sortCriteriaAccuntAAdmin = await getSortCriteria( + source, + tier5AccountsA.admin.id, + ); + expect(sortCriteriaAccuntAAdmin).toBe(null); + + // 第五階層のアカウントAの一般ユーザーが持つsortCriteriaが削除されていること + const sortCriteriaAccuntAUser = await getSortCriteria( + source, + userA?.id ?? 0, + ); + expect(sortCriteriaAccuntAUser).toBe(null); + + // 第五階層のアカウントAのJobNumberが削除されていること + const jobNumberAccuntA = await getJobNumber( + source, + tier5AccountsA.account.id, + ); + expect(jobNumberAccuntA).toBe(null); + // 第五階層のアカウントBは削除されていないこと const accountRecordB = await getAccount(source, tier5AccountsB.account.id); expect(accountRecordB?.id).not.toBeNull(); @@ -7266,6 +7286,31 @@ describe('deleteAccountAndData', () => { await getLicenseAllocationHistoryArchive(source); expect(LicenseAllocationHistoryArchive.length).toBe(1); + // 第五階層のアカウントBの管理者ユーザーが持つsortCriteriaが削除されていないこと + const sortCriteriaAccuntBAdmin = await getSortCriteria( + source, + tier5AccountsB.admin.id, + ); + expect(sortCriteriaAccuntBAdmin?.user_id).toBe(tier5AccountsB.admin.id); + expect(sortCriteriaAccuntBAdmin?.direction).toBe('ASC'); + expect(sortCriteriaAccuntBAdmin?.parameter).toBe('JOB_NUMBER'); + // 第五階層のアカウントBの一般ユーザーが持つsortCriteriaが削除されていないこと + const sortCriteriaAccuntBUser = await getSortCriteria( + source, + userB?.id ?? 0, + ); + expect(sortCriteriaAccuntBUser?.user_id).toBe(userB?.id ?? 0); + expect(sortCriteriaAccuntBUser?.direction).toBe('ASC'); + expect(sortCriteriaAccuntBUser?.parameter).toBe('FILE_LENGTH'); + + // 第五階層のアカウントBのJobNumberが削除されていないこと + const jobNumberAccuntB = await getJobNumber( + source, + tier5AccountsB.account.id, + ); + expect(jobNumberAccuntB?.account_id).toBe(tier5AccountsB.account.id); + expect(jobNumberAccuntB?.job_number).toBe('00000000'); + expect(_subject).toBe('Account Deleted Notification [U-111]'); expect(_url).toBe('http://localhost:8081/'); }); @@ -8524,6 +8569,21 @@ describe('deletePartnerAccount', () => { tier4Account.id, ); expect(templateFileRecord.length).toBe(1); + const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); + expect(tier4AccountJobNumber?.job_number).toBe('00000000'); + expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); + const tier4AccountAdminSortCriteria = await getSortCriteria( + source, + tier4Admin?.id ?? 0, + ); + expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); + const tier4AccountTypistSortCriteria = await getSortCriteria( + source, + typist.id, + ); + expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); } // パートナーアカウント情報の削除 @@ -8539,6 +8599,25 @@ describe('deletePartnerAccount', () => { expect(account4Record).toBe(null); const userRecordA = await getUser(source, tier4Admin?.id ?? 0); expect(userRecordA).toBe(null); + // パートナーアカウントのユーザーが削除されていること + const userRecord = await getUsers(source); + expect( + userRecord.filter((x) => x.account_id === tier4Account.id).length, + ).toBe(0); + // パートナーアカウントのJobNumberが削除されていること + const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); + expect(tier4AccountJobNumber).toBe(null); + // パートナーアカウントのソート条件が削除されていること + const tier4AccountAdminSortCriteria = await getSortCriteria( + source, + tier4Admin?.id ?? 0, + ); + expect(tier4AccountAdminSortCriteria).toBe(null); + const tier4AccountTypistSortCriteria = await getSortCriteria( + source, + typist.id, + ); + expect(tier4AccountTypistSortCriteria).toBe(null); // パートナーアカウントのライセンスが削除されていること const licenseRecord = await source.manager.find(License, { @@ -8716,6 +8795,21 @@ describe('deletePartnerAccount', () => { tier4Account.id, ); expect(templateFileRecord.length).toBe(1); + const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); + expect(tier4AccountJobNumber?.job_number).toBe('00000000'); + expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); + const tier4AccountAdminSortCriteria = await getSortCriteria( + source, + tier4Admin?.id ?? 0, + ); + expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); + const tier4AccountTypistSortCriteria = await getSortCriteria( + source, + typist.id, + ); + expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); } try { @@ -8865,6 +8959,21 @@ describe('deletePartnerAccount', () => { tier4Account.id, ); expect(templateFileRecord.length).toBe(1); + const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); + expect(tier4AccountJobNumber?.job_number).toBe('00000000'); + expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); + const tier4AccountAdminSortCriteria = await getSortCriteria( + source, + tier4Admin?.id ?? 0, + ); + expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); + const tier4AccountTypistSortCriteria = await getSortCriteria( + source, + typist.id, + ); + expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); } try { @@ -9028,6 +9137,21 @@ describe('deletePartnerAccount', () => { tier4Account.id, ); expect(templateFileRecord.length).toBe(1); + const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); + expect(tier4AccountJobNumber?.job_number).toBe('00000000'); + expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); + const tier4AccountAdminSortCriteria = await getSortCriteria( + source, + tier4Admin?.id ?? 0, + ); + expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); + const tier4AccountTypistSortCriteria = await getSortCriteria( + source, + typist.id, + ); + expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); } // パートナーアカウント情報の削除 @@ -9047,6 +9171,25 @@ describe('deletePartnerAccount', () => { expect(account4Record).toBe(null); const userRecordA = await getUser(source, tier4Admin?.id ?? 0); expect(userRecordA).toBe(null); + // パートナーアカウントのユーザーが削除されていること + const userRecord = await getUsers(source); + expect( + userRecord.filter((x) => x.account_id === tier4Account.id).length, + ).toBe(0); + // パートナーアカウントのJobNumberが削除されていること + const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); + expect(tier4AccountJobNumber).toBe(null); + // パートナーアカウントのソート条件が削除されていること + const tier4AccountAdminSortCriteria = await getSortCriteria( + source, + tier4Admin?.id ?? 0, + ); + expect(tier4AccountAdminSortCriteria).toBe(null); + const tier4AccountTypistSortCriteria = await getSortCriteria( + source, + typist.id, + ); + expect(tier4AccountTypistSortCriteria).toBe(null); // パートナーアカウントのライセンスが削除されていること const licenseRecord = await source.manager.find(License, { @@ -9234,6 +9377,21 @@ describe('deletePartnerAccount', () => { tier4Account.id, ); expect(templateFileRecord.length).toBe(1); + const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); + expect(tier4AccountJobNumber?.job_number).toBe('00000000'); + expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); + const tier4AccountAdminSortCriteria = await getSortCriteria( + source, + tier4Admin?.id ?? 0, + ); + expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); + const tier4AccountTypistSortCriteria = await getSortCriteria( + source, + typist.id, + ); + expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); } // パートナーアカウント情報の削除 @@ -9253,6 +9411,25 @@ describe('deletePartnerAccount', () => { expect(account4Record).toBe(null); const userRecordA = await getUser(source, tier4Admin?.id ?? 0); expect(userRecordA).toBe(null); + // パートナーアカウントのユーザーが削除されていること + const userRecord = await getUsers(source); + expect( + userRecord.filter((x) => x.account_id === tier4Account.id).length, + ).toBe(0); + // パートナーアカウントのJobNumberが削除されていること + const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); + expect(tier4AccountJobNumber).toBe(null); + // パートナーアカウントのソート条件が削除されていること + const tier4AccountAdminSortCriteria = await getSortCriteria( + source, + tier4Admin?.id ?? 0, + ); + expect(tier4AccountAdminSortCriteria).toBe(null); + const tier4AccountTypistSortCriteria = await getSortCriteria( + source, + typist.id, + ); + expect(tier4AccountTypistSortCriteria).toBe(null); // パートナーアカウントのライセンスが削除されていること const licenseRecord = await source.manager.find(License, { @@ -9426,6 +9603,21 @@ describe('deletePartnerAccount', () => { tier4Account.id, ); expect(templateFileRecord.length).toBe(1); + const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); + expect(tier4AccountJobNumber?.job_number).toBe('00000000'); + expect(tier4AccountJobNumber?.account_id).toBe(tier4Account.id); + const tier4AccountAdminSortCriteria = await getSortCriteria( + source, + tier4Admin?.id ?? 0, + ); + expect(tier4AccountAdminSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountAdminSortCriteria?.parameter).toBe('JOB_NUMBER'); + const tier4AccountTypistSortCriteria = await getSortCriteria( + source, + typist.id, + ); + expect(tier4AccountTypistSortCriteria?.direction).toBe('ASC'); + expect(tier4AccountTypistSortCriteria?.parameter).toBe('FILE_LENGTH'); } // パートナーアカウント情報の削除 @@ -9441,6 +9633,25 @@ describe('deletePartnerAccount', () => { expect(account4Record).toBe(null); const userRecordA = await getUser(source, tier4Admin?.id ?? 0); expect(userRecordA).toBe(null); + // パートナーアカウントのユーザーが削除されていること + const userRecord = await getUsers(source); + expect( + userRecord.filter((x) => x.account_id === tier4Account.id).length, + ).toBe(0); + // パートナーアカウントのJobNumberが削除されていること + const tier4AccountJobNumber = await getJobNumber(source, tier4Account.id); + expect(tier4AccountJobNumber).toBe(null); + // パートナーアカウントのソート条件が削除されていること + const tier4AccountAdminSortCriteria = await getSortCriteria( + source, + tier4Admin?.id ?? 0, + ); + expect(tier4AccountAdminSortCriteria).toBe(null); + const tier4AccountTypistSortCriteria = await getSortCriteria( + source, + typist.id, + ); + expect(tier4AccountTypistSortCriteria).toBe(null); // パートナーアカウントのライセンスが削除されていること const licenseRecord = await source.manager.find(License, { diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts index 75b53e9..6547f1c 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -2359,7 +2359,7 @@ export class AccountsService { country = targetAccount.country; } catch (e) { // アカウントの削除に失敗した場合はエラーを返す - this.logger.log(`[${context.getTrackingId()}] ${e}`); + this.logger.log(`[${context.getTrackingId()}] error=${e}`); this.logger.log( `[OUT] [${context.getTrackingId()}] ${this.deleteAccountAndData.name}`, ); @@ -2382,7 +2382,7 @@ export class AccountsService { ); } catch (e) { // ADB2Cユーザーの削除失敗時は、MANUAL_RECOVERY_REQUIREDを出して処理続行 - this.logger.log(`[${context.getTrackingId()}] ${e}`); + this.logger.log(`[${context.getTrackingId()}] error=${e}`); this.logger.log( `${MANUAL_RECOVERY_REQUIRED} [${context.getTrackingId()}] Failed to delete ADB2C users: ${accountId}, users_id: ${dbUsers.map( (x) => x.external_id, @@ -2398,7 +2398,7 @@ export class AccountsService { ); } catch (e) { // blobstorageコンテナを削除で失敗した場合は、MANUAL_RECOVERY_REQUIRED出して正常終了 - this.logger.log(`[${context.getTrackingId()}] ${e}`); + this.logger.log(`[${context.getTrackingId()}] error=${e}`); this.logger.log( `${MANUAL_RECOVERY_REQUIRED}[${context.getTrackingId()}] Failed to delete blob container: ${accountId}, country: ${country}`, ); diff --git a/dictation_server/src/features/accounts/test/utility.ts b/dictation_server/src/features/accounts/test/utility.ts index 046c2d2..473a258 100644 --- a/dictation_server/src/features/accounts/test/utility.ts +++ b/dictation_server/src/features/accounts/test/utility.ts @@ -17,7 +17,7 @@ import { AudioFile } from '../../../repositories/audio_files/entity/audio_file.e * @param dataSource データソース * @returns 該当ソート条件一覧 */ -export const getSortCriteria = async (dataSource: DataSource) => { +export const getSortCriteriaList = async (dataSource: DataSource) => { return await dataSource.getRepository(SortCriteria).find(); }; diff --git a/dictation_server/src/features/auth/test/utility.ts b/dictation_server/src/features/auth/test/utility.ts index fbbc0e4..fc78418 100644 --- a/dictation_server/src/features/auth/test/utility.ts +++ b/dictation_server/src/features/auth/test/utility.ts @@ -2,6 +2,7 @@ import { DataSource } from 'typeorm'; import { Term } from '../../../repositories/terms/entity/term.entity'; import { Account } from '../../../repositories/accounts/entity/account.entity'; import { User } from '../../../repositories/users/entity/user.entity'; +import { JobNumber } from '../../../repositories/job_number/entity/job_number.entity'; export const createTermInfo = async ( datasource: DataSource, @@ -34,5 +35,6 @@ export const deleteAccount = async ( id: number, ): Promise => { await dataSource.getRepository(User).delete({ account_id: id }); + await dataSource.getRepository(JobNumber).delete({ account_id: id }); await dataSource.getRepository(Account).delete({ id: id }); }; diff --git a/dictation_server/src/features/files/files.service.spec.ts b/dictation_server/src/features/files/files.service.spec.ts index 01d2b1e..813b155 100644 --- a/dictation_server/src/features/files/files.service.spec.ts +++ b/dictation_server/src/features/files/files.service.spec.ts @@ -3,7 +3,6 @@ import { makeErrorResponse } from '../../common/error/makeErrorResponse'; import { makeBlobstorageServiceMockValue } from './test/files.service.mock'; import { DataSource } from 'typeorm'; import { - createJobNumber, createLicense, createTask, createUserGroupAndMember, @@ -14,12 +13,14 @@ import { import { FilesService } from './files.service'; import { makeContext } from '../../common/log'; import { + createJobNumber, getJobNumber, getTasks, makeHierarchicalAccounts, makeTestAccount, makeTestSimpleAccount, makeTestUser, + updateJobNumber, } from '../../common/test/utility'; import { makeTestingModule } from '../../common/test/modules'; import { @@ -858,8 +859,8 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } - // 最新のジョブナンバーでjob_numberテーブルを作成 - await createJobNumber(source, accountId, '00000003'); + // 最新のジョブナンバーでjob_numberテーブルを更新 + await updateJobNumber(source, accountId, '00000003'); const service = module.get(FilesService); const spy = jest @@ -944,8 +945,8 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } - // 最新のジョブナンバーでjob_numberテーブルを作成 - await createJobNumber(source, accountId, '00000003'); + // 最新のジョブナンバーでjob_numberテーブルを更新 + await updateJobNumber(source, accountId, '00000003'); const service = module.get(FilesService); // メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。 @@ -1033,8 +1034,8 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } - // 最新のジョブナンバーでjob_numberテーブルを作成 - await createJobNumber(source, accountId, '00000004'); + // 最新のジョブナンバーでjob_numberテーブルを更新 + await updateJobNumber(source, accountId, '00000004'); const service = module.get(FilesService); // メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。 @@ -1127,8 +1128,8 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { ); } - // 最新のジョブナンバーでjob_numberテーブルを作成 - await createJobNumber(source, accountId, '00000004'); + // 最新のジョブナンバーでjob_numberテーブルを更新 + await updateJobNumber(source, accountId, '00000004'); const service = module.get(FilesService); // メール送信関数が呼ばれたかどうかで判定を行う。実際のメール送信は行わない。 @@ -2335,8 +2336,6 @@ describe('タスク作成から自動ルーティング(DB使用)', () => { expect(jobNumber?.job_number).toEqual('99999999'); } - - const module = await makeTestingModuleWithBlobAndNotification( source, blobParam, diff --git a/dictation_server/src/features/files/test/utility.ts b/dictation_server/src/features/files/test/utility.ts index 9239a1f..5b96e99 100644 --- a/dictation_server/src/features/files/test/utility.ts +++ b/dictation_server/src/features/files/test/utility.ts @@ -110,18 +110,6 @@ export const createTask = async ( return { audioFileId: audioFile.id, taskId: task.id }; }; -// job_numberテーブルにレコードを作成する -export const createJobNumber = async ( - datasource: DataSource, - accountId: number, - jobNumber: string, -): Promise => { - await datasource.getRepository(JobNumber).insert({ - account_id: accountId, - job_number: jobNumber, - }); -}; - export const getTaskFromJobNumber = async ( datasource: DataSource, jobNumber: string, diff --git a/dictation_server/src/features/tasks/tasks.service.spec.ts b/dictation_server/src/features/tasks/tasks.service.spec.ts index 5fe8662..04af977 100644 --- a/dictation_server/src/features/tasks/tasks.service.spec.ts +++ b/dictation_server/src/features/tasks/tasks.service.spec.ts @@ -24,9 +24,11 @@ import { import { Adb2cTooManyRequestsError } from '../../gateways/adb2c/adb2c.service'; import { makeContext } from '../../common/log'; import { + createSortCriteria, makeTestAccount, makeTestSimpleAccount, makeTestUser, + updateSortCriteria, } from '../../common/test/utility'; import { ADMIN_ROLES, @@ -36,7 +38,6 @@ import { USER_ROLES, } from '../../constants'; import { makeTestingModule } from '../../common/test/modules'; -import { createSortCriteria } from '../users/test/utility'; import { createWorktype } from '../accounts/test/utility'; import { createWorkflow, @@ -3863,7 +3864,7 @@ describe('getNextTask', () => { role: USER_ROLES.TYPIST, }); - await createSortCriteria(source, typistUserId, 'JOB_NUMBER', 'ASC'); + await updateSortCriteria(source, typistUserId, 'JOB_NUMBER', 'ASC'); const { taskId: taskId1 } = await createTask( source, @@ -4007,7 +4008,7 @@ describe('getNextTask', () => { role: USER_ROLES.TYPIST, }); - await createSortCriteria(source, typistUserId, 'JOB_NUMBER', 'ASC'); + await updateSortCriteria(source, typistUserId, 'JOB_NUMBER', 'ASC'); const { taskId: taskId1, audioFileId: audioFileId1 } = await createTask( source, diff --git a/dictation_server/src/features/tasks/test/utility.ts b/dictation_server/src/features/tasks/test/utility.ts index 676f1ad..b0c9637 100644 --- a/dictation_server/src/features/tasks/test/utility.ts +++ b/dictation_server/src/features/tasks/test/utility.ts @@ -263,7 +263,6 @@ export const getTask = async ( return task; }; - export const getCheckoutPermissions = async ( datasource: DataSource, task_id: number, diff --git a/dictation_server/src/features/templates/templates.service.spec.ts b/dictation_server/src/features/templates/templates.service.spec.ts index 442dc87..aca4887 100644 --- a/dictation_server/src/features/templates/templates.service.spec.ts +++ b/dictation_server/src/features/templates/templates.service.spec.ts @@ -6,7 +6,11 @@ import { getTemplateFiles, updateTaskTemplateFile, } from './test/utility'; -import { getTasks, makeTestAccount, makeTestUser } from '../../common/test/utility'; +import { + getTasks, + makeTestAccount, + makeTestUser, +} from '../../common/test/utility'; import { makeContext } from '../../common/log'; import { TemplateFilesRepositoryService } from '../../repositories/template_files/template_files.repository.service'; import { HttpException, HttpStatus } from '@nestjs/common'; diff --git a/dictation_server/src/features/users/test/utility.ts b/dictation_server/src/features/users/test/utility.ts index 849aa34..99a6413 100644 --- a/dictation_server/src/features/users/test/utility.ts +++ b/dictation_server/src/features/users/test/utility.ts @@ -165,16 +165,3 @@ export const makeTestingModuleWithAdb2c = async ( console.log(e); } }; - -export const createSortCriteria = async ( - datasource: DataSource, - userId: number, - parameter: string, - direction: string, -): Promise => { - await datasource.getRepository(SortCriteria).insert({ - user_id: userId, - parameter: parameter, - direction: direction, - }); -}; diff --git a/dictation_server/src/repositories/accounts/accounts.repository.service.ts b/dictation_server/src/repositories/accounts/accounts.repository.service.ts index b5cbe5d..ecd571f 100644 --- a/dictation_server/src/repositories/accounts/accounts.repository.service.ts +++ b/dictation_server/src/repositories/accounts/accounts.repository.service.ts @@ -67,6 +67,7 @@ import { PartnerLicenseInfoForRepository, } from '../../features/accounts/types/types'; import { AccountArchive } from './entity/account_archive.entity'; +import { JobNumber } from '../job_number/entity/job_number.entity'; @Injectable() export class AccountsRepositoryService { @@ -1245,6 +1246,15 @@ export class AccountsRepositoryService { context, ); + // jobNumberのテーブルのレコードを削除する + const jobNumberRepo = entityManager.getRepository(JobNumber); + await deleteEntity( + jobNumberRepo, + { account_id: accountId }, + this.isCommentOut, + context, + ); + // アカウントを削除 await deleteEntity( accountRepo, @@ -1624,6 +1634,15 @@ export class AccountsRepositoryService { context, ); + // JobNumberのテーブルのレコードを削除する + const jobNumberRepo = entityManager.getRepository(JobNumber); + await deleteEntity( + jobNumberRepo, + { account_id: targetAccountId }, + this.isCommentOut, + context, + ); + // アカウントを削除 await deleteEntity( accountRepo, From fe5e8b8e1c87731e3a3ebd2a8e8075d1df41adde Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Tue, 14 May 2024 07:18:57 +0000 Subject: [PATCH 6/9] =?UTF-8?q?Merged=20PR=20895:=20API=E4=BF=AE=E6=AD=A3(?= =?UTF-8?q?=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E4=BD=9C=E6=88=90?= =?UTF-8?q?=E7=B3=BB)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task4043: API修正(アカウント作成系)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4043) - アカウント作成時にJobNumberの初期値を設定するように修正 - パートナーアカウント作成時にJobNumberの初期値を設定するように修正 - リカバリ処理にJobNumberのレコード削除を追加 - テスト修正 ## レビューポイント - JobNumber作成処理の追加する箇所に問題はないか - テストケースに不足はないか ## クエリの変更 - Repositoryを変更し、クエリが変更された場合は変更内容を確認する - Before/Afterのクエリ - 既存のクエリに修正はなし ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - 具体的にどのような確認をしたか - 既存テストが通ることを確認 - パートナーアカウント作成のテストにメール送信内容のチェックを追加 - ソート条件が作成・削除されていることを確認するテストを追加 ## 補足 - 相談、参考資料などがあれば --- dictation_server/src/common/test/utility.ts | 1 - dictation_server/src/constants/index.ts | 13 +++++ .../accounts/accounts.service.spec.ts | 53 ++++++++++++++++++- .../accounts/accounts.repository.service.ts | 23 ++++++++ .../tasks/tasks.repository.service.ts | 3 +- 5 files changed, 90 insertions(+), 3 deletions(-) diff --git a/dictation_server/src/common/test/utility.ts b/dictation_server/src/common/test/utility.ts index d784663..2d531a4 100644 --- a/dictation_server/src/common/test/utility.ts +++ b/dictation_server/src/common/test/utility.ts @@ -454,7 +454,6 @@ export const createJobNumber = async ( }); }; - // job_numberテーブルのレコードを更新する export const updateJobNumber = async ( datasource: DataSource, diff --git a/dictation_server/src/constants/index.ts b/dictation_server/src/constants/index.ts index 6478a26..c91dd23 100644 --- a/dictation_server/src/constants/index.ts +++ b/dictation_server/src/constants/index.ts @@ -339,3 +339,16 @@ export const STORAGE_SIZE_PER_LICENSE = 5; * @const {number} */ export const STORAGE_WARNING_THRESHOLD_PERCENT = 80; + +/** + * JobNumberの初期値 + * @const {string} + */ +export const INITIAL_JOB_NUMBER = '00000000'; + + +/** + * JobNumberの最大値 + * @const {string} + */ +export const MAX_JOB_NUMBER = '99999999'; diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index dcad6ac..1dc617b 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -49,6 +49,7 @@ import { AccountsService } from './accounts.service'; import { Context, makeContext } from '../../common/log'; import { ADB2C_SIGN_IN_TYPE, + INITIAL_JOB_NUMBER, LICENSE_ALLOCATED_STATUS, LICENSE_ISSUE_STATUS, LICENSE_TYPE, @@ -223,6 +224,16 @@ describe('createAccount', () => { expect(user?.account_id).toBe(accountId); expect(user?.role).toBe(role); + // jobNumberの初期値が正しく設定されているか確認 + const jobNumber = await getJobNumber(source, accountId); + expect(jobNumber?.job_number).toBe(INITIAL_JOB_NUMBER); + + // sortCriteriaが正しく設定されているか確認 + const sortCriteria = await getSortCriteria(source, user?.id ?? 0); + expect(sortCriteria?.user_id).toBe(user?.id); + expect(sortCriteria?.direction).toBe('ASC'); + expect(sortCriteria?.parameter).toBe('JOB_NUMBER'); + // 想定通りのメールが送られているか確認 expect(_subject).toBe('User Registration Notification [U-102]'); expect( @@ -878,7 +889,26 @@ describe('createPartnerAccount', () => { }, }); - overrideSendgridService(service, {}); + let _subject = ''; + let _url: string | undefined = ''; + overrideSendgridService(service, { + sendMail: async ( + context: Context, + to: string[], + cc: string[], + from: string, + subject: string, + text: string, + html: string, + ) => { + const urlPattern = /https?:\/\/[^\s]+/g; + const urls = text.match(urlPattern); + const url = urls?.pop(); + + _subject = subject; + _url = url; + }, + }); overrideBlobstorageService(service, { createContainer: async () => { @@ -918,6 +948,19 @@ describe('createPartnerAccount', () => { expect(createdAccount?.tier).toBe(2); expect(createdAccount?.primary_admin_user_id).toBe(createdUser?.id); expect(createdAccount?.secondary_admin_user_id).toBe(null); + const sortCriteria = await getSortCriteria(source, createdUser?.id ?? 0); + expect(sortCriteria).not.toBeNull(); + expect(sortCriteria?.user_id).toBe(createdUser?.id); + expect(sortCriteria?.direction).toBe('ASC'); + expect(sortCriteria?.parameter).toBe('JOB_NUMBER'); + const jobNumber = await getJobNumber(source, accountId); + expect(jobNumber?.job_number).toBe(INITIAL_JOB_NUMBER); + + // 想定通りのメールが送られているか確認 + expect(_subject).toBe('User Registration Notification [U-114]'); + expect( + _url?.startsWith('http://localhost:8081/mail-confirm/user?verify='), + ).toBeTruthy(); } }); @@ -1308,6 +1351,10 @@ describe('createPartnerAccount', () => { expect(users.length).toBe(2); expect(users[0].external_id).toBe(parentExternalId); expect(users[1].external_id).toBe(partnerExternalId); + const sortCriteria = await getSortCriteriaList(source); + expect(sortCriteria.length).toBe(2); + const jobNumber = await getJobNumber(source, accounts[1].id); + expect(jobNumber?.job_number).toBe(INITIAL_JOB_NUMBER); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith(partnerExternalId, context); } @@ -1487,6 +1534,10 @@ describe('createPartnerAccount', () => { expect(users.length).toBe(2); expect(users[0].external_id).toBe(parentExternalId); expect(users[1].external_id).toBe(partnerExternalId); + const sortCriteria = await getSortCriteriaList(source); + expect(sortCriteria.length).toBe(2); + const jobNumber = await getJobNumber(source, accounts[1].id); + expect(jobNumber?.job_number).toBe(INITIAL_JOB_NUMBER); // ADB2Cユーザー削除メソッドが呼ばれているか確認 expect(b2cService.deleteUser).toBeCalledWith(partnerExternalId, context); // コンテナ削除メソッドが呼ばれているか確認 diff --git a/dictation_server/src/repositories/accounts/accounts.repository.service.ts b/dictation_server/src/repositories/accounts/accounts.repository.service.ts index ecd571f..06ea6ef 100644 --- a/dictation_server/src/repositories/accounts/accounts.repository.service.ts +++ b/dictation_server/src/repositories/accounts/accounts.repository.service.ts @@ -26,6 +26,7 @@ import { getTaskListSortableAttribute, } from '../../common/types/sort/util'; import { + INITIAL_JOB_NUMBER, LICENSE_ALLOCATED_STATUS, LICENSE_EXPIRATION_THRESHOLD_DAYS, LICENSE_ISSUE_STATUS, @@ -210,6 +211,20 @@ export class AccountsRepositoryService { throw new Error(`invalid update. result.affected=${result.affected}`); } + // job_numberの初期値を設定 + const jobNumberRepo = entityManager.getRepository(JobNumber); + const initialJobNumber = jobNumberRepo.create({ + account_id: persistedAccount.id, + job_number: INITIAL_JOB_NUMBER, + }); + await insertEntity( + JobNumber, + jobNumberRepo, + initialJobNumber, + this.isCommentOut, + context, + ); + // ユーザーのタスクソート条件を作成 const sortCriteria = new SortCriteria(); { @@ -245,6 +260,14 @@ export class AccountsRepositoryService { const accountsRepo = entityManager.getRepository(Account); const usersRepo = entityManager.getRepository(User); const sortCriteriaRepo = entityManager.getRepository(SortCriteria); + const jobNumberRepo = entityManager.getRepository(JobNumber); + // JobNumberを削除 + await deleteEntity( + jobNumberRepo, + { account_id: accountId }, + this.isCommentOut, + context, + ); // ソート条件を削除 await deleteEntity( sortCriteriaRepo, diff --git a/dictation_server/src/repositories/tasks/tasks.repository.service.ts b/dictation_server/src/repositories/tasks/tasks.repository.service.ts index 1e30896..aef734f 100644 --- a/dictation_server/src/repositories/tasks/tasks.repository.service.ts +++ b/dictation_server/src/repositories/tasks/tasks.repository.service.ts @@ -12,6 +12,7 @@ import { import { Task } from './entity/task.entity'; import { ADMIN_ROLES, + MAX_JOB_NUMBER, NODE_ENV_TEST, TASK_STATUS, USER_ROLES, @@ -921,7 +922,7 @@ export class TasksRepositoryService { } let newJobNumber: string = ''; - if (currentJobNumberData.job_number === '99999999') { + if (currentJobNumberData.job_number === MAX_JOB_NUMBER) { // 末尾なら00000001に戻る newJobNumber = '00000001'; } else { From 0cca61517c8b47fe8ba412cd68083d340baab439 Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Wed, 15 May 2024 06:15:22 +0000 Subject: [PATCH 7/9] =?UTF-8?q?Merged=20PR=20896:=20=E3=83=90=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=83=A7=E3=83=B3=E3=82=A2=E3=83=83=E3=83=97=E7=94=A8?= =?UTF-8?q?SQL=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task4044: バージョンアップ用SQLを作成](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4044) - jobNumberの初期値を設定するマイグレーションファイル作成 - タスクテーブルにレコードがある(=タスクを作成したことがある)アカウントに対しては最新のJobNumberで初期値をセットする - タスクテーブルにレコードがない(=タスク作成をしたことがない)アカウントに対しては`00000000`をセットする ## レビューポイント - セットする初期値は認識あっているか - migrate downの処理は問題ないか ## 動作確認状況 - ローカルで確認、develop環境で確認など - 行った修正がデグレを発生させていないことを確認できるか - 具体的にどのような確認をしたか - マイグレーションファイルの作成のみなのでほかに影響はない想定 ## 補足 - 相談、参考資料などがあれば --- .../066-insert_initial_job_number.sql | 19 +++++++++++++++++++ dictation_server/src/constants/index.ts | 1 - 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 dictation_server/db/migrations/066-insert_initial_job_number.sql diff --git a/dictation_server/db/migrations/066-insert_initial_job_number.sql b/dictation_server/db/migrations/066-insert_initial_job_number.sql new file mode 100644 index 0000000..985026e --- /dev/null +++ b/dictation_server/db/migrations/066-insert_initial_job_number.sql @@ -0,0 +1,19 @@ +-- +migrate Up +INSERT INTO job_number (account_id, job_number) +SELECT + a.id AS account_id, + COALESCE(t.max_job_number, '00000000') AS job_number +FROM + accounts a +LEFT JOIN ( + SELECT + account_id, + MAX(job_number) AS max_job_number + FROM + tasks + GROUP BY + account_id +) t ON a.id = t.account_id; + +-- +migrate Down +TRUNCATE TABLE job_number; \ No newline at end of file diff --git a/dictation_server/src/constants/index.ts b/dictation_server/src/constants/index.ts index c91dd23..f295ed5 100644 --- a/dictation_server/src/constants/index.ts +++ b/dictation_server/src/constants/index.ts @@ -346,7 +346,6 @@ export const STORAGE_WARNING_THRESHOLD_PERCENT = 80; */ export const INITIAL_JOB_NUMBER = '00000000'; - /** * JobNumberの最大値 * @const {string} From ddf338bc726eb16b694d378bc384e838e85e7afc Mon Sep 17 00:00:00 2001 From: "makabe.t" Date: Wed, 15 May 2024 07:15:35 +0000 Subject: [PATCH 8/9] =?UTF-8?q?Merged=20PR=20890:=20=E7=BF=BB=E8=A8=B3?= =?UTF-8?q?=E6=83=85=E5=A0=B1=E5=8F=8D=E6=98=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task4058: 翻訳情報反映](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4058) - 6月末リリース分の翻訳をクライアントに適用しました。 - いただいた翻訳内容は開発の管理ファイルに反映しています。(メンテナンスについては未反映です。) [ラベル・メッセージ管理](https://sonyjpn.sharepoint.com/:x:/r/sites/S127-OMDS-EXTERNAL-NDS/_layouts/15/doc2.aspx?sourcedoc=%7BCBFBB1F9-3AB4-4E2E-A21D-572684D20B29%7D&file=%E3%83%A9%E3%83%99%E3%83%AB%E3%83%BB%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E7%AE%A1%E7%90%86_dictation.xlsx&action=default&mobileredirect=true) ## レビューポイント - 翻訳の適用は適切か? ## UIの変更 - 翻訳の適用 ## クエリの変更 - なし ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - リテラルの内容のみなので影響なし --- dictation_client/src/translation/de.json | 156 +++++++++++----------- dictation_client/src/translation/en.json | 112 ++++++++-------- dictation_client/src/translation/es.json | 160 ++++++++++++----------- dictation_client/src/translation/fr.json | 156 +++++++++++----------- 4 files changed, 300 insertions(+), 284 deletions(-) diff --git a/dictation_client/src/translation/de.json b/dictation_client/src/translation/de.json index f6ba44f..67396de 100644 --- a/dictation_client/src/translation/de.json +++ b/dictation_client/src/translation/de.json @@ -49,6 +49,10 @@ "newUser": "Neuer Benutzer", "signUpButton": "Benutzerkonto erstellen", "logoAlt": "OM Dictation Management System in the Cloud" + }, + "text": { + "maintenanceNotificationTitle": "(de)サービス停止のお知らせ", + "maintenanceNotification": "Aufgrund von Systemwartungsarbeiten wird ODMS Cloud ab dem 12. Juni, 6:00 Uhr UTC-Zeit, etwa eine Stunde lang nicht verfügbar sein. Wir entschuldigen uns für etwaige Unannehmlichkeiten, die während der Wartung entstanden sind." } }, "signupPage": { @@ -129,18 +133,18 @@ "roleChangeError": "Die Benutzerrolle kann nicht geändert werden. Die angezeigten Informationen sind möglicherweise veraltet. Aktualisieren Sie daher bitte den Bildschirm, um den neuesten Status anzuzeigen.", "encryptionPasswordCorrectError": "Das Verschlüsselungskennwort entspricht nicht den Regeln.", "alreadyLicenseDeallocatedError": "Die zugewiesene Lizenz wurde bereits storniert. Die angezeigten Informationen sind möglicherweise veraltet. Aktualisieren Sie daher bitte den Bildschirm, um den neuesten Status anzuzeigen.", - "userDeletionLicenseActiveError": "(de)ユーザーの削除に失敗しました。対象ユーザーのライセンス割り当てを解除してください。", - "typistDeletionRoutingRuleError": "(de)ユーザーの削除に失敗しました。Workflow画面でルーティングルールから対象Transcriptionistを外してください。", - "adminUserDeletionError": "(de)ユーザーの削除に失敗しました。アカウント画面で対象ユーザーをPrimary/Secondary Administratorから外してください。", - "typistUserDeletionTranscriptionTaskError": "(de)ユーザーの削除に失敗しました。Dictation画面でタスクのルーティングから対象Transcriptionistを外してください。", - "authorUserDeletionTranscriptionTaskError": "(de)ユーザーの削除に失敗しました。Dictation画面で対象AuthorのAuthorIDが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。", - "typistUserDeletionTranscriptionistGroupError": "(de)ユーザーの削除に失敗しました。Workflow画面でTranscriptionistGroupから対象Transcriptionistを外してください。", - "authorDeletionRoutingRuleError": "(de)ユーザーの削除に失敗しました。Workflow画面でルーティングルールから対象AuthorのAuthorIDを外してください。", - "importSuccess": "(de)ユーザー一括追加を受け付けました。登録処理が完了次第メールが届きますのでご確認ください。", - "duplicateEmailError": "(de)以下の行のメールアドレスがCSV中で重複しています。", - "duplicateAuthorIdError": "(de)以下の行のAuthorIDがCSV中で重複しています。", - "overMaxUserError": "(de)一度に追加できるユーザーは100件までです。", - "invalidInputError": "(de)以下の行のユーザー情報が入力ルールに準拠していません。" + "userDeletionLicenseActiveError": "Der Benutzer konnte nicht gelöscht werden. Bitte heben Sie die Lizenzzuweisung vom Benutzer auf.", + "typistDeletionRoutingRuleError": "Der Benutzer konnte nicht gelöscht werden. Dieser Benutzer ist als Transkriptionist registriert, der in den Routing-Regeln enthalten ist. Bitte entfernen Sie den Transcriptionist aus der entsprechenden Routing-Regel auf der Registerkarte „Workflow“.", + "adminUserDeletionError": "Der Benutzer konnte nicht gelöscht werden. Bitte entfernen Sie den Benutzer vom primären oder sekundären Administrator auf der Registerkarte „Konto“.", + "typistUserDeletionTranscriptionTaskError": "Der Benutzer konnte nicht gelöscht werden. Diesem Transkriptionisten ist eine Aufgabe zugewiesen. Bitte ändern Sie die für die Aufgabe verantwortliche Person auf der Registerkarte „Diktieren“ in einen anderen Transkriptionisten.", + "authorUserDeletionTranscriptionTaskError": "Der Benutzer konnte nicht gelöscht werden. Es gibt von diesem Autor erstellte Aufgaben, die unvollständig sind. Bitte löschen Sie die Aufgaben, die von diesem Autor erstellt wurden, oder markieren Sie sie als erledigt.", + "typistUserDeletionTranscriptionistGroupError": "Der Benutzer konnte nicht gelöscht werden. Bitte entfernen Sie diesen Transkriptionisten aus der Transkriptionistengruppe auf der Registerkarte „Workflow“.", + "authorDeletionRoutingRuleError": "Der Benutzer konnte nicht gelöscht werden. Bitte entfernen Sie diesen Autor aus den Weiterleitungsregeln auf der Registerkarte „Workflow“.", + "importSuccess": "Wir haben Ihre Anfrage zur Massenbenutzerregistrierung erhalten. Bitte überprüfen Sie Ihre E-Mails, da Sie eine E-Mail erhalten, sobald der Registrierungsprozess abgeschlossen ist.", + "duplicateEmailError": "Die E-Mail-Adressen in den folgenden Zeilen werden in der CSV-Datei dupliziert.", + "duplicateAuthorIdError": "Die Autoren-ID in der folgenden Zeile wird in der CSV-Datei dupliziert.", + "overMaxUserError": "Durch die Benutzerregistrierung per CSV-Datei können bis zu 100 Benutzer gleichzeitig registriert werden.", + "invalidInputError": "Die Benutzerinformationen in der folgenden Zeile entsprechen nicht den Eingaberegeln." }, "label": { "title": "Benutzer", @@ -174,32 +178,32 @@ "none": "Keiner", "encryptionPassword": "Passwort", "encryptionPasswordTerm": "Bitte legen Sie Ihr Passwort mit 4 bis 16 alphanumerischen Zeichen und Symbolen fest.", - "bulkImport": "(de)Bulk import", - "downloadCsv": "(de)Download CSV", - "importCsv": "(de)Import CSV", - "inputRules": "(de)Input rules", - "nameLabel": "(de)Name", - "emailAddressLabel": "(de)Email Address", - "roleLabel": "(de)Role", - "authorIdLabel": "(de)Author ID", - "autoRenewLabel": "(de)Auto Renew", - "notificationLabel": "(de)Notification", - "encryptionLabel": "(de)Encryption", - "encryptionPasswordLabel": "(de)Encryption Password", - "promptLabel": "(de)Prompt", - "addUsers": "(de)Add users" + "bulkImport": "Benutzer-Massenregistrierung", + "downloadCsv": "Laden Sie eine Beispiel-CSV-Datei herunter", + "importCsv": "CSV-Datei importieren", + "inputRules": "Eingaberegeln", + "nameLabel": "Name", + "emailAddressLabel": "E-Mail-Addresse", + "roleLabel": "Rolle", + "authorIdLabel": "Autoren-ID", + "autoRenewLabel": "Automatisch zuweisen", + "notificationLabel": "Benachrichtigung", + "encryptionLabel": "Verschlüsselung", + "encryptionPasswordLabel": "Verschlüsselungspasswort", + "promptLabel": "Eingabeaufforderung", + "addUsers": "Benutzer hinzufügen" }, "text": { - "downloadExplain": "(de)Download the csv format and enter it according to the rules below.", - "nameRule": "(de)Maximum 225 characters", - "emailAddressRule": "(de)Maximum 225 characters\nCannot use an email address that is already in use.", - "roleRule": "(de)None : 0\nAuthor : 1\nTranscriptionist : 2", - "authorIdRule": "(de)Required only when Role=Author(1)\nMaximum 16 characters\nOnly uppercase alphanumeric characters and \"_\" can be entered.\nCannot use an Author ID that is already in use.", - "autoRenewRule": "(de)0 or 1", - "notificationRule": "(de)0 or 1", - "encryptionRule": "(de)Required only when Role=Author(1)\n0 or 1", - "encryptionPasswordRule": "(de)Required only when Role=Author(1) and Encryption=ON(1)\nOnly 4 to 16 letters, numbers, and symbols can be entered.", - "promptRule": "(de)Required only when Role=Author(1)\n0 or 1" + "downloadExplain": "Bitte laden Sie die CSV-Beispieldatei herunter und geben Sie die erforderlichen Informationen gemäß den folgenden Regeln ein.", + "nameRule": "> Maximal 225 Zeichen", + "emailAddressRule": "> Maximal 225 Zeichen\n> Eine bereits verwendete E-Mail-Adresse kann nicht verwendet werden.", + "roleRule": "Keiner : 0\nAutor : 1\nTranskriptionist : 2", + "authorIdRule": "> In diesem Element kann nur dann ein Wert festgelegt werden, wenn die „Rolle“ „Autor“ ist.\n> Maximal 16 Zeichen\n> Es dürfen nur großgeschriebene alphanumerische Zeichen und „_“ verwendet werden.\n> Eine bereits verwendete Autoren-ID kann nicht verwendet werden.", + "autoRenewRule": "> Wert : 0 oder 1 (1=EIN)", + "notificationRule": "> Wert : 0 oder 1 (1=EIN)", + "encryptionRule": "> In diesem Element kann nur dann ein Wert festgelegt werden, wenn die „Rolle“ „Autor“ ist.\n> Wert : 0 oder 1 (1=EIN)", + "encryptionPasswordRule": "> In diesem Element kann nur dann ein Wert festgelegt werden, wenn die „Rolle“ „Autor“ ist und die Verschlüsselung EIN ist.\n> Es können nur 4 bis 16 Buchstaben, Zahlen und Symbole eingegeben werden.", + "promptRule": "> In diesem Element kann nur dann ein Wert festgelegt werden, wenn die „Rolle“ „Autor“ ist.\n> Wert : 0 oder 1 (1=EIN)" } }, "LicenseSummaryPage": { @@ -220,10 +224,10 @@ "storageAvailable": "Speicher nicht verfügbar (Menge überschritten)", "licenseLabel": "Lizenz", "storageLabel": "Lagerung", - "storageUnavailableCheckbox": "(de)Storage Unavailable" + "storageUnavailableCheckbox": "Beschränken Sie die Kontonutzung" }, "message": { - "storageUnavalableSwitchingConfirm": "(de)対象アカウントのストレージ使用制限状態を変更します。よろしいですか?" + "storageUnavalableSwitchingConfirm": "Sind Sie sicher, dass Sie den Speichernutzungsstatus für dieses Konto ändern möchten?" } }, "licenseOrderPage": { @@ -251,12 +255,12 @@ "taskNotEditable": "Der Transkriptionist kann nicht geändert werden, da die Transkription bereits ausgeführt wird oder die Datei nicht vorhanden ist. Bitte aktualisieren Sie den Bildschirm und prüfen Sie den aktuellen Status.", "backupFailedError": "Der Prozess „Dateisicherung“ ist fehlgeschlagen. Bitte versuchen Sie es später noch einmal. Wenn der Fehler weiterhin besteht, wenden Sie sich an Ihren Systemadministrator.", "cancelFailedError": "Die Diktate konnten nicht gelöscht werden. Bitte aktualisieren Sie Ihren Bildschirm und versuchen Sie es erneut.", - "deleteFailedError": "(de)タスクの削除に失敗しました。画面を更新し、再度ご確認ください。", + "deleteFailedError": "Die Aufgabe konnte nicht gelöscht werden. Bitte aktualisieren Sie den Bildschirm und überprüfen Sie ihn erneut.", "licenseNotAssignedError": "Die Transkription ist nicht möglich, da keine gültige Lizenz zugewiesen ist. Bitten Sie Ihren Administrator, eine gültige Lizenz zuzuweisen.", "licenseExpiredError": "Die Transkription ist nicht möglich, da Ihre Lizenz abgelaufen ist. Bitte bitten Sie Ihren Administrator, Ihnen eine gültige Lizenz zuzuweisen.", - "fileAlreadyDeletedError": "(de)既に削除された音声ファイルが含まれています。画面を更新し、再度ご確認ください", - "fileRenameFailedError": "(de)ファイル名の変更に失敗しました。画面を更新し、再度ご確認ください。", - "fileNameAleadyExistsError": "(de)このファイル名は既に登録されています。他のファイル名で登録してください。" + "fileAlreadyDeletedError": "Die Bildschirminformationen sind nicht aktuell, sie enthalten bereits gelöschte Audiodateien. Bitte aktualisieren Sie den Bildschirm und wählen Sie die zu löschenden Dateien erneut aus.", + "fileRenameFailedError": "Da die Bildschirminformationen nicht aktuell sind, ist eine Inkonsistenz in den Dateiinformationen aufgetreten und die Datei konnte nicht umbenannt werden. Bitte aktualisieren Sie den Bildschirm und versuchen Sie es erneut.", + "fileNameAleadyExistsError": "Dieser Dateiname ist bereits registriert. Bitte registrieren Sie sich mit einem anderen Dateinamen." }, "label": { "title": "Diktate", @@ -298,13 +302,13 @@ "changeTranscriptionist": "Transkriptionist ändern", "deleteDictation": "Diktat löschen", "selectedTranscriptionist": "Ausgewählter transkriptionist", - "poolTranscriptionist": "Transkriptionsliste", + "poolTranscriptionist": "Liste der Transkriptionisten", "fileBackup": "Dateisicherung", "downloadForBackup": "Zur Sicherung herunterladen", "applications": "Desktopanwendung", "cancelDictation": "Transkription abbrechen", - "rawFileName": "(de)Raw File Name", - "fileNameSave": "(de)Save" + "rawFileName": "Ursprünglicher Dateiname", + "fileNameSave": "Führen Sie eine Dateiumbenennung durch" } }, "cardLicenseIssuePopupPage": { @@ -374,7 +378,7 @@ "issueRequesting": "Lizenzen auf Bestellung", "viewDetails": "Details anzeigen", "accounts": "konten", - "changeOwnerButton": "(de)Change Owner" + "changeOwnerButton": "Change Owner" } }, "orderHistoriesPage": { @@ -440,13 +444,13 @@ "templateOptional": "Vorlage (Optional)", "editRule": "Regel bearbeiten", "selected": "Ausgewählter transkriptionist", - "pool": "Transkriptionsliste", + "pool": "Liste der Transkriptionisten", "selectAuthor": "Autoren-ID auswählen", "selectWorktypeId": "Aufgabentypkennung auswählen", "selectTemplate": "Vorlage auswählen" }, "message": { - "selectedTypistEmptyError": "Transkriptionist oder Transkriptionistgruppe wurde nicht ausgewählt. Bitte wählen Sie eine oder mehrere aus der Transkriptionsliste aus.", + "selectedTypistEmptyError": "Transkriptionist oder Transkriptionistgruppe wurde nicht ausgewählt. Bitte wählen Sie eine oder mehrere aus der Transkriptionistenliste aus.", "workflowConflictError": "Eine Routing-Regel wurde bereits mit der angegebenen Kombination aus AuthorID und WorktypeID registriert. Bitte registrieren Sie sich mit einer anderen Kombination.", "inputEmptyError": "Pflichtfeld", "saveFailedError": "Die Routing-Regel konnte nicht gespeichert werden. Bitte aktualisieren Sie den Bildschirm und versuchen Sie es erneut." @@ -460,7 +464,7 @@ "addTypistGroup": "Transkriptionist Gruppe hinzufügen", "transcriptionist": "Transkriptionist", "selected": "Ausgewählter transkriptionist", - "pool": "Transkriptionsliste", + "pool": "Liste der Transkriptionisten", "add": "Hinzufügen", "remove": "Entfernen", "editTypistGroup": "Transkriptionistengruppe bearbeiten" @@ -469,8 +473,8 @@ "selectedTypistEmptyError": "Um eine Transkriptionsgruppe zu speichern, müssen ein oder mehrere Transkriptionisten ausgewählt werden.", "groupSaveFailedError": "Die Transkriptionistengruppe konnte nicht gespeichert werden. Die angezeigten Informationen sind möglicherweise veraltet. Aktualisieren Sie daher bitte den Bildschirm, um den neuesten Status anzuzeigen.", "GroupNameAlreadyExistError": "Der Name dieser Transkriptionistengruppe ist bereits registriert. Bitte registrieren Sie sich mit einem anderen Namen der Transkriptionistengruppe.", - "deleteFailedWorkflowAssigned": "(de)TranscriptionistGroupの削除に失敗しました。Workflow画面でルーティングルールから対象TranscriptionistGroupを外してください。", - "deleteFailedCheckoutPermissionExisted": "(de)TranscriptionistGroupの削除に失敗しました。Dictation画面でタスクのルーティングから対象TranscriptionistGroupを外してください。" + "deleteFailedWorkflowAssigned": "Die Transkriptionistengruppe konnte nicht gelöscht werden. Bitte entfernen Sie die Transcriptionist-Gruppe aus der Routing-Regel auf der Registerkarte „Workflow“.", + "deleteFailedCheckoutPermissionExisted": "Die Transkriptionistengruppe konnte nicht gelöscht werden. Dieser Transkriptionistengruppe ist eine Aufgabe zugewiesen. Bitte weisen Sie die Aufgabe über die Registerkarte „Diktieren“ einem anderen Transkriptionisten oder einer anderen Transkriptionistengruppe zu." } }, "worktypeIdSetting": { @@ -520,8 +524,8 @@ "fileEmptyError": "Dateiauswahl ist erforderlich. Bitte wählen Sie eine Datei aus." }, "message": { - "deleteFailedWorkflowAssigned": "(de)テンプレートファイルの削除に失敗しました。Workflow画面でルーティングルールから対象テンプレートファイルを外してください。", - "deleteFailedTaskAssigned": "(de)テンプレートファイルの削除に失敗しました。Dictation画面で対象テンプレートファイルが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。" + "deleteFailedWorkflowAssigned": "Die Vorlagendatei konnte nicht gelöscht werden. Bitte entfernen Sie die Vorlagendatei aus den Routing-Regeln auf der Registerkarte „Workflow“.", + "deleteFailedTaskAssigned": "Die Vorlagendatei konnte nicht gelöscht werden. Mit dieser Vorlage sind Aufgaben verbunden. Bitte löschen Sie die Aufgaben, die mit dieser Vorlage verknüpft sind, oder markieren Sie sie als erledigt." } }, "partnerPage": { @@ -537,19 +541,19 @@ "dealerManagement": "Erlauben Sie dem Händler, Änderungen vorzunehmen", "partners": "Partner", "deleteAccount": "Konto löschen", - "editAccount": "(de)Edit Account", - "accountInformation": "(de)Account information", - "primaryAdminInfo": "(de)Primary administrator's information", - "adminName": "(de)Admin Name", - "saveChanges": "(de)Save Changes" + "editAccount": "Konto bearbeiten", + "accountInformation": "Kontoinformationen", + "primaryAdminInfo": "Informationen des primären Administrators", + "adminName": "Name des Administrators", + "saveChanges": "Änderungen speichern" }, "message": { "delegateNotAllowedError": "Aktionen im Namen des Partners sind nicht zulässig. Bitte aktualisieren Sie den Bildschirm und überprüfen Sie ihn erneut.", "deleteFailedError": "Der Delegierungsvorgang ist fehlgeschlagen. Bitte aktualisieren Sie den Bildschirm und überprüfen Sie ihn erneut.", "delegateCancelError": "Der delegierte Vorgang wurde beendet, da die Berechtigung für den delegierten Vorgang widerrufen wurde.", - "partnerDeleteConfirm": "(de)選択したアカウントを削除します。削除したアカウントは復元できませんが本当によろしいですか?対象アカウント:", - "partnerDeleteFailedError": "(de)削除対象アカウントにLower layerアカウントが存在するため削除できません。Lower layerアカウントに対して削除、または現地法人以上のアカウントに階層構造の変更を依頼してください。", - "editFailedError": "(de)パートナーアカウントの編集に失敗しました。画面を更新し、再度ご確認ください。" + "partnerDeleteConfirm": "Löschen Sie das ausgewählte Konto. Ein gelöschtes Konto kann nicht wiederhergestellt werden. Sind Sie sicher? Zielkonto:", + "partnerDeleteFailedError": "Dieses Konto kann nicht gelöscht werden, da untergeordnete Konten mit diesem übergeordneten Konto verknüpft sind. Sie müssen die untergeordneten Konten verschieben oder löschen, bevor Sie dieses übergeordnete Konto löschen. Für weitere Informationen wenden Sie sich bitte an OMDS.", + "editFailedError": "Da die Bildschirminformationen nicht aktuell sind, ist beim Bearbeiten Ihres Partnerkontos eine Inkonsistenz aufgetreten. Bitte aktualisieren Sie den Bildschirm und versuchen Sie es erneut." } }, "accountPage": { @@ -571,7 +575,7 @@ "selectSecondaryAdministrator": "Sekundäradministrator auswählen", "saveChanges": "Änderungen speichern", "deleteAccount": "Konto löschen", - "fileRetentionDays": "(de)自動ファイル削除までの保持日数" + "fileRetentionDays": "Anzahl der Tage, die Dateien aufbewahrt werden, bevor sie automatisch gelöscht werden." }, "message": { "updateAccountFailedError": "Kontoinformationen konnten nicht gespeichert werden. Bitte aktualisieren Sie den Bildschirm und versuchen Sie es erneut." @@ -626,26 +630,26 @@ }, "fileDeleteSettingPopup": { "label": { - "title": "(de)Auto File Delete Setting", - "autoFileDeleteCheck": "(de)Auto file delete", - "daysAnnotation": "(de)Number of days from transcription finished to delete the files.", - "days": "(de)Days", - "saveButton": "(de)Save Settings", - "daysValidationError": "(de)Daysには1~999の数字を入力してください。" + "title": "Einstellung zum automatischen Löschen von Dateien", + "autoFileDeleteCheck": "Automatisches Löschen von Dateien", + "daysAnnotation": "Anzahl der Tage nach Abschluss der Transkription bis zum Löschen der Dateien.", + "days": "Tage", + "saveButton": "Einstellungen speichern", + "daysValidationError": "Bitte geben Sie für Tage eine Zahl zwischen 1 und 999 ein." } }, "changeOwnerPopup": { "message": { - "accountNotFoundError": "(de)変更先のアカウントIDは存在しません。", - "hierarchyMismatchError": "(de)パートナーアカウントの変更に失敗しました。\nLower layerの1階層上のアカウントを切り替え先に指定してください。", - "regionMismatchError": "(de)パートナーアカウントの変更に失敗しました。\nLower layerと同じリージョンのアカウントを切り替え先に指定してください。", - "countryMismatchError": "(de)パートナーアカウントの変更に失敗しました。\nLower layerと同じ国のアカウントを切り替え先に指定してください。" + "accountNotFoundError": "The account ID specified to change does not exist.", + "hierarchyMismatchError": "Failed to change partner account.\nPlease specify the account one level above the child account as the switch destination.。", + "regionMismatchError": "Failed to change partner account.\nPlease specify an account in the same region as the child account to switch to.", + "countryMismatchError": "Failed to change partner account.\nPlease specify an account in the same country as the child account to switch to." }, "label": { - "invalidInputError": "(de)変更先アカウントIDには1~9999999の数字を入力してください。", - "title": "(de)Change Owner", - "upperLayerId": "(de)Upper Layer ID", - "lowerLayerId": "(de)Lower Layer ID" + "invalidInputError": "Please enter a number between 1 and 9999999 for the destination account ID.", + "title": "Change Owner", + "upperLayerId": "Upper Layer ID", + "lowerLayerId": "Lower Layer ID" } } } \ No newline at end of file diff --git a/dictation_client/src/translation/en.json b/dictation_client/src/translation/en.json index cee7426..041783f 100644 --- a/dictation_client/src/translation/en.json +++ b/dictation_client/src/translation/en.json @@ -49,6 +49,10 @@ "newUser": "New User", "signUpButton": "Create Account", "logoAlt": "OM Dictation Management System in the Cloud" + }, + "text": { + "maintenanceNotificationTitle": "サービス停止のお知らせ", + "maintenanceNotification": "Due to system maintenance, ODMS Cloud will be unavailable for approximately one hour starting from June 12th, 6:00AM UTC time. We apologize for any inconvenience caused during the maintenance." } }, "signupPage": { @@ -129,18 +133,18 @@ "roleChangeError": "Unable to change the User Role. The displayed information may be outdated, so please refresh the screen to see the latest status.", "encryptionPasswordCorrectError": "Encryption password does not meet the rules.", "alreadyLicenseDeallocatedError": "Assigned license has already been canceled. The displayed information may be outdated, so please refresh the screen to see the latest status.", - "userDeletionLicenseActiveError": "ユーザーの削除に失敗しました。対象ユーザーのライセンス割り当てを解除してください。", - "typistDeletionRoutingRuleError": "ユーザーの削除に失敗しました。Workflow画面でルーティングルールから対象Transcriptionistを外してください。", - "adminUserDeletionError": "ユーザーの削除に失敗しました。アカウント画面で対象ユーザーをPrimary/Secondary Administratorから外してください。", - "typistUserDeletionTranscriptionTaskError": "ユーザーの削除に失敗しました。Dictation画面でタスクのルーティングから対象Transcriptionistを外してください。", - "authorUserDeletionTranscriptionTaskError": "ユーザーの削除に失敗しました。Dictation画面で対象AuthorのAuthorIDが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。", - "typistUserDeletionTranscriptionistGroupError": "ユーザーの削除に失敗しました。Workflow画面でTranscriptionistGroupから対象Transcriptionistを外してください。", - "authorDeletionRoutingRuleError": "ユーザーの削除に失敗しました。Workflow画面でルーティングルールから対象AuthorのAuthorIDを外してください。", - "importSuccess": "ユーザー一括追加を受け付けました。登録処理が完了次第メールが届きますのでご確認ください。", - "duplicateEmailError": "以下の行のメールアドレスがCSV中で重複しています。", - "duplicateAuthorIdError": "以下の行のAuthorIDがCSV中で重複しています。", - "overMaxUserError": "一度に追加できるユーザーは100件までです。", - "invalidInputError": "以下の行のユーザー情報が入力ルールに準拠していません。" + "userDeletionLicenseActiveError": "Failed to delete the user. Please unassign the license from the user.", + "typistDeletionRoutingRuleError": "Failed to delete the user. This user is registered as a Transcriptionist that is included in the routing rules. Please remove the Transcriptionist from the corresponding routing rule from the Workflow tab.", + "adminUserDeletionError": "Failed to delete the user. Please remove the user from the Primary or Secondary Administrator from the Account tab.", + "typistUserDeletionTranscriptionTaskError": "Failed to delete the user. There is a task assigned to this Transcriptionist. Please change the person in charge of the task to another Transcriptionist from the Dictation tab.", + "authorUserDeletionTranscriptionTaskError": "Failed to delete the user. There are tasks created by this Author that are incomplete. Please delete or mark the tasks as finished for tasks created by this Author.", + "typistUserDeletionTranscriptionistGroupError": "Failed to delete the user. Please remove this Transcriptionist from the Transcriptionist Group from the Workflow tab.", + "authorDeletionRoutingRuleError": "Failed to delete the user. Please remove this Author from the routing rules from the Workflow tab.", + "importSuccess": "We have received your bulk user registration request. Please check your email as you will receive an email once the registration process is complete.", + "duplicateEmailError": "The email addresses in the following lines are duplicated in the CSV file.", + "duplicateAuthorIdError": "The Author ID in the following line is duplicated in the CSV file.", + "overMaxUserError": "Up to 100 users can be registered at one time by user registration via CSV file.", + "invalidInputError": "The user information in the following line does not comply with the input rules." }, "label": { "title": "User", @@ -174,32 +178,32 @@ "none": "None", "encryptionPassword": "Password", "encryptionPasswordTerm": "Please set your password using 4 to 16 alphanumeric and symbols.", - "bulkImport": "Bulk import", - "downloadCsv": "Download CSV", - "importCsv": "Import CSV", + "bulkImport": "User Bulk Registration", + "downloadCsv": "Download sample CSV file", + "importCsv": "Import CSV file", "inputRules": "Input rules", "nameLabel": "Name", "emailAddressLabel": "Email Address", "roleLabel": "Role", "authorIdLabel": "Author ID", - "autoRenewLabel": "Auto Renew", + "autoRenewLabel": "Auto Assign", "notificationLabel": "Notification", "encryptionLabel": "Encryption", "encryptionPasswordLabel": "Encryption Password", "promptLabel": "Prompt", - "addUsers": "Add users" + "addUsers": "Add User" }, "text": { - "downloadExplain": "Download the csv format and enter it according to the rules below.", - "nameRule": "Maximum 225 characters", - "emailAddressRule": "Maximum 225 characters\nCannot use an email address that is already in use.", + "downloadExplain": "Please download the sample CSV file and apply the required information according to the rules below.", + "nameRule": "> Maximum 225 characters", + "emailAddressRule": "> Maximum 225 characters\n> Cannot use an email address that is already in use.", "roleRule": "None : 0\nAuthor : 1\nTranscriptionist : 2", - "authorIdRule": "Required only when Role=Author(1)\nMaximum 16 characters\nOnly uppercase alphanumeric characters and \"_\" can be entered.\nCannot use an Author ID that is already in use.", - "autoRenewRule": "0 or 1", - "notificationRule": "0 or 1", - "encryptionRule": "Required only when Role=Author(1)\n0 or 1", - "encryptionPasswordRule": "Required only when Role=Author(1) and Encryption=ON(1)\nOnly 4 to 16 letters, numbers, and symbols can be entered.", - "promptRule": "Required only when Role=Author(1)\n0 or 1" + "authorIdRule": "> A value can be set in this item only when the \"Role\" is ”Author”.\n> Maximum 16 characters.\n> Only uppercase alphanumeric characters and \"_\" can be used.\n> Cannot use an Author ID that is already in use.", + "autoRenewRule": "> Value : 0 or 1 (1=ON)", + "notificationRule": "> Value : 0 or 1 (1=ON)", + "encryptionRule": "> A value can be set in this item only when the \"Role\" is ”Author”.\n> Value : 0 or 1 (1=ON)", + "encryptionPasswordRule": "> A value can be set in this item only when the \"Role\" is ”Author” and Encryption is ON.\n> Only 4 to 16 letters, numbers, and symbols can be entered.", + "promptRule": "> A value can be set in this item only when the \"Role\" is ”Author”.\n> Value : 0 or 1 (1=ON)" } }, "LicenseSummaryPage": { @@ -220,10 +224,10 @@ "storageAvailable": "Storage Unavailable (Exceeded Amount)", "licenseLabel": "License", "storageLabel": "Storage", - "storageUnavailableCheckbox": "Storage Unavailable" + "storageUnavailableCheckbox": "Restrict account usage" }, "message": { - "storageUnavalableSwitchingConfirm": "対象アカウントのストレージ使用制限状態を変更します。よろしいですか?" + "storageUnavalableSwitchingConfirm": "Are you sure you would like to change the storage usage status for this account?" } }, "licenseOrderPage": { @@ -251,12 +255,12 @@ "taskNotEditable": "The transcriptionist cannot be changed because the transcription is already in progress or the file does not exist. Please refresh the screen and check the latest status.", "backupFailedError": "The \"File Backup\" process has failed. Please try again later. If the error continues, contact your system administrator.", "cancelFailedError": "Failed to delete the dictations. Please refresh your screen and try again.", - "deleteFailedError": "タスクの削除に失敗しました。画面を更新し、再度ご確認ください。", + "deleteFailedError": "Failed to delete the task. Please refresh the screen and check again.", "licenseNotAssignedError": "Transcription is not possible because a valid license is not assigned. Please ask your administrator to assign a valid license.", "licenseExpiredError": "Transcription is not possible because your license is expired. Please ask your administrator to assign a valid license.", - "fileAlreadyDeletedError": "既に削除された音声ファイルが含まれています。画面を更新し、再度ご確認ください", - "fileRenameFailedError": "ファイル名の変更に失敗しました。画面を更新し、再度ご確認ください。", - "fileNameAleadyExistsError": "このファイル名は既に登録されています。他のファイル名で登録してください。" + "fileAlreadyDeletedError": "The screen information is not up to date, it contains audio files that have already been deleted. Please refresh the screen, and select the files to delete again.", + "fileRenameFailedError": "Since the screen information is not up-to-date, an inconsistency occurred in the file information and failed to rename the file. Please refresh the screen and try again.", + "fileNameAleadyExistsError": "This file name is already registered. Please register with a different file name." }, "label": { "title": "Dictations", @@ -298,13 +302,13 @@ "changeTranscriptionist": "Change Transcriptionist", "deleteDictation": "Delete Dictation", "selectedTranscriptionist": "Selected Transcriptionist", - "poolTranscriptionist": "Transcription List", + "poolTranscriptionist": "Transcriptionist List", "fileBackup": "File Backup", "downloadForBackup": "Download for backup", "applications": "Desktop Application", "cancelDictation": "Cancel Transcription", - "rawFileName": "Raw File Name", - "fileNameSave": "Save" + "rawFileName": "Original File Name", + "fileNameSave": "Execute file rename" } }, "cardLicenseIssuePopupPage": { @@ -440,13 +444,13 @@ "templateOptional": "Template (Optional)", "editRule": "Edit Rule", "selected": "Selected Transcriptionist", - "pool": "Transcription List", + "pool": "Transcriptionist List", "selectAuthor": "Select Author ID", "selectWorktypeId": "Select Worktype ID", "selectTemplate": "Select Template" }, "message": { - "selectedTypistEmptyError": "Transcriptionist, or Transcriptionist Group has not been selected. Please select one or more from the Transcription List.", + "selectedTypistEmptyError": "Transcriptionist, or Transcriptionist Group has not been selected. Please select one or more from the Transcriptionist List.", "workflowConflictError": "A routing rule has already been registered with the specified AuthorID and WorktypeID combination. Please register with different combination.", "inputEmptyError": "Mandatory Field", "saveFailedError": "Failed to save the routing rule. Please refresh the screen and try again." @@ -460,7 +464,7 @@ "addTypistGroup": "Add Transcriptionist Group", "transcriptionist": "Transcriptionist", "selected": "Selected Transcriptionist", - "pool": "Transcription List", + "pool": "Transcriptionist List", "add": "Add", "remove": "Remove", "editTypistGroup": "Edit Transcriptionist Group" @@ -469,8 +473,8 @@ "selectedTypistEmptyError": "One or more transcriptonist must be selected to save a transcrption group.", "groupSaveFailedError": "Transcriptionist Group could not be saved. The displayed information may be outdated, so please refresh the screen to see the latest status.", "GroupNameAlreadyExistError": "This Transcriptionist Group name is already registered. Please register with another Transcriptionist Group name.", - "deleteFailedWorkflowAssigned": "TranscriptionistGroupの削除に失敗しました。Workflow画面でルーティングルールから対象TranscriptionistGroupを外してください。", - "deleteFailedCheckoutPermissionExisted": "TranscriptionistGroupの削除に失敗しました。Dictation画面でタスクのルーティングから対象TranscriptionistGroupを外してください。" + "deleteFailedWorkflowAssigned": "Failed to delete the Transcriptionist Group. Please remove the Transcriptionist Group from the routing rule from the Workflow tab.", + "deleteFailedCheckoutPermissionExisted": "Failed to delete the Transcriptionist Group. There is a task assigned to this Transcriptionist Group. Please reassign the task to another Transcriptionist or Transcriptionist Group from the Dictation tab." } }, "worktypeIdSetting": { @@ -520,8 +524,8 @@ "fileEmptyError": "File selection is required. Please select a file." }, "message": { - "deleteFailedWorkflowAssigned": "テンプレートファイルの削除に失敗しました。Workflow画面でルーティングルールから対象テンプレートファイルを外してください。", - "deleteFailedTaskAssigned": "テンプレートファイルの削除に失敗しました。Dictation画面で対象テンプレートファイルが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。" + "deleteFailedWorkflowAssigned": "Failed to delete the template file. Please remove the template file from the routing rules from the Workflow tab.", + "deleteFailedTaskAssigned": "Failed to delete the template file. There are tasks associated with this template. Please delete or mark the tasks as finished for tasks associated with this template. " } }, "partnerPage": { @@ -538,18 +542,18 @@ "partners": "Partners", "deleteAccount": "Delete Account", "editAccount": "Edit Account", - "accountInformation": "Account information", + "accountInformation": "Account Information", "primaryAdminInfo": "Primary administrator's information", - "adminName": "Admin Name", + "adminName": "Administrator‘s Name", "saveChanges": "Save Changes" }, "message": { "delegateNotAllowedError": "Actions on behalf of partner are not allowed. Please refresh the screen and check again.", "deleteFailedError": "Delegate operation failed. Please refresh the screen and check again.", "delegateCancelError": "The delegated operation has been terminated because permission for the delegated operation has been revoked.", - "partnerDeleteConfirm": "選択したアカウントを削除します。削除したアカウントは復元できませんが本当によろしいですか?対象アカウント:", - "partnerDeleteFailedError": "削除対象アカウントにLower layerアカウントが存在するため削除できません。Lower layerアカウントに対して削除、または現地法人以上のアカウントに階層構造の変更を依頼してください。", - "editFailedError": "パートナーアカウントの編集に失敗しました。画面を更新し、再度ご確認ください。" + "partnerDeleteConfirm": "Delete the selected account. A deleted account cannot be restored, are you sure? Target account:", + "partnerDeleteFailedError": "This account cannot be deleted because child accounts are associated with this parent account. You need to move or delete the child accounts prior to deleting this parent account. Please contact OMDS for more information.", + "editFailedError": "Since the screen information is not up to date, an inconsistency occurred when editing your partner account. Please refresh the screen and try again." } }, "accountPage": { @@ -571,7 +575,7 @@ "selectSecondaryAdministrator": "Select Secondary Administrator", "saveChanges": "Save Changes", "deleteAccount": "Delete Account", - "fileRetentionDays": "自動ファイル削除までの保持日数" + "fileRetentionDays": "Number of days files are kept before they are automatically deleted." }, "message": { "updateAccountFailedError": "Failed to save account information. Please refresh the screen and try again." @@ -631,18 +635,18 @@ "daysAnnotation": "Number of days from transcription finished to delete the files.", "days": "Days", "saveButton": "Save Settings", - "daysValidationError": "Daysには1~999の数字を入力してください。" + "daysValidationError": "Please enter a number between 1 and 999 for Days." } }, "changeOwnerPopup": { "message": { - "accountNotFoundError": "変更先のアカウントIDは存在しません。", - "hierarchyMismatchError": "パートナーアカウントの変更に失敗しました。\nLower layerの1階層上のアカウントを切り替え先に指定してください。", - "regionMismatchError": "パートナーアカウントの変更に失敗しました。\nLower layerと同じリージョンのアカウントを切り替え先に指定してください。", - "countryMismatchError": "パートナーアカウントの変更に失敗しました。\nLower layerと同じ国のアカウントを切り替え先に指定してください。" + "accountNotFoundError": "The account ID specified to change does not exist.", + "hierarchyMismatchError": "Failed to change partner account.\nPlease specify the account one level above the child account as the switch destination.。", + "regionMismatchError": "Failed to change partner account.\nPlease specify an account in the same region as the child account to switch to.", + "countryMismatchError": "Failed to change partner account.\nPlease specify an account in the same country as the child account to switch to." }, "label": { - "invalidInputError": "変更先アカウントIDには1~9999999の数字を入力してください。", + "invalidInputError": "Please enter a number between 1 and 9999999 for the destination account ID.", "title": "Change Owner", "upperLayerId": "Upper Layer ID", "lowerLayerId": "Lower Layer ID" diff --git a/dictation_client/src/translation/es.json b/dictation_client/src/translation/es.json index c6bfe33..2c8f63c 100644 --- a/dictation_client/src/translation/es.json +++ b/dictation_client/src/translation/es.json @@ -49,6 +49,10 @@ "newUser": "Nuevo usuario", "signUpButton": "Crear una cuenta", "logoAlt": "OM Dictation Management System in the Cloud" + }, + "text": { + "maintenanceNotificationTitle": "(es)サービス停止のお知らせ", + "maintenanceNotification": "Debido al mantenimiento del sistema, ODMS Cloud no estará disponible durante aproximadamente una hora a partir del 12 de junio a las 6:00 am, hora UTC. Pedimos disculpas por cualquier inconveniente causado durante el mantenimiento." } }, "signupPage": { @@ -129,18 +133,18 @@ "roleChangeError": "No se puede cambiar la función de usuario. La información mostrada puede estar desactualizada, así que actualice la pantalla para ver el estado más reciente.", "encryptionPasswordCorrectError": "La contraseña de cifrado no cumple con las reglas.", "alreadyLicenseDeallocatedError": "La licencia asignada ya ha sido cancelada. La información mostrada puede estar desactualizada, así que actualice la pantalla para ver el estado más reciente.", - "userDeletionLicenseActiveError": "(es)ユーザーの削除に失敗しました。対象ユーザーのライセンス割り当てを解除してください。", - "typistDeletionRoutingRuleError": "(es)ユーザーの削除に失敗しました。Workflow画面でルーティングルールから対象Transcriptionistを外してください。", - "adminUserDeletionError": "(es)ユーザーの削除に失敗しました。アカウント画面で対象ユーザーをPrimary/Secondary Administratorから外してください。", - "typistUserDeletionTranscriptionTaskError": "(es)ユーザーの削除に失敗しました。Dictation画面でタスクのルーティングから対象Transcriptionistを外してください。", - "authorUserDeletionTranscriptionTaskError": "(es)ユーザーの削除に失敗しました。Dictation画面で対象AuthorのAuthorIDが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。", - "typistUserDeletionTranscriptionistGroupError": "(es)ユーザーの削除に失敗しました。Workflow画面でTranscriptionistGroupから対象Transcriptionistを外してください。", - "authorDeletionRoutingRuleError": "(es)ユーザーの削除に失敗しました。Workflow画面でルーティングルールから対象AuthorのAuthorIDを外してください。", - "importSuccess": "(es)ユーザー一括追加を受け付けました。登録処理が完了次第メールが届きますのでご確認ください。", - "duplicateEmailError": "(es)以下の行のメールアドレスがCSV中で重複しています。", - "duplicateAuthorIdError": "(es)以下の行のAuthorIDがCSV中で重複しています。", - "overMaxUserError": "(es)一度に追加できるユーザーは100件までです。", - "invalidInputError": "(es)以下の行のユーザー情報が入力ルールに準拠していません。" + "userDeletionLicenseActiveError": "No se pudo eliminar el usuario. Desasignar la licencia al usuario.", + "typistDeletionRoutingRuleError": "No se pudo eliminar el usuario. Este usuario está registrado como Transcriptor que está incluido en las reglas de enrutamiento. Elimine al transcriptor de la regla de enrutamiento correspondiente en la pestaña Flujo de trabajo.", + "adminUserDeletionError": "No se pudo eliminar el usuario. Elimine el usuario del administrador principal o secundario desde la pestaña Cuenta.", + "typistUserDeletionTranscriptionTaskError": "No se pudo eliminar el usuario. Hay una tarea asignada a este transcriptor. Cambie la persona a cargo de la tarea a otro Transcriptor desde la pestaña Dictado.", + "authorUserDeletionTranscriptionTaskError": "No se pudo eliminar el usuario. Hay tareas creadas por este Autor que están incompletas. Elimine o marque las tareas como finalizadas para las tareas creadas por este autor.", + "typistUserDeletionTranscriptionistGroupError": "No se pudo eliminar el usuario. Elimine a este transcriptor del grupo de transcriptores de la pestaña Flujo de trabajo.", + "authorDeletionRoutingRuleError": "No se pudo eliminar el usuario. Elimine a este autor de las reglas de enrutamiento de la pestaña Flujo de trabajo.", + "importSuccess": "Hemos recibido su solicitud de registro de usuario masivo. Por favor revise su correo electrónico ya que recibirá un correo electrónico una vez que se complete el proceso de registro.", + "duplicateEmailError": "Las direcciones de correo electrónico de las siguientes líneas están duplicadas en el archivo CSV.", + "duplicateAuthorIdError": "El ID del autor en la siguiente línea está duplicado en el archivo CSV.", + "overMaxUserError": "Se pueden registrar hasta 100 usuarios a la vez mediante el registro de usuario mediante un archivo CSV.", + "invalidInputError": "La información del usuario en la siguiente línea no cumple con las reglas de entrada." }, "label": { "title": "Usuario", @@ -165,7 +169,7 @@ "addToGroup": "Agregar grupo (opcional)", "author": "Autor", "transcriptionist": "Transcriptor", - "encryption": "Codificación", + "encryption": "Cifrado", "prompt": "Solicitar", "emailVerified": "Correo electrónico verificado", "editUser": "Editar usuario", @@ -174,32 +178,32 @@ "none": "Ninguno", "encryptionPassword": "Contraseña", "encryptionPasswordTerm": "Configure su contraseña utilizando de 4 a 16 símbolos alfanuméricos y.", - "bulkImport": "(es)Bulk import", - "downloadCsv": "(es)Download CSV", - "importCsv": "(es)Import CSV", - "inputRules": "(es)Input rules", - "nameLabel": "(es)Name", - "emailAddressLabel": "(es)Email Address", - "roleLabel": "(es)Role", - "authorIdLabel": "(es)Author ID", - "autoRenewLabel": "(es)Auto Renew", - "notificationLabel": "(es)Notification", - "encryptionLabel": "(es)Encryption", - "encryptionPasswordLabel": "(es)Encryption Password", - "promptLabel": "(es)Prompt", - "addUsers": "(es)Add users" + "bulkImport": "Registro masivo de usuarios", + "downloadCsv": "Descargar archivo CSV de muestra", + "importCsv": "Importar archivo CSV", + "inputRules": "Reglas de entrada", + "nameLabel": "Nombre", + "emailAddressLabel": "Dirección de correo electrónico", + "roleLabel": "Role", + "authorIdLabel": "ID de autor", + "autoRenewLabel": "Asignación automática", + "notificationLabel": "Notificación", + "encryptionLabel": "Cifrado ", + "encryptionPasswordLabel": "Contraseña de cifrado", + "promptLabel": "Solicitar", + "addUsers": "Agregar usuario" }, "text": { - "downloadExplain": "(es)Download the csv format and enter it according to the rules below.", - "nameRule": "(es)Maximum 225 characters", - "emailAddressRule": "(es)Maximum 225 characters\nCannot use an email address that is already in use.", - "roleRule": "(es)None : 0\nAuthor : 1\nTranscriptionist : 2", - "authorIdRule": "(es)Required only when Role=Author(1)\nMaximum 16 characters\nOnly uppercase alphanumeric characters and \"_\" can be entered.\nCannot use an Author ID that is already in use.", - "autoRenewRule": "(es)0 or 1", - "notificationRule": "(es)0 or 1", - "encryptionRule": "(es)Required only when Role=Author(1)\n0 or 1", - "encryptionPasswordRule": "(es)Required only when Role=Author(1) and Encryption=ON(1)\nOnly 4 to 16 letters, numbers, and symbols can be entered.", - "promptRule": "(es)Required only when Role=Author(1)\n0 or 1" + "downloadExplain": "Descargue el archivo CSV de muestra y aplique la información requerida de acuerdo con las reglas siguientes.", + "nameRule": "> Máximo 225 caracteres", + "emailAddressRule": "> Máximo 225\n> No se puede utilizar una dirección de correo electrónico que ya esté en uso.", + "roleRule": "Ninguno : 0\nAutor : 1\nTranscriptor : 2", + "authorIdRule": "> Se puede establecer un valor en este elemento sólo cuando el \"Rol\" es \"Autor\".\n> Máximo 16 caracteres\n> Sólo se pueden utilizar caracteres alfanuméricos en mayúsculas y \"_\".\n> No se puede utilizar una ID de autor que ya esté en uso.", + "autoRenewRule": "> Valor : 0 o 1 (1=ENCENDIDO)", + "notificationRule": "> Valor : 0 o 1 (1=ENCENDIDO)", + "encryptionRule": "> Se puede establecer un valor en este elemento sólo cuando el \"Rol\" es \"Autor\".\n> Valor : 0 o 1 (1=ENCENDIDO)", + "encryptionPasswordRule": "> Se puede establecer un valor en este elemento solo cuando el \"Rol\" es \"Autor\" y el cifrado está activado.\n> Sólo se pueden ingresar de 4 a 16 letras, números y símbolos.", + "promptRule": "> Se puede establecer un valor en este elemento sólo cuando el \"Rol\" es \"Autor\".\n> Valor : 0 o 1 (1=ENCENDIDO)" } }, "LicenseSummaryPage": { @@ -220,10 +224,10 @@ "storageAvailable": "Almacenamiento no disponible (cantidad excedida)", "licenseLabel": "Licencia", "storageLabel": "Almacenamiento", - "storageUnavailableCheckbox": "(es)Storage Unavailable" + "storageUnavailableCheckbox": "Restringir el uso de la cuenta" }, "message": { - "storageUnavalableSwitchingConfirm": "(es)対象アカウントのストレージ使用制限状態を変更します。よろしいですか?" + "storageUnavalableSwitchingConfirm": "¿Está seguro de que desea cambiar el estado de uso del almacenamiento de esta cuenta?" } }, "licenseOrderPage": { @@ -251,12 +255,12 @@ "taskNotEditable": "No se puede cambiar el transcriptor porque la transcripción ya está en curso o el archivo no existe. Actualice la pantalla y verifique el estado más reciente.", "backupFailedError": "El proceso de \"Copia de seguridad de archivos\" ha fallado. Por favor, inténtelo de nuevo más tarde. Si el error continúa, comuníquese con el administrador del sistema.", "cancelFailedError": "No se pudieron eliminar los dictados. Actualice su pantalla e inténtelo nuevamente.", - "deleteFailedError": "(es)タスクの削除に失敗しました。画面を更新し、再度ご確認ください。", + "deleteFailedError": "No se pudo eliminar la tarea. Actualice la pantalla y verifique nuevamente.", "licenseNotAssignedError": "La transcripción no es posible porque no se ha asignado una licencia válida. Solicite a su administrador que le asigne una licencia válida.", "licenseExpiredError": "La transcripción no es posible porque su licencia ha caducado. Solicite a su administrador que le asigne una licencia válida.", - "fileAlreadyDeletedError": "(es)既に削除された音声ファイルが含まれています。画面を更新し、再度ご確認ください", - "fileRenameFailedError": "(es)ファイル名の変更に失敗しました。画面を更新し、再度ご確認ください。", - "fileNameAleadyExistsError": "(es)このファイル名は既に登録されています。他のファイル名で登録してください。" + "fileAlreadyDeletedError": "La información de la pantalla no está actualizada, contiene archivos de audio que ya han sido eliminados. Actualice la pantalla y seleccione los archivos que desea eliminar nuevamente.", + "fileRenameFailedError": "Dado que la información de la pantalla no está actualizada, se produjo una inconsistencia en la información del archivo y no se pudo cambiar el nombre del archivo. Actualice la pantalla e inténtelo de nuevo.", + "fileNameAleadyExistsError": "Este nombre de archivo ya está registrado. Regístrese con un nombre de archivo diferente." }, "label": { "title": "Dictado", @@ -264,7 +268,7 @@ "jobNumber": "Número de trabajo", "status": "Estado", "priority": "Prioridad", - "encryption": "Codificación", + "encryption": "Cifrado ", "authorId": "ID de autor", "workType": "ID de tipo de trabajo", "fileName": "Nombre de archivo", @@ -298,13 +302,13 @@ "changeTranscriptionist": "Cambiar transcriptor", "deleteDictation": "Borrar dictado", "selectedTranscriptionist": "Transcriptor seleccionado", - "poolTranscriptionist": "Lista de transcriptor", + "poolTranscriptionist": "Lista de transcriptores", "fileBackup": "Copia de seguridad de archivos", "downloadForBackup": "Descargar para respaldo", "applications": "Aplicación de escritorio", "cancelDictation": "Cancelar transcripción", - "rawFileName": "(es)Raw File Name", - "fileNameSave": "(es)Save" + "rawFileName": "Nombre de archivo original", + "fileNameSave": "Ejecutar cambio de nombre de archivo" } }, "cardLicenseIssuePopupPage": { @@ -374,7 +378,7 @@ "issueRequesting": "Licencias en Pedido", "viewDetails": "Ver detalles", "accounts": "cuentas", - "changeOwnerButton": "(es)Change Owner" + "changeOwnerButton": "Change Owner" } }, "orderHistoriesPage": { @@ -440,13 +444,13 @@ "templateOptional": "Plantilla (Opcional)", "editRule": "Editar regla", "selected": "Transcriptor seleccionado", - "pool": "Lista de transcriptor", + "pool": "Lista de transcriptores", "selectAuthor": "Seleccionar ID de autor", "selectWorktypeId": "Seleccionar ID de tipo de trabajo", "selectTemplate": "Seleccionar Plantilla" }, "message": { - "selectedTypistEmptyError": "No se ha seleccionado el transcriptor o el grupo de transcriptores. Seleccione uno o más de la lista de transcripción.", + "selectedTypistEmptyError": "No se ha seleccionado el transcriptor o el grupo de transcriptores. Seleccione uno o más de la lista de transcriptores.", "workflowConflictError": "Ya se ha registrado una regla de enrutamiento con la combinación AuthorID y WorktypeID especificada. Regístrese con una combinación diferente.", "inputEmptyError": "Campo obligatorio", "saveFailedError": "No se pudo guardar la regla de enrutamiento. Actualice la pantalla e inténtelo de nuevo." @@ -460,7 +464,7 @@ "addTypistGroup": "Agregar grupo transcriptor", "transcriptionist": "Transcriptor", "selected": "Transcriptor seleccionado", - "pool": "Lista de transcriptor", + "pool": "Lista de transcriptores", "add": "Añadir", "remove": "Eliminar", "editTypistGroup": "Editar grupo transcriptor" @@ -469,8 +473,8 @@ "selectedTypistEmptyError": "Se deben seleccionar uno o más transcriptores para guardar un grupo de transcripción.", "groupSaveFailedError": "El grupo transcriptor no se pudo salvar. La información mostrada puede estar desactualizada. Así que actualice la pantalla para ver el estado más reciente.", "GroupNameAlreadyExistError": "El nombre de este grupo transcriptor ya está registrado. Regístrese con otro nombre de grupo transcriptor.", - "deleteFailedWorkflowAssigned": "(es)TranscriptionistGroupの削除に失敗しました。Workflow画面でルーティングルールから対象TranscriptionistGroupを外してください。", - "deleteFailedCheckoutPermissionExisted": "(es)TranscriptionistGroupの削除に失敗しました。Dictation画面でタスクのルーティングから対象TranscriptionistGroupを外してください。" + "deleteFailedWorkflowAssigned": "No se pudo eliminar el grupo transcriptor. Elimine el grupo de transcriptores de la regla de enrutamiento de la pestaña Flujo de trabajo.", + "deleteFailedCheckoutPermissionExisted": "No se pudo eliminar el grupo transcriptor. Hay una tarea asignada a este Grupo Transcripcionista. Reasigne la tarea a otro transcriptor o grupo de transcriptores desde la pestaña Dictado." } }, "worktypeIdSetting": { @@ -520,8 +524,8 @@ "fileEmptyError": "Se requiere selección de archivos. Por favor seleccione un archivo." }, "message": { - "deleteFailedWorkflowAssigned": "(es)テンプレートファイルの削除に失敗しました。Workflow画面でルーティングルールから対象テンプレートファイルを外してください。", - "deleteFailedTaskAssigned": "(es)テンプレートファイルの削除に失敗しました。Dictation画面で対象テンプレートファイルが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。" + "deleteFailedWorkflowAssigned": "No se pudo eliminar el archivo de plantilla. Elimine el archivo de plantilla adjunto a la regla de enrutamiento de la pestaña Flujo de trabajo.", + "deleteFailedTaskAssigned": "No se pudo eliminar el archivo de plantilla. Hay tareas asociadas con esta plantilla. Elimine o marque las tareas como finalizadas para las tareas asociadas con esta plantilla." } }, "partnerPage": { @@ -537,19 +541,19 @@ "dealerManagement": "Permitir que el distribuidor realice los cambios", "partners": "Socios", "deleteAccount": "Borrar cuenta", - "editAccount": "(es)Edit Account", - "accountInformation": "(es)Account information", - "primaryAdminInfo": "(es)Primary administrator's information", - "adminName": "(es)Admin Name", - "saveChanges": "(es)Save Changes" + "editAccount": "Editar cuenta", + "accountInformation": "Información de la cuenta", + "primaryAdminInfo": "Información del administrador principal", + "adminName": "Nombre del administrador", + "saveChanges": "Guardar cambios" }, "message": { "delegateNotAllowedError": "No se permiten acciones en nombre del socio. Actualice la pantalla y verifique nuevamente.", "deleteFailedError": "La operación del delegado falló. Actualice la pantalla y verifique nuevamente.", "delegateCancelError": "La operación delegada finalizó porque se revocó el permiso para la operación delegada.", - "partnerDeleteConfirm": "(es)選択したアカウントを削除します。削除したアカウントは復元できませんが本当によろしいですか?対象アカウント:", - "partnerDeleteFailedError": "(es)削除対象アカウントにLower layerアカウントが存在するため削除できません。Lower layerアカウントに対して削除、または現地法人以上のアカウントに階層構造の変更を依頼してください。", - "editFailedError": "(es)パートナーアカウントの編集に失敗しました。画面を更新し、再度ご確認ください。" + "partnerDeleteConfirm": "Eliminar la cuenta seleccionada. Una cuenta eliminada no se puede restaurar, ¿estás seguro? Cuenta objetivo:", + "partnerDeleteFailedError": "Esta cuenta no se puede eliminar porque las cuentas infantiles están asociadas con esta cuenta principal. Debe mover o eliminar las cuentas infantiles antes de eliminar esta cuenta principal. Comuníquese con OMDS para obtener más información.", + "editFailedError": "Dado que la información de la pantalla no está actualizada, se produjo una inconsistencia al editar su cuenta de socio. Actualice la pantalla e inténtelo de nuevo." } }, "accountPage": { @@ -571,7 +575,7 @@ "selectSecondaryAdministrator": "Seleccionar administrador secundario", "saveChanges": "Guardar cambios", "deleteAccount": "Borrar cuenta", - "fileRetentionDays": "(es)自動ファイル削除までの保持日数" + "fileRetentionDays": "Número de días que se conservan los archivos antes de que se eliminen automáticamente." }, "message": { "updateAccountFailedError": "No se pudo guardar la información de la cuenta. Actualice la pantalla e inténtelo de nuevo." @@ -626,26 +630,26 @@ }, "fileDeleteSettingPopup": { "label": { - "title": "(es)Auto File Delete Setting", - "autoFileDeleteCheck": "(es)Auto file delete", - "daysAnnotation": "(es)Number of days from transcription finished to delete the files.", - "days": "(es)Days", - "saveButton": "(es)Save Settings", - "daysValidationError": "(es)Daysには1~999の数字を入力してください。" + "title": "Configuración de eliminación automática de archivos", + "autoFileDeleteCheck": "Eliminación automática de archivos", + "daysAnnotation": "Número de días desde que finalizó la transcripción para eliminar los archivos.", + "days": "Días", + "saveButton": "Guardar ajustes", + "daysValidationError": "Ingrese un número entre 1 y 999 para Días." } }, "changeOwnerPopup": { "message": { - "accountNotFoundError": "(es)変更先のアカウントIDは存在しません。", - "hierarchyMismatchError": "(es)パートナーアカウントの変更に失敗しました。\nLower layerの1階層上のアカウントを切り替え先に指定してください。", - "regionMismatchError": "(es)パートナーアカウントの変更に失敗しました。\nLower layerと同じリージョンのアカウントを切り替え先に指定してください。", - "countryMismatchError": "(es)パートナーアカウントの変更に失敗しました。\nLower layerと同じ国のアカウントを切り替え先に指定してください。" + "accountNotFoundError": "The account ID specified to change does not exist.", + "hierarchyMismatchError": "Failed to change partner account.\nPlease specify the account one level above the child account as the switch destination.。", + "regionMismatchError": "Failed to change partner account.\nPlease specify an account in the same region as the child account to switch to.", + "countryMismatchError": "Failed to change partner account.\nPlease specify an account in the same country as the child account to switch to." }, "label": { - "invalidInputError": "(es)変更先アカウントIDには1~9999999の数字を入力してください。", - "title": "(es)Change Owner", - "upperLayerId": "(es)Upper Layer ID", - "lowerLayerId": "(es)Lower Layer ID" + "invalidInputError": "Please enter a number between 1 and 9999999 for the destination account ID.", + "title": "Change Owner", + "upperLayerId": "Upper Layer ID", + "lowerLayerId": "Lower Layer ID" } } } \ No newline at end of file diff --git a/dictation_client/src/translation/fr.json b/dictation_client/src/translation/fr.json index 071295b..9ee60f0 100644 --- a/dictation_client/src/translation/fr.json +++ b/dictation_client/src/translation/fr.json @@ -49,6 +49,10 @@ "newUser": "Nouvel utilisateur", "signUpButton": "Créer un compte", "logoAlt": "OM Dictation Management System in the Cloud" + }, + "text": { + "maintenanceNotificationTitle": "(fr)サービス停止のお知らせ", + "maintenanceNotification": "En raison de la maintenance du système, ODMS Cloud sera indisponible pendant environ une heure à partir du 12 juin à 6h00, heure UTC. Nous nous excusons pour tout inconvénient causé lors de la maintenance." } }, "signupPage": { @@ -129,18 +133,18 @@ "roleChangeError": "Impossible de modifier le rôle de l'utilisateur. Les informations affichées peuvent être obsolètes, veuillez donc actualiser l'écran pour voir le dernier statut.", "encryptionPasswordCorrectError": "Le mot de passe de cryptage n'est pas conforme aux règles.", "alreadyLicenseDeallocatedError": "La licence attribuée a déjà été annulée. Les informations affichées peuvent être obsolètes, veuillez donc actualiser l'écran pour voir le dernier statut.", - "userDeletionLicenseActiveError": "(fr)ユーザーの削除に失敗しました。対象ユーザーのライセンス割り当てを解除してください。", - "typistDeletionRoutingRuleError": "(fr)ユーザーの削除に失敗しました。Workflow画面でルーティングルールから対象Transcriptionistを外してください。", - "adminUserDeletionError": "(fr)ユーザーの削除に失敗しました。アカウント画面で対象ユーザーをPrimary/Secondary Administratorから外してください。", - "typistUserDeletionTranscriptionTaskError": "(fr)ユーザーの削除に失敗しました。Dictation画面でタスクのルーティングから対象Transcriptionistを外してください。", - "authorUserDeletionTranscriptionTaskError": "(fr)ユーザーの削除に失敗しました。Dictation画面で対象AuthorのAuthorIDが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。", - "typistUserDeletionTranscriptionistGroupError": "(fr)ユーザーの削除に失敗しました。Workflow画面でTranscriptionistGroupから対象Transcriptionistを外してください。", - "authorDeletionRoutingRuleError": "(fr)ユーザーの削除に失敗しました。Workflow画面でルーティングルールから対象AuthorのAuthorIDを外してください。", - "importSuccess": "(fr)ユーザー一括追加を受け付けました。登録処理が完了次第メールが届きますのでご確認ください。", - "duplicateEmailError": "(fr)以下の行のメールアドレスがCSV中で重複しています。", - "duplicateAuthorIdError": "(fr)以下の行のAuthorIDがCSV中で重複しています。", - "overMaxUserError": "(fr)一度に追加できるユーザーは100件までです。", - "invalidInputError": "(fr)以下の行のユーザー情報が入力ルールに準拠していません。" + "userDeletionLicenseActiveError": "Échec de la suppression de l'utilisateur. Veuillez annuler l'attribution de la licence à l'utilisateur.", + "typistDeletionRoutingRuleError": "Échec de la suppression de l'utilisateur. Cet utilisateur est enregistré en tant que Transcripteur qui est inclus dans les règles de routage. Supprimez le transcripteur de la règle de routage correspondante dans l'onglet Workflow. Routing-Regel auf der Registerkarte „Workflow“.", + "adminUserDeletionError": "Échec de la suppression de l'utilisateur. Veuillez supprimer l'utilisateur de l'administrateur principal ou secondaire de l'onglet Compte.", + "typistUserDeletionTranscriptionTaskError": "Échec de la suppression de l'utilisateur. Une tâche est assignée à ce transcripteur. Veuillez remplacer la personne en charge de la tâche par un autre transcripteur depuis l'onglet Dictée.", + "authorUserDeletionTranscriptionTaskError": "Échec de la suppression de l'utilisateur. Certaines tâches créées par cet auteur sont incomplètes. Veuillez supprimer ou marquer les tâches comme terminées pour les tâches créées par cet auteur.", + "typistUserDeletionTranscriptionistGroupError": "Échec de la suppression de l'utilisateur. Veuillez supprimer ce transcripteur du groupe transcripteur de l'onglet Workflow.", + "authorDeletionRoutingRuleError": "Échec de la suppression de l'utilisateur. Supprimez cet auteur des règles de routage dans l'onglet Workflow.", + "importSuccess": "Nous avons reçu votre demande d'enregistrement groupé d'utilisateur. Veuillez vérifier votre courrier électronique car vous recevrez un e-mail une fois le processus d'inscription terminé.", + "duplicateEmailError": "Les adresses email des lignes suivantes sont dupliquées dans le fichier CSV.", + "duplicateAuthorIdError": "L'ID d'auteur dans la ligne suivante est dupliqué dans le fichier CSV.", + "overMaxUserError": "Jusqu'à 100 utilisateurs peuvent être enregistrés en même temps par enregistrement d'utilisateur via un fichier CSV.", + "invalidInputError": "Les informations utilisateur de la ligne suivante ne sont pas conformes aux règles de saisie." }, "label": { "title": "Utilisateur", @@ -174,32 +178,32 @@ "none": "Aucun", "encryptionPassword": "Mot de passe", "encryptionPasswordTerm": "Veuillez définir votre mot de passe en utilisant 4 à 16 caractères alphanumériques et symboles.", - "bulkImport": "(fr)Bulk import", - "downloadCsv": "(fr)Download CSV", - "importCsv": "(fr)Import CSV", - "inputRules": "(fr)Input rules", - "nameLabel": "(fr)Name", - "emailAddressLabel": "(fr)Email Address", - "roleLabel": "(fr)Role", - "authorIdLabel": "(fr)Author ID", - "autoRenewLabel": "(fr)Auto Renew", - "notificationLabel": "(fr)Notification", - "encryptionLabel": "(fr)Encryption", - "encryptionPasswordLabel": "(fr)Encryption Password", - "promptLabel": "(fr)Prompt", - "addUsers": "(fr)Add users" + "bulkImport": "Inscription des utilisateurs de groupe", + "downloadCsv": "Télécharger un exemple de fichier CSV", + "importCsv": "Importer un fichier CSV", + "inputRules": "Règles de saisie", + "nameLabel": "Nom", + "emailAddressLabel": "Adresse e-mail", + "roleLabel": "Rôle", + "authorIdLabel": "Identifiant Auteur", + "autoRenewLabel": "Assignation automatique", + "notificationLabel": "Notification", + "encryptionLabel": "Chiffrement", + "encryptionPasswordLabel": "Mot de passe de chiffrement", + "promptLabel": "Invite", + "addUsers": "Ajouter un utilisateur" }, "text": { - "downloadExplain": "(fr)Download the csv format and enter it according to the rules below.", - "nameRule": "(fr)Maximum 225 characters", - "emailAddressRule": "(fr)Maximum 225 characters\nCannot use an email address that is already in use.", - "roleRule": "(fr)None : 0\nAuthor : 1\nTranscriptionist : 2", - "authorIdRule": "(fr)Required only when Role=Author(1)\nMaximum 16 characters\nOnly uppercase alphanumeric characters and \"_\" can be entered.\nCannot use an Author ID that is already in use.", - "autoRenewRule": "(fr)0 or 1", - "notificationRule": "(fr)0 or 1", - "encryptionRule": "(fr)Required only when Role=Author(1)\n0 or 1", - "encryptionPasswordRule": "(fr)Required only when Role=Author(1) and Encryption=ON(1)\nOnly 4 to 16 letters, numbers, and symbols can be entered.", - "promptRule": "(fr)Required only when Role=Author(1)\n0 or 1" + "downloadExplain": "Veuillez télécharger l'exemple de fichier CSV et appliquer les informations requises conformément aux règles ci-dessous.", + "nameRule": "> 225 caractères maximum", + "emailAddressRule": "> 225 caractères maximum\n> Impossible d'utiliser une adresse email déjà utilisée.", + "roleRule": "Aucun : 0\nAuteur : 1\nTranscriptionniste : 2", + "authorIdRule": "> Une valeur peut être définie dans cet élément uniquement lorsque le « Rôle » est « Auteur »\n> 16 caractères maximum\n> Seuls les caractères alphanumériques majuscules et \"_\" peuvent être utilisés.\n> Impossible d'utiliser un identifiant d'auteur déjà utilisé.", + "autoRenewRule": "> Valeur : 0 or 1 (1=ON)", + "notificationRule": "> Valeur : 0 or 1 (1=ON)", + "encryptionRule": "> Une valeur peut être définie dans cet élément uniquement lorsque le « Rôle » est « Auteur ».\n> Valeur : 0 ou 1 (1=ON)", + "encryptionPasswordRule": "> Une valeur peut être définie dans cet élément uniquement lorsque le « Rôle » est « Auteur » et que le cryptage est activé.\n> Seuls 4 à 16 lettres, chiffres et symboles peuvent être saisis.", + "promptRule": "> Une valeur peut être définie dans cet élément uniquement lorsque le « Rôle » est « Auteur ».\n> Valeur : 0 ou 1 (1=ON)" } }, "LicenseSummaryPage": { @@ -220,10 +224,10 @@ "storageAvailable": "Stockage indisponible (montant dépassée)", "licenseLabel": "Licence", "storageLabel": "Stockage", - "storageUnavailableCheckbox": "(fr)Storage Unavailable" + "storageUnavailableCheckbox": "Restreindre l'utilisation du compte" }, "message": { - "storageUnavalableSwitchingConfirm": "(fr)対象アカウントのストレージ使用制限状態を変更します。よろしいですか?" + "storageUnavalableSwitchingConfirm": "Êtes-vous sûr de vouloir modifier l'état d'utilisation du stockage pour ce compte ?" } }, "licenseOrderPage": { @@ -251,12 +255,12 @@ "taskNotEditable": "Le transcripteur ne peut pas être changé car la transcription est déjà en cours ou le fichier n'existe pas. Veuillez actualiser l'écran et vérifier le dernier statut.", "backupFailedError": "Le processus de « Sauvegarde de fichier » a échoué. Veuillez réessayer plus tard. Si l'erreur persiste, contactez votre administrateur système.", "cancelFailedError": "Échec de la suppression des dictées. Veuillez actualiser votre écran et réessayer.", - "deleteFailedError": "(fr)タスクの削除に失敗しました。画面を更新し、再度ご確認ください。", + "deleteFailedError": "Échec de la suppression de la tâche. Veuillez actualiser l'écran et vérifier à nouveau.", "licenseNotAssignedError": "La transcription n'est pas possible car aucune licence valide n'a été attribuée. Veuillez demander à votre administrateur d'attribuer une licence valide.", "licenseExpiredError": "La transcription n'est pas possible car votre licence est expirée. Veuillez demander à votre administrateur de vous attribuer une licence valide.", - "fileAlreadyDeletedError": "(fr)既に削除された音声ファイルが含まれています。画面を更新し、再度ご確認ください", - "fileRenameFailedError": "(fr)ファイル名の変更に失敗しました。画面を更新し、再度ご確認ください。", - "fileNameAleadyExistsError": "(fr)このファイル名は既に登録されています。他のファイル名で登録してください。" + "fileAlreadyDeletedError": "Les informations à l'écran ne sont pas à jour, elles contiennent des fichiers audio qui ont déjà été supprimés. Veuillez actualiser l'écran et sélectionner à nouveau les fichiers à supprimer.", + "fileRenameFailedError": "Étant donné que les informations à l'écran ne sont pas à jour, une incohérence s'est produite dans les informations du fichier et il n'a pas été possible de renommer le fichier. Veuillez actualiser l'écran et réessayer.", + "fileNameAleadyExistsError": "Ce nom de fichier est déjà enregistré. Veuillez vous inscrire avec un nom de fichier différent." }, "label": { "title": "Dictées", @@ -298,13 +302,13 @@ "changeTranscriptionist": "Changer de transcriptionniste ", "deleteDictation": "Supprimer la dictée", "selectedTranscriptionist": "Transcriptionniste sélectionné", - "poolTranscriptionist": "Liste de transcriptionniste", + "poolTranscriptionist": "Liste des transcripteurs", "fileBackup": "Sauvegarde de fichiers", "downloadForBackup": "Télécharger pour sauvegarde", "applications": "Application de bureau", "cancelDictation": "Annuler la transcription", - "rawFileName": "(fr)Raw File Name", - "fileNameSave": "(fr)Save" + "rawFileName": "Nom du fichier d'origine", + "fileNameSave": "Exécuter le changement de nom du fichier" } }, "cardLicenseIssuePopupPage": { @@ -374,7 +378,7 @@ "issueRequesting": "Licences en commande", "viewDetails": "Voir les détails", "accounts": "comptes", - "changeOwnerButton": "(fr)Change Owner" + "changeOwnerButton": "Change Owner" } }, "orderHistoriesPage": { @@ -440,13 +444,13 @@ "templateOptional": "Masque (Facultatif)", "editRule": "Modifier la règle", "selected": "Transcriptionniste sélectionné", - "pool": "Liste de transcriptionniste", + "pool": "Liste des transcripteurs", "selectAuthor": "Sélectionner le Identifiant Auteur", "selectWorktypeId": "Sélectionner le Identifiant du Type de travail", "selectTemplate": "Sélectionner le Masque" }, "message": { - "selectedTypistEmptyError": "Transcriptionist ou Transcriptionist Group n’a pas été sélectionné. Veuillez en sélectionner un ou plusieurs dans la liste de transcription.", + "selectedTypistEmptyError": "Transcriptionist ou Transcriptionist Group n’a pas été sélectionné. Veuillez en sélectionner un ou plusieurs dans la liste de transcripteurs.", "workflowConflictError": "Une règle de routage a déjà été enregistrée avec la combinaison AuthorID et WorktypeID spécifiée. Veuillez vous inscrire avec une combinaison différente.", "inputEmptyError": "Champ obligatoire", "saveFailedError": "Échec de l'enregistrement de la règle de routage. Veuillez actualiser l'écran et réessayer." @@ -460,7 +464,7 @@ "addTypistGroup": "Ajouter un groupe de transcripteurs", "transcriptionist": "Transcriptionniste", "selected": "Transcriptionniste sélectionné", - "pool": "Liste de transcriptionniste", + "pool": "Liste des transcripteurs", "add": "Ajouter", "remove": "Supprimer", "editTypistGroup": "Modifier le groupe de transcripteurs" @@ -469,8 +473,8 @@ "selectedTypistEmptyError": "Un ou plusieurs transcripteurs doivent être sélectionnés pour enregistrer un groupe de transcription.", "groupSaveFailedError": "Le groupe de transcriptionniste n'a pas pu être enregistré. Les informations affichées peuvent être obsolètes, veuillez donc actualiser l'écran pour voir le dernier statut.", "GroupNameAlreadyExistError": "Ce nom de groupe transcripteur est déjà enregistré. Veuillez vous inscrire avec un autre nom de groupe transcripteur.", - "deleteFailedWorkflowAssigned": "(fr)TranscriptionistGroupの削除に失敗しました。Workflow画面でルーティングルールから対象TranscriptionistGroupを外してください。", - "deleteFailedCheckoutPermissionExisted": "(fr)TranscriptionistGroupの削除に失敗しました。Dictation画面でタスクのルーティングから対象TranscriptionistGroupを外してください。" + "deleteFailedWorkflowAssigned": "Échec de la suppression du groupe transcripteur. Veuillez supprimer le groupe Transcriptionist de la règle de routage depuis l'onglet Workflow.", + "deleteFailedCheckoutPermissionExisted": "Échec de la suppression du groupe transcripteur. Une tâche est assignée à ce groupe transcripteur. Veuillez réaffecter la tâche à un autre transcripteur ou groupe de transcripteurs à partir de l'onglet Dictée." } }, "worktypeIdSetting": { @@ -520,8 +524,8 @@ "fileEmptyError": "La sélection de fichiers est requise. Veuillez sélectionner un fichier." }, "message": { - "deleteFailedWorkflowAssigned": "(fr)テンプレートファイルの削除に失敗しました。Workflow画面でルーティングルールから対象テンプレートファイルを外してください。", - "deleteFailedTaskAssigned": "(fr)テンプレートファイルの削除に失敗しました。Dictation画面で対象テンプレートファイルが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。" + "deleteFailedWorkflowAssigned": "Échec de la suppression du fichier modèle. Veuillez supprimer le fichier modèle joint à la règle de routage de l'onglet Workflow.", + "deleteFailedTaskAssigned": "Échec de la suppression du fichier modèle. Certaines tâches sont associées à ce modèle. Veuillez supprimer ou marquer les tâches comme terminées pour les tâches associées à ce modèle." } }, "partnerPage": { @@ -537,19 +541,19 @@ "dealerManagement": "Autoriser le revendeur à modifier les paramètres", "partners": "Partenaires", "deleteAccount": "Supprimer le compte", - "editAccount": "(fr)Edit Account", - "accountInformation": "(fr)Account information", - "primaryAdminInfo": "(fr)Primary administrator's information", - "adminName": "(fr)Admin Name", - "saveChanges": "(fr)Save Changes" + "editAccount": "Modifier le compte", + "accountInformation": "Information sur le compte", + "primaryAdminInfo": "Informations sur l'administrateur principal", + "adminName": "Nom de l'administrateur", + "saveChanges": "Sauvegarder les modifications" }, "message": { "delegateNotAllowedError": "Les actions au nom du partenaire ne sont pas autorisées. Veuillez actualiser l'écran et vérifier à nouveau.", "deleteFailedError": "L’opération de délégation a échoué. Veuillez actualiser l'écran et vérifier à nouveau.", "delegateCancelError": "L'opération déléguée a été interrompue car l'autorisation pour l'opération déléguée a été révoquée.", - "partnerDeleteConfirm": "(fr)選択したアカウントを削除します。削除したアカウントは復元できませんが本当によろしいですか?対象アカウント:", - "partnerDeleteFailedError": "(fr)削除対象アカウントにLower layerアカウントが存在するため削除できません。Lower layerアカウントに対して削除、または現地法人以上のアカウントに階層構造の変更を依頼してください。", - "editFailedError": "(fr)パートナーアカウントの編集に失敗しました。画面を更新し、再度ご確認ください。" + "partnerDeleteConfirm": "Supprimez le compte sélectionné. Un compte supprimé ne peut pas être restauré, en êtes-vous sûr ? Compte cible :", + "partnerDeleteFailedError": "Ce compte ne peut pas être supprimé car des comptes enfants sont associés à ce compte parent. Vous devez déplacer ou supprimer les comptes enfants avant de supprimer ce compte parent. Veuillez contacter l'OMDS pour plus d'informations.", + "editFailedError": "Les informations à l'écran n'étant pas à jour, une incohérence est survenue lors de la modification de votre compte partenaire. Veuillez actualiser l'écran et réessayer." } }, "accountPage": { @@ -571,7 +575,7 @@ "selectSecondaryAdministrator": "Sélectionner le administrateur secondaire", "saveChanges": "Sauvegarder les modifications", "deleteAccount": "Supprimer le compte", - "fileRetentionDays": "(fr)自動ファイル削除までの保持日数" + "fileRetentionDays": "Nombre de jours pendant lesquels les fichiers sont conservés avant d'être automatiquement supprimés." }, "message": { "updateAccountFailedError": "Échec de l'enregistrement des informations du compte. Veuillez actualiser l'écran et réessayer." @@ -626,26 +630,26 @@ }, "fileDeleteSettingPopup": { "label": { - "title": "(fr)Auto File Delete Setting", - "autoFileDeleteCheck": "(fr)Auto file delete", - "daysAnnotation": "(fr)Number of days from transcription finished to delete the files.", - "days": "(fr)Days", - "saveButton": "(fr)Save Settings", - "daysValidationError": "(fr)Daysには1~999の数字を入力してください。" + "title": "Paramètre de suppression automatique de fichiers", + "autoFileDeleteCheck": "Suppression automatique des fichiers", + "daysAnnotation": "Número de días desde que finalizó la transcripción para eliminar los archivos.", + "days": "Jours", + "saveButton": "Enregistrer les paramètres", + "daysValidationError": "Veuillez saisir un nombre compris entre 1 et 999 pour les jours." } }, "changeOwnerPopup": { "message": { - "accountNotFoundError": "(fr)変更先のアカウントIDは存在しません。", - "hierarchyMismatchError": "(fr)パートナーアカウントの変更に失敗しました。\nLower layerの1階層上のアカウントを切り替え先に指定してください。", - "regionMismatchError": "(fr)パートナーアカウントの変更に失敗しました。\nLower layerと同じリージョンのアカウントを切り替え先に指定してください。", - "countryMismatchError": "(fr)パートナーアカウントの変更に失敗しました。\nLower layerと同じ国のアカウントを切り替え先に指定してください。" + "accountNotFoundError": "The account ID specified to change does not exist.", + "hierarchyMismatchError": "Failed to change partner account.\nPlease specify the account one level above the child account as the switch destination.。", + "regionMismatchError": "Failed to change partner account.\nPlease specify an account in the same region as the child account to switch to.", + "countryMismatchError": "Failed to change partner account.\nPlease specify an account in the same country as the child account to switch to." }, "label": { - "invalidInputError": "(fr)変更先アカウントIDには1~9999999の数字を入力してください。", - "title": "(fr)Change Owner", - "upperLayerId": "(fr)Upper Layer ID", - "lowerLayerId": "(fr)Lower Layer ID" + "invalidInputError": "Please enter a number between 1 and 9999999 for the destination account ID.", + "title": "Change Owner", + "upperLayerId": "Upper Layer ID", + "lowerLayerId": "Lower Layer ID" } } } \ No newline at end of file From 4dac420bedad3d26993aedb32d4a400b1a870b99 Mon Sep 17 00:00:00 2001 From: "SAITO-PC-3\\saito.k" Date: Wed, 15 May 2024 17:00:20 +0900 Subject: [PATCH 9/9] =?UTF-8?q?typeORM=E3=81=AE=E3=83=A2=E3=82=B8=E3=83=A5?= =?UTF-8?q?=E3=83=BC=E3=83=AB=E8=A8=AD=E5=AE=9A=E3=81=8C=E6=BC=8F=E3=82=8C?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=81=9F=E3=81=AE=E3=81=A7=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dictation_server/src/app.module.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dictation_server/src/app.module.ts b/dictation_server/src/app.module.ts index f35fe54..cdc19b7 100644 --- a/dictation_server/src/app.module.ts +++ b/dictation_server/src/app.module.ts @@ -54,6 +54,7 @@ import { RedisModule } from './gateways/redis/redis.module'; import * as redisStore from 'cache-manager-redis-store'; import { SystemAccessGuardsModule } from './common/guards/system/accessguards.module'; import { CheckHeaderMiddleware } from './common/check-header.middleware'; +import { JobNumberRepositoryModule } from './repositories/job_number/job_number.repository.module'; @Module({ imports: [ ServeStaticModule.forRootAsync({ @@ -140,6 +141,7 @@ import { CheckHeaderMiddleware } from './common/check-header.middleware'; WorktypesRepositoryModule, TermsModule, RedisModule, + JobNumberRepositoryModule, ], controllers: [ HealthController,