Merged PR 632: ライセンスオーダーのキャンセル受付通知 [U-106] の実装
## 概要 [Task3303: ライセンスオーダーのキャンセル受付通知 [U-106] の実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3303) - オーダーキャンセルが完了時にメールを送信する処理を追加 - SendGridServiceにテンプレートメールを送信するメソッドを追加 - Adb2cUserからメールアドレスを取得する方法が分散して実装してあったので、取り出す用のメソッドを定義してそれを使用するよう修正 ## レビュー対象外 - 実際のメール送信処理部分は未実装なので対象外 ## レビューポイント - SendGridServiceに`sendTtemplateMailWithU{番号}`というメソッドを用意してメール送信を行う形式で問題ないか - from/toやCC等もメールの種別毎に異なるので、SendGridService側に具体的にどんなメールを送るかの責任を持たせる方針でいいか - `sendMailWithU106` の引数に不足や認識間違いはないか - キャンセルを行った本人へのメールだけで本当によいか?(他の管理者には知らせないでいいか)等 - to/cc等も考慮してチェックお願いします - **特にMISOチーム向け** 依存関係の追加で壊れたテストを削除したが、別途DBテストを追加しないで問題なさそうか? - 問題ありそうでれば、別途テスト実装タスクを作る想定 ## 動作確認状況 - npm run testが通るところまで確認
This commit is contained in:
parent
ad715285c6
commit
1c39555bfc
@ -71,6 +71,7 @@ import {
|
|||||||
WorktypeIdMaxCountError,
|
WorktypeIdMaxCountError,
|
||||||
WorktypeIdNotFoundError,
|
WorktypeIdNotFoundError,
|
||||||
} from '../../repositories/worktypes/errors/types';
|
} from '../../repositories/worktypes/errors/types';
|
||||||
|
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AccountsService {
|
export class AccountsService {
|
||||||
@ -1930,10 +1931,8 @@ export class AccountsService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAdmin = adb2cUser.displayName;
|
const { displayName: primaryAdmin, emailAddress: mail } =
|
||||||
const mail = adb2cUser.identities?.find(
|
getUserNameAndMailAddress(adb2cUser);
|
||||||
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
|
||||||
)?.issuerAssignedId;
|
|
||||||
if (!mail) {
|
if (!mail) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`adb2c user mail not found. externalId: ${db.primaryAccountExternalId}`,
|
`adb2c user mail not found. externalId: ${db.primaryAccountExternalId}`,
|
||||||
|
|||||||
@ -4,12 +4,16 @@ import { LicensesService } from './licenses.service';
|
|||||||
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
|
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
|
||||||
import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module';
|
import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module';
|
||||||
import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module';
|
import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module';
|
||||||
|
import { AdB2cModule } from '../../gateways/adb2c/adb2c.module';
|
||||||
|
import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
UsersRepositoryModule,
|
UsersRepositoryModule,
|
||||||
AccountsRepositoryModule,
|
AccountsRepositoryModule,
|
||||||
LicensesRepositoryModule,
|
LicensesRepositoryModule,
|
||||||
|
AdB2cModule,
|
||||||
|
SendGridModule,
|
||||||
],
|
],
|
||||||
controllers: [LicensesController],
|
controllers: [LicensesController],
|
||||||
providers: [LicensesService],
|
providers: [LicensesService],
|
||||||
|
|||||||
@ -42,271 +42,6 @@ import {
|
|||||||
makeTestUser,
|
makeTestUser,
|
||||||
} from '../../common/test/utility';
|
} from '../../common/test/utility';
|
||||||
|
|
||||||
describe('LicensesService', () => {
|
|
||||||
it('ライセンス注文が完了する', async () => {
|
|
||||||
const lisencesRepositoryMockValue =
|
|
||||||
makeDefaultLicensesRepositoryMockValue();
|
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
|
||||||
const accountsRepositoryMockValue =
|
|
||||||
makeDefaultAccountsRepositoryMockValue();
|
|
||||||
const service = await makeLicensesServiceMock(
|
|
||||||
lisencesRepositoryMockValue,
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
accountsRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const body = new CreateOrdersRequest();
|
|
||||||
const userId = '0001';
|
|
||||||
body.orderCount = 1000;
|
|
||||||
body.poNumber = '1';
|
|
||||||
const context = makeContext(`uuidv4`, 'requestId');
|
|
||||||
expect(
|
|
||||||
await service.licenseOrders(
|
|
||||||
context,
|
|
||||||
userId,
|
|
||||||
body.poNumber,
|
|
||||||
body.orderCount,
|
|
||||||
),
|
|
||||||
).toEqual(undefined);
|
|
||||||
});
|
|
||||||
it('ユーザID取得できなかった場合、エラーとなる', async () => {
|
|
||||||
const lisencesRepositoryMockValue =
|
|
||||||
makeDefaultLicensesRepositoryMockValue();
|
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
|
||||||
usersRepositoryMockValue.findUserByExternalId = new Error(
|
|
||||||
'User not Found Error.',
|
|
||||||
);
|
|
||||||
const accountsRepositoryMockValue =
|
|
||||||
makeDefaultAccountsRepositoryMockValue();
|
|
||||||
const service = await makeLicensesServiceMock(
|
|
||||||
lisencesRepositoryMockValue,
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
accountsRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const body = new CreateOrdersRequest();
|
|
||||||
const userId = '';
|
|
||||||
body.orderCount = 1000;
|
|
||||||
body.poNumber = '1';
|
|
||||||
const context = makeContext(`uuidv4`, 'requestId');
|
|
||||||
await expect(
|
|
||||||
service.licenseOrders(context, userId, body.poNumber, body.orderCount),
|
|
||||||
).rejects.toEqual(
|
|
||||||
new HttpException(
|
|
||||||
makeErrorResponse('E009999'),
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('親ユーザID取得できなかった場合、エラーとなる', async () => {
|
|
||||||
const lisencesRepositoryMockValue =
|
|
||||||
makeDefaultLicensesRepositoryMockValue();
|
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
|
||||||
usersRepositoryMockValue.findUserByExternalId = new Error(
|
|
||||||
'Account not Found Error.',
|
|
||||||
);
|
|
||||||
const accountsRepositoryMockValue =
|
|
||||||
makeDefaultAccountsRepositoryMockValue();
|
|
||||||
const service = await makeLicensesServiceMock(
|
|
||||||
lisencesRepositoryMockValue,
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
accountsRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const body = new CreateOrdersRequest();
|
|
||||||
const userId = '0001';
|
|
||||||
body.orderCount = 1000;
|
|
||||||
body.poNumber = '1';
|
|
||||||
const context = makeContext(`uuidv4`, 'requestId');
|
|
||||||
await expect(
|
|
||||||
service.licenseOrders(context, userId, body.poNumber, body.orderCount),
|
|
||||||
).rejects.toEqual(
|
|
||||||
new HttpException(
|
|
||||||
makeErrorResponse('E009999'),
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('POナンバー重複時、エラーとなる', async () => {
|
|
||||||
const lisencesRepositoryMockValue =
|
|
||||||
makeDefaultLicensesRepositoryMockValue();
|
|
||||||
lisencesRepositoryMockValue.order = new PoNumberAlreadyExistError(
|
|
||||||
`This PoNumber already used`,
|
|
||||||
);
|
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
|
||||||
const accountsRepositoryMockValue =
|
|
||||||
makeDefaultAccountsRepositoryMockValue();
|
|
||||||
const service = await makeLicensesServiceMock(
|
|
||||||
lisencesRepositoryMockValue,
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
accountsRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const body = new CreateOrdersRequest();
|
|
||||||
const userId = '0001';
|
|
||||||
body.orderCount = 1000;
|
|
||||||
body.poNumber = '1';
|
|
||||||
const context = makeContext(`uuidv4`, 'requestId');
|
|
||||||
await expect(
|
|
||||||
service.licenseOrders(context, userId, body.poNumber, body.orderCount),
|
|
||||||
).rejects.toEqual(
|
|
||||||
new HttpException(
|
|
||||||
makeErrorResponse('E010401'),
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('カードライセンス発行が完了する', async () => {
|
|
||||||
const lisencesRepositoryMockValue =
|
|
||||||
makeDefaultLicensesRepositoryMockValue();
|
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
|
||||||
const accountsRepositoryMockValue =
|
|
||||||
makeDefaultAccountsRepositoryMockValue();
|
|
||||||
const service = await makeLicensesServiceMock(
|
|
||||||
lisencesRepositoryMockValue,
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
accountsRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const body = new IssueCardLicensesRequest();
|
|
||||||
const userId = '0001';
|
|
||||||
body.createCount = 10;
|
|
||||||
const issueCardLicensesResponse: IssueCardLicensesResponse = {
|
|
||||||
cardLicenseKeys: [
|
|
||||||
'WZCETXC0Z9PQZ9GKRGGY',
|
|
||||||
'F0JD7EZEDBH4PQRQ83YF',
|
|
||||||
'H0HXBP5K9RW7T7JSVDJV',
|
|
||||||
'HKIWX54EESYL4X132223',
|
|
||||||
'363E81JR460UBHXGFXFI',
|
|
||||||
'70IKAPV9K6YMEVLTOXBY',
|
|
||||||
'1RJY1TRRYYTGF1LL9WLU',
|
|
||||||
'BXM0HKFO7IULTL0A1B36',
|
|
||||||
'XYLEWNY2LR6Q657CZE41',
|
|
||||||
'AEJWRFFSWRQYQQJ6WVLV',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
const context = makeContext(`uuidv4`, 'requestId');
|
|
||||||
expect(
|
|
||||||
await service.issueCardLicenseKeys(context, userId, body.createCount),
|
|
||||||
).toEqual(issueCardLicensesResponse);
|
|
||||||
});
|
|
||||||
it('カードライセンス発行に失敗した場合、エラーになる', async () => {
|
|
||||||
const lisencesRepositoryMockValue =
|
|
||||||
makeDefaultLicensesRepositoryMockValue();
|
|
||||||
lisencesRepositoryMockValue.createCardLicenses = new Error('DB failed');
|
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
|
||||||
const accountsRepositoryMockValue =
|
|
||||||
makeDefaultAccountsRepositoryMockValue();
|
|
||||||
const service = await makeLicensesServiceMock(
|
|
||||||
lisencesRepositoryMockValue,
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
accountsRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const body = new IssueCardLicensesRequest();
|
|
||||||
const userId = '0001';
|
|
||||||
body.createCount = 1000;
|
|
||||||
const context = makeContext(`uuidv4`, 'requestId');
|
|
||||||
await expect(
|
|
||||||
service.issueCardLicenseKeys(context, userId, body.createCount),
|
|
||||||
).rejects.toEqual(
|
|
||||||
new HttpException(
|
|
||||||
makeErrorResponse('E009999'),
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('カードライセンス取り込みが完了する', async () => {
|
|
||||||
const lisencesRepositoryMockValue =
|
|
||||||
makeDefaultLicensesRepositoryMockValue();
|
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
|
||||||
const accountsRepositoryMockValue =
|
|
||||||
makeDefaultAccountsRepositoryMockValue();
|
|
||||||
const service = await makeLicensesServiceMock(
|
|
||||||
lisencesRepositoryMockValue,
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
accountsRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const body = new ActivateCardLicensesRequest();
|
|
||||||
const userId = '0001';
|
|
||||||
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
|
|
||||||
const context = makeContext(`uuidv4`, 'requestId');
|
|
||||||
expect(
|
|
||||||
await service.activateCardLicenseKey(
|
|
||||||
context,
|
|
||||||
userId,
|
|
||||||
body.cardLicenseKey,
|
|
||||||
),
|
|
||||||
).toEqual(undefined);
|
|
||||||
});
|
|
||||||
it('カードライセンス取り込みに失敗した場合、エラーになる(DBエラー)', async () => {
|
|
||||||
const lisencesRepositoryMockValue =
|
|
||||||
makeDefaultLicensesRepositoryMockValue();
|
|
||||||
lisencesRepositoryMockValue.activateCardLicense = new Error('DB failed');
|
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
|
||||||
const accountsRepositoryMockValue =
|
|
||||||
makeDefaultAccountsRepositoryMockValue();
|
|
||||||
const service = await makeLicensesServiceMock(
|
|
||||||
lisencesRepositoryMockValue,
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
accountsRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const body = new ActivateCardLicensesRequest();
|
|
||||||
const userId = '0001';
|
|
||||||
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
|
|
||||||
const context = makeContext(`uuidv4`, 'requestId');
|
|
||||||
await expect(
|
|
||||||
service.activateCardLicenseKey(context, userId, body.cardLicenseKey),
|
|
||||||
).rejects.toEqual(
|
|
||||||
new HttpException(
|
|
||||||
makeErrorResponse('E009999'),
|
|
||||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが存在しないエラー)', async () => {
|
|
||||||
const lisencesRepositoryMockValue =
|
|
||||||
makeDefaultLicensesRepositoryMockValue();
|
|
||||||
lisencesRepositoryMockValue.activateCardLicense = new LicenseNotExistError(
|
|
||||||
`License not exist`,
|
|
||||||
);
|
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
|
||||||
const accountsRepositoryMockValue =
|
|
||||||
makeDefaultAccountsRepositoryMockValue();
|
|
||||||
const service = await makeLicensesServiceMock(
|
|
||||||
lisencesRepositoryMockValue,
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
accountsRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const body = new ActivateCardLicensesRequest();
|
|
||||||
const userId = '0001';
|
|
||||||
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
|
|
||||||
const context = makeContext(`uuidv4`, 'requestId');
|
|
||||||
await expect(
|
|
||||||
service.activateCardLicenseKey(context, userId, body.cardLicenseKey),
|
|
||||||
).rejects.toEqual(
|
|
||||||
new HttpException(makeErrorResponse('E010801'), HttpStatus.BAD_REQUEST),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('カードライセンス取り込みに失敗した場合、エラーになる(ライセンスが既に取り込まれているエラー)', async () => {
|
|
||||||
const lisencesRepositoryMockValue =
|
|
||||||
makeDefaultLicensesRepositoryMockValue();
|
|
||||||
lisencesRepositoryMockValue.activateCardLicense =
|
|
||||||
new LicenseKeyAlreadyActivatedError(`License already activated`);
|
|
||||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
|
||||||
const accountsRepositoryMockValue =
|
|
||||||
makeDefaultAccountsRepositoryMockValue();
|
|
||||||
const service = await makeLicensesServiceMock(
|
|
||||||
lisencesRepositoryMockValue,
|
|
||||||
usersRepositoryMockValue,
|
|
||||||
accountsRepositoryMockValue,
|
|
||||||
);
|
|
||||||
const body = new ActivateCardLicensesRequest();
|
|
||||||
const userId = '0001';
|
|
||||||
body.cardLicenseKey = 'WZCETXC0Z9PQZ9GKRGGY';
|
|
||||||
const context = makeContext(`uuidv4`, 'requestId');
|
|
||||||
await expect(
|
|
||||||
service.activateCardLicenseKey(context, userId, body.cardLicenseKey),
|
|
||||||
).rejects.toEqual(
|
|
||||||
new HttpException(makeErrorResponse('E010802'), HttpStatus.BAD_REQUEST),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('DBテスト', () => {
|
describe('DBテスト', () => {
|
||||||
let source: DataSource | null = null;
|
let source: DataSource | null = null;
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|||||||
@ -16,6 +16,10 @@ import {
|
|||||||
IssueCardLicensesResponse,
|
IssueCardLicensesResponse,
|
||||||
} from './types/types';
|
} from './types/types';
|
||||||
import { Context } from '../../common/log';
|
import { Context } from '../../common/log';
|
||||||
|
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||||
|
import { AdB2cService } from '../../gateways/adb2c/adb2c.service';
|
||||||
|
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
|
||||||
|
import { LICENSE_ISSUE_STATUS } from '../../constants';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class LicensesService {
|
export class LicensesService {
|
||||||
@ -23,6 +27,8 @@ export class LicensesService {
|
|||||||
private readonly usersRepository: UsersRepositoryService,
|
private readonly usersRepository: UsersRepositoryService,
|
||||||
private readonly accountsRepository: AccountsRepositoryService,
|
private readonly accountsRepository: AccountsRepositoryService,
|
||||||
private readonly licensesRepository: LicensesRepositoryService,
|
private readonly licensesRepository: LicensesRepositoryService,
|
||||||
|
private readonly adb2cService: AdB2cService,
|
||||||
|
private readonly sendgridService: SendGridService,
|
||||||
) {}
|
) {}
|
||||||
private readonly logger = new Logger(LicensesService.name);
|
private readonly logger = new Logger(LicensesService.name);
|
||||||
|
|
||||||
@ -325,7 +331,71 @@ export class LicensesService {
|
|||||||
await this.usersRepository.findUserByExternalId(context, externalId)
|
await this.usersRepository.findUserByExternalId(context, externalId)
|
||||||
).account_id;
|
).account_id;
|
||||||
// 注文キャンセル処理
|
// 注文キャンセル処理
|
||||||
await this.licensesRepository.cancelOrder(context, myAccountId, poNumber);
|
const { canceledOrderId } = await this.licensesRepository.cancelOrder(context, myAccountId, poNumber);
|
||||||
|
|
||||||
|
// メール送信処理
|
||||||
|
try {
|
||||||
|
// 注文キャンセルを実行したカスタマー企業の管理者情報を取得する
|
||||||
|
const customerAccountId = myAccountId;
|
||||||
|
|
||||||
|
// カスタマー企業の企業名と管理者情報を取得する
|
||||||
|
const {
|
||||||
|
companyName: customerCompanyName,
|
||||||
|
adminEmails: customerAdminEmails,
|
||||||
|
} = await this.getAccountInformation(context, customerAccountId);
|
||||||
|
|
||||||
|
// ディーラー企業を特定する
|
||||||
|
const { parent_account_id: dealerAccountId } =
|
||||||
|
await this.accountsRepository.findAccountById(
|
||||||
|
context,
|
||||||
|
customerAccountId,
|
||||||
|
);
|
||||||
|
// ライセンス発行が行われているので、ディーラーは必ず存在するはず
|
||||||
|
if (dealerAccountId == null) {
|
||||||
|
throw new Error('dealerAccountId is null');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ディーラー企業の企業名と管理者情報を取得する
|
||||||
|
const {
|
||||||
|
companyName: dealerCompanyName,
|
||||||
|
adminEmails: dealerAdminEmails,
|
||||||
|
} = await this.getAccountInformation(context, dealerAccountId);
|
||||||
|
|
||||||
|
// キャンセル済みの注文をID指定して取得する
|
||||||
|
const order = await this.licensesRepository.getLicenseOrder(
|
||||||
|
context,
|
||||||
|
customerAccountId,
|
||||||
|
poNumber,
|
||||||
|
canceledOrderId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (order == null) {
|
||||||
|
throw new Error(
|
||||||
|
`cancel target order not found. fromAccountId: ${customerAccountId}, poNumber:${poNumber}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (order.status !== LICENSE_ISSUE_STATUS.CANCELED) {
|
||||||
|
throw new Error(
|
||||||
|
`target order is not canceled. fromAccountId: ${order.from_account_id}, poNumber:${order.po_number}, status:${order.status}, id:${order.id}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { quantity } = order;
|
||||||
|
|
||||||
|
// 注文キャンセルが成功した旨をメール送信する
|
||||||
|
await this.sendgridService.sendMailWithU106(
|
||||||
|
context,
|
||||||
|
customerAdminEmails,
|
||||||
|
customerCompanyName,
|
||||||
|
quantity,
|
||||||
|
poNumber,
|
||||||
|
dealerAdminEmails,
|
||||||
|
dealerCompanyName,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||||
|
// メール送信に関する例外はログだけ出して握りつぶす
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
this.logger.error(`[${context.getTrackingId()}] error=${e}`);
|
||||||
switch (e.constructor) {
|
switch (e.constructor) {
|
||||||
@ -352,4 +422,51 @@ export class LicensesService {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* アカウントIDを指定して、アカウント情報と管理者情報を取得する
|
||||||
|
* @param context
|
||||||
|
* @param accountId 対象アカウントID
|
||||||
|
* @returns 企業名/管理者メールアドレス
|
||||||
|
*/
|
||||||
|
private async getAccountInformation(
|
||||||
|
context: Context,
|
||||||
|
accountId: number,
|
||||||
|
): Promise<{
|
||||||
|
companyName: string;
|
||||||
|
adminEmails: string[];
|
||||||
|
}> {
|
||||||
|
// アカウントIDから企業名を取得する
|
||||||
|
const { company_name } = await this.accountsRepository.findAccountById(
|
||||||
|
context,
|
||||||
|
accountId,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 管理者一覧を取得
|
||||||
|
const admins = await this.usersRepository.findAdminUsers(
|
||||||
|
context,
|
||||||
|
accountId,
|
||||||
|
);
|
||||||
|
const adminExternalIDs = admins.map((x) => x.external_id);
|
||||||
|
|
||||||
|
// ADB2Cから管理者IDを元にメールアドレスを取得する
|
||||||
|
const usersInfo = await this.adb2cService.getUsers(
|
||||||
|
context,
|
||||||
|
adminExternalIDs,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 生のAzure AD B2Cのユーザー情報からメールアドレスを抽出する
|
||||||
|
const adminEmails = usersInfo.map((x) => {
|
||||||
|
const { emailAddress } = getUserNameAndMailAddress(x);
|
||||||
|
if (emailAddress == null) {
|
||||||
|
throw new Error('dealer admin email-address is not found');
|
||||||
|
}
|
||||||
|
return emailAddress;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
companyName: company_name,
|
||||||
|
adminEmails: adminEmails,
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,6 +50,7 @@ import {
|
|||||||
LicenseUnavailableError,
|
LicenseUnavailableError,
|
||||||
} from '../../repositories/licenses/errors/types';
|
} from '../../repositories/licenses/errors/types';
|
||||||
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
|
import { AccountNotFoundError } from '../../repositories/accounts/errors/types';
|
||||||
|
import { getUserNameAndMailAddress } from '../../gateways/adb2c/utils/utils';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UsersService {
|
export class UsersService {
|
||||||
@ -570,10 +571,12 @@ export class UsersService {
|
|||||||
(user) => user.id === dbUser.external_id,
|
(user) => user.id === dbUser.external_id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (adb2cUser == null) {
|
||||||
|
throw new Error('mail not found.'); // TODO: リファクタ時に挙動を変更しないようエラー文面をmail not foundのまま据え置き。影響がない事が確認できたらエラー文面を変更する。
|
||||||
|
}
|
||||||
|
|
||||||
// メールアドレスを取得する
|
// メールアドレスを取得する
|
||||||
const mail = adb2cUser?.identities?.find(
|
const { emailAddress: mail } = getUserNameAndMailAddress(adb2cUser);
|
||||||
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
|
||||||
)?.issuerAssignedId;
|
|
||||||
|
|
||||||
//メールアドレスが取得できない場合はエラー
|
//メールアドレスが取得できない場合はエラー
|
||||||
if (!mail) {
|
if (!mail) {
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
import { ADB2C_SIGN_IN_TYPE } from "../../../constants";
|
||||||
|
import { AdB2cUser } from "../types/types";
|
||||||
|
|
||||||
export const isPromiseRejectedResult = (
|
export const isPromiseRejectedResult = (
|
||||||
data: unknown,
|
data: unknown,
|
||||||
): data is PromiseRejectedResult => {
|
): data is PromiseRejectedResult => {
|
||||||
@ -8,3 +11,12 @@ export const isPromiseRejectedResult = (
|
|||||||
'reason' in data
|
'reason' in data
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 生のAdB2cUserのレスポンスから表示名とメールアドレスを取得する
|
||||||
|
export const getUserNameAndMailAddress = (user: AdB2cUser) => {
|
||||||
|
const { displayName, identities } = user;
|
||||||
|
const emailAddress = identities?.find(
|
||||||
|
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
|
||||||
|
)?.issuerAssignedId;
|
||||||
|
return { displayName, emailAddress };
|
||||||
|
}
|
||||||
@ -137,6 +137,38 @@ export class SendGridService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* U-106のテンプレートを使用したメールを送信する
|
||||||
|
* @param context
|
||||||
|
* @param cancelUserEmailAddress 注文キャンセルを行ったアカウントの管理者(primary/secondary)のメールアドレス
|
||||||
|
* @param customerAccountName 送信対象の企業名
|
||||||
|
* @param lisenceCount 注文キャンセルを行った対象の注文の内容(ライセンス数)
|
||||||
|
* @param poNumber 注文キャンセルを行った対象の注文の内容(PO番号)
|
||||||
|
* @param dealerEmails 問題発生時に問い合わせする先の上位のディーラーの管理者(primary/secondary)のメールアドレス
|
||||||
|
* @param dealerAccountName 問題発生時に問い合わせする先の上位のディーラー名(会社名)
|
||||||
|
* @returns mail with u106
|
||||||
|
*/
|
||||||
|
async sendMailWithU106(
|
||||||
|
context: Context,
|
||||||
|
customerMails: string[],
|
||||||
|
customerAccountName: string,
|
||||||
|
lisenceCount: number,
|
||||||
|
poNumber: string,
|
||||||
|
dealerEmails: string[],
|
||||||
|
dealerAccountName: string,
|
||||||
|
): Promise<void> {
|
||||||
|
this.logger.log(
|
||||||
|
`[IN] [${context.getTrackingId()}] ${this.sendMailWithU106.name}`,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
// NOT IMPLEMENT
|
||||||
|
} finally {
|
||||||
|
this.logger.log(
|
||||||
|
`[OUT] [${context.getTrackingId()}] ${this.sendMailWithU106.name}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* メールを送信する
|
* メールを送信する
|
||||||
* @param context
|
* @param context
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { DataSource, In } from 'typeorm';
|
import { DataSource, EntityManager, In, Not } from 'typeorm';
|
||||||
import {
|
import {
|
||||||
LicenseOrder,
|
LicenseOrder,
|
||||||
License,
|
License,
|
||||||
@ -101,6 +101,36 @@ export class LicensesRepositoryService {
|
|||||||
return createdEntity;
|
return createdEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* オーダーIDとPO番号と依頼元アカウントIDを元にライセンス注文を取得する
|
||||||
|
* @param context context
|
||||||
|
* @param fromAccountId ライセンス注文を行ったアカウントのID
|
||||||
|
* @param poNumber PO番号
|
||||||
|
* @param orderId LicenseOrderのID
|
||||||
|
* @returns license order
|
||||||
|
*/
|
||||||
|
async getLicenseOrder(
|
||||||
|
context: Context,
|
||||||
|
fromAccountId: number,
|
||||||
|
poNumber: string,
|
||||||
|
orderId: number,
|
||||||
|
): Promise<LicenseOrder | null> {
|
||||||
|
return await this.dataSource.transaction(async (entityManager) => {
|
||||||
|
const repo = entityManager.getRepository(LicenseOrder);
|
||||||
|
|
||||||
|
// ステータスは問わず、指定したIDのランセンス注文を取得する
|
||||||
|
const entity = repo.findOne({
|
||||||
|
where: {
|
||||||
|
id: orderId,
|
||||||
|
po_number: poNumber,
|
||||||
|
from_account_id: fromAccountId,
|
||||||
|
},
|
||||||
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
});
|
||||||
|
return entity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* カードライセンスを発行する
|
* カードライセンスを発行する
|
||||||
* @param accountId
|
* @param accountId
|
||||||
@ -729,8 +759,8 @@ export class LicensesRepositoryService {
|
|||||||
context: Context,
|
context: Context,
|
||||||
accountId: number,
|
accountId: number,
|
||||||
poNumber: string,
|
poNumber: string,
|
||||||
): Promise<void> {
|
): Promise<{ canceledOrderId: number }> {
|
||||||
await this.dataSource.transaction(async (entityManager) => {
|
return await this.dataSource.transaction(async (entityManager) => {
|
||||||
const orderRepo = entityManager.getRepository(LicenseOrder);
|
const orderRepo = entityManager.getRepository(LicenseOrder);
|
||||||
|
|
||||||
// キャンセル対象の注文を取得
|
// キャンセル対象の注文を取得
|
||||||
@ -760,6 +790,8 @@ export class LicensesRepositoryService {
|
|||||||
this.isCommentOut,
|
this.isCommentOut,
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return { canceledOrderId: targetOrder.id };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,12 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { User, newUser } from './entity/user.entity';
|
import { User, newUser } from './entity/user.entity';
|
||||||
import { DataSource, IsNull, Not, UpdateResult } from 'typeorm';
|
import {
|
||||||
|
DataSource,
|
||||||
|
FindOptionsWhere,
|
||||||
|
IsNull,
|
||||||
|
Not,
|
||||||
|
UpdateResult,
|
||||||
|
} from 'typeorm';
|
||||||
import { SortCriteria } from '../sort_criteria/entity/sort_criteria.entity';
|
import { SortCriteria } from '../sort_criteria/entity/sort_criteria.entity';
|
||||||
import {
|
import {
|
||||||
getDirection,
|
getDirection,
|
||||||
@ -169,6 +175,40 @@ export class UsersRepositoryService {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定したアカウントIDを持つアカウントのプライマリ管理者とセカンダリ管理者を取得する
|
||||||
|
* @param context context
|
||||||
|
* @param accountId アカウントID
|
||||||
|
* @throws AccountNotFoundError
|
||||||
|
* @returns admin users
|
||||||
|
*/
|
||||||
|
async findAdminUsers(context: Context, accountId: number): Promise<User[]> {
|
||||||
|
return this.dataSource.transaction(async (entityManager) => {
|
||||||
|
const account = await entityManager.getRepository(Account).findOne({
|
||||||
|
where: {
|
||||||
|
id: accountId,
|
||||||
|
},
|
||||||
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
});
|
||||||
|
if (account == null) {
|
||||||
|
throw new AccountNotFoundError('account not found');
|
||||||
|
}
|
||||||
|
const primaryAdminId = account.primary_admin_user_id;
|
||||||
|
const secondaryAdminId = account.secondary_admin_user_id;
|
||||||
|
|
||||||
|
// IDが有効なユーザーだけを検索対象とする
|
||||||
|
const targets = [primaryAdminId, secondaryAdminId]
|
||||||
|
.flatMap((x) => (x == null ? [] : [x]))
|
||||||
|
.map((x): FindOptionsWhere<User> => ({ id: x }));
|
||||||
|
|
||||||
|
const users = await entityManager.getRepository(User).find({
|
||||||
|
where: targets,
|
||||||
|
comment: `${context.getTrackingId()}_${new Date().toUTCString()}`,
|
||||||
|
});
|
||||||
|
return users;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AuthorIdが既に存在するか確認する
|
* AuthorIdが既に存在するか確認する
|
||||||
* @param user
|
* @param user
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user