diff --git a/app/Controllers/CatalogController.php b/app/Controllers/CatalogController.php
index f2f4ff8..5c65e1e 100644
--- a/app/Controllers/CatalogController.php
+++ b/app/Controllers/CatalogController.php
@@ -84,8 +84,8 @@ final class CatalogController
['title' => 'Каталог', 'url' => '/catalog'],
];
- if (!empty($product['category_id'])) {
- $breadcrumbs = $categoryRepository->breadcrumbs((int) $product['category_id']);
+ if (!empty($product['display_category_id'] ?? $product['category_id'] ?? null)) {
+ $breadcrumbs = $categoryRepository->breadcrumbs((int) ($product['display_category_id'] ?? $product['category_id']));
}
$breadcrumbs[] = ['title' => (string) $product['name']];
diff --git a/app/Repositories/ProductRepository.php b/app/Repositories/ProductRepository.php
index 8e85349..3fe5a9f 100644
--- a/app/Repositories/ProductRepository.php
+++ b/app/Repositories/ProductRepository.php
@@ -50,13 +50,21 @@ final class ProductRepository extends BaseRepository
$placeholders = implode(',', array_fill(0, count($categoryIds), '?'));
$sql = $this->previewSelectSql(
'AND (
- p.category_id IN (' . $placeholders . ')
- OR EXISTS (
+ EXISTS (
SELECT 1
FROM product_category_links pcl
WHERE pcl.product_id = p.id
AND pcl.category_id IN (' . $placeholders . ')
)
+ OR (
+ p.category_id IN (' . $placeholders . ')
+ AND (p.legacy_path IS NULL OR p.legacy_path NOT LIKE \'/katalog/product/view/1/%\')
+ AND NOT EXISTS (
+ SELECT 1
+ FROM product_category_links pcl_any
+ WHERE pcl_any.product_id = p.id
+ )
+ )
)
ORDER BY p.sort_order, p.name',
$limit
@@ -74,9 +82,23 @@ final class ProductRepository extends BaseRepository
public function findBySlug(string $slug): ?array
{
$statement = $this->pdo()->prepare(
- 'SELECT p.*, c.name AS category_name, c.slug AS category_slug
+ 'SELECT
+ p.*,
+ COALESCE(linked_c.id, c.id) AS display_category_id,
+ COALESCE(linked_c.name, c.name) AS category_name,
+ COALESCE(linked_c.slug, c.slug) AS category_slug
FROM products p
LEFT JOIN categories c ON c.id = p.category_id
+ LEFT JOIN categories linked_c
+ ON linked_c.id = (
+ SELECT pcl.category_id
+ FROM product_category_links pcl
+ INNER JOIN categories lc ON lc.id = pcl.category_id
+ WHERE pcl.product_id = p.id
+ AND lc.is_active = 1
+ ORDER BY lc.parent_id IS NULL, pcl.sort_order, pcl.category_id
+ LIMIT 1
+ )
WHERE p.slug = :slug
AND p.is_published = 1
AND p.is_available = 1
diff --git a/public/admin/import-old-site.php b/public/admin/import-old-site.php
index 223400f..41a0c95 100644
--- a/public/admin/import-old-site.php
+++ b/public/admin/import-old-site.php
@@ -170,6 +170,28 @@ function execute_old_site_import_batch(string $sqlPath, string $progressPath, ar
];
}
+function normalize_old_site_product_categories(PDO $pdo): void
+{
+ $pdo->exec(
+ "UPDATE products p
+ SET p.category_id = (
+ SELECT pcl.category_id
+ FROM product_category_links pcl
+ INNER JOIN categories c ON c.id = pcl.category_id
+ WHERE pcl.product_id = p.id
+ AND c.is_active = 1
+ ORDER BY c.parent_id IS NULL, pcl.sort_order, pcl.category_id
+ LIMIT 1
+ )
+ WHERE p.legacy_path LIKE '/katalog/product/view/1/%'
+ AND EXISTS (
+ SELECT 1
+ FROM product_category_links pcl_exists
+ WHERE pcl_exists.product_id = p.id
+ )"
+ );
+}
+
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
try {
$action = (string) ($_POST['action'] ?? 'start');
@@ -186,6 +208,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$progress = execute_old_site_import_batch($sqlPath, $progressPath, $progress);
if (!empty($progress['done'])) {
+ normalize_old_site_product_categories(Database::pdo());
$message = 'Импорт выполнен. SQL-команд выполнено: ' . (int) $progress['executed'] . '.';
} else {
$message = 'Импорт продолжается: ' . (float) $progress['percent'] . '%, команд за этот шаг: ' . (int) $progress['batch_executed'] . '.';
diff --git a/public/assets/css/app.css b/public/assets/css/app.css
index 5c276fd..991514e 100644
--- a/public/assets/css/app.css
+++ b/public/assets/css/app.css
@@ -1007,7 +1007,13 @@ a:hover {
font-weight: 700;
}
-.product-grid,
+.product-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+ gap: 16px;
+ align-items: stretch;
+}
+
.admin-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
@@ -1024,6 +1030,13 @@ a:hover {
box-shadow: 0 10px 24px rgba(22, 60, 43, 0.05);
}
+.product-card {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+}
+
.product-card-image {
display: block;
overflow: hidden;
@@ -1044,6 +1057,23 @@ a:hover {
transform: scale(1.025);
}
+.product-card-image-placeholder {
+ display: grid;
+ place-items: center;
+ aspect-ratio: 4 / 3;
+ color: var(--brand-green);
+ font-weight: 900;
+ background:
+ linear-gradient(135deg, rgba(23, 61, 44, 0.08), rgba(196, 61, 49, 0.08)),
+ #eef4ea;
+}
+
+.product-card-image-placeholder span {
+ display: inline-flex;
+ border-bottom: 2px solid var(--brand-red);
+ padding-bottom: 3px;
+}
+
.product-card p {
margin: 0 0 10px;
font-size: 14px;
@@ -1051,15 +1081,33 @@ a:hover {
}
.product-card .product-category {
+ display: flex;
+ align-items: flex-start;
+ overflow: hidden;
margin-bottom: 8px;
font-size: 12px;
+ line-height: 1.2;
font-weight: 700;
text-transform: uppercase;
color: #7a856f;
}
+.product-card h3 {
+ display: -webkit-box;
+ overflow: hidden;
+ min-height: 50px;
+ margin-bottom: 12px;
+ line-height: 1.22;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+}
+
.price-line {
- display: block;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 4px 6px;
+ align-items: baseline;
+ min-height: 22px;
margin-bottom: 6px;
font-size: 14px;
color: #2d372f;
@@ -1067,25 +1115,58 @@ a:hover {
.package-price-line {
position: relative;
- display: flex;
- flex-wrap: wrap;
- gap: 4px 7px;
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto 22px;
+ gap: 6px;
align-items: center;
+ min-height: 30px;
+ border-top: 1px solid #e3e8de;
+ padding-top: 10px;
+ font-size: 14px;
+}
+
+.package-price-label {
+ min-width: 0;
+ color: #2d372f;
+}
+
+.package-price-values {
+ display: inline-flex;
+ gap: 6px;
+ align-items: baseline;
+ justify-content: flex-end;
+ min-width: 0;
+ color: var(--brand-green);
+ white-space: nowrap;
+}
+
+.package-price-note {
+ display: block;
+ margin: 3px 0 8px;
+ font-size: 12px;
+ line-height: 1.25;
+ color: var(--muted);
}
.old-price {
display: inline-block;
- margin-right: 8px;
+ margin-right: 0;
font-size: 13px;
font-weight: 400;
color: #8b9485;
text-decoration: line-through;
+ white-space: nowrap;
}
.variant-choice {
display: grid;
+ align-self: start;
gap: 7px;
- margin-top: 12px;
+ min-height: 76px;
+ margin: 0 0 12px;
+ border-top: 0;
+ border-bottom: 1px solid #edf1e9;
+ padding-bottom: 10px;
}
.variant-choice > span {
@@ -1098,20 +1179,27 @@ a:hover {
display: flex;
flex-wrap: wrap;
gap: 6px;
+ align-items: flex-start;
+ min-height: 32px;
}
.variant-choice-button {
appearance: none;
+ max-width: 100%;
+ box-sizing: border-box;
min-height: 30px;
border: 1px solid #cfd8c9;
border-radius: 7px;
padding: 6px 9px;
font: inherit;
- font-size: 13px;
+ font-size: 12px;
+ line-height: 1.15;
font-weight: 800;
color: var(--brand-green);
background: #ffffff;
cursor: pointer;
+ overflow-wrap: anywhere;
+ transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}
.variant-choice-button:hover,
@@ -1123,11 +1211,12 @@ a:hover {
.add-to-cart-button {
appearance: none;
+ align-self: end;
width: 100%;
min-height: 40px;
border: 0;
border-radius: 8px;
- margin-top: 14px;
+ margin-top: 0;
padding: 10px 12px;
font: inherit;
font-size: 14px;
@@ -1141,11 +1230,58 @@ a:hover {
background: #102f21;
}
+.product-card-actions {
+ display: grid;
+ grid-template-columns: minmax(96px, 0.44fr) minmax(0, 1fr);
+ gap: 8px;
+ align-items: stretch;
+ min-height: 40px;
+ margin-top: auto;
+}
+
+.quantity-stepper {
+ display: grid;
+ grid-template-columns: 30px minmax(32px, 1fr) 30px;
+ min-width: 0;
+ overflow: hidden;
+ border: 1px solid #cfd8c9;
+ border-radius: 8px;
+ background: #ffffff;
+}
+
+.quantity-stepper button,
+.quantity-stepper input {
+ min-width: 0;
+ border: 0;
+ font: inherit;
+ font-size: 14px;
+ font-weight: 900;
+ text-align: center;
+ color: var(--brand-green);
+ background: transparent;
+}
+
+.quantity-stepper button {
+ cursor: pointer;
+}
+
+.quantity-stepper button:hover {
+ color: #ffffff;
+ background: var(--brand-green);
+}
+
+.quantity-stepper input {
+ border-inline: 1px solid #e3e8de;
+ padding: 0 3px;
+}
+
.price-tier-trigger {
appearance: none;
- display: inline-grid;
+ display: none;
place-items: center;
+ justify-self: end;
width: 20px;
+ min-width: 20px;
height: 20px;
border: 1px solid #b9c6b1;
border-radius: 50%;
@@ -1153,11 +1289,16 @@ a:hover {
font: inherit;
font-size: 12px;
font-weight: 900;
+ line-height: 1;
color: var(--brand-green);
background: #ffffff;
cursor: help;
}
+body[data-payment-mode="cash"] .price-tier-trigger {
+ display: inline-grid;
+}
+
.cart-link span {
display: inline-grid;
place-items: center;
@@ -1174,9 +1315,9 @@ a:hover {
.price-tier-popover {
position: absolute;
- left: 16px;
right: 16px;
- bottom: 58px;
+ bottom: 62px;
+ left: 16px;
z-index: 8;
display: grid;
gap: 9px;
@@ -1188,20 +1329,17 @@ a:hover {
box-shadow: 0 18px 44px rgba(23, 61, 44, 0.18);
opacity: 0;
visibility: hidden;
- transform: translateY(6px);
pointer-events: none;
+ transform: translateY(6px);
transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s ease;
}
-.product-card {
- position: relative;
-}
-
-.product-card:hover .price-tier-popover,
-.product-card:focus-within .price-tier-popover {
+body[data-payment-mode="cash"] .package-price-line:has(.price-tier-trigger:hover) + .price-tier-popover,
+body[data-payment-mode="cash"] .price-tier-popover:hover {
opacity: 1;
visibility: visible;
transform: translateY(0);
+ pointer-events: auto;
}
.price-tier-popover strong {
@@ -1218,7 +1356,7 @@ a:hover {
.price-tier-popover div {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
- gap: 10px;
+ gap: 8px;
align-items: baseline;
}
@@ -1226,6 +1364,7 @@ a:hover {
font-size: 12px;
line-height: 1.25;
color: var(--muted);
+ white-space: nowrap;
}
.price-tier-popover dd {
@@ -1320,7 +1459,7 @@ a:hover {
.variant-row {
display: grid;
- grid-template-columns: minmax(0, 1.2fr) minmax(130px, 0.7fr) minmax(110px, 0.55fr) minmax(120px, 0.55fr);
+ grid-template-columns: minmax(0, 1.2fr) minmax(130px, 0.7fr) minmax(110px, 0.55fr) minmax(220px, 0.75fr);
gap: 12px;
align-items: center;
border: 1px solid var(--line);
@@ -1329,6 +1468,10 @@ a:hover {
background: #f8faf5;
}
+.variant-row .product-card-actions {
+ margin-top: 0;
+}
+
.variant-row b {
color: var(--brand-green);
text-align: right;
@@ -2338,6 +2481,10 @@ a:hover {
grid-template-columns: 1fr;
}
+ .product-card-actions {
+ grid-template-columns: 1fr;
+ }
+
.variant-row b {
text-align: left;
}
diff --git a/views/catalog/product.php b/views/catalog/product.php
index 671f10e..a8fe4e5 100644
--- a/views/catalog/product.php
+++ b/views/catalog/product.php
@@ -62,7 +62,14 @@ $mainImage = $product['main_image_path'] ?? '';
= e(money_label($variant['package_price'])) ?>
- В корзину
+
+
+ -
+
+ +
+
+
В корзину
+
diff --git a/views/layouts/main.php b/views/layouts/main.php
index fa4fca6..f9c3923 100644
--- a/views/layouts/main.php
+++ b/views/layouts/main.php
@@ -266,8 +266,7 @@ try {
const tierValues = (base) => ({
base: base,
cash20: base * 0.95,
- cash50: base * 0.93,
- invoice: base * 1.08
+ cash50: base * 0.93
});
const updateDynamicPrices = (scope) => {
@@ -287,7 +286,7 @@ try {
};
const updateCardPrices = (card) => {
- const packageNode = card.querySelector('[data-package-price]');
+ const packageNode = card.querySelector('[data-dynamic-package-price]');
updateDynamicPrices(card);
if (packageNode) {
@@ -297,12 +296,10 @@ try {
const tierBase = card.querySelector('[data-tier-base]');
const tierCash20 = card.querySelector('[data-tier-cash-20]');
const tierCash50 = card.querySelector('[data-tier-cash-50]');
- const tierInvoice = card.querySelector('[data-tier-invoice]');
if (tierBase) tierBase.textContent = formatRub(tiers.base);
if (tierCash20) tierCash20.textContent = formatRub(tiers.cash20);
if (tierCash50) tierCash50.textContent = formatRub(tiers.cash50);
- if (tierInvoice) tierInvoice.textContent = formatRub(tiers.invoice);
}
};
@@ -378,6 +375,8 @@ try {
const updateModeButtons = () => {
const payment = getPaymentMode();
+ document.body.dataset.paymentMode = payment;
+
document.querySelectorAll('[data-payment-mode]').forEach((button) => {
button.classList.toggle('is-active', button.dataset.paymentMode === payment);
});
@@ -425,6 +424,37 @@ try {
window.RybStockPricing.setPaymentMode(modeButton.dataset.paymentMode || 'cash');
});
+ document.addEventListener('click', (event) => {
+ const quantityButton = event.target.closest('[data-quantity-minus], [data-quantity-plus]');
+
+ if (!quantityButton) {
+ return;
+ }
+
+ const control = quantityButton.closest('[data-quantity-control]');
+ const input = control?.querySelector('[data-quantity-input]');
+
+ if (!input) {
+ return;
+ }
+
+ const current = Math.max(1, parseInt(input.value || '1', 10) || 1);
+ const next = quantityButton.matches('[data-quantity-plus]')
+ ? current + 1
+ : Math.max(1, current - 1);
+ input.value = String(next);
+ });
+
+ document.addEventListener('input', (event) => {
+ const input = event.target.closest('[data-quantity-input]');
+
+ if (!input) {
+ return;
+ }
+
+ input.value = String(Math.max(1, parseInt(input.value.replace(/\D/g, '') || '1', 10) || 1));
+ });
+
document.addEventListener('click', (event) => {
const addButton = event.target.closest('.add-to-cart-button');
@@ -435,19 +465,21 @@ try {
const productScope = addButton.closest('.price-aware');
const row = addButton.closest('.variant-row');
const variantButton = productScope?.querySelector('.variant-choice-button.is-active');
- const packagePriceNode = row?.querySelector('[data-dynamic-package-price]') || productScope?.querySelector('[data-package-price]');
+ const packagePriceNode = row?.querySelector('[data-dynamic-package-price]') || productScope?.querySelector('[data-dynamic-package-price]');
+ const quantityInput = row?.querySelector('[data-quantity-input]') || productScope?.querySelector('[data-quantity-input]');
if (!productScope || !packagePriceNode) {
return;
}
+ const addQuantity = Math.max(1, parseInt(quantityInput?.value || '1', 10) || 1);
const variantId = row?.dataset.variantId || variantButton?.dataset.variantId || 'default';
const key = String(productScope.dataset.productId || '') + ':' + variantId;
const items = getCartItems();
const existing = items.find((item) => item.key === key);
if (existing) {
- existing.quantity = Number(existing.quantity || 0) + 1;
+ existing.quantity = Number(existing.quantity || 0) + addQuantity;
} else {
items.push({
key,
@@ -459,7 +491,7 @@ try {
variantName: row?.dataset.variantName || variantButton?.dataset.variantName || 'Фасовка',
basePackagePrice: Number(packagePriceNode.dataset.basePackagePrice || 0),
baseUnitPrice: Number(row?.dataset.baseUnitPrice || variantButton?.dataset.baseUnitPrice || 0),
- quantity: 1
+ quantity: addQuantity
});
}
@@ -526,15 +558,23 @@ try {
});
const unitNode = card.querySelector('[data-unit-label]');
- const unitPriceNode = card.querySelector('[data-price-unit]');
- const packagePriceNode = card.querySelector('[data-package-price]');
- const oldPriceNode = card.querySelector('[data-old-price]');
+ const unitPriceNode = card.querySelector('[data-dynamic-unit-price]');
+ const packagePriceNode = card.querySelector('[data-dynamic-package-price]');
+ const packageLabelNode = card.querySelector('[data-package-label-display]');
+ const packageLabelNoteNode = card.querySelector('[data-package-label-note]');
+ const unitLabelNoteNode = card.querySelector('[data-unit-label-note]');
+ const oldPriceNode = card.querySelector('[data-old-package-price-display]');
+ const oldUnitPriceNode = card.querySelector('[data-old-unit-price-display]');
if (unitNode) {
unitNode.textContent = button.dataset.unit || 'ед.';
}
- if (unitPriceNode && button.dataset.priceUnit) {
+ if (unitLabelNoteNode) {
+ unitLabelNoteNode.textContent = button.dataset.unit || 'ед.';
+ }
+
+ if (unitPriceNode) {
unitPriceNode.dataset.baseUnitPrice = button.dataset.baseUnitPrice || '';
}
@@ -542,9 +582,26 @@ try {
packagePriceNode.dataset.basePackagePrice = button.dataset.basePackagePrice || '';
}
+ if (packageLabelNode) {
+ packageLabelNode.textContent = button.dataset.variantPackageLabel || button.dataset.variantName || 'Фасовка';
+ }
+
+ if (packageLabelNoteNode) {
+ packageLabelNoteNode.textContent = button.dataset.variantPackageLabel || button.dataset.variantName || 'Фасовка';
+ }
+
if (oldPriceNode) {
- oldPriceNode.textContent = button.dataset.oldPrice || '';
- oldPriceNode.hidden = !button.dataset.oldPrice;
+ const oldPackagePrice = button.dataset.oldPrice || '';
+ oldPriceNode.textContent = oldPackagePrice;
+ oldPriceNode.hidden = oldPackagePrice === '';
+ }
+
+ if (oldUnitPriceNode) {
+ const oldUnitPrice = Number(button.dataset.oldUnitPrice || 0);
+ const baseUnitPrice = Number(button.dataset.baseUnitPrice || 0);
+ const hasOldUnitPrice = oldUnitPrice > baseUnitPrice;
+ oldUnitPriceNode.textContent = hasOldUnitPrice ? formatRub(oldUnitPrice) : '';
+ oldUnitPriceNode.hidden = !hasOldUnitPrice;
}
updateCardPrices(card);
diff --git a/views/partials/product-card.php b/views/partials/product-card.php
index 132d528..e36ae4a 100644
--- a/views/partials/product-card.php
+++ b/views/partials/product-card.php
@@ -9,11 +9,26 @@ $activeVariant = $variants[0] ?? [
'unit' => $product['unit'] ?? '',
'price_per_unit' => $product['price_per_unit'] ?? null,
'package_price' => $product['package_price'] ?? null,
+ 'package_quantity' => $product['package_quantity'] ?? 0,
'old_package_price' => $product['old_package_price'] ?? null,
];
$cardId = 'product-card-' . (int) ($product['id'] ?? 0);
$activePackagePrice = (float) ($activeVariant['package_price'] ?? 0);
$activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $activeVariant['price_per_unit'];
+$activePackageQuantity = (float) ($activeVariant['package_quantity'] ?? 0);
+$activeVariantOldPackagePrice = $activeVariant['old_package_price'] ?? null;
+$activeOldPackagePrice = (
+ $activeVariantOldPackagePrice !== null
+ && (float) $activeVariantOldPackagePrice > $activePackagePrice
+)
+ ? (float) $activeVariantOldPackagePrice
+ : null;
+$activeOldUnitPrice = ($activeOldPackagePrice !== null && $activePackageQuantity > 0 && $activeUnitPrice !== null)
+ ? $activeOldPackagePrice / $activePackageQuantity
+ : null;
+if ($activeOldUnitPrice !== null && $activeOldUnitPrice <= $activeUnitPrice) {
+ $activeOldUnitPrice = null;
+}
?>
+
+
+ Рыбсток
+
= e($product['category_display_name'] ?? $product['category_name'] ?? 'Каталог') ?>
-
- Цена за = e(unit_label((string) $activeVariant['unit'])) ?> : = e(money_label($activeVariant['price_per_unit'])) ?>
-
-
-
-
- >= e(money_label($activeVariant['old_package_price'] ?? null)) ?>
- За упаковку: = e(money_label($activeVariant['package_price'])) ?>
- ?
-
-
-
-
-
Варианты цены
-
-
Базовая цена = e(money_label($activePackagePrice)) ?>
-
Цена при сумме заказа от 20 000р. наличными = e(money_label(floor($activePackagePrice * 0.95))) ?>
-
Цена при сумме заказа от 50 000р. наличными = e(money_label(floor($activePackagePrice * 0.93))) ?>
-
Оплата по расчетному счету = e(money_label(floor($activePackagePrice * 1.08))) ?>
-
-
-
-
Фасовка:
+
Доступные фасовки:
$variant): ?>
+ $variantPackagePrice
+ )
+ ? (float) $variant['old_package_price']
+ : null;
+ $variantOldUnitPrice = ($variantOldPackagePrice !== null && $variantPackageQuantity > 0 && $variantUnitPrice !== null)
+ ? $variantOldPackagePrice / $variantPackageQuantity
+ : null;
+ if ($variantOldUnitPrice !== null && $variantOldUnitPrice <= $variantUnitPrice) {
+ $variantOldUnitPrice = null;
+ }
+ ?>
= e((string) $variant['name']) ?>
@@ -79,5 +95,43 @@ $activeUnitPrice = $activeVariant['price_per_unit'] === null ? null : (float) $a
-
В корзину
+
+
+ Цена за = e(unit_label((string) $activeVariant['unit'])) ?> :
+ >= e(money_label($activeOldUnitPrice)) ?>
+ = e(money_label($activeVariant['price_per_unit'])) ?>
+
+
+
+
+
+ За фасовку = e((string) $activeVariant['name']) ?> :
+
+ >= e(money_label($activeOldPackagePrice)) ?>
+ = e(money_label($activeVariant['package_price'])) ?>
+
+ ?
+
+
+
Цена за = e(unit_label((string) $activeVariant['unit'])) ?> × = e((string) $activeVariant['name']) ?>
+
+
+
+
+
Варианты цены за наличный расчет:
+
+
Базовая цена = e(money_label($activePackagePrice)) ?>
+
При заказе от 20 000р. = e(money_label(floor($activePackagePrice * 0.95))) ?>
+
При заказе от 50 000р. = e(money_label(floor($activePackagePrice * 0.93))) ?>
+
+
+
+
+
+ -
+
+ +
+
+
В корзину
+
diff --git a/ПЛАН_ДЕЙСТВИЙ_РЫБСТОК.md b/ПЛАН_ДЕЙСТВИЙ_РЫБСТОК.md
index f4c9367..e0179db 100644
--- a/ПЛАН_ДЕЙСТВИЙ_РЫБСТОК.md
+++ b/ПЛАН_ДЕЙСТВИЙ_РЫБСТОК.md
@@ -383,7 +383,7 @@
15. Нужно ли сохранять расчет доставки в заказе или только показывать клиенту предварительно?
16. Нужен ли ручной пересчет доставки администратором в заказе?
17. Как учитывать подъем 390 руб.: отдельной галочкой в корзине или только текстом?
-18. Нужен ли выбор даты доставки в календаре?
+18. Нужен ли выбор даты доставки в календаре..S?
19. Нужно ли запрещать выбор выходных и праздников в календаре?
20. Где брать производственный календарь РФ: встроенный список, API или ручная настройка в админке?
21. Какой должен быть внешний стиль: более премиальный, складской/оптовый, домашний семейный или нейтральный продуктовый?