Merged PR 313: 第五階層のShortageについて数値をlicensesummaryのものと同じにする

## 概要
[Task2283: 第五階層のShortageについて数値をlicensesummaryのものと同じにする](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2283)

以下のプルリクで受けた指摘点を修正しました。
https://dev.azure.com/ODMSCloud/ODMS%20Cloud/_git/ODMS%20Cloud/pullrequest/273
具体的な修正内容は以下になります。
・子アカウントの数分だけDBクエリしにいく問題を修正
・有効期限の閾値となる日時を算出するclassを追加

## レビューポイント
ご指摘いただいた問題がこの対応で解消されているか。

## UIの変更
なし

## 動作確認状況
ローカルで動作確認済み

## 補足
なし
This commit is contained in:
oura.a 2023-08-08 08:11:56 +00:00
parent c304271494
commit 98e9937d9d
6 changed files with 236 additions and 71 deletions

View File

@ -14,6 +14,7 @@ import {
createAccount,
createLicense,
createLicenseOrder,
createLicenseSetExpiryDateAndStatus,
} from './test/utility';
import { DataSource } from 'typeorm';
import { makeTestingModule } from '../../common/test/modules';
@ -346,7 +347,7 @@ const expectedAccountLisenceCounts = {
isStorageAvailable: false,
};
describe('createPartnerAccount', () => {
describe('getPartnerAccount', () => {
let source: DataSource = null;
beforeEach(async () => {
source = new DataSource({
@ -387,6 +388,20 @@ describe('createPartnerAccount', () => {
'CHILDCORP2',
);
// 第二にリクエストを投げる用の第三を作成
const { accountId: childAccountId3 } = await createAccount(
source,
childAccountId1,
3,
'CHILDCORP3',
);
const { accountId: childAccountId4 } = await createAccount(
source,
childAccountId2,
3,
'CHILDCORP4',
);
// 所有ライセンスを追加3、子11、子22
await createLicense(source, parentAccountId);
await createLicense(source, parentAccountId);
@ -407,6 +422,10 @@ describe('createPartnerAccount', () => {
);
await createLicenseOrder(source, childAccountId2, parentAccountId, 5);
// ライセンス注文を追加子3→子110ライセンス、子4→子210ライセンス
await createLicenseOrder(source, childAccountId3, childAccountId1, 10);
await createLicenseOrder(source, childAccountId4, childAccountId2, 10);
const service = module.get<AccountsService>(AccountsService);
const accountId = parentAccountId;
const offset = 0;
@ -426,14 +445,139 @@ describe('createPartnerAccount', () => {
expect(response.childrenPartnerLicenses[0].tier).toBe(2);
expect(response.childrenPartnerLicenses[0].stockLicense).toBe(1);
expect(response.childrenPartnerLicenses[0].issueRequesting).toBe(10);
expect(response.childrenPartnerLicenses[0].shortage).toBe(9);
expect(response.childrenPartnerLicenses[1].companyName).toBe('CHILDCORP2');
expect(response.childrenPartnerLicenses[1].tier).toBe(2);
expect(response.childrenPartnerLicenses[1].stockLicense).toBe(2);
expect(response.childrenPartnerLicenses[1].issueRequesting).toBe(5);
expect(response.childrenPartnerLicenses[1].shortage).toBe(8);
});
});
describe('getPartnerAccount', () => {
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('パラメータのアカウント自身と子アカウントに紐つくライセンス情報を取得する(第五のshortage確認)', async () => {
const module = await makeTestingModule(source);
// 親アカウントと子アカウント2つ作成
const { accountId: parentAccountId } = await createAccount(
source,
0,
4,
'PARENTCORP',
);
const { accountId: childAccountId1 } = await createAccount(
source,
parentAccountId,
5,
'CHILDCORP1',
);
const { accountId: childAccountId2 } = await createAccount(
source,
parentAccountId,
5,
'CHILDCORP2',
);
const { accountId: childAccountId3 } = await createAccount(
source,
parentAccountId,
5,
'CHILDCORP3',
);
// 有効期限が14日後のライセンスを追加5ライセンス
let expiryDate = new Date();
expiryDate.setDate(expiryDate.getDate() + 14);
expiryDate.setHours(23, 59, 59, 999);
for (let i = 0; i < 5; i++) {
await createLicenseSetExpiryDateAndStatus(
source,
childAccountId1,
expiryDate,
'Allocated',
);
await createLicenseSetExpiryDateAndStatus(
source,
childAccountId2,
expiryDate,
'Allocated',
);
}
// 有効期限が15日後のライセンスを追加
expiryDate.setDate(expiryDate.getDate() + 15);
expiryDate.setHours(23, 59, 59, 999);
await createLicenseSetExpiryDateAndStatus(
source,
childAccountId3,
expiryDate,
'Allocated',
);
// 有効期限が迫っていないライセンスを追加子1各ステータスのライセンスを1つずつ、計4つ
const status = ['Unallocated', 'Allocated', 'Reusable', 'Deleted'];
status.forEach(async (element) => {
await createLicenseSetExpiryDateAndStatus(
source,
childAccountId1,
new Date(2500, 1, 1, 23, 59, 59),
element,
);
});
// 有効期限が迫っていないライセンスを追加子2Unallocatedを10件
for (let i = 0; i < 10; i++) {
await createLicenseSetExpiryDateAndStatus(
source,
childAccountId2,
new Date(2500, 1, 1, 23, 59, 59),
'Unallocated',
);
}
// 有効期限未設定のライセンスを1件追加子1
await createLicense(source, childAccountId1);
const service = module.get<AccountsService>(AccountsService);
const accountId = parentAccountId;
const offset = 0;
const limit = 20;
const response = await service.getPartnerLicenses(limit, offset, accountId);
// 有効期限間近5件 有効期限間近でない3件'Unallocated', 'Allocated', 'Reusable' 有効期限未設定1件  9件
expect(response.childrenPartnerLicenses[0].stockLicense).toBe(9);
// 有効期限間近5件 - {有効期限間近でない未割当2件'Unallocated', 'Reusable' 有効期限未設定1件}  2件
expect(response.childrenPartnerLicenses[0].shortage).toBe(2);
// 有効期限間近5件 有効期限間近でない10件  15件
expect(response.childrenPartnerLicenses[1].stockLicense).toBe(15);
// 有効期限間近5件- 有効期限間近でない未割当10件'Unallocated'  有効期限未設定1件  -5件  0件
expect(response.childrenPartnerLicenses[1].shortage).toBe(0);
expect(response.childrenPartnerLicenses[2].stockLicense).toBe(1);
// 有効期限が15日後のものはshortageにカウントされない
expect(response.childrenPartnerLicenses[2].shortage).toBe(0);
});
});
describe('getOrderHistories', () => {
let source: DataSource = null;
beforeEach(async () => {

View File

@ -26,7 +26,10 @@ import {
Dealer,
GetMyAccountResponse,
} from './types/types';
import { DateWithZeroTime } from '../licenses/types/types';
import {
DateWithZeroTime,
ExpirationThresholdDate,
} from '../licenses/types/types';
import { GetLicenseSummaryResponse, Typist } from './types/types';
import { AccessToken } from '../../common/token';
import { UserNotFoundError } from '../../repositories/users/errors/types';
@ -58,13 +61,9 @@ export class AccountsService {
try {
const currentDate = new DateWithZeroTime();
const expiringSoonDate = new Date(currentDate.getTime());
expiringSoonDate.setDate(
currentDate.getDate() + LICENSE_EXPIRATION_THRESHOLD_DAYS,
const expiringSoonDate = new ExpirationThresholdDate(
currentDate.getTime(),
);
// システム上有効期限日付の23時59分59秒999ミリ秒までライセンスは有効なため、各値最大値をセット
expiringSoonDate.setHours(23, 59, 59, 999);
const { licenseSummary, isStorageAvailable } =
await this.accountRepository.getLicenseSummaryInfo(
@ -446,11 +445,17 @@ export class AccountsService {
try {
const currentDate = new DateWithZeroTime();
// 第五階層のshortage算出に使用する日付情報
// 「有効期限が現在日付からしきい値以内のライセンス数」を取得するため、しきい値となる日付を作成する
const expiringSoonDate = new ExpirationThresholdDate(
currentDate.getTime(),
);
const getPartnerLicenseResult =
await this.accountRepository.getPartnerLicense(
accountId,
currentDate,
expiringSoonDate,
offset,
limit,
);
@ -471,27 +476,14 @@ export class AccountsService {
},
);
// 第五階層のshortage算出に使用する日付情報
// 「有効期限が現在日付からしきい値以内のライセンス数」を取得するため、しきい値となる日付を作成する
const expiringSoonDate = new Date(currentDate.getTime());
expiringSoonDate.setDate(
currentDate.getDate() + LICENSE_EXPIRATION_THRESHOLD_DAYS,
);
expiringSoonDate.setHours(23, 59, 59, 999);
// 各子アカウントのShortageを算出してreturn用の変数にマージする
const childrenPartnerLicenses: PartnerLicenseInfo[] = [];
for (const childPartnerLicenseFromRepository of getPartnerLicenseResult.childPartnerLicensesFromRepository) {
let childShortage;
if (childPartnerLicenseFromRepository.tier === TIERS.TIER5) {
// 第五階層の場合計算式が異なるため、別途値を取得する
const { expiringSoonLicense, allocatableLicenseWithMargin } =
await this.accountRepository.getLicenseCountForShortage(
childPartnerLicenseFromRepository.accountId,
currentDate,
expiringSoonDate,
);
childShortage = allocatableLicenseWithMargin - expiringSoonLicense;
childShortage =
childPartnerLicenseFromRepository.allocatableLicenseWithMargin -
childPartnerLicenseFromRepository.expiringSoonLicense;
} else {
childShortage =
childPartnerLicenseFromRepository.stockLicense -

View File

@ -50,6 +50,30 @@ export const createLicense = async (
identifiers.pop() as License;
};
// 有効期限・ステータス付きのライセンスを作成
export const createLicenseSetExpiryDateAndStatus = async (
datasource: DataSource,
accountId: number,
expiryDate: Date,
status: string,
): Promise<void> => {
const { identifiers } = await datasource.getRepository(License).insert({
expiry_date: expiryDate,
account_id: accountId,
type: 'NORMAL',
status: status,
allocated_user_id: null,
order_id: null,
deleted_at: null,
delete_order_id: null,
created_by: 'test_runner',
created_at: new Date(),
updated_by: 'updater',
updated_at: new Date(),
});
identifiers.pop() as License;
};
export const createLicenseOrder = async (
datasource: DataSource,
fromAccountId: number,

View File

@ -189,11 +189,17 @@ export class GetPartnerLicensesResponse {
childrenPartnerLicenses: PartnerLicenseInfo[];
}
// 第五階層のshortage算出用
export class PartnerLicenseInfoForShortage {
expiringSoonLicense?:number;
allocatableLicenseWithMargin?:number;
}
// RepositoryからPartnerLicenseInfoに関する情報を取得する際の型
export type PartnerLicenseInfoForRepository = Omit<
PartnerLicenseInfo,
PartnerLicenseInfo & PartnerLicenseInfoForShortage,
'shortage'
>;
>;
export class GetOrderHistoriesRequest {
@ApiProperty({ description: '取得件数' })

View File

@ -1,5 +1,6 @@
import { ApiProperty } from '@nestjs/swagger';
import { IsInt, Matches, Max, Min, Length } from 'class-validator';
import { LICENSE_EXPIRATION_THRESHOLD_DAYS } from '../../../constants';
export class CreateOrdersRequest {
@ApiProperty()
@ -48,3 +49,16 @@ export class DateWithZeroTime extends Date {
this.setHours(0, 0, 0, 0); // 時分秒を"0:00:00.000"に固定
}
}
// ライセンスの算出用に、閾値となる時刻23:59:59.999)の日付を取得する
export class ExpirationThresholdDate extends Date {
constructor(...args: any[]) {
if (args.length === 0) {
super(); // 引数がない場合、現在の日付で初期化
} else {
super(...(args as [string])); // 引数がある場合、引数をそのままDateクラスのコンストラクタに渡す
}
this.setDate(this.getDate() + LICENSE_EXPIRATION_THRESHOLD_DAYS);
this.setHours(23, 59, 59, 999); // 時分秒を"23:59:59.999"に固定
}
}

View File

@ -429,6 +429,7 @@ export class AccountsRepositoryService {
async getPartnerLicense(
id: number,
currentDate: Date,
expiringSoonDate: Date,
offset: number,
limit: number,
): Promise<{
@ -486,16 +487,36 @@ export class AccountsRepositoryService {
entityManager,
);
// 第五の不足数を算出するためのライセンス数情報を取得する
let expiringSoonLicense: number;
let allocatableLicenseWithMargin: number;
if (childAccount.tier === TIERS.TIER5) {
expiringSoonLicense = await this.getExpiringSoonLicense(
entityManager,
childAccount.id,
currentDate,
expiringSoonDate,
);
allocatableLicenseWithMargin =
await this.getAllocatableLicenseWithMargin(
entityManager,
childAccount.id,
expiringSoonDate,
);
}
// 戻り値用の値を設定
const childPartnerLicenseFromRepository: PartnerLicenseInfoForRepository =
{
accountId: childAccount.id,
tier: childAccount.tier,
companyName: childAccount.company_name,
stockLicense: childLicenseOrderStatus.stockLicense,
issuedRequested: childLicenseOrderStatus.issuedRequested,
issueRequesting: childLicenseOrderStatus.issueRequesting,
};
{
accountId: childAccount.id,
tier: childAccount.tier,
companyName: childAccount.company_name,
stockLicense: childLicenseOrderStatus.stockLicense,
issuedRequested: childLicenseOrderStatus.issuedRequested,
issueRequesting: childLicenseOrderStatus.issueRequesting,
expiringSoonLicense: expiringSoonLicense,
allocatableLicenseWithMargin: allocatableLicenseWithMargin,
};
childPartnerLicensesFromRepository.push(
childPartnerLicenseFromRepository,
@ -517,42 +538,6 @@ export class AccountsRepositoryService {
});
}
/**
*
* @param id
* @param currentDate
* @param expiringSoonDate
* @returns expiringSoonLicense
* @returns expiringSoonDate
*/
async getLicenseCountForShortage(
id: number,
currentDate: Date,
expiringSoonDate: Date,
): Promise<{
expiringSoonLicense: number;
allocatableLicenseWithMargin: number;
}> {
return await this.dataSource.transaction(async (entityManager) => {
const expiringSoonLicense = await this.getExpiringSoonLicense(
entityManager,
id,
currentDate,
expiringSoonDate,
);
const allocatableLicenseWithMargin =
await this.getAllocatableLicenseWithMargin(
entityManager,
id,
expiringSoonDate,
);
return {
expiringSoonLicense: expiringSoonLicense,
allocatableLicenseWithMargin: allocatableLicenseWithMargin,
};
});
}
/**
* Dealer(Tier4)
* @returns dealer accounts