-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Quantity allocation refactoring and other fixes #64
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -63,8 +63,23 @@ public function section104PoolCostBasis(): FiatAmount | |
return $this->costBasis->dividedBy($this->quantity)->multipliedBy($this->section104PoolQuantity()); | ||
} | ||
|
||
/** Increase the same-day quantity and adjust the 30-day quantity accordingly. */ | ||
public function increaseSameDayQuantity(Quantity $quantity): self | ||
{ | ||
if ($quantity->isGreaterThan($availableQuantity = $this->section104PoolQuantity())) { | ||
throw SharePoolingAssetAcquisitionException::insufficientSameDayQuantityToIncrease($quantity, $availableQuantity); | ||
} | ||
|
||
$this->sameDayQuantity = $this->sameDayQuantity->plus($quantity); | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* Increase the same-day quantity and adjust the 30-day quantity accordingly. | ||
* | ||
* @return Quantity the added quantity | ||
*/ | ||
public function increaseSameDayQuantityUpToAvailableQuantity(Quantity $quantity): Quantity | ||
{ | ||
// Adjust same-day quantity | ||
$quantityToAdd = Quantity::minimum($quantity, $this->availableSameDayQuantity()); | ||
|
@@ -74,14 +89,14 @@ public function increaseSameDayQuantity(Quantity $quantity): self | |
$quantityToDeduct = Quantity::minimum($quantityToAdd, $this->thirtyDayQuantity); | ||
$this->thirtyDayQuantity = $this->thirtyDayQuantity->minus($quantityToDeduct); | ||
|
||
return $this; | ||
return $quantityToAdd; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ℹ️ |
||
} | ||
|
||
/** @throws SharePoolingAssetAcquisitionException */ | ||
public function decreaseSameDayQuantity(Quantity $quantity): self | ||
{ | ||
if ($quantity->isGreaterThan($this->sameDayQuantity)) { | ||
throw SharePoolingAssetAcquisitionException::insufficientSameDayQuantity($quantity, $this->sameDayQuantity); | ||
throw SharePoolingAssetAcquisitionException::insufficientSameDayQuantityToDecrease($quantity, $this->sameDayQuantity); | ||
} | ||
|
||
$this->sameDayQuantity = $this->sameDayQuantity->minus($quantity); | ||
|
@@ -90,18 +105,30 @@ public function decreaseSameDayQuantity(Quantity $quantity): self | |
} | ||
|
||
public function increaseThirtyDayQuantity(Quantity $quantity): self | ||
{ | ||
if ($quantity->isGreaterThan($availableQuantity = $this->section104PoolQuantity())) { | ||
throw SharePoolingAssetAcquisitionException::insufficientThirtyDayQuantityToIncrease($quantity, $availableQuantity); | ||
} | ||
|
||
$this->thirtyDayQuantity = $this->thirtyDayQuantity->plus($quantity); | ||
|
||
return $this; | ||
} | ||
|
||
/** @return Quantity the added quantity */ | ||
public function increaseThirtyDayQuantityUpToAvailableQuantity(Quantity $quantity): Quantity | ||
{ | ||
$quantityToAdd = Quantity::minimum($quantity, $this->availableThirtyDayQuantity()); | ||
$this->thirtyDayQuantity = $this->thirtyDayQuantity->plus($quantityToAdd); | ||
|
||
return $this; | ||
return $quantityToAdd; | ||
} | ||
|
||
/** @throws SharePoolingAssetAcquisitionException */ | ||
public function decreaseThirtyDayQuantity(Quantity $quantity): self | ||
{ | ||
if ($quantity->isGreaterThan($this->thirtyDayQuantity)) { | ||
throw SharePoolingAssetAcquisitionException::insufficientThirtyDayQuantity($quantity, $this->thirtyDayQuantity); | ||
throw SharePoolingAssetAcquisitionException::insufficientThirtyDayQuantityToDecrease($quantity, $this->thirtyDayQuantity); | ||
} | ||
|
||
$this->thirtyDayQuantity = $this->thirtyDayQuantity->minus($quantity); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ | |
|
||
declare(strict_types=1); | ||
|
||
namespace Domain\Aggregates\SharePoolingAsset\Services\DisposalProcessor; | ||
namespace Domain\Aggregates\SharePoolingAsset\Services\DisposalBuilder; | ||
|
||
use Brick\DateTime\LocalDate; | ||
use Domain\Aggregates\SharePoolingAsset\Actions\DisposeOfSharePoolingAsset; | ||
|
@@ -16,9 +16,9 @@ | |
* This service essentially calculates the cost basis of a disposal by looking at past and future | ||
* transactions, following the various share pooling asset rules (same-day, 30-day, section 104 pool). | ||
*/ | ||
final class DisposalProcessor | ||
final class DisposalBuilder | ||
{ | ||
public static function process( | ||
public static function make( | ||
DisposeOfSharePoolingAsset $disposal, | ||
SharePoolingAssetTransactions $transactions, | ||
): SharePoolingAssetDisposal { | ||
|
@@ -107,11 +107,10 @@ private static function processSameDayAcquisitions( | |
// Deduct the applied quantity from the same-day acquisitions | ||
$remainder = $availableSameDayQuantity; | ||
foreach ($sameDayAcquisitions as $acquisition) { | ||
$quantityToAllocate = Quantity::minimum($remainder, $acquisition->availableSameDayQuantity()); | ||
$quantityToAllocate = $acquisition->increaseSameDayQuantityUpToAvailableQuantity($remainder); | ||
$sameDayQuantityAllocation->allocateQuantity($quantityToAllocate, $acquisition); | ||
$acquisition->increaseSameDayQuantity($remainder); | ||
$remainder = $remainder->minus($quantityToAllocate); | ||
if ($remainder->isZero()) { | ||
|
||
if (($remainder = $remainder->minus($quantityToAllocate))->isZero()) { | ||
break; | ||
} | ||
} | ||
|
@@ -144,37 +143,13 @@ private static function processAcquisitionsWithinThirtyDays( | |
|
||
foreach ($withinThirtyDaysAcquisitions as $acquisition) { | ||
// Apply the acquisition's cost basis to the disposed of asset up to the remaining quantity | ||
$thirtyDayQuantityToApply = Quantity::minimum($acquisition->availableThirtyDayQuantity(), $remainingQuantity); | ||
|
||
// Also deduct same-day disposals with available same-day quantity that haven't been processed yet | ||
$sameDayDisposals = $transactions->disposalsMadeOn($acquisition->date) | ||
->unprocessed() | ||
->withAvailableSameDayQuantity(); | ||
|
||
foreach ($sameDayDisposals as $disposal) { | ||
$sameDayQuantityToApply = Quantity::minimum($disposal->availableSameDayQuantity(), $thirtyDayQuantityToApply); | ||
$disposal->sameDayQuantityAllocation->allocateQuantity($sameDayQuantityToApply, $acquisition); | ||
$acquisition->increaseSameDayQuantity($sameDayQuantityToApply); | ||
$thirtyDayQuantityToApply = $thirtyDayQuantityToApply->minus($sameDayQuantityToApply); | ||
if ($thirtyDayQuantityToApply->isZero()) { | ||
break; | ||
} | ||
} | ||
|
||
if ($thirtyDayQuantityToApply->isZero()) { | ||
continue; | ||
} | ||
Comment on lines
-149
to
-166
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ℹ️ Disposals are not being added as unprocessed anymore, so this section is now redundant |
||
|
||
$quantityToAllocate = $acquisition->increaseThirtyDayQuantityUpToAvailableQuantity($remainingQuantity); | ||
$averageCostBasisPerUnit = $acquisition->averageCostBasisPerUnit(); | ||
$costBasis = $costBasis->plus($averageCostBasisPerUnit->multipliedBy($quantityToAllocate)); | ||
$thirtyDayQuantityAllocation->allocateQuantity($quantityToAllocate, $acquisition); | ||
|
||
$costBasis = $costBasis->plus($averageCostBasisPerUnit->multipliedBy($thirtyDayQuantityToApply)); | ||
|
||
$thirtyDayQuantityAllocation->allocateQuantity($thirtyDayQuantityToApply, $acquisition); | ||
$acquisition->increaseThirtyDayQuantity($thirtyDayQuantityToApply); | ||
$remainingQuantity = $remainingQuantity->minus($thirtyDayQuantityToApply); | ||
|
||
// Continue until there are no more transactions or we've covered all disposed tokens | ||
if ($remainingQuantity->isZero()) { | ||
// Continue until there are no more transactions or we've covered all disposed of tokens | ||
if (($remainingQuantity = $remainingQuantity->minus($quantityToAllocate))->isZero()) { | ||
break; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,24 +12,37 @@ | |
use Domain\Aggregates\SharePoolingAsset\Services\QuantityAdjuster\Exceptions\QuantityAdjusterException; | ||
use Domain\Aggregates\SharePoolingAsset\ValueObjects\QuantityAllocation; | ||
|
||
/** | ||
* This service restores the quantities from acquisitions that were | ||
* previously matched with a disposal that is now being reverted. | ||
*/ | ||
final class QuantityAdjuster | ||
{ | ||
/** | ||
* Adjust acquisition quantities based on the disposal's allocated quantities. | ||
* | ||
* @throws SharePoolingAssetAcquisitionException | ||
*/ | ||
public static function applyDisposal( | ||
SharePoolingAssetDisposal $disposal, | ||
SharePoolingAssetTransactions $transactions, | ||
): void { | ||
foreach (self::getAcquisitions($disposal->sameDayQuantityAllocation, $transactions) as $acquisition) { | ||
$acquisition->increaseSameDayQuantity($disposal->sameDayQuantityAllocation->quantityAllocatedTo($acquisition)); | ||
} | ||
|
||
foreach (self::getAcquisitions($disposal->thirtyDayQuantityAllocation, $transactions) as $acquisition) { | ||
$acquisition->increaseThirtyDayQuantity($disposal->thirtyDayQuantityAllocation->quantityAllocatedTo($acquisition)); | ||
} | ||
} | ||
Comment on lines
+22
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ℹ️ An acquisition's same-day and 30-day quantities are now only adjusted upon applying a disposal's event, using this method |
||
|
||
/** | ||
* Restore acquisition quantities that were previously allocated to a disposal that is now being reverted. | ||
* | ||
* @throws SharePoolingAssetAcquisitionException | ||
*/ | ||
public static function revertDisposal( | ||
SharePoolingAssetDisposal $disposal, | ||
SharePoolingAssetTransactions $transactions, | ||
): void { | ||
foreach (self::getAcquisitions($disposal->sameDayQuantityAllocation, $transactions) as $acquisition) { | ||
try { | ||
$acquisition->decreaseSameDayQuantity($disposal->sameDayQuantityAllocation->quantityAllocatedTo($acquisition)); | ||
} catch (SharePoolingAssetAcquisitionException) { | ||
// @TODO When re-acquiring within 30 days an asset that was disposed of on the same day it was acquired, | ||
// decreasing the same-day quantity of the concerned acquisitions fails, because at the time the latter | ||
// were recorded within the SharePoolingAssetAcquired events that had no same-day quantity yet | ||
} | ||
$acquisition->decreaseSameDayQuantity($disposal->sameDayQuantityAllocation->quantityAllocatedTo($acquisition)); | ||
Comment on lines
-26
to
+45
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ℹ️ Since an acquisition's quantities are now adjusted upon applying a disposal's event, these quantities are always right and this hack is not necessary anymore |
||
} | ||
|
||
foreach (self::getAcquisitions($disposal->thirtyDayQuantityAllocation, $transactions) as $acquisition) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ℹ️ There are now two separate methods –
increaseSameDayQuantity
andincreaseSameDayQuantityUpToAvailableQuantity
.The former will throw an exception if the specified quantity exceeds the currently unallocated same-day quantity and is used by the
QuantityAdjuster
service (see further down).The latter will allocate as much as possible the specified quantity, also reallocating some or all of the 30-day quantity to the same-day quantity as necessary.