Merged PR 142: API実装(第五階層用ライセンス情報取得API)

## 概要
[Task1846: API実装(第五階層用ライセンス情報取得API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1846)

- 元PBI or タスクへのリンク(内容・目的などはそちらにあるはず)
- 何をどう変更したか、追加したライブラリなど
accounts.controller.tsからaccountService.getLicenseSummaryを呼び出す。
一度のトランザクションで処理を行うよう、serviceとrepositoryをリファクタリング。
license.entity.tsにはライセンス系のテーブルで不足していたエンティティを追加。

- このPull Requestでの対象/対象外
Storage Sizeの値はPBI1203では対象外のため0固定
Used Sizeの値はPBI1203では対象外のため0固定
LicenseSummaryInfo2と定義している個所は、別タスクで修正します。
[タスク 1961: API IF修正(LicenseSummaryInfo)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/OMDSDictation/_sprints/taskboard/OMDSDictation%20%E3%83%81%E3%83%BC%E3%83%A0/OMDSDictation/%E3%82%B9%E3%83%97%E3%83%AA%E3%83%B3%E3%83%88%2011-1?workitem=1961)

- 影響範囲(他の機能にも影響があるか)
新規追加のため、なし

## レビューポイント
- 特にレビューしてほしい箇所

## UIの変更
- なし

## 動作確認状況
- ローカルで確認
PostmanでAPI実行。
各ライセンス数値が期待通りの結果であることを確認。
![image.png](https://dev.azure.com/ODMSCloud/6023ff7b-d41c-4fa7-9c6f-f576ba48c07c/_apis/git/repositories/302da463-a2d7-40f9-b2bb-6e8edf324fa9/pullRequests/142/attachments/image.png)

## 補足
テスト内容は、添付のテストデータを参照ください。
[テストデータ.xlsx](https://dev.azure.com/ODMSCloud/6023ff7b-d41c-4fa7-9c6f-f576ba48c07c/_apis/git/repositories/302da463-a2d7-40f9-b2bb-6e8edf324fa9/pullRequests/142/attachments/%E3%83%86%E3%82%B9%E3%83%88%E3%83%87%E3%83%BC%E3%82%BF.xlsx)
This commit is contained in:
maruyama.t 2023-06-15 08:56:03 +00:00
parent 2cdcae4924
commit fd69541e1a
9 changed files with 237 additions and 374 deletions

View File

@ -120,6 +120,23 @@ export const LICENSE_STATUS_ISSUE_REQUESTING = 'Issue Requesting';
*/
export const LICENSE_STATUS_ISSUED = 'Issued';
/**
*
* @const {string[]}
*/
export const LICENSE_ALLOCATED_STATUS = {
UNALLOCATED: 'Unallocated',
ALLOCATED: 'Allocated',
REUSABLE: 'Reusable',
DELETED: 'Deleted',
} as const;
/**
*
* @const {number}
*/
export const LICENSE_EXPIRATION_THRESHOLD_DAYS = 14;
/**
*
* @const {string}

View File

@ -118,40 +118,11 @@ export class AccountsController {
): Promise<GetLicenseSummaryResponse> {
console.log(req.header('Authorization'));
console.log(body);
// アクセストークンにより権限を確認する
/* const pubKey = await this.cryptoService.getPublicKey();
const accessToken = retrieveAuthorizationToken(req);
//アクセストークンが存在しない場合のエラー
if (accessToken == undefined) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const payload = verify<AccessToken>(accessToken, pubKey);
//アクセストークン形式エラー
if (isVerifyError(payload)) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
} */
//アクセストークンの権限不足エラー
/* if (!confirmPermission(payload.role)) {
throw new HttpException(
makeErrorResponse('E000108'),
HttpStatus.UNAUTHORIZED,
);
} */
/* const info = await this.accountService.getLicenseSummary(
Number(payload.userId),
); */
/* return {
const info = await this.accountService.getLicenseSummary(body.accountId);
return {
licenseSummaryInfo: info,
}; */
return;
};
}
@ApiResponse({

View File

@ -10,7 +10,7 @@ import {
describe('AccountsService', () => {
it('アカウントに紐づくライセンス情報を取得する', async () => {
const user_id = 1;
const accountId = 1;
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue =
@ -22,17 +22,17 @@ describe('AccountsService', () => {
adb2cParam,
sendGridMockValue,
);
expect(await service.getLicenseSummary(user_id)).toEqual(
expect(await service.getLicenseSummary(accountId)).toEqual(
expectedAccountLisenceCounts,
);
});
});
it('ライセンス情報が取得できない場合、エラーとなる', async () => {
const user_id = 1;
const accountId = 1;
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
usersRepositoryMockValue.findUserById = new Error('DB error');
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue();
accountsRepositoryMockValue.getLicenseSummaryInfo = new Error('DB error');
const sendGridMockValue = makeDefaultSendGridlValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
@ -40,49 +40,7 @@ it('ライセンス情報が取得できない場合、エラーとなる', asyn
adb2cParam,
sendGridMockValue,
);
await expect(service.getLicenseSummary(user_id)).rejects.toEqual(
new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
),
);
});
it('アクセストークンからユーザ情報を取得する', async () => {
const token = {
userId: 'ede66c43-9b9d-4222-93ed-5f11c96e08e2',
role: 'none admin',
tier: 5,
};
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
adb2cParam,
sendGridMockValue,
);
expect(await service.getMyAccountInfo(token)).toEqual(1234567890123456);
});
it('ユーザ情報が取得できない場合エラーとなる', async () => {
const token = {
userId: 'ede66c43-9b9d-4222-93ed-5f11c96e08e2',
role: 'none admin',
tier: 5,
};
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
usersRepositoryMockValue.findUserByExternalId = new Error('DB error');
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue = makeDefaultAccountsRepositoryMockValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
adb2cParam,
sendGridMockValue,
);
await expect(service.getMyAccountInfo(token)).rejects.toEqual(
await expect(service.getLicenseSummary(accountId)).rejects.toEqual(
new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
@ -98,7 +56,7 @@ const expectedAccountLisenceCounts = {
expiringWithin14daysLicense: 5,
issueRequesting: 6,
numberOfRequesting: 7,
shortage: 8,
shortage: 3,
storageSize: 0,
usedSize: 0,
isAccountLock: false,

View File

@ -13,10 +13,9 @@ import {
} from '../../gateways/adb2c/adb2c.service';
import { Account } from '../../repositories/accounts/entity/account.entity';
import { User } from '../../repositories/users/entity/user.entity';
import { TIER_5 } from '../../constants';
import { LICENSE_EXPIRATION_THRESHOLD_DAYS, TIER_5 } from '../../constants';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import { LicenseSummaryInfo } from './types/types';
import { User as EntityUser } from '../../repositories/users/entity/user.entity';
import { AccessToken } from '../../common/token';
@Injectable()
@ -31,58 +30,67 @@ export class AccountsService {
private readonly logger = new Logger(AccountsService.name);
/**
*
* @param userId
* @param accountId
* @returns LicenseSummary
*/
async getLicenseSummary(userId: number): Promise<LicenseSummaryInfo> {
async getLicenseSummary(accountId: number): Promise<LicenseSummaryInfo> {
this.logger.log(`[IN] ${this.getLicenseSummary.name}`);
//DBよりアクセス者の所属するアカウントIDを取得する
let adminUser: EntityUser;
const licenseSammury = new LicenseSummaryInfo();
try {
adminUser = await this.usersRepository.findUserById(userId);
const accountId = adminUser.account_id;
// Total Licenseの取得を行う
licenseSammury.totalLicense =
await this.accountRepository.getTotalLicense(accountId);
// Allocated licenseの取得を行う
licenseSammury.allocatedLicense =
await this.accountRepository.getAllocatedLicense(accountId);
// Reusable licenseの取得を行う
licenseSammury.reusableLicense =
await this.accountRepository.getReusableLicense(accountId);
// Free licenseの取得を行う
licenseSammury.freeLicense = await this.accountRepository.getFreeLicense(
accountId,
const currentDate = new Date();
// 有効期限との比較は時間まで見ず日付だけで判別するため、各値0をセット
currentDate.setHours(0, 0, 0, 0);
const expiringSoonDate = new Date(currentDate.getTime());
expiringSoonDate.setDate(
currentDate.getDate() + LICENSE_EXPIRATION_THRESHOLD_DAYS,
);
// Expiring within 14days licenseの取得を行う
licenseSammury.expiringWithin14daysLicense =
await this.accountRepository.getExpiringWithin14DaysLicense(accountId);
// Issue Requestingの取得を行う
licenseSammury.issueRequesting =
await this.accountRepository.getIssueRequesting(accountId);
// Number of Requestingの取得を行う
licenseSammury.numberOfRequesting =
await this.accountRepository.getNumberOfRequesting(accountId);
// Shortageの取得を行う
licenseSammury.shortage = await this.accountRepository.getShortage(
accountId,
);
// XXX Storage Size、PBI1203では対象外のため0固定
licenseSammury.storageSize = 0;
// XXX Used Size、PBI1203では対象外のため0固定
licenseSammury.usedSize = 0;
// Account Lockの状態を取得する
licenseSammury.isAccountLock =
await this.accountRepository.getIsAccountLocked(accountId);
// システム上有効期限日付の23時59分59秒999ミリ秒までライセンスは有効なため、各値最大値をセット
expiringSoonDate.setHours(23, 59, 59, 999);
const { licenseSummary, isAccountLock } =
await this.accountRepository.getLicenseSummaryInfo(
accountId,
currentDate,
expiringSoonDate,
);
const {
allocatableLicenseWithMargin,
expiringSoonLicense,
totalLicense,
allocatedLicense,
reusableLicense,
freeLicense,
issueRequesting,
numberOfRequesting,
} = licenseSummary;
let shortage = allocatableLicenseWithMargin - expiringSoonLicense;
shortage = shortage >= 0 ? 0 : Math.abs(shortage);
const licenseSummaryInfo: LicenseSummaryInfo = {
totalLicense,
allocatedLicense,
reusableLicense,
freeLicense,
expiringWithin14daysLicense: expiringSoonLicense,
issueRequesting,
numberOfRequesting,
storageSize: 0, // XXX PBI1201対象外
usedSize: 0, // XXX PBI1201対象外
shortage,
isAccountLock,
};
return licenseSummaryInfo;
} catch (e) {
console.log(e);
console.log('get licenseSummary failed');
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
return licenseSammury;
}
/**
* DBに作成する

View File

@ -9,6 +9,7 @@ import {
ConflictError,
} from '../../../gateways/adb2c/adb2c.service';
import { SendGridService } from '../../../gateways/sendgrid/sendgrid.service';
import { LicenseSummaryInfo } from '../types/types';
export type UsersRepositoryMockValue = {
findUserById: User | Error;
findUserByExternalId: User | Error;
@ -25,15 +26,7 @@ export type SendGridMockValue = {
sendMail: undefined | Error;
};
export type AccountsRepositoryMockValue = {
getTotalLicense: number | Error;
getAllocatedLicense: number | Error;
getReusableLicense: number | Error;
getFreeLicense: number | Error;
getExpiringWithin14DaysLicense: number | Error;
getIssueRequesting: number | Error;
getNumberOfRequesting: number | Error;
getShortage: number | Error;
getIsAccountLocked: boolean | Error;
getLicenseSummaryInfo: LicenseSummaryInfo | Error;
};
export const makeAccountsServiceMock = async (
accountsRepositoryMockValue: AccountsRepositoryMockValue,
@ -70,74 +63,24 @@ export const makeAccountsServiceMock = async (
export const makeAccountsRepositoryMock = (
value: AccountsRepositoryMockValue,
) => {
const {
getTotalLicense,
getAllocatedLicense,
getReusableLicense,
getFreeLicense,
getExpiringWithin14DaysLicense,
getIssueRequesting,
getNumberOfRequesting,
getShortage,
getIsAccountLocked,
} = value;
const { getLicenseSummaryInfo } = value;
return {
getTotalLicense:
getTotalLicense instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getTotalLicense)
: jest.fn<Promise<number>, []>().mockResolvedValue(getTotalLicense),
getAllocatedLicense:
getAllocatedLicense instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getAllocatedLicense)
: jest.fn<Promise<number>, []>().mockResolvedValue(getAllocatedLicense),
getReusableLicense:
getReusableLicense instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getReusableLicense)
: jest.fn<Promise<number>, []>().mockResolvedValue(getReusableLicense),
getFreeLicense:
getFreeLicense instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getFreeLicense)
: jest.fn<Promise<number>, []>().mockResolvedValue(getFreeLicense),
getExpiringWithin14DaysLicense:
getExpiringWithin14DaysLicense instanceof Error
? jest
.fn<Promise<void>, []>()
.mockRejectedValue(getExpiringWithin14DaysLicense)
getLicenseSummaryInfo:
getLicenseSummaryInfo instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getLicenseSummaryInfo)
: jest
.fn<Promise<number>, []>()
.mockResolvedValue(getExpiringWithin14DaysLicense),
getIssueRequesting:
getIssueRequesting instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getIssueRequesting)
: jest.fn<Promise<number>, []>().mockResolvedValue(getIssueRequesting),
getNumberOfRequesting:
getNumberOfRequesting instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getNumberOfRequesting)
: jest
.fn<Promise<number>, []>()
.mockResolvedValue(getNumberOfRequesting),
getShortage:
getShortage instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getShortage)
: jest.fn<Promise<number>, []>().mockResolvedValue(getShortage),
getIsAccountLocked:
getIsAccountLocked instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(getIsAccountLocked)
: jest.fn<Promise<boolean>, []>().mockResolvedValue(getIsAccountLocked),
.fn<Promise<LicenseSummaryInfo>, []>()
.mockResolvedValue(getLicenseSummaryInfo),
};
};
export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
const { findUserById, findUserByExternalId } = value;
const { findUserById } = value;
return {
findUserById:
findUserById instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(findUserById)
: jest.fn<Promise<User>, []>().mockResolvedValue(findUserById),
findUserByExternalId:
findUserByExternalId instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(findUserByExternalId)
: jest.fn<Promise<User>, []>().mockResolvedValue(findUserByExternalId),
};
};
export const makeAdB2cServiceMock = (value: AdB2cMockValue) => {
@ -172,17 +115,21 @@ export const makeSendGridServiceMock = (value: SendGridMockValue) => {
// 個別のテストケースに対応してそれぞれのMockを用意するのは無駄が多いのでテストケース内で個別の値を設定する
export const makeDefaultAccountsRepositoryMockValue =
(): AccountsRepositoryMockValue => {
return {
getTotalLicense: 1,
getAllocatedLicense: 2,
getReusableLicense: 3,
getFreeLicense: 4,
getExpiringWithin14DaysLicense: 5,
getIssueRequesting: 6,
getNumberOfRequesting: 7,
getShortage: 8,
getIsAccountLocked: false,
let licenseSummaryInfo = new LicenseSummaryInfo();
licenseSummaryInfo = {
totalLicense: 1,
allocatedLicense: 2,
reusableLicense: 3,
freeLicense: 4,
expiringWithin14daysLicense: 5,
issueRequesting: 6,
numberOfRequesting: 7,
shortage: 8,
storageSize: 0,
usedSize: 0,
isAccountLock: false,
};
return { getLicenseSummaryInfo: licenseSummaryInfo };
};
export const makeDefaultUsersRepositoryMockValue =
(): UsersRepositoryMockValue => {
@ -199,7 +146,7 @@ export const makeDefaultUsersRepositoryMockValue =
user.notification = false;
user.deleted_at = null;
user.created_by = 'test';
user.created_at = new Date('2023-06-13 00:00:00');
user.created_at = new Date();
user.updated_by = null;
user.updated_at = null;

View File

@ -33,7 +33,7 @@ export class GetLicenseSummaryRequest {
@ApiProperty()
accountId: number;
}
// XXX Task1961で直す、レスポンス内に直接定義する
export class LicenseSummaryInfo {
@ApiProperty()
totalLicense: number;
@ -68,6 +68,17 @@ export class LicenseSummaryInfo {
@ApiProperty()
isAccountLock: boolean;
}
// XXX Task1961で直すLicenseSummaryInfo2→LicenseSummaryInfo
export class LicenseSummaryInfo2 {
totalLicense: number;
allocatedLicense: number;
reusableLicense: number;
freeLicense: number;
expiringSoonLicense: number;
issueRequesting: number;
numberOfRequesting: number;
allocatableLicenseWithMargin: number;
}
export class GetLicenseSummaryResponse {
@ApiProperty({ type: LicenseSummaryInfo })
licenseSummaryInfo: LicenseSummaryInfo;

View File

@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Account } from './entity/account.entity';
import { AccountsRepositoryService } from './accounts.repository.service';
import { License, LicenseOrder } from '../licenses/entity/license.entity';
@Module({
imports: [TypeOrmModule.forFeature([Account])],
imports: [TypeOrmModule.forFeature([Account, License, LicenseOrder])],
providers: [AccountsRepositoryService],
exports: [AccountsRepositoryService],
})

View File

@ -2,8 +2,10 @@ import { Injectable } from '@nestjs/common';
import {
Between,
DataSource,
In,
IsNull,
MoreThan,
MoreThanOrEqual,
Not,
UpdateResult,
} from 'typeorm';
@ -15,7 +17,11 @@ import {
getDirection,
getTaskListSortableAttribute,
} from '../../common/types/sort/util';
import { LICENSE_STATUS_ISSUE_REQUESTING } from '../../constants';
import {
LICENSE_ALLOCATED_STATUS,
LICENSE_STATUS_ISSUE_REQUESTING,
} from '../../constants';
import { LicenseSummaryInfo2 } from '../../features/accounts/types/types';
export class AccountNotFoundError extends Error {}
@ -157,186 +163,130 @@ export class AccountsRepositoryService {
}
return account;
}
/**
* IDから有効な総ライセンス数を取得する
* IDからライセンス情報を取得する
* @param id
* @returns count
* @param currentDate
* @param expiringSoonDate
* @returns licenseSummary
*/
async getTotalLicense(id: number): Promise<number> {
const count = await this.dataSource.getRepository(License).count({
where: [
{ account_id: id, expiry_date: MoreThan(new Date()) },
{ account_id: id, expiry_date: IsNull() },
],
});
return count;
}
/**
* IDから有効な総ライセンス数のうち
* @param id
* @returns count
*/
async getAllocatedLicense(id: number): Promise<number> {
const count = await this.dataSource.getRepository(License).count({
where: {
account_id: id,
type: Not(IsNull()),
},
});
return count;
}
/**
* IDから総ライセンス数のうち
*
*
* @param id
* @returns count
*/
async getReusableLicense(id: number): Promise<number> {
const count = await this.dataSource
.getRepository(License)
.createQueryBuilder('License')
.leftJoinAndSelect(
'lisence.lisence_history',
'history',
'license.id=lisence_history.license_id',
)
.where('lisence.account_id = :id', { id })
.andWhere('history.id IS NOT NULL')
// XXX Unallocated(未割当)は仮、値が決定したら修正する
.andWhere('license.status = :status', { status: 'Unallocated' })
.getCount();
return count;
}
/**
* IDから総ライセンス数のうち
*
* @param id
* @returns count
*/
async getFreeLicense(id: number): Promise<number> {
const count = await this.dataSource
.getRepository(License)
.createQueryBuilder('License')
.leftJoinAndSelect(
'lisence.lisence_history',
'history',
'license.id=lisence_history.license_id',
)
.where('lisence.account_id = :id', { id })
.andWhere('history.id IS NULL')
.getCount();
return count;
}
/**
* IDから総ライセンス数のうち
* 14
* @param id
* @returns count
*/
async getExpiringWithin14DaysLicense(id: number): Promise<number> {
const currentDate = new Date();
const fourteenDaysLater = new Date();
fourteenDaysLater.setDate(fourteenDaysLater.getDate() + 14);
const options = {
where: [
{
// XXX Task1961で直すLicenseSummaryInfo2→LicenseSummaryInfo
async getLicenseSummaryInfo(
id: number,
currentDate: Date,
expiringSoonDate: Date,
): Promise<{ licenseSummary: LicenseSummaryInfo2; isAccountLock: boolean }> {
return await this.dataSource.transaction(async (entityManager) => {
const license = entityManager.getRepository(License);
const licenseOrder = entityManager.getRepository(LicenseOrder);
// 有効な総ライセンス数を取得する
const totalLicense = await license.count({
where: [
{
account_id: id,
expiry_date: MoreThanOrEqual(currentDate),
status: Not(LICENSE_ALLOCATED_STATUS.DELETED),
},
{
account_id: id,
expiry_date: IsNull(),
status: Not(LICENSE_ALLOCATED_STATUS.DELETED),
},
],
});
// 有効な総ライセンス数のうち、ユーザーに割り当て済みのライセンス数を取得する
const allocatedLicense = await license.count({
where: {
account_id: id,
expiry_date: Between(currentDate, fourteenDaysLater),
allocated_user_id: Not(IsNull()),
status: LICENSE_ALLOCATED_STATUS.ALLOCATED,
},
{ account_id: id, expiry_date: Not(IsNull()) },
],
};
});
const count = await this.dataSource.getRepository(License).count(options);
// 総ライセンス数のうち、ユーザーに割り当てたことがあるが、現在は割り当て解除され誰にも割り当たっていないライセンス数を取得する
const reusableLicense = await license.count({
where: {
account_id: id,
status: LICENSE_ALLOCATED_STATUS.REUSABLE,
},
});
return count;
}
/**
* IDから未発行状態あるいは発行キャンセルされた注文数を取得する
* @param id
* @returns count
*/
async getIssueRequesting(id: number): Promise<number> {
const count = await this.dataSource.getRepository(LicenseOrder).count({
where: [
{
// 総ライセンス数のうち、一度もユーザーに割り当てたことのないライセンス数を取得する
const freeLicense = await license.count({
where: {
account_id: id,
status: LICENSE_ALLOCATED_STATUS.UNALLOCATED,
},
});
// 有効期限が現在日付からしきい値以内のライセンス数を取得する
const expiringSoonLicense = await license.count({
where: {
account_id: id,
expiry_date: Between(currentDate, expiringSoonDate),
status: Not(LICENSE_ALLOCATED_STATUS.DELETED),
},
});
// 未発行状態あるいは発行キャンセルされた注文数を取得する
const issueRequesting = await licenseOrder.count({
where: {
from_account_id: id,
status: LICENSE_STATUS_ISSUE_REQUESTING,
},
],
});
// 未発行状態あるいは発行キャンセルされた注文の総ライセンス数を取得する
const result = await licenseOrder
.createQueryBuilder('license_orders')
.select('SUM(license_orders.quantity)', 'sum')
.where('license_orders.from_account_id = :id', { id })
.andWhere('license_orders.status = :status', {
status: LICENSE_STATUS_ISSUE_REQUESTING,
})
.getRawOne();
const numberOfRequesting = parseInt(result.sum, 10) || 0;
// 有効期限がしきい値より未来または未設定で、割り当て可能なライセンス数の取得を行う
const allocatableLicenseWithMargin = await license.count({
where: [
{
account_id: id,
status: In([
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
LICENSE_ALLOCATED_STATUS.REUSABLE,
]),
expiry_date: MoreThan(expiringSoonDate),
},
{
account_id: id,
status: In([
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
LICENSE_ALLOCATED_STATUS.REUSABLE,
]),
expiry_date: IsNull(),
},
],
});
// アカウントのロック状態を取得する
const isAccountLock = (await this.findAccountById(id)).locked;
let licenseSummary = new LicenseSummaryInfo2();
licenseSummary = {
totalLicense: totalLicense,
allocatedLicense: allocatedLicense,
reusableLicense: reusableLicense,
freeLicense: freeLicense,
expiringSoonLicense: expiringSoonLicense,
allocatableLicenseWithMargin: allocatableLicenseWithMargin,
issueRequesting: issueRequesting,
numberOfRequesting: numberOfRequesting,
};
return { licenseSummary: licenseSummary, isAccountLock };
});
return count;
}
/**
* IDから未発行状態あるいは発行キャンセルされた注文の総ライセンス数を取得する
* @param id
* @returns count
*/
async getNumberOfRequesting(id: number): Promise<number> {
const result = await this.dataSource
.getRepository(LicenseOrder)
.createQueryBuilder()
.select('SUM(license_orders.quantity)', 'sum')
.where('license_orders.from_account_id = :id', { id })
.andWhere('license_orders.status = :status', {
status: LICENSE_STATUS_ISSUE_REQUESTING,
})
.getRawOne();
const sum = parseInt(result.sum, 10) || 0;
return sum;
}
/**
* IDから不足数
* {15} - {14}
* 00
* @param id
* @returns count
*/
async getShortage(id: number): Promise<number> {
const fifteenDaysLater = new Date();
fifteenDaysLater.setDate(fifteenDaysLater.getDate() + 15);
const options = {
where: [
{
status: 'Unallocated',
account_id: id,
expiry_date: MoreThan(fifteenDaysLater),
},
{ status: 'Unallocated', account_id: id, expiry_date: Not(IsNull()) },
],
};
const shortageCount = await this.dataSource
.getRepository(License)
.count(options);
const expiringWithin14DaysCount = await this.getExpiringWithin14DaysLicense(
id,
);
let result = shortageCount - expiringWithin14DaysCount;
result = Math.abs(result); // 絶対値を取得
return result >= 0 ? result : 0;
}
/**
* IDからアカウントのロック状態を取得する
* @param id
* @returns isLock
*/
async getIsAccountLocked(id: number): Promise<boolean> {
const result = await this.dataSource.getRepository(Account).findOne({
where: [
{
id: id,
},
],
});
return result.locked;
}
}

View File

@ -35,7 +35,7 @@ export class LicenseOrder {
canceled_at?: Date;
}
@Entity({ name: 'license' })
@Entity({ name: 'licenses' })
export class License {
@PrimaryGeneratedColumn()
id: number;
@ -64,7 +64,7 @@ export class License {
@Column()
delete_order_id: number;
}
@Entity({ name: 'license_history' })
@Entity({ name: 'licenses_history' })
export class LicenseHistory {
@PrimaryGeneratedColumn()
id: number;