From 4f7d65f0e88b349804ce5de6eda87a51089cbb9b Mon Sep 17 00:00:00 2001 From: "makabe.t" Date: Tue, 4 Jun 2024 04:25:54 +0000 Subject: [PATCH 1/4] Merged PR 905: POST /accounts/worktypes/{id} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task3986: POST /accounts/worktypes/{id}](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3986) - 元PBI or タスクへのリンク(内容・目的などはそちらにあるはず) - 何をどう変更したか、追加したライブラリなど - このPull Requestでの対象/対象外 - 影響範囲(他の機能にも影響があるか) ## レビューポイント - 特にレビューしてほしい箇所 - 軽微なものや自明なものは記載不要 - 修正範囲が大きい場合などに記載 - 全体的にや仕様を満たしているか等は本当に必要な時のみ記載 - 修正箇所がほかの機能に影響していないか ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## クエリの変更 - Repositoryを変更し、クエリが変更された場合は変更内容を確認する - Before/Afterのクエリ - クエリ置き場 ## 動作確認状況 - ローカルで確認、develop環境で確認など - 行った修正がデグレを発生させていないことを確認できるか - 具体的にどのような確認をしたか - どのケースに対してどのような手段でデグレがないことを担保しているか ## 補足 - 相談、参考資料などがあれば --- .../accounts/accounts.controller.spec.ts | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/dictation_server/src/features/accounts/accounts.controller.spec.ts b/dictation_server/src/features/accounts/accounts.controller.spec.ts index 8ea840c..24bf667 100644 --- a/dictation_server/src/features/accounts/accounts.controller.spec.ts +++ b/dictation_server/src/features/accounts/accounts.controller.spec.ts @@ -9,6 +9,8 @@ import { GetPartnerUsersRequest, UpdatePartnerInfoRequest, CreateWorktypesRequest, + UpdateWorktypesRequest, + UpdateWorktypeRequestParam, } from './types/types'; import { plainToClass } from 'class-transformer'; import { validate } from 'class-validator'; @@ -124,6 +126,101 @@ describe('AccountsController', () => { }); }); + describe('valdation UpdateWorktypesRequest', () => { + it('最低限の有効なリクエストが成功する', async () => { + const request = new UpdateWorktypesRequest(); + request.worktypeId = 'TEST'; + const valdationObject = plainToClass(UpdateWorktypesRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(0); + }); + it('worktypeIdが指定されていない場合、リクエストが失敗する', async () => { + const request = new UpdateWorktypesRequest(); + const valdationObject = plainToClass(UpdateWorktypesRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('worktypeIdが空文字の場合、リクエストが失敗する', async () => { + const request = new UpdateWorktypesRequest(); + request.worktypeId = ''; + const valdationObject = plainToClass(UpdateWorktypesRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('worktypeIdが16文字を超える場合、リクエストが失敗する', async () => { + const request = new UpdateWorktypesRequest(); + request.worktypeId = '123456789A1234567'; + const valdationObject = plainToClass(UpdateWorktypesRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('worktypeIdが16文字の場合、リクエストが成功する', async () => { + const request = new UpdateWorktypesRequest(); + request.worktypeId = '123456789A123456'; + const valdationObject = plainToClass(UpdateWorktypesRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(0); + }); + it('worktypeIdに使用不可文字が含まれる場合、リクエストが失敗する', async () => { + const request = new UpdateWorktypesRequest(); + request.worktypeId = 'test.test'; + const valdationObject = plainToClass(UpdateWorktypesRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('descriptionが255文字を超える場合、リクエストが失敗する', async () => { + const request = new UpdateWorktypesRequest(); + request.worktypeId = 'TEST'; + request.description = + '1234567A0A1234567A1A1234567A2A1234567A3A1234567A4A1234567A5A1234567A6A1234567A7A1234567A8A1234567A9A' + + '1234567B0B1234567B1B1234567B2B1234567B3B1234567B4B1234567B5B1234567B6B1234567B7B1234567B8B1234567B9B' + + '1234567A0A1234567A1A1234567A2A1234567A3A1234567A4A123456'; + const valdationObject = plainToClass(UpdateWorktypesRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('descriptionが255文字の場合、リクエストが成功する', async () => { + const request = new UpdateWorktypesRequest(); + request.worktypeId = 'TEST'; + request.description = + '1234567A0A1234567A1A1234567A2A1234567A3A1234567A4A1234567A5A1234567A6A1234567A7A1234567A8A1234567A9A' + + '1234567B0B1234567B1B1234567B2B1234567B3B1234567B4B1234567B5B1234567B6B1234567B7B1234567B8B1234567B9B' + + '1234567A0A1234567A1A1234567A2A1234567A3A1234567A4A12345'; + const valdationObject = plainToClass(UpdateWorktypesRequest, request); + const errors = await validate(valdationObject); + expect(errors.length).toBe(0); + }); + }); + + describe('valdation UpdateWorktypeRequestParam', () => { + it('最低限の有効なリクエストが成功する', async () => { + const request = new UpdateWorktypeRequestParam(); + request.id = 1; + + const valdationObject = plainToClass(UpdateWorktypeRequestParam, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(0); + }); + it('idが1より小さい場合、リクエストが失敗する', async () => { + const request = new UpdateWorktypeRequestParam(); + request.id = 0; + + const valdationObject = plainToClass(UpdateWorktypeRequestParam, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + it('idが数値でない場合、リクエストが失敗する', async () => { + const request = { id: '0' }; + + const valdationObject = plainToClass(UpdateWorktypeRequestParam, request); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(1); + }); + }); + describe('valdation switchParentRequest', () => { it('最低限の有効なリクエストが成功する', async () => { const request = new SwitchParentRequest(); From 6b1650a6342a50f52226a327a5f6c7705bd64c16 Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Tue, 4 Jun 2024 06:54:42 +0000 Subject: [PATCH 2/4] =?UTF-8?q?Merged=20PR=20910:=20=E3=83=A1=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E6=96=87=E9=9D=A2=E4=BF=AE=E6=AD=A3=EF=BC=86=E3=83=A6?= =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=83=BC=E4=B8=80=E6=8B=AC=E7=99=BB=E9=8C=B2?= =?UTF-8?q?=E3=81=AE=E3=83=86=E3=83=B3=E3=83=97=E3=83=AC=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task4190: メール文面修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4190) [Task4191: ユーザー一括登録のテンプレートファイル修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4191) - ユーザー一括追加時に送信するメールの内容を修正 - SCVとなっていたところをCSVに修正 - ユーザー一括追加用のエクセルの項目名を画面上の項目名と合わせる - auto_renewとなっていたところをauto_assignに修正 ## レビューポイント - 修正内容の認識は合っているか ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - 具体的にどのような確認をしたか - CSV変換のテストが通ることを確認 - メール内容はdev動作確認で確認 ## 補足 - 相談、参考資料などがあれば --- dictation_client/src/common/parser.test.ts | 14 +++++++------- dictation_client/src/common/parser.ts | 6 +++--- dictation_client/src/common/test/test_001.csv | 2 +- dictation_client/src/common/test/test_002.csv | 2 +- dictation_client/src/common/test/test_003.csv | 2 +- dictation_client/src/common/test/test_004.csv | 2 +- dictation_client/src/common/test/test_005.csv | 2 +- dictation_client/src/common/test/test_006.csv | 2 +- dictation_client/src/common/test/test_007.csv | 2 +- dictation_client/src/common/test/test_008.csv | 2 +- dictation_client/src/features/user/operations.ts | 2 +- dictation_client/src/features/user/selectors.ts | 4 ++-- .../src/pages/UserListPage/importPopup.tsx | 2 +- dictation_server/src/templates/template_U_120.html | 6 +++--- dictation_server/src/templates/template_U_120.txt | 6 +++--- .../src/templates/template_U_120_no_parent.html | 6 +++--- .../src/templates/template_U_120_no_parent.txt | 6 +++--- dictation_server/src/templates/template_U_121.html | 6 +++--- dictation_server/src/templates/template_U_121.txt | 6 +++--- .../src/templates/template_U_121_no_parent.html | 6 +++--- .../src/templates/template_U_121_no_parent.txt | 6 +++--- 21 files changed, 46 insertions(+), 46 deletions(-) diff --git a/dictation_client/src/common/parser.test.ts b/dictation_client/src/common/parser.test.ts index a318a76..d690cd3 100644 --- a/dictation_client/src/common/parser.test.ts +++ b/dictation_client/src/common/parser.test.ts @@ -13,7 +13,7 @@ describe("parse", () => { email: "sample@example.com", role: 1, author_id: "HOGE", - auto_renew: 1, + auto_assign: 1, notification: 1, encryption: 1, encryption_password: "abcd", @@ -58,7 +58,7 @@ describe("parse", () => { email: "sample@example.com", role: 1, author_id: null, - auto_renew: 1, + auto_assign: 1, notification: 1, encryption: 1, encryption_password: "abcd", @@ -76,7 +76,7 @@ describe("parse", () => { email: "sample@example.com", role: null, author_id: "HOGE", - auto_renew: 1, + auto_assign: 1, notification: 1, encryption: 1, encryption_password: "abcd", @@ -94,7 +94,7 @@ describe("parse", () => { email: "sample@example.com", role: 1, author_id: "HOGE", - auto_renew: 1, + auto_assign: 1, notification: 1, encryption: 1, encryption_password: "abcd", @@ -105,7 +105,7 @@ describe("parse", () => { email: "sample2@example.com", role: 1, author_id: "HOGE2", - auto_renew: 1, + auto_assign: 1, notification: 1, encryption: 1, encryption_password: "abcd2", @@ -119,7 +119,7 @@ describe("parse", () => { const actualValue = actualData[i]; const expectValue = expectData[i]; expect(actualValue.author_id).toEqual(expectValue.author_id); - expect(actualValue.auto_renew).toEqual(expectValue.auto_renew); + expect(actualValue.auto_assign).toEqual(expectValue.auto_assign); expect(actualValue.email).toEqual(expectValue.email); expect(actualValue.encryption).toEqual(expectValue.encryption); expect(actualValue.encryption_password).toEqual( @@ -141,7 +141,7 @@ describe("parse", () => { email: "sample@example.com", role: 1, author_id: "1111", - auto_renew: 1, + auto_assign: 1, notification: 1, encryption: 1, encryption_password: "222222", diff --git a/dictation_client/src/common/parser.ts b/dictation_client/src/common/parser.ts index 30e0b43..8d76b30 100644 --- a/dictation_client/src/common/parser.ts +++ b/dictation_client/src/common/parser.ts @@ -6,7 +6,7 @@ export type CSVType = { email: string | null; role: number | null; author_id: string | null; - auto_renew: number | null; + auto_assign: number | null; notification: number; encryption: number | null; encryption_password: string | null; @@ -19,7 +19,7 @@ const CSVTypeFields: (keyof CSVType)[] = [ "email", "role", "author_id", - "auto_renew", + "auto_assign", "notification", "encryption", "encryption_password", @@ -45,7 +45,7 @@ export const parseCSV = async (csvString: string): Promise => dynamicTyping: { // author_id, encryption_passwordは数値のみの場合、numberに変換されたくないためdynamicTypingをtrueにしない role: true, - auto_renew: true, + auto_assign: true, notification: true, encryption: true, prompt: true, diff --git a/dictation_client/src/common/test/test_001.csv b/dictation_client/src/common/test/test_001.csv index 7f52660..4b48912 100644 --- a/dictation_client/src/common/test/test_001.csv +++ b/dictation_client/src/common/test/test_001.csv @@ -1,2 +1,2 @@ -name,email,role,author_id,auto_renew,notification,encryption,encryption_password,prompt +name,email,role,author_id,auto_assign,notification,encryption,encryption_password,prompt hoge,sample@example.com,1,"HOGE",1,1,1,abcd,0 \ No newline at end of file diff --git a/dictation_client/src/common/test/test_002.csv b/dictation_client/src/common/test/test_002.csv index 6a385a7..31409e4 100644 --- a/dictation_client/src/common/test/test_002.csv +++ b/dictation_client/src/common/test/test_002.csv @@ -1,2 +1,2 @@ -name,email,role,auto_renew,notification,encryption,encryption_password,prompt +name,email,role,auto_assign,notification,encryption,encryption_password,prompt hoge,sample@example.com,1,"HOGE",1,1,1,abcd,0 \ No newline at end of file diff --git a/dictation_client/src/common/test/test_003.csv b/dictation_client/src/common/test/test_003.csv index 4fb47b2..443c07f 100644 --- a/dictation_client/src/common/test/test_003.csv +++ b/dictation_client/src/common/test/test_003.csv @@ -1,2 +1,2 @@ -name,role,author_id,auto_renew,notification,encryption,encryption_password,prompt +name,role,author_id,auto_assign,notification,encryption,encryption_password,prompt hoge,sample@example.com,1,"HOGE",1,1,1,abcd,0 \ No newline at end of file diff --git a/dictation_client/src/common/test/test_004.csv b/dictation_client/src/common/test/test_004.csv index fec0f30..a35bfa1 100644 --- a/dictation_client/src/common/test/test_004.csv +++ b/dictation_client/src/common/test/test_004.csv @@ -1,2 +1,2 @@ -name,emeil,role,author_id,auto_renew,notification,encryption,encryption_password,prompt +name,emeil,role,author_id,auto_assign,notification,encryption,encryption_password,prompt hoge,sample@example.com,1,"HOGE",1,1,1,abcd,0 \ No newline at end of file diff --git a/dictation_client/src/common/test/test_005.csv b/dictation_client/src/common/test/test_005.csv index b8165d9..4e9f9c7 100644 --- a/dictation_client/src/common/test/test_005.csv +++ b/dictation_client/src/common/test/test_005.csv @@ -1,2 +1,2 @@ -name,email,role,author_id,auto_renew,notification,encryption,encryption_password,prompt +name,email,role,author_id,auto_assign,notification,encryption,encryption_password,prompt hoge,sample@example.com,1,,1,1,1,abcd,0 \ No newline at end of file diff --git a/dictation_client/src/common/test/test_006.csv b/dictation_client/src/common/test/test_006.csv index 18ee411..93e65f3 100644 --- a/dictation_client/src/common/test/test_006.csv +++ b/dictation_client/src/common/test/test_006.csv @@ -1,2 +1,2 @@ -name,email,role,author_id,auto_renew,notification,encryption,encryption_password,prompt +name,email,role,author_id,auto_assign,notification,encryption,encryption_password,prompt hoge,sample@example.com,,"HOGE",1,1,1,abcd,0 \ No newline at end of file diff --git a/dictation_client/src/common/test/test_007.csv b/dictation_client/src/common/test/test_007.csv index ea049ff..9447f41 100644 --- a/dictation_client/src/common/test/test_007.csv +++ b/dictation_client/src/common/test/test_007.csv @@ -1,3 +1,3 @@ -name,email,role,author_id,auto_renew,notification,encryption,encryption_password,prompt +name,email,role,author_id,auto_assign,notification,encryption,encryption_password,prompt hoge,sample@example.com,1,"HOGE",1,1,1,abcd,0,x hoge2,sample2@example.com,1,"HOGE2",1,1,1,abcd2,0,1,32,4,aa \ No newline at end of file diff --git a/dictation_client/src/common/test/test_008.csv b/dictation_client/src/common/test/test_008.csv index ccc9f52..a40daec 100644 --- a/dictation_client/src/common/test/test_008.csv +++ b/dictation_client/src/common/test/test_008.csv @@ -1,2 +1,2 @@ -name,email,role,author_id,auto_renew,notification,encryption,encryption_password,prompt +name,email,role,author_id,auto_assign,notification,encryption,encryption_password,prompt hoge,sample@example.com,1,1111,1,1,1,222222,0 \ No newline at end of file diff --git a/dictation_client/src/features/user/operations.ts b/dictation_client/src/features/user/operations.ts index 8b9c782..6e2cf2c 100644 --- a/dictation_client/src/features/user/operations.ts +++ b/dictation_client/src/features/user/operations.ts @@ -534,7 +534,7 @@ export const importUsersAsync = createAsyncThunk< email: user.email ?? "", role: user.role ?? 0, authorId: user.author_id ?? undefined, - autoRenew: user.auto_renew ?? 0, + autoRenew: user.auto_assign ?? 0, notification: user.notification ?? 0, encryption: user.encryption ?? undefined, encryptionPassword: user.encryption_password ?? undefined, diff --git a/dictation_client/src/features/user/selectors.ts b/dictation_client/src/features/user/selectors.ts index eb9852c..8cd9bdf 100644 --- a/dictation_client/src/features/user/selectors.ts +++ b/dictation_client/src/features/user/selectors.ts @@ -493,8 +493,8 @@ export const selectImportValidationErrors = (state: RootState) => { } } - // auto_renew - if (csvUser.auto_renew === null || ![0, 1].includes(csvUser.auto_renew)) { + // auto_assign + if (csvUser.auto_assign === null || ![0, 1].includes(csvUser.auto_assign)) { invalidInput.push(rowNumber); // eslint-disable-next-line no-continue continue; diff --git a/dictation_client/src/pages/UserListPage/importPopup.tsx b/dictation_client/src/pages/UserListPage/importPopup.tsx index fcc879a..c844730 100644 --- a/dictation_client/src/pages/UserListPage/importPopup.tsx +++ b/dictation_client/src/pages/UserListPage/importPopup.tsx @@ -52,7 +52,7 @@ export const ImportPopup: React.FC = (props) => { "email", "role", "author_id", - "auto_renew", + "auto_assign", "notification", "encryption", "encryption_password", diff --git a/dictation_server/src/templates/template_U_120.html b/dictation_server/src/templates/template_U_120.html index 3f5c3ad..03dd097 100644 --- a/dictation_server/src/templates/template_U_120.html +++ b/dictation_server/src/templates/template_U_120.html @@ -13,7 +13,7 @@

We have received your bulk user registration request.
- Date and time: $REQUEST_TIME$
- - SCV file name: $FILE_NAME$ + - CSV file name: $FILE_NAME$

・Please wait until the registration is complete. This may take a few minutes to process.
@@ -36,7 +36,7 @@

Wir haben Ihre Anfrage zur Massenbenutzerregistrierung erhalten.
- Datum und Uhrzeit: $REQUEST_TIME$
- - SCV-Dateiname: $FILE_NAME$ + - CSV-Dateiname: $FILE_NAME$

・Bitte warten Sie, bis die Registrierung abgeschlossen ist. Die Bearbeitung kann einige Minuten dauern.
@@ -59,7 +59,7 @@

Nous avons reçu votre demande d'enregistrement groupé d'utilisateur.
- Date et heure : $REQUEST_TIME$
- - Nom du fichier SCV : $FILE_NAME$ + - Nom du fichier CSV : $FILE_NAME$

・Veuillez attendre que l'inscription soit terminée. Le traitement peut prendre quelques minutes.
diff --git a/dictation_server/src/templates/template_U_120.txt b/dictation_server/src/templates/template_U_120.txt index e33e7e0..2d856d9 100644 --- a/dictation_server/src/templates/template_U_120.txt +++ b/dictation_server/src/templates/template_U_120.txt @@ -4,7 +4,7 @@ Dear $CUSTOMER_NAME$, We have received your bulk user registration request. - Date and time: $REQUEST_TIME$ - - SCV file name: $FILE_NAME$ + - CSV file name: $FILE_NAME$ ・Please wait until the registration is complete. This may take a few minutes to process. ・Notification will be sent separately upon completion. @@ -21,7 +21,7 @@ Sehr geehrte(r) $CUSTOMER_NAME$, Wir haben Ihre Anfrage zur Massenbenutzerregistrierung erhalten. - Datum und Uhrzeit: $REQUEST_TIME$ - - SCV-Dateiname: $FILE_NAME$ + - CSV-Dateiname: $FILE_NAME$ ・Bitte warten Sie, bis die Registrierung abgeschlossen ist. Die Bearbeitung kann einige Minuten dauern. ・Eine Benachrichtigung wird nach Abschluss separat verschickt. @@ -38,7 +38,7 @@ Chère/Cher $CUSTOMER_NAME$, Nous avons reçu votre demande d'enregistrement groupé d'utilisateur. - Date et heure : $REQUEST_TIME$ - - Nom du fichier SCV : $FILE_NAME$ + - Nom du fichier CSV : $FILE_NAME$ ・Veuillez attendre que l'inscription soit terminée. Le traitement peut prendre quelques minutes. ・Une notification sera envoyée séparément une fois terminée. diff --git a/dictation_server/src/templates/template_U_120_no_parent.html b/dictation_server/src/templates/template_U_120_no_parent.html index 730449c..ae319b0 100644 --- a/dictation_server/src/templates/template_U_120_no_parent.html +++ b/dictation_server/src/templates/template_U_120_no_parent.html @@ -13,7 +13,7 @@

We have received your bulk user registration request.
- Date and time: $REQUEST_TIME$
- - SCV file name: $FILE_NAME$ + - CSV file name: $FILE_NAME$

・Please wait until the registration is complete. This may take a few minutes to process.
@@ -33,7 +33,7 @@

Wir haben Ihre Anfrage zur Massenbenutzerregistrierung erhalten.
- Datum und Uhrzeit: $REQUEST_TIME$
- - SCV-Dateiname: $FILE_NAME$ + - CSV-Dateiname: $FILE_NAME$

・Bitte warten Sie, bis die Registrierung abgeschlossen ist. Die Bearbeitung kann einige Minuten dauern.
@@ -53,7 +53,7 @@

Nous avons reçu votre demande d'enregistrement groupé d'utilisateur.
- Date et heure : $REQUEST_TIME$
- - Nom du fichier SCV : $FILE_NAME$ + - Nom du fichier CSV : $FILE_NAME$

・Veuillez attendre que l'inscription soit terminée. Le traitement peut prendre quelques minutes.
diff --git a/dictation_server/src/templates/template_U_120_no_parent.txt b/dictation_server/src/templates/template_U_120_no_parent.txt index d5d85fa..4e46961 100644 --- a/dictation_server/src/templates/template_U_120_no_parent.txt +++ b/dictation_server/src/templates/template_U_120_no_parent.txt @@ -4,7 +4,7 @@ Dear $CUSTOMER_NAME$, We have received your bulk user registration request. - Date and time: $REQUEST_TIME$ - - SCV file name: $FILE_NAME$ + - CSV file name: $FILE_NAME$ ・Please wait until the registration is complete. This may take a few minutes to process. ・Notification will be sent separately upon completion. @@ -19,7 +19,7 @@ Sehr geehrte(r) $CUSTOMER_NAME$, Wir haben Ihre Anfrage zur Massenbenutzerregistrierung erhalten. - Datum und Uhrzeit: $REQUEST_TIME$ - - SCV-Dateiname: $FILE_NAME$ + - CSV-Dateiname: $FILE_NAME$ ・Bitte warten Sie, bis die Registrierung abgeschlossen ist. Die Bearbeitung kann einige Minuten dauern. ・Eine Benachrichtigung wird nach Abschluss separat verschickt. @@ -34,7 +34,7 @@ Chère/Cher $CUSTOMER_NAME$, Nous avons reçu votre demande d'enregistrement groupé d'utilisateur. - Date et heure : $REQUEST_TIME$ - - Nom du fichier SCV : $FILE_NAME$ + - Nom du fichier CSV : $FILE_NAME$ ・Veuillez attendre que l'inscription soit terminée. Le traitement peut prendre quelques minutes. ・Une notification sera envoyée séparément une fois terminée. diff --git a/dictation_server/src/templates/template_U_121.html b/dictation_server/src/templates/template_U_121.html index 4598b6b..e2eac21 100644 --- a/dictation_server/src/templates/template_U_121.html +++ b/dictation_server/src/templates/template_U_121.html @@ -10,7 +10,7 @@

Bulk user registration using the CSV file has been completed.
- Date and time: $REQUEST_TIME$
- - SCV file name: $FILE_NAME$ + - CSV file name: $FILE_NAME$

・User Registration Notification [U-114] will be sent to the registered users.
@@ -33,7 +33,7 @@

Die Massenbenutzerregistrierung mithilfe der CSV-Datei wurde abgeschlossen.
- Datum und Uhrzeit: $REQUEST_TIME$
- - SCV-Dateiname: $FILE_NAME$ + - CSV-Dateiname: $FILE_NAME$

・Die Benutzerregistrierungsbenachrichtigung [U-114] wird an die registrierten Benutzer gesendet.
@@ -57,7 +57,7 @@

L'enregistrement groupé des utilisateurs à l'aide du fichier CSV est terminé.
- Date et heure : $REQUEST_TIME$
- - Nom du fichier SCV : $FILE_NAME$ + - Nom du fichier CSV : $FILE_NAME$

・La notification d'enregistrement de l'utilisateur [U-114] sera envoyée aux utilisateurs enregistrés.
diff --git a/dictation_server/src/templates/template_U_121.txt b/dictation_server/src/templates/template_U_121.txt index 63e9176..b04a729 100644 --- a/dictation_server/src/templates/template_U_121.txt +++ b/dictation_server/src/templates/template_U_121.txt @@ -4,7 +4,7 @@ Dear $CUSTOMER_NAME$, Bulk user registration using the CSV file has been completed. - Date and time: $REQUEST_TIME$ - - SCV file name: $FILE_NAME$ + - CSV file name: $FILE_NAME$ ・User Registration Notification [U-114] will be sent to the registered users. ・Registration will not be completed unless the user verifies their email address. @@ -21,7 +21,7 @@ Sehr geehrte(r) $CUSTOMER_NAME$, Die Massenbenutzerregistrierung mithilfe der CSV-Datei wurde abgeschlossen. - Datum und Uhrzeit: $REQUEST_TIME$ - - SCV-Dateiname: $FILE_NAME$ + - CSV-Dateiname: $FILE_NAME$ ・Die Benutzerregistrierungsbenachrichtigung [U-114] wird an die registrierten Benutzer gesendet. ・Die Registrierung wird erst abgeschlossen, wenn der Benutzer seine E-Mail-Adresse bestätigt. @@ -38,7 +38,7 @@ Chère/Cher $CUSTOMER_NAME$, L'enregistrement groupé des utilisateurs à l'aide du fichier CSV est terminé. - Date et heure : $REQUEST_TIME$ - - Nom du fichier SCV : $FILE_NAME$ + - Nom du fichier CSV : $FILE_NAME$ ・La notification d'enregistrement de l'utilisateur [U-114] sera envoyée aux utilisateurs enregistrés. ・L'inscription ne sera complétée que si l'utilisateur vérifie son adresse e-mail. diff --git a/dictation_server/src/templates/template_U_121_no_parent.html b/dictation_server/src/templates/template_U_121_no_parent.html index 25393c9..2f52ad9 100644 --- a/dictation_server/src/templates/template_U_121_no_parent.html +++ b/dictation_server/src/templates/template_U_121_no_parent.html @@ -10,7 +10,7 @@

Bulk user registration using the CSV file has been completed.
- Date and time: $REQUEST_TIME$
- - SCV file name: $FILE_NAME$ + - CSV file name: $FILE_NAME$

・User Registration Notification [U-114] will be sent to the registered users.
@@ -30,7 +30,7 @@

Die Massenbenutzerregistrierung mithilfe der CSV-Datei wurde abgeschlossen.
- Datum und Uhrzeit: $REQUEST_TIME$
- - SCV-Dateiname: $FILE_NAME$ + - CSV-Dateiname: $FILE_NAME$

・Die Benutzerregistrierungsbenachrichtigung [U-114] wird an die registrierten Benutzer gesendet.
@@ -51,7 +51,7 @@

L'enregistrement groupé des utilisateurs à l'aide du fichier CSV est terminé.
- Date et heure : $REQUEST_TIME$
- - Nom du fichier SCV : $FILE_NAME$ + - Nom du fichier CSV : $FILE_NAME$

・La notification d'enregistrement de l'utilisateur [U-114] sera envoyée aux utilisateurs enregistrés.
diff --git a/dictation_server/src/templates/template_U_121_no_parent.txt b/dictation_server/src/templates/template_U_121_no_parent.txt index c1f7987..650d6b7 100644 --- a/dictation_server/src/templates/template_U_121_no_parent.txt +++ b/dictation_server/src/templates/template_U_121_no_parent.txt @@ -4,7 +4,7 @@ Dear $CUSTOMER_NAME$, Bulk user registration using the CSV file has been completed. - Date and time: $REQUEST_TIME$ - - SCV file name: $FILE_NAME$ + - CSV file name: $FILE_NAME$ ・User Registration Notification [U-114] will be sent to the registered users. ・Registration will not be completed unless the user verifies their email address. @@ -19,7 +19,7 @@ Sehr geehrte(r) $CUSTOMER_NAME$, Die Massenbenutzerregistrierung mithilfe der CSV-Datei wurde abgeschlossen. - Datum und Uhrzeit: $REQUEST_TIME$ - - SCV-Dateiname: $FILE_NAME$ + - CSV-Dateiname: $FILE_NAME$ ・Die Benutzerregistrierungsbenachrichtigung [U-114] wird an die registrierten Benutzer gesendet. ・Die Registrierung wird erst abgeschlossen, wenn der Benutzer seine E-Mail-Adresse bestätigt. @@ -34,7 +34,7 @@ Chère/Cher $CUSTOMER_NAME$, L'enregistrement groupé des utilisateurs à l'aide du fichier CSV est terminé. - Date et heure : $REQUEST_TIME$ - - Nom du fichier SCV : $FILE_NAME$ + - Nom du fichier CSV : $FILE_NAME$ ・La notification d'enregistrement de l'utilisateur [U-114] sera envoyée aux utilisateurs enregistrés. ・L'inscription ne sera complétée que si l'utilisateur vérifie son adresse e-mail. From 83732fa1b5ac45aecaf1ffe5ec9b0ea2a729e24d Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Thu, 6 Jun 2024 07:36:17 +0000 Subject: [PATCH 3/4] =?UTF-8?q?Merged=20PR=20911:=20=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=EF=BC=88=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB?= =?UTF-8?q?=E5=90=8D=E5=A4=89=E6=9B=B4=E3=83=9C=E3=82=BF=E3=83=B3=E3=81=AE?= =?UTF-8?q?=E3=83=87=E3=82=B6=E3=82=A4=E3=83=B3=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task4213: 画面修正(ファイル名変更ボタンのデザイン)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4213) - ファイル名変更ボタンのデザイン修正 - 言語を英語以外にすると、ファイル名の入力欄と重なってしまうのを修正 - ボタンの位置を入力欄の下に修正 - リテラル修正も含めて対応 - https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/OMDSDictation/_sprints/taskboard/OMDSDictation%20%E3%83%81%E3%83%BC%E3%83%A0/OMDSDictation/%E3%82%B9%E3%83%97%E3%83%AA%E3%83%B3%E3%83%88%2036-1?workitem=4214 ## レビューポイント - 特になし ## UIの変更 - Before/Afterのスクショなど - https://ndstokyo.sharepoint.com/:f:/r/sites/Piranha/Shared%20Documents/General/OMDS/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88/Task4213?csf=1&web=1&e=ftBwWm ## 動作確認状況 - ローカルで確認 - デザインの修正のみのためデグレは発生しない ## 補足 - 修正したデザインに対してはOMDS様に確認済み --- .../src/pages/DictationPage/filePropertyPopup.tsx | 9 +++++++++ dictation_client/src/translation/de.json | 2 +- dictation_client/src/translation/en.json | 2 +- dictation_client/src/translation/es.json | 4 ++-- dictation_client/src/translation/fr.json | 4 ++-- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/dictation_client/src/pages/DictationPage/filePropertyPopup.tsx b/dictation_client/src/pages/DictationPage/filePropertyPopup.tsx index 4f3e9bb..e3b2e09 100644 --- a/dictation_client/src/pages/DictationPage/filePropertyPopup.tsx +++ b/dictation_client/src/pages/DictationPage/filePropertyPopup.tsx @@ -104,6 +104,15 @@ export const FilePropertyPopup: React.FC = (props) => { name="submit" value={t(getTranslationID("dictationPage.label.fileNameSave"))} className={`${styles.formSubmit} ${styles.isActive}`} + style={{ + position: "relative", + marginTop: "0.2rem", + right: "auto", + maxWidth: "18rem", + whiteSpace: "normal", + overflowWrap: "break-word", + fontSize: "small", + }} onClick={saveFileName} /> {isPushSaveButton && fileName.length === 0 && ( diff --git a/dictation_client/src/translation/de.json b/dictation_client/src/translation/de.json index 1a3a348..67b2367 100644 --- a/dictation_client/src/translation/de.json +++ b/dictation_client/src/translation/de.json @@ -654,4 +654,4 @@ "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 ec99d53..2059296 100644 --- a/dictation_client/src/translation/en.json +++ b/dictation_client/src/translation/en.json @@ -654,4 +654,4 @@ "lowerLayerId": "Lower Layer ID" } } -} +} \ No newline at end of file diff --git a/dictation_client/src/translation/es.json b/dictation_client/src/translation/es.json index 930f15b..86d5a23 100644 --- a/dictation_client/src/translation/es.json +++ b/dictation_client/src/translation/es.json @@ -15,7 +15,7 @@ "copyRight": "© OM Digital Solutions Corporation", "edit": "Editar", "save": "Ahorrar", - "delete": "Delete", + "delete": "Borrar", "return": "Devolver", "operationInsteadOf": "Operar la nube ODMS en nombre de:", "headerAccount": "Cuenta", @@ -654,4 +654,4 @@ "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 7bc9f65..083360c 100644 --- a/dictation_client/src/translation/fr.json +++ b/dictation_client/src/translation/fr.json @@ -15,7 +15,7 @@ "copyRight": "© OM Digital Solutions Corporation", "edit": "Éditer", "save": "Sauvegarder", - "delete": "Delete", + "delete": "Supprimer", "return": "Retour", "operationInsteadOf": "Exploiter le Cloud ODMS pour le compte de :", "headerAccount": "Compte", @@ -654,4 +654,4 @@ "lowerLayerId": "Lower Layer ID" } } -} +} \ No newline at end of file From 474cdd56c6133187992b24089dfa00e1cbfdc087 Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Mon, 10 Jun 2024 02:31:35 +0000 Subject: [PATCH 4/4] =?UTF-8?q?Merged=20PR=20913:=20=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=82=BB=E3=83=B3=E3=82=B9=E5=8F=96=E5=BE=97API=E3=81=AE?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task4220: ライセンス取得APIの修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4220) - 割り当て可能ライセンス取得APIを修正 - 有効期限が当日のライセンスは割り当て可能としないように修正 - 有効期限が翌日以降のライセンスを取得 - テスト修正 - ライセンス割り当ての処理にコメント追加 - 結果として有効期限が当日のライセンスは割り当てられないので問題はないが、記述方法が紛らわしいのでコメントを追加した。 ## レビューポイント - テストケースは足りているか - 追加したコメントの意図は伝わるか ## クエリの変更 - Repositoryを変更し、クエリが変更された場合は変更内容を確認する - クエリ部分を修正したが、基準となる日付を変更したのみなので、クエリ自体に変更はなし - Before/Afterで確認したが、変更はなかった ## 動作確認状況 - ローカルで確認 - 行った修正がデグレを発生させていないことを確認できるか - テストを追加して、デグレがないことを検証した ## 補足 - 相談、参考資料などがあれば --- .../licenses/licenses.service.spec.ts | 415 +++++++++++++++++- .../licenses/licenses.repository.service.ts | 14 +- 2 files changed, 418 insertions(+), 11 deletions(-) diff --git a/dictation_server/src/features/licenses/licenses.service.spec.ts b/dictation_server/src/features/licenses/licenses.service.spec.ts index 72ac6ea..37912c3 100644 --- a/dictation_server/src/features/licenses/licenses.service.spec.ts +++ b/dictation_server/src/features/licenses/licenses.service.spec.ts @@ -25,6 +25,7 @@ import { } from '../../constants'; import { makeHierarchicalAccounts, + makeTestAccount, makeTestSimpleAccount, makeTestUser, } from '../../common/test/utility'; @@ -387,8 +388,12 @@ describe('カードライセンスを取り込む', () => { if (!source) fail(); const module = await makeTestingModule(source); if (!module) fail(); + // 明日の日付を取得 + // ミリ秒以下は切り捨てる + const tommorow = new Date(); + tommorow.setDate(tommorow.getDate() + 1); + tommorow.setMilliseconds(0); - const now = new Date(); const { id: accountId } = await makeTestSimpleAccount(source); const { external_id: externalId } = await makeTestUser(source, { account_id: accountId, @@ -402,7 +407,7 @@ describe('カードライセンスを取り込む', () => { await createLicense( source, 1, - new Date(now.getTime() + 60 * 60 * 1000), + new Date(tommorow.getTime() + 60 * 60 * 1000), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, @@ -428,7 +433,7 @@ describe('カードライセンスを取り込む', () => { await createLicense( source, 3, - new Date(now.getTime() + 60 * 60 * 1000), + new Date(tommorow.getTime() + 60 * 60 * 1000), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, @@ -441,7 +446,7 @@ describe('カードライセンスを取り込む', () => { await createLicense( source, 4, - new Date(now.getTime() + 60 * 60 * 1000 * 2), + new Date(tommorow.getTime() + 60 * 60 * 1000 * 2), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, @@ -467,7 +472,7 @@ describe('カードライセンスを取り込む', () => { await createLicense( source, 6, - new Date(now.getTime() + 60 * 60 * 1000 * 2), + new Date(tommorow.getTime() + 60 * 60 * 1000 * 2), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.ALLOCATED, @@ -480,7 +485,7 @@ describe('カードライセンスを取り込む', () => { await createLicense( source, 7, - new Date(now.getTime() + 60 * 60 * 1000 * 2), + new Date(tommorow.getTime() + 60 * 60 * 1000 * 2), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.DELETED, @@ -493,7 +498,7 @@ describe('カードライセンスを取り込む', () => { await createLicense( source, 8, - new Date(now.getTime() + 60 * 60 * 1000), + new Date(tommorow.getTime() + 60 * 60 * 1000), accountId + 1, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, @@ -506,7 +511,7 @@ describe('カードライセンスを取り込む', () => { await createLicense( source, 9, - new Date(now.getTime() - 60 * 60 * 1000 * 24), + new Date(tommorow.getTime() - 60 * 60 * 1000 * 24), accountId, LICENSE_TYPE.NORMAL, LICENSE_ALLOCATED_STATUS.UNALLOCATED, @@ -1610,3 +1615,397 @@ describe('ライセンス注文キャンセル', () => { ); }); }); + +describe('割り当て可能なライセンス取得', () => { + let source: DataSource | null = null; + beforeAll(async () => { + if (source == null) { + source = await (async () => { + const s = new DataSource({ + type: 'mysql', + host: 'test_mysql_db', + port: 3306, + username: 'user', + password: 'password', + database: 'odms', + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: false, // trueにすると自動的にmigrationが行われるため注意 + logger: new TestLogger('none'), + logging: true, + }); + return await s.initialize(); + })(); + } + }); + + beforeEach(async () => { + if (source) { + await truncateAllTable(source); + } + }); + + afterAll(async () => { + await source?.destroy(); + source = null; + }); + + it('割り当て可能なライセンスを取得できる', async () => { + if (!source) fail(); + const module = await makeTestingModule(source); + if (!module) fail(); + const { account, admin } = await makeTestAccount(source, { + company_name: 'AAA', + tier: 5, + }); + // ライセンスを作成 + // 有効期限が当日のライセンスを1つ、有効期限が翌日のライセンスを1つ、有効期限が翌々日のライセンスを1つ作成 + // ミリ秒以下は切り捨てる DBで扱っていないため + const today = new Date(); + today.setMilliseconds(0); + const tomorrow = new Date(); + tomorrow.setDate(tomorrow.getDate() + 1); + tomorrow.setMilliseconds(0); + const dayAfterTomorrow = new Date(); + dayAfterTomorrow.setDate(dayAfterTomorrow.getDate() + 2); + dayAfterTomorrow.setMilliseconds(0); + await createLicense( + source, + 1, + today, + account.id, + LICENSE_TYPE.NORMAL, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + null, + null, + null, + null, + ); + await createLicense( + source, + 2, + tomorrow, + account.id, + LICENSE_TYPE.CARD, + LICENSE_ALLOCATED_STATUS.REUSABLE, + null, + null, + null, + null, + ); + await createLicense( + source, + 3, + dayAfterTomorrow, + account.id, + LICENSE_TYPE.TRIAL, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + null, + null, + null, + null, + ); + // ライセンス作成したデータを確認 + { + const license1 = await selectLicense(source, 1); + expect(license1.license?.id).toBe(1); + expect(license1.license?.expiry_date).toEqual(today); + expect(license1.license?.allocated_user_id).toBe(null); + expect(license1.license?.status).toBe( + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ); + expect(license1.license?.account_id).toBe(account.id); + expect(license1.license?.type).toBe(LICENSE_TYPE.NORMAL); + const license2 = await selectLicense(source, 2); + expect(license2.license?.id).toBe(2); + expect(license2.license?.expiry_date).toEqual(tomorrow); + expect(license2.license?.allocated_user_id).toBe(null); + expect(license2.license?.status).toBe(LICENSE_ALLOCATED_STATUS.REUSABLE); + expect(license2.license?.account_id).toBe(account.id); + expect(license2.license?.type).toBe(LICENSE_TYPE.CARD); + const license3 = await selectLicense(source, 3); + expect(license3.license?.id).toBe(3); + expect(license3.license?.expiry_date).toEqual(dayAfterTomorrow); + expect(license3.license?.allocated_user_id).toBe(null); + expect(license3.license?.status).toBe( + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ); + expect(license3.license?.account_id).toBe(account.id); + expect(license3.license?.type).toBe(LICENSE_TYPE.TRIAL); + } + const service = module.get(LicensesService); + const context = makeContext('trackingId', 'requestId'); + const response = await service.getAllocatableLicenses( + context, + admin.external_id, + ); + + // 有効期限が当日のライセンスは取得されない + // 有効期限が長い順に取得される + expect(response.allocatableLicenses.length).toBe(2); + expect(response.allocatableLicenses[0].licenseId).toBe(3); + expect(response.allocatableLicenses[0].expiryDate).toEqual( + dayAfterTomorrow, + ); + expect(response.allocatableLicenses[1].licenseId).toBe(2); + expect(response.allocatableLicenses[1].expiryDate).toEqual(tomorrow); + }); + + it('割り当て可能なライセンスが存在しない場合、空の配列を返却する', async () => { + if (!source) fail(); + const module = await makeTestingModule(source); + if (!module) fail(); + + const { account, admin } = await makeTestAccount(source, { + company_name: 'AAA', + tier: 5, + }); + // ライセンスを作成 + // 有効期限が当日のライセンスを3つ、有効期限が昨日のライセンスを1つ作成 + // ミリ秒以下は切り捨てる DBで扱っていないため + const today = new Date(); + today.setMilliseconds(0); + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + yesterday.setMilliseconds(0); + for (let i = 1; i <= 3; i++) { + await createLicense( + source, + i, + today, + account.id, + LICENSE_TYPE.NORMAL, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + null, + null, + null, + null, + ); + } + await createLicense( + source, + 4, + yesterday, + account.id, + LICENSE_TYPE.NORMAL, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + null, + null, + null, + null, + ); + // ライセンス作成したデータを確認 + { + const license1 = await selectLicense(source, 1); + expect(license1.license?.id).toBe(1); + expect(license1.license?.expiry_date).toEqual(today); + expect(license1.license?.allocated_user_id).toBe(null); + expect(license1.license?.status).toBe( + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ); + expect(license1.license?.account_id).toBe(account.id); + expect(license1.license?.type).toBe(LICENSE_TYPE.NORMAL); + const license2 = await selectLicense(source, 2); + expect(license2.license?.id).toBe(2); + expect(license2.license?.expiry_date).toEqual(today); + expect(license2.license?.allocated_user_id).toBe(null); + expect(license2.license?.status).toBe( + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ); + expect(license2.license?.account_id).toBe(account.id); + expect(license2.license?.type).toBe(LICENSE_TYPE.NORMAL); + const license3 = await selectLicense(source, 3); + expect(license3.license?.id).toBe(3); + expect(license3.license?.expiry_date).toEqual(today); + expect(license3.license?.allocated_user_id).toBe(null); + expect(license3.license?.status).toBe( + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ); + expect(license3.license?.account_id).toBe(account.id); + expect(license3.license?.type).toBe(LICENSE_TYPE.NORMAL); + const license4 = await selectLicense(source, 4); + expect(license4.license?.id).toBe(4); + expect(license4.license?.expiry_date).toEqual(yesterday); + expect(license4.license?.allocated_user_id).toBe(null); + expect(license4.license?.status).toBe( + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ); + expect(license4.license?.account_id).toBe(account.id); + expect(license4.license?.type).toBe(LICENSE_TYPE.NORMAL); + } + const service = module.get(LicensesService); + const context = makeContext('trackingId', 'requestId'); + const response = await service.getAllocatableLicenses( + context, + admin.external_id, + ); + // 有効期限が当日のライセンスは取得されない + // 有効期限が切れているライセンスは取得されない + expect(response.allocatableLicenses.length).toBe(0); + expect(response.allocatableLicenses).toEqual([]); + }); + + it('割り当て可能なライセンスを100件取得できる', async () => { + if (!source) fail(); + const module = await makeTestingModule(source); + if (!module) fail(); + + const { account, admin } = await makeTestAccount(source, { + company_name: 'AAA', + tier: 5, + }); + // ライセンスを作成 + // 有効期限が30日後のライセンスを100件作成 + // ミリ秒以下は切り捨てる DBで扱っていないため + const date = new Date(); + date.setDate(date.getDate() + 30); + date.setMilliseconds(0); + for (let i = 1; i <= 100; i++) { + await createLicense( + source, + i, + date, + account.id, + LICENSE_TYPE.NORMAL, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + null, + null, + null, + null, + ); + } + // ライセンス作成したデータを確認 + for (let i = 1; i <= 100; i++) { + const license = await selectLicense(source, i); + expect(license.license?.id).toBe(i); + expect(license.license?.expiry_date).toEqual(date); + expect(license.license?.allocated_user_id).toBe(null); + expect(license.license?.status).toBe( + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ); + expect(license.license?.account_id).toBe(account.id); + expect(license.license?.type).toBe(LICENSE_TYPE.NORMAL); + } + const service = module.get(LicensesService); + const context = makeContext('trackingId', 'requestId'); + const response = await service.getAllocatableLicenses( + context, + admin.external_id, + ); + // 100件取得できる + expect(response.allocatableLicenses.length).toBe(100); + }); + it('既に割り当てられているライセンスは取得されない', async () => { + if (!source) fail(); + const module = await makeTestingModule(source); + if (!module) fail(); + const { account, admin } = await makeTestAccount(source, { + company_name: 'AAA', + tier: 5, + }); + // ライセンスを作成 + // ライセンスを5件作成(10日後から1日ずつ有効期限を設定) + // 有効期限が設定されていないライセンス(新規ライセンス)を1件作成 + // 既に割り当てられているライセンスを1件作成 + // ミリ秒以下は切り捨てる DBで扱っていないため + const date = new Date(); + date.setMinutes(0); + date.setSeconds(0); + date.setDate(date.getDate() + 10); + date.setMilliseconds(0); + for (let i = 1; i <= 5; i++) { + await createLicense( + source, + i, + date, + account.id, + LICENSE_TYPE.NORMAL, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + admin.id, + null, + null, + null, + ); + date.setDate(date.getDate() + 1); + } + // 新規ライセンス + await createLicense( + source, + 6, + null, + account.id, + LICENSE_TYPE.NORMAL, + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + null, + null, + null, + null, + ); + // 既に割り当てられているライセンス + await createLicense( + source, + 7, + date, + account.id, + LICENSE_TYPE.NORMAL, + LICENSE_ALLOCATED_STATUS.ALLOCATED, + admin.id, + null, + null, + null, + ); + // ライセンス作成したデータを確認 + { + const date = new Date(); + date.setMinutes(0); + date.setSeconds(0); + date.setDate(date.getDate() + 10); + date.setMilliseconds(0); + for (let i = 1; i <= 5; i++) { + const license = await selectLicense(source, i); + expect(license.license?.id).toBe(i); + expect(license.license?.expiry_date).toEqual(date); + expect(license.license?.allocated_user_id).toBe(admin.id); + expect(license.license?.status).toBe( + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ); + expect(license.license?.account_id).toBe(account.id); + expect(license.license?.type).toBe(LICENSE_TYPE.NORMAL); + date.setDate(date.getDate() + 1); + } + const newLicense = await selectLicense(source, 6); + expect(newLicense.license?.id).toBe(6); + expect(newLicense.license?.expiry_date).toBe(null); + expect(newLicense.license?.allocated_user_id).toBe(null); + expect(newLicense.license?.status).toBe( + LICENSE_ALLOCATED_STATUS.UNALLOCATED, + ); + const allocatedLicense = await selectLicense(source, 7); + expect(allocatedLicense.license?.id).toBe(7); + expect(allocatedLicense.license?.expiry_date).toEqual(date); + expect(allocatedLicense.license?.allocated_user_id).toBe(admin.id); + expect(allocatedLicense.license?.status).toBe( + LICENSE_ALLOCATED_STATUS.ALLOCATED, + ); + expect(allocatedLicense.license?.account_id).toBe(account.id); + expect(allocatedLicense.license?.type).toBe(LICENSE_TYPE.NORMAL); + } + + const service = module.get(LicensesService); + const context = makeContext('trackingId', 'requestId'); + const response = await service.getAllocatableLicenses( + context, + admin.external_id, + ); + // 既に割り当てられているライセンスは取得されない + // 新規ライセンスは取得される + // 有効期限が長い順に取得される + expect(response.allocatableLicenses.length).toBe(6); + expect(response.allocatableLicenses[0].licenseId).toBe(6); + expect(response.allocatableLicenses[0].expiryDate).toBe(undefined); + expect(response.allocatableLicenses[1].licenseId).toBe(5); // 有効期限が最も長い + expect(response.allocatableLicenses[2].licenseId).toBe(4); + expect(response.allocatableLicenses[3].licenseId).toBe(3); + expect(response.allocatableLicenses[4].licenseId).toBe(2); + expect(response.allocatableLicenses[5].licenseId).toBe(1); + }); +}); diff --git a/dictation_server/src/repositories/licenses/licenses.repository.service.ts b/dictation_server/src/repositories/licenses/licenses.repository.service.ts index 5d20eb4..5b6e873 100644 --- a/dictation_server/src/repositories/licenses/licenses.repository.service.ts +++ b/dictation_server/src/repositories/licenses/licenses.repository.service.ts @@ -525,9 +525,14 @@ export class LicensesRepositoryService { context: Context, myAccountId: number, ): Promise { - const nowDate = new DateWithZeroTime(); const licenseRepo = this.dataSource.getRepository(License); // EntityManagerではorderBy句で、expiry_dateに対して複数条件でソートを使用するため出来ない為、createQueryBuilderを使用する。 + // プロダクト バックログ項目 4218: [FB対応]有効期限当日のライセンスは一覧に表示しない の対応 + // 有効期限が当日のライセンスは取得しない + // 明日の00:00:00を取得 + const tomorrowDate = new DateWithZeroTime( + new Date().setDate(new Date().getDate() + 1), + ); const queryBuilder = licenseRepo .createQueryBuilder('license') .where('license.account_id = :accountId', { accountId: myAccountId }) @@ -538,8 +543,8 @@ export class LicensesRepositoryService { ], }) .andWhere( - '(license.expiry_date >= :nowDate OR license.expiry_date IS NULL)', - { nowDate }, + '(license.expiry_date >= :tomorrowDate OR license.expiry_date IS NULL)', + { tomorrowDate }, ) .comment(`${context.getTrackingId()}_${new Date().toUTCString()}`) .orderBy('license.expiry_date IS NULL', 'DESC') @@ -597,6 +602,9 @@ export class LicensesRepositoryService { } // 期限切れの場合はエラー + // 有効期限が当日のライセンスは割り当て不可 + // XXX 記述は「有効期限が過去のライセンスは割り当て不可」のような意図だと思われるが、実際の処理は「有効期限が当日のライセンスは割り当て不可」になっている + // より正確な記述に修正したほうが良いが、リリース後のため、修正は保留(2024年6月7日) if (targetLicense.expiry_date) { const currentDay = new Date(); currentDay.setHours(23, 59, 59, 999);