Skip to content

Commit

Permalink
[shopsys] out of stock products behavior (#3587)
Browse files Browse the repository at this point in the history
  • Loading branch information
vitek-rostislav authored Dec 2, 2024
2 parents f8df890 + 14f8478 commit 35d0daa
Show file tree
Hide file tree
Showing 47 changed files with 708 additions and 328 deletions.
5 changes: 4 additions & 1 deletion assets/styles/admin/libs/tooltip/tooltip-custom.less
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@

.tooltip-inner {
min-width: 120px;
}
white-space: normal;
word-break: break-word;
overflow-wrap: break-word;
}
1 change: 1 addition & 0 deletions src/Component/Plugin/PluginCrudExtensionRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class PluginCrudExtensionRegistry
protected const KNOWN_TYPES = [
'product',
'category',
'stockSettings',
];

/**
Expand Down
43 changes: 26 additions & 17 deletions src/Component/Setting/Setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,24 @@

class Setting
{
public const PERSONAL_DATA_DISPLAY_SITE_CONTENT = 'personalDataDisplaySiteContent';
public const PERSONAL_DATA_EXPORT_SITE_CONTENT = 'personalDataExportSiteContent';
public const DEFAULT_PRICING_GROUP = 'defaultPricingGroupId';
public const TERMS_AND_CONDITIONS_ARTICLE_ID = 'termsAndConditionsArticleId';
public const PRIVACY_POLICY_ARTICLE_ID = 'privacyPolicyArticleId';
public const USER_CONSENT_POLICY_ARTICLE_ID = 'userConsentPolicyArticleId';
public const DOMAIN_DATA_CREATED = 'domainDataCreated';
public const FEED_HASH = 'feedHash';
public const DEFAULT_UNIT = 'defaultUnitId';
public const BASE_URL = 'baseUrl';
public const FEED_NAME_TO_CONTINUE = 'feedNameToContinue';
public const FEED_DOMAIN_ID_TO_CONTINUE = 'feedDomainIdToContinue';
public const FEED_ITEM_ID_TO_CONTINUE = 'feedItemIdToContinue';
public const TRANSFER_DAYS_BETWEEN_STOCKS = 'transferDaysBetweenStocks';
public const IMAGE_STRUCTURE_MIGRATED_FOR_PROXY = 'imageStructureMigratedForProxy';
public const CUSTOMER_USER_DEFAULT_GROUP_ROLE_ID = 'customerUserDefaultGroupRoleId';
public const FILE_STRUCTURE_MIGRATED_FOR_RELATIONS = 'fileStructureMigratedForRelations';
public const string PERSONAL_DATA_DISPLAY_SITE_CONTENT = 'personalDataDisplaySiteContent';
public const string PERSONAL_DATA_EXPORT_SITE_CONTENT = 'personalDataExportSiteContent';
public const string DEFAULT_PRICING_GROUP = 'defaultPricingGroupId';
public const string TERMS_AND_CONDITIONS_ARTICLE_ID = 'termsAndConditionsArticleId';
public const string PRIVACY_POLICY_ARTICLE_ID = 'privacyPolicyArticleId';
public const string USER_CONSENT_POLICY_ARTICLE_ID = 'userConsentPolicyArticleId';
public const string DOMAIN_DATA_CREATED = 'domainDataCreated';
public const string FEED_HASH = 'feedHash';
public const string DEFAULT_UNIT = 'defaultUnitId';
public const string BASE_URL = 'baseUrl';
public const string FEED_NAME_TO_CONTINUE = 'feedNameToContinue';
public const string FEED_DOMAIN_ID_TO_CONTINUE = 'feedDomainIdToContinue';
public const string FEED_ITEM_ID_TO_CONTINUE = 'feedItemIdToContinue';
public const string TRANSFER_DAYS_BETWEEN_STOCKS = 'transferDaysBetweenStocks';
public const string FEED_DELIVERY_DAYS_FOR_OUT_OF_STOCK_PRODUCTS = 'feedDeliveryDaysForOutOfStockProducts';
public const string IMAGE_STRUCTURE_MIGRATED_FOR_PROXY = 'imageStructureMigratedForProxy';
public const string CUSTOMER_USER_DEFAULT_GROUP_ROLE_ID = 'customerUserDefaultGroupRoleId';
public const string FILE_STRUCTURE_MIGRATED_FOR_RELATIONS = 'fileStructureMigratedForRelations';

/**
* @var \Shopsys\FrameworkBundle\Component\Setting\SettingValue[][]
Expand Down Expand Up @@ -166,6 +167,14 @@ protected function loadDomainValues($domainId)
}
}

/**
* @param string $name
*/
public function deleteByName(string $name): void
{
$this->settingValueRepository->deleteByName($name);
}

public function clearCache()
{
$this->allValuesLoaded = false;
Expand Down
13 changes: 13 additions & 0 deletions src/Component/Setting/SettingValueRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,17 @@ public function copyAllMultidomainSettings($fromDomainId, $toDomainId)
],
);
}

/**
* @param string $name
*/
public function deleteByName(string $name): void
{
$this->getSettingValueRepository()->createQueryBuilder('sv')
->delete(SettingValue::class, 'sv')
->where('sv.name = :name')
->setParameter('name', $name)
->getQuery()
->execute();
}
}
2 changes: 1 addition & 1 deletion src/Controller/Admin/StockController.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ public function saveSettingsAction(Request $request): RedirectResponse
$this
->addSuccessFlashTwig(
t(
'Warehouse setting %domainName% saved.',
'Setting for domain "%domainName%" was saved.',
[
'%domainName%' => $this->adminDomainTabsFacade->getSelectedDomainConfig()->getName(),
],
Expand Down
4 changes: 2 additions & 2 deletions src/Form/Admin/Payment/PaymentFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
use Shopsys\FrameworkBundle\Form\GroupType;
use Shopsys\FrameworkBundle\Form\ImageUploadType;
use Shopsys\FrameworkBundle\Form\Locale\LocalizedType;
use Shopsys\FrameworkBundle\Form\MessageType;
use Shopsys\FrameworkBundle\Form\PriceAndVatTableByDomainsType;
use Shopsys\FrameworkBundle\Form\WarningMessageType;
use Shopsys\FrameworkBundle\Model\GoPay\PaymentMethod\GoPayPaymentMethod;
use Shopsys\FrameworkBundle\Model\GoPay\PaymentMethod\GoPayPaymentMethodFacade;
use Shopsys\FrameworkBundle\Model\Payment\Payment;
Expand Down Expand Up @@ -294,7 +294,7 @@ public function addHiddenByGoPayWarning(PaymentData $paymentData, FormBuilderInt
$domainNames[] = $this->domain->getDomainConfigById($domainId)->getName();
}

$builder->add('hiddenByGoPay', WarningMessageType::class, [
$builder->add('hiddenByGoPay', MessageType::class, [
'data' => t('This payment method is hidden by GoPay on domains: %domains%', [
'%domains%' => implode(', ', $domainNames),
]),
Expand Down
69 changes: 49 additions & 20 deletions src/Form/Admin/Product/ProductFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@
use Shopsys\FrameworkBundle\Form\ImageUploadType;
use Shopsys\FrameworkBundle\Form\Locale\LocalizedType;
use Shopsys\FrameworkBundle\Form\LocalizedFullWidthType;
use Shopsys\FrameworkBundle\Form\MessageType;
use Shopsys\FrameworkBundle\Form\MultiLocaleFileUploadType;
use Shopsys\FrameworkBundle\Form\ProductParameterValueType;
use Shopsys\FrameworkBundle\Form\ProductsType;
use Shopsys\FrameworkBundle\Form\Transformers\ProductParameterValueToProductParameterValuesLocalizedTransformer;
use Shopsys\FrameworkBundle\Form\Transformers\RemoveDuplicatesFromArrayTransformer;
use Shopsys\FrameworkBundle\Form\UrlListType;
use Shopsys\FrameworkBundle\Form\WarningMessageType;
use Shopsys\FrameworkBundle\Model\Category\CategoryFacade;
use Shopsys\FrameworkBundle\Model\Product\Brand\BrandFacade;
use Shopsys\FrameworkBundle\Model\Product\Flag\FlagFacade;
Expand Down Expand Up @@ -107,18 +107,39 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
];
}

$builder->add('name', LocalizedFullWidthType::class, [
'required' => false,
'entry_options' => [
'constraints' => [
new Constraints\Length(
['max' => 255, 'maxMessage' => 'Product name cannot be longer than {{ limit }} characters'],
),
$builder
->add('namePrefix', LocalizedFullWidthType::class, [
'required' => false,
'entry_options' => [
'constraints' => [
new Constraints\Length(['max' => 255, 'maxMessage' => 'Product prefix name cannot be longer than {{ limit }} characters']),
],
],
],
'label' => t('Name'),
'render_form_row' => false,
]);
'label' => t('Name prefix'),
'render_form_row' => false,
])
->add('name', LocalizedFullWidthType::class, [
'required' => false,
'entry_options' => [
'constraints' => [
new Constraints\Length(
['max' => 255, 'maxMessage' => 'Product name cannot be longer than {{ limit }} characters'],
),
],
],
'label' => t('Name'),
'render_form_row' => false,
])
->add('nameSuffix', LocalizedFullWidthType::class, [
'required' => false,
'entry_options' => [
'constraints' => [
new Constraints\Length(['max' => 255, 'maxMessage' => 'Product suffix name cannot be longer than {{ limit }} characters']),
],
],
'label' => t('Name suffix'),
'render_form_row' => false,
]);

if ($this->isProductVariant($product) || $this->isProductMainVariant($product)) {
$builder->add($this->createVariantGroup($builder, $product));
Expand All @@ -127,7 +148,7 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
$builder->add($this->createBasicInformationGroup($builder, $product, $disabledItemInMainVariantAttr));
$builder->add($this->createDisplayAvailabilityGroup($builder, $product));
$builder->add($this->createPricesGroup($builder, $product));
$builder->add($this->createStocksGroup($builder));
$builder->add($this->createStocksGroup($builder, $product));
$builder->add($this->createDescriptionsGroup($builder, $product));
$builder->add($this->createShortDescriptionsGroup($builder, $product));
$builder->add($this->createShortDescriptionsUspGroup($builder));
Expand Down Expand Up @@ -434,7 +455,7 @@ private function createDisplayAvailabilityGroup(
&& $product->getCalculatedSellingDenied()
) {
$builderDisplayAvailabilityGroup
->add('productCalculatedSellingDeniedInfo', WarningMessageType::class, [
->add('productCalculatedSellingDeniedInfo', MessageType::class, [
'data' => t('Product is excluded from the sale'),
]);
}
Expand Down Expand Up @@ -505,19 +526,27 @@ private function createDisplayAvailabilityGroup(

/**
* @param \Symfony\Component\Form\FormBuilderInterface $builder
* @param \Shopsys\FrameworkBundle\Model\Product\Product|null $product
* @return \Symfony\Component\Form\FormBuilderInterface
*/
private function createStocksGroup(FormBuilderInterface $builder)
private function createStocksGroup(FormBuilderInterface $builder, ?Product $product): FormBuilderInterface
{
$stockGroupBuilder = $builder->create('stocksGroup', GroupType::class, [
'label' => t('Warehouses'),
]);

$stockGroupBuilder->add('productStockData', CollectionType::class, [
'required' => false,
'entry_type' => ProductStockFormType::class,
'render_form_row' => false,
]);
if ($this->isProductMainVariant($product)) {
$stockGroupBuilder
->add('productStockData', DisplayOnlyType::class, [
'data' => t('The stock quantities are set for the product variants separately.'),
]);
} else {
$stockGroupBuilder->add('productStockData', CollectionType::class, [
'required' => false,
'entry_type' => ProductStockFormType::class,
'render_form_row' => false,
]);
}

return $stockGroupBuilder;
}
Expand Down
6 changes: 4 additions & 2 deletions src/Form/Admin/Stock/ProductStockFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'placeholder' => '0',
],
'constraints' => [
new Constraints\GreaterThanOrEqual(['value' => 0]),
new Constraints\Regex(['pattern' => '/^\d+$/']),
new Constraints\Regex([
'pattern' => '/^-?\d+$/',
'message' => 'Quantity must be an integer',
]),
],
]);
}
Expand Down
47 changes: 46 additions & 1 deletion src/Form/Admin/Stock/StockSettingsFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,75 @@

namespace Shopsys\FrameworkBundle\Form\Admin\Stock;

use Shopsys\FrameworkBundle\Component\Plugin\PluginCrudExtensionFacade;
use Shopsys\FrameworkBundle\Form\GroupType;
use Shopsys\FrameworkBundle\Form\MessageType;
use Shopsys\FrameworkBundle\Model\Stock\StockSettingsData;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints;
use Twig\Environment;

class StockSettingsFormType extends AbstractType
{
/**
* @param \Twig\Environment $environment
* @param \Shopsys\FrameworkBundle\Component\Plugin\PluginCrudExtensionFacade $pluginCrudExtensionFacade
*/
public function __construct(
protected readonly Environment $environment,
protected readonly PluginCrudExtensionFacade $pluginCrudExtensionFacade,
) {
}

/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
$builderStockSettingGroup = $builder->create('stockSettings', GroupType::class, [
'label' => t('Warehouse settings'),
]);

$builderStockSettingGroup
->add('transfer', TextType::class, [
'label' => t('Days for transfer between warehouses'),
'constraints' => [
new Constraints\NotBlank(),
new Constraints\Regex(['pattern' => '/^\d+$/']),
new Constraints\GreaterThanOrEqual(['value' => 0]),
],
]);

$builderFeedSettingGroup = $builder->create('feedSettings', GroupType::class, [
'label' => t('XML feeds settings'),
]);

$builderFeedSettingGroup
->add('feedDeliveryDaysForOutOfStockProducts', IntegerType::class, [
'label' => t('Number of delivery days for out of stock products in XML feeds'),
'required' => true,
'constraints' => [
new Constraints\NotNull([
'message' => 'Please enter the number of delivery days.',
]),
],
])
->add('feedDeliveryDaysForOutOfStockProductsInfo', MessageType::class, [
'message_level' => MessageType::MESSAGE_LEVEL_INFO,
'data' => $this->environment->render('@ShopsysFramework/Admin/Content/Feed/feedDeliveryDaysForOutOfStockProductsInfo.html.twig'),
]);

$builder
->add($builderStockSettingGroup)
->add($builderFeedSettingGroup)
->add('save', SubmitType::class);

$this->pluginCrudExtensionFacade->extendForm($builder, 'stockSettings', 'pluginData');
}

/**
Expand All @@ -39,6 +83,7 @@ public function configureOptions(OptionsResolver $resolver): void
$resolver
->setDefaults([
'data_class' => StockSettingsData::class,
'attr' => ['novalidate' => 'novalidate'],
]);
}
}
44 changes: 44 additions & 0 deletions src/Form/MessageType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Shopsys\FrameworkBundle\Form;

use Override;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;

class MessageType extends AbstractType
{
public const string MESSAGE_LEVEL_WARNING = 'warning';
public const string MESSAGE_LEVEL_INFO = 'info';

/**
* @param \Symfony\Component\OptionsResolver\OptionsResolver $resolver
*/
#[Override]
public function configureOptions(OptionsResolver $resolver): void
{
$resolver
->setDefined('message_level')
->setAllowedValues('message_level', [self::MESSAGE_LEVEL_WARNING, self::MESSAGE_LEVEL_INFO])
->setDefaults([
'mapped' => false,
'required' => false,
'message_level' => self::MESSAGE_LEVEL_WARNING,
]);
}

/**
* {@inheritdoc}
*/
#[Override]
public function buildView(FormView $view, FormInterface $form, array $options): void
{
parent::buildView($view, $form, $options);

$view->vars['message_level'] = $options['message_level'];
}
}
Loading

0 comments on commit 35d0daa

Please sign in to comment.