Skip to content

Commit

Permalink
Fiat balance (#61)
Browse files Browse the repository at this point in the history
* fiat balance update based on share pooling asset transactions

* fiat balance update based on non-fungible asset transactions

* fiat balance displayed in terminal
  • Loading branch information
osteel authored Aug 18, 2023
1 parent eca00cb commit a379b7f
Show file tree
Hide file tree
Showing 47 changed files with 551 additions and 37 deletions.
13 changes: 9 additions & 4 deletions app/Commands/Review.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Domain\Aggregates\TaxYear\Repositories\TaxYearSummaryRepository;
use Domain\Aggregates\TaxYear\ValueObjects\Exceptions\TaxYearIdException;
use Domain\Aggregates\TaxYear\ValueObjects\TaxYearId;
use Domain\Repositories\SummaryRepository;
use Illuminate\Database\QueryException;

final class Review extends Command
Expand All @@ -26,7 +27,7 @@ final class Review extends Command
protected $description = 'Display a tax year\'s summary';

/** Execute the console command. */
public function handle(TaxYearSummaryRepository $repository): int
public function handle(SummaryRepository $summaryRepository, TaxYearSummaryRepository $taxYearRepository): int
{
$taxYear = $this->argument('taxyear');

Expand All @@ -41,7 +42,7 @@ public function handle(TaxYearSummaryRepository $repository): int
try {
$availableTaxYears = array_filter(array_map(
fn (mixed $taxYearId) => $taxYearId instanceof TaxYearId ? $taxYearId->toString() : null,
array_column($repository->all(), 'tax_year_id'),
array_column($taxYearRepository->all(), 'tax_year_id'),
));
} catch (QueryException) {
$availableTaxYears = [];
Expand All @@ -58,16 +59,20 @@ public function handle(TaxYearSummaryRepository $repository): int
$taxYear = null;
}

if (is_null($taxYear)) {
$this->presenter->info(sprintf('Current fiat balance: %s', $summaryRepository->get()?->fiat_balance ?? ''));
}

// Order tax years from more recent to older
rsort($availableTaxYears);

$taxYear ??= count($availableTaxYears) === 1
? $availableTaxYears[0]
: $this->presenter->choice('Please select a tax year', $availableTaxYears, $availableTaxYears[0]);
: $this->presenter->choice('Please select a tax year for details', $availableTaxYears, $availableTaxYears[0]);

assert(is_string($taxYear));

return $this->summary($repository, $taxYear, $availableTaxYears);
return $this->summary($taxYearRepository, $taxYear, $availableTaxYears);
}

/** @param list<string> $availableTaxYears */
Expand Down
4 changes: 4 additions & 0 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Providers;

use App\Repositories\SummaryRepository;
use App\Services\ActionRunner\ActionRunner;
use App\Services\CommandRunner\CommandRunner;
use App\Services\CommandRunner\CommandRunnerContract;
Expand All @@ -13,6 +14,7 @@
use App\Services\TransactionProcessor\TransactionProcessorContract;
use App\Services\TransactionReader\Adapters\PhpSpreadsheetAdapter;
use App\Services\TransactionReader\TransactionReader;
use Domain\Repositories\SummaryRepository as SummaryRepositoryInterface;
use Domain\Services\ActionRunner\ActionRunner as ActionRunnerInterface;
use Domain\Services\TransactionDispatcher\TransactionDispatcher;
use Domain\Services\TransactionDispatcher\TransactionDispatcherContract;
Expand All @@ -33,6 +35,8 @@ public function register(): void
$this->app->singleton(TransactionProcessorContract::class, fn (Application $app) => resolve(TransactionProcessor::class));
$this->app->singleton(TransactionReader::class, fn (Application $app) => new PhpSpreadsheetAdapter());

$this->app->bind(SummaryRepositoryInterface::class, SummaryRepository::class);

if (! $this->isProduction()) {
$this->app->register(TinkerZeroServiceProvider::class);
}
Expand Down
16 changes: 16 additions & 0 deletions app/Repositories/SummaryRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Repositories;

use Domain\Projections\Summary;
use Domain\Repositories\SummaryRepository as SummaryRepositoryInterface;

final class SummaryRepository implements SummaryRepositoryInterface
{
public function get(): ?Summary
{
return Summary::first();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public function cast(mixed $value, ObjectMapper $hydrator): mixed
date: LocalDate::parse($value['date']),
quantity: new Quantity($value['quantity']),
costBasis: new FiatAmount($value['cost_basis']['quantity'], FiatCurrency::from($value['cost_basis']['currency'])),
forFiat: (bool) $value['for_fiat'],
sameDayQuantity: new Quantity($value['same_day_quantity']),
thirtyDayQuantity: new Quantity($value['thirty_day_quantity']),
);
Expand All @@ -41,6 +42,7 @@ public function serialize(mixed $value, ObjectMapper $hydrator): mixed
'date' => (string) $value->date,
'quantity' => (string) $value->quantity,
'cost_basis' => ['quantity' => (string) $value->costBasis->quantity, 'currency' => $value->costBasis->currency->value],
'for_fiat' => $value->forFiat,
'same_day_quantity' => (string) $value->sameDayQuantity(),
'thirty_day_quantity' => (string) $value->thirtyDayQuantity(),
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public function cast(mixed $value, ObjectMapper $hydrator): mixed
quantity: new Quantity($value['quantity']),
costBasis: new FiatAmount($value['cost_basis']['quantity'], FiatCurrency::from($value['cost_basis']['currency'])),
proceeds: new FiatAmount($value['proceeds']['quantity'], FiatCurrency::from($value['proceeds']['currency'])),
forFiat: (bool) $value['for_fiat'],
sameDayQuantityAllocation: new QuantityAllocation(
array_map(fn (string $quantity) => new Quantity($quantity), $value['same_day_quantity_allocation']),
),
Expand All @@ -50,6 +51,7 @@ public function serialize(mixed $value, ObjectMapper $hydrator): mixed
'quantity' => (string) $value->quantity,
'cost_basis' => ['quantity' => (string) $value->costBasis->quantity, 'currency' => $value->costBasis->currency->value],
'proceeds' => ['quantity' => (string) $value->proceeds->quantity, 'currency' => $value->proceeds->currency->value],
'for_fiat' => $value->forFiat,
'same_day_quantity_allocation' => $this->serializeQuantityAllocation($value->sameDayQuantityAllocation),
'thirty_day_quantity_allocation' => $this->serializeQuantityAllocation($value->thirtyDayQuantityAllocation),
'processed' => $value->processed,
Expand Down
2 changes: 1 addition & 1 deletion app/Services/Presenter/PresenterContract.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function summary(
string $nonAttributableAllowableCost,
string $totalCostBasis,
string $capitalGain,
string $income
string $income,
): void;

/** Initiate a progress bar. */
Expand Down
23 changes: 23 additions & 0 deletions database/migrations/2023_08_18_144245_create_summaries_table.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class () extends Migration {
/** Run the migrations. */
public function up(): void
{
Schema::create('summaries', function (Blueprint $table) {
$table->id();
$table->string('currency', 3);
$table->string('fiat_balance')->default('0');
});
}

/** Reverse the migrations. */
public function down(): void
{
Schema::dropIfExists('summaries');
}
};
25 changes: 25 additions & 0 deletions domain/src/Actions/UpdateSummary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Domain\Actions;

use Domain\Projections\Summary;
use Domain\ValueObjects\FiatAmount;

final class UpdateSummary
{
final public function __construct(public readonly FiatAmount $fiatBalanceUpdate)
{
}

public function __invoke(): void
{
$summary = Summary::firstOrNew([], [
'currency' => $this->fiatBalanceUpdate->currency,
'fiat_balance' => new FiatAmount('0', $this->fiatBalanceUpdate->currency),
]);

$summary->fill(['fiat_balance' => $summary->fiat_balance->plus($this->fiatBalanceUpdate)])->save();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public function __construct(
public Asset $asset,
public LocalDate $date,
public FiatAmount $costBasis,
public bool $forFiat,
) {
}

Expand All @@ -32,6 +33,7 @@ public function __invoke(NonFungibleAssetRepository $nonFungibleAssetRepository,
asset: $this->asset,
date: $this->date,
costBasisIncrease: $this->costBasis,
forFiat: $this->forFiat,
));

return;
Expand All @@ -48,6 +50,13 @@ public function getAsset(): Asset

public function __toString(): string
{
return sprintf('%s (asset: %s, date: %s, cost basis: %s)', self::class, $this->asset, $this->date, $this->costBasis);
return sprintf(
'%s (asset: %s, date: %s, cost basis: %s, for fiat: %s)',
self::class,
$this->asset,
$this->date,
$this->costBasis,
$this->forFiat ? 'yes' : 'no',
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public function __construct(
public Asset $asset,
public LocalDate $date,
public FiatAmount $proceeds,
public bool $forFiat,
) {
}

Expand All @@ -43,6 +44,13 @@ public function getDate(): LocalDate

public function __toString(): string
{
return sprintf('%s (asset: %s, date: %s, proceeds: %s)', self::class, $this->asset, $this->date, $this->proceeds);
return sprintf(
'%s (asset: %s, date: %s, proceeds: %s, for fiat: %s)',
self::class,
$this->asset,
$this->date,
$this->proceeds,
$this->forFiat ? 'yes' : 'no',
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public function __construct(
public Asset $asset,
public LocalDate $date,
public FiatAmount $costBasisIncrease,
public bool $forFiat,
) {
}

Expand All @@ -44,11 +45,12 @@ public function getDate(): LocalDate
public function __toString(): string
{
return sprintf(
'%s (asset: %s, date: %s, cost basis increase: %s)',
'%s (asset: %s, date: %s, cost basis increase: %s, for fiat: %s)',
self::class,
$this->asset,
$this->date,
$this->costBasisIncrease,
$this->forFiat ? 'yes' : 'no',
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
public function __construct(
public LocalDate $date,
public FiatAmount $costBasis,
public bool $forFiat,
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public function __construct(
public LocalDate $date,
public FiatAmount $costBasisIncrease,
public FiatAmount $newCostBasis,
public bool $forFiat,
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public function __construct(
public LocalDate $date,
public FiatAmount $costBasis,
public FiatAmount $proceeds,
public bool $forFiat,
) {
}
}
3 changes: 3 additions & 0 deletions domain/src/Aggregates/NonFungibleAsset/NonFungibleAsset.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public function acquire(AcquireNonFungibleAsset $action): void
$this->recordThat(new NonFungibleAssetAcquired(
date: $action->date,
costBasis: $action->costBasis,
forFiat: $action->forFiat,
));
}

Expand All @@ -76,6 +77,7 @@ public function increaseCostBasis(IncreaseNonFungibleAssetCostBasis $action): vo
date: $action->date,
costBasisIncrease: $action->costBasisIncrease,
newCostBasis: $this->costBasis?->plus($action->costBasisIncrease) ?? $action->costBasisIncrease,
forFiat: $action->forFiat,
));
}

Expand All @@ -101,6 +103,7 @@ public function disposeOf(DisposeOfNonFungibleAsset $action): void
date: $action->date,
costBasis: $this->costBasis,
proceeds: $action->proceeds,
forFiat: $action->forFiat,
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace Domain\Aggregates\NonFungibleAsset\Reactors;

use Domain\Actions\UpdateSummary;
use Domain\Aggregates\NonFungibleAsset\Events\NonFungibleAssetAcquired;
use Domain\Aggregates\NonFungibleAsset\Events\NonFungibleAssetCostBasisIncreased;
use Domain\Aggregates\NonFungibleAsset\Events\NonFungibleAssetDisposedOf;
use Domain\Aggregates\TaxYear\Actions\UpdateCapitalGain;
use Domain\Aggregates\TaxYear\ValueObjects\CapitalGain;
Expand All @@ -17,11 +20,33 @@ public function __construct(private readonly ActionRunner $runner)
{
}

public function handleNonFungibleAssetAcquired(NonFungibleAssetAcquired $event, Message $message): void
{
if ($event->forFiat) {
$this->runner->run(
new UpdateSummary(fiatBalanceUpdate: $event->costBasis->minus($event->costBasis->multipliedBy('2'))),
);
}
}

public function handleNonFungibleAssetCostBasisIncreased(NonFungibleAssetCostBasisIncreased $event, Message $message): void
{
if ($event->forFiat) {
$this->runner->run(
new UpdateSummary(fiatBalanceUpdate: $event->costBasisIncrease->minus($event->costBasisIncrease->multipliedBy('2'))),
);
}
}

public function handleNonFungibleAssetDisposedOf(NonFungibleAssetDisposedOf $event, Message $message): void
{
$this->runner->run(new UpdateCapitalGain(
date: $event->date,
capitalGainUpdate: new CapitalGain($event->costBasis, $event->proceeds),
));

if ($event->forFiat) {
$this->runner->run(new UpdateSummary(fiatBalanceUpdate: $event->proceeds));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function __construct(
public LocalDate $date,
public Quantity $quantity,
public FiatAmount $costBasis,
public bool $forFiat,
// Testing purposes only
public ?SharePoolingAssetTransactionId $transactionId = null,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public function __construct(
public LocalDate $date,
public Quantity $quantity,
public FiatAmount $proceeds,
public bool $forFiat,
// Only present whenever the disposal has been reverted and is now being replayed
public ?SharePoolingAssetTransactionId $transactionId = null,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public function __construct(
LocalDate $date,
Quantity $quantity,
FiatAmount $costBasis,
public readonly bool $forFiat,
?SharePoolingAssetTransactionId $id = null,
?Quantity $sameDayQuantity = null,
?Quantity $thirtyDayQuantity = null,
Expand Down Expand Up @@ -110,6 +111,12 @@ public function decreaseThirtyDayQuantity(Quantity $quantity): self

public function __toString(): string
{
return sprintf('%s: acquired %s tokens for %s', $this->date, $this->quantity, $this->costBasis);
return sprintf(
'%s: acquired %s tokens for %s (for fiat: %s)',
$this->date,
$this->quantity,
$this->costBasis,
$this->forFiat ? 'yes' : 'no',
);
}
}
Loading

0 comments on commit a379b7f

Please sign in to comment.