Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -100,6 +100,7 @@ translations = {
|
|
| 100 |
'error_page_title': 'Ошибка',
|
| 101 |
'order_not_found_by_id': 'Заказ с таким ID не найден.',
|
| 102 |
'whatsapp_greeting': 'Здравствуйте! Хочу подтвердить свой заказ на emir:',
|
|
|
|
| 103 |
'whatsapp_order_number': 'Номер заказа:',
|
| 104 |
'whatsapp_order_link': 'Ссылка на заказ:',
|
| 105 |
'whatsapp_contact_request': 'Пожалуйста, свяжитесь со мной для уточнения деталей оплаты и доставки.',
|
|
@@ -205,11 +206,11 @@ translations = {
|
|
| 205 |
'flash_category_delete_success': "Категория '{category_name}' удалена.",
|
| 206 |
'flash_category_delete_failed': "Не удалось удалить категорию '{category_name}'.",
|
| 207 |
'flash_subcategory_added_success': "Подкатегория '{subcategory_name}' успешно добавлена.",
|
| 208 |
-
'flash_subcategory_name_empty': 'Название
|
| 209 |
'flash_parent_category_not_selected': 'Необходимо выбрать родительскую категорию.',
|
| 210 |
'flash_subcategory_already_exists': "Подкатегория '{subcategory_name}' уже существует в этой категории.",
|
| 211 |
'flash_subcategory_delete_success': "Подкатегория '{subcategory_name}' удалена.",
|
| 212 |
-
'flash_subcategory_delete_failed': "Не удалось удалить
|
| 213 |
'flash_supplier_added_success': "Поставщик '{supplier_name}' успешно добавлен.",
|
| 214 |
'flash_supplier_name_empty': 'Имя поставщика не может быть пустым.',
|
| 215 |
'flash_supplier_already_exists': "Поставщик '{supplier_name}' уже существует.",
|
|
@@ -238,7 +239,7 @@ translations = {
|
|
| 238 |
'flash_failed_delete_hf_files_warning': "Не удалось удалить файлы для товара '{product_name}' с сервера. Товар удален локально.",
|
| 239 |
'flash_hf_token_write_not_set_files_not_deleted_warning': "Товар '{product_name}' удален локально, но файлы не удалены с сервера (токен не задан).",
|
| 240 |
'flash_product_deleted_success': "Товар '{product_name}' удален.",
|
| 241 |
-
'flash_delete_error_not_found': "Ошибка удаления: товар с ID
|
| 242 |
'flash_unknown_action': 'Неизвестное действие: {action}',
|
| 243 |
'flash_internal_error': "Произошла внутренняя ошибка при выполнении действия '{action}'. Подробности в логе сервера.",
|
| 244 |
'flash_data_uploaded_hf_success': 'Данные успешно загружены на Hugging Face.',
|
|
@@ -255,7 +256,7 @@ translations = {
|
|
| 255 |
'flash_settings_updated': 'Настройки успешно обновлены.',
|
| 256 |
'prices_disabled_notice_catalog': 'Цены временно не отображаются. Добавьте товары в корзину для запроса стоимости.',
|
| 257 |
'prices_disabled_notice_cart': 'Цены не отображаются. Стоимость будет рассчитана после оформления заказа.',
|
| 258 |
-
'request_price_calculation_button': '
|
| 259 |
'price_on_request': 'Цена по запросу',
|
| 260 |
'video_modal_title': 'Видео о товаре',
|
| 261 |
'save_all_changes_button': 'Сохранить все изменения ({count})',
|
|
@@ -941,14 +942,18 @@ CATALOG_TEMPLATE = '''
|
|
| 941 |
<div class="product-price-container">
|
| 942 |
{% set discount = product.get('discount_percent', 0) %}
|
| 943 |
{% set original_price_kgs = product.price_kgs %}
|
| 944 |
-
{% if app_config.prices_enabled
|
| 945 |
{% if discount > 0 %}
|
| 946 |
{% set discounted_price_kgs = original_price_kgs * (100 - discount) / 100 %}
|
| 947 |
<span class="original-price">{{ "%.2f"|format(original_price_kgs) }} {{ currency_code }}</span><br>
|
| 948 |
<span class="product-price discounted">{{ "%.2f"|format(discounted_price_kgs) }} {{ currency_code }}</span>
|
| 949 |
<span class="discount-badge">{{ get_translation('discount_badge_text', discount=discount) }}</span>
|
| 950 |
{% else %}
|
| 951 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 952 |
{% endif %}
|
| 953 |
{% else %}
|
| 954 |
<div class="product-price on-request">{{ get_translation('price_on_request') }}</div>
|
|
@@ -962,9 +967,16 @@ CATALOG_TEMPLATE = '''
|
|
| 962 |
<i class="fas fa-play-circle"></i> {{ get_translation('view_video_button') }}
|
| 963 |
</button>
|
| 964 |
{% endif %}
|
| 965 |
-
|
| 966 |
-
|
| 967 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 968 |
</div>
|
| 969 |
</div>
|
| 970 |
</div>
|
|
@@ -1061,6 +1073,21 @@ CATALOG_TEMPLATE = '''
|
|
| 1061 |
return text;
|
| 1062 |
}
|
| 1063 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1064 |
function openModal(index) {
|
| 1065 |
loadProductDetails(index);
|
| 1066 |
const modal = document.getElementById('productModal');
|
|
@@ -1202,6 +1229,12 @@ CATALOG_TEMPLATE = '''
|
|
| 1202 |
if (product.discount_percent && product.discount_percent > 0) {
|
| 1203 |
priceToAdd = product.price_kgs * (100 - product.discount_percent) / 100;
|
| 1204 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1205 |
|
| 1206 |
if (existingItemIndex > -1) {
|
| 1207 |
cart[existingItemIndex].quantity += quantity;
|
|
@@ -1540,7 +1573,7 @@ PRODUCT_DETAIL_TEMPLATE = '''
|
|
| 1540 |
|
| 1541 |
{% set discount = product_info.get('discount_percent', 0) %}
|
| 1542 |
{% set original_price_kgs = product_info.price_kgs %}
|
| 1543 |
-
{% if app_config.prices_enabled
|
| 1544 |
<div style="margin: 20px 0; padding: 15px; background: var(--bg-steel-mid); border-radius: 8px;">
|
| 1545 |
<strong style="font-size: 1.1rem; color: var(--text-light);">{{ get_translation('price') }}</strong>
|
| 1546 |
{% if discount > 0 %}
|
|
@@ -1548,7 +1581,11 @@ PRODUCT_DETAIL_TEMPLATE = '''
|
|
| 1548 |
<span style="font-size: 1.8rem; font-weight: 700; color: var(--accent-red); margin-left: 10px;">{{ "%.2f"|format(discounted_price_kgs) }} {{ currency_code }}</span>
|
| 1549 |
<span style="text-decoration: line-through; color: var(--text-steel); font-size: 1.2rem; margin-left: 15px;">{{ "%.2f"|format(original_price_kgs) }} {{ currency_code }}</span>
|
| 1550 |
{% else %}
|
| 1551 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1552 |
{% endif %}
|
| 1553 |
</div>
|
| 1554 |
{% else %}
|
|
@@ -1624,13 +1661,13 @@ ORDER_TEMPLATE = '''
|
|
| 1624 |
<img src="{{ item.photo_url }}" alt="{{ item.name }}" {% if not item.photo %}style="opacity: 0.5;"{% endif %}>
|
| 1625 |
<div class="item-details">
|
| 1626 |
<strong>{{ item.name }} {% if item.color != 'N/A' %}({{ item.color }}){% endif %}</strong>
|
| 1627 |
-
{% if order.prices_were_enabled_at_order %}
|
| 1628 |
<span>{{ "%.2f"|format(item.price_kgs_at_order) }} {{ currency_code }} × {{ item.quantity }}</span>
|
| 1629 |
{% else %}
|
| 1630 |
<span>{{ get_translation('quantity') }} {{ item.quantity }}</span>
|
| 1631 |
{% endif %}
|
| 1632 |
</div>
|
| 1633 |
-
{% if order.prices_were_enabled_at_order %}
|
| 1634 |
<div class="item-total">
|
| 1635 |
{{ "%.2f"|format(item.price_kgs_at_order * item.quantity) }} {{ currency_code }}
|
| 1636 |
</div>
|
|
@@ -1643,7 +1680,7 @@ ORDER_TEMPLATE = '''
|
|
| 1643 |
{% endfor %}
|
| 1644 |
</div>
|
| 1645 |
|
| 1646 |
-
{% if order.prices_were_enabled_at_order %}
|
| 1647 |
<div class="order-summary">
|
| 1648 |
<p>{{ get_translation('total_products_sum') }} <strong>{{ "%.2f"|format(order.total_price_kgs) }} {{ currency_code }}</strong></p>
|
| 1649 |
</div>
|
|
@@ -1676,7 +1713,7 @@ ORDER_TEMPLATE = '''
|
|
| 1676 |
message += `*{{ get_translation('whatsapp_order_number') | replace("'", "\\'") }}* ${orderId}` + "%0A";
|
| 1677 |
message += `*{{ get_translation('whatsapp_order_link') | replace("'", "\\'") }}* ${encodedOrderUrl}` + "%0A%0A";
|
| 1678 |
|
| 1679 |
-
{% if not order.prices_were_enabled_at_order %}
|
| 1680 |
message += `{{ get_translation('whatsapp_request_price_calculation') | replace("'", "\\'") }}` + "%0A%0A";
|
| 1681 |
{% endif %}
|
| 1682 |
|
|
@@ -1805,6 +1842,7 @@ ADMIN_TEMPLATE = '''
|
|
| 1805 |
<h2><i class="fas fa-cog"></i> {{ get_translation('admin_settings_title') }}</h2>
|
| 1806 |
<form method="POST" action="{{ url_for('admin_actions') }}">
|
| 1807 |
<input type="hidden" name="action" value="update_settings">
|
|
|
|
| 1808 |
<div style="margin-top: 15px;">
|
| 1809 |
<input type="checkbox" id="prices_enabled" name="prices_enabled" {% if app_config.prices_enabled %}checked{% endif %}>
|
| 1810 |
<label for="prices_enabled" class="inline-label">{{ get_translation('admin_prices_enabled_checkbox_label') }}</label>
|
|
@@ -2039,7 +2077,7 @@ ADMIN_TEMPLATE = '''
|
|
| 2039 |
<p><strong>{{ get_translation('supplier') }}:</strong> {{ product_info.supplier_name }}</p>
|
| 2040 |
<p><strong>{{ get_translation('category') }}:</strong> {{ product_info.category_name }}</p>
|
| 2041 |
<p><strong>{{ get_translation('subcategory') }}:</strong> {{ product_info.subcategory_name }}</p>
|
| 2042 |
-
<p><strong>{{ get_translation('price') }}:</strong> {{ "%.2f"|format(product_info.price_kgs) }} {{ currency_code }}</p>
|
| 2043 |
{% if product_info.get('discount_percent', 0) > 0 %}
|
| 2044 |
<p><strong class="discount-info">{{ get_translation('discount_label_item') }} {{ product_info.discount_percent }}%</strong></p>
|
| 2045 |
{% endif %}
|
|
@@ -2538,10 +2576,11 @@ def create_order():
|
|
| 2538 |
price_kgs_in_cart = float(item['price_kgs'])
|
| 2539 |
original_price_kgs = float(item.get('original_price_kgs', price_kgs_in_cart))
|
| 2540 |
quantity = int(item['quantity'])
|
| 2541 |
-
if
|
| 2542 |
-
raise ValueError("Invalid
|
| 2543 |
-
|
| 2544 |
-
|
|
|
|
| 2545 |
total_price_kgs += price_kgs_in_cart * quantity
|
| 2546 |
|
| 2547 |
processed_cart.append({
|
|
@@ -2673,24 +2712,24 @@ def admin_api():
|
|
| 2673 |
try:
|
| 2674 |
if action == 'add_product':
|
| 2675 |
name = request.form.get('name', '').strip()
|
|
|
|
| 2676 |
if not name:
|
| 2677 |
-
return jsonify({'error': get_translation('flash_product_name_price_required')}), 400
|
| 2678 |
|
| 2679 |
-
price_kgs_str = request.form.get('price_kgs', '').replace(',', '.')
|
| 2680 |
-
if not price_kgs_str:
|
| 2681 |
-
|
| 2682 |
-
|
| 2683 |
-
|
| 2684 |
-
|
| 2685 |
-
|
| 2686 |
-
|
| 2687 |
-
return jsonify({'error': get_translation('flash_invalid_price_format')}), 400
|
| 2688 |
|
| 2689 |
discount_percent_str = request.form.get('discount_percent', '0')
|
| 2690 |
try:
|
| 2691 |
discount_percent = int(discount_percent_str)
|
| 2692 |
if not (0 <= discount_percent <= 100): discount_percent = 0
|
| 2693 |
-
except
|
| 2694 |
discount_percent = 0
|
| 2695 |
|
| 2696 |
description = request.form.get('description', '').strip()
|
|
@@ -2701,6 +2740,9 @@ def admin_api():
|
|
| 2701 |
on_order = 'on_order' in request.form
|
| 2702 |
is_top = 'is_top' in request.form
|
| 2703 |
|
|
|
|
|
|
|
|
|
|
| 2704 |
photos_list = []
|
| 2705 |
video_filename = None
|
| 2706 |
|
|
@@ -2769,24 +2811,25 @@ def admin_api():
|
|
| 2769 |
|
| 2770 |
product_to_edit['name'] = request.form.get('name', product_to_edit['name']).strip()
|
| 2771 |
|
| 2772 |
-
price_kgs_str = request.form.get('price_kgs'
|
| 2773 |
-
|
| 2774 |
-
|
| 2775 |
-
|
|
|
|
|
|
|
| 2776 |
try:
|
| 2777 |
price_kgs = round(float(price_kgs_str), 2)
|
| 2778 |
-
if price_kgs < 0: price_kgs = 0
|
| 2779 |
product_to_edit['price_kgs'] = price_kgs
|
| 2780 |
-
except ValueError:
|
| 2781 |
-
|
| 2782 |
|
| 2783 |
discount_percent_str = request.form.get('discount_percent', str(product_to_edit.get('discount_percent',0)))
|
| 2784 |
try:
|
| 2785 |
discount_percent = int(discount_percent_str)
|
| 2786 |
if not (0 <= discount_percent <= 100): discount_percent = product_to_edit.get('discount_percent', 0)
|
| 2787 |
product_to_edit['discount_percent'] = discount_percent
|
| 2788 |
-
except
|
| 2789 |
-
pass
|
| 2790 |
|
| 2791 |
product_to_edit['description'] = request.form.get('description', product_to_edit.get('description', '')).strip()
|
| 2792 |
product_to_edit['subcategory_id'] = request.form.get('subcategory_id') or None
|
|
|
|
| 100 |
'error_page_title': 'Ошибка',
|
| 101 |
'order_not_found_by_id': 'Заказ с таким ID не найден.',
|
| 102 |
'whatsapp_greeting': 'Здравствуйте! Хочу подтвердить свой заказ на emir:',
|
| 103 |
+
'whatsapp_product_inquiry': 'Здравствуйте! Хочу уточнить цену на товар: {product_name}',
|
| 104 |
'whatsapp_order_number': 'Номер заказа:',
|
| 105 |
'whatsapp_order_link': 'Ссылка на заказ:',
|
| 106 |
'whatsapp_contact_request': 'Пожалуйста, свяжитесь со мной для уточнения деталей оплаты и доставки.',
|
|
|
|
| 206 |
'flash_category_delete_success': "Категория '{category_name}' удалена.",
|
| 207 |
'flash_category_delete_failed': "Не удалось удалить категорию '{category_name}'.",
|
| 208 |
'flash_subcategory_added_success': "Подкатегория '{subcategory_name}' успешно добавлена.",
|
| 209 |
+
'flash_subcategory_name_empty': 'Название подкатегодии не может быть пустым.',
|
| 210 |
'flash_parent_category_not_selected': 'Необходимо выбрать родительскую категорию.',
|
| 211 |
'flash_subcategory_already_exists': "Подкатегория '{subcategory_name}' уже существует в этой категории.",
|
| 212 |
'flash_subcategory_delete_success': "Подкатегория '{subcategory_name}' удалена.",
|
| 213 |
+
'flash_subcategory_delete_failed': "Не удалось удалить подкатегодию.",
|
| 214 |
'flash_supplier_added_success': "Поставщик '{supplier_name}' успешно добавлен.",
|
| 215 |
'flash_supplier_name_empty': 'Имя поставщика не может быть пустым.',
|
| 216 |
'flash_supplier_already_exists': "Поставщик '{supplier_name}' уже существует.",
|
|
|
|
| 239 |
'flash_failed_delete_hf_files_warning': "Не удалось удалить файлы для товара '{product_name}' с сервера. Товар удален локально.",
|
| 240 |
'flash_hf_token_write_not_set_files_not_deleted_warning': "Товар '{product_name}' удален локально, но файлы не удалены с сервера (токен не задан).",
|
| 241 |
'flash_product_deleted_success': "Товар '{product_name}' удален.",
|
| 242 |
+
'flash_delete_error_not_found': "Ошибка удаления: товар с таким ID не найден.",
|
| 243 |
'flash_unknown_action': 'Неизвестное действие: {action}',
|
| 244 |
'flash_internal_error': "Произошла внутренняя ошибка при выполнении действия '{action}'. Подробности в логе сервера.",
|
| 245 |
'flash_data_uploaded_hf_success': 'Данные успешно загружены на Hugging Face.',
|
|
|
|
| 256 |
'flash_settings_updated': 'Настройки успешно обновлены.',
|
| 257 |
'prices_disabled_notice_catalog': 'Цены временно не отображаются. Добавьте товары в корзину для запроса стоимости.',
|
| 258 |
'prices_disabled_notice_cart': 'Цены не отображаются. Стоимость будет рассчитана после оформления заказа.',
|
| 259 |
+
'request_price_calculation_button': 'Уточнить цену в WhatsApp',
|
| 260 |
'price_on_request': 'Цена по запросу',
|
| 261 |
'video_modal_title': 'Видео о товаре',
|
| 262 |
'save_all_changes_button': 'Сохранить все изменения ({count})',
|
|
|
|
| 942 |
<div class="product-price-container">
|
| 943 |
{% set discount = product.get('discount_percent', 0) %}
|
| 944 |
{% set original_price_kgs = product.price_kgs %}
|
| 945 |
+
{% if app_config.prices_enabled %}
|
| 946 |
{% if discount > 0 %}
|
| 947 |
{% set discounted_price_kgs = original_price_kgs * (100 - discount) / 100 %}
|
| 948 |
<span class="original-price">{{ "%.2f"|format(original_price_kgs) }} {{ currency_code }}</span><br>
|
| 949 |
<span class="product-price discounted">{{ "%.2f"|format(discounted_price_kgs) }} {{ currency_code }}</span>
|
| 950 |
<span class="discount-badge">{{ get_translation('discount_badge_text', discount=discount) }}</span>
|
| 951 |
{% else %}
|
| 952 |
+
{% if original_price_kgs > 0 %}
|
| 953 |
+
<span class="product-price">{{ "%.2f"|format(original_price_kgs) }} {{ currency_code }}</span>
|
| 954 |
+
{% else %}
|
| 955 |
+
<div class="product-price on-request">{{ get_translation('price_on_request') }}</div>
|
| 956 |
+
{% endif %}
|
| 957 |
{% endif %}
|
| 958 |
{% else %}
|
| 959 |
<div class="product-price on-request">{{ get_translation('price_on_request') }}</div>
|
|
|
|
| 967 |
<i class="fas fa-play-circle"></i> {{ get_translation('view_video_button') }}
|
| 968 |
</button>
|
| 969 |
{% endif %}
|
| 970 |
+
|
| 971 |
+
{% if app_config.prices_enabled %}
|
| 972 |
+
<button class="product-button add-to-cart" onclick="openQuantityModal({{ loop.index0 }})">
|
| 973 |
+
<i class="fas fa-cart-plus"></i> {{ get_translation('add_to_cart') }}
|
| 974 |
+
</button>
|
| 975 |
+
{% else %}
|
| 976 |
+
<button class="product-button add-to-cart" style="background-color: #25D366;" onclick="inquireProductWhatsApp({{ loop.index0 }})">
|
| 977 |
+
<i class="fab fa-whatsapp"></i> {{ get_translation('request_price_calculation_button') }}
|
| 978 |
+
</button>
|
| 979 |
+
{% endif %}
|
| 980 |
</div>
|
| 981 |
</div>
|
| 982 |
</div>
|
|
|
|
| 1073 |
return text;
|
| 1074 |
}
|
| 1075 |
|
| 1076 |
+
function inquireProductWhatsApp(index) {
|
| 1077 |
+
const product = products[index];
|
| 1078 |
+
if (!product) return;
|
| 1079 |
+
|
| 1080 |
+
const phoneNumber = "996553303156";
|
| 1081 |
+
|
| 1082 |
+
const inquiryMessageTemplate = `{{ get_translation('whatsapp_product_inquiry', product_name='PLACEHOLDER_NAME') }}`;
|
| 1083 |
+
const inquiryMessage = encodeURIComponent(
|
| 1084 |
+
inquiryMessageTemplate.replace('PLACEHOLDER_NAME', product.name)
|
| 1085 |
+
);
|
| 1086 |
+
|
| 1087 |
+
const whatsappUrl = `https://wa.me/${phoneNumber}?text=${inquiryMessage}`;
|
| 1088 |
+
window.open(whatsappUrl, '_blank');
|
| 1089 |
+
}
|
| 1090 |
+
|
| 1091 |
function openModal(index) {
|
| 1092 |
loadProductDetails(index);
|
| 1093 |
const modal = document.getElementById('productModal');
|
|
|
|
| 1229 |
if (product.discount_percent && product.discount_percent > 0) {
|
| 1230 |
priceToAdd = product.price_kgs * (100 - product.discount_percent) / 100;
|
| 1231 |
}
|
| 1232 |
+
|
| 1233 |
+
// If price is 0 (optional price field not set by admin) but prices are enabled in config, we still use 0.
|
| 1234 |
+
if (!pricesEnabled && priceToAdd === 0) {
|
| 1235 |
+
// If prices are disabled, we treat the price as irrelevant for calculation here, but we save 0 to the cart item if not set.
|
| 1236 |
+
}
|
| 1237 |
+
|
| 1238 |
|
| 1239 |
if (existingItemIndex > -1) {
|
| 1240 |
cart[existingItemIndex].quantity += quantity;
|
|
|
|
| 1573 |
|
| 1574 |
{% set discount = product_info.get('discount_percent', 0) %}
|
| 1575 |
{% set original_price_kgs = product_info.price_kgs %}
|
| 1576 |
+
{% if app_config.prices_enabled %}
|
| 1577 |
<div style="margin: 20px 0; padding: 15px; background: var(--bg-steel-mid); border-radius: 8px;">
|
| 1578 |
<strong style="font-size: 1.1rem; color: var(--text-light);">{{ get_translation('price') }}</strong>
|
| 1579 |
{% if discount > 0 %}
|
|
|
|
| 1581 |
<span style="font-size: 1.8rem; font-weight: 700; color: var(--accent-red); margin-left: 10px;">{{ "%.2f"|format(discounted_price_kgs) }} {{ currency_code }}</span>
|
| 1582 |
<span style="text-decoration: line-through; color: var(--text-steel); font-size: 1.2rem; margin-left: 15px;">{{ "%.2f"|format(original_price_kgs) }} {{ currency_code }}</span>
|
| 1583 |
{% else %}
|
| 1584 |
+
{% if original_price_kgs > 0 %}
|
| 1585 |
+
<span style="font-size: 1.8rem; font-weight: 700; color: var(--text-light); margin-left: 10px;">{{ "%.2f"|format(original_price_kgs) }} {{ currency_code }}</span>
|
| 1586 |
+
{% else %}
|
| 1587 |
+
<span style="font-size: 1.2rem; font-weight: bold; color: var(--text-steel); margin-left: 10px;">{{ get_translation('price_on_request') }}</span>
|
| 1588 |
+
{% endif %}
|
| 1589 |
{% endif %}
|
| 1590 |
</div>
|
| 1591 |
{% else %}
|
|
|
|
| 1661 |
<img src="{{ item.photo_url }}" alt="{{ item.name }}" {% if not item.photo %}style="opacity: 0.5;"{% endif %}>
|
| 1662 |
<div class="item-details">
|
| 1663 |
<strong>{{ item.name }} {% if item.color != 'N/A' %}({{ item.color }}){% endif %}</strong>
|
| 1664 |
+
{% if order.prices_were_enabled_at_order and item.price_kgs_at_order > 0 %}
|
| 1665 |
<span>{{ "%.2f"|format(item.price_kgs_at_order) }} {{ currency_code }} × {{ item.quantity }}</span>
|
| 1666 |
{% else %}
|
| 1667 |
<span>{{ get_translation('quantity') }} {{ item.quantity }}</span>
|
| 1668 |
{% endif %}
|
| 1669 |
</div>
|
| 1670 |
+
{% if order.prices_were_enabled_at_order and item.price_kgs_at_order > 0 %}
|
| 1671 |
<div class="item-total">
|
| 1672 |
{{ "%.2f"|format(item.price_kgs_at_order * item.quantity) }} {{ currency_code }}
|
| 1673 |
</div>
|
|
|
|
| 1680 |
{% endfor %}
|
| 1681 |
</div>
|
| 1682 |
|
| 1683 |
+
{% if order.prices_were_enabled_at_order and order.total_price_kgs > 0 %}
|
| 1684 |
<div class="order-summary">
|
| 1685 |
<p>{{ get_translation('total_products_sum') }} <strong>{{ "%.2f"|format(order.total_price_kgs) }} {{ currency_code }}</strong></p>
|
| 1686 |
</div>
|
|
|
|
| 1713 |
message += `*{{ get_translation('whatsapp_order_number') | replace("'", "\\'") }}* ${orderId}` + "%0A";
|
| 1714 |
message += `*{{ get_translation('whatsapp_order_link') | replace("'", "\\'") }}* ${encodedOrderUrl}` + "%0A%0A";
|
| 1715 |
|
| 1716 |
+
{% if not order.prices_were_enabled_at_order or order.total_price_kgs == 0 %}
|
| 1717 |
message += `{{ get_translation('whatsapp_request_price_calculation') | replace("'", "\\'") }}` + "%0A%0A";
|
| 1718 |
{% endif %}
|
| 1719 |
|
|
|
|
| 1842 |
<h2><i class="fas fa-cog"></i> {{ get_translation('admin_settings_title') }}</h2>
|
| 1843 |
<form method="POST" action="{{ url_for('admin_actions') }}">
|
| 1844 |
<input type="hidden" name="action" value="update_settings">
|
| 1845 |
+
<label>{{ get_translation('admin_prices_visibility_label') }}</label>
|
| 1846 |
<div style="margin-top: 15px;">
|
| 1847 |
<input type="checkbox" id="prices_enabled" name="prices_enabled" {% if app_config.prices_enabled %}checked{% endif %}>
|
| 1848 |
<label for="prices_enabled" class="inline-label">{{ get_translation('admin_prices_enabled_checkbox_label') }}</label>
|
|
|
|
| 2077 |
<p><strong>{{ get_translation('supplier') }}:</strong> {{ product_info.supplier_name }}</p>
|
| 2078 |
<p><strong>{{ get_translation('category') }}:</strong> {{ product_info.category_name }}</p>
|
| 2079 |
<p><strong>{{ get_translation('subcategory') }}:</strong> {{ product_info.subcategory_name }}</p>
|
| 2080 |
+
<p><strong>{{ get_translation('price') }}:</strong> {% if product_info.price_kgs > 0 %} {{ "%.2f"|format(product_info.price_kgs) }} {{ currency_code }} {% else %} {{ get_translation('price_on_request') }} {% endif %}</p>
|
| 2081 |
{% if product_info.get('discount_percent', 0) > 0 %}
|
| 2082 |
<p><strong class="discount-info">{{ get_translation('discount_label_item') }} {{ product_info.discount_percent }}%</strong></p>
|
| 2083 |
{% endif %}
|
|
|
|
| 2576 |
price_kgs_in_cart = float(item['price_kgs'])
|
| 2577 |
original_price_kgs = float(item.get('original_price_kgs', price_kgs_in_cart))
|
| 2578 |
quantity = int(item['quantity'])
|
| 2579 |
+
if quantity <= 0:
|
| 2580 |
+
raise ValueError("Invalid quantity")
|
| 2581 |
+
|
| 2582 |
+
# If prices are enabled OR the price is positive, calculate total
|
| 2583 |
+
if prices_currently_enabled and price_kgs_in_cart > 0:
|
| 2584 |
total_price_kgs += price_kgs_in_cart * quantity
|
| 2585 |
|
| 2586 |
processed_cart.append({
|
|
|
|
| 2712 |
try:
|
| 2713 |
if action == 'add_product':
|
| 2714 |
name = request.form.get('name', '').strip()
|
| 2715 |
+
|
| 2716 |
if not name:
|
| 2717 |
+
return jsonify({'error': get_translation('flash_product_name_price_required')}), 400
|
| 2718 |
|
| 2719 |
+
price_kgs_str = request.form.get('price_kgs', '0').replace(',', '.')
|
| 2720 |
+
if not price_kgs_str.strip(): price_kgs_str = '0'
|
| 2721 |
+
|
| 2722 |
+
try:
|
| 2723 |
+
price_kgs = round(float(price_kgs_str), 2)
|
| 2724 |
+
if price_kgs < 0: price_kgs = 0
|
| 2725 |
+
except ValueError:
|
| 2726 |
+
return jsonify({'error': get_translation('flash_invalid_price_format')}), 400
|
|
|
|
| 2727 |
|
| 2728 |
discount_percent_str = request.form.get('discount_percent', '0')
|
| 2729 |
try:
|
| 2730 |
discount_percent = int(discount_percent_str)
|
| 2731 |
if not (0 <= discount_percent <= 100): discount_percent = 0
|
| 2732 |
+
except ValueError:
|
| 2733 |
discount_percent = 0
|
| 2734 |
|
| 2735 |
description = request.form.get('description', '').strip()
|
|
|
|
| 2740 |
on_order = 'on_order' in request.form
|
| 2741 |
is_top = 'is_top' in request.form
|
| 2742 |
|
| 2743 |
+
if in_stock and on_order: # Ensure mutual exclusion
|
| 2744 |
+
on_order = False
|
| 2745 |
+
|
| 2746 |
photos_list = []
|
| 2747 |
video_filename = None
|
| 2748 |
|
|
|
|
| 2811 |
|
| 2812 |
product_to_edit['name'] = request.form.get('name', product_to_edit['name']).strip()
|
| 2813 |
|
| 2814 |
+
price_kgs_str = request.form.get('price_kgs')
|
| 2815 |
+
|
| 2816 |
+
if price_kgs_str is not None:
|
| 2817 |
+
price_kgs_str = price_kgs_str.replace(',', '.')
|
| 2818 |
+
if not price_kgs_str.strip(): price_kgs_str = '0'
|
| 2819 |
+
|
| 2820 |
try:
|
| 2821 |
price_kgs = round(float(price_kgs_str), 2)
|
| 2822 |
+
if price_kgs < 0: price_kgs = 0
|
| 2823 |
product_to_edit['price_kgs'] = price_kgs
|
| 2824 |
+
except ValueError:
|
| 2825 |
+
logging.warning(get_translation('flash_invalid_price_format_edit_warning', product_name=product_to_edit['name']))
|
| 2826 |
|
| 2827 |
discount_percent_str = request.form.get('discount_percent', str(product_to_edit.get('discount_percent',0)))
|
| 2828 |
try:
|
| 2829 |
discount_percent = int(discount_percent_str)
|
| 2830 |
if not (0 <= discount_percent <= 100): discount_percent = product_to_edit.get('discount_percent', 0)
|
| 2831 |
product_to_edit['discount_percent'] = discount_percent
|
| 2832 |
+
except ValueError: pass
|
|
|
|
| 2833 |
|
| 2834 |
product_to_edit['description'] = request.form.get('description', product_to_edit.get('description', '')).strip()
|
| 2835 |
product_to_edit['subcategory_id'] = request.form.get('subcategory_id') or None
|