Merged PR 918: analysisLicenses修正

## 概要
[Task4313: analysisLicenses修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/4313)

- ライセンス推移CSVの出力内容を修正
  - 移行ライセンスの利用状況も出力する
  - 出力する移行ライセンスの条件は以下
     - 第五階層
     - 割り当て済
     - 有効期限が当日以降
     - 年間ライセンス
- テスト修正
  - 移行ライセンスの取得・変換ロジックのテスト修正

## レビューポイント
- 特になし

## UIの変更
- Before/Afterのスクショなど
- スクショ置き場

## クエリの変更
- Repositoryを変更し、クエリが変更された場合は変更内容を確認する
- Before/Afterのクエリ
- クエリ置き場

## 動作確認状況
- ローカルで確認、develop環境で確認など
- 行った修正がデグレを発生させていないことを確認できるか
  - 具体的にどのような確認をしたか
    - どのケースに対してどのような手段でデグレがないことを担保しているか

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2024-07-23 03:35:55 +00:00
parent df55be5e19
commit 353d5ad462
3 changed files with 217 additions and 4 deletions

View File

@ -106,6 +106,7 @@ export const LICENSE_TYPE = {
TRIAL: "TRIAL", TRIAL: "TRIAL",
NORMAL: "NORMAL", NORMAL: "NORMAL",
CARD: "CARD", CARD: "CARD",
NONE: "NONE",
} as const; } as const;
/** /**
* *
@ -385,6 +386,7 @@ export const LICENSE_COUNT_ANALYSIS_LICENSE_TYPE = {
CARD: "Card", CARD: "Card",
SWITCH_FROM_TRIAL: "トライアルから切り替え", SWITCH_FROM_TRIAL: "トライアルから切り替え",
SWITCH_FROM_CARD: "カードから切り替え", SWITCH_FROM_CARD: "カードから切り替え",
NONE: "移行ライセンス",
}; };
/** /**
* CSV項目で使用する日本語 * CSV項目で使用する日本語

View File

@ -486,6 +486,7 @@ export async function transferData(
(license) => license.account_id === account.id (license) => license.account_id === account.id
); );
// 抽出したライセンスを種別ごとに分ける。typeカラムで判別トライアル・通常・カード // 抽出したライセンスを種別ごとに分ける。typeカラムで判別トライアル・通常・カード
// 種別にNoneを追加⇒旧システムからの移行分の状況も確認したい 2024年7月9日
const trialLicenses = accountLicenses.filter( const trialLicenses = accountLicenses.filter(
(license) => license.type === LICENSE_TYPE.TRIAL (license) => license.type === LICENSE_TYPE.TRIAL
); );
@ -495,6 +496,9 @@ export async function transferData(
const cardLicenses = accountLicenses.filter( const cardLicenses = accountLicenses.filter(
(license) => license.type === LICENSE_TYPE.CARD (license) => license.type === LICENSE_TYPE.CARD
); );
const noneLicense = accountLicenses.filter(
(license) => license.type === LICENSE_TYPE.NONE
);
// 種別ごとのライセンスから使用中のライセンスを抽出statusカラムがAllocated // 種別ごとのライセンスから使用中のライセンスを抽出statusカラムがAllocated
const usedTrialLicenses = trialLicenses.filter( const usedTrialLicenses = trialLicenses.filter(
(license) => license.status === LICENSE_ALLOCATED_STATUS.ALLOCATED (license) => license.status === LICENSE_ALLOCATED_STATUS.ALLOCATED
@ -505,6 +509,9 @@ export async function transferData(
const usedCardLicenses = cardLicenses.filter( const usedCardLicenses = cardLicenses.filter(
(license) => license.status === LICENSE_ALLOCATED_STATUS.ALLOCATED (license) => license.status === LICENSE_ALLOCATED_STATUS.ALLOCATED
); );
const usedNoneLicense = noneLicense.filter(
(license) => license.status === LICENSE_ALLOCATED_STATUS.ALLOCATED
);
// どのロールのユーザーが使用しているライセンスかを判別し、ロールごとに分ける。 // どのロールのユーザーが使用しているライセンスかを判別し、ロールごとに分ける。
// allcated_user_idからユーザーを特定 // allcated_user_idからユーザーを特定
// (Author・Typist・None) // (Author・Typist・None)
@ -584,6 +591,7 @@ export async function transferData(
const usedCardLicensesAuthorCount = usedCardLicensesAuthor.length; const usedCardLicensesAuthorCount = usedCardLicensesAuthor.length;
const usedCardLicensesTypistCount = usedCardLicensesTypist.length; const usedCardLicensesTypistCount = usedCardLicensesTypist.length;
const usedCardLicensesNoneCount = usedCardLicensesNone.length; const usedCardLicensesNoneCount = usedCardLicensesNone.length;
const usedNoneLicenseCount = usedNoneLicense.length;
// アカウントに紐づく当月発行ライセンスを取得 // アカウントに紐づく当月発行ライセンスを取得
const accountCurrentMonthIssuedLicenses = const accountCurrentMonthIssuedLicenses =
@ -817,6 +825,7 @@ export async function transferData(
trialLicensesCount, trialLicensesCount,
normalLicensesCount, normalLicensesCount,
cardLicensesCount, cardLicensesCount,
usedNoneLicenseCount,
usedTrialLicensesAuthorCount, usedTrialLicensesAuthorCount,
usedTrialLicensesTypistCount, usedTrialLicensesTypistCount,
usedTrialLicensesNoneCount, usedTrialLicensesNoneCount,
@ -858,6 +867,7 @@ export async function transferData(
trialLicensesCount, trialLicensesCount,
normalLicensesCount, normalLicensesCount,
cardLicensesCount, cardLicensesCount,
usedNoneLicenseCount,
usedTrialLicensesAuthorCount, usedTrialLicensesAuthorCount,
usedTrialLicensesTypistCount, usedTrialLicensesTypistCount,
usedTrialLicensesNoneCount, usedTrialLicensesNoneCount,
@ -899,6 +909,7 @@ export async function transferData(
trialLicensesCount, trialLicensesCount,
normalLicensesCount, normalLicensesCount,
cardLicensesCount, cardLicensesCount,
usedNoneLicenseCount,
usedTrialLicensesAuthorCount, usedTrialLicensesAuthorCount,
usedTrialLicensesTypistCount, usedTrialLicensesTypistCount,
usedTrialLicensesNoneCount, usedTrialLicensesNoneCount,
@ -959,6 +970,7 @@ export async function transferData(
(license) => license.account_id === account.id (license) => license.account_id === account.id
); );
// 抽出したライセンスを種別ごとに分ける。typeカラムで判別トライアル・通常・カード // 抽出したライセンスを種別ごとに分ける。typeカラムで判別トライアル・通常・カード
// 種別にNoneを追加⇒旧システムからの移行分の状況も確認したい 2024年7月9日
const trialLicenses = accountLicenses.filter( const trialLicenses = accountLicenses.filter(
(license) => license.type === LICENSE_TYPE.TRIAL (license) => license.type === LICENSE_TYPE.TRIAL
); );
@ -968,6 +980,9 @@ export async function transferData(
const cardLicenses = accountLicenses.filter( const cardLicenses = accountLicenses.filter(
(license) => license.type === LICENSE_TYPE.CARD (license) => license.type === LICENSE_TYPE.CARD
); );
const noneLicense = accountLicenses.filter(
(license) => license.type === LICENSE_TYPE.NONE
);
// 種別ごとのライセンスから使用中のライセンスを抽出statusカラムがAllocated // 種別ごとのライセンスから使用中のライセンスを抽出statusカラムがAllocated
const usedTrialLicenses = trialLicenses.filter( const usedTrialLicenses = trialLicenses.filter(
(license) => license.status === LICENSE_ALLOCATED_STATUS.ALLOCATED (license) => license.status === LICENSE_ALLOCATED_STATUS.ALLOCATED
@ -978,6 +993,9 @@ export async function transferData(
const usedCardLicenses = cardLicenses.filter( const usedCardLicenses = cardLicenses.filter(
(license) => license.status === LICENSE_ALLOCATED_STATUS.ALLOCATED (license) => license.status === LICENSE_ALLOCATED_STATUS.ALLOCATED
); );
const usedNoneLicense = noneLicense.filter(
(license) => (license.status === LICENSE_ALLOCATED_STATUS.ALLOCATED)
);
// どのロールのユーザーが使用しているライセンスかを判別し、ロールごとに分ける。 // どのロールのユーザーが使用しているライセンスかを判別し、ロールごとに分ける。
// allcated_user_idからユーザーを特定 // allcated_user_idからユーザーを特定
// (Author・Typist・None) // (Author・Typist・None)
@ -1048,6 +1066,7 @@ export async function transferData(
const usedCardLicensesAuthorCount = usedCardLicensesAuthor.length; const usedCardLicensesAuthorCount = usedCardLicensesAuthor.length;
const usedCardLicensesTypistCount = usedCardLicensesTypist.length; const usedCardLicensesTypistCount = usedCardLicensesTypist.length;
const usedCardLicensesNoneCount = usedCardLicensesNone.length; const usedCardLicensesNoneCount = usedCardLicensesNone.length;
const usedNoneLicenseCount = usedNoneLicense.length;
// アカウントに紐づく当月発行ライセンスを取得 // アカウントに紐づく当月発行ライセンスを取得
const accountCurrentMonthIssuedLicenses = const accountCurrentMonthIssuedLicenses =
@ -1256,6 +1275,7 @@ export async function transferData(
trialLicensesCount, trialLicensesCount,
normalLicensesCount, normalLicensesCount,
cardLicensesCount, cardLicensesCount,
usedNoneLicenseCount,
usedTrialLicensesAuthorCount, usedTrialLicensesAuthorCount,
usedTrialLicensesTypistCount, usedTrialLicensesTypistCount,
usedTrialLicensesNoneCount, usedTrialLicensesNoneCount,
@ -1297,6 +1317,7 @@ export async function transferData(
trialLicensesCount, trialLicensesCount,
normalLicensesCount, normalLicensesCount,
cardLicensesCount, cardLicensesCount,
usedNoneLicenseCount,
usedTrialLicensesAuthorCount, usedTrialLicensesAuthorCount,
usedTrialLicensesTypistCount, usedTrialLicensesTypistCount,
usedTrialLicensesNoneCount, usedTrialLicensesNoneCount,
@ -1338,6 +1359,7 @@ export async function transferData(
trialLicensesCount, trialLicensesCount,
normalLicensesCount, normalLicensesCount,
cardLicensesCount, cardLicensesCount,
usedNoneLicenseCount,
usedTrialLicensesAuthorCount, usedTrialLicensesAuthorCount,
usedTrialLicensesTypistCount, usedTrialLicensesTypistCount,
usedTrialLicensesNoneCount, usedTrialLicensesNoneCount,
@ -1433,6 +1455,7 @@ async function createOutputData(
trialLicensesCount: number, trialLicensesCount: number,
normalLicensesCount: number, normalLicensesCount: number,
cardLicensesCount: number, cardLicensesCount: number,
usedNoneLicenseCount: number,
usedTrialLicensesAuthorCount: number, usedTrialLicensesAuthorCount: number,
usedTrialLicensesTypistCount: number, usedTrialLicensesTypistCount: number,
usedTrialLicensesNoneCount: number, usedTrialLicensesNoneCount: number,
@ -1505,6 +1528,16 @@ async function createOutputData(
'"' + "" + '",', '"' + "" + '",',
'"' + cardLicensesCount.toString() + '"\r\n' '"' + cardLicensesCount.toString() + '"\r\n'
); );
// ユーザーが使用中の移行ライセンス
resultOutputData.push(
'"' + company_name + '",',
'"' + targetMonthYYYYMM + '",',
'"' + LICENSE_COUNT_ANALYSIS_CATEGORY_1.VALID_LICENSES + '",',
'"' + LICENSE_COUNT_ANALYSIS_CATEGORY_2.IN_USE_LICENSES + '",',
'"' + LICENSE_COUNT_ANALYSIS_LICENSE_TYPE.NONE + '",',
'"' + "" + '",',
'"' + usedNoneLicenseCount.toString() + '"\r\n'
);
// Authorが使用中のトライアルライセンス[] // Authorが使用中のトライアルライセンス[]
resultOutputData.push( resultOutputData.push(
'"' + company_name + '",', '"' + company_name + '",',

View File

@ -305,6 +305,35 @@ describe("analysisLicenses", () => {
last2Month last2Month
); );
// 有効な移行ライセンスの作成
await createLicense(
source,
26,
expiringSoonDate,
account5_1.id,
"NONE",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
null,
null,
null,
null,
last2Month
);
// 期限切れの移行ライセンスの作成
await createLicense(
source,
27,
lastMonth,
account5_1.id,
"NONE",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
null,
null,
null,
null,
last2Month
);
// 第五階層がその月におこなったライセンス切り替え情報を作成 // 第五階層がその月におこなったライセンス切り替え情報を作成
// 条件: // 条件:
// ・第五アカウント // ・第五アカウント
@ -379,23 +408,25 @@ describe("analysisLicenses", () => {
throw new Error("ユーザー取得できていないので失敗"); throw new Error("ユーザー取得できていないので失敗");
} }
expect(result.avairableLicenses).toHaveLength(6); expect(result.avairableLicenses).toHaveLength(7);
expect(result.avairableLicenses[0].id).toBe(1); expect(result.avairableLicenses[0].id).toBe(1);
expect(result.avairableLicenses[1].id).toBe(2); expect(result.avairableLicenses[1].id).toBe(2);
expect(result.avairableLicenses[2].id).toBe(3); expect(result.avairableLicenses[2].id).toBe(3);
expect(result.avairableLicenses[3].id).toBe(11); expect(result.avairableLicenses[3].id).toBe(11);
expect(result.avairableLicenses[4].id).toBe(12); expect(result.avairableLicenses[4].id).toBe(12);
expect(result.avairableLicenses[5].id).toBe(13); expect(result.avairableLicenses[5].id).toBe(13);
expect(result.avairableLicenses[6].id).toBe(26);
expect(result.licensesIssuedInTargetMonth).toHaveLength(3); expect(result.licensesIssuedInTargetMonth).toHaveLength(3);
expect(result.licensesIssuedInTargetMonth[0].id).toBe(11); expect(result.licensesIssuedInTargetMonth[0].id).toBe(11);
expect(result.licensesIssuedInTargetMonth[1].id).toBe(12); expect(result.licensesIssuedInTargetMonth[1].id).toBe(12);
expect(result.licensesIssuedInTargetMonth[2].id).toBe(13); expect(result.licensesIssuedInTargetMonth[2].id).toBe(13);
expect(result.licensesExpiredInTargetMonth).toHaveLength(3); expect(result.licensesExpiredInTargetMonth).toHaveLength(4);
expect(result.licensesExpiredInTargetMonth[0].id).toBe(21); expect(result.licensesExpiredInTargetMonth[0].id).toBe(21);
expect(result.licensesExpiredInTargetMonth[1].id).toBe(22); expect(result.licensesExpiredInTargetMonth[1].id).toBe(22);
expect(result.licensesExpiredInTargetMonth[2].id).toBe(23); expect(result.licensesExpiredInTargetMonth[2].id).toBe(23);
expect(result.licensesExpiredInTargetMonth[3].id).toBe(27);
expect(result.switchedlicensesInTargetMonth).toHaveLength(2); expect(result.switchedlicensesInTargetMonth).toHaveLength(2);
expect(result.switchedlicensesInTargetMonth[0].id).toBe(1); expect(result.switchedlicensesInTargetMonth[0].id).toBe(1);
@ -647,6 +678,35 @@ describe("analysisLicenses", () => {
last2Month last2Month
); );
// 有効な移行ライセンスの作成
await createLicenseArchive(
source,
26,
null,
account5_1.id,
"NONE",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
last2Month
);
// 期限切れの移行ライセンスの作成
await createLicenseArchive(
source,
27,
lastMonth,
account5_1.id,
"NONE",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
last2Month
);
// 第五階層がその月におこなったライセンス切り替え情報を作成 // 第五階層がその月におこなったライセンス切り替え情報を作成
// 条件: // 条件:
// ・第五アカウント // ・第五アカウント
@ -722,23 +782,25 @@ describe("analysisLicenses", () => {
); );
} }
expect(result.deletedAvairableLicenses).toHaveLength(6); expect(result.deletedAvairableLicenses).toHaveLength(7);
expect(result.deletedAvairableLicenses[0].id).toBe(1); expect(result.deletedAvairableLicenses[0].id).toBe(1);
expect(result.deletedAvairableLicenses[1].id).toBe(2); expect(result.deletedAvairableLicenses[1].id).toBe(2);
expect(result.deletedAvairableLicenses[2].id).toBe(3); expect(result.deletedAvairableLicenses[2].id).toBe(3);
expect(result.deletedAvairableLicenses[3].id).toBe(11); expect(result.deletedAvairableLicenses[3].id).toBe(11);
expect(result.deletedAvairableLicenses[4].id).toBe(12); expect(result.deletedAvairableLicenses[4].id).toBe(12);
expect(result.deletedAvairableLicenses[5].id).toBe(13); expect(result.deletedAvairableLicenses[5].id).toBe(13);
expect(result.deletedAvairableLicenses[6].id).toBe(26);
expect(result.deletedLicensesIssuedInTargetMonth).toHaveLength(3); expect(result.deletedLicensesIssuedInTargetMonth).toHaveLength(3);
expect(result.deletedLicensesIssuedInTargetMonth[0].id).toBe(11); expect(result.deletedLicensesIssuedInTargetMonth[0].id).toBe(11);
expect(result.deletedLicensesIssuedInTargetMonth[1].id).toBe(12); expect(result.deletedLicensesIssuedInTargetMonth[1].id).toBe(12);
expect(result.deletedLicensesIssuedInTargetMonth[2].id).toBe(13); expect(result.deletedLicensesIssuedInTargetMonth[2].id).toBe(13);
expect(result.deletedLicensesExpiredInTargetMonth).toHaveLength(3); expect(result.deletedLicensesExpiredInTargetMonth).toHaveLength(4);
expect(result.deletedLicensesExpiredInTargetMonth[0].id).toBe(21); expect(result.deletedLicensesExpiredInTargetMonth[0].id).toBe(21);
expect(result.deletedLicensesExpiredInTargetMonth[1].id).toBe(22); expect(result.deletedLicensesExpiredInTargetMonth[1].id).toBe(22);
expect(result.deletedLicensesExpiredInTargetMonth[2].id).toBe(23); expect(result.deletedLicensesExpiredInTargetMonth[2].id).toBe(23);
expect(result.deletedLicensesExpiredInTargetMonth[3].id).toBe(27);
expect(result.deletedSwitchedlicensesInTargetMonth).toHaveLength(2); expect(result.deletedSwitchedlicensesInTargetMonth).toHaveLength(2);
expect(result.deletedSwitchedlicensesInTargetMonth[0].id).toBe(1); expect(result.deletedSwitchedlicensesInTargetMonth[0].id).toBe(1);
@ -1682,6 +1744,62 @@ describe("analysisLicenses", () => {
SWITCH_FROM_TYPE.CARD SWITCH_FROM_TYPE.CARD
); );
// 移行ライセンス用のデータを作成
// 移行ライセンスを持つ生きているアカウントのユーザーの情報を作成する
const activeUserForTransitionData1 = await makeTestUser(source, {
account_id: account5_1.id,
role: "author",
});
const activeUserForTransitionData2 = await makeTestUser(source, {
account_id: account5_1.id,
role: "author",
});
// ユーザーに紐づく有効な移行ライセンスを作成
await createLicense(
source,
56,
expiringSoonDate,
account5_1.id,
"NONE",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
activeUserForTransitionData1.id,
null,
null,
null,
last2Month
);
// ユーザーに紐づく期限切れの移行ライセンスを作成
// これは集計しないのでCSVには出力されない
await createLicense(
source,
57,
lastMonth,
account5_1.id,
"NONE",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
activeUserForTransitionData2.id,
null,
null,
null,
last2Month
);
// 誰にも紐づかない移行ライセンスを作成
// これは集計しないのでCSVには出力されない
await createLicense(
source,
58,
expiringSoonDate,
account5_1.id,
"NONE",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
last2Month
);
const result = await getBaseData(context, lastMonthYYYYMM, source); const result = await getBaseData(context, lastMonthYYYYMM, source);
// 削除されたアカウントとユーザーの情報を作成する // 削除されたアカウントとユーザーの情報を作成する
@ -2585,6 +2703,62 @@ describe("analysisLicenses", () => {
lastMonth, lastMonth,
SWITCH_FROM_TYPE.CARD SWITCH_FROM_TYPE.CARD
); );
// 移行ライセンス用のデータを作成
// 移行ライセンスを持つ削除アカウントのユーザーの情報を作成する
const DeletedUserForTransitionData1 = await makeTestUserArchive(source, {
account_id: account5_1_D.id,
role: "author",
});
const DeletedUserForTransitionData2 = await makeTestUserArchive(source, {
account_id: account5_1_D.id,
role: "author",
});
// 移行ライセンスを生きているアカウントに作成
await createLicenseArchive(
source,
56,
expiringSoonDate,
account5_1_D.id,
"NONE",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
DeletedUserForTransitionData1.id,
null,
null,
null,
last2Month
);
// ユーザーに紐づく期限切れの移行ライセンスを作成
// これは集計しないのでCSVには出力されない
await createLicenseArchive(
source,
57,
lastMonth,
account5_1_D.id,
"NONE",
LICENSE_ALLOCATED_STATUS.ALLOCATED,
DeletedUserForTransitionData2.id,
null,
null,
null,
last2Month
);
// 誰にも紐づかない移行ライセンスを作成
// これは集計しないのでCSVには出力されない
await createLicenseArchive(
source,
58,
expiringSoonDate,
account5_1_D.id,
"NONE",
LICENSE_ALLOCATED_STATUS.UNALLOCATED,
null,
null,
null,
null,
last2Month
);
const result_D = await getBaseDataFromDeletedAccounts( const result_D = await getBaseDataFromDeletedAccounts(
context, context,
lastMonthYYYYMM, lastMonthYYYYMM,
@ -2609,6 +2783,8 @@ describe("analysisLicenses", () => {
`"test inc.","${lastMonthYYYYMM}","有効ライセンス数","所有ライセンス数","Standard","","9"` + `"test inc.","${lastMonthYYYYMM}","有効ライセンス数","所有ライセンス数","Standard","","9"` +
"\r\n" + "\r\n" +
`"test inc.","${lastMonthYYYYMM}","有効ライセンス数","所有ライセンス数","Card","","7"` + `"test inc.","${lastMonthYYYYMM}","有効ライセンス数","所有ライセンス数","Card","","7"` +
`\r\n` +
`"test inc.","${lastMonthYYYYMM}","有効ライセンス数","使用中ライセンス数","移行ライセンス","","1"` +
"\r\n" + "\r\n" +
`"test inc.","${lastMonthYYYYMM}","有効ライセンス数","使用中ライセンス数","Trial","Author","1"` + `"test inc.","${lastMonthYYYYMM}","有効ライセンス数","使用中ライセンス数","Trial","Author","1"` +
"\r\n" + "\r\n" +
@ -2676,6 +2852,8 @@ describe("analysisLicenses", () => {
"\r\n" + "\r\n" +
`"1","${lastMonthYYYYMM}","有効ライセンス数","所有ライセンス数","Card","","7"` + `"1","${lastMonthYYYYMM}","有効ライセンス数","所有ライセンス数","Card","","7"` +
"\r\n" + "\r\n" +
`"1","${lastMonthYYYYMM}","有効ライセンス数","使用中ライセンス数","移行ライセンス","","1"` +
"\r\n" +
`"1","${lastMonthYYYYMM}","有効ライセンス数","使用中ライセンス数","Trial","Author","1"` + `"1","${lastMonthYYYYMM}","有効ライセンス数","使用中ライセンス数","Trial","Author","1"` +
"\r\n" + "\r\n" +
`"1","${lastMonthYYYYMM}","有効ライセンス数","使用中ライセンス数","Trial","Typist","2"` + `"1","${lastMonthYYYYMM}","有効ライセンス数","使用中ライセンス数","Trial","Typist","2"` +