mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2026-02-02 15:39:35 +00:00
commit
ed39710f7f
6 changed files with 610 additions and 3 deletions
|
|
@ -278,6 +278,16 @@ The following env configuration options are available:
|
||||||
* `PROVIDER_BUERKLIN_CURRENCY`: The currency you want to get prices in if available (optional, 3 letter ISO-code, default: `EUR`).
|
* `PROVIDER_BUERKLIN_CURRENCY`: The currency you want to get prices in if available (optional, 3 letter ISO-code, default: `EUR`).
|
||||||
* `PROVIDER_BUERKLIN_LANGUAGE`: The language you want to get the descriptions in. Possible values: `de` = German, `en` = English. (optional, default: `en`)
|
* `PROVIDER_BUERKLIN_LANGUAGE`: The language you want to get the descriptions in. Possible values: `de` = German, `en` = English. (optional, default: `en`)
|
||||||
|
|
||||||
|
### Conrad
|
||||||
|
|
||||||
|
The conrad provider the [Conrad API](https://developer.conrad.com/) to search for parts and retried their information.
|
||||||
|
To use it you have to request access to the API, however it seems currently your mail address needs to be allowlisted before you can register for an account.
|
||||||
|
The conrad webpages uses the API key in the requests, so you might be able to extract a working API key by listening to browser requests.
|
||||||
|
That method is not officially supported nor encouraged by Part-DB, and might break at any moment.
|
||||||
|
|
||||||
|
The following env configuration options are available:
|
||||||
|
* `PROVIDER_CONRAD_API_KEY`: The API key you got from Conrad (mandatory)
|
||||||
|
|
||||||
### Custom provider
|
### Custom provider
|
||||||
|
|
||||||
To create a custom provider, you have to create a new class implementing the `InfoProviderInterface` interface. As long
|
To create a custom provider, you have to create a new class implementing the `InfoProviderInterface` interface. As long
|
||||||
|
|
|
||||||
320
src/Services/InfoProviderSystem/Providers/ConradProvider.php
Normal file
320
src/Services/InfoProviderSystem/Providers/ConradProvider.php
Normal file
|
|
@ -0,0 +1,320 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Services\InfoProviderSystem\Providers;
|
||||||
|
|
||||||
|
use App\Services\InfoProviderSystem\DTOs\FileDTO;
|
||||||
|
use App\Services\InfoProviderSystem\DTOs\ParameterDTO;
|
||||||
|
use App\Services\InfoProviderSystem\DTOs\PartDetailDTO;
|
||||||
|
use App\Services\InfoProviderSystem\DTOs\PriceDTO;
|
||||||
|
use App\Services\InfoProviderSystem\DTOs\PurchaseInfoDTO;
|
||||||
|
use App\Services\InfoProviderSystem\DTOs\SearchResultDTO;
|
||||||
|
use App\Settings\InfoProviderSystem\ConradSettings;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
readonly class ConradProvider implements InfoProviderInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
private const SEARCH_ENDPOINT = '/search/1/v3/facetSearch';
|
||||||
|
public const DISTRIBUTOR_NAME = 'Conrad';
|
||||||
|
|
||||||
|
private HttpClientInterface $httpClient;
|
||||||
|
|
||||||
|
public function __construct( HttpClientInterface $httpClient, private ConradSettings $settings)
|
||||||
|
{
|
||||||
|
//We want everything in JSON
|
||||||
|
$this->httpClient = $httpClient->withOptions([
|
||||||
|
'headers' => [
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProviderInfo(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => 'Conrad',
|
||||||
|
'description' => 'Retrieves part information from conrad.de',
|
||||||
|
'url' => 'https://www.conrad.de/',
|
||||||
|
'disabled_help' => 'Set API key in settings',
|
||||||
|
'settings_class' => ConradSettings::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProviderKey(): string
|
||||||
|
{
|
||||||
|
return 'conrad';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isActive(): bool
|
||||||
|
{
|
||||||
|
return !empty($this->settings->apiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getProductUrl(string $productId): string
|
||||||
|
{
|
||||||
|
return 'https://' . $this->settings->shopID->getDomain() . '/' . $this->settings->shopID->getLanguage() . '/p/' . $productId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFootprintFromTechnicalDetails(array $technicalDetails): ?string
|
||||||
|
{
|
||||||
|
foreach ($technicalDetails as $detail) {
|
||||||
|
if ($detail['name'] === 'ATT_LOV_HOUSING_SEMICONDUCTORS') {
|
||||||
|
return $detail['values'][0] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchByKeyword(string $keyword): array
|
||||||
|
{
|
||||||
|
$url = $this->settings->shopID->getAPIRoot() . self::SEARCH_ENDPOINT . '/'
|
||||||
|
. $this->settings->shopID->getDomainEnd() . '/' . $this->settings->shopID->getLanguage()
|
||||||
|
. '/' . $this->settings->shopID->getCustomerType();
|
||||||
|
|
||||||
|
$response = $this->httpClient->request('POST', $url, [
|
||||||
|
'query' => [
|
||||||
|
'apikey' => $this->settings->apiKey,
|
||||||
|
],
|
||||||
|
'json' => [
|
||||||
|
'query' => $keyword,
|
||||||
|
'size' => 50,
|
||||||
|
'sort' => [["field"=>"_score","order"=>"desc"]],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$out = [];
|
||||||
|
$results = $response->toArray();
|
||||||
|
|
||||||
|
foreach($results['hits'] as $result) {
|
||||||
|
|
||||||
|
$out[] = new SearchResultDTO(
|
||||||
|
provider_key: $this->getProviderKey(),
|
||||||
|
provider_id: $result['productId'],
|
||||||
|
name: $result['manufacturerId'] ?? $result['productId'],
|
||||||
|
description: $result['title'] ?? '',
|
||||||
|
manufacturer: $result['brand']['name'] ?? null,
|
||||||
|
mpn: $result['manufacturerId'] ?? null,
|
||||||
|
preview_image_url: $result['image'] ?? null,
|
||||||
|
provider_url: $this->getProductUrl($result['productId']),
|
||||||
|
footprint: $this->getFootprintFromTechnicalDetails($result['technicalDetails'] ?? []),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getFootprintFromTechnicalAttributes(array $technicalDetails): ?string
|
||||||
|
{
|
||||||
|
foreach ($technicalDetails as $detail) {
|
||||||
|
if ($detail['attributeID'] === 'ATT.LOV.HOUSING_SEMICONDUCTORS') {
|
||||||
|
return $detail['values'][0]['value'] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $technicalAttributes
|
||||||
|
* @return array<ParameterDTO>
|
||||||
|
*/
|
||||||
|
private function technicalAttributesToParameters(array $technicalAttributes): array
|
||||||
|
{
|
||||||
|
return array_map(static function (array $p) {
|
||||||
|
if (count($p['values']) === 1) { //Single value attribute
|
||||||
|
if (array_key_exists('unit', $p['values'][0])) {
|
||||||
|
return ParameterDTO::parseValueField( //With unit
|
||||||
|
name: $p['attributeName'],
|
||||||
|
value: $p['values'][0]['value'],
|
||||||
|
unit: $p['values'][0]['unit']['name'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParameterDTO::parseValueIncludingUnit(
|
||||||
|
name: $p['attributeName'],
|
||||||
|
value: $p['values'][0]['value'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($p['values']) === 2) { //Multi value attribute (e.g. min/max)
|
||||||
|
$value = $p['values'][0]['value'] ?? null;
|
||||||
|
$value2 = $p['values'][1]['value'] ?? null;
|
||||||
|
$unit = $p['values'][0]['unit']['name'] ?? '';
|
||||||
|
$unit2 = $p['values'][1]['unit']['name'] ?? '';
|
||||||
|
if ($unit === $unit2 && is_numeric($value) && is_numeric($value2)) {
|
||||||
|
if (array_key_exists('unit', $p['values'][0])) { //With unit
|
||||||
|
return new ParameterDTO(
|
||||||
|
name: $p['attributeName'],
|
||||||
|
value_min: (float)$value,
|
||||||
|
value_max: (float)$value2,
|
||||||
|
unit: $unit,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ParameterDTO(
|
||||||
|
name: $p['attributeName'],
|
||||||
|
value_min: (float)$value,
|
||||||
|
value_max: (float)$value2,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback implementation
|
||||||
|
$values = implode(", ", array_map(fn($q) =>
|
||||||
|
array_key_exists('unit', $q) ? $q['value']." ". ($q['unit']['name'] ?? $q['unit']) : $q['value']
|
||||||
|
, $p['values']));
|
||||||
|
return ParameterDTO::parseValueIncludingUnit(
|
||||||
|
name: $p['attributeName'],
|
||||||
|
value: $values,
|
||||||
|
);
|
||||||
|
}, $technicalAttributes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array $productMedia
|
||||||
|
* @return array<FileDTO>
|
||||||
|
*/
|
||||||
|
public function productMediaToDatasheets(array $productMedia): array
|
||||||
|
{
|
||||||
|
$files = [];
|
||||||
|
foreach ($productMedia['manuals'] as $manual) {
|
||||||
|
//Filter out unwanted languages
|
||||||
|
if (!empty($this->settings->attachmentLanguageFilter) && !in_array($manual['language'], $this->settings->attachmentLanguageFilter, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$files[] = new FileDTO($manual['fullUrl'], $manual['title'] . ' (' . $manual['language'] . ')');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries prices for a given product ID. It makes a POST request to the Conrad API
|
||||||
|
* @param string $productId
|
||||||
|
* @return PurchaseInfoDTO
|
||||||
|
*/
|
||||||
|
private function queryPrices(string $productId): PurchaseInfoDTO
|
||||||
|
{
|
||||||
|
$priceQueryURL = $this->settings->shopID->getAPIRoot() . '/price-availability/4/'
|
||||||
|
. $this->settings->shopID->getShopID() . '/facade';
|
||||||
|
|
||||||
|
$response = $this->httpClient->request('POST', $priceQueryURL, [
|
||||||
|
'query' => [
|
||||||
|
'apikey' => $this->settings->apiKey,
|
||||||
|
'overrideCalculationSchema' => $this->settings->includeVAT ? 'GROSS' : 'NET'
|
||||||
|
],
|
||||||
|
'json' => [
|
||||||
|
'ns:inputArticleItemList' => [
|
||||||
|
"#namespaces" => [
|
||||||
|
"ns" => "http://www.conrad.de/ccp/basit/service/article/priceandavailabilityservice/api"
|
||||||
|
],
|
||||||
|
'articles' => [
|
||||||
|
[
|
||||||
|
"articleID" => $productId,
|
||||||
|
"calculatePrice" => true,
|
||||||
|
"checkAvailability" => true,
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$result = $response->toArray();
|
||||||
|
|
||||||
|
$priceInfo = $result['priceAndAvailabilityFacadeResponse']['priceAndAvailability']['price'] ?? [];
|
||||||
|
$price = $priceInfo['price'] ?? "0.0";
|
||||||
|
$currency = $priceInfo['currency'] ?? "EUR";
|
||||||
|
$includesVat = $priceInfo['isGrossAmount'] === "true" ?? true;
|
||||||
|
$minOrderAmount = $result['priceAndAvailabilityFacadeResponse']['priceAndAvailability']['availabilityStatus']['minimumOrderQuantity'] ?? 1;
|
||||||
|
|
||||||
|
$prices = [];
|
||||||
|
foreach ($priceInfo['priceScale'] ?? [] as $priceScale) {
|
||||||
|
$prices[] = new PriceDTO(
|
||||||
|
minimum_discount_amount: max($priceScale['scaleFrom'], $minOrderAmount),
|
||||||
|
price: (string)$priceScale['pricePerUnit'],
|
||||||
|
currency_iso_code: $currency,
|
||||||
|
includes_tax: $includesVat
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (empty($prices)) { //Fallback if no price scales are defined
|
||||||
|
$prices[] = new PriceDTO(
|
||||||
|
minimum_discount_amount: $minOrderAmount,
|
||||||
|
price: (string)$price,
|
||||||
|
currency_iso_code: $currency,
|
||||||
|
includes_tax: $includesVat
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PurchaseInfoDTO(
|
||||||
|
distributor_name: self::DISTRIBUTOR_NAME,
|
||||||
|
order_number: $productId,
|
||||||
|
prices: $prices,
|
||||||
|
product_url: $this->getProductUrl($productId)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDetails(string $id): PartDetailDTO
|
||||||
|
{
|
||||||
|
$productInfoURL = $this->settings->shopID->getAPIRoot() . '/product/1/service/' . $this->settings->shopID->getShopID()
|
||||||
|
. '/product/' . $id;
|
||||||
|
|
||||||
|
$response = $this->httpClient->request('GET', $productInfoURL, [
|
||||||
|
'query' => [
|
||||||
|
'apikey' => $this->settings->apiKey,
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$data = $response->toArray();
|
||||||
|
|
||||||
|
return new PartDetailDTO(
|
||||||
|
provider_key: $this->getProviderKey(),
|
||||||
|
provider_id: $data['shortProductNumber'],
|
||||||
|
name: $data['productFullInformation']['manufacturer']['name'] ?? $data['productFullInformation']['manufacturer']['id'] ?? $data['shortProductNumber'],
|
||||||
|
description: $data['productShortInformation']['title'] ?? '',
|
||||||
|
category: $data['productShortInformation']['articleGroupName'] ?? null,
|
||||||
|
manufacturer: $data['brand']['displayName'] !== null ? preg_replace("/[\u{2122}\u{00ae}]/", "", $data['brand']['displayName']) : null, //Replace ™ and ® symbols
|
||||||
|
mpn: $data['productFullInformation']['manufacturer']['id'] ?? null,
|
||||||
|
preview_image_url: $data['productShortInformation']['mainImage']['imageUrl'] ?? null,
|
||||||
|
provider_url: $this->getProductUrl($data['shortProductNumber']),
|
||||||
|
footprint: $this->getFootprintFromTechnicalAttributes($data['productFullInformation']['technicalAttributes'] ?? []),
|
||||||
|
notes: $data['productFullInformation']['description'] ?? null,
|
||||||
|
datasheets: $this->productMediaToDatasheets($data['productMedia'] ?? []),
|
||||||
|
parameters: $this->technicalAttributesToParameters($data['productFullInformation']['technicalAttributes'] ?? []),
|
||||||
|
vendor_infos: [$this->queryPrices($data['shortProductNumber'])]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCapabilities(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
ProviderCapabilities::BASIC,
|
||||||
|
ProviderCapabilities::PICTURE,
|
||||||
|
ProviderCapabilities::DATASHEET,
|
||||||
|
ProviderCapabilities::PRICE,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/Settings/InfoProviderSystem/ConradSettings.php
Normal file
77
src/Settings/InfoProviderSystem/ConradSettings.php
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Settings\InfoProviderSystem;
|
||||||
|
|
||||||
|
use App\Form\Type\APIKeyType;
|
||||||
|
use App\Settings\SettingsIcon;
|
||||||
|
use Jbtronics\SettingsBundle\Metadata\EnvVarMode;
|
||||||
|
use Jbtronics\SettingsBundle\ParameterTypes\ArrayType;
|
||||||
|
use Jbtronics\SettingsBundle\ParameterTypes\StringType;
|
||||||
|
use Jbtronics\SettingsBundle\Settings\Settings;
|
||||||
|
use Jbtronics\SettingsBundle\Settings\SettingsParameter;
|
||||||
|
use Jbtronics\SettingsBundle\Settings\SettingsTrait;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\CountryType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\EnumType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\LanguageType;
|
||||||
|
use Symfony\Component\Translation\TranslatableMessage as TM;
|
||||||
|
use Symfony\Component\Validator\Constraints as Assert;
|
||||||
|
|
||||||
|
#[Settings(label: new TM("settings.ips.conrad"))]
|
||||||
|
#[SettingsIcon("fa-plug")]
|
||||||
|
class ConradSettings
|
||||||
|
{
|
||||||
|
use SettingsTrait;
|
||||||
|
|
||||||
|
#[SettingsParameter(label: new TM("settings.ips.element14.apiKey"),
|
||||||
|
formType: APIKeyType::class,
|
||||||
|
formOptions: ["help_html" => true], envVar: "PROVIDER_CONRAD_API_KEY", envVarMode: EnvVarMode::OVERWRITE)]
|
||||||
|
public ?string $apiKey = null;
|
||||||
|
|
||||||
|
#[SettingsParameter(label: new TM("settings.ips.conrad.shopID"),
|
||||||
|
description: new TM("settings.ips.conrad.shopID.description"),
|
||||||
|
formType: EnumType::class,
|
||||||
|
formOptions: ['class' => ConradShopIDs::class],
|
||||||
|
)]
|
||||||
|
public ConradShopIDs $shopID = ConradShopIDs::COM_B2B;
|
||||||
|
|
||||||
|
#[SettingsParameter(label: new TM("settings.ips.reichelt.include_vat"))]
|
||||||
|
public bool $includeVAT = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array|string[] Only attachments in these languages will be downloaded (ISO 639-1 codes)
|
||||||
|
*/
|
||||||
|
#[Assert\Unique()]
|
||||||
|
#[Assert\All([new Assert\Language()])]
|
||||||
|
#[SettingsParameter(type: ArrayType::class,
|
||||||
|
label: new TM("settings.ips.conrad.attachment_language_filter"), description: new TM("settings.ips.conrad.attachment_language_filter.description"),
|
||||||
|
options: ['type' => StringType::class],
|
||||||
|
formType: LanguageType::class,
|
||||||
|
formOptions: [
|
||||||
|
'multiple' => true,
|
||||||
|
'preferred_choices' => ['en', 'de', 'fr', 'it', 'cs', 'da', 'nl', 'hu', 'hr', 'sk', 'pl']
|
||||||
|
],
|
||||||
|
)]
|
||||||
|
public array $attachmentLanguageFilter = ['en'];
|
||||||
|
}
|
||||||
167
src/Settings/InfoProviderSystem/ConradShopIDs.php
Normal file
167
src/Settings/InfoProviderSystem/ConradShopIDs.php
Normal file
|
|
@ -0,0 +1,167 @@
|
||||||
|
<?php
|
||||||
|
/*
|
||||||
|
* This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 - 2026 Jan Böhmer (https://github.com/jbtronics)
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
|
||||||
|
namespace App\Settings\InfoProviderSystem;
|
||||||
|
|
||||||
|
use Symfony\Contracts\Translation\TranslatableInterface;
|
||||||
|
use Symfony\Contracts\Translation\TranslatorInterface;
|
||||||
|
|
||||||
|
enum ConradShopIDs: string implements TranslatableInterface
|
||||||
|
{
|
||||||
|
case COM_B2B = 'HP_COM_B2B';
|
||||||
|
case DE_B2B = 'CQ_DE_B2B';
|
||||||
|
case DE_B2C = 'CQ_DE_B2C';
|
||||||
|
case AT_B2C = 'CQ_AT_B2C';
|
||||||
|
case CH_B2C_DE = 'CQ_CH_B2C_DE';
|
||||||
|
case CH_B2C_FR = 'CQ_CH_B2C_FR';
|
||||||
|
case SE_B2B = 'HP_SE_B2B';
|
||||||
|
case HU_B2C = 'CQ_HU_B2C';
|
||||||
|
case CZ_B2B = 'HP_CZ_B2B';
|
||||||
|
case SI_B2B = 'HP_SI_B2B';
|
||||||
|
case SK_B2B = 'HP_SK_B2B';
|
||||||
|
case BE_B2B = 'HP_BE_B2B';
|
||||||
|
case PL_B2B = 'HP_PL_B2B';
|
||||||
|
case NL_B2B = 'CQ_NL_B2B';
|
||||||
|
case NL_B2C = 'CQ_NL_B2C';
|
||||||
|
case DK_B2B = 'HP_DK_B2B';
|
||||||
|
case IT_B2B = 'HP_IT_B2B';
|
||||||
|
|
||||||
|
case FR_B2B = 'HP_FR_B2B';
|
||||||
|
case AT_B2B = 'CQ_AT_B2B';
|
||||||
|
case HR_B2B = 'HP_HR_B2B';
|
||||||
|
|
||||||
|
|
||||||
|
public function trans(TranslatorInterface $translator, ?string $locale = null): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::DE_B2B => "conrad.de (B2B)",
|
||||||
|
self::AT_B2C => "conrad.at (B2C)",
|
||||||
|
self::CH_B2C_DE => "conrad.ch DE (B2C)",
|
||||||
|
self::CH_B2C_FR => "conrad.ch FR (B2C)",
|
||||||
|
self::SE_B2B => "conrad.se (B2B)",
|
||||||
|
self::HU_B2C => "conrad.hu (B2C)",
|
||||||
|
self::CZ_B2B => "conrad.cz (B2B)",
|
||||||
|
self::SI_B2B => "conrad.si (B2B)",
|
||||||
|
self::SK_B2B => "conrad.sk (B2B)",
|
||||||
|
self::BE_B2B => "conrad.be (B2B)",
|
||||||
|
self::DE_B2C => "conrad.de (B2C)",
|
||||||
|
self::PL_B2B => "conrad.pl (B2B)",
|
||||||
|
self::NL_B2B => "conrad.nl (B2B)",
|
||||||
|
self::DK_B2B => "conradelektronik.dk (B2B)",
|
||||||
|
self::IT_B2B => "conrad.it (B2B)",
|
||||||
|
self::NL_B2C => "conrad.nl (B2C)",
|
||||||
|
self::FR_B2B => "conrad.fr (B2B)",
|
||||||
|
self::COM_B2B => "conrad.com (B2B)",
|
||||||
|
self::AT_B2B => "conrad.at (B2B)",
|
||||||
|
self::HR_B2B => "conrad.hr (B2B)",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDomain(): string
|
||||||
|
{
|
||||||
|
if ($this === self::DK_B2B) {
|
||||||
|
return 'conradelektronik.dk';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'conrad.' . $this->getDomainEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the API root URL for this shop ID. e.g. https://api.conrad.de
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getAPIRoot(): string
|
||||||
|
{
|
||||||
|
return 'https://api.' . $this->getDomain();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the shop ID value used in the API requests. e.g. 'CQ_DE_B2B'
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getShopID(): string
|
||||||
|
{
|
||||||
|
if ($this === self::CH_B2C_FR || $this === self::CH_B2C_DE) {
|
||||||
|
return 'CQ_CH_B2C';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDomainEnd(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::DE_B2B, self::DE_B2C => 'de',
|
||||||
|
self::AT_B2B, self::AT_B2C => 'at',
|
||||||
|
self::CH_B2C_DE => 'ch', self::CH_B2C_FR => 'ch',
|
||||||
|
self::SE_B2B => 'se',
|
||||||
|
self::HU_B2C => 'hu',
|
||||||
|
self::CZ_B2B => 'cz',
|
||||||
|
self::SI_B2B => 'si',
|
||||||
|
self::SK_B2B => 'sk',
|
||||||
|
self::BE_B2B => 'be',
|
||||||
|
self::PL_B2B => 'pl',
|
||||||
|
self::NL_B2B, self::NL_B2C => 'nl',
|
||||||
|
self::DK_B2B => 'dk',
|
||||||
|
self::IT_B2B => 'it',
|
||||||
|
self::FR_B2B => 'fr',
|
||||||
|
self::COM_B2B => 'com',
|
||||||
|
self::HR_B2B => 'hr',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLanguage(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::DE_B2B, self::DE_B2C, self::AT_B2B, self::AT_B2C => 'de',
|
||||||
|
self::CH_B2C_DE => 'de', self::CH_B2C_FR => 'fr',
|
||||||
|
self::SE_B2B => 'sv',
|
||||||
|
self::HU_B2C => 'hu',
|
||||||
|
self::CZ_B2B => 'cs',
|
||||||
|
self::SI_B2B => 'sl',
|
||||||
|
self::SK_B2B => 'sk',
|
||||||
|
self::BE_B2B => 'nl',
|
||||||
|
self::PL_B2B => 'pl',
|
||||||
|
self::NL_B2B, self::NL_B2C => 'nl',
|
||||||
|
self::DK_B2B => 'da',
|
||||||
|
self::IT_B2B => 'it',
|
||||||
|
self::FR_B2B => 'fr',
|
||||||
|
self::COM_B2B => 'en',
|
||||||
|
self::HR_B2B => 'hr',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the customer type for this shop ID. e.g. 'b2b' or 'b2c'
|
||||||
|
* @return string 'b2b' or 'b2c'
|
||||||
|
*/
|
||||||
|
public function getCustomerType(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::DE_B2B, self::AT_B2B, self::SE_B2B, self::CZ_B2B, self::SI_B2B,
|
||||||
|
self::SK_B2B, self::BE_B2B, self::PL_B2B, self::NL_B2B, self::DK_B2B,
|
||||||
|
self::IT_B2B, self::FR_B2B, self::COM_B2B, self::HR_B2B => 'b2b',
|
||||||
|
self::DE_B2C, self::AT_B2C, self::CH_B2C_DE, self::CH_B2C_FR, self::HU_B2C, self::NL_B2C => 'b2c',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -63,7 +63,10 @@ class InfoProviderSettings
|
||||||
|
|
||||||
#[EmbeddedSettings]
|
#[EmbeddedSettings]
|
||||||
public ?PollinSettings $pollin = null;
|
public ?PollinSettings $pollin = null;
|
||||||
|
|
||||||
#[EmbeddedSettings]
|
#[EmbeddedSettings]
|
||||||
public ?BuerklinSettings $buerklin = null;
|
public ?BuerklinSettings $buerklin = null;
|
||||||
|
|
||||||
|
#[EmbeddedSettings]
|
||||||
|
public ?ConradSettings $conrad = null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9928,13 +9928,13 @@ Element 1 -> Element 1.2]]></target>
|
||||||
<unit id="NdZ1t7a" name="project.builds.number_of_builds_possible">
|
<unit id="NdZ1t7a" name="project.builds.number_of_builds_possible">
|
||||||
<segment state="translated">
|
<segment state="translated">
|
||||||
<source>project.builds.number_of_builds_possible</source>
|
<source>project.builds.number_of_builds_possible</source>
|
||||||
<target>You have enough stocked to build <b>%max_builds%</b> builds of this [project].</target>
|
<target><![CDATA[You have enough stocked to build <b>%max_builds%</b> builds of this [project].]]></target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="iuSpPbg" name="project.builds.check_project_status">
|
<unit id="iuSpPbg" name="project.builds.check_project_status">
|
||||||
<segment state="translated">
|
<segment state="translated">
|
||||||
<source>project.builds.check_project_status</source>
|
<source>project.builds.check_project_status</source>
|
||||||
<target>The current [project] status is <b>"%project_status%"</b>. You should check if you really want to build the [project] with this status!</target>
|
<target><![CDATA[The current [project] status is <b>"%project_status%"</b>. You should check if you really want to build the [project] with this status!]]></target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
<unit id="Y7vSSxi" name="project.builds.following_bom_entries_miss_instock_n">
|
<unit id="Y7vSSxi" name="project.builds.following_bom_entries_miss_instock_n">
|
||||||
|
|
@ -14286,5 +14286,35 @@ Buerklin-API Authentication server:
|
||||||
<target>Transport error while retrieving information from the providers. Check that your server has internet accesss. See server logs for more info.</target>
|
<target>Transport error while retrieving information from the providers. Check that your server has internet accesss. See server logs for more info.</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
|
<unit id="kHKChQB" name="settings.ips.conrad">
|
||||||
|
<segment>
|
||||||
|
<source>settings.ips.conrad</source>
|
||||||
|
<target>Conrad</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="gwZXJ0F" name="settings.ips.conrad.shopID">
|
||||||
|
<segment>
|
||||||
|
<source>settings.ips.conrad.shopID</source>
|
||||||
|
<target>Shop ID</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="honqnBf" name="settings.ips.conrad.shopID.description">
|
||||||
|
<segment>
|
||||||
|
<source>settings.ips.conrad.shopID.description</source>
|
||||||
|
<target>The version of the conrad store you wanna get results from. This determines language, prices and currency of the results. If both a B2B and a B2C version if available, you should choose the B2C version if you want prices including VAT. </target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="EVcQbEK" name="settings.ips.conrad.attachment_language_filter">
|
||||||
|
<segment>
|
||||||
|
<source>settings.ips.conrad.attachment_language_filter</source>
|
||||||
|
<target>Language filter for attachments</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="MWPmQf2" name="settings.ips.conrad.attachment_language_filter.description">
|
||||||
|
<segment>
|
||||||
|
<source>settings.ips.conrad.attachment_language_filter.description</source>
|
||||||
|
<target>Only includes attachments in the selected languages in the results.</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue