Merged PR 378: API実装(ライセンス発行キャンセルAPI)
## 概要 [Task2498: API実装(ライセンス発行キャンセルAPI)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2498) - 元PBI or タスクへのリンク(内容・目的などはそちらにあるはず) - 何をどう変更したか、追加したライブラリなど ライセンス発行をキャンセルするAPIを実装 下位のアカウント情報と、上位のアカウント情報をセットすると、パートナー関係であるかを返す関数を追加 既存のユニットテストのライセンス作成箇所で、注文ID、削除日時、削除注文IDを指定できるように修正 - このPull Requestでの対象/対象外 - 影響範囲(他の機能にも影響があるか) 既存ユニットテストのライセンス作成部分 ## レビューポイント - 特にレビューしてほしい箇所 パートナー関係かどうかを返す箇所、共通的に使いやすいかどうか 14日より経過していた場合の箇所、ライセンスの有効期限の定数を使っているが分けたほうが良いか ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## 動作確認状況 - ローカルで確認 ■正常系 ライセンス発行のキャンセルが完了できる(第一階層で実行) ライセンス発行のキャンセルが完了できる(第二階層で実行) キャンセルした発行の注文状態が発行待ちに戻る 発行されたライセンスは物理削除される 論理削除されていたライセンスは未割当で、削除前の状態に戻る ■異常系 第一、第二階層以外で実行した場合はエラー キャンセル対象の発行が存在しない場合エラー キャンセル対象の発行が14日より経過していた場合はエラー キャンセル対象の発行のライセンスが使われていた場合はエラー 自身のパートナー以外の発行をキャンセルしようとした場合、エラー ## 補足 - 相談、参考資料などがあれば
This commit is contained in:
parent
2d378b8e66
commit
7524abbae6
@ -48,6 +48,9 @@ export const ErrorCodes = [
|
|||||||
'E010806', // ライセンス割り当て不可エラー
|
'E010806', // ライセンス割り当て不可エラー
|
||||||
'E010807', // ライセンス割り当て解除済みエラー
|
'E010807', // ライセンス割り当て解除済みエラー
|
||||||
'E010808', // ライセンス注文キャンセル不可エラー
|
'E010808', // ライセンス注文キャンセル不可エラー
|
||||||
|
'E010809', // ライセンス発行キャンセル不可エラー(ステータスが変えられている場合)
|
||||||
|
'E010810', // ライセンス発行キャンセル不可エラー(発行から一定期間経過した場合)
|
||||||
|
'E010811', // ライセンス発行キャンセル不可エラー(発行したライセンスが割り当てされている場合)
|
||||||
'E010908', // タイピストグループ不在エラー
|
'E010908', // タイピストグループ不在エラー
|
||||||
'E011001', // ワークタイプ重複エラー
|
'E011001', // ワークタイプ重複エラー
|
||||||
'E011002', // ワークタイプ登録上限超過エラー
|
'E011002', // ワークタイプ登録上限超過エラー
|
||||||
|
|||||||
@ -37,6 +37,9 @@ export const errors: Errors = {
|
|||||||
E010806: 'License is unavailable Error',
|
E010806: 'License is unavailable Error',
|
||||||
E010807: 'License is already deallocated Error',
|
E010807: 'License is already deallocated Error',
|
||||||
E010808: 'Order cancel failed Error',
|
E010808: 'Order cancel failed Error',
|
||||||
|
E010809: 'Already license order status changed Error',
|
||||||
|
E010810: 'Cancellation period expired error',
|
||||||
|
E010811: 'Already license allocated Error',
|
||||||
E010908: 'Typist Group not exist Error',
|
E010908: 'Typist Group not exist Error',
|
||||||
E011001: 'Thiw WorkTypeID already used Error',
|
E011001: 'Thiw WorkTypeID already used Error',
|
||||||
E011002: 'WorkTypeID create limit exceeded Error',
|
E011002: 'WorkTypeID create limit exceeded Error',
|
||||||
|
|||||||
@ -636,12 +636,12 @@ export class AccountsController {
|
|||||||
|
|
||||||
const context = makeContext(payload.userId);
|
const context = makeContext(payload.userId);
|
||||||
|
|
||||||
// TODO: 発行キャンセル処理(仮)。API実装のタスク(2498)で本実装
|
await this.accountService.cancelIssue(
|
||||||
// await this.accountService.cancelIssue(
|
context,
|
||||||
// context,
|
payload.userId,
|
||||||
// body.poNumber,
|
body.poNumber,
|
||||||
// body.orderedAccountId,
|
body.orderedAccountId,
|
||||||
// );
|
);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,10 +32,14 @@ import {
|
|||||||
getUserFromExternalId,
|
getUserFromExternalId,
|
||||||
getUsers,
|
getUsers,
|
||||||
makeTestUser,
|
makeTestUser,
|
||||||
|
makeHierarchicalAccounts,
|
||||||
} from '../../common/test/utility';
|
} from '../../common/test/utility';
|
||||||
import { AccountsService } from './accounts.service';
|
import { AccountsService } from './accounts.service';
|
||||||
import { Context, makeContext } from '../../common/log';
|
import { Context, makeContext } from '../../common/log';
|
||||||
import {
|
import {
|
||||||
|
LICENSE_ALLOCATED_STATUS,
|
||||||
|
LICENSE_ISSUE_STATUS,
|
||||||
|
LICENSE_TYPE,
|
||||||
OPTION_ITEM_VALUE_TYPE,
|
OPTION_ITEM_VALUE_TYPE,
|
||||||
TIERS,
|
TIERS,
|
||||||
USER_ROLES,
|
USER_ROLES,
|
||||||
@ -51,8 +55,12 @@ import {
|
|||||||
import { AdB2cService } from '../../gateways/adb2c/adb2c.service';
|
import { AdB2cService } from '../../gateways/adb2c/adb2c.service';
|
||||||
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
||||||
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
|
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
|
||||||
|
import {
|
||||||
|
createOrder,
|
||||||
|
selectLicense,
|
||||||
|
selectOrderLicense,
|
||||||
|
} from '../licenses/test/utility';
|
||||||
import { WorktypesRepositoryService } from '../../repositories/worktypes/worktypes.repository.service';
|
import { WorktypesRepositoryService } from '../../repositories/worktypes/worktypes.repository.service';
|
||||||
import exp from 'constants';
|
|
||||||
|
|
||||||
describe('createAccount', () => {
|
describe('createAccount', () => {
|
||||||
let source: DataSource = null;
|
let source: DataSource = null;
|
||||||
@ -1827,14 +1835,79 @@ describe('getPartnerAccount', () => {
|
|||||||
).account;
|
).account;
|
||||||
|
|
||||||
// 所有ライセンスを追加(親:3、子1:1、子2:2)
|
// 所有ライセンスを追加(親:3、子1:1、子2:2)
|
||||||
await createLicense(source, parentAccountId);
|
await createLicense(
|
||||||
await createLicense(source, parentAccountId);
|
source,
|
||||||
await createLicense(source, parentAccountId);
|
1,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
2,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
3,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
4,
|
||||||
|
null,
|
||||||
|
childAccountId1,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
await createLicense(source, childAccountId1);
|
await createLicense(
|
||||||
|
source,
|
||||||
await createLicense(source, childAccountId2);
|
5,
|
||||||
await createLicense(source, childAccountId2);
|
null,
|
||||||
|
childAccountId2,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
3,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
6,
|
||||||
|
null,
|
||||||
|
childAccountId2,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
3,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
// ライセンス注文を追加(子1→親:10ライセンス、子2→親:5ライセンス)
|
// ライセンス注文を追加(子1→親:10ライセンス、子2→親:5ライセンス)
|
||||||
await createLicenseOrder(
|
await createLicenseOrder(
|
||||||
@ -1980,7 +2053,12 @@ describe('getPartnerAccount', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 有効期限未設定のライセンスを1件追加(子1)
|
// 有効期限未設定のライセンスを1件追加(子1)
|
||||||
await createLicense(source, childAccountId1);
|
await createLicenseSetExpiryDateAndStatus(
|
||||||
|
source,
|
||||||
|
childAccountId1,
|
||||||
|
null,
|
||||||
|
'Unallocated',
|
||||||
|
);
|
||||||
|
|
||||||
const service = module.get<AccountsService>(AccountsService);
|
const service = module.get<AccountsService>(AccountsService);
|
||||||
const accountId = parentAccountId;
|
const accountId = parentAccountId;
|
||||||
@ -2172,9 +2250,42 @@ describe('issueLicense', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 親のライセンスを作成する(3個)
|
// 親のライセンスを作成する(3個)
|
||||||
await createLicense(source, parentAccountId);
|
await createLicense(
|
||||||
await createLicense(source, parentAccountId);
|
source,
|
||||||
await createLicense(source, parentAccountId);
|
1,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
2,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
3,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
// 子から親への注文を作成する(2個)
|
// 子から親への注文を作成する(2個)
|
||||||
await createLicenseOrder(
|
await createLicenseOrder(
|
||||||
source,
|
source,
|
||||||
@ -2231,9 +2342,42 @@ describe('issueLicense', () => {
|
|||||||
role: 'admin',
|
role: 'admin',
|
||||||
});
|
});
|
||||||
// 親のライセンスを作成する(3個)
|
// 親のライセンスを作成する(3個)
|
||||||
await createLicense(source, parentAccountId);
|
await createLicense(
|
||||||
await createLicense(source, parentAccountId);
|
source,
|
||||||
await createLicense(source, parentAccountId);
|
1,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
2,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
3,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
// 子から親への注文を作成する(2個)
|
// 子から親への注文を作成する(2個)
|
||||||
await createLicenseOrder(
|
await createLicenseOrder(
|
||||||
source,
|
source,
|
||||||
@ -2289,9 +2433,42 @@ describe('issueLicense', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 親のライセンスを作成する(3個)
|
// 親のライセンスを作成する(3個)
|
||||||
await createLicense(source, parentAccountId);
|
await createLicense(
|
||||||
await createLicense(source, parentAccountId);
|
source,
|
||||||
await createLicense(source, parentAccountId);
|
1,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
2,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
3,
|
||||||
|
null,
|
||||||
|
parentAccountId,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
// 子から親への注文を作成する(4個)
|
// 子から親への注文を作成する(4個)
|
||||||
await createLicenseOrder(
|
await createLicenseOrder(
|
||||||
source,
|
source,
|
||||||
@ -3414,3 +3591,297 @@ describe('createWorktype', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('ライセンス発行キャンセル', () => {
|
||||||
|
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('ライセンス発行のキャンセルが完了する(第一階層で実行)', async () => {
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } =
|
||||||
|
await makeHierarchicalAccounts(source);
|
||||||
|
const tier5Accounts = await makeTestAccount(source, {
|
||||||
|
parent_account_id: tier4Accounts[0].account.id,
|
||||||
|
tier: 5,
|
||||||
|
});
|
||||||
|
const poNumber = 'CANCEL_TEST';
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() - 10);
|
||||||
|
await createOrder(
|
||||||
|
source,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
tier5Accounts.account.parent_account_id,
|
||||||
|
date,
|
||||||
|
1,
|
||||||
|
LICENSE_ISSUE_STATUS.ISSUED,
|
||||||
|
);
|
||||||
|
date.setDate(date.getDate() + 10);
|
||||||
|
// 発行時に論理削除されたライセンス情報
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
1,
|
||||||
|
date,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
date,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
const service = module.get<AccountsService>(AccountsService);
|
||||||
|
await service.cancelIssue(
|
||||||
|
makeContext('trackingId'),
|
||||||
|
tier1Accounts[0].users[0].external_id,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 発行待ちに戻した注文の状態確認
|
||||||
|
const orderRecord = await selectOrderLicense(
|
||||||
|
source,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
poNumber,
|
||||||
|
);
|
||||||
|
expect(orderRecord.orderLicense.issued_at).toBe(null);
|
||||||
|
expect(orderRecord.orderLicense.status).toBe(
|
||||||
|
LICENSE_ISSUE_STATUS.ISSUE_REQUESTING,
|
||||||
|
);
|
||||||
|
// 未割当に戻したライセンスの状態確認
|
||||||
|
const licenseRecord = await selectLicense(source, 1);
|
||||||
|
expect(licenseRecord.license.status).toBe(
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
);
|
||||||
|
expect(licenseRecord.license.delete_order_id).toBe(null);
|
||||||
|
expect(licenseRecord.license.deleted_at).toBe(null);
|
||||||
|
});
|
||||||
|
it('ライセンス発行のキャンセルが完了する(第二階層で実行)', async () => {
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
const { tier2Accounts: tier2Accounts, tier4Accounts: tier4Accounts } =
|
||||||
|
await makeHierarchicalAccounts(source);
|
||||||
|
const tier5Accounts = await makeTestAccount(source, {
|
||||||
|
parent_account_id: tier4Accounts[0].account.id,
|
||||||
|
tier: 5,
|
||||||
|
});
|
||||||
|
const poNumber = 'CANCEL_TEST';
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() - 10);
|
||||||
|
await createOrder(
|
||||||
|
source,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
tier5Accounts.account.parent_account_id,
|
||||||
|
date,
|
||||||
|
1,
|
||||||
|
LICENSE_ISSUE_STATUS.ISSUED,
|
||||||
|
);
|
||||||
|
date.setDate(date.getDate() + 10);
|
||||||
|
// 発行時に論理削除されたライセンス情報
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
1,
|
||||||
|
date,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
date,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
||||||
|
const service = module.get<AccountsService>(AccountsService);
|
||||||
|
await service.cancelIssue(
|
||||||
|
makeContext('trackingId'),
|
||||||
|
tier2Accounts[0].users[0].external_id,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 発行待ちに戻した注文の状態確認
|
||||||
|
const orderRecord = await selectOrderLicense(
|
||||||
|
source,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
poNumber,
|
||||||
|
);
|
||||||
|
expect(orderRecord.orderLicense.issued_at).toBe(null);
|
||||||
|
expect(orderRecord.orderLicense.status).toBe(
|
||||||
|
LICENSE_ISSUE_STATUS.ISSUE_REQUESTING,
|
||||||
|
);
|
||||||
|
// 未割当に戻したライセンスの状態確認
|
||||||
|
const licenseRecord = await selectLicense(source, 1);
|
||||||
|
expect(licenseRecord.license.status).toBe(
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
);
|
||||||
|
expect(licenseRecord.license.delete_order_id).toBe(null);
|
||||||
|
expect(licenseRecord.license.deleted_at).toBe(null);
|
||||||
|
});
|
||||||
|
it('キャンセル対象の発行が存在しない場合エラー', async () => {
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } =
|
||||||
|
await makeHierarchicalAccounts(source);
|
||||||
|
const tier5Accounts = await makeTestAccount(source, {
|
||||||
|
parent_account_id: tier4Accounts[0].account.id,
|
||||||
|
tier: 5,
|
||||||
|
});
|
||||||
|
const poNumber = 'CANCEL_TEST';
|
||||||
|
const service = module.get<AccountsService>(AccountsService);
|
||||||
|
await expect(
|
||||||
|
service.cancelIssue(
|
||||||
|
makeContext('trackingId'),
|
||||||
|
tier1Accounts[0].users[0].external_id,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
),
|
||||||
|
).rejects.toEqual(
|
||||||
|
new HttpException(makeErrorResponse('E010809'), HttpStatus.BAD_REQUEST),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('キャンセル対象の発行が14日より経過していた場合エラー', async () => {
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } =
|
||||||
|
await makeHierarchicalAccounts(source);
|
||||||
|
const tier5Accounts = await makeTestAccount(source, {
|
||||||
|
parent_account_id: tier4Accounts[0].account.id,
|
||||||
|
tier: 5,
|
||||||
|
});
|
||||||
|
const poNumber = 'CANCEL_TEST';
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() - 15);
|
||||||
|
await createOrder(
|
||||||
|
source,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
tier5Accounts.account.parent_account_id,
|
||||||
|
date,
|
||||||
|
1,
|
||||||
|
LICENSE_ISSUE_STATUS.ISSUED,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
1,
|
||||||
|
date,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
const service = module.get<AccountsService>(AccountsService);
|
||||||
|
await expect(
|
||||||
|
service.cancelIssue(
|
||||||
|
makeContext('trackingId'),
|
||||||
|
tier1Accounts[0].users[0].external_id,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
),
|
||||||
|
).rejects.toEqual(
|
||||||
|
new HttpException(makeErrorResponse('E010810'), HttpStatus.BAD_REQUEST),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('キャンセル対象の発行のライセンスが使われていた場合エラー', async () => {
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
const { tier1Accounts: tier1Accounts, tier4Accounts: tier4Accounts } =
|
||||||
|
await makeHierarchicalAccounts(source);
|
||||||
|
const tier5Accounts = await makeTestAccount(source, {
|
||||||
|
parent_account_id: tier4Accounts[0].account.id,
|
||||||
|
tier: 5,
|
||||||
|
});
|
||||||
|
const poNumber = 'CANCEL_TEST';
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() - 14);
|
||||||
|
await createOrder(
|
||||||
|
source,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
tier5Accounts.account.parent_account_id,
|
||||||
|
date,
|
||||||
|
1,
|
||||||
|
LICENSE_ISSUE_STATUS.ISSUED,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
1,
|
||||||
|
date,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
const service = module.get<AccountsService>(AccountsService);
|
||||||
|
await expect(
|
||||||
|
service.cancelIssue(
|
||||||
|
makeContext('trackingId'),
|
||||||
|
tier1Accounts[0].users[0].external_id,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
),
|
||||||
|
).rejects.toEqual(
|
||||||
|
new HttpException(makeErrorResponse('E010811'), HttpStatus.BAD_REQUEST),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
it('自身のパートナー以外の発行をキャンセルしようとした場合、エラー', async () => {
|
||||||
|
const module = await makeTestingModule(source);
|
||||||
|
const { tier1Accounts: tier1Accounts } = await makeHierarchicalAccounts(
|
||||||
|
source,
|
||||||
|
);
|
||||||
|
const tier5Accounts = await makeTestAccount(source, {
|
||||||
|
parent_account_id: 100,
|
||||||
|
tier: 5,
|
||||||
|
});
|
||||||
|
const poNumber = 'CANCEL_TEST';
|
||||||
|
const date = new Date();
|
||||||
|
date.setDate(date.getDate() - 14);
|
||||||
|
await createOrder(
|
||||||
|
source,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
tier5Accounts.account.parent_account_id,
|
||||||
|
date,
|
||||||
|
1,
|
||||||
|
LICENSE_ISSUE_STATUS.ISSUED,
|
||||||
|
);
|
||||||
|
await createLicense(
|
||||||
|
source,
|
||||||
|
1,
|
||||||
|
date,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
LICENSE_TYPE.NORMAL,
|
||||||
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
null,
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
const service = module.get<AccountsService>(AccountsService);
|
||||||
|
await expect(
|
||||||
|
service.cancelIssue(
|
||||||
|
makeContext('trackingId'),
|
||||||
|
tier1Accounts[0].users[0].external_id,
|
||||||
|
poNumber,
|
||||||
|
tier5Accounts.account.id,
|
||||||
|
),
|
||||||
|
).rejects.toEqual(
|
||||||
|
new HttpException(makeErrorResponse('E000108'), HttpStatus.UNAUTHORIZED),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -40,6 +40,9 @@ import {
|
|||||||
LicensesShortageError,
|
LicensesShortageError,
|
||||||
AlreadyIssuedError,
|
AlreadyIssuedError,
|
||||||
OrderNotFoundError,
|
OrderNotFoundError,
|
||||||
|
AlreadyLicenseStatusChangedError,
|
||||||
|
AlreadyLicenseAllocatedError,
|
||||||
|
CancellationPeriodExpiredError,
|
||||||
} from '../../repositories/licenses/errors/types';
|
} from '../../repositories/licenses/errors/types';
|
||||||
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
import { BlobstorageService } from '../../gateways/blobstorage/blobstorage.service';
|
||||||
import {
|
import {
|
||||||
@ -1057,6 +1060,90 @@ export class AccountsService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ライセンス発行をキャンセルする
|
||||||
|
* @param context
|
||||||
|
* @param extarnalId
|
||||||
|
* @param poNumber
|
||||||
|
* @param orderedAccountId
|
||||||
|
*/
|
||||||
|
async cancelIssue(
|
||||||
|
context: Context,
|
||||||
|
extarnalId: string,
|
||||||
|
poNumber: string,
|
||||||
|
orderedAccountId: number,
|
||||||
|
): Promise<void> {
|
||||||
|
this.logger.log(
|
||||||
|
`[IN] [${context.trackingId}] ${this.cancelIssue.name} | params: { ` +
|
||||||
|
`extarnalId: ${extarnalId}, ` +
|
||||||
|
`poNumber: ${poNumber}, ` +
|
||||||
|
`orderedAccountId: ${orderedAccountId}, };`,
|
||||||
|
);
|
||||||
|
let myAccountId: number;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// ユーザIDからアカウントIDを取得する
|
||||||
|
myAccountId = (
|
||||||
|
await this.usersRepository.findUserByExternalId(extarnalId)
|
||||||
|
).account_id;
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error(`error=${e}`);
|
||||||
|
switch (e.constructor) {
|
||||||
|
default:
|
||||||
|
throw new HttpException(
|
||||||
|
makeErrorResponse('E009999'),
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.logger.log(`[OUT] [${context.trackingId}] ${this.cancelIssue.name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注文元アカウントIDの親世代を取得
|
||||||
|
const parentAccountIds = await this.accountRepository.getHierarchyParents(
|
||||||
|
orderedAccountId,
|
||||||
|
);
|
||||||
|
// 自身が存在しない場合、エラー
|
||||||
|
if (!parentAccountIds.includes(myAccountId)) {
|
||||||
|
this.logger.log(`[OUT] [${context.trackingId}] ${this.cancelIssue.name}`);
|
||||||
|
throw new HttpException(
|
||||||
|
makeErrorResponse('E000108'),
|
||||||
|
HttpStatus.UNAUTHORIZED,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 発行キャンセル処理
|
||||||
|
await this.accountRepository.cancelIssue(orderedAccountId, poNumber);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error(`error=${e}`);
|
||||||
|
switch (e.constructor) {
|
||||||
|
case AlreadyLicenseStatusChangedError:
|
||||||
|
throw new HttpException(
|
||||||
|
makeErrorResponse('E010809'),
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
case CancellationPeriodExpiredError:
|
||||||
|
throw new HttpException(
|
||||||
|
makeErrorResponse('E010810'),
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
case AlreadyLicenseAllocatedError:
|
||||||
|
throw new HttpException(
|
||||||
|
makeErrorResponse('E010811'),
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw new HttpException(
|
||||||
|
makeErrorResponse('E009999'),
|
||||||
|
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.logger.log(`[OUT] [${context.trackingId}] ${this.cancelIssue.name}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ワークタイプ一覧を取得します
|
* ワークタイプ一覧を取得します
|
||||||
* @param context
|
* @param context
|
||||||
|
|||||||
@ -20,17 +20,26 @@ export const getSortCriteria = async (dataSource: DataSource) => {
|
|||||||
|
|
||||||
export const createLicense = async (
|
export const createLicense = async (
|
||||||
datasource: DataSource,
|
datasource: DataSource,
|
||||||
|
licenseId: number,
|
||||||
|
expiry_date: Date,
|
||||||
accountId: number,
|
accountId: number,
|
||||||
|
type: string,
|
||||||
|
status: string,
|
||||||
|
allocated_user_id: number,
|
||||||
|
order_id: number,
|
||||||
|
deleted_at: Date,
|
||||||
|
delete_order_id: number,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const { identifiers } = await datasource.getRepository(License).insert({
|
const { identifiers } = await datasource.getRepository(License).insert({
|
||||||
expiry_date: null,
|
id: licenseId,
|
||||||
|
expiry_date: expiry_date,
|
||||||
account_id: accountId,
|
account_id: accountId,
|
||||||
type: 'NORMAL',
|
type: type,
|
||||||
status: 'Unallocated',
|
status: status,
|
||||||
allocated_user_id: null,
|
allocated_user_id: allocated_user_id,
|
||||||
order_id: null,
|
order_id: order_id,
|
||||||
deleted_at: null,
|
deleted_at: deleted_at,
|
||||||
delete_order_id: null,
|
delete_order_id: delete_order_id,
|
||||||
created_by: 'test_runner',
|
created_by: 'test_runner',
|
||||||
created_at: new Date(),
|
created_at: new Date(),
|
||||||
updated_by: 'updater',
|
updated_by: 'updater',
|
||||||
|
|||||||
@ -346,6 +346,9 @@ describe('DBテスト', () => {
|
|||||||
LICENSE_TYPE.CARD,
|
LICENSE_TYPE.CARD,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createCardLicense(source, license_id, issueId, cardLicenseKey);
|
await createCardLicense(source, license_id, issueId, cardLicenseKey);
|
||||||
await createCardLicenseIssue(source, issueId);
|
await createCardLicenseIssue(source, issueId);
|
||||||
@ -386,6 +389,9 @@ describe('DBテスト', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
// 2件目、expiry_dateがnull(OneYear)
|
// 2件目、expiry_dateがnull(OneYear)
|
||||||
await createLicense(
|
await createLicense(
|
||||||
@ -396,6 +402,9 @@ describe('DBテスト', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
// 3件目、1件目と同じ有効期限
|
// 3件目、1件目と同じ有効期限
|
||||||
await createLicense(
|
await createLicense(
|
||||||
@ -406,6 +415,9 @@ describe('DBテスト', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
// 4件目、expiry_dateが一番遠いデータ
|
// 4件目、expiry_dateが一番遠いデータ
|
||||||
await createLicense(
|
await createLicense(
|
||||||
@ -416,6 +428,9 @@ describe('DBテスト', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
// 5件目、expiry_dateがnull(OneYear)
|
// 5件目、expiry_dateがnull(OneYear)
|
||||||
await createLicense(
|
await createLicense(
|
||||||
@ -426,6 +441,9 @@ describe('DBテスト', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
// 6件目、ライセンス状態が割当済
|
// 6件目、ライセンス状態が割当済
|
||||||
await createLicense(
|
await createLicense(
|
||||||
@ -436,6 +454,9 @@ describe('DBテスト', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
// 7件目、ライセンス状態が削除済
|
// 7件目、ライセンス状態が削除済
|
||||||
await createLicense(
|
await createLicense(
|
||||||
@ -446,6 +467,9 @@ describe('DBテスト', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.DELETED,
|
LICENSE_ALLOCATED_STATUS.DELETED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
// 8件目、別アカウントの未割当のライセンス
|
// 8件目、別アカウントの未割当のライセンス
|
||||||
await createLicense(
|
await createLicense(
|
||||||
@ -456,6 +480,9 @@ describe('DBテスト', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
// 9件目、有効期限切れのライセンス
|
// 9件目、有効期限切れのライセンス
|
||||||
await createLicense(
|
await createLicense(
|
||||||
@ -466,6 +493,9 @@ describe('DBテスト', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
const service = module.get<LicensesService>(LicensesService);
|
const service = module.get<LicensesService>(LicensesService);
|
||||||
const context = makeContext('userId');
|
const context = makeContext('userId');
|
||||||
@ -518,6 +548,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.CARD,
|
LICENSE_TYPE.CARD,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
||||||
|
|
||||||
@ -570,6 +603,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
||||||
|
|
||||||
@ -616,6 +652,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||||
userId,
|
userId,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicense(
|
await createLicense(
|
||||||
source,
|
source,
|
||||||
@ -625,6 +664,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.CARD,
|
LICENSE_TYPE.CARD,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
||||||
|
|
||||||
@ -697,6 +739,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||||
userId,
|
userId,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicense(
|
await createLicense(
|
||||||
source,
|
source,
|
||||||
@ -706,6 +751,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.CARD,
|
LICENSE_TYPE.CARD,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
||||||
|
|
||||||
@ -742,6 +790,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.CARD,
|
LICENSE_TYPE.CARD,
|
||||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||||
userId,
|
userId,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicense(
|
await createLicense(
|
||||||
source,
|
source,
|
||||||
@ -751,6 +802,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.CARD,
|
LICENSE_TYPE.CARD,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicenseAllocationHistory(source, 1, userId, 1, 'CARD');
|
await createLicenseAllocationHistory(source, 1, userId, 1, 'CARD');
|
||||||
|
|
||||||
@ -787,6 +841,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.TRIAL,
|
LICENSE_TYPE.TRIAL,
|
||||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||||
userId,
|
userId,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicense(
|
await createLicense(
|
||||||
source,
|
source,
|
||||||
@ -796,6 +853,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.CARD,
|
LICENSE_TYPE.CARD,
|
||||||
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicenseAllocationHistory(source, 1, userId, 1, 'TRIAL');
|
await createLicenseAllocationHistory(source, 1, userId, 1, 'TRIAL');
|
||||||
|
|
||||||
@ -832,6 +892,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
const service = module.get<UsersService>(UsersService);
|
const service = module.get<UsersService>(UsersService);
|
||||||
@ -863,6 +926,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicense(
|
await createLicense(
|
||||||
source,
|
source,
|
||||||
@ -872,6 +938,9 @@ describe('ライセンス割り当て', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.DELETED,
|
LICENSE_ALLOCATED_STATUS.DELETED,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
const service = module.get<UsersService>(UsersService);
|
const service = module.get<UsersService>(UsersService);
|
||||||
@ -927,6 +996,9 @@ describe('ライセンス割り当て解除', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||||
userId,
|
userId,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
||||||
|
|
||||||
@ -987,6 +1059,9 @@ describe('ライセンス割り当て解除', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
LICENSE_ALLOCATED_STATUS.ALLOCATED,
|
||||||
2,
|
2,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicense(
|
await createLicense(
|
||||||
source,
|
source,
|
||||||
@ -996,6 +1071,9 @@ describe('ライセンス割り当て解除', () => {
|
|||||||
LICENSE_TYPE.NORMAL,
|
LICENSE_TYPE.NORMAL,
|
||||||
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
LICENSE_ALLOCATED_STATUS.REUSABLE,
|
||||||
userId,
|
userId,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
);
|
);
|
||||||
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
await createLicenseAllocationHistory(source, 1, userId, 1, 'NONE');
|
||||||
|
|
||||||
@ -1037,6 +1115,7 @@ describe('ライセンス注文キャンセル', () => {
|
|||||||
poNumber,
|
poNumber,
|
||||||
tier2Accounts[0].account.id,
|
tier2Accounts[0].account.id,
|
||||||
tier2Accounts[0].account.parent_account_id,
|
tier2Accounts[0].account.parent_account_id,
|
||||||
|
null,
|
||||||
10,
|
10,
|
||||||
'Issue Requesting',
|
'Issue Requesting',
|
||||||
);
|
);
|
||||||
@ -1046,6 +1125,7 @@ describe('ライセンス注文キャンセル', () => {
|
|||||||
poNumber,
|
poNumber,
|
||||||
tier2Accounts[0].account.id,
|
tier2Accounts[0].account.id,
|
||||||
tier2Accounts[0].account.parent_account_id,
|
tier2Accounts[0].account.parent_account_id,
|
||||||
|
null,
|
||||||
10,
|
10,
|
||||||
'Order Canceled',
|
'Order Canceled',
|
||||||
);
|
);
|
||||||
@ -1078,6 +1158,7 @@ describe('ライセンス注文キャンセル', () => {
|
|||||||
poNumber,
|
poNumber,
|
||||||
tier2Accounts[0].account.id,
|
tier2Accounts[0].account.id,
|
||||||
tier2Accounts[0].account.parent_account_id,
|
tier2Accounts[0].account.parent_account_id,
|
||||||
|
null,
|
||||||
10,
|
10,
|
||||||
'Issued',
|
'Issued',
|
||||||
);
|
);
|
||||||
@ -1106,6 +1187,7 @@ describe('ライセンス注文キャンセル', () => {
|
|||||||
poNumber,
|
poNumber,
|
||||||
tier2Accounts[0].account.id,
|
tier2Accounts[0].account.id,
|
||||||
tier2Accounts[0].account.parent_account_id,
|
tier2Accounts[0].account.parent_account_id,
|
||||||
|
null,
|
||||||
10,
|
10,
|
||||||
'Order Canceled',
|
'Order Canceled',
|
||||||
);
|
);
|
||||||
|
|||||||
@ -15,6 +15,9 @@ export const createLicense = async (
|
|||||||
type: string,
|
type: string,
|
||||||
status: string,
|
status: string,
|
||||||
allocated_user_id: number,
|
allocated_user_id: number,
|
||||||
|
order_id: number,
|
||||||
|
deleted_at: Date,
|
||||||
|
delete_order_id: number,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const { identifiers } = await datasource.getRepository(License).insert({
|
const { identifiers } = await datasource.getRepository(License).insert({
|
||||||
id: licenseId,
|
id: licenseId,
|
||||||
@ -23,9 +26,9 @@ export const createLicense = async (
|
|||||||
type: type,
|
type: type,
|
||||||
status: status,
|
status: status,
|
||||||
allocated_user_id: allocated_user_id,
|
allocated_user_id: allocated_user_id,
|
||||||
order_id: null,
|
order_id: order_id,
|
||||||
deleted_at: null,
|
deleted_at: deleted_at,
|
||||||
delete_order_id: null,
|
delete_order_id: delete_order_id,
|
||||||
created_by: 'test_runner',
|
created_by: 'test_runner',
|
||||||
created_at: new Date(),
|
created_at: new Date(),
|
||||||
updated_by: 'updater',
|
updated_by: 'updater',
|
||||||
@ -100,6 +103,7 @@ export const createOrder = async (
|
|||||||
poNumber: string,
|
poNumber: string,
|
||||||
fromId: number,
|
fromId: number,
|
||||||
toId: number,
|
toId: number,
|
||||||
|
issuedAt: Date,
|
||||||
quantity: number,
|
quantity: number,
|
||||||
status: string,
|
status: string,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
@ -108,7 +112,7 @@ export const createOrder = async (
|
|||||||
from_account_id: fromId,
|
from_account_id: fromId,
|
||||||
to_account_id: toId,
|
to_account_id: toId,
|
||||||
ordered_at: new Date(),
|
ordered_at: new Date(),
|
||||||
issued_at: null,
|
issued_at: issuedAt,
|
||||||
quantity: quantity,
|
quantity: quantity,
|
||||||
status: status,
|
status: status,
|
||||||
canceled_at: null,
|
canceled_at: null,
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import {
|
|||||||
} from '../../common/types/sort/util';
|
} from '../../common/types/sort/util';
|
||||||
import {
|
import {
|
||||||
LICENSE_ALLOCATED_STATUS,
|
LICENSE_ALLOCATED_STATUS,
|
||||||
|
LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
||||||
LICENSE_ISSUE_STATUS,
|
LICENSE_ISSUE_STATUS,
|
||||||
TIERS,
|
TIERS,
|
||||||
} from '../../constants';
|
} from '../../constants';
|
||||||
@ -28,6 +29,12 @@ import {
|
|||||||
PartnerLicenseInfoForRepository,
|
PartnerLicenseInfoForRepository,
|
||||||
} from '../../features/accounts/types/types';
|
} from '../../features/accounts/types/types';
|
||||||
import { AccountNotFoundError } from './errors/types';
|
import { AccountNotFoundError } from './errors/types';
|
||||||
|
import {
|
||||||
|
AlreadyLicenseAllocatedError,
|
||||||
|
AlreadyLicenseStatusChangedError,
|
||||||
|
CancellationPeriodExpiredError,
|
||||||
|
} from '../licenses/errors/types';
|
||||||
|
import { DateWithZeroTime } from '../../features/licenses/types/types';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AccountsRepositoryService {
|
export class AccountsRepositoryService {
|
||||||
@ -572,4 +579,110 @@ export class AccountsRepositoryService {
|
|||||||
|
|
||||||
return accounts;
|
return accounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 対象のアカウントIDの親世代のアカウントIDをすべて取得する
|
||||||
|
* 順番は、階層(tier)の下位から上位に向かって格納
|
||||||
|
* @param targetAccountId
|
||||||
|
* @returns accountIds
|
||||||
|
*/
|
||||||
|
async getHierarchyParents(targetAccountId: number): Promise<number[]> {
|
||||||
|
return await this.dataSource.transaction(async (entityManager) => {
|
||||||
|
const accountRepository = entityManager.getRepository(Account);
|
||||||
|
const maxTierDifference = TIERS.TIER5 - TIERS.TIER1;
|
||||||
|
const parentAccountIds = [];
|
||||||
|
|
||||||
|
let currentAccountId = targetAccountId;
|
||||||
|
// システム的な最大の階層差異分、親を参照する
|
||||||
|
for (let i = 0; i < maxTierDifference; i++) {
|
||||||
|
const account = await accountRepository.findOne({
|
||||||
|
where: {
|
||||||
|
id: currentAccountId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!account) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
parentAccountIds.push(account.parent_account_id);
|
||||||
|
currentAccountId = account.parent_account_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parentAccountIds;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注文元アカウントIDとPOナンバーに紐づくライセンス発行をキャンセルする
|
||||||
|
* @param orderedAccountId:キャンセルしたい発行の注文元アカウントID
|
||||||
|
* @param poNumber:POナンバー
|
||||||
|
*/
|
||||||
|
async cancelIssue(orderedAccountId: number, poNumber: string): Promise<void> {
|
||||||
|
await this.dataSource.transaction(async (entityManager) => {
|
||||||
|
const orderRepo = entityManager.getRepository(LicenseOrder);
|
||||||
|
|
||||||
|
// キャンセル対象の発行を取得
|
||||||
|
const targetOrder = await orderRepo.findOne({
|
||||||
|
where: {
|
||||||
|
from_account_id: orderedAccountId,
|
||||||
|
po_number: poNumber,
|
||||||
|
status: LICENSE_ISSUE_STATUS.ISSUED,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// キャンセル対象の発行が存在しない場合エラー
|
||||||
|
if (!targetOrder) {
|
||||||
|
throw new AlreadyLicenseStatusChangedError(
|
||||||
|
`Cancel issue is failed. Already license order status changed. fromAccountId: ${orderedAccountId}, poNumber: ${poNumber}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// キャンセル可能な日付(発行日から14日経過)かどうかに判定する時刻を取得する
|
||||||
|
const currentDateWithoutTime = new DateWithZeroTime();
|
||||||
|
const issuedDateWithoutTime = new DateWithZeroTime(targetOrder.issued_at);
|
||||||
|
const timeDifference =
|
||||||
|
currentDateWithoutTime.getTime() - issuedDateWithoutTime.getTime();
|
||||||
|
const daysDifference = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
|
||||||
|
// 発行日から14日経過しているかをチェック
|
||||||
|
if (daysDifference > LICENSE_EXPIRATION_THRESHOLD_DAYS) {
|
||||||
|
throw new CancellationPeriodExpiredError(
|
||||||
|
`Cancel issue is failed. Cancellation period expired. fromAccountId: ${orderedAccountId}, poNumber: ${poNumber}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// すでに割り当て済みライセンスを含む注文か確認する
|
||||||
|
const licenseRepo = entityManager.getRepository(License);
|
||||||
|
const allocatedLicense = await licenseRepo.findOne({
|
||||||
|
where: {
|
||||||
|
order_id: targetOrder.id,
|
||||||
|
status: Not(LICENSE_ALLOCATED_STATUS.UNALLOCATED),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 存在した場合エラー
|
||||||
|
if (allocatedLicense) {
|
||||||
|
throw new AlreadyLicenseAllocatedError(
|
||||||
|
`Cancel issue is failed. Already license allocated. fromAccountId: ${orderedAccountId}, poNumber: ${poNumber}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用の変数に値をコピー
|
||||||
|
const updatedOrder = { ...targetOrder };
|
||||||
|
|
||||||
|
// 注文を発行待ちに戻す
|
||||||
|
updatedOrder.issued_at = null;
|
||||||
|
updatedOrder.status = LICENSE_ISSUE_STATUS.ISSUE_REQUESTING;
|
||||||
|
await orderRepo.save(updatedOrder);
|
||||||
|
// 発行時に削除したライセンスを未割当に戻す
|
||||||
|
await licenseRepo.update(
|
||||||
|
{ delete_order_id: targetOrder.id },
|
||||||
|
{
|
||||||
|
status: LICENSE_ALLOCATED_STATUS.UNALLOCATED,
|
||||||
|
deleted_at: null,
|
||||||
|
delete_order_id: null,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// 発行時に発行されたライセンスを削除する
|
||||||
|
await licenseRepo.delete({ order_id: targetOrder.id });
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,3 +24,12 @@ export class LicenseAlreadyDeallocatedError extends Error {}
|
|||||||
|
|
||||||
// 注文キャンセル失敗エラー
|
// 注文キャンセル失敗エラー
|
||||||
export class CancelOrderFailedError extends Error {}
|
export class CancelOrderFailedError extends Error {}
|
||||||
|
|
||||||
|
// ライセンス発行キャンセル不可エラー(ステータスが変えられている場合)
|
||||||
|
export class AlreadyLicenseStatusChangedError extends Error {}
|
||||||
|
|
||||||
|
// ライセンス発行キャンセル不可エラー(発行から一定期間経過した場合)
|
||||||
|
export class CancellationPeriodExpiredError extends Error {}
|
||||||
|
|
||||||
|
// ライセンス発行キャンセル不可エラー(発行したライセンスが割り当てされている場合)
|
||||||
|
export class AlreadyLicenseAllocatedError extends Error {}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { DataSource, In, IsNull, MoreThanOrEqual } from 'typeorm';
|
import { DataSource, In } from 'typeorm';
|
||||||
import {
|
import {
|
||||||
LicenseOrder,
|
LicenseOrder,
|
||||||
License,
|
License,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user