<?php
declare(strict_types=1);
namespace Orcamultimedia\OciPunchout\Subscriber;
use Orcamultimedia\OciPunchout\Service\OciService;
use Orcamultimedia\OciPunchout\Struct\OciCartData;
use Shopware\Core\Checkout\Cart\Event\BeforeLineItemAddedEvent;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\System\SalesChannel\Entity\SalesChannelRepository;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class LineItemAddedSubscriber implements EventSubscriberInterface
{
public const CONFIG_KEYS = [
'extcategoryid',
'leadtime',
'manufactmat',
'manufactcode',
'matgroup',
'matnr',
'unit',
'unitmapping',
'vendor',
'extschematype',
'customfield1',
'customfield2',
'customfield3',
'customfield4',
'customfield5',
'orderitemid',
'extproductcatalogid',
'descriptionlong',
'currencyid',
'tax',
'classificationid',
'classificationgroupid',
'deliverydate',
'sourceproductcatalogid',
'previewurl',
'ordertext',
'gwg',
'kontierung',
'standard'
];
private SalesChannelRepository $productRepository;
private OciService $ociService;
private SystemConfigService $systemConfigService;
public function __construct(
SalesChannelRepository $productRepository,
OciService $ociService,
SystemConfigService $systemConfigService
)
{
$this->productRepository = $productRepository;
$this->ociService = $ociService;
$this->systemConfigService = $systemConfigService;
}
public static function getSubscribedEvents(): array
{
return [
BeforeLineItemAddedEvent::class => 'addPayloadValues',
];
}
public function addPayloadValues(BeforeLineItemAddedEvent $event): void
{
if (!$this->ociService->isPunchout()) {
return;
}
$lineItem = $event->getLineItem();
$context = $event->getSalesChannelContext();
$config = $this->systemConfigService->get('OciPunchout.config', $context->getSalesChannel()->getId());
// Get customer-specific configuration
$customer = $context->getCustomer();
$customerConfig = [];
if ($customer && $customer->getCustomFields()) {
foreach (self::CONFIG_KEYS as $key) {
$customerFieldKey = 'oci_' . $key; // Assuming custom fields are prefixed with 'oci_'
if (isset($customer->getCustomFields()[$customerFieldKey])) {
$customerConfig[$key] = $customer->getCustomFields()[$customerFieldKey];
}
}
}
$criteria = new Criteria([$lineItem->getReferencedId()]);
$criteria->addAssociation('manufacturer');
$criteria->addAssociation('categories');
$product = $this->productRepository->search($criteria, $context)->first();
$extensionData = new OciCartData();
if ($product) {
$cartDataArray = $this->getCartDataArray($config, $customerConfig, $product);
$this->mapUnitField($cartDataArray, $lineItem, $config, $customerConfig);
$extensionData->assign($cartDataArray);
}
$lineItem->addExtension(OciCartData::EXTENSION_NAME, $extensionData);
}
private function getCartDataArray(array $config, array $customerConfig, $product): array
{
$cartDataArray = [];
foreach (self::CONFIG_KEYS as $key) {
if($key === 'unitmapping') {
continue;
}
// Use customer-specific configuration if available
$configValue = $customerConfig[$key] ?? $config[$key] ?? '';
// Split the configValue into multiple attributes if it contains pipes
if (str_contains($configValue, '|')) {
$attributes = explode('|', $configValue);
foreach ($attributes as $attribute) {
$data = $this->fetchProductAttributeData($attribute, $product);
if ($data !== null) {
$cartDataArray[$key] = $data;
break; // Break the loop once data is found
}
}
} else {
// Handle single attribute or static value
$cartDataArray[$key] = $this->fetchProductAttributeData($configValue, $product);
}
// If configuration of leadtime is an attribute
if ($key === 'leadtime') {
$deliveryTime = $product->getDeliveryTime();
$minimumLeadTime = $this->systemConfigService->get('OciPunchout.config.minimumLeadTime');
$maximumLeadTime = $this->systemConfigService->get('OciPunchout.config.maximumLeadTime');
$threshold = $this->systemConfigService->get('OciPunchout.config.leadtimeStockThreshold');
if (str_starts_with($configValue, '@')){
$stock = $cartDataArray[$key];
if ($stock && is_numeric($stock) && $minimumLeadTime && $maximumLeadTime && $threshold !== null) {
$cartDataArray[$key] = $stock > $threshold ? $minimumLeadTime : $maximumLeadTime;
}
}elseif ($deliveryTime && !is_numeric($cartDataArray[$key])) {
$unit = strtolower($deliveryTime->getUnit());
$maxTime = (int)$deliveryTime->getMax();
# If maximum time is 0, use $maximumLeadTime if set
if ($maxTime === 0 && $maximumLeadTime) {
$maxTime = (int)$maximumLeadTime;
}
$conversionFactor = match($unit) {
'hour' => 1 / 24,
'day' => 1,
'week' => 7,
'month' => 30,
'year' => 365,
default => 1,
};
$cartDataArray[$key] = round($maxTime * $conversionFactor);
}
}
}
$cartDataArray['description'] = $product->getDescription();
return $cartDataArray;
}
private function fetchProductAttributeData($attribute, $product)
{
if (str_starts_with($attribute, '@')) {
$attribute = str_replace('@', '', $attribute);
if (str_starts_with($attribute, 'manufacturer.')) {
return $this->fetchManufacturerData($attribute, $product);
} elseif (str_starts_with($attribute, 'categories.')) {
return $this->fetchCategoryData($attribute, $product);
} else {
return $this->fetchCustomFieldOrProductData($attribute, $product);
}
} else {
return $attribute; // Static value
}
}
private function fetchManufacturerData($attribute, $product)
{
$manufacturer = $product->getManufacturer();
if (!$manufacturer) {
return null;
}
$attribute = str_replace('manufacturer.', '', $attribute);
return $manufacturer->getVars()[$attribute] ?? $manufacturer->getCustomFields()[$attribute] ?? null;
}
private function fetchCategoryData($attribute, $product)
{
$categories = $product->getCategories();
if (!$categories) {
return null;
}
$attribute = str_replace('categories.', '', $attribute);
$firstCategory = $categories->first();
return $firstCategory ? $firstCategory->getVars()[$attribute] ?? $firstCategory->getCustomFields()[$attribute] ?? null : null;
}
private function fetchCustomFieldOrProductData($attribute, $product)
{
$value = $product->getCustomFields()[$attribute] ?? $product->getVars()[$attribute] ?? null;
if ($value === null && is_array($product->getTranslated())) {
$translatedCustomFields = $product->getTranslated()['customFields'] ?? null;
if (is_array($translatedCustomFields) && isset($translatedCustomFields[$attribute])) {
$value = $translatedCustomFields[$attribute];
}
}
return $value;
}
private function mapUnitField(array &$cartDataArray, $lineItem, array $config, array $customerConfig): void
{
// Check if customer has a specific unit mapping
$unitMapping = $customerConfig['unitmapping'] ?? $config['unitmapping'] ?? null;
if ($unitMapping) {
$mapping = json_decode($unitMapping, true);
if (!is_array($mapping)) {
$mapping = $this->parseUnitMapping($unitMapping);
}
$normalizedMapping = array_change_key_case($mapping, CASE_LOWER);
$unit = $cartDataArray['unit'] ?? $lineItem->getPayload()['unit'] ?? null;
if ($unit instanceof \Shopware\Core\System\Unit\UnitEntity) {
$unitCode = $unit->getShortCode() ?? $unit->getName() ?? null;
} else {
$unitCode = is_string($unit) ? $unit : null;
}
if ($unitCode && isset($mapping[$unitCode])) {
$normalizedUnit = strtolower($unitCode);
if (isset($normalizedMapping[$normalizedUnit])) {
$cartDataArray['unit'] = $normalizedMapping[$normalizedUnit];
}
}
}
}
private function parseUnitMapping(string $unitMapping): array
{
$mapping = [];
$lines = explode("\n", trim($unitMapping));
$pattern = '/\s*(.*?)\s*(?:=>|→|->|:|=|-|>)\s*(.*)\s*/i';
foreach ($lines as $line) {
if (preg_match($pattern, $line, $matches) && count($matches) === 3) {
$fromUnit = trim($matches[1]);
$toUnit = trim($matches[2]);
if (!empty($fromUnit) && !empty($toUnit)) {
$mapping[$fromUnit] = $toUnit;
}
}
}
return $mapping;
}
}