@@ -10,14 +10,11 @@ use App\ApiResource\TipjarApiResource;
|
||||
use App\ApiResource\User\UserApiResource;
|
||||
use App\Entity\Accounting\Accounting;
|
||||
use App\Entity\Matchfunding\MatchCall;
|
||||
use App\Entity\Money;
|
||||
use App\Entity\Project\Project;
|
||||
use App\Entity\Tipjar;
|
||||
use App\Entity\User\User;
|
||||
use App\Mapping\Transformer\AccountingBalanceMapTransformer;
|
||||
use App\State\Accounting\AccountingStateProcessor;
|
||||
use App\State\Accounting\AccountingStateProvider;
|
||||
use AutoMapper\Attribute\MapFrom;
|
||||
|
||||
/**
|
||||
* v4 features an advanced economy model under the hood.
|
||||
@@ -34,7 +31,7 @@ use AutoMapper\Attribute\MapFrom;
|
||||
processor: AccountingStateProcessor::class,
|
||||
)]
|
||||
#[API\GetCollection()]
|
||||
#[API\Get()]
|
||||
#[API\Get(output: BalancedAccountingApiResource::class)]
|
||||
#[API\Patch(security: 'is_granted("ACCOUNTING_EDIT", object)')]
|
||||
class AccountingApiResource
|
||||
{
|
||||
@@ -46,16 +43,21 @@ class AccountingApiResource
|
||||
*/
|
||||
public string $currency;
|
||||
|
||||
/**
|
||||
* The money currently held by the Accounting.
|
||||
*/
|
||||
#[MapFrom(Accounting::class, transformer: AccountingBalanceMapTransformer::class)]
|
||||
#[API\ApiProperty(writable: false, security: 'is_granted("ACCOUNTING_VIEW", object)')]
|
||||
public Money $balance;
|
||||
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public string $ownerClass;
|
||||
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?UserApiResource $user = null;
|
||||
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?ProjectApiResource $project = null;
|
||||
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?TipjarApiResource $tipjar = null;
|
||||
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?MatchCallApiResource $matchCall;
|
||||
|
||||
/**
|
||||
* The resource owning this Accounting.
|
||||
*
|
||||
@@ -76,16 +78,4 @@ class AccountingApiResource
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?UserApiResource $user = null;
|
||||
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?ProjectApiResource $project = null;
|
||||
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?TipjarApiResource $tipjar = null;
|
||||
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?MatchCallApiResource $matchCall;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\ApiResource\Accounting;
|
||||
|
||||
use ApiPlatform\Metadata as API;
|
||||
use App\ApiResource\Matchfunding\MatchCallApiResource;
|
||||
use App\ApiResource\Project\ProjectApiResource;
|
||||
use App\ApiResource\TipjarApiResource;
|
||||
use App\ApiResource\User\UserApiResource;
|
||||
use App\Entity\Accounting\Accounting;
|
||||
use App\Entity\Matchfunding\MatchCall;
|
||||
use App\Entity\Money;
|
||||
use App\Entity\Project\Project;
|
||||
use App\Entity\Tipjar;
|
||||
use App\Entity\User\User;
|
||||
use App\Mapping\Transformer\AccountingBalanceMapTransformer;
|
||||
use AutoMapper\Attribute\MapFrom;
|
||||
use Symfony\Component\Serializer\Attribute\Ignore;
|
||||
|
||||
class BalancedAccountingApiResource
|
||||
{
|
||||
public int $id;
|
||||
|
||||
/**
|
||||
* The preferred currency for monetary operations.\
|
||||
* 3-letter ISO 4217 currency code.
|
||||
*/
|
||||
public string $currency;
|
||||
|
||||
/**
|
||||
* The money currently held by the Accounting.
|
||||
*/
|
||||
#[MapFrom(Accounting::class, transformer: AccountingBalanceMapTransformer::class)]
|
||||
#[API\ApiProperty(writable: false, security: 'is_granted("ACCOUNTING_VIEW", object)')]
|
||||
public Money $balance;
|
||||
|
||||
#[Ignore]
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public string $ownerClass;
|
||||
|
||||
#[Ignore]
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?UserApiResource $user = null;
|
||||
|
||||
#[Ignore]
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?ProjectApiResource $project = null;
|
||||
|
||||
#[Ignore]
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?TipjarApiResource $tipjar = null;
|
||||
|
||||
#[Ignore]
|
||||
#[API\ApiProperty(readable: false, writable: false)]
|
||||
public ?MatchCallApiResource $matchCall;
|
||||
|
||||
/**
|
||||
* The resource owning this Accounting.
|
||||
*
|
||||
* @return UserApiResource|ProjectApiResource|TipjarApiResource
|
||||
*/
|
||||
public function getOwner(): ?object
|
||||
{
|
||||
switch ($this->ownerClass) {
|
||||
case User::class:
|
||||
return $this->user;
|
||||
case Project::class:
|
||||
return $this->project;
|
||||
case Tipjar::class:
|
||||
return $this->tipjar;
|
||||
case MatchCall::class:
|
||||
return $this->matchCall;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ use Symfony\Component\Validator\Constraints as Assert;
|
||||
#[MapProvider(EntityMapProvider::class)]
|
||||
#[ORM\Table(name: 'accounting_transaction')]
|
||||
#[ORM\Entity(repositoryClass: TransactionRepository::class)]
|
||||
#[ORM\Index(fields: ['origin'])]
|
||||
#[ORM\Index(fields: ['target'])]
|
||||
class Transaction
|
||||
{
|
||||
use TimestampedCreationEntity;
|
||||
|
||||
@@ -2,12 +2,47 @@
|
||||
|
||||
namespace App\Gateway\Gateway;
|
||||
|
||||
use App\Entity\Gateway\Charge;
|
||||
use App\Entity\Gateway\Checkout;
|
||||
use App\Gateway\ChargeType;
|
||||
use App\Gateway\GatewayInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
abstract class CecaGateway implements GatewayInterface
|
||||
class CecaGateway implements GatewayInterface
|
||||
{
|
||||
public const LEGACY_MESSAGE = 'This Gateway implementation is only meant to give support to legacy-records. DO NOT USE. DO NOT IMPLEMENT.';
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'tpv';
|
||||
}
|
||||
|
||||
public static function getSupportedChargeTypes(): array
|
||||
{
|
||||
return [
|
||||
ChargeType::Single,
|
||||
];
|
||||
}
|
||||
|
||||
public function process(Checkout $checkout): Checkout
|
||||
{
|
||||
throw new \Exception(self::LEGACY_MESSAGE);
|
||||
}
|
||||
|
||||
public function handleRedirect(Request $request): RedirectResponse
|
||||
{
|
||||
throw new \Exception(self::LEGACY_MESSAGE);
|
||||
}
|
||||
|
||||
public function handleWebhook(Request $request): Response
|
||||
{
|
||||
throw new \Exception(self::LEGACY_MESSAGE);
|
||||
}
|
||||
|
||||
public function processRefund(Charge $charge): void
|
||||
{
|
||||
throw new \Exception(self::LEGACY_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,19 +34,15 @@ class TransactionRepository extends ServiceEntityRepository
|
||||
?\DateTimeInterface $dateEnd = null,
|
||||
): array {
|
||||
$queryBuilder = $this->createQueryBuilder('t');
|
||||
$accountingExpr = $queryBuilder->expr();
|
||||
|
||||
if ($dateEnd === null) {
|
||||
$dateEnd = new \DateTime();
|
||||
}
|
||||
|
||||
$query = $queryBuilder
|
||||
->andWhere($accountingExpr->orX(
|
||||
$accountingExpr->eq('t.origin', $accounting->getId()),
|
||||
$accountingExpr->eq('t.target', $accounting->getId()),
|
||||
->andWhere($queryBuilder->expr()->orX(
|
||||
't.origin = :accountingId',
|
||||
't.target = :accountingId'
|
||||
))
|
||||
->setParameter('accountingId', $accounting->getId())
|
||||
->andWhere('t.dateCreated <= :dateEnd')
|
||||
->setParameter('dateEnd', $dateEnd);
|
||||
->setParameter('dateEnd', $dateEnd ?? new \DateTime());
|
||||
|
||||
if ($dateStart !== null) {
|
||||
$query->andWhere('t.dateCreated >= :dateStart')
|
||||
@@ -58,6 +54,45 @@ class TransactionRepository extends ServiceEntityRepository
|
||||
return $query->getQuery()->getResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DateTimeInterface|null $dateStart a date to act as lower bound of the selection
|
||||
* @param \DateTimeInterface|null $dateEnd A date to act as upper bound of the selection. Current date will be used if null.
|
||||
*
|
||||
* @return array Simplified array data of Transactions originated from or targeting the Accounting
|
||||
*/
|
||||
public function findForBalanceCalc(
|
||||
Accounting $accounting,
|
||||
?\DateTimeInterface $dateStart = null,
|
||||
?\DateTimeInterface $dateEnd = null,
|
||||
): array {
|
||||
$queryBuilder = $this->createQueryBuilder('t');
|
||||
|
||||
$query = $queryBuilder
|
||||
->select('
|
||||
t.id,
|
||||
t.money.amount,
|
||||
t.money.currency,
|
||||
IDENTITY(t.origin) AS origin_id,
|
||||
IDENTITY(t.target) AS target_id
|
||||
')
|
||||
->andWhere($queryBuilder->expr()->orX(
|
||||
't.origin = :accountingId',
|
||||
't.target = :accountingId'
|
||||
))
|
||||
->setParameter('accountingId', $accounting->getId())
|
||||
->andWhere('t.dateCreated <= :dateEnd')
|
||||
->setParameter('dateEnd', $dateEnd ?? new \DateTime());
|
||||
|
||||
if ($dateStart !== null) {
|
||||
$query->andWhere('t.dateCreated >= :dateStart')
|
||||
->setParameter('dateStart', $dateStart);
|
||||
}
|
||||
|
||||
$query->orderBy('t.id', 'ASC');
|
||||
|
||||
return $query->getQuery()->getArrayResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Transaction[] Transactions originated from the Accounting
|
||||
*/
|
||||
|
||||
@@ -27,16 +27,19 @@ class AccountingService
|
||||
return $this->wallet->getBalance($accounting);
|
||||
}
|
||||
|
||||
$balance = new Money(0, $accounting->getCurrency());
|
||||
$trxs = $this->transactionRepository->findByAccounting($accounting);
|
||||
$trxs = $this->transactionRepository->findForBalanceCalc($accounting);
|
||||
|
||||
foreach ($trxs as $transaction) {
|
||||
if ($transaction->getTarget() === $accounting) {
|
||||
$balance = $this->money->add($transaction->getMoney(), $balance);
|
||||
$accountingId = $accounting->getId();
|
||||
$balance = new Money(0, $accounting->getCurrency());
|
||||
foreach ($trxs as $trx) {
|
||||
$transaction = new Money($trx['money.amount'], $trx['money.currency']);
|
||||
|
||||
if ($trx['target_id'] === $accountingId) {
|
||||
$balance = $this->money->add($transaction, $balance);
|
||||
}
|
||||
|
||||
if ($transaction->getOrigin() === $accounting) {
|
||||
$balance = $this->money->substract($transaction->getMoney(), $balance);
|
||||
if ($trx['origin_id'] === $accountingId) {
|
||||
$balance = $this->money->substract($transaction, $balance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +98,7 @@ class AccountingService
|
||||
$points = [];
|
||||
$totalBalance = new Money(0, $accounting->getCurrency());
|
||||
|
||||
$accountingId = $accounting->getId();
|
||||
foreach ($period as $start) {
|
||||
$end = \DateTime::createFromInterface($start)->add($period->getDateInterval());
|
||||
$periodTrxs = $this->getTransactionsInPeriod($trxs, $start, $end);
|
||||
@@ -103,11 +107,11 @@ class AccountingService
|
||||
foreach ($periodTrxs as $trx) {
|
||||
$trxMoney = $trx->getMoney();
|
||||
|
||||
if ($trx->getTarget() === $accounting) {
|
||||
if ($trx->getTarget()->getId() === $accountingId) {
|
||||
$balance = $this->money->add($trxMoney, $balance);
|
||||
}
|
||||
|
||||
if ($trx->getOrigin() === $accounting) {
|
||||
if ($trx->getOrigin()->getId() === $accountingId) {
|
||||
$balance = $this->money->substract($trxMoney, $balance);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use ApiPlatform\Metadata\Operation;
|
||||
use ApiPlatform\State\Pagination\TraversablePaginator;
|
||||
use ApiPlatform\State\ProviderInterface;
|
||||
use App\ApiResource\Accounting\AccountingApiResource;
|
||||
use App\ApiResource\Accounting\BalancedAccountingApiResource;
|
||||
use App\Mapping\AutoMapper;
|
||||
use App\Service\AccountingService;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
@@ -48,6 +49,6 @@ class AccountingStateProvider implements ProviderInterface
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->autoMapper->map($accounting, AccountingApiResource::class);
|
||||
return $this->autoMapper->map($accounting, BalancedAccountingApiResource::class);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user