Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/controllers/ShippingCategoriesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,16 @@ public function actionSave(): ?Response
$shippingCategory->default = (bool)$this->request->getBodyParam('default');

// Set the new product types
$postedProductTypes = $this->request->getBodyParam('productTypes', []) ?: [];
$productTypes = [];
foreach ($postedProductTypes as $productTypeId) {
if ($productTypeId && $productType = Plugin::getInstance()->getProductTypes()->getProductTypeById($productTypeId)) {
$productTypes[] = $productType;
// If this is the default category, it should be available to all product types
if ($shippingCategory->default) {
$productTypes = Plugin::getInstance()->getProductTypes()->getAllProductTypes();
} else {
$postedProductTypes = $this->request->getBodyParam('productTypes', []) ?: [];
$productTypes = [];
foreach ($postedProductTypes as $productTypeId) {
if ($productTypeId && $productType = Plugin::getInstance()->getProductTypes()->getProductTypeById($productTypeId)) {
$productTypes[] = $productType;
}
}
}
$shippingCategory->setProductTypes($productTypes);
Expand Down
36 changes: 36 additions & 0 deletions src/services/ShippingCategories.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,42 @@ public function saveShippingCategory(ShippingCategory $shippingCategory, bool $r
// Newly set product types this shipping category is available to
$newProductTypeIds = ArrayHelper::getColumn($shippingCategory->getProductTypes(), 'id');

// Find product types that are being removed from this shipping category
$removedProductTypeIds = array_diff($currentProductTypeIds, $newProductTypeIds);

// Update purchasables to default shipping category when product types are removed
if (!empty($removedProductTypeIds)) {
$defaultShippingCategory = $this->getDefaultShippingCategory($shippingCategory->storeId);

// Get all variant purchasables that currently have this shipping category but whose product type is being removed
$purchasableIds = (new Query())
->select(['ps.purchasableId'])
->from(['ps' => Table::PURCHASABLES_STORES])
->innerJoin(['v' => Table::VARIANTS], '[[ps.purchasableId]] = [[v.id]]')
->innerJoin(['p' => Table::PRODUCTS], '[[v.primaryOwnerId]] = [[p.id]]')
->where([
'ps.shippingCategoryId' => $shippingCategory->id,
'ps.storeId' => $shippingCategory->storeId,
'p.typeId' => $removedProductTypeIds,
])
->column();

if (!empty($purchasableIds)) {
// Update these purchasables to use the default shipping category
Craft::$app->getDb()->createCommand()
->update(
Table::PURCHASABLES_STORES,
['shippingCategoryId' => $defaultShippingCategory->id],
[
'purchasableId' => $purchasableIds,
'storeId' => $shippingCategory->storeId,
'shippingCategoryId' => $shippingCategory->id,
]
)
->execute();
}
}

foreach ($currentProductTypeIds as $oldProductTypeId) {
// If we are removing a product type for this shipping category the products of that type should be re-saved
if (!in_array($oldProductTypeId, $newProductTypeIds, false)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,17 +43,27 @@
{% set warning = "" %}
{% endif %}

{% set isDefault = shippingCategory is defined and shippingCategory.default %}
{% set productTypeValues = isDefault ? productTypesOptions|keys : (shippingCategory is defined ? shippingCategory.productTypeIds : []) %}

{{ forms.checkboxSelectField({
label: "Available to Product Types"|t('commerce'),
instructions: "Which product types should this category be available to?"|t('commerce'),
instructions: isDefault ? "The default shipping category is automatically available to all product types."|t('commerce') : "Which product types should this category be available to?"|t('commerce'),
warning: warning,
id: 'productTypes',
name: 'productTypes',
options: productTypesOptions,
values: shippingCategory is defined ? shippingCategory.productTypeIds : [],
values: productTypeValues,
showAllOption: false,
disabled: isDefault,
}) }}

{% if isDefault %}
{% for productTypeId in productTypesOptions|keys %}
{{ hiddenInput('productTypes[]', productTypeId) }}
{% endfor %}
{% endif %}

{% set defaultInput %}
{{ forms.lightswitchField({
instructions: "This category will be used as the default for all purchasables in this store."|t('commerce'),
Expand All @@ -80,3 +90,48 @@
new Craft.HandleGenerator('#{{ nameId }}', '#{{ handleId }}');
{% endjs %}
{% endif %}

{% set defaultSwitchId = 'default'|namespaceInputId|e('js') %}
{% set productTypesId = 'productTypes'|namespaceInputId|e('js') %}
{% js %}
(function() {
const defaultSwitch = document.getElementById('{{ defaultSwitchId }}');
const productTypesContainer = document.getElementById('{{ productTypesId }}');

if (!defaultSwitch || !productTypesContainer) return;

function updateProductTypes() {
const isDefault = defaultSwitch.classList.contains('on');
const checkboxes = productTypesContainer.querySelectorAll('input[type="checkbox"]');

checkboxes.forEach(checkbox => {
if (isDefault) {
checkbox.checked = true;
checkbox.disabled = true;
} else {
checkbox.disabled = false;
}
});

// Update hidden inputs
const existingHiddenInputs = productTypesContainer.parentElement.querySelectorAll('input[type="hidden"][name="productTypes[]"]');
existingHiddenInputs.forEach(input => input.remove());

if (isDefault) {
checkboxes.forEach(checkbox => {
const hiddenInput = document.createElement('input');
hiddenInput.type = 'hidden';
hiddenInput.name = 'productTypes[]';
hiddenInput.value = checkbox.value;
productTypesContainer.parentElement.appendChild(hiddenInput);
});
}
}

// Listen for lightswitch changes
defaultSwitch.addEventListener('change', updateProductTypes);

// Also listen for the custom lightswitch event
$(defaultSwitch).on('change', updateProductTypes);
})();
{% endjs %}
Loading