feat: добавлена интеграция синхронизации с Recommerce
This commit is contained in:
128
myproject/products/static/products/js/recommerce-sync.js
Normal file
128
myproject/products/static/products/js/recommerce-sync.js
Normal file
@@ -0,0 +1,128 @@
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const syncBtn = document.getElementById('bulk-recommerce-sync');
|
||||
const modalEl = document.getElementById('recommerceSyncModal');
|
||||
const startBtn = document.getElementById('startRecommerceSyncBtn');
|
||||
|
||||
if (!syncBtn || !modalEl) return;
|
||||
|
||||
const modal = new bootstrap.Modal(modalEl);
|
||||
|
||||
syncBtn.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
// Получаем выбранные элементы
|
||||
// Предполагается, что на странице есть механизм BatchSelection или просто чекбоксы
|
||||
let selectedItems = [];
|
||||
|
||||
// Проверяем глобальный объект BatchSelection (из batch-selection.js)
|
||||
if (window.BatchSelection && typeof window.BatchSelection.getSelectedItems === 'function') {
|
||||
selectedItems = window.BatchSelection.getSelectedItems();
|
||||
} else {
|
||||
// Fallback: собираем вручную
|
||||
document.querySelectorAll('.item-checkbox:checked').forEach(cb => {
|
||||
selectedItems.push(cb.value);
|
||||
});
|
||||
}
|
||||
|
||||
// Фильтруем только товары (формат value="product:123")
|
||||
// Комплекты (kit:123) пока игнорируем, так как бэкенд ожидает Product ID
|
||||
const productIds = selectedItems
|
||||
.filter(val => val.startsWith('product:'))
|
||||
.map(val => val.split(':')[1]);
|
||||
|
||||
if (productIds.length === 0) {
|
||||
// Если выбраны только комплекты или ничего не выбрано
|
||||
if (selectedItems.length > 0) {
|
||||
alert('Для синхронизации с Recommerce выберите товары (комплекты пока не поддерживаются).');
|
||||
} else {
|
||||
alert('Выберите товары для синхронизации.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Обновляем UI модального окна
|
||||
document.getElementById('recommerceSyncCount').textContent = productIds.length;
|
||||
|
||||
// Сохраняем ID для отправки
|
||||
startBtn.dataset.productIds = JSON.stringify(productIds);
|
||||
|
||||
modal.show();
|
||||
});
|
||||
|
||||
startBtn.addEventListener('click', function() {
|
||||
const productIds = JSON.parse(this.dataset.productIds || '[]');
|
||||
|
||||
const options = {
|
||||
fields: [],
|
||||
create_if_missing: document.getElementById('syncCreateNew').checked
|
||||
};
|
||||
|
||||
if (document.getElementById('syncPrice').checked) options.fields.push('price');
|
||||
if (document.getElementById('syncStock').checked) options.fields.push('count');
|
||||
if (document.getElementById('syncContent').checked) options.fields.push('content');
|
||||
if (document.getElementById('syncImages').checked) options.fields.push('images');
|
||||
|
||||
// Блокируем кнопку
|
||||
startBtn.disabled = true;
|
||||
const originalText = startBtn.innerHTML;
|
||||
startBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Запуск...';
|
||||
|
||||
fetch('/settings/integrations/recommerce/sync/', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRFToken': getCsrfToken()
|
||||
},
|
||||
body: JSON.stringify({
|
||||
product_ids: productIds,
|
||||
options: options
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
modal.hide();
|
||||
// Используем стандартный alert или toast, если есть
|
||||
alert(`Синхронизация запущена успешно!\nID задачи: ${data.task_id}\nОбрабатывается товаров: ${productIds.length}`);
|
||||
|
||||
// Снимаем выделение
|
||||
if (window.BatchSelection && typeof window.BatchSelection.clearSelection === 'function') {
|
||||
window.BatchSelection.clearSelection();
|
||||
}
|
||||
} else {
|
||||
alert('Ошибка при запуске: ' + (data.error || 'Неизвестная ошибка'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error:', error);
|
||||
alert('Ошибка сети или сервера');
|
||||
})
|
||||
.finally(() => {
|
||||
startBtn.disabled = false;
|
||||
startBtn.innerHTML = originalText;
|
||||
});
|
||||
});
|
||||
|
||||
// Helper для получения CSRF токена
|
||||
function getCsrfToken() {
|
||||
// Сначала пробуем получить из скрытого поля (для CSRF_USE_SESSIONS = True)
|
||||
const csrfInput = document.querySelector('[name=csrfmiddlewaretoken]');
|
||||
if (csrfInput) {
|
||||
return csrfInput.value;
|
||||
}
|
||||
// Fallback: из куки
|
||||
const name = 'csrftoken';
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== '') {
|
||||
const cookies = document.cookie.split(';');
|
||||
for (let i = 0; i < cookies.length; i++) {
|
||||
const cookie = cookies[i].trim();
|
||||
if (cookie.substring(0, name.length + 1) === (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
<!-- Модальное окно для синхронизации с Recommerce -->
|
||||
<div class="modal fade" id="recommerceSyncModal" tabindex="-1" aria-labelledby="recommerceSyncModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="recommerceSyncModalLabel">
|
||||
<i class="bi bi-arrow-repeat"></i> Синхронизация с Recommerce
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Закрыть"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="alert alert-info mb-3">
|
||||
<i class="bi bi-info-circle"></i> <strong>Выбрано товаров:</strong> <span id="recommerceSyncCount">0</span>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-bold">Что обновлять?</label>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="price" id="syncPrice" checked>
|
||||
<label class="form-check-label" for="syncPrice">
|
||||
Цены
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="count" id="syncStock" checked>
|
||||
<label class="form-check-label" for="syncStock">
|
||||
Остатки
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="content" id="syncContent">
|
||||
<label class="form-check-label" for="syncContent">
|
||||
Название и описание
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" value="images" id="syncImages">
|
||||
<label class="form-check-label" for="syncImages">
|
||||
Изображения
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="syncCreateNew">
|
||||
<label class="form-check-label" for="syncCreateNew">
|
||||
Создавать товары, если не найдены
|
||||
</label>
|
||||
<div class="form-text text-muted">
|
||||
Если товар отсутствует в Recommerce, он будет создан (требуется полное заполнение).
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
|
||||
<button type="button" class="btn btn-primary" id="startRecommerceSyncBtn">
|
||||
<i class="bi bi-play-fill"></i> Запустить
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -4,6 +4,7 @@
|
||||
{% block title %}Товары и комплекты{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% csrf_token %}
|
||||
<div class="container-fluid mt-4">
|
||||
<h2 class="mb-4">
|
||||
<i class="bi bi-box-seam"></i> Товары и комплекты
|
||||
@@ -188,6 +189,12 @@
|
||||
<i class="bi bi-bookmark-fill"></i> Изменить категории
|
||||
</a>
|
||||
</li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="#" id="bulk-recommerce-sync">
|
||||
<i class="bi bi-arrow-repeat"></i> Синхронизация с Recommerce
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -398,6 +405,8 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% include "products/includes/recommerce_sync_modal.html" %}
|
||||
|
||||
<!-- Модальное окно для массового изменения категорий -->
|
||||
<div class="modal fade" id="bulkCategoryModal" tabindex="-1" aria-labelledby="bulkCategoryModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg">
|
||||
@@ -487,4 +496,5 @@
|
||||
{% load static %}
|
||||
<script src="{% static 'products/js/batch-selection.js' %}?v=1.2"></script>
|
||||
<script src="{% static 'products/js/bulk-category-modal.js' %}?v=1.6"></script>
|
||||
<script src="{% static 'products/js/recommerce-sync.js' %}?v=1.1"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user