mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-07-05 08:51:34 +00:00
Compare commits
6 commits
5ecf51e7cf
...
ad0c60f766
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad0c60f766 | ||
|
|
527c42c227 | ||
|
|
506d5f8173 | ||
|
|
846ecdf02e | ||
|
|
a9fa92c98e | ||
|
|
d237446334 |
17 changed files with 1042 additions and 999 deletions
|
|
@ -61,7 +61,8 @@ for the first time.
|
||||||
* Automatic thumbnail generation for pictures
|
* Automatic thumbnail generation for pictures
|
||||||
* Use cloud providers (like Octopart, Digikey, Farnell, LCSC or TME) to automatically get part information, datasheets, and
|
* Use cloud providers (like Octopart, Digikey, Farnell, LCSC or TME) to automatically get part information, datasheets, and
|
||||||
prices for parts
|
prices for parts
|
||||||
* Retrieve part information from arbitrary shop websites, using either conventional data extraction from structured metadata, or AI based data extraction
|
* Retrieve part information from arbitrary shop websites, using either conventional data extraction from structured metadata, or AI based data extraction.
|
||||||
|
A browser plugin allows to quickly submit parts from any website to your Part-DB instance, and even allows to circumvent anti-bot measures on shop websites.
|
||||||
* API to access Part-DB from other applications/scripts
|
* API to access Part-DB from other applications/scripts
|
||||||
* [Integration with KiCad](https://docs.part-db.de/usage/eda_integration.html): Use Part-DB as the central datasource for your
|
* [Integration with KiCad](https://docs.part-db.de/usage/eda_integration.html): Use Part-DB as the central datasource for your
|
||||||
KiCad and see available parts from Part-DB directly inside KiCad.
|
KiCad and see available parts from Part-DB directly inside KiCad.
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
"api-platform/json-api": "^4.0.0",
|
"api-platform/json-api": "^4.0.0",
|
||||||
"api-platform/symfony": "^4.0.0",
|
"api-platform/symfony": "^4.0.0",
|
||||||
"beberlei/doctrineextensions": "^1.2",
|
"beberlei/doctrineextensions": "^1.2",
|
||||||
"brick/math": "^0.14.8",
|
"brick/math": "^0.17.0",
|
||||||
"brick/schema": "^0.2.0",
|
"brick/schema": "^0.2.0",
|
||||||
"composer/ca-bundle": "^1.5",
|
"composer/ca-bundle": "^1.5",
|
||||||
"composer/package-versions-deprecated": "^1.11.99.5",
|
"composer/package-versions-deprecated": "^1.11.99.5",
|
||||||
|
|
@ -57,9 +57,9 @@
|
||||||
"scheb/2fa-trusted-device": "^v7.11.0",
|
"scheb/2fa-trusted-device": "^v7.11.0",
|
||||||
"shivas/versioning-bundle": "^4.0",
|
"shivas/versioning-bundle": "^4.0",
|
||||||
"spatie/db-dumper": "^3.3.1",
|
"spatie/db-dumper": "^3.3.1",
|
||||||
"symfony/ai-bundle": "^0.8.0",
|
"symfony/ai-bundle": "^0.9.0",
|
||||||
"symfony/ai-lm-studio-platform": "^0.8.0",
|
"symfony/ai-lm-studio-platform": "^0.9.0",
|
||||||
"symfony/ai-open-router-platform": "^0.8.0",
|
"symfony/ai-open-router-platform": "^0.9.0",
|
||||||
"symfony/apache-pack": "^1.0",
|
"symfony/apache-pack": "^1.0",
|
||||||
"symfony/asset": "7.4.*",
|
"symfony/asset": "7.4.*",
|
||||||
"symfony/console": "7.4.*",
|
"symfony/console": "7.4.*",
|
||||||
|
|
|
||||||
611
composer.lock
generated
611
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -2823,6 +2823,13 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||||
* region?: scalar|Param|null, // The region for OpenAI API (EU, US, or null for default) // Default: null
|
* region?: scalar|Param|null, // The region for OpenAI API (EU, US, or null for default) // Default: null
|
||||||
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
|
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
|
||||||
* },
|
* },
|
||||||
|
* openresponses?: array<string, array{ // Default: []
|
||||||
|
* base_url?: string|Param,
|
||||||
|
* api_key?: string|Param,
|
||||||
|
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
|
||||||
|
* model_catalog?: string|Param, // Service ID of the model catalog to use
|
||||||
|
* responses_path?: string|Param, // Default: "/v1/responses"
|
||||||
|
* }>,
|
||||||
* openrouter?: array{
|
* openrouter?: array{
|
||||||
* api_key?: string|Param,
|
* api_key?: string|Param,
|
||||||
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
|
* http_client?: string|Param, // Service ID of the HTTP client to use // Default: "http_client"
|
||||||
|
|
@ -2957,6 +2964,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||||
* endpoint?: string|Param,
|
* endpoint?: string|Param,
|
||||||
* api_key?: string|Param,
|
* api_key?: string|Param,
|
||||||
* index_name?: string|Param,
|
* index_name?: string|Param,
|
||||||
|
* http_client?: string|Param, // Default: "http_client"
|
||||||
* embedder?: string|Param, // Default: "default"
|
* embedder?: string|Param, // Default: "default"
|
||||||
* vector_field?: string|Param, // Default: "_vectors"
|
* vector_field?: string|Param, // Default: "_vectors"
|
||||||
* dimensions?: int|Param, // Default: 1536
|
* dimensions?: int|Param, // Default: 1536
|
||||||
|
|
@ -3019,6 +3027,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param;
|
||||||
* table_name?: string|Param,
|
* table_name?: string|Param,
|
||||||
* vector_field?: string|Param, // Default: "embedding"
|
* vector_field?: string|Param, // Default: "embedding"
|
||||||
* distance?: "cosine"|"inner_product"|"l1"|"l2"|Param, // Distance metric to use for vector similarity search // Default: "l2"
|
* distance?: "cosine"|"inner_product"|"l1"|"l2"|Param, // Distance metric to use for vector similarity search // Default: "l2"
|
||||||
|
* lang?: string|Param, // Default: "english"
|
||||||
* dbal_connection?: string|Param,
|
* dbal_connection?: string|Param,
|
||||||
* setup_options?: array{
|
* setup_options?: array{
|
||||||
* vector_type?: string|Param, // Default: "vector"
|
* vector_type?: string|Param, // Default: "vector"
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,15 @@ the parts you want to update. In the bulk actions dropdown select "Bulk info pro
|
||||||
You will be redirected to a page, where you can select how part fields should be mapped to info provider fields, and the
|
You will be redirected to a page, where you can select how part fields should be mapped to info provider fields, and the
|
||||||
results will be shown.
|
results will be shown.
|
||||||
|
|
||||||
|
## Browser plugin
|
||||||
|
There is a browser plugin available for [Chrome](https://chromewebstore.google.com/detail/part-db-page-submitter/bckkfkpidiiibmjdhjakleoagjmepioi) and [Firefox](https://addons.mozilla.org/de/firefox/addon/part-db-page-submitter/)
|
||||||
|
that allows to submit a website from your browser with one click to Part-DB, which then utilizes the Generic Web URL or the AI Web Provider to extract the part information from the page and pre-fill the part creation form.
|
||||||
|
The advantage is that it also works for pages behind logins, CAPTCHAs, or bot-blocking sites, as the plugin sends the already loaded page HTML to Part-DB.
|
||||||
|
The plugin is open source and available on [GitHub](https://github.com/Part-DB/browser-plugin).
|
||||||
|
|
||||||
|
To use it install it in your browser, enable one or more of the web page providers in Part-DB and allow the plugin support
|
||||||
|
in Part-DB settings. After that you can submit any product page to Part-DB with one click and the part creation form will be pre-filled with the information from the page.
|
||||||
|
|
||||||
## Data providers
|
## Data providers
|
||||||
|
|
||||||
The system tries to be as flexible as possible, so many different information sources can be used.
|
The system tries to be as flexible as possible, so many different information sources can be used.
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ use App\Services\EntityURLGenerator;
|
||||||
use App\Services\Formatters\AmountFormatter;
|
use App\Services\Formatters\AmountFormatter;
|
||||||
use App\Services\Formatters\MoneyFormatter;
|
use App\Services\Formatters\MoneyFormatter;
|
||||||
use App\Services\ProjectSystem\ProjectBuildHelper;
|
use App\Services\ProjectSystem\ProjectBuildHelper;
|
||||||
|
use Brick\Math\BigDecimal;
|
||||||
use Brick\Math\RoundingMode;
|
use Brick\Math\RoundingMode;
|
||||||
use Doctrine\ORM\AbstractQuery;
|
use Doctrine\ORM\AbstractQuery;
|
||||||
use Doctrine\ORM\Query;
|
use Doctrine\ORM\Query;
|
||||||
|
|
@ -93,14 +94,14 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
|
||||||
return htmlspecialchars($this->amountFormatter->format($context->getQuantity(), $context->getPart()->getPartUnit()));
|
return htmlspecialchars($this->amountFormatter->format($context->getQuantity(), $context->getPart()->getPartUnit()));
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
->add('partId', TextColumn::class, [
|
->add('partId', TextColumn::class, [
|
||||||
'label' => $this->translator->trans('project.bom.part_id'),
|
'label' => $this->translator->trans('project.bom.part_id'),
|
||||||
'visible' => true,
|
'visible' => true,
|
||||||
'orderField' => 'part.id',
|
'orderField' => 'part.id',
|
||||||
'render' => function ($value, ProjectBOMEntry $context) {
|
'render' => function ($value, ProjectBOMEntry $context) {
|
||||||
return $context->getPart() instanceof Part ? (string) $context->getPart()->getId() : '';
|
return $context->getPart() instanceof Part ? (string) $context->getPart()->getId() : '';
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
->add('name', TextColumn::class, [
|
->add('name', TextColumn::class, [
|
||||||
'label' => $this->translator->trans('part.table.name'),
|
'label' => $this->translator->trans('part.table.name'),
|
||||||
'orderField' => 'NATSORT(part.name)',
|
'orderField' => 'NATSORT(part.name)',
|
||||||
|
|
@ -161,7 +162,7 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
|
||||||
'label' => $this->translator->trans('part.table.manufacturingStatus'),
|
'label' => $this->translator->trans('part.table.manufacturingStatus'),
|
||||||
'data' => static fn(ProjectBOMEntry $context): ?ManufacturingStatus => $context->getPart()?->getManufacturingStatus(),
|
'data' => static fn(ProjectBOMEntry $context): ?ManufacturingStatus => $context->getPart()?->getManufacturingStatus(),
|
||||||
'orderField' => 'part.manufacturing_status',
|
'orderField' => 'part.manufacturing_status',
|
||||||
'class' => ManufacturingStatus::class,
|
'class' => ManufacturingStatus::class,
|
||||||
'render' => function (?ManufacturingStatus $status, ProjectBOMEntry $context): string {
|
'render' => function (?ManufacturingStatus $status, ProjectBOMEntry $context): string {
|
||||||
if ($status === null) {
|
if ($status === null) {
|
||||||
return '';
|
return '';
|
||||||
|
|
@ -212,7 +213,7 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
|
||||||
'visible' => false,
|
'visible' => false,
|
||||||
'render' => function ($value, ProjectBOMEntry $context) {
|
'render' => function ($value, ProjectBOMEntry $context) {
|
||||||
$price = $this->projectBuildHelper->getEntryUnitPrice($context);
|
$price = $this->projectBuildHelper->getEntryUnitPrice($context);
|
||||||
return $this->moneyFormatter->format($price->toScale(2, RoundingMode::UP)->toFloat(), null, 2, true);
|
return $this->moneyFormatter->format($price->toScale(2, RoundingMode::Up)->toFloat(), null, 2, true);
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
->add('ext_price', TextColumn::class, [
|
->add('ext_price', TextColumn::class, [
|
||||||
|
|
@ -221,7 +222,8 @@ class ProjectBomEntriesDataTable implements DataTableTypeInterface
|
||||||
'render' => function ($value, ProjectBOMEntry $context) {
|
'render' => function ($value, ProjectBOMEntry $context) {
|
||||||
$price = $this->projectBuildHelper->getEntryUnitPrice($context);
|
$price = $this->projectBuildHelper->getEntryUnitPrice($context);
|
||||||
return $this->moneyFormatter->format(
|
return $this->moneyFormatter->format(
|
||||||
$price->multipliedBy($context->getQuantity())->toScale(2, RoundingMode::UP)->toFloat(),
|
$price->multipliedBy(BigDecimal::fromFloatShortest($context->getQuantity()))
|
||||||
|
->toScale(2, RoundingMode::Up)->toFloat(),
|
||||||
null,
|
null,
|
||||||
2,
|
2,
|
||||||
true
|
true
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ class BigDecimalType extends Type
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return BigDecimal::of($value);
|
return BigDecimal::of(is_float($value) ? BigDecimal::fromFloatShortest($value) : $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ class Currency extends AbstractStructuralDBElement
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return BigDecimal::one()->dividedBy($tmp, $tmp->getScale(), RoundingMode::HALF_UP);
|
return BigDecimal::one()->dividedBy($tmp, $tmp->getScale(), RoundingMode::HalfUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -195,10 +195,10 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface
|
||||||
#[SerializedName('price_per_unit')]
|
#[SerializedName('price_per_unit')]
|
||||||
public function getPricePerUnit(float|string|BigDecimal $multiplier = 1.0): BigDecimal
|
public function getPricePerUnit(float|string|BigDecimal $multiplier = 1.0): BigDecimal
|
||||||
{
|
{
|
||||||
$tmp = BigDecimal::of($multiplier);
|
$tmp = is_float($multiplier) ? BigDecimal::fromFloatShortest($multiplier) : BigDecimal::of($multiplier);
|
||||||
$tmp = $tmp->multipliedBy($this->price);
|
$tmp = $tmp->multipliedBy($this->price);
|
||||||
|
|
||||||
return $tmp->dividedBy($this->price_related_quantity, static::PRICE_PRECISION, RoundingMode::HALF_UP);
|
return $tmp->dividedBy(BigDecimal::fromFloatShortest($this->price_related_quantity), static::PRICE_PRECISION, RoundingMode::HalfUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -317,7 +317,7 @@ class Pricedetail extends AbstractDBElement implements TimeStampableInterface
|
||||||
*/
|
*/
|
||||||
public function setPrice(BigDecimal $new_price): self
|
public function setPrice(BigDecimal $new_price): self
|
||||||
{
|
{
|
||||||
$tmp = $new_price->toScale(self::PRICE_PRECISION, RoundingMode::HALF_UP);
|
$tmp = $new_price->toScale(self::PRICE_PRECISION, RoundingMode::HalfUp);
|
||||||
//Only change the object, if the value changes, so that doctrine does not detect it as changed.
|
//Only change the object, if the value changes, so that doctrine does not detect it as changed.
|
||||||
if ((string) $tmp !== (string) $this->price) {
|
if ((string) $tmp !== (string) $this->price) {
|
||||||
$this->price = $tmp;
|
$this->price = $tmp;
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,16 @@ class BigDecimalMoneyType extends AbstractType implements DataTransformerInterfa
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return BigDecimal::of($value);
|
if ($value instanceof BigDecimal) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
if (is_float($value)) {
|
||||||
|
return BigDecimal::fromFloatShortest($value);
|
||||||
|
}
|
||||||
|
if (is_string($value)) {
|
||||||
|
return BigDecimal::of($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(sprintf('Expected a string, float or BigDecimal, got %s', get_debug_type($value)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,17 @@ class BigDecimalNumberType extends AbstractType implements DataTransformerInterf
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return BigDecimal::of($value);
|
if ($value instanceof BigDecimal) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
if (is_float($value)) {
|
||||||
|
return BigDecimal::fromFloatShortest($value);
|
||||||
|
}
|
||||||
|
if (is_string($value)) {
|
||||||
|
return BigDecimal::of($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \InvalidArgumentException(sprintf('Expected a string, float or BigDecimal, got %s', get_debug_type($value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -286,7 +286,7 @@ class PKPartImporter
|
||||||
//Partkeepr stores the price per item, we need to convert it to the price per packaging unit
|
//Partkeepr stores the price per item, we need to convert it to the price per packaging unit
|
||||||
$price_per_item = BigDecimal::of($partdistributor['price']);
|
$price_per_item = BigDecimal::of($partdistributor['price']);
|
||||||
$packaging_unit = (float) ($partdistributor['packagingUnit'] ?? 1);
|
$packaging_unit = (float) ($partdistributor['packagingUnit'] ?? 1);
|
||||||
$pricedetail->setPrice($price_per_item->multipliedBy($packaging_unit));
|
$pricedetail->setPrice($price_per_item->multipliedBy(BigDecimal::fromFloatShortest($packaging_unit)));
|
||||||
$pricedetail->setPriceRelatedQuantity($packaging_unit);
|
$pricedetail->setPriceRelatedQuantity($packaging_unit);
|
||||||
//We have to set the minimum discount quantity to the packaging unit (PartKeepr does not know this concept)
|
//We have to set the minimum discount quantity to the packaging unit (PartKeepr does not know this concept)
|
||||||
//But in Part-DB the minimum discount qty have to be unique across a orderdetail
|
//But in Part-DB the minimum discount qty have to be unique across a orderdetail
|
||||||
|
|
|
||||||
|
|
@ -222,7 +222,7 @@ class TimeTravel
|
||||||
if (isset($metadata->fieldMappings[$field])) {
|
if (isset($metadata->fieldMappings[$field])) {
|
||||||
//We need to convert the string to a BigDecimal first
|
//We need to convert the string to a BigDecimal first
|
||||||
if (!$data instanceof BigDecimal && ('big_decimal' === $metadata->getFieldMapping($field)->type)) {
|
if (!$data instanceof BigDecimal && ('big_decimal' === $metadata->getFieldMapping($field)->type)) {
|
||||||
$data = BigDecimal::of($data);
|
$data = is_float($data) ? BigDecimal::fromFloatShortest($data) : BigDecimal::of($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$data instanceof \DateTimeInterface
|
if (!$data instanceof \DateTimeInterface
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,7 @@ class PricedetailHelper
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $avg->dividedBy($count, Pricedetail::PRICE_PRECISION, RoundingMode::HALF_UP);
|
return $avg->dividedBy($count, Pricedetail::PRICE_PRECISION, RoundingMode::HalfUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -213,6 +213,6 @@ class PricedetailHelper
|
||||||
$val_target = $val_base->multipliedBy($targetCurrency->getInverseExchangeRate());
|
$val_target = $val_base->multipliedBy($targetCurrency->getInverseExchangeRate());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $val_target->toScale(Pricedetail::PRICE_PRECISION, RoundingMode::HALF_UP);
|
return $val_target->toScale(Pricedetail::PRICE_PRECISION, RoundingMode::HalfUp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -190,7 +190,7 @@ final readonly class ProjectBuildHelper
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$has_price = true;
|
$has_price = true;
|
||||||
$total = $total->plus($unit_price->multipliedBy($entry->getQuantity())->multipliedBy($number_of_builds));
|
$total = $total->plus($unit_price->multipliedBy(BigDecimal::fromFloatShortest($entry->getQuantity()))->multipliedBy($number_of_builds));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $has_price ? $total : null;
|
return $has_price ? $total : null;
|
||||||
|
|
@ -206,7 +206,7 @@ final readonly class ProjectBuildHelper
|
||||||
if ($total === null) {
|
if ($total === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return $total->dividedBy($number_of_builds, 10, RoundingMode::HALF_UP);
|
return $total->dividedBy($number_of_builds, 10, RoundingMode::HalfUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -215,7 +215,7 @@ final readonly class ProjectBuildHelper
|
||||||
public function roundedTotalBuildPrice(Project $project, int $number_of_builds = 1, ?Currency $currency = null): ?BigDecimal
|
public function roundedTotalBuildPrice(Project $project, int $number_of_builds = 1, ?Currency $currency = null): ?BigDecimal
|
||||||
{
|
{
|
||||||
return $this->calculateTotalBuildPrice($project, $number_of_builds, $currency)
|
return $this->calculateTotalBuildPrice($project, $number_of_builds, $currency)
|
||||||
?->toScale(2, RoundingMode::UP);
|
?->toScale(2, RoundingMode::Up);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -224,7 +224,7 @@ final readonly class ProjectBuildHelper
|
||||||
public function roundedUnitBuildPrice(Project $project, int $number_of_builds = 1, ?Currency $currency = null): ?BigDecimal
|
public function roundedUnitBuildPrice(Project $project, int $number_of_builds = 1, ?Currency $currency = null): ?BigDecimal
|
||||||
{
|
{
|
||||||
return $this->calculateUnitBuildPrice($project, $number_of_builds, $currency)
|
return $this->calculateUnitBuildPrice($project, $number_of_builds, $currency)
|
||||||
?->toScale(2, RoundingMode::UP);
|
?->toScale(2, RoundingMode::Up);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -44,15 +44,15 @@ class ExchangeRateUpdater
|
||||||
try {
|
try {
|
||||||
//Try it in the direction QUOTE/BASE first, as most providers provide rates in this direction
|
//Try it in the direction QUOTE/BASE first, as most providers provide rates in this direction
|
||||||
$rate = $this->swap->latest($currency->getIsoCode().'/'.$this->localizationSettings->baseCurrency);
|
$rate = $this->swap->latest($currency->getIsoCode().'/'.$this->localizationSettings->baseCurrency);
|
||||||
$effective_rate = BigDecimal::of($rate->getValue());
|
$effective_rate = BigDecimal::fromFloatShortest($rate->getValue());
|
||||||
} catch (UnsupportedCurrencyPairException|UnsupportedExchangeQueryException $exception) {
|
} catch (UnsupportedCurrencyPairException|UnsupportedExchangeQueryException $exception) {
|
||||||
//Otherwise try to get it inverse and calculate it ourselfes, from the format "BASE/QUOTE"
|
//Otherwise try to get it inverse and calculate it ourselfes, from the format "BASE/QUOTE"
|
||||||
$rate = $this->swap->latest($this->localizationSettings->baseCurrency.'/'.$currency->getIsoCode());
|
$rate = $this->swap->latest($this->localizationSettings->baseCurrency.'/'.$currency->getIsoCode());
|
||||||
//The rate says how many quote units are worth one base unit
|
//The rate says how many quote units are worth one base unit
|
||||||
//So we need to invert it to get the exchange rate
|
//So we need to invert it to get the exchange rate
|
||||||
|
|
||||||
$rate_bd = BigDecimal::of($rate->getValue());
|
$rate_bd = BigDecimal::fromFloatShortest($rate->getValue());
|
||||||
$effective_rate = BigDecimal::one()->dividedBy($rate_bd, Currency::PRICE_SCALE, RoundingMode::HALF_UP);
|
$effective_rate = BigDecimal::one()->dividedBy($rate_bd, Currency::PRICE_SCALE, RoundingMode::HalfUp);
|
||||||
}
|
}
|
||||||
|
|
||||||
$currency->setExchangeRate($effective_rate);
|
$currency->setExchangeRate($effective_rate);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue