Merge branch 'develop' into ccb

# Conflicts:
#	dictation_server/src/features/users/users.service.spec.ts
This commit is contained in:
SAITO-PC-3\saito.k 2024-02-27 09:26:46 +09:00
commit ddd4d31f25
10 changed files with 253 additions and 37 deletions

View File

@ -134,7 +134,7 @@ export function isAccountsInputFile(obj: any): obj is AccountsInputFile {
"country" in obj &&
typeof obj.country === "string" &&
("dealerAccountId" in obj
? typeof obj.dealerAccountId === "number"
? obj.dealerAccountId === null || typeof obj.dealerAccountId === "number"
: true) &&
"adminName" in obj &&
typeof obj.adminName === "string" &&

View File

@ -1,20 +1,18 @@
import { Injectable } from '@nestjs/common';
import {
DataSource,
} from 'typeorm';
import { User } from '../users/entity/user.entity';
import { Account } from './entity/account.entity';
import { Injectable } from "@nestjs/common";
import { DataSource } from "typeorm";
import { User } from "../users/entity/user.entity";
import { Account } from "./entity/account.entity";
import {
getDirection,
getTaskListSortableAttribute,
} from '../../common/types/sort/util';
} from "../../common/types/sort/util";
import { SortCriteria } from "../sort_criteria/entity/sort_criteria.entity";
import {
insertEntity,
updateEntity,
deleteEntity,
} from '../../common/repository';
import { Context } from '../../common/log';
} from "../../common/repository";
import { Context } from "../../common/log";
@Injectable()
export class AccountsRepositoryService {
@ -81,6 +79,7 @@ export class AccountsRepositoryService {
user.accepted_privacy_notice_version =
adminUserAcceptedPrivacyNoticeVersion ?? null;
user.accepted_dpa_version = adminUserAcceptedDpaVersion ?? null;
user.email_verified = true;
}
const usersRepo = entityManager.getRepository(User);
const newUser = usersRepo.create(user);

View File

@ -129,6 +129,33 @@ export class CardLicense {
@Column({ nullable: true, type: "datetime" })
updated_by: string | null;
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: "datetime",
})
updated_at: Date;
}
@Entity({ name: "card_license_issue" })
export class CardLicenseIssue {
@PrimaryGeneratedColumn()
id: number;
@Column()
issued_at: Date;
@Column({ nullable: true, type: "datetime" })
created_by: string | null;
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: "datetime",
})
created_at: Date;
@Column({ nullable: true, type: "datetime" })
updated_by: string | null;
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: "datetime",

View File

@ -2,6 +2,7 @@ import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import {
CardLicense,
CardLicenseIssue,
License,
LicenseAllocationHistory,
} from "./entity/license.entity";
@ -9,7 +10,11 @@ import { LicensesRepositoryService } from "./licenses.repository.service";
@Module({
imports: [
TypeOrmModule.forFeature([License, CardLicense, LicenseAllocationHistory]),
TypeOrmModule.forFeature([
License,
CardLicense,
CardLicenseIssue, LicenseAllocationHistory,
]),
],
providers: [LicensesRepositoryService],
exports: [LicensesRepositoryService],

View File

@ -4,14 +4,19 @@ import {
License,
LicenseAllocationHistory,
CardLicense,
CardLicenseIssue,
} from "./entity/license.entity";
import { insertEntities } from "../../common/repository";
import { insertEntity, insertEntities } from "../../common/repository";
import { Context } from "../../common/log";
import {
LicensesInputFile,
CardLicensesInputFile,
} from "../../common/types/types";
import {AUTO_INCREMENT_START} from "../../constants/index"
import {
LICENSE_ALLOCATED_STATUS,
LICENSE_TYPE,
} from "../../constants";
@Injectable()
export class LicensesRepositoryService {
//クエリログにコメントを出力するかどうか
@ -38,7 +43,9 @@ export class LicensesRepositoryService {
license.account_id = licensesInputFile.account_id;
license.status = licensesInputFile.status;
license.type = licensesInputFile.type;
license.expiry_date = (licensesInputFile.expiry_date) ? new Date(licensesInputFile.expiry_date) : null;
license.expiry_date = licensesInputFile.expiry_date
? new Date(licensesInputFile.expiry_date)
: null;
if (licensesInputFile.allocated_user_id) {
license.allocated_user_id = licensesInputFile.allocated_user_id;
}
@ -97,22 +104,61 @@ export class LicensesRepositoryService {
): Promise<{}> {
return await this.dataSource.transaction(async (entityManager) => {
const cardLicenseRepo = entityManager.getRepository(CardLicense);
const licensesRepo = entityManager.getRepository(License);
const cardLicenseIssueRepo =
entityManager.getRepository(CardLicenseIssue);
const licenses: License[] = [];
// ライセンステーブルを作成するBULK INSERT)
for (let i = 0; i < cardLicensesInputFiles.length; i++) {
const license = new License();
license.account_id = AUTO_INCREMENT_START; // 最初に登場するアカウント(第一アカウント)
license.status = LICENSE_ALLOCATED_STATUS.UNALLOCATED;
license.type = LICENSE_TYPE.CARD;
licenses.push(license);
}
const savedLicenses = await insertEntities(
License,
licensesRepo,
licenses,
this.isCommentOut,
context
);
let newCardLicenses: CardLicense[] = [];
cardLicensesInputFiles.forEach((cardLicensesInputFile) => {
// カードライセンス発行テーブルを作成する
const cardLicenseIssue = new CardLicenseIssue();
cardLicenseIssue.issued_at = new Date();
const newCardLicenseIssue = cardLicenseIssueRepo.create(cardLicenseIssue);
const savedCardLicensesIssue = await insertEntity(
CardLicenseIssue,
cardLicenseIssueRepo,
newCardLicenseIssue,
this.isCommentOut,
context
);
const newCardLicenses: CardLicense[] = [];
// カードライセンステーブルを作成するBULK INSERT)
for (let i = 0; i < cardLicensesInputFiles.length; i++) {
const cardLicense = new CardLicense();
cardLicense.license_id = cardLicensesInputFile.license_id;
cardLicense.issue_id = cardLicensesInputFile.issue_id;
cardLicense.card_license_key = cardLicensesInputFile.card_license_key;
cardLicense.activated_at = (cardLicensesInputFile.activated_at) ? new Date(cardLicensesInputFile.activated_at) : null;
cardLicense.created_at = (cardLicensesInputFile.created_at) ? new Date(cardLicensesInputFile.created_at) : null;
cardLicense.created_by = cardLicensesInputFile.created_by;
cardLicense.updated_at = (cardLicensesInputFile.updated_at) ? new Date(cardLicensesInputFile.updated_at) : null;
cardLicense.updated_by = cardLicensesInputFile.updated_by;
cardLicense.license_id = savedLicenses[i].id; // Licenseテーブルの自動採番されたIDを挿入
cardLicense.issue_id = savedCardLicensesIssue.id; // CardLicenseIssueテーブルの自動採番されたIDを挿入
cardLicense.card_license_key =
cardLicensesInputFiles[i].card_license_key;
cardLicense.activated_at = cardLicensesInputFiles[i].activated_at
? new Date(cardLicensesInputFiles[i].activated_at)
: null;
cardLicense.created_at = cardLicensesInputFiles[i].created_at
? new Date(cardLicensesInputFiles[i].created_at)
: null;
cardLicense.created_by = cardLicensesInputFiles[i].created_by;
cardLicense.updated_at = cardLicensesInputFiles[i].updated_at
? new Date(cardLicensesInputFiles[i].updated_at)
: null;
cardLicense.updated_by = cardLicensesInputFiles[i].updated_by;
newCardLicenses.push(cardLicense);
});
}
const query = cardLicenseRepo
.createQueryBuilder()
@ -126,5 +172,4 @@ export class LicensesRepositoryService {
return {};
});
}
}

View File

@ -11,6 +11,7 @@ import {
selectEmail,
selectInputValidationErrors,
selectIsLoading,
selectOffset,
} from "features/partner/selectors";
import {
changeAdminName,
@ -19,7 +20,11 @@ import {
changeEmail,
cleanupAddPartner,
} from "features/partner/partnerSlice";
import { createPartnerAccountAsync } from "features/partner";
import {
LIMIT_PARTNER_VIEW_NUM,
createPartnerAccountAsync,
getPartnerInfoAsync,
} from "features/partner";
import close from "../../assets/images/close.svg";
import progress_activit from "../../assets/images/progress_activit.svg";
import { COUNTRY_LIST } from "../SignupPage/constants";
@ -50,6 +55,7 @@ export const AddPartnerAccountPopup: React.FC<AddPartnerAccountPopup> = (
const adminName = useSelector(selectAdminName);
const email = useSelector(selectEmail);
const isLoading = useSelector(selectIsLoading);
const offset = useSelector(selectOffset);
// ポップアップを閉じる処理
const closePopup = useCallback(() => {
@ -84,6 +90,12 @@ export const AddPartnerAccountPopup: React.FC<AddPartnerAccountPopup> = (
setIsPushCreateButton(false);
if (meta.requestStatus === "fulfilled") {
dispatch(
getPartnerInfoAsync({
limit: LIMIT_PARTNER_VIEW_NUM,
offset,
})
);
closePopup();
}
}, [

View File

@ -786,6 +786,103 @@ describe('TasksService', () => {
expect(task.optionItemList).toEqual(audioOptionItems);
}
});
it('[Author] Authorは自分が作成者のTask一覧を取得できる(ソート条件がJob_number以外)', async () => {
const notificationhubServiceMockValue =
makeDefaultNotificationhubServiceMockValue();
if (!source) fail();
const module = await makeTaskTestingModuleWithNotificaiton(
source,
notificationhubServiceMockValue,
);
if (!module) fail();
const { id: accountId } = await makeTestSimpleAccount(source);
const { id: userId, external_id } = await makeTestUser(source, {
account_id: accountId,
external_id: 'userId',
role: 'author',
author_id: 'MY_AUTHOR_ID',
});
//「バグ 3661: [FB対応]Option Itemにチェックを付けると真っ白な画面になる」の確認のため
// audio_file_idをTaskIdと異なる値にするために、AudioFileを作成
await createAudioFile(
source,
accountId,
userId,
'MY_AUTHOR_ID',
'',
'00',
);
// Taskを作成
await createTask(
source,
accountId,
userId,
'MY_AUTHOR_ID',
'WORKTYPE1',
'01',
'00000001',
'Uploaded',
);
await createTask(
source,
accountId,
userId,
'MY_AUTHOR_ID',
'WORKTYPE2',
'01',
'00000002',
'Uploaded',
);
const service = module.get<TasksService>(TasksService);
const offset = 0;
const limit = 20;
const status = ['Uploaded', 'Backup'];
// バグ 3786: [FB対応]タスク一覧画面のOptionItemがソート条件によって表示順がおかしくなる の確認のため
// Job_number以外のソート条件を指定
const paramName = 'WORK_TYPE';
const direction = 'DESC';
const { tasks, total } = await service.getTasks(
makeContext('trackingId', 'requestId'),
external_id,
[USER_ROLES.AUTHOR],
offset,
limit,
status,
paramName,
direction,
);
expect(total).toEqual(2);
{
const task = tasks[0];
expect(task.jobNumber).toEqual('00000002');
// ソート条件がJob_number以外でもOptionItemがid順に取得されていることを確認
const audioOptionItems = Array.from({ length: 10 }).map((_, i) => {
return {
optionItemLabel: `label${i}:audio_file_id${task.audioFileId}`,
optionItemValue: `value${i}:audio_file_id${task.audioFileId}`,
};
});
expect(task.optionItemList).toEqual(audioOptionItems);
}
{
const task = tasks[1];
expect(task.jobNumber).toEqual('00000001');
// ソート条件がJob_number以外でもOptionItemがid順に取得されていることを確認
const audioOptionItems = Array.from({ length: 10 }).map((_, i) => {
return {
optionItemLabel: `label${i}:audio_file_id${task.audioFileId}`,
optionItemValue: `value${i}:audio_file_id${task.audioFileId}`,
};
});
expect(task.optionItemList).toEqual(audioOptionItems);
}
});
it('[Author] Authorは同一アカウントであっても自分以外のAuhtorのTaskは取得できない', async () => {
const notificationhubServiceMockValue =
makeDefaultNotificationhubServiceMockValue();

View File

@ -179,7 +179,7 @@ describe('UsersService.confirmUser', () => {
});
expect(_subject).toBe('Account Registered Notification [U-101]');
expect(_url).toBe('http://localhost:8081/');
}, 600000);
});
it('トークンの形式が不正な場合、形式不正エラーとなる。', async () => {
if (!source) fail();
@ -2744,17 +2744,21 @@ describe('UsersService.getRelations', () => {
const worktype1 = await createWorktype(
source,
account.id,
'worktype1',
'worktypeB',
undefined,
true,
);
await createOptionItems(source, worktype1.id);
const worktype2 = await createWorktype(source, account.id, 'worktype2');
const worktype2 = await createWorktype(source, account.id, 'worktypeC');
await createOptionItems(source, worktype2.id);
const worktype3 = await createWorktype(source, account.id, 'worktypeA');
await createOptionItems(source, worktype3.id);
await createWorkflow(source, account.id, user1, worktype1.id);
await createWorkflow(source, account.id, user1, worktype2.id);
await createWorkflow(source, account.id, user1, worktype3.id);
await createWorkflow(source, account.id, user1);
await createWorkflow(source, account.id, user2, worktype1.id);
@ -2762,15 +2766,18 @@ describe('UsersService.getRelations', () => {
{
const workflows = await getWorkflows(source, account.id);
workflows.sort((a, b) => a.id - b.id);
expect(workflows.length).toBe(4);
expect(workflows.length).toBe(5);
expect(workflows[0].worktype_id).toBe(worktype1.id);
expect(workflows[0].author_id).toBe(user1);
expect(workflows[1].worktype_id).toBe(worktype2.id);
expect(workflows[1].author_id).toBe(user1);
expect(workflows[2].worktype_id).toBe(null);
expect(workflows[2].worktype_id).toBe(worktype3.id);
expect(workflows[2].author_id).toBe(user1);
expect(workflows[3].worktype_id).toBe(worktype1.id);
expect(workflows[3].author_id).toBe(user2);
expect(workflows[3].worktype_id).toBe(null);
expect(workflows[3].author_id).toBe(user1);
expect(workflows[4].worktype_id).toBe(worktype1.id);
expect(workflows[4].author_id).toBe(user2);
}
const context = makeContext(external_id, 'requestId');
@ -2786,14 +2793,17 @@ describe('UsersService.getRelations', () => {
expect(relations.authorIdList[1]).toBe('AUTHOR_2');
const workTypeList = relations.workTypeList;
expect(relations.workTypeList.length).toBe(2);
expect(workTypeList[0].workTypeId).toBe(worktype1.custom_worktype_id);
expect(relations.workTypeList.length).toBe(3);
// Workflowの作成順ではなくcustom_worktype_idの昇順で取得するためWorkTypeListの先頭はworktype3
expect(workTypeList[0].workTypeId).toBe(worktype3.custom_worktype_id);
expect(workTypeList[0].optionItemList.length).toBe(10);
expect(workTypeList[0].optionItemList[0].label).toBe('');
expect(workTypeList[0].optionItemList[0].initialValueType).toBe(2);
expect(workTypeList[0].optionItemList[0].defaultValue).toBe('');
expect(workTypeList[1].workTypeId).toBe(worktype2.custom_worktype_id);
expect(workTypeList[1].workTypeId).toBe(worktype1.custom_worktype_id);
expect(workTypeList[1].optionItemList.length).toBe(10);
expect(workTypeList[2].workTypeId).toBe(worktype2.custom_worktype_id);
expect(workTypeList[2].optionItemList.length).toBe(10);
expect(relations.isEncrypted).toBe(true);
expect(relations.encryptionPassword).toBe('password');

View File

@ -1582,78 +1582,91 @@ const makeOrder = (
priority: 'DESC',
job_number: direction,
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'STATUS':
return {
priority: 'DESC',
status: direction,
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'TRANSCRIPTION_FINISHED_DATE':
return {
priority: 'DESC',
finished_at: direction,
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'TRANSCRIPTION_STARTED_DATE':
return {
priority: 'DESC',
started_at: direction,
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'AUTHOR_ID':
return {
priority: 'DESC',
file: { author_id: direction },
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'ENCRYPTION':
return {
priority: 'DESC',
file: { is_encrypted: direction },
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'FILE_LENGTH':
return {
priority: 'DESC',
file: { duration: direction },
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'FILE_NAME':
return {
priority: 'DESC',
file: { file_name: direction },
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'FILE_SIZE':
return {
priority: 'DESC',
file: { file_size: direction },
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'RECORDING_FINISHED_DATE':
return {
priority: 'DESC',
file: { finished_at: direction },
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'RECORDING_STARTED_DATE':
return {
priority: 'DESC',
file: { started_at: direction },
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'UPLOAD_DATE':
return {
priority: 'DESC',
file: { uploaded_at: direction },
id: 'ASC',
option_items: { id: 'ASC' },
};
case 'WORK_TYPE':
return {
priority: 'DESC',
file: { work_type_id: direction },
id: 'ASC',
option_items: { id: 'ASC' },
};
default:
// switchのcase漏れが発生した場合に型エラーになるようにする

View File

@ -1166,6 +1166,14 @@ export class UsersRepositoryService {
option_items: true,
},
},
order: {
worktype: {
custom_worktype_id: 'ASC',
option_items: {
id: 'ASC',
},
},
},
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
});