Merge branch 'develop' into main
This commit is contained in:
commit
3b02f709b2
@ -663,6 +663,19 @@ export interface GetMyAccountResponse {
|
||||
*/
|
||||
'account': Account;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface GetOptionItemsResponse
|
||||
*/
|
||||
export interface GetOptionItemsResponse {
|
||||
/**
|
||||
*
|
||||
* @type {Array<GetWorktypeOptionItem>}
|
||||
* @memberof GetOptionItemsResponse
|
||||
*/
|
||||
'optionItems': Array<GetWorktypeOptionItem>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@ -908,6 +921,37 @@ export interface GetUsersResponse {
|
||||
*/
|
||||
'users': Array<User>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface GetWorktypeOptionItem
|
||||
*/
|
||||
export interface GetWorktypeOptionItem {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof GetWorktypeOptionItem
|
||||
*/
|
||||
'itemLabel': string;
|
||||
/**
|
||||
* Default / Blank / LastInput
|
||||
* @type {string}
|
||||
* @memberof GetWorktypeOptionItem
|
||||
*/
|
||||
'defaultValueType': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof GetWorktypeOptionItem
|
||||
*/
|
||||
'initialValue': string;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof GetWorktypeOptionItem
|
||||
*/
|
||||
'id': number;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@ -920,6 +964,12 @@ export interface GetWorktypesResponse {
|
||||
* @memberof GetWorktypesResponse
|
||||
*/
|
||||
'worktypes': Array<Worktype>;
|
||||
/**
|
||||
* Active WorktypeIDに設定されているWorkTypeの内部ID
|
||||
* @type {number}
|
||||
* @memberof GetWorktypesResponse
|
||||
*/
|
||||
'acrive'?: number;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@ -1145,6 +1195,19 @@ export interface PartnerLicenseInfo {
|
||||
*/
|
||||
'issueRequesting': number;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface PostActiveWorktypeRequest
|
||||
*/
|
||||
export interface PostActiveWorktypeRequest {
|
||||
/**
|
||||
* Active WorkTypeIDにするWorktypeの内部ID
|
||||
* @type {number}
|
||||
* @memberof PostActiveWorktypeRequest
|
||||
*/
|
||||
'id'?: number;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@ -1238,6 +1301,31 @@ export interface PostUpdateUserRequest {
|
||||
*/
|
||||
'prompt'?: boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface PostWorktypeOptionItem
|
||||
*/
|
||||
export interface PostWorktypeOptionItem {
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PostWorktypeOptionItem
|
||||
*/
|
||||
'itemLabel': string;
|
||||
/**
|
||||
* Default / Blank / LastInput
|
||||
* @type {string}
|
||||
* @memberof PostWorktypeOptionItem
|
||||
*/
|
||||
'defaultValueType': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PostWorktypeOptionItem
|
||||
*/
|
||||
'initialValue': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@ -1577,6 +1665,19 @@ export interface TypistGroup {
|
||||
*/
|
||||
'name': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface UpdateOptionItemsRequest
|
||||
*/
|
||||
export interface UpdateOptionItemsRequest {
|
||||
/**
|
||||
*
|
||||
* @type {Array<PostWorktypeOptionItem>}
|
||||
* @memberof UpdateOptionItemsRequest
|
||||
*/
|
||||
'optionItems': Array<PostWorktypeOptionItem>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@ -1744,6 +1845,46 @@ export interface Worktype {
|
||||
*/
|
||||
export const AccountsApiAxiosParamCreator = function (configuration?: Configuration) {
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {PostActiveWorktypeRequest} postActiveWorktypeRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
activeWorktype: async (postActiveWorktypeRequest: PostActiveWorktypeRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'postActiveWorktypeRequest' is not null or undefined
|
||||
assertParamExists('activeWorktype', 'postActiveWorktypeRequest', postActiveWorktypeRequest)
|
||||
const localVarPath = `/accounts/active-worktype`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(postActiveWorktypeRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* ライセンス発行をキャンセルします
|
||||
* @summary
|
||||
@ -2035,6 +2176,44 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {number} id Worktypeの内部ID
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getOptionItems: async (id: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('getOptionItems', 'id', id)
|
||||
const localVarPath = `/accounts/worktypes/{id}/option-items`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
@ -2352,6 +2531,50 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {number} id Worktypeの内部ID
|
||||
* @param {UpdateOptionItemsRequest} updateOptionItemsRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
updateOptionItems: async (id: number, updateOptionItemsRequest: UpdateOptionItemsRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'id' is not null or undefined
|
||||
assertParamExists('updateOptionItems', 'id', id)
|
||||
// verify required parameter 'updateOptionItemsRequest' is not null or undefined
|
||||
assertParamExists('updateOptionItems', 'updateOptionItemsRequest', updateOptionItemsRequest)
|
||||
const localVarPath = `/accounts/worktypes/{id}/option-items`
|
||||
.replace(`{${"id"}}`, encodeURIComponent(String(id)));
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(updateOptionItemsRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* ログインしているユーザーのアカウント配下でIDで指定されたタイピストグループを更新します
|
||||
* @summary
|
||||
@ -2450,6 +2673,17 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
|
||||
export const AccountsApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosParamCreator = AccountsApiAxiosParamCreator(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {PostActiveWorktypeRequest} postActiveWorktypeRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async activeWorktype(postActiveWorktypeRequest: PostActiveWorktypeRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.activeWorktype(postActiveWorktypeRequest, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
* ライセンス発行をキャンセルします
|
||||
* @summary
|
||||
@ -2536,6 +2770,17 @@ export const AccountsApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getMyAccount(options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {number} id Worktypeの内部ID
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async getOptionItems(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<GetOptionItemsResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.getOptionItems(id, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
@ -2622,6 +2867,18 @@ export const AccountsApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.issueLicense(issueLicenseRequest, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {number} id Worktypeの内部ID
|
||||
* @param {UpdateOptionItemsRequest} updateOptionItemsRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async updateOptionItems(id: number, updateOptionItemsRequest: UpdateOptionItemsRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.updateOptionItems(id, updateOptionItemsRequest, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
* ログインしているユーザーのアカウント配下でIDで指定されたタイピストグループを更新します
|
||||
* @summary
|
||||
@ -2656,6 +2913,16 @@ export const AccountsApiFp = function(configuration?: Configuration) {
|
||||
export const AccountsApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
|
||||
const localVarFp = AccountsApiFp(configuration)
|
||||
return {
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {PostActiveWorktypeRequest} postActiveWorktypeRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
activeWorktype(postActiveWorktypeRequest: PostActiveWorktypeRequest, options?: any): AxiosPromise<object> {
|
||||
return localVarFp.activeWorktype(postActiveWorktypeRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* ライセンス発行をキャンセルします
|
||||
* @summary
|
||||
@ -2734,6 +3001,16 @@ export const AccountsApiFactory = function (configuration?: Configuration, baseP
|
||||
getMyAccount(options?: any): AxiosPromise<GetMyAccountResponse> {
|
||||
return localVarFp.getMyAccount(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {number} id Worktypeの内部ID
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
getOptionItems(id: number, options?: any): AxiosPromise<GetOptionItemsResponse> {
|
||||
return localVarFp.getOptionItems(id, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
@ -2812,6 +3089,17 @@ export const AccountsApiFactory = function (configuration?: Configuration, baseP
|
||||
issueLicense(issueLicenseRequest: IssueLicenseRequest, options?: any): AxiosPromise<object> {
|
||||
return localVarFp.issueLicense(issueLicenseRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {number} id Worktypeの内部ID
|
||||
* @param {UpdateOptionItemsRequest} updateOptionItemsRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
updateOptionItems(id: number, updateOptionItemsRequest: UpdateOptionItemsRequest, options?: any): AxiosPromise<object> {
|
||||
return localVarFp.updateOptionItems(id, updateOptionItemsRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* ログインしているユーザーのアカウント配下でIDで指定されたタイピストグループを更新します
|
||||
* @summary
|
||||
@ -2844,6 +3132,18 @@ export const AccountsApiFactory = function (configuration?: Configuration, baseP
|
||||
* @extends {BaseAPI}
|
||||
*/
|
||||
export class AccountsApi extends BaseAPI {
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {PostActiveWorktypeRequest} postActiveWorktypeRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof AccountsApi
|
||||
*/
|
||||
public activeWorktype(postActiveWorktypeRequest: PostActiveWorktypeRequest, options?: AxiosRequestConfig) {
|
||||
return AccountsApiFp(this.configuration).activeWorktype(postActiveWorktypeRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* ライセンス発行をキャンセルします
|
||||
* @summary
|
||||
@ -2938,6 +3238,18 @@ export class AccountsApi extends BaseAPI {
|
||||
return AccountsApiFp(this.configuration).getMyAccount(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {number} id Worktypeの内部ID
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof AccountsApi
|
||||
*/
|
||||
public getOptionItems(id: number, options?: AxiosRequestConfig) {
|
||||
return AccountsApiFp(this.configuration).getOptionItems(id, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
@ -3032,6 +3344,19 @@ export class AccountsApi extends BaseAPI {
|
||||
return AccountsApiFp(this.configuration).issueLicense(issueLicenseRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {number} id Worktypeの内部ID
|
||||
* @param {UpdateOptionItemsRequest} updateOptionItemsRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof AccountsApi
|
||||
*/
|
||||
public updateOptionItems(id: number, updateOptionItemsRequest: UpdateOptionItemsRequest, options?: AxiosRequestConfig) {
|
||||
return AccountsApiFp(this.configuration).updateOptionItems(id, updateOptionItemsRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* ログインしているユーザーのアカウント配下でIDで指定されたタイピストグループを更新します
|
||||
* @summary
|
||||
|
||||
1
dictation_client/src/features/partner/constants.ts
Normal file
1
dictation_client/src/features/partner/constants.ts
Normal file
@ -0,0 +1 @@
|
||||
export const LIMIT_PARTNER_VIEW_NUM = 15;
|
||||
@ -2,3 +2,4 @@ export * from "./state";
|
||||
export * from "./operations";
|
||||
export * from "./selectors";
|
||||
export * from "./partnerSlice";
|
||||
export * from "./constants";
|
||||
|
||||
@ -3,7 +3,11 @@ import type { RootState } from "app/store";
|
||||
import { ErrorObject, createErrorObject } from "common/errors";
|
||||
import { getTranslationID } from "translation";
|
||||
import { openSnackbar } from "features/ui/uiSlice";
|
||||
import { AccountsApi, CreatePartnerAccountRequest } from "../../api/api";
|
||||
import {
|
||||
AccountsApi,
|
||||
CreatePartnerAccountRequest,
|
||||
GetPartnersResponse,
|
||||
} from "../../api/api";
|
||||
import { Configuration } from "../../api/configuration";
|
||||
|
||||
export const createPartnerAccountAsync = createAsyncThunk<
|
||||
@ -62,3 +66,53 @@ export const createPartnerAccountAsync = createAsyncThunk<
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
|
||||
// パートナー一覧取得APIからパートナーのアカウント情報をもらう
|
||||
export const getPartnerInfoAsync = createAsyncThunk<
|
||||
// 正常時の戻り値の型
|
||||
GetPartnersResponse,
|
||||
{
|
||||
// パラメータ
|
||||
limit: number;
|
||||
offset: number;
|
||||
},
|
||||
{
|
||||
// rejectした時の返却値の型
|
||||
rejectValue: {
|
||||
error: ErrorObject;
|
||||
};
|
||||
}
|
||||
>("partner/getPartnerInfoAsync", async (args, thunkApi) => {
|
||||
const { limit, offset } = args;
|
||||
const { getState } = thunkApi;
|
||||
const state = getState() as RootState;
|
||||
const { configuration, accessToken } = state.auth;
|
||||
const config = new Configuration(configuration);
|
||||
const accountsApi = new AccountsApi(config);
|
||||
|
||||
try {
|
||||
const res = await accountsApi.getPartners(limit, offset, {
|
||||
headers: { authorization: `Bearer ${accessToken}` },
|
||||
});
|
||||
const ret = {
|
||||
partners: res.data.partners,
|
||||
total: res.data.total,
|
||||
};
|
||||
return ret;
|
||||
} catch (e) {
|
||||
const error = createErrorObject(e);
|
||||
const errorMessage =
|
||||
error.code === "E000108"
|
||||
? getTranslationID("common.message.permissionDeniedError")
|
||||
: getTranslationID("common.message.internalServerError");
|
||||
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
})
|
||||
);
|
||||
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
|
||||
@ -1,8 +1,15 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { PartnerState } from "./state";
|
||||
import { createPartnerAccountAsync } from "./operations";
|
||||
import { createPartnerAccountAsync, getPartnerInfoAsync } from "./operations";
|
||||
import { LIMIT_PARTNER_VIEW_NUM } from "./constants";
|
||||
|
||||
const initialState: PartnerState = {
|
||||
domain: {
|
||||
getPartnersInfo: {
|
||||
total: 0,
|
||||
partners: [],
|
||||
},
|
||||
},
|
||||
apps: {
|
||||
addPartner: {
|
||||
companyName: "",
|
||||
@ -10,6 +17,8 @@ const initialState: PartnerState = {
|
||||
adminName: "",
|
||||
email: "",
|
||||
},
|
||||
limit: LIMIT_PARTNER_VIEW_NUM,
|
||||
offset: 0,
|
||||
isLoading: false,
|
||||
},
|
||||
};
|
||||
@ -37,6 +46,20 @@ export const partnerSlice = createSlice({
|
||||
cleanupAddPartner: (state) => {
|
||||
state.apps.addPartner = initialState.apps.addPartner;
|
||||
},
|
||||
cleanupApps: (state) => {
|
||||
state.domain = initialState.domain;
|
||||
},
|
||||
savePageInfo: (
|
||||
state,
|
||||
action: PayloadAction<{
|
||||
limit: number;
|
||||
offset: number;
|
||||
}>
|
||||
) => {
|
||||
const { limit, offset } = action.payload;
|
||||
state.apps.limit = limit;
|
||||
state.apps.offset = offset;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(createPartnerAccountAsync.pending, (state) => {
|
||||
@ -48,6 +71,17 @@ export const partnerSlice = createSlice({
|
||||
builder.addCase(createPartnerAccountAsync.rejected, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
builder.addCase(getPartnerInfoAsync.pending, (state) => {
|
||||
state.apps.isLoading = true;
|
||||
});
|
||||
builder.addCase(getPartnerInfoAsync.fulfilled, (state, action) => {
|
||||
state.domain.getPartnersInfo.total = action.payload.total;
|
||||
state.domain.getPartnersInfo.partners = action.payload.partners;
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
builder.addCase(getPartnerInfoAsync.rejected, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
export const {
|
||||
@ -56,5 +90,6 @@ export const {
|
||||
changeCompany,
|
||||
changeCountry,
|
||||
cleanupAddPartner,
|
||||
savePageInfo,
|
||||
} = partnerSlice.actions;
|
||||
export default partnerSlice.reducer;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { RootState } from "app/store";
|
||||
import { ceil, floor } from "lodash";
|
||||
|
||||
export const selectInputValidationErrors = (state: RootState) => {
|
||||
// 必須項目のチェック
|
||||
@ -33,3 +34,22 @@ export const selectEmail = (state: RootState) =>
|
||||
state.partner.apps.addPartner.email;
|
||||
export const selectIsLoading = (state: RootState) =>
|
||||
state.partner.apps.isLoading;
|
||||
|
||||
export const selectPartnersInfo = (state: RootState) =>
|
||||
state.partner.domain.getPartnersInfo;
|
||||
export const selectTotal = (state: RootState) =>
|
||||
state.partner.domain.getPartnersInfo.total;
|
||||
export const seletctLimit = (state: RootState) => state.partner.apps.limit;
|
||||
export const selectOffset = (state: RootState) => state.partner.apps.offset;
|
||||
export const selectTotalPage = (state: RootState) => {
|
||||
const { limit } = state.partner.apps;
|
||||
const { total } = state.partner.domain.getPartnersInfo;
|
||||
const page = ceil(total / limit);
|
||||
return page;
|
||||
};
|
||||
|
||||
export const selectCurrentPage = (state: RootState) => {
|
||||
const { limit, offset } = state.partner.apps;
|
||||
const page = floor(offset / limit) + 1;
|
||||
return page;
|
||||
};
|
||||
|
||||
@ -1,10 +1,20 @@
|
||||
import { CreatePartnerAccountRequest } from "../../api/api";
|
||||
import {
|
||||
CreatePartnerAccountRequest,
|
||||
GetPartnersResponse,
|
||||
} from "../../api/api";
|
||||
|
||||
export interface PartnerState {
|
||||
domain: Domain;
|
||||
apps: Apps;
|
||||
}
|
||||
|
||||
export interface Domain {
|
||||
getPartnersInfo: GetPartnersResponse;
|
||||
}
|
||||
|
||||
export interface Apps {
|
||||
limit: number;
|
||||
offset: number;
|
||||
addPartner: CreatePartnerAccountRequest;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
@ -51,6 +51,7 @@ export const worktypeSlice = createSlice({
|
||||
state.apps.isLoading = true;
|
||||
});
|
||||
builder.addCase(listWorktypesAsync.fulfilled, (state, action) => {
|
||||
// TODO:Active WorktypeIDも取得する
|
||||
const { worktypes } = action.payload;
|
||||
state.domain.worktypes = worktypes;
|
||||
state.apps.isLoading = false;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { AppDispatch } from "app/store";
|
||||
import React, { useState, useCallback, useEffect } from "react";
|
||||
import React, { useState, useCallback } from "react";
|
||||
import styles from "styles/app.module.scss";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getTranslationID } from "translation";
|
||||
|
||||
@ -1,44 +1,89 @@
|
||||
import { useMsal } from "@azure/msal-react";
|
||||
/* eslint-disable jsx-a11y/control-has-associated-label */
|
||||
import { AppDispatch } from "app/store";
|
||||
import { UpdateTokenTimer } from "components/auth/updateTokenTimer";
|
||||
import Footer from "components/footer";
|
||||
import Header from "components/header";
|
||||
import { clearToken } from "features/auth";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import styles from "styles/app.module.scss";
|
||||
import { loadAccessToken, isApproveTier } from "features/auth/utils";
|
||||
import postAdd from "../../assets/images/post_add.svg";
|
||||
import { decodeToken } from "../../common/decodeToken";
|
||||
import { isApproveTier } from "features/auth/utils";
|
||||
import {
|
||||
LIMIT_PARTNER_VIEW_NUM,
|
||||
selectCurrentPage,
|
||||
selectIsLoading,
|
||||
selectOffset,
|
||||
selectTotal,
|
||||
selectTotalPage,
|
||||
getPartnerInfoAsync,
|
||||
selectPartnersInfo,
|
||||
} from "features/partner/index";
|
||||
import { savePageInfo } from "features/partner/partnerSlice";
|
||||
import { getTranslationID } from "translation";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import personAdd from "../../assets/images/person_add.svg";
|
||||
import { TIERS } from "../../components/auth/constants";
|
||||
import { AddPartnerAccountPopup } from "./addPartnerAccountPopup";
|
||||
import checkFill from "../../assets/images/check_fill.svg";
|
||||
|
||||
const PartnerPage: React.FC = (): JSX.Element => {
|
||||
const { instance } = useMsal();
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
const [isPopupOpen, setIsPopupOpen] = useState(false);
|
||||
const [t] = useTranslation();
|
||||
const total = useSelector(selectTotal);
|
||||
const totalPage = useSelector(selectTotalPage);
|
||||
const offset = useSelector(selectOffset);
|
||||
const currentPage = useSelector(selectCurrentPage);
|
||||
const isLoading = useSelector(selectIsLoading);
|
||||
|
||||
/* XXX 本実装の際に消す想定です。
|
||||
POデモ時に階層情報を表示するための実装です。 */
|
||||
const getUserTier = () => {
|
||||
const jwt = loadAccessToken(); // トークンを取得
|
||||
const token = jwt && decodeToken(jwt); // トークンをデコード
|
||||
// apiからの値取得関係
|
||||
const partnerInfo = useSelector(selectPartnersInfo);
|
||||
|
||||
if (token && token.tier) {
|
||||
return token.tier.toString(); // ユーザーの階層情報を取得
|
||||
}
|
||||
|
||||
return "error!"; // 階層情報が見つからない場合はerror!を返す
|
||||
// 階層表示用
|
||||
const tierNames: { [key: number]: string } = {
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
1: t(getTranslationID("common.label.tier1")),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
2: t(getTranslationID("common.label.tier2")),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
3: t(getTranslationID("common.label.tier3")),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
4: t(getTranslationID("common.label.tier4")),
|
||||
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
5: t(getTranslationID("common.label.tier5")),
|
||||
};
|
||||
|
||||
/* XXX 本実装の際に消す想定です。
|
||||
ログインしているアカウントの階層を確認するために実装 */
|
||||
const userTier = getUserTier();
|
||||
// 第1~3階層にボタンを表示する
|
||||
const isVisible = isApproveTier([TIERS.TIER1, TIERS.TIER2, TIERS.TIER3]);
|
||||
const isVisibleButton = isApproveTier([
|
||||
TIERS.TIER1,
|
||||
TIERS.TIER2,
|
||||
TIERS.TIER3,
|
||||
]);
|
||||
|
||||
// 第4階層でdealerManagementを表示
|
||||
const isVisibleDealerManagement = isApproveTier([TIERS.TIER4]);
|
||||
|
||||
const onOpen = useCallback(() => {
|
||||
setIsPopupOpen(true);
|
||||
}, [setIsPopupOpen]);
|
||||
|
||||
// パートナー取得APIを呼び出す
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
getPartnerInfoAsync({
|
||||
limit: LIMIT_PARTNER_VIEW_NUM,
|
||||
offset,
|
||||
})
|
||||
);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [dispatch, currentPage]);
|
||||
|
||||
// ページネーションのボタンクリック時のアクション
|
||||
const movePage = (targetOffset: number) => {
|
||||
dispatch(
|
||||
savePageInfo({ limit: LIMIT_PARTNER_VIEW_NUM, offset: targetOffset })
|
||||
);
|
||||
};
|
||||
|
||||
// HTML
|
||||
return (
|
||||
<>
|
||||
@ -52,52 +97,167 @@ const PartnerPage: React.FC = (): JSX.Element => {
|
||||
<Header userName="XXXXXX" />
|
||||
<UpdateTokenTimer />
|
||||
<main className={styles.main}>
|
||||
<ul className={styles.menuAction}>
|
||||
<li>
|
||||
{isVisible && (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<a
|
||||
className={`${styles.menuLink} ${styles.isActive}`}
|
||||
onClick={onOpen}
|
||||
>
|
||||
<img src={postAdd} alt="" className={styles.menuIcon} />
|
||||
Add Account
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
<form className={styles.form}>
|
||||
<dl className={`${styles.formList} ${styles.hasbg}`}>
|
||||
<dt className={styles.formTitle} />
|
||||
<dt />
|
||||
<dd className="">
|
||||
<input
|
||||
type="text"
|
||||
size={40}
|
||||
name=""
|
||||
value={`Tier:${userTier}`}
|
||||
maxLength={20}
|
||||
className={styles.formInput}
|
||||
/>
|
||||
</dd>
|
||||
</dl>
|
||||
</form>
|
||||
<div className={styles.pageHeader}>
|
||||
<h1 className={styles.pageTitle}>
|
||||
{t(getTranslationID("partnerPage.label.title"))}
|
||||
</h1>
|
||||
</div>
|
||||
<section className={styles.partners}>
|
||||
<div>
|
||||
<ul className={styles.menuAction}>
|
||||
<li>
|
||||
{isVisibleButton && (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<a
|
||||
className={`${styles.menuLink} ${styles.isActive}`}
|
||||
onClick={onOpen}
|
||||
>
|
||||
<img src={personAdd} alt="" className={styles.menuIcon} />
|
||||
{t(getTranslationID("partnerPage.label.addAccount"))}
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
<table
|
||||
className={`${styles.table} ${styles.partner} ${
|
||||
styles.marginBtm3
|
||||
} ${isVisibleDealerManagement ? styles.role4 : ""}`}
|
||||
>
|
||||
<tr className={styles.tableHeader}>
|
||||
<th className={styles.clm0}>{/** th is empty */}</th>
|
||||
<th>
|
||||
<a>{t(getTranslationID("partnerPage.label.name"))}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a>{t(getTranslationID("partnerPage.label.category"))}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a>{t(getTranslationID("partnerPage.label.accountId"))}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a>{t(getTranslationID("partnerPage.label.country"))}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a>
|
||||
{t(getTranslationID("partnerPage.label.primaryAdmin"))}
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a>{t(getTranslationID("partnerPage.label.email"))}</a>
|
||||
</th>
|
||||
<th>
|
||||
<a>
|
||||
{t(
|
||||
getTranslationID("partnerPage.label.dealerManagement")
|
||||
)}
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
{!isLoading &&
|
||||
partnerInfo.partners.length !== 0 &&
|
||||
partnerInfo.partners.map((x) => (
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
<tr>
|
||||
<td className={styles.clm0}>
|
||||
<ul className={styles.menuInTable}>
|
||||
<li>
|
||||
{isVisibleButton && (
|
||||
<a>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"partnerPage.label.deleteAccount"
|
||||
)
|
||||
)}
|
||||
</a>
|
||||
)}
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td>{x.name}</td>
|
||||
<td>{tierNames[x.tier]}</td>
|
||||
<td>{x.accountId}</td>
|
||||
<td>{x.country}</td>
|
||||
<td>{x.primaryAdmin ?? "-"}</td>
|
||||
<td>{x.email ?? "-"}</td>
|
||||
<td>
|
||||
<img
|
||||
src={checkFill}
|
||||
alt=""
|
||||
className={styles.icCheckCircle}
|
||||
/>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</table>
|
||||
{/** pagenation */}
|
||||
<div className={styles.pagenation}>
|
||||
<nav className={styles.pagenationNav}>
|
||||
<span className={styles.pagenationTotal}>
|
||||
{`${total} ${t(
|
||||
getTranslationID("partnerPage.label.partners")
|
||||
)}`}
|
||||
</span>
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
|
||||
<a
|
||||
className={`${
|
||||
!isLoading && currentPage !== 1 ? styles.isActive : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
movePage(0);
|
||||
}}
|
||||
>
|
||||
«
|
||||
</a>
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
|
||||
<a
|
||||
className={`${
|
||||
!isLoading && currentPage !== 1 ? styles.isActive : ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
movePage((currentPage - 2) * LIMIT_PARTNER_VIEW_NUM);
|
||||
}}
|
||||
>
|
||||
‹
|
||||
</a>
|
||||
{` ${total !== 0 ? currentPage : 0} of ${
|
||||
total !== 0 ? totalPage : 0
|
||||
} `}
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
|
||||
<a
|
||||
className={`${
|
||||
!isLoading && currentPage < totalPage
|
||||
? styles.isActive
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
movePage(currentPage * LIMIT_PARTNER_VIEW_NUM);
|
||||
}}
|
||||
>
|
||||
›
|
||||
</a>
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
|
||||
<a
|
||||
className={`${
|
||||
!isLoading && currentPage < totalPage
|
||||
? styles.isActive
|
||||
: ""
|
||||
}`}
|
||||
onClick={() => {
|
||||
movePage((totalPage - 1) * LIMIT_PARTNER_VIEW_NUM);
|
||||
}}
|
||||
>
|
||||
»
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.buttonText}
|
||||
onClick={() => {
|
||||
instance.logout({ postLogoutRedirectUri: "/" });
|
||||
dispatch(clearToken());
|
||||
}}
|
||||
>
|
||||
sign out
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PartnerPage;
|
||||
|
||||
@ -2165,8 +2165,7 @@ tr.isSelected .menuInTable li a {
|
||||
}
|
||||
.formChange ul.chooseMember li input + label:hover,
|
||||
.formChange ul.holdMember li input + label:hover {
|
||||
background: #e6e6e6 url(../assets/images/arrow_circle_left.svg) no-repeat left
|
||||
center;
|
||||
background: #e6e6e6 url(../assets/images/arrow_circle_left.svg) no-repeat left center;
|
||||
background-size: 1.3rem;
|
||||
}
|
||||
.formChange ul.chooseMember li input:checked + label,
|
||||
@ -2177,8 +2176,8 @@ tr.isSelected .menuInTable li a {
|
||||
}
|
||||
.formChange ul.chooseMember li input:checked + label:hover,
|
||||
.formChange ul.holdMember li input:checked + label:hover {
|
||||
background: #e6e6e6 url(../assets/images/arrow_circle_right.svg) no-repeat
|
||||
right center;
|
||||
background: #e6e6e6 url(../assets/images/arrow_circle_right.svg) no-repeat right
|
||||
center;
|
||||
background-size: 1.3rem;
|
||||
}
|
||||
.formChange > p {
|
||||
@ -2337,8 +2336,7 @@ tr.isSelected .menuInTable li a {
|
||||
}
|
||||
.formChange ul.chooseMember li input + label:hover,
|
||||
.formChange ul.holdMember li input + label:hover {
|
||||
background: #e6e6e6 url(../assets/images/arrow_circle_left.svg) no-repeat left
|
||||
center;
|
||||
background: #e6e6e6 url(../assets/images/arrow_circle_left.svg) no-repeat left center;
|
||||
background-size: 1.3rem;
|
||||
}
|
||||
.formChange ul.chooseMember li input:checked + label,
|
||||
@ -2349,8 +2347,8 @@ tr.isSelected .menuInTable li a {
|
||||
}
|
||||
.formChange ul.chooseMember li input:checked + label:hover,
|
||||
.formChange ul.holdMember li input:checked + label:hover {
|
||||
background: #e6e6e6 url(../assets/images/arrow_circle_right.svg) no-repeat
|
||||
right center;
|
||||
background: #e6e6e6 url(../assets/images/arrow_circle_right.svg) no-repeat right
|
||||
center;
|
||||
background-size: 1.3rem;
|
||||
}
|
||||
.formChange > p {
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
-- +migrate Up
|
||||
ALTER TABLE `option_items`
|
||||
MODIFY COLUMN `item_label` VARCHAR(16) NOT NULL COMMENT 'アイテムラベル',
|
||||
MODIFY COLUMN `initial_value` VARCHAR(20) NOT NULL COMMENT 'オプションアイテム初期値';
|
||||
|
||||
-- +migrate Down
|
||||
ALTER TABLE `option_items`
|
||||
MODIFY COLUMN `item_label` VARCHAR(50) NOT NULL COMMENT 'アイテムラベル',
|
||||
MODIFY COLUMN `initial_value` VARCHAR(50) NOT NULL COMMENT 'オプションアイテム初期値';
|
||||
@ -224,6 +224,59 @@
|
||||
},
|
||||
"tags": ["accounts"],
|
||||
"security": [{ "bearer": [] }]
|
||||
},
|
||||
"post": {
|
||||
"operationId": "me",
|
||||
"summary": "",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateAccountInfoRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "成功時のレスポンス",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateAccountInfoResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "パラメータ不正/アカウント・ユーザー不在/管理者ユーザ不在",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "認証エラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "想定外のサーバーエラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["accounts"],
|
||||
"security": [{ "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/accounts/typists": {
|
||||
@ -1015,6 +1068,61 @@
|
||||
"security": [{ "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/accounts/active-worktype": {
|
||||
"post": {
|
||||
"operationId": "activeWorktype",
|
||||
"summary": "",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PostActiveWorktypeRequest"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "成功時のレスポンス",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/PostActiveWorktypeResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "WorktypeIDが存在しない",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"401": {
|
||||
"description": "認証エラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "想定外のサーバーエラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["accounts"],
|
||||
"security": [{ "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/accounts/partners": {
|
||||
"get": {
|
||||
"operationId": "getPartners",
|
||||
@ -2985,6 +3093,10 @@
|
||||
"worktypes": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/components/schemas/Worktype" }
|
||||
},
|
||||
"acrive": {
|
||||
"type": "number",
|
||||
"description": "Active WorktypeIDに設定されているWorkTypeの内部ID"
|
||||
}
|
||||
},
|
||||
"required": ["worktypes"]
|
||||
@ -3015,7 +3127,33 @@
|
||||
"required": ["worktypeId"]
|
||||
},
|
||||
"UpdateWorktypeResponse": { "type": "object", "properties": {} },
|
||||
"WorktypeOptionItem": {
|
||||
"GetWorktypeOptionItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"itemLabel": { "type": "string", "maxLength": 16 },
|
||||
"defaultValueType": {
|
||||
"type": "string",
|
||||
"maxLength": 20,
|
||||
"description": "Default / Blank / LastInput"
|
||||
},
|
||||
"initialValue": { "type": "string", "maxLength": 20 },
|
||||
"id": { "type": "number" }
|
||||
},
|
||||
"required": ["itemLabel", "defaultValueType", "initialValue", "id"]
|
||||
},
|
||||
"GetOptionItemsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"optionItems": {
|
||||
"maxItems": 10,
|
||||
"minItems": 10,
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/components/schemas/GetWorktypeOptionItem" }
|
||||
}
|
||||
},
|
||||
"required": ["optionItems"]
|
||||
},
|
||||
"PostWorktypeOptionItem": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"itemLabel": { "type": "string", "maxLength": 16 },
|
||||
@ -3028,18 +3166,6 @@
|
||||
},
|
||||
"required": ["itemLabel", "defaultValueType", "initialValue"]
|
||||
},
|
||||
"GetOptionItemsResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"optionItems": {
|
||||
"maxItems": 10,
|
||||
"minItems": 10,
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/components/schemas/WorktypeOptionItem" }
|
||||
}
|
||||
},
|
||||
"required": ["optionItems"]
|
||||
},
|
||||
"UpdateOptionItemsRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -3047,12 +3173,22 @@
|
||||
"maxItems": 10,
|
||||
"minItems": 10,
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/components/schemas/WorktypeOptionItem" }
|
||||
"items": { "$ref": "#/components/schemas/PostWorktypeOptionItem" }
|
||||
}
|
||||
},
|
||||
"required": ["optionItems"]
|
||||
},
|
||||
"UpdateOptionItemsResponse": { "type": "object", "properties": {} },
|
||||
"PostActiveWorktypeRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"description": "Active WorkTypeIDにするWorktypeの内部ID"
|
||||
}
|
||||
}
|
||||
},
|
||||
"PostActiveWorktypeResponse": { "type": "object", "properties": {} },
|
||||
"Partner": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -3094,6 +3230,34 @@
|
||||
},
|
||||
"required": ["total", "partners"]
|
||||
},
|
||||
"UpdateAccountInfoRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"parentAccountId": {
|
||||
"type": "number",
|
||||
"description": "親アカウントのID"
|
||||
},
|
||||
"delegationPermission": {
|
||||
"type": "boolean",
|
||||
"description": "代行操作許可"
|
||||
},
|
||||
"primaryAdminUserId": {
|
||||
"type": "number",
|
||||
"description": "プライマリ管理者ID"
|
||||
},
|
||||
"secondryAdminUserId": {
|
||||
"type": "number",
|
||||
"description": "セカンダリ管理者ID"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"parentAccountId",
|
||||
"delegationPermission",
|
||||
"primaryAdminUserId",
|
||||
"secondryAdminUserId"
|
||||
]
|
||||
},
|
||||
"UpdateAccountInfoResponse": { "type": "object", "properties": {} },
|
||||
"ConfirmRequest": {
|
||||
"type": "object",
|
||||
"properties": { "token": { "type": "string" } },
|
||||
@ -3235,7 +3399,7 @@
|
||||
},
|
||||
"activeWorktype": {
|
||||
"type": "string",
|
||||
"description": "アカウントがデフォルトで利用するWorkTypeID(アカウントに紐づくWorkTypeIDから一つ指定)"
|
||||
"description": "アカウントがデフォルトで利用するWorkTypeID(アカウントに紐づくWorkTypeIDから一つ指定。activeWorktypeがなければ空文字を返却する)"
|
||||
},
|
||||
"audioFormat": {
|
||||
"type": "string",
|
||||
|
||||
@ -41,7 +41,6 @@ import { UserGroupsRepositoryModule } from './repositories/user_groups/user_grou
|
||||
import { SortCriteriaRepositoryModule } from './repositories/sort_criteria/sort_criteria.repository.module';
|
||||
import { TemplateFilesRepositoryModule } from './repositories/template_files/template_files.repository.module';
|
||||
import { WorktypesRepositoryModule } from './repositories/worktypes/worktypes.repository.module';
|
||||
import { OptionItemsRepositoryModule } from './repositories/option_items/option_items.repository.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -97,7 +96,6 @@ import { OptionItemsRepositoryModule } from './repositories/option_items/option_
|
||||
AuthGuardsModule,
|
||||
SortCriteriaRepositoryModule,
|
||||
WorktypesRepositoryModule,
|
||||
OptionItemsRepositoryModule,
|
||||
],
|
||||
controllers: [
|
||||
HealthController,
|
||||
|
||||
@ -41,7 +41,7 @@ export const errors: Errors = {
|
||||
E010810: 'Cancellation period expired error',
|
||||
E010811: 'Already license allocated Error',
|
||||
E010908: 'Typist Group not exist Error',
|
||||
E011001: 'Thiw WorkTypeID already used Error',
|
||||
E011001: 'This WorkTypeID already used Error',
|
||||
E011002: 'WorkTypeID create limit exceeded Error',
|
||||
E011003: 'WorkTypeID not found Error',
|
||||
};
|
||||
|
||||
@ -30,7 +30,6 @@ import { NotificationhubService } from '../../gateways/notificationhub/notificat
|
||||
import { FilesService } from '../../features/files/files.service';
|
||||
import { LicensesService } from '../../features/licenses/licenses.service';
|
||||
import { TasksService } from '../../features/tasks/tasks.service';
|
||||
import { OptionItemsRepositoryModule } from '../../repositories/option_items/option_items.repository.module';
|
||||
|
||||
export const makeTestingModule = async (
|
||||
datasource: DataSource,
|
||||
@ -66,7 +65,6 @@ export const makeTestingModule = async (
|
||||
AuthGuardsModule,
|
||||
SortCriteriaRepositoryModule,
|
||||
WorktypesRepositoryModule,
|
||||
OptionItemsRepositoryModule,
|
||||
],
|
||||
providers: [
|
||||
AuthService,
|
||||
|
||||
@ -5,11 +5,19 @@ import {
|
||||
ValidationOptions,
|
||||
registerDecorator,
|
||||
} from 'class-validator';
|
||||
import { PostWorktypeOptionItem } from '../../features/accounts/types/types';
|
||||
import { OPTION_ITEM_VALUE_TYPE } from '../../constants';
|
||||
|
||||
@ValidatorConstraint()
|
||||
export class IsWorktypeIdCharacters implements ValidatorConstraintInterface {
|
||||
export class IsRecorderAllowedCharacters
|
||||
implements ValidatorConstraintInterface
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
validate(value: string) {
|
||||
if (value === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 正規表現でWorktypeIDのチェックを行う
|
||||
// 以下の禁則文字を除く半角英数記号
|
||||
// \ (backslash)
|
||||
@ -28,23 +36,69 @@ export class IsWorktypeIdCharacters implements ValidatorConstraintInterface {
|
||||
}
|
||||
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
return `WorktypeID rule not satisfied`;
|
||||
return `Recorder allowed characters rule not satisfied`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WorktypeIDで使用できる文字列かをチェックする
|
||||
* レコーダーで使用できる文字列かをチェックする
|
||||
* @param [validationOptions]
|
||||
* @returns
|
||||
*/
|
||||
export function IsWorktypeId(validationOptions?: ValidationOptions) {
|
||||
export function IsRecorderAllowed(validationOptions?: ValidationOptions) {
|
||||
return function (object: object, propertyName: string) {
|
||||
registerDecorator({
|
||||
name: 'IsWorktypeId',
|
||||
name: 'IsRecorderAllowed',
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
constraints: [],
|
||||
options: validationOptions,
|
||||
validator: IsWorktypeIdCharacters,
|
||||
validator: IsRecorderAllowedCharacters,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ValidatorConstraint()
|
||||
export class IsNotEmptyValue implements ValidatorConstraintInterface {
|
||||
propertyName: string;
|
||||
constructor(propertyName: string) {
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
validate(value: any, args: ValidationArguments): boolean {
|
||||
const request = args.object as PostWorktypeOptionItem;
|
||||
const { itemLabel, defaultValueType } = request;
|
||||
|
||||
// アイテムラベルがあり、Defaultの場合は必ず値が必要
|
||||
if (
|
||||
itemLabel !== '' &&
|
||||
defaultValueType === OPTION_ITEM_VALUE_TYPE.DEFAULT &&
|
||||
value === ''
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
defaultMessage(): string {
|
||||
return `When default value is default, ${this.propertyName} cannot be empty`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 有効なオプションアイテムでDefaultの場合にinitialValueが空でないことをチェックする
|
||||
* @template T
|
||||
* @param [validationOptions]
|
||||
* @returns
|
||||
*/
|
||||
export function IsInitialValue(validationOptions?: ValidationOptions) {
|
||||
return (object: PostWorktypeOptionItem, propertyName: string) => {
|
||||
registerDecorator({
|
||||
name: 'IsInitialValue',
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
constraints: [],
|
||||
options: validationOptions,
|
||||
validator: new IsNotEmptyValue(propertyName),
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
@ -56,13 +56,12 @@ import {
|
||||
UpdateOptionItemsResponse,
|
||||
UpdateOptionItemsRequestParam,
|
||||
UpdateOptionItemsRequest,
|
||||
PostActiveWorktypeRequest,
|
||||
PostActiveWorktypeResponse,
|
||||
UpdateAccountInfoRequest,
|
||||
UpdateAccountInfoResponse,
|
||||
} from './types/types';
|
||||
import {
|
||||
USER_ROLES,
|
||||
ADMIN_ROLES,
|
||||
TIERS,
|
||||
OPTION_ITEM_VALUE_TYPE,
|
||||
} from '../../constants';
|
||||
import { USER_ROLES, ADMIN_ROLES, TIERS } from '../../constants';
|
||||
import { AuthGuard } from '../../common/guards/auth/authguards';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { retrieveAuthorizationToken } from '../../common/http/helper';
|
||||
@ -815,63 +814,13 @@ export class AccountsController {
|
||||
|
||||
const context = makeContext(userId);
|
||||
|
||||
console.log('id', id);
|
||||
console.log(context.trackingId);
|
||||
const optionItems = await this.accountService.getOptionItems(
|
||||
context,
|
||||
userId,
|
||||
id,
|
||||
);
|
||||
|
||||
return {
|
||||
optionItems: [
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
],
|
||||
};
|
||||
return optionItems;
|
||||
}
|
||||
|
||||
@Post('/worktypes/:id/option-items')
|
||||
@ -911,8 +860,52 @@ export class AccountsController {
|
||||
|
||||
const context = makeContext(userId);
|
||||
|
||||
await this.accountService.updateOptionItems(
|
||||
context,
|
||||
userId,
|
||||
id,
|
||||
optionItems,
|
||||
);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@Post('/active-worktype')
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
type: PostActiveWorktypeResponse,
|
||||
description: '成功時のレスポンス',
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.BAD_REQUEST,
|
||||
description: 'WorktypeIDが存在しない',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.UNAUTHORIZED,
|
||||
description: '認証エラー',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
description: '想定外のサーバーエラー',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiOperation({ operationId: 'activeWorktype' })
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
|
||||
async activeWorktype(
|
||||
@Req() req: Request,
|
||||
@Body() body: PostActiveWorktypeRequest,
|
||||
): Promise<PostActiveWorktypeResponse> {
|
||||
const { id } = body;
|
||||
const token = retrieveAuthorizationToken(req);
|
||||
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
|
||||
console.log('id', id);
|
||||
console.log('optionItems', optionItems);
|
||||
console.log(context.trackingId);
|
||||
|
||||
return {};
|
||||
@ -966,4 +959,60 @@ export class AccountsController {
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
@Post('/me')
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
type: UpdateAccountInfoResponse,
|
||||
description: '成功時のレスポンス',
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.BAD_REQUEST,
|
||||
description: 'パラメータ不正/アカウント・ユーザー不在/管理者ユーザ不在',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.UNAUTHORIZED,
|
||||
description: '認証エラー',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
description: '想定外のサーバーエラー',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiOperation({ operationId: 'me' })
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(AuthGuard)
|
||||
@UseGuards(
|
||||
RoleGuard.requireds({
|
||||
roles: [ADMIN_ROLES.ADMIN],
|
||||
}),
|
||||
)
|
||||
async updateAccountInfo(
|
||||
@Req() req: Request,
|
||||
@Body() body: UpdateAccountInfoRequest,
|
||||
): Promise<UpdateAccountInfoResponse> {
|
||||
const {
|
||||
parentAccountId,
|
||||
delegationPermission,
|
||||
primaryAdminUserId,
|
||||
secondryAdminUserId,
|
||||
} = body;
|
||||
const token = retrieveAuthorizationToken(req);
|
||||
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
// 仮。API実装で本実装
|
||||
// await this.accountService.updateAccountInfo(
|
||||
// context,
|
||||
// userId,
|
||||
// parentAccountId,
|
||||
// delegationPermission,
|
||||
// primaryAdminUserId,
|
||||
// secondryAdminUserId,
|
||||
// );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
createLicense,
|
||||
createLicenseOrder,
|
||||
createLicenseSetExpiryDateAndStatus,
|
||||
createOptionItems,
|
||||
createWorktype,
|
||||
getOptionItems,
|
||||
getSortCriteria,
|
||||
@ -1505,6 +1506,7 @@ describe('AccountsService', () => {
|
||||
makeDefaultLicensesRepositoryMockValue();
|
||||
const worktypesRepositoryMockValue =
|
||||
makeDefaultWorktypesRepositoryMockValue();
|
||||
|
||||
const service = await makeAccountsServiceMock(
|
||||
accountsRepositoryMockValue,
|
||||
usersRepositoryMockValue,
|
||||
@ -3863,6 +3865,457 @@ describe('updateWorktype', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOptionItems', () => {
|
||||
let source: DataSource = null;
|
||||
beforeEach(async () => {
|
||||
source = new DataSource({
|
||||
type: 'sqlite',
|
||||
database: ':memory:',
|
||||
logging: false,
|
||||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return source.initialize();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
|
||||
it('指定WorktypeIDに紐づいたOptionItemを取得できる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
const worktype = await createWorktype(source, account.id, 'worktype1');
|
||||
const optionItems = await createOptionItems(source, worktype.id);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
expect(optionItems.length).toBe(10);
|
||||
expect(optionItems[0].item_label).toBe('');
|
||||
expect(optionItems[0].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(optionItems[0].initial_value).toBe('');
|
||||
}
|
||||
|
||||
const resOptionItems = await service.getOptionItems(
|
||||
context,
|
||||
admin.external_id,
|
||||
worktype.id,
|
||||
);
|
||||
|
||||
//実行結果を確認
|
||||
{
|
||||
expect(resOptionItems.optionItems.length).toBe(10);
|
||||
expect(resOptionItems.optionItems.map((x) => x.id)).toEqual(
|
||||
optionItems.map((x) => x.id),
|
||||
);
|
||||
expect(resOptionItems.optionItems[0].itemLabel).toBe('');
|
||||
expect(resOptionItems.optionItems[0].defaultValueType).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(resOptionItems.optionItems[0].initialValue).toBe('');
|
||||
}
|
||||
});
|
||||
|
||||
it('WorktypeIDが存在しない場合、400エラーとなること', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
const worktype = await createWorktype(source, account.id, 'worktype1');
|
||||
const optionItems = await createOptionItems(source, worktype.id);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
expect(optionItems.length).toBe(10);
|
||||
expect(optionItems[0].item_label).toBe('');
|
||||
expect(optionItems[0].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(optionItems[0].initial_value).toBe('');
|
||||
}
|
||||
|
||||
try {
|
||||
await service.getOptionItems(context, admin.external_id, 999);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E011003'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('DBアクセスに失敗した場合、500エラーを返却する', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
const worktype = await createWorktype(source, account.id, 'worktype1');
|
||||
const optionItems = await createOptionItems(source, worktype.id);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
expect(optionItems.length).toBe(10);
|
||||
expect(optionItems[0].item_label).toBe('');
|
||||
expect(optionItems[0].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(optionItems[0].initial_value).toBe('');
|
||||
}
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const worktypesService = module.get<WorktypesRepositoryService>(
|
||||
WorktypesRepositoryService,
|
||||
);
|
||||
worktypesService.getOptionItems = jest.fn().mockRejectedValue('DB failed');
|
||||
|
||||
try {
|
||||
await service.getOptionItems(context, admin.external_id, worktype.id);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateOptionItems', () => {
|
||||
let source: DataSource = null;
|
||||
beforeEach(async () => {
|
||||
source = new DataSource({
|
||||
type: 'sqlite',
|
||||
database: ':memory:',
|
||||
logging: false,
|
||||
entities: [__dirname + '/../../**/*.entity{.ts,.js}'],
|
||||
synchronize: true, // trueにすると自動的にmigrationが行われるため注意
|
||||
});
|
||||
return source.initialize();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
|
||||
it('指定WorktypeIDに紐づいたOptionItemを更新できる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
const worktype = await createWorktype(source, account.id, 'worktype1');
|
||||
const optionItems = await createOptionItems(source, worktype.id);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
expect(optionItems.length).toBe(10);
|
||||
|
||||
expect(optionItems[0].item_label).toBe('');
|
||||
expect(optionItems[0].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(optionItems[0].initial_value).toBe('');
|
||||
|
||||
expect(optionItems[1].item_label).toBe('');
|
||||
expect(optionItems[1].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(optionItems[1].initial_value).toBe('');
|
||||
|
||||
expect(optionItems[2].item_label).toBe('');
|
||||
expect(optionItems[2].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(optionItems[2].initial_value).toBe('');
|
||||
|
||||
expect(optionItems[3].item_label).toBe('');
|
||||
expect(optionItems[3].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(optionItems[3].initial_value).toBe('');
|
||||
}
|
||||
|
||||
await service.updateOptionItems(context, admin.external_id, worktype.id, [
|
||||
{
|
||||
itemLabel: 'itemLabel1',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: 'initialValue1',
|
||||
},
|
||||
{
|
||||
itemLabel: 'itemLabel2',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.BLANK,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: 'itemLabel3',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.LAST_INPUT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.BLANK,
|
||||
initialValue: 'initialValue4',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
]);
|
||||
|
||||
//実行結果を確認
|
||||
{
|
||||
const resOptionItems = await getOptionItems(source, worktype.id);
|
||||
expect(resOptionItems.length).toBe(10);
|
||||
|
||||
expect(resOptionItems[0].item_label).toBe('itemLabel1');
|
||||
expect(resOptionItems[0].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(resOptionItems[0].initial_value).toBe('initialValue1');
|
||||
|
||||
expect(resOptionItems[1].item_label).toBe('itemLabel2');
|
||||
expect(resOptionItems[1].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.BLANK,
|
||||
);
|
||||
expect(resOptionItems[1].initial_value).toBe('');
|
||||
|
||||
expect(resOptionItems[2].item_label).toBe('itemLabel3');
|
||||
expect(resOptionItems[2].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.LAST_INPUT,
|
||||
);
|
||||
expect(resOptionItems[2].initial_value).toBe('');
|
||||
|
||||
expect(resOptionItems[3].item_label).toBe('');
|
||||
expect(resOptionItems[3].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.BLANK,
|
||||
);
|
||||
expect(resOptionItems[3].initial_value).toBe('');
|
||||
}
|
||||
});
|
||||
|
||||
it('WorktypeIDが存在しない場合、400エラーとなること', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
const worktype = await createWorktype(source, account.id, 'worktype1');
|
||||
const optionItems = await createOptionItems(source, worktype.id);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
expect(optionItems.length).toBe(10);
|
||||
expect(optionItems[0].item_label).toBe('');
|
||||
expect(optionItems[0].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(optionItems[0].initial_value).toBe('');
|
||||
}
|
||||
|
||||
try {
|
||||
await service.updateOptionItems(context, admin.external_id, 999, [
|
||||
{
|
||||
itemLabel: 'itemLabel1',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: 'initialValue1',
|
||||
},
|
||||
{
|
||||
itemLabel: 'itemLabel2',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.BLANK,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: 'itemLabel3',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.LAST_INPUT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
]);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E011003'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('DBアクセスに失敗した場合、500エラーを返却する', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
// 第五階層のアカウント作成
|
||||
const { account, admin } = await makeTestAccount(source, { tier: 5 });
|
||||
|
||||
const service = module.get<AccountsService>(AccountsService);
|
||||
const context = makeContext(admin.external_id);
|
||||
|
||||
const worktype = await createWorktype(source, account.id, 'worktype1');
|
||||
const optionItems = await createOptionItems(source, worktype.id);
|
||||
|
||||
//作成したデータを確認
|
||||
{
|
||||
expect(optionItems.length).toBe(10);
|
||||
expect(optionItems[0].item_label).toBe('');
|
||||
expect(optionItems[0].default_value_type).toBe(
|
||||
OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
);
|
||||
expect(optionItems[0].initial_value).toBe('');
|
||||
}
|
||||
|
||||
//DBアクセスに失敗するようにする
|
||||
const worktypesService = module.get<WorktypesRepositoryService>(
|
||||
WorktypesRepositoryService,
|
||||
);
|
||||
worktypesService.updateOptionItems = jest
|
||||
.fn()
|
||||
.mockRejectedValue('DB failed');
|
||||
|
||||
try {
|
||||
await service.updateOptionItems(context, admin.external_id, worktype.id, [
|
||||
{
|
||||
itemLabel: 'itemLabel1',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: 'initialValue1',
|
||||
},
|
||||
{
|
||||
itemLabel: 'itemLabel2',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.BLANK,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: 'itemLabel3',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.LAST_INPUT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
{
|
||||
itemLabel: '',
|
||||
defaultValueType: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initialValue: '',
|
||||
},
|
||||
]);
|
||||
} catch (e) {
|
||||
if (e instanceof HttpException) {
|
||||
expect(e.getStatus()).toEqual(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(e.getResponse()).toEqual(makeErrorResponse('E009999'));
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('ライセンス発行キャンセル', () => {
|
||||
let source: DataSource = null;
|
||||
beforeEach(async () => {
|
||||
|
||||
@ -10,7 +10,12 @@ import {
|
||||
} from '../../gateways/adb2c/adb2c.service';
|
||||
import { Account } from '../../repositories/accounts/entity/account.entity';
|
||||
import { User } from '../../repositories/users/entity/user.entity';
|
||||
import { TIERS, USER_ROLES, ADB2C_SIGN_IN_TYPE } from '../../constants';
|
||||
import {
|
||||
TIERS,
|
||||
USER_ROLES,
|
||||
ADB2C_SIGN_IN_TYPE,
|
||||
OPTION_ITEM_VALUE_TYPE,
|
||||
} from '../../constants';
|
||||
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
|
||||
import {
|
||||
TypistGroup,
|
||||
@ -23,7 +28,9 @@ import {
|
||||
GetMyAccountResponse,
|
||||
GetTypistGroupResponse,
|
||||
GetWorktypesResponse,
|
||||
GetOptionItemsResponse,
|
||||
GetPartnersResponse,
|
||||
PostWorktypeOptionItem,
|
||||
} from './types/types';
|
||||
import {
|
||||
DateWithZeroTime,
|
||||
@ -1314,6 +1321,137 @@ export class AccountsService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ワークタイプに紐づいたオプションアイテム一覧を取得します
|
||||
* @param context
|
||||
* @param externalId
|
||||
* @param id Worktypeの内部ID
|
||||
* @returns option items
|
||||
*/
|
||||
async getOptionItems(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
id: number,
|
||||
): Promise<GetOptionItemsResponse> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.trackingId}] ${this.getOptionItems.name} | params: { ` +
|
||||
`externalId: ${externalId}, ` +
|
||||
`id: ${id} };`,
|
||||
);
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
|
||||
// オプションアイテム一覧を取得する
|
||||
const optionItems = await this.worktypesRepository.getOptionItems(
|
||||
accountId,
|
||||
id,
|
||||
);
|
||||
|
||||
return {
|
||||
optionItems: optionItems.map((x) => ({
|
||||
id: x.id,
|
||||
itemLabel: x.item_label,
|
||||
defaultValueType: x.default_value_type,
|
||||
initialValue: x.initial_value,
|
||||
})),
|
||||
};
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
// 内部IDで指定されたWorktypeが存在しない場合は400エラーを返す
|
||||
case WorktypeIdNotFoundError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E011003'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
default:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.trackingId}] ${this.getOptionItems.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* オプションアイテムを更新します
|
||||
* @param context
|
||||
* @param externalId
|
||||
* @param id ワークタイプの内部ID
|
||||
* @param optionItems
|
||||
* @returns option items
|
||||
*/
|
||||
async updateOptionItems(
|
||||
context: Context,
|
||||
externalId: string,
|
||||
id: number,
|
||||
optionItems: PostWorktypeOptionItem[],
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.trackingId}] ${this.updateOptionItems.name} | params: { ` +
|
||||
`externalId: ${externalId}, ` +
|
||||
`id: ${id}, ` +
|
||||
`optionItems: ${JSON.stringify(optionItems)} };`,
|
||||
);
|
||||
try {
|
||||
// 外部IDをもとにユーザー情報を取得する
|
||||
const { account_id: accountId } =
|
||||
await this.usersRepository.findUserByExternalId(externalId);
|
||||
|
||||
// オプションアイテムを更新する
|
||||
await this.worktypesRepository.updateOptionItems(
|
||||
accountId,
|
||||
id,
|
||||
// initialValueはdefaultValueTypeがDEFAULTの場合以外は空文字を設定する
|
||||
optionItems.map((item) => ({
|
||||
itemLabel: item.itemLabel,
|
||||
defaultValueType: item.defaultValueType,
|
||||
initialValue:
|
||||
item.defaultValueType === OPTION_ITEM_VALUE_TYPE.DEFAULT
|
||||
? item.initialValue
|
||||
: '',
|
||||
})),
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(e);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
// 内部IDで指定されたWorktypeが存在しない場合は400エラーを返す
|
||||
case WorktypeIdNotFoundError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E011003'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
default:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.trackingId}] ${this.updateOptionItems.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* パートナー一覧を取得します
|
||||
* @param context
|
||||
|
||||
@ -7,7 +7,8 @@ import { SortCriteria } from '../../../repositories/sort_criteria/entity/sort_cr
|
||||
import { UserGroup } from '../../../repositories/user_groups/entity/user_group.entity';
|
||||
import { UserGroupMember } from '../../../repositories/user_groups/entity/user_group_member.entity';
|
||||
import { Worktype } from '../../../repositories/worktypes/entity/worktype.entity';
|
||||
import { OptionItem } from '../../../repositories/option_items/entity/option_item.entity';
|
||||
import { OptionItem } from '../../../repositories/worktypes/entity/option_item.entity';
|
||||
import { OPTION_ITEM_VALUE_TYPE } from '../../../constants';
|
||||
|
||||
/**
|
||||
* テスト ユーティリティ: すべてのソート条件を取得する
|
||||
@ -153,6 +154,35 @@ export const getWorktypes = async (
|
||||
});
|
||||
};
|
||||
|
||||
// オプションアイテムを作成する
|
||||
export const createOptionItems = async (
|
||||
datasource: DataSource,
|
||||
worktypeId: number,
|
||||
): Promise<OptionItem[]> => {
|
||||
const optionItems = [];
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
optionItems.push({
|
||||
worktype_id: worktypeId,
|
||||
item_label: '',
|
||||
default_value_type: OPTION_ITEM_VALUE_TYPE.DEFAULT,
|
||||
initial_value: '',
|
||||
created_by: 'test_runner',
|
||||
created_at: new Date(),
|
||||
updated_by: 'updater',
|
||||
updated_at: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
await datasource.getRepository(OptionItem).insert(optionItems);
|
||||
|
||||
const items = datasource
|
||||
.getRepository(OptionItem)
|
||||
.find({ where: { worktype_id: worktypeId } });
|
||||
|
||||
return items;
|
||||
};
|
||||
|
||||
// オプションアイテムを取得する
|
||||
export const getOptionItems = async (
|
||||
datasource: DataSource,
|
||||
|
||||
@ -16,7 +16,10 @@ import {
|
||||
import { IsAdminPasswordvalid } from '../../../common/validators/admin.validator';
|
||||
import { IsUnique } from '../../../common/validators/IsUnique.validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsWorktypeId } from '../../../common/validators/worktype.validator';
|
||||
import {
|
||||
IsInitialValue,
|
||||
IsRecorderAllowed,
|
||||
} from '../../../common/validators/worktype.validator';
|
||||
import { OPTION_ITEM_VALUE_TYPE } from '../../../constants';
|
||||
|
||||
export class CreateAccountRequest {
|
||||
@ -350,13 +353,19 @@ export class Worktype {
|
||||
export class GetWorktypesResponse {
|
||||
@ApiProperty({ type: [Worktype] })
|
||||
worktypes: Worktype[];
|
||||
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
description: 'Active WorktypeIDに設定されているWorkTypeの内部ID',
|
||||
})
|
||||
acrive?: number | undefined;
|
||||
}
|
||||
|
||||
export class CreateWorktypesRequest {
|
||||
@ApiProperty({ minLength: 1, description: 'WorktypeID' })
|
||||
@ApiProperty({ minLength: 1, maxLength: 255, description: 'WorktypeID' })
|
||||
@MinLength(1)
|
||||
@MaxLength(255)
|
||||
@IsWorktypeId()
|
||||
@IsRecorderAllowed()
|
||||
worktypeId: string;
|
||||
@ApiProperty({ description: 'Worktypeの説明', required: false })
|
||||
@MaxLength(255)
|
||||
@ -370,7 +379,7 @@ export class UpdateWorktypesRequest {
|
||||
@ApiProperty({ minLength: 1, description: 'WorktypeID' })
|
||||
@MinLength(1)
|
||||
@MaxLength(255)
|
||||
@IsWorktypeId()
|
||||
@IsRecorderAllowed()
|
||||
worktypeId: string;
|
||||
@ApiProperty({ description: 'Worktypeの説明', required: false })
|
||||
@MaxLength(255)
|
||||
@ -380,9 +389,10 @@ export class UpdateWorktypesRequest {
|
||||
|
||||
export class UpdateWorktypeResponse {}
|
||||
|
||||
export class WorktypeOptionItem {
|
||||
export class PostWorktypeOptionItem {
|
||||
@ApiProperty({ maxLength: 16 })
|
||||
@MaxLength(16)
|
||||
@IsRecorderAllowed()
|
||||
itemLabel: string;
|
||||
@ApiProperty({
|
||||
maxLength: 20,
|
||||
@ -393,15 +403,22 @@ export class WorktypeOptionItem {
|
||||
defaultValueType: string;
|
||||
@ApiProperty({ maxLength: 20 })
|
||||
@MaxLength(20)
|
||||
@IsRecorderAllowed()
|
||||
@IsInitialValue()
|
||||
initialValue: string;
|
||||
}
|
||||
export class GetWorktypeOptionItem extends PostWorktypeOptionItem {
|
||||
@ApiProperty()
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class GetOptionItemsResponse {
|
||||
@ApiProperty({
|
||||
maxItems: 10,
|
||||
minItems: 10,
|
||||
type: [WorktypeOptionItem],
|
||||
type: [GetWorktypeOptionItem],
|
||||
})
|
||||
optionItems: WorktypeOptionItem[];
|
||||
optionItems: GetWorktypeOptionItem[];
|
||||
}
|
||||
|
||||
export class GetOptionItemsRequestParam {
|
||||
@ -416,14 +433,14 @@ export class UpdateOptionItemsRequest {
|
||||
@ApiProperty({
|
||||
maxItems: 10,
|
||||
minItems: 10,
|
||||
type: [WorktypeOptionItem],
|
||||
type: [PostWorktypeOptionItem],
|
||||
})
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => WorktypeOptionItem)
|
||||
@Type(() => PostWorktypeOptionItem)
|
||||
@ArrayMinSize(10)
|
||||
@ArrayMaxSize(10)
|
||||
optionItems: WorktypeOptionItem[];
|
||||
optionItems: PostWorktypeOptionItem[];
|
||||
}
|
||||
|
||||
export class UpdateOptionItemsResponse {}
|
||||
@ -444,6 +461,20 @@ export class UpdateWorktypeRequestParam {
|
||||
id: number;
|
||||
}
|
||||
|
||||
export class PostActiveWorktypeRequest {
|
||||
@ApiProperty({
|
||||
required: false,
|
||||
description: 'Active WorkTypeIDにするWorktypeの内部ID',
|
||||
})
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(0)
|
||||
id?: number | undefined;
|
||||
}
|
||||
|
||||
export class PostActiveWorktypeResponse {}
|
||||
|
||||
export class GetPartnersRequest {
|
||||
@ApiProperty({ description: '取得件数' })
|
||||
@IsInt()
|
||||
@ -490,3 +521,18 @@ export type PartnerInfoFromDb = {
|
||||
primaryAccountExternalId: string;
|
||||
dealerManagement: boolean;
|
||||
};
|
||||
|
||||
export class UpdateAccountInfoRequest {
|
||||
@ApiProperty({ description: '親アカウントのID' })
|
||||
parentAccountId: number;
|
||||
@ApiProperty({ description: '代行操作許可' })
|
||||
delegationPermission: boolean;
|
||||
@ApiProperty({ description: 'プライマリ管理者ID' })
|
||||
@IsOptional()
|
||||
primaryAdminUserId?: number;
|
||||
@ApiProperty({ description: 'セカンダリ管理者ID' })
|
||||
@IsOptional()
|
||||
secondryAdminUserId?: number;
|
||||
}
|
||||
|
||||
export class UpdateAccountInfoResponse {}
|
||||
|
||||
@ -164,7 +164,7 @@ export class GetRelationsResponse {
|
||||
encryptionPassword?: string | undefined;
|
||||
@ApiProperty({
|
||||
description:
|
||||
'アカウントがデフォルトで利用するWorkTypeID(アカウントに紐づくWorkTypeIDから一つ指定)',
|
||||
'アカウントがデフォルトで利用するWorkTypeID(アカウントに紐づくWorkTypeIDから一つ指定。activeWorktypeがなければ空文字を返却する)',
|
||||
})
|
||||
activeWorktype: string;
|
||||
@ApiProperty({ description: '録音形式: DSS/DS2(SP)/DS2(QP): DS2固定' })
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { OptionItem } from './entity/option_item.entity';
|
||||
import { OptionItemsRepositoryService } from './option_items.repository.service';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([OptionItem])],
|
||||
providers: [OptionItemsRepositoryService],
|
||||
exports: [OptionItemsRepositoryService],
|
||||
})
|
||||
export class OptionItemsRepositoryModule {}
|
||||
@ -1,7 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class OptionItemsRepositoryService {
|
||||
constructor(private dataSource: DataSource) {}
|
||||
}
|
||||
@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Worktype } from './entity/worktype.entity';
|
||||
import { WorktypesRepositoryService } from './worktypes.repository.service';
|
||||
import { OptionItem } from './entity/option_item.entity';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Worktype])],
|
||||
imports: [TypeOrmModule.forFeature([Worktype, OptionItem])],
|
||||
providers: [WorktypesRepositoryService],
|
||||
exports: [WorktypesRepositoryService],
|
||||
})
|
||||
|
||||
@ -11,7 +11,8 @@ import {
|
||||
WorktypeIdMaxCountError,
|
||||
WorktypeIdNotFoundError,
|
||||
} from './errors/types';
|
||||
import { OptionItem } from '../option_items/entity/option_item.entity';
|
||||
import { OptionItem } from './entity/option_item.entity';
|
||||
import { PostWorktypeOptionItem } from '../../features/accounts/types/types';
|
||||
|
||||
@Injectable()
|
||||
export class WorktypesRepositoryService {
|
||||
@ -135,4 +136,80 @@ export class WorktypesRepositoryService {
|
||||
await worktypeRepo.save(worktype);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* オプションアイテム一覧を取得する
|
||||
* @param accountId
|
||||
* @param worktypeId worktypeの内部ID
|
||||
* @returns option items
|
||||
*/
|
||||
async getOptionItems(
|
||||
accountId: number,
|
||||
worktypeId: number,
|
||||
): Promise<OptionItem[]> {
|
||||
return await this.dataSource.transaction(async (entityManager) => {
|
||||
const repoWorktype = entityManager.getRepository(Worktype);
|
||||
const repoOptionItem = entityManager.getRepository(OptionItem);
|
||||
|
||||
const worktype = await repoWorktype.findOne({
|
||||
where: { account_id: accountId, id: worktypeId },
|
||||
});
|
||||
|
||||
// ワークタイプが存在しない場合はエラー
|
||||
if (!worktype) {
|
||||
throw new WorktypeIdNotFoundError(
|
||||
`Worktype is not found. id: ${worktypeId}`,
|
||||
);
|
||||
}
|
||||
|
||||
const optionItems = await repoOptionItem.find({
|
||||
where: { worktype_id: worktypeId },
|
||||
});
|
||||
|
||||
return optionItems;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* オプションアイテムを更新する
|
||||
* @param accountId
|
||||
* @param id ワークタイプの内部ID
|
||||
* @param optionItems
|
||||
* @returns option items
|
||||
*/
|
||||
async updateOptionItems(
|
||||
accountId: number,
|
||||
worktypeId: number,
|
||||
worktypeOptionItems: PostWorktypeOptionItem[],
|
||||
): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
const worktypeRepo = entityManager.getRepository(Worktype);
|
||||
const optionItemRepo = entityManager.getRepository(OptionItem);
|
||||
|
||||
const worktype = await worktypeRepo.findOne({
|
||||
where: { account_id: accountId, id: worktypeId },
|
||||
});
|
||||
// ワークタイプが存在しない場合はエラー
|
||||
if (!worktype) {
|
||||
throw new WorktypeIdNotFoundError(
|
||||
`Worktype is not found. id: ${worktypeId}`,
|
||||
);
|
||||
}
|
||||
|
||||
// 引数で渡されたオプションアイテムをDB保存用オプションアイテムに変換
|
||||
const optionItems = worktypeOptionItems.map((item) => {
|
||||
const optionItem = new OptionItem();
|
||||
optionItem.worktype_id = worktype.id;
|
||||
optionItem.item_label = item.itemLabel;
|
||||
optionItem.default_value_type = item.defaultValueType;
|
||||
optionItem.initial_value = item.initialValue;
|
||||
|
||||
return optionItem;
|
||||
});
|
||||
|
||||
// ワークタイプに紐づくオプションアイテムを削除してから再登録
|
||||
await optionItemRepo.delete({ worktype_id: worktypeId });
|
||||
await optionItemRepo.save(optionItems);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user