Source of file EcommerceCurrency.php
Size: 23,403 Bytes - Last Modified: 2021-12-23T10:39:35+00:00
/var/www/docs.ssmods.com/process/src/src/Model/Money/EcommerceCurrency.php
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843 | <?php namespace Sunnysideup\Ecommerce\Model\Money; use SilverStripe\Core\Config\Config; use SilverStripe\Core\Injector\Injector; use SilverStripe\Forms\HeaderField; use SilverStripe\Forms\ReadonlyField; use SilverStripe\ORM\DataList; use SilverStripe\ORM\DataObject; use SilverStripe\ORM\FieldType\DBCurrency; use SilverStripe\ORM\FieldType\DBField; use SilverStripe\Security\Permission; use SilverStripe\Security\Security; use Sunnysideup\CmsEditLinkField\Api\CMSEditLinkAPI; use Sunnysideup\Ecommerce\Api\ShoppingCart; use Sunnysideup\Ecommerce\Config\EcommerceConfig; use Sunnysideup\Ecommerce\Control\ShoppingCartController; use Sunnysideup\Ecommerce\Interfaces\EditableEcommerceObject; use Sunnysideup\Ecommerce\Model\Extensions\EcommerceRole; use Sunnysideup\Ecommerce\Model\Order; use Sunnysideup\Ecommerce\Money\EcommerceMoney; use Sunnysideup\Ecommerce\Money\ExchangeRateProvider; use Sunnysideup\Ecommerce\Tasks\EcommerceTaskDebugCart; /** * Object to manage currencies. * * @authors: Nicolaas [at] Sunny Side Up .co.nz * @package: ecommerce * @sub-package: money * Precondition : There should always be at least one currency usable. */ class EcommerceCurrency extends DataObject implements EditableEcommerceObject { /** * standard SS variable. * * @var string */ private static $table_name = 'EcommerceCurrency'; /** * @var string */ private static $exchange_provider_class = ExchangeRateProvider::class; private static $show_currency_at_all = true; /** * @var string */ private static $default_currency = 'NZD'; private static $db = [ 'Code' => 'Varchar(3)', 'Name' => 'Varchar(100)', 'InUse' => 'Boolean', ]; /** * standard SS variable. * * @var array */ private static $indexes = [ 'Code' => true, 'InUse' => true, 'Name' => true, ]; /** * standard SS variable. * * @var array */ private static $casting = [ 'IsDefault' => 'Boolean', 'IsDefaultNice' => 'Varchar', 'InUseNice' => 'Varchar', 'ExchangeRate' => 'Double', 'DefaultSymbol' => 'Varchar', 'ShortSymbol' => 'Varchar', 'LongSymbol' => 'Varchar', ]; /** * standard SS variable. * * @var array */ private static $searchable_fields = [ 'Code' => 'PartialMatchFilter', 'Name' => 'PartialMatchFilter', ]; /** * standard SS variable. * * @var array */ private static $field_labels = [ 'Code' => 'Short Code', 'Name' => 'Name', 'InUse' => 'It is available for use?', 'ExchangeRate' => 'Exchange Rate', 'ExchangeRateExplanation' => 'Exchange Rate explanation', 'IsDefaultNice' => 'Is default currency for site', 'DefaultSymbol' => 'Default symbol', 'ShortSymbol' => 'Short symbol', 'LongSymbol' => 'Long symbol', ]; /** * standard SS variable. * * @var array */ private static $summary_fields = [ 'Code' => 'Code', 'Name' => 'Name', 'InUseNice' => 'Available', 'IsDefaultNice' => 'Default Currency', 'ExchangeRate' => 'Exchange Rate', ]; //note no => for relational fields /** * standard SS variable. * * @var string */ private static $singular_name = 'Currency'; /** * standard SS variable. * * @var string */ private static $plural_name = 'Currencies'; /** * standard SS variable. * * @var array */ private static $default_sort = [ 'InUse' => 'DESC', 'Name' => 'ASC', 'Code' => 'ASC', 'ID' => 'DESC', ]; /** * standard SS variable. * * @var array */ private static $defaults = [ 'InUse' => true, ]; private static $currencies = [ 'AFA' => 'afghanistan afghanis', 'ALL' => 'albania leke', 'DZD' => 'algeria dinars', 'ARS' => 'argentina pesos', 'AUD' => 'australia dollars', 'ATS' => 'austria schillings*', 'BSD' => 'bahamas dollars', 'BHD' => 'bahrain dinars', 'BDT' => 'bangladesh taka', 'BBD' => 'barbados dollars', 'BEF' => 'belgium francs*', 'BMD' => 'bermuda dollars', 'BRL' => 'brazil reais', 'BGN' => 'bulgaria leva', 'CAD' => 'canada dollars', 'XOF' => 'cfa bceao francs', 'XAF' => 'cfa beac francs', 'CLP' => 'chile pesos', 'CNY' => 'china yuan renminbi', 'COP' => 'colombia pesos', 'CRC' => 'costa rica colones', 'HRK' => 'croatia kuna', 'CYP' => 'cyprus pounds', 'CZK' => 'czech republic koruny', 'DKK' => 'denmark kroner', 'DOP' => 'dominican republic pesos', 'XCD' => 'eastern caribbean dollars', 'EGP' => 'egypt pounds', 'EEK' => 'estonia krooni', 'EUR' => 'euro', 'FJD' => 'fiji dollars', 'DEM' => 'germany deutsche marks*', 'XAU' => 'gold ounces', 'NLG' => 'holland (netherlands) guilders*', 'HKD' => 'hong kong dollars', 'HUF' => 'hungary forint', 'ISK' => 'iceland kronur', 'XDR' => 'imf special drawing right', 'INR' => 'india rupees', 'IDR' => 'indonesia rupiahs', 'IRR' => 'iran rials', 'IQD' => 'iraq dinars', 'ILS' => 'israel new shekels', 'JMD' => 'jamaica dollars', 'JPY' => 'japan yen', 'JOD' => 'jordan dinars', 'KES' => 'kenya shillings', 'KRW' => 'korea (south) won', 'KWD' => 'kuwait dinars', 'LBP' => 'lebanon pounds', 'MYR' => 'malaysia ringgits', 'MTL' => 'malta liri', 'MUR' => 'mauritius rupees', 'MXN' => 'mexico pesos', 'MAD' => 'morocco dirhams', 'NZD' => 'new zealand dollars', 'NOK' => 'norway kroner', 'OMR' => 'oman rials', 'PKR' => 'pakistan rupees', 'XPD' => 'palladium ounces', 'PEN' => 'peru nuevos soles', 'PHP' => 'philippines pesos', 'PLN' => 'poland zlotych', 'QAR' => 'qatar riyals', 'ROL' => 'romania lei', 'RUB' => 'russia rubles', 'SAR' => 'saudi arabia riyals', 'XAG' => 'silver ounces', 'SGD' => 'singapore dollars', 'SKK' => 'slovakia koruny', 'SIT' => 'slovenia tolars', 'ZAR' => 'south africa rand', 'LKR' => 'sri lanka rupees', 'SDD' => 'sudan dinars', 'SEK' => 'sweden kronor', 'CHF' => 'switzerland francs', 'TWD' => 'taiwan new dollars', 'THB' => 'thailand baht', 'TTD' => 'trinidad and tobago dollars', 'TND' => 'tunisia dinars', 'TRY' => 'turkey new lira', 'AED' => 'united arab emirates dirhams', 'gbp' => 'united kingdom pounds', 'USD' => 'united states dollars', 'VEB' => 'venezuela bolivares', 'VND' => 'vietnam dong', 'ZMK' => 'zambia kwacha', ]; public function i18n_singular_name() { return _t('EcommerceCurrency.CURRENCY', 'Currency'); } public function i18n_plural_name() { return _t('EcommerceCurrency.CURRENCIES', 'Currencies'); } /** * Standard SS Method. * * @param \SilverStripe\Security\Member $member * @param mixed $context * * @var bool */ public function canCreate($member = null, $context = []) { if (! $member) { $member = Security::getCurrentUser(); } $extended = $this->extendedCan(__FUNCTION__, $member); if (null !== $extended) { return $extended; } if (Permission::checkMember($member, Config::inst()->get(EcommerceRole::class, 'admin_permission_code'))) { return true; } return parent::canEdit($member); } /** * Standard SS Method. * * @param \SilverStripe\Security\Member $member * @param mixed $context * * @var bool */ public function canView($member = null, $context = []) { if (! $member) { $member = Security::getCurrentUser(); } $extended = $this->extendedCan(__FUNCTION__, $member); if (null !== $extended) { return $extended; } if (Permission::checkMember($member, Config::inst()->get(EcommerceRole::class, 'admin_permission_code'))) { return true; } return parent::canEdit($member); } /** * Standard SS Method. * * @param \SilverStripe\Security\Member $member * @param mixed $context * * @var bool */ public function canEdit($member = null, $context = []) { if (! $member) { $member = Security::getCurrentUser(); } $extended = $this->extendedCan(__FUNCTION__, $member); if (null !== $extended) { return $extended; } if (Permission::checkMember($member, Config::inst()->get(EcommerceRole::class, 'admin_permission_code'))) { return true; } return parent::canEdit($member); } /** * Standard SS method. * * @param \SilverStripe\Security\Member $member * * @return bool */ public function canDelete($member = null) { if (! $this->InUse && EcommerceCurrency::get()->Count() > 1) { if (! $member) { $member = Security::getCurrentUser(); } $extended = $this->extendedCan(__FUNCTION__, $member); if (null !== $extended) { return $extended; } if (Permission::checkMember($member, Config::inst()->get(EcommerceRole::class, 'admin_permission_code'))) { return true; } return parent::canEdit($member); } return false; } /** * NOTE: when there is only one currency we return an empty DataList * as one currency is meaningless. * * @return null|\SilverStripe\ORM\DataList */ public static function ecommerce_currency_list() { $dos = EcommerceCurrency::get() ->Filter(['InUse' => 1]) ->Sort( [ "IF(\"Code\" = '" . strtoupper(EcommerceConfig::get(EcommerceCurrency::class, 'default_currency')) . "', 0, 1)" => 'ASC', 'Name' => 'ASC', 'Code' => 'ASC', ] ) ; if ($dos->count() < 2) { return null; } return $dos; } public static function get_list(): DataList { return EcommerceCurrency::get() ->filter(['InUse' => 1]) ->sort( [ "IF(\"Code\" = '" . EcommerceConfig::get(EcommerceCurrency::class, 'default_currency') . "', 0, 1)" => 'ASC', 'Name' => 'ASC', 'Code' => 'ASC', ] ) ; } /** * @param DBCurrency|float $price * * @return \SilverStripe\ORM\FieldType\DBField|\SilverStripe\ORM\FieldType\DBMoney */ public static function get_money_object_from_order_currency($price, Order $order = null) { if ($price instanceof DBCurrency) { $price = $price->getValue(); } if (! $order) { $order = ShoppingCart::current_order(); } $currencyCode = ''; if (Config::inst()->get('show_currency_at_all')) { $currency = $order->CurrencyUsed(); $currencyCode = $currency->Code; if ($order) { if ($order->HasAlternativeCurrency()) { $exchangeRate = $order->ExchangeRate; if ($exchangeRate && 1 !== $exchangeRate) { $price = $exchangeRate * $price; } } } $updatedCurrencyCode = Injector::inst()->get(EcommerceCurrency::class)->extend('updateCurrencyCodeForMoneyObect', $currencyCode); if (null !== $updatedCurrencyCode && is_array($updatedCurrencyCode) && count($updatedCurrencyCode)) { $currencyCode = $updatedCurrencyCode[0]; } } return DBField::create_field( 'Money', [ 'Amount' => $price, 'Currency' => $currencyCode, ] ); } /** * returns the default currency. */ public static function default_currency() { return DataObject::get_one( EcommerceCurrency::class, [ 'Code' => trim(strtolower(EcommerceConfig::get(EcommerceCurrency::class, 'default_currency'))), 'InUse' => 1, ] ); } /** * returns the default currency as Code. * * @return string - e.g. NZD */ public static function default_currency_code() { $code = ''; $obj = self::default_currency(); if ($obj) { $code = $obj->Code; } if (! $code) { $code = EcommerceConfig::get(EcommerceCurrency::class, 'default_currency'); } if (! $code) { $code = 'NZD'; } return strtoupper($code); } /** * @return int */ public static function default_currency_id() { $currency = self::default_currency(); return $currency ? $currency->ID : 0; } /** * Only returns a currency when it is a valid currency. * * @param string $currencyCode - the code of the currency, e.g. nzd * * @return DataObject|EcommerceCurrency null */ public static function get_one_from_code($currencyCode) { return DataObject::get_one( EcommerceCurrency::class, [ 'Code' => trim(strtoupper($currencyCode)), 'InUse' => 1, ] ); } /** * STANDARD SILVERSTRIPE STUFF. */ public function getCMSFields() { $fields = parent::getCMSFields(); $fieldLabels = $this->fieldLabels(); $codeField = $fields->dataFieldByName('Code'); $codeField->setDescription('e.g. NZD, use uppercase codes'); $titleField = $fields->dataFieldByName('Name'); $titleField->setDescription('e.g. New Zealand Dollar'); $fields->addFieldToTab('Root.Main', new ReadonlyField('IsDefaulNice', $fieldLabels['IsDefaultNice'], $this->getIsDefaultNice())); if (! $this->isDefault()) { $fields->addFieldToTab('Root.Main', new ReadonlyField('ExchangeRate', $fieldLabels['ExchangeRate'], $this->ExchangeRate())); $fields->addFieldToTab('Root.Main', new ReadonlyField('ExchangeRateExplanation', $fieldLabels['ExchangeRateExplanation'], $this->ExchangeRateExplanation())); } $fields->addFieldsToTab('Root.Main', [ new HeaderField('Symbols', 'Symbols'), new ReadonlyField('DefaultSymbol', 'Default'), new ReadonlyField('ShortSymbol', 'Short'), new ReadonlyField('LongSymbol', 'Long'), ]); return $fields; } /** * link to edit the record. * * @param null|string $action - e.g. edit * * @return string */ public function CMSEditLink($action = null) { return CMSEditLinkAPI::find_edit_link_for_object($this, $action); } public function DefaultSymbol() { return $this->getDefaultSymbol(); } public function getDefaultSymbol() { return EcommerceMoney::get_default_symbol($this->Code); } public function ShortSymbol() { return $this->getShortSymbol(); } public function getShortSymbol() { return EcommerceMoney::get_short_symbol($this->Code); } public function LongSymbol() { return $this->getLongSymbol(); } public function getLongSymbol() { return EcommerceMoney::get_long_symbol($this->Code); } /** * casted variable method. * * @return bool */ public function IsDefault() { return $this->getIsDefault(); } public function getIsDefault() { if ($this->exists()) { if (! $this->Code) { user_error('This currency (ID = ' . $this->ID . ') does not have a code '); } } return strtoupper($this->Code) === strtoupper(EcommerceConfig::get(EcommerceCurrency::class, 'default_currency')); } /** * casted variable method. * * @return string */ public function IsDefaultNice() { return $this->getIsDefaultNice(); } public function getIsDefaultNice() { if ($this->getIsDefault()) { return _t('EcommerceCurrency.YES', 'Yes'); } return _t('EcommerceCurrency.NO', 'No'); } /** * casted variable method. * * @return string */ public function InUseNice() { return $this->getInUseNice(); } public function getInUseNice() { if ($this->InUse) { return _t('EcommerceCurrency.YES', 'Yes'); } return _t('EcommerceCurrency.NO', 'No'); } /** * casted variable. * * @alias for getExchangeRate * * @return float */ public function ExchangeRate() { return $this->getExchangeRate(); } /** * @return float */ public function getExchangeRate() { $exchangeRateProviderClassName = EcommerceConfig::get(EcommerceCurrency::class, 'exchange_provider_class'); $exchangeRateProvider = new $exchangeRateProviderClassName(); return $exchangeRateProvider->ExchangeRate(EcommerceConfig::get(EcommerceCurrency::class, 'default_currency'), $this->Code); } /** * casted variable. * * @return string */ public function ExchangeRateExplanation() { return $this->getExchangeRateExplanation(); } public function getExchangeRateExplanation(): string { $string = '1 ' . EcommerceConfig::get(EcommerceCurrency::class, 'default_currency') . ' = ' . round($this->getExchangeRate(), 3) . ' ' . $this->Code; $exchangeRate = $this->getExchangeRate(); $exchangeRateError = ''; if (! $exchangeRate) { $exchangeRate = 1; $exchangeRateError = _t('EcommerceCurrency.EXCHANGE_RATE_ERROR', 'Error in exchange rate. '); } return $string . ', 1 ' . $this->Code . ' = ' . round(1 / $exchangeRate, 3) . ' ' . EcommerceConfig::get(EcommerceCurrency::class, 'default_currency') . '. ' . $exchangeRateError; } /** * @return bool */ public function IsCurrent() { $order = ShoppingCart::current_order(); return $order && $order->CurrencyUsedID === $this->ID; } /** * Returns the link that can be used in the shopping cart to * set the preferred currency to this one. * For example: /shoppingcart/setcurrency/nzd/ * Dont be fooled by the set_ part in the set_currency_link.... * * @return string */ public function Link() { return ShoppingCartController::set_currency_link($this->Code); } /** * returns the link type. * * @return string (link | default | current) */ public function LinkingMode() { $linkingMode = ''; if ($this->IsDefault()) { $linkingMode .= ' default'; } if ($this->IsCurrent()) { $linkingMode .= ' current'; } else { $linkingMode .= ' link'; } return $linkingMode; } public function validate() { $result = parent::validate(); $errors = []; if (! $this->Code || 3 !== mb_strlen($this->Code)) { $errors[] = 'The code must be 3 characters long.'; } if (! $this->Name) { $errors[] = 'The name is required.'; } if (! count($errors)) { $this->Code = strtoupper($this->Code); // Check that there are no 2 same code currencies in use if ($this->isChanged('Code')) { $exists = EcommerceCurrency::get() ->where("UPPER(\"Code\") = '" . $this->Code . "'") ->exclude('ID', (int) $this->ID) ->exists() ; if ($exists) { $errors[] = "There is alreay another currency in use with code: '{$this->Code}'."; } } } foreach ($errors as $error) { $result->addError($error); } return $result; } /** * Standard SS Method * Adds the default currency. */ public function populateDefaults() { $this->InUse = true; return parent::populateDefaults(); } /** * Standard SS Method * Adds the default currency. */ public function requireDefaultRecords() { parent::requireDefaultRecords(); if (! self::default_currency()) { self::create_new(EcommerceConfig::get(EcommerceCurrency::class, 'default_currency')); } } /** * checks if a currency exists, creates it and returns it. * * @param string $code * @param string $name OPTIONAL */ public static function create_new($code, $name = '') { $code = trim(strtoupper($code)); if (! $name) { $currencies = Config::inst()->get(EcommerceCurrency::class, 'currencies'); $name = isset($currencies[$code]) ? $currencies[$code] : $code; } $name = ucwords($name); $currency = DataObject::get_one( EcommerceCurrency::class, ['Code' => $code], $cacheDataObjectGetOne = false ); if ($currency) { $currency->Name = $name; $currency->InUse = true; } else { $currency = EcommerceCurrency::create( [ 'Code' => $code, 'Name' => $name, 'InUse' => true, ] ); } $valid = $currency->write(); if ($valid) { return $currency; } } /** * Debug helper method. * Can be called from /shoppingcart/debug/. * * @return string */ public function debug() { return EcommerceTaskDebugCart::debug_object($this); } protected function onBeforeWrite() { parent::onBeforeWrite(); // Check that there is always at least one currency in use $this->Code = strtoupper($this->Code); if (! $this->InUse) { $list = self::get_list(); $count = $list->Count(); if (0 === $count || (1 === $count && $list->First()->ID === $this->ID)) { $this->InUse = true; } } } } |