Document::fromArray
which is called after getting a document or creating a new one (returned value is a new objected created from response in order to have all computed values filled from FIC) is already multiplying * 100
all amounts to work with integers. This of course is great when working with amounts, also makes happy Money constructor.
Unfortunately FIC api response sends back floats with a precision of 4 digits. Multiplying by 100 does not give us an integer as expected but leaves us a float. Example we get 32.9644
in the response, we multiply new Money($item['prezzo_netto'] * 100, $this->currency)
, we have now 3296.44 which is passed to Money constructor.
There a Money\Number
object is created in order to check if the amount passed is an integer or not and unfortunately in our case we still have a decimal part:
if (filter_var($amount, FILTER_VALIDATE_INT) === false) {
$numberFromString = Number::fromString($amount);
if (!$numberFromString->isInteger()) {
throw new \InvalidArgumentException('Amount must be an integer(ish) value');
}
$amount = $numberFromString->getIntegerPart();
}
This affects only Document
so far and a quick fix would be to round down amounts to a precision of 2 digits (keeping PHP_ROUND_HALF_UP
mode) before multiplying by 100:
new Money(round($item['prezzo_netto'], 2) * 100, $this->currency)
EDIT:
However this approach would work for most cases (currencies with subunits down to cents) it wouldn't work for other cases like JPY
, ISK
(just to mention two) without cents or TND
which goes down to milim
(1/1000).
Knowing that it would be good to get the subunit
for a currency in order to know how to round it and how to multiply it to get an integer. Below example:
$eur = new Currency('EUR');
$subUnit = (new ISOCurrencies())->subunitFor($eur);
$amount = new Money(round($item['prezzo_netto'], $subUnit) * (10 ** $subUnit), $this->currency)
This would cover all currencies! @alekitto what do you think?