feat: moneta 2.0, MONETA.Assistant payment module for PHPShop 6.7+
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MonetaPhpShop\receipt;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use MonetaPhpShop\Money;
|
||||
|
||||
/**
|
||||
* Êîëè÷åñòâî ïîçèöèè ÷åêà ñ òî÷íîñòüþ êàññû — òðè çíàêà ïîñëå òî÷êè (`kassaspecification.pdf`,
|
||||
* â ñõåìå Check URL `multipleOf 0.001`). Õðàíèòñÿ öåëûì ÷èñëîì òûñÿ÷íûõ, êàê êîïåéêè â {@see Money}:
|
||||
* Êîðçèíà ÿäðà õðàíèò êîëè÷åñòâî êàê åñòü.
|
||||
*/
|
||||
final class Quantity
|
||||
{
|
||||
private const SCALE = 1000;
|
||||
|
||||
/** Ïîòîëîê êàññû: 99999.999. */
|
||||
public const MAX_THOUSANDTHS = 99999999;
|
||||
|
||||
/** @var int */
|
||||
private $thousandths;
|
||||
|
||||
private function __construct(int $thousandths)
|
||||
{
|
||||
if (($thousandths < 1) || ($thousandths > self::MAX_THOUSANDTHS)) {
|
||||
throw new InvalidArgumentException('Êîëè÷åñòâî ïîçèöèè ÷åêà âíå ãðàíèö êàññû.');
|
||||
}
|
||||
|
||||
$this->thousandths = $thousandths;
|
||||
}
|
||||
|
||||
public static function pieces(int $pieces): self
|
||||
{
|
||||
return new self($pieces * self::SCALE);
|
||||
}
|
||||
|
||||
public static function fromThousandths(int $thousandths): self
|
||||
{
|
||||
return new self($thousandths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Êîëè÷åñòâî èç êîðçèíû çàêàçà (`num`): ÷èñëî èëè ÷èñëîâàÿ ñòðîêà, îêðóãëÿåòñÿ äî òûñÿ÷íûõ.
|
||||
* Íîëü, îòðèöàòåëüíîå, íå ÷èñëî èëè áîëüøå ïîòîëêà êàññû — null.
|
||||
*
|
||||
* @param mixed $raw
|
||||
*/
|
||||
public static function fromCart($raw): ?self
|
||||
{
|
||||
if (is_string($raw)) {
|
||||
$raw = trim($raw);
|
||||
}
|
||||
if (!is_int($raw) && !is_float($raw) && !(is_string($raw) && is_numeric($raw))) {
|
||||
return null;
|
||||
}
|
||||
$thousandths = (int) round((float) $raw * self::SCALE);
|
||||
if (($thousandths < 1) || ($thousandths > self::MAX_THOUSANDTHS)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new self($thousandths);
|
||||
}
|
||||
|
||||
public function getThousandths(): int
|
||||
{
|
||||
return $this->thousandths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Öåëîå ëè êîëè÷åñòâî: òîëüêî òàêóþ ñòðîêó ìîæíî ðåçàòü íà äâå ñ ðàçíèöåé öåí â êîïåéêó.
|
||||
*/
|
||||
public function isWhole(): bool
|
||||
{
|
||||
return $this->thousandths % self::SCALE === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Öåëîå ÷èñëî øòóê; òîëüêî äëÿ {@see self::isWhole()}.
|
||||
*/
|
||||
public function getPieces(): int
|
||||
{
|
||||
return intdiv($this->thousandths, self::SCALE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Äåñÿòè÷íàÿ çàïèñü áåç ëèøíèõ íóëåé: `2`, `2.5`, `0.125`.
|
||||
*/
|
||||
public function toDecimal(): string
|
||||
{
|
||||
if ($this->isWhole()) {
|
||||
return (string) $this->getPieces();
|
||||
}
|
||||
|
||||
return rtrim(
|
||||
sprintf('%d.%03d', intdiv($this->thousandths, self::SCALE), $this->thousandths % self::SCALE),
|
||||
'0',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ñóììà ïîçèöèè: öåíà x êîëè÷åñòâî, îêðóãëåíèå äî êîïåéêè (ïîëîâèíà — ââåðõ).
|
||||
*/
|
||||
public function totalOf(Money $price): Money
|
||||
{
|
||||
return Money::fromMinorUnits(
|
||||
intdiv(($price->toMinorUnits() * $this->thousandths) + intdiv(self::SCALE, 2), self::SCALE),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user