<?php declare(strict_types=1);
namespace Uandi\UandiEfbDownloadCenter\Service;
use Mpdf\Mpdf;
use Mpdf\MpdfException;
use Mpdf\Output\Destination;
use Psr\Log\LoggerInterface;
use Shopware\Core\Content\Product\ProductEntity;
use Shopware\Core\Content\Property\PropertyGroupEntity;
use Shopware\Core\Defaults;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsAnyFilter;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Uandi\EFB\Storefront\Service\VariantLoader;
use Uandi\UandiEfbErpSynchronization\Service\Document\Lists\DeclarationOfConformity;
use Uandi\UandiEfbErpSynchronization\Service\Document\Lists\UkcaDeclaration;
class PdfRenderer
{
private Environment $twig;
private EntityRepository $propertyGroupRepository;
private SystemConfigService $systemConfigService;
private DeclarationOfConformity $conformityDeclaration;
private UkcaDeclaration $ukcaDeclaration;
private VariantLoader $variantLoader;
private LoggerInterface $logger;
public function __construct(
Environment $twig,
EntityRepository $propertyGroupRepository,
SystemConfigService $systemConfigService,
DeclarationOfConformity $conformityDeclaration,
UkcaDeclaration $ukcaDeclaration,
VariantLoader $variantLoader,
LoggerInterface $logger
) {
$this->twig = $twig;
$this->propertyGroupRepository = $propertyGroupRepository;
$this->systemConfigService = $systemConfigService;
$this->conformityDeclaration = $conformityDeclaration;
$this->ukcaDeclaration = $ukcaDeclaration;
$this->variantLoader = $variantLoader;
$this->logger = $logger;
}
/**
* Render datasheet pdf for the supplied product.
*
* @param ProductEntity $product
* @param SalesChannelContext $salesChannelContext
*
* @return string|null
* @throws LoaderError
* @throws MpdfException
* @throws RuntimeError
* @throws SyntaxError
*/
public function renderDataSheetPdf(ProductEntity $product, SalesChannelContext $salesChannelContext): ?string
{
$propertyData = $this->getPropertyData($product, $salesChannelContext->getContext());
$this->variantLoader->loadVariantTable($product, $salesChannelContext);
$pdf = new Mpdf();
$datasheetTemplate = $this->twig->createTemplate($this->getConfig('uandidownloadcenterdatasheetcontent'));
$header = $this->twig->createTemplate($this->getConfig('uandidownloadcenterdatasheetheader'));
$footer = $this->twig->createTemplate($this->getConfig('uandidownloadcenterdatasheetfooter'));
$html = $this->twig->render($datasheetTemplate, ['properties' => $propertyData, 'product' => $product]);
$header = $this->twig->render($header, ['product' => $product]);
$footer = $this->twig->render($footer, ['product' => $product]);
$pdf->setAutoTopMargin = 'pad';
$pdf->setAutoBottomMargin = 'pad';
$pdf->SetHTMLHeader($header);
$pdf->SetHTMLFooter($footer);
$pdf->WriteHTML($html);
$renderedPdf = $pdf->Output('', Destination::STRING_RETURN);
if ($renderedPdf === null) {
$this->logger->error(
sprintf('%s: Product %s: Generated datasheet is empty.', __METHOD__, $product->getProductNumber())
);
}
return $renderedPdf;
}
/**
* Render declaration of conformity pdf for the supplied customer number and products.
*
* @param array $products
* @param SalesChannelContext $salesChannelContext
*
* @return bool|string|null
* @throws LoaderError
* @throws MpdfException
* @throws RuntimeError
* @throws SyntaxError
*/
public function renderConfirmityDeclarationPdf(
array $products,
SalesChannelContext $salesChannelContext
): bool|string|null {
$customer = $salesChannelContext->getCustomer();
$customerNumber = $salesChannelContext->getCustomer() ? $customer->getCustomerNumber() : '';
$productNumbers = array_map(function (ProductEntity $product) {
return $product->getProductNumber();
}, $products);
$conformityInfo = $this->conformityDeclaration->getConformityDeclarationAttributes(
$customerNumber,
$productNumbers,
$salesChannelContext
);
if (count($products) == 1) {
$product = array_pop($products);
$this->variantLoader->loadVariantTable($product, $salesChannelContext);
}
if ($conformityInfo === []) {
$this->logger->warning(
sprintf(
'%s: Product(s) %s: ERP API response is empty.',
__METHOD__,
implode(',', $productNumbers)
)
);
return false;
}
if ($conformityInfo['attributes']['ROHS'] == 0) {
$this->logger->warning(
sprintf('%s: Product(s) %s: ROHS = 0 in API response.', __METHOD__, implode(',', $productNumbers))
);
return false;
}
$pdf = new Mpdf();
$header = $this->twig->createTemplate($this->getConfig('uandidownloadcenterconformityheader'));
$footer = $this->twig->createTemplate($this->getConfig('uandidownloadcenterconformityfooter'));
$html = $this->getConfig('uandidownloadcenterconformityrohs');
if ($conformityInfo['attributes']['EMV'] == 1) {
$html .= $this->getConfig('uandidownloadcenterconformityemv');
}
if ($conformityInfo['attributes']['NSR'] == 1) {
$html .= $this->getConfig('uandidownloadcenterconformitynsr');
}
$html .= $this->getConfig('uandidownloadcenterconformityproductlist');
$header = $this->twig->render($header, []);
$footer = $this->twig->render($footer, []);
$renderedHtml = $this->twig->render(
$this->twig->createTemplate($html),
['productList' => $conformityInfo['product_list'], 'product' => $product]
);
$pdf->setAutoTopMargin = 'pad';
$pdf->setAutoBottomMargin = 'pad';
$pdf->SetHTMLHeader($header);
$pdf->SetHTMLFooter($footer);
$pdf->WriteHTML($renderedHtml);
$renderedPdf = $pdf->Output('', Destination::STRING_RETURN);
if ($renderedPdf === null) {
$this->logger->error(
sprintf(
'%s: Product(s) %s: Generated declaration of conformity is empty.',
__METHOD__,
implode(',', $productNumbers)
)
);
}
return $renderedPdf;
}
/**
* Render UKCA declaration of conformity pdf for the supplied customer number and products.
*
* @param array $products
* @param SalesChannelContext $salesChannelContext
*
* @return bool|string|null
* @throws LoaderError
* @throws MpdfException
* @throws RuntimeError
* @throws SyntaxError
*/
public function renderUkcaPdf(array $products, SalesChannelContext $salesChannelContext): bool|string|null
{
$customer = $salesChannelContext->getCustomer();
$customerNumber = $salesChannelContext->getCustomer() ? $customer->getCustomerNumber() : '';
$productNumbers = array_map(function (ProductEntity $product) {
return $product->getProductNumber();
}, $products);
$conformityInfo = $this->ukcaDeclaration->getUkcaAttributes(
$customerNumber,
$productNumbers,
$salesChannelContext
);
if (count($products) == 1) {
$product = array_pop($products);
$this->variantLoader->loadVariantTable($product, $salesChannelContext);
}
if ($conformityInfo === []) {
$this->logger->warning(
sprintf('%s: Product(s) %s: ERP API response is empty.', __METHOD__, implode(',', $productNumbers))
);
return false;
}
if ($conformityInfo['attributes']['ROHS'] == 0) {
$this->logger->warning(
sprintf('%s: Product(s) %s: ROHS = 0 in API response.', __METHOD__, implode(',', $productNumbers))
);
return false;
}
$pdf = new Mpdf();
$header = $this->twig->createTemplate($this->getConfig('uandidownloadcenterukcaheader'));
$footer = $this->twig->createTemplate($this->getConfig('uandidownloadcenterukcafooter'));
$html = $this->getConfig('uandidownloadcenterukcarohs');
if ($conformityInfo['attributes']['EMV'] == 1) {
$html .= $this->getConfig('uandidownloadcenterukcaemv');
}
if ($conformityInfo['attributes']['NSR'] == 1) {
$html .= $this->getConfig('uandidownloadcenterukcansr');
}
$html .= $this->getConfig('uandidownloadcenterukcaproductlist');
$header = $this->twig->render($header, []);
$footer = $this->twig->render($footer, []);
$renderedHtml = $this->twig->render(
$this->twig->createTemplate($html),
['productList' => $conformityInfo['product_list'], 'product' => $product]
);
$pdf->setAutoTopMargin = 'pad';
$pdf->setAutoBottomMargin = 'pad';
$pdf->SetHTMLHeader($header);
$pdf->SetHTMLFooter($footer);
$pdf->WriteHTML($renderedHtml);
$renderedPdf = $pdf->Output('', Destination::STRING_RETURN);
if ($renderedPdf === null) {
$this->logger->error(
sprintf(
'%s: Product(s) %s: Generated declaration of conformity is empty.',
__METHOD__,
implode(',', $productNumbers)
)
);
}
return $renderedPdf;
}
/**
* Read product properties into an array.
* - Format: [ 'Group Name' => [ propertyKey => propertyValue, ... ], ... ]
*
* @param ProductEntity $product
* @param Context $context
*
* @return array
*/
private function getPropertyData(ProductEntity $product, Context $context): array
{
$criteria = new Criteria();
$criteria->addAssociation('translations');
$criteria->addFilter(new EqualsAnyFilter('id', $product->getProperties()->getPropertyGroupIds()));
$propertyGroups = $this->propertyGroupRepository->search($criteria, $context);
$propertyGroupData = [];
/** @var PropertyGroupEntity $propertyGroup */
foreach ($propertyGroups as $propertyGroup) {
$propertyGroupData[$propertyGroup->getId()] = [
'name' => $propertyGroup->getName(),
'attributeGroup' => $this->getAttributeGroupFromDefaultStoreLanguage($propertyGroup)
];
}
$result = [];
foreach ($product->getProperties() as $property) {
$parentName = $propertyGroupData[$property->getGroupId()]['name'];
$parentAttributeGroup = $propertyGroupData[$property->getGroupId()]['attributeGroup'];
$propertyValue = $property->getName();
if (empty($parentName)) {
continue;
}
$result[$parentAttributeGroup][$parentName] = $propertyValue;
}
return $result;
}
/**
* Shortcut for reading config fields.
*
* @param string $configField
*
* @return array|bool|float|int|string|null
*/
private function getConfig(string $configField): float|int|bool|array|string|null
{
return $this->systemConfigService->get('UandiEfbDownloadCenter.config.' . $configField);
}
/**
* Get attribute group in custom field from store main language
* - custom field is saved only for main language, it is necessayr to get attribute group for other languages
*
* @param PropertyGroupEntity $propertyGroup
* @return string
*/
private function getAttributeGroupFromDefaultStoreLanguage(PropertyGroupEntity $propertyGroup): string
{
$mainLanguageTranslation = array_filter($propertyGroup->getTranslations()->getElements(), function ($k) {
return $k->getLanguageId() === Defaults::LANGUAGE_SYSTEM;
}, ARRAY_FILTER_USE_BOTH);
$translation = current($mainLanguageTranslation);
$customFields = $translation->getCustomFields() ?? [];
return $customFields['attribute_group'] ?? '';
}
}