Skip to content

Instantly share code, notes, and snippets.

@Ensive
Last active February 1, 2022 14:13
Show Gist options
  • Select an option

  • Save Ensive/cdc46f7a941689fe9ae0eff6328e6f1d to your computer and use it in GitHub Desktop.

Select an option

Save Ensive/cdc46f7a941689fe9ae0eff6328e6f1d to your computer and use it in GitHub Desktop.
BuyTradingSchoolCourseCommand.php
<?php
declare(strict_types=1);
namespace Fndx\Application\UseCase\TradingSchoolCourse;
use Fndx\Application\UseCase\AbstractCommand;
use Fndx\Domain\Permission\AggregateRoot\Permission;
use Fndx\Infrastructure\Core\Exception\AccessNotAllowedException;
use Fndx\Domain\Permission\Contract\PermissionServiceInterface;
use Fndx\Infrastructure\Transaction\DTO\Response\TransactionResponseDTO;
use Fndx\Infrastructure\Transaction\Transformer\TransactionResponseDTOTransformer;
use Fndx\Domain\Identity\Exception\IdentityNotFoundException;
use Fndx\Domain\TradingSchoolCourse\Contract\TradingSchoolCourseServiceInterface;
use Fndx\Infrastructure\TradingSchoolCourse\DTO\Request\BuyTradingSchoolCourseRequestDTO;
use Fndx\Infrastructure\Transaction\DTO\Request\CreateTransactionRequestDTO;
use Fndx\Domain\Client\Contract\ClientServiceInterface;
use Fndx\Domain\PersonalAccount\Contract\PersonalAccountServiceInterface;
use Fndx\Domain\SystemAccount\Contract\SystemAccountServiceInterface;
use Fndx\Domain\Transaction\Contract\TransactionServiceInterface;
use Fndx\Domain\Transaction\AggregateRoot\Transaction;
use Fndx\Domain\PaymentMethod\AggregateRoot\PaymentMethod;
use Fndx\Domain\Permission\Exception\PermissionNotFoundException;
use Fndx\Domain\Client\Exception\ClientNotFoundException;
use Fndx\Domain\Money\Exception\AmountCannotBeZeroException;
use Fndx\Domain\Money\Exception\NegativeAmountException;
use Fndx\Domain\Money\Exception\NotEnoughBalanceException;
use Fndx\Domain\PaymentMethod\Exception\InvalidPaymentMethodException;
use Fndx\Domain\PersonalAccount\Exception\PersonalAccountNotFoundException;
use Fndx\Domain\Transaction\Exception\InvalidTransactionCreationTypeException;
use Fndx\Domain\Transaction\Exception\InvalidTransactionStatusException;
use Fndx\Domain\Transaction\Exception\InvalidTransactionTypeException;
use Fndx\Domain\TradingSchoolCourse\Exception\TradingSchoolCourseAlreadyPurchasedException;
use Fndx\Infrastructure\SystemAccount\Repository\SystemAccountPostgresqlRepository;
use Fndx\Domain\Transaction\ValueObject\TransactionCreationType;
use Fndx\Domain\Transaction\ValueObject\TransactionStatus;
use Fndx\Domain\Transaction\ValueObject\TransactionType;
use function Fndx\Infrastructure\Helpers\map;
class BuyTradingSchoolCourseCommand extends AbstractCommand
{
protected const TARGET_PERMISSION = Permission::TRADING_SCHOOL_COURSE_BUY;
protected TradingSchoolCourseServiceInterface $courseService;
protected ClientServiceInterface $clientService;
protected PersonalAccountServiceInterface $personalAccountService;
protected SystemAccountServiceInterface $systemAccountService;
protected TransactionServiceInterface $transactionService;
protected PermissionServiceInterface $permissionService;
/**
* @param TradingSchoolCourseServiceInterface $courseService
* @param ClientServiceInterface $clientService
* @param PersonalAccountServiceInterface $personalAccountService
* @param SystemAccountServiceInterface $systemAccountService
* @param TransactionServiceInterface $transactionService
* @param PermissionServiceInterface $permissionService
*/
public function __construct(
ClientServiceInterface $clientService,
TradingSchoolCourseServiceInterface $courseService,
PersonalAccountServiceInterface $personalAccountService,
SystemAccountServiceInterface $systemAccountService,
TransactionServiceInterface $transactionService,
PermissionServiceInterface $permissionService
) {
parent::__construct();
$this->clientService = $clientService;
$this->courseService = $courseService;
$this->personalAccountService = $personalAccountService;
$this->systemAccountService = $systemAccountService;
$this->transactionService = $transactionService;
$this->permissionService = $permissionService;
}
/**
* @param string $tradingSchoolCourseWpId
* @param BuyTradingSchoolCourseRequestDTO $payload
*
* @return TransactionResponseDTO
* @throws IdentityNotFoundException
* @throws AmountCannotBeZeroException
* @throws ClientNotFoundException
* @throws InvalidPaymentMethodException
* @throws InvalidTransactionCreationTypeException
* @throws InvalidTransactionStatusException
* @throws InvalidTransactionTypeException
* @throws NegativeAmountException
* @throws NotEnoughBalanceException
* @throws PersonalAccountNotFoundException
* @throws PermissionNotFoundException
* @throws TradingSchoolCourseAlreadyPurchasedException
* @throws AccessNotAllowedException
*/
// TODO: this doesn't work, fixes required
// TODO: what is better name for tradingSchoolCourseWpId ?
public function run(
string $tradingSchoolCourseWpId,
BuyTradingSchoolCourseRequestDTO $payload
): TransactionResponseDTO {
$this->checkPermission();
$performerIdentity = $this->getPerformerIdentity();
if ($performerIdentity->can($payload->permissionKey, $performerIdentity->id())) {
throw new TradingSchoolCourseAlreadyPurchasedException();
}
$client = $this->clientService->getByIdentityId($performerIdentity->id());
$course = $this->courseService->getByWpId($tradingSchoolCourseWpId);
$personalAccount = $this->personalAccountService->getByClientId($client->id());
$systemAccount = $this->systemAccountService->getSystemAccount();
// TODO: wrap the following code into database SQL transaction
// update personal account balance
$personalAccount->withdraw($course->price()->amount());
$this->personalAccountService->save($personalAccount);
// update system account balance
$systemAccount->topUp($course->price()->amount());
$this->systemAccountService->save($systemAccount);
// todo: do we add "description" ?
$transactionDto = map(CreateTransactionRequestDTO::class)->from(
[
'clientId' => $client->id(),
'type' => TransactionType::PURCHASE,
'status' => TransactionStatus::COMPLETED,
'amount' => $course->price()->amount(),
'creationType' => TransactionCreationType::AUTO,
'productId' => $course->productId(),
'fromPaymentMethodId' => PaymentMethod::PERSONAL_ACCOUNT,
'fromSourceId' => $client->id(),
'toPaymentMethodId' => PaymentMethod::SYSTEM_ACCOUNT,
'toSourceId' => SystemAccountPostgresqlRepository::SYSTEM_ACCOUNT_ID,
]
);
// TODO: test
$transaction = $this->transactionService->create($transactionDto, $performerIdentity->id());
$this->transactionService->save($transaction);
// set permission after everything is successful
$permission = $this->permissionService->getByKey($payload->permissionKey);
// TODO: shall we rework this to identityService->addPermission or $this->getPerformerIdentity()->addPermission()
$this->permissionService->setPermission($performerIdentity->id(), $permission->id());
return map(TransactionResponseDTO::class)->from(
$transaction,
fn(Transaction $transaction) => TransactionResponseDTOTransformer::transform($transaction)
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment