Source of file Transaction.php
Size: 11,646 Bytes - Last Modified: 2021-12-24T06:51:34+00:00
/var/www/docs.ssmods.com/process/src/src/Transaction/Transaction.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399 | <?php namespace Heystack\Ecommerce\Transaction; use Heystack\Core\Exception\ConfigurationException; use Heystack\Core\State\State; use Heystack\Core\State\StateableInterface; use Heystack\Core\Storage\Backends\SilverStripeOrm\Backend; use Heystack\Core\Traits\HasStateServiceTrait; use Heystack\Ecommerce\Currency\Interfaces\CurrencyServiceInterface; use Heystack\Ecommerce\Currency\Traits\HasCurrencyServiceTrait; use Heystack\Ecommerce\Transaction\Interfaces\HasLinkedTransactionModifiersInterface; use Heystack\Ecommerce\Transaction\Interfaces\HasTransactionInterface; use Heystack\Ecommerce\Transaction\Interfaces\TransactionInterface; use Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface; /** * Transaction Service * * Handles all the TransactionModifiers and calculates the order's total. * * @copyright Heyday * @author Glenn Bautista <glenn@heyday.co.nz> * @author Cam Spiers <cameron@heyday.co.nz> * @author Stevie Mayhew <stevie@heyday.co.nz> * @package Ecommerce-Core */ class Transaction implements TransactionInterface, StateableInterface { use HasStateServiceTrait; use HasCurrencyServiceTrait; /** * Holds the key used for storing state */ const IDENTIFIER = 'transaction'; /** * @var \Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface[] */ protected $modifiers = []; /** * @var \SebastianBergmann\Money\Money */ protected $total; /** * @var string */ protected $status; /** * Holds an array of statuses that is accepted by the setStatus() method * @var array */ protected $validStatuses; /** * Tracks if a update has been requested * @var bool */ protected $updateRequested; /** * Creates the Transaction object * @param \Heystack\Core\State\State $stateService * @param \Heystack\Ecommerce\Currency\Interfaces\CurrencyServiceInterface $currencyService * @param array $validStatuses * @param string $defaultStatus * @throws ConfigurationException */ public function __construct( State $stateService, CurrencyServiceInterface $currencyService, array $validStatuses, $defaultStatus ) { $this->stateService = $stateService; $this->currencyService = $currencyService; $this->validStatuses = $validStatuses; if (!$this->isValidStatus($defaultStatus)) { throw new ConfigurationException( sprintf("The default status '%s' is not a valid status", $defaultStatus) ); } $this->status = $defaultStatus; $this->total = $this->currencyService->getZeroMoney(); } /** * Saves the state of the Transaction object * @return void */ public function saveState() { $this->stateService->setByKey( self::IDENTIFIER, [ $this->total, $this->status, $this->updateRequested ] ); } /** * Restores the state of the Transaction object * @return void */ public function restoreState() { if ($data = $this->stateService->getByKey(self::IDENTIFIER)) { list($this->total, $this->status, $this->updateRequested) = $data; } } /** * Add a TransactionModifier to the Transaction * @param \Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface $modifier * @throws \InvalidArgumentException * @return void */ public function addModifier(TransactionModifierInterface $modifier) { $this->modifiers[$modifier->getIdentifier()->getFull()] = $modifier; if ($modifier instanceof HasTransactionInterface) { $modifier->setTransaction($this); } } /** * Returns a TransactionModifier based on the identifier * @param string $identifier * @return \Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface|null */ public function getModifier($identifier) { $modifiers = $this->getModifiers(); return isset($modifiers[$identifier]) ? $modifiers[$identifier] : null; } /** * Returns all the TransactionModifiers held by the Transaction object * @return \Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface[] */ public function getModifiers() { return $this->modifiers; } /** * Returns modifiers on the transaction by TranactionModifierType * @param string $type * @throws \InvalidArgumentException * @return \Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface[] */ public function getModifiersByType($type) { $modifiers = []; foreach ($this->modifiers as $modifier) { if ($modifier->getType() === $type) { $modifiers[$modifier->getIdentifier()->getFull()] = $modifier; } } return $modifiers; } /** * @return \Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface[] */ public function getChargeableModifiers() { return $this->getModifiersByType(TransactionModifierTypes::CHARGEABLE); } /** * @return \Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface[] */ public function getDeductibleModifiers() { return $this->getModifiersByType(TransactionModifierTypes::DEDUCTIBLE); } /** * @return \Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface[] */ public function getNeutralModifiers() { return $this->getModifiersByType(TransactionModifierTypes::NEUTRAL); } /** * Returns the aggregate total of the TransactionModifers held by the Transaction object * @return \SebastianBergmann\Money\Money */ public function getTotal() { if ($this->updateRequested) { $this->total = $this->getTotalWithExclusions([]); $this->updateRequested = false; $this->saveState(); } return $this->total; } /** * Update the aggregate total of the TransactionModifers held by the Transaction object * @return void */ public function updateTotal() { $this->updateRequested = true; $this->saveState(); } /** * Retrieves the total excluding specified modifiers * * @param array $exclude an array of identifiers to be excluded * @throws \SebastianBergmann\Money\OverflowException * @throws \RuntimeException * @return \SebastianBergmann\Money\Money */ public function getTotalWithExclusions(array $exclude) { return $this->getChargeableTotalWithExclusions($exclude) ->subtract($this->getDeductibleTotalWithExclusions($exclude)); } /** * @param array $exclude * @return \SebastianBergmann\Money\Money */ public function getChargeableTotalWithExclusions(array $exclude) { $total = $this->currencyService->getZeroMoney(); foreach ($this->getChargeableModifiers() as $chargeableModifier) { // Exclude specified modifiers if (in_array($chargeableModifier->getIdentifier()->getFull(), $exclude)) { continue; } $total = $total->add($chargeableModifier->getTotal()); } return $total; } /** * @param array $exclude * @return \SebastianBergmann\Money\Money */ public function getDeductibleTotalWithExclusions(array $exclude) { $deductibleTotal = $this->currencyService->getZeroMoney(); foreach ($this->getChargeableModifiers() as $chargeableModifier) { // Exclude specified modifiers if (in_array($chargeableModifier->getIdentifier()->getFull(), $exclude)) { continue; } $chargeableModifierTotal = $chargeableModifier->getTotal(); $discountSubTotal = $this->currencyService->getZeroMoney(); foreach ($this->getLinkedModifers($chargeableModifier, $this->getDeductibleModifiers()) as $discountModifier) { if (in_array($discountModifier->getIdentifier()->getFull(), $exclude)) { continue; } $discountSubTotal = $discountSubTotal->add($discountModifier->getTotal()); } if ($discountSubTotal->greaterThan($chargeableModifierTotal)) { $deductibleTotal = $deductibleTotal->add($chargeableModifierTotal); } else { $deductibleTotal = $deductibleTotal->add($discountSubTotal); } } return $deductibleTotal; } /** * @param Interfaces\TransactionModifierInterface $modifier * @param \Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface[]|void $fromModifiers * @return \Heystack\Ecommerce\Transaction\Interfaces\TransactionModifierInterface[] */ public function getLinkedModifers(TransactionModifierInterface $modifier, array $fromModifiers = null) { $linkedModifiers = []; $fromModifiers = ($fromModifiers ? : $this->getModifiers()); foreach ((array)$fromModifiers as $fromModifier) { if ( $fromModifier instanceof HasLinkedTransactionModifiersInterface && in_array($modifier, $fromModifier->getLinkedModifiers(), true) ) { $linkedModifiers[] = $fromModifier; } } return $linkedModifiers; } /** * Get the identifier for this system * @return string */ public function getStorableIdentifier() { return self::IDENTIFIER; } /** * Get the name of the schema this system relates to * @return string */ public function getSchemaName() { return 'Transaction'; } /** * Get the data to store * @return array The data to store */ public function getStorableData() { return [ 'id' => 'Transaction', 'flat' => [ 'Total' => \Heystack\Ecommerce\convertMoneyToString($this->total), 'Status' => $this->status, 'Currency' => $this->currencyService->getActiveCurrencyCode() ], 'related' => [] ]; } /** * Get the type of storage that is being used * @return array The type of storage in use */ public function getStorableBackendIdentifiers() { return [ Backend::IDENTIFIER ]; } /** * Sets the status of the transaction * @param string $status the status of the transaction * @throws \InvalidArgumentException * @return void */ public function setStatus($status) { if ($this->isValidStatus($status)) { $this->status = $status; $this->saveState(); } else { throw new \InvalidArgumentException( sprintf("Status '%s' is not a valid status", $status) ); } } /** * Retrieves the Transaction's status * @return string */ public function getStatus() { return $this->status; } /** * Checks if a status is valid * @param string $status * @return bool */ protected function isValidStatus($status) { return in_array($status, $this->validStatuses); } } |