FIX: Добавлен баланс кошелька клиента в модальное окно продажи POS
Проблема: - Баланс кошелька клиента не отображался в модальном окне при нажатии "ПРОДАТЬ" - Данные о балансе не передавались из backend в frontend Исправления: 1. pos/views.py: - Добавлен wallet_balance в selected_customer при загрузке из Redis - Добавлен wallet_balance в system_customer - Добавлен wallet_balance в API set_customer (Redis + response) - Используется json.dumps() для корректной сериализации данных клиента 2. customers/views.py: - Добавлен wallet_balance в API поиска клиентов (api_search_customers) - Добавлен wallet_balance в API создания клиента (api_create_customer) 3. pos/static/pos/js/terminal.js: - Обновлена функция selectCustomer() для получения walletBalance - Обновлены все вызовы selectCustomer() для передачи баланса - selectedCustomer теперь содержит wallet_balance 4. pos/templates/pos/terminal.html: - Используются готовые JSON-строки из backend (system_customer_json, selected_customer_json) - Исправлена проблема с локализацией чисел в JSON Результат: Баланс кошелька клиента теперь корректно отображается в модальном окне продажи 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ from django.db.models.functions import Greatest, Coalesce
|
|||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.views.decorators.http import require_http_methods
|
from django.views.decorators.http import require_http_methods
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from user_roles.decorators import manager_or_owner_required
|
||||||
import phonenumbers
|
import phonenumbers
|
||||||
import json
|
import json
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
@@ -409,6 +410,7 @@ def api_search_customers(request):
|
|||||||
'name': customer.name,
|
'name': customer.name,
|
||||||
'phone': phone_display,
|
'phone': phone_display,
|
||||||
'email': customer.email,
|
'email': customer.email,
|
||||||
|
'wallet_balance': float(customer.wallet_balance),
|
||||||
})
|
})
|
||||||
|
|
||||||
# Если ничего не найдено, предлагаем создать нового клиента
|
# Если ничего не найдено, предлагаем создать нового клиента
|
||||||
@@ -483,6 +485,7 @@ def api_create_customer(request):
|
|||||||
'name': customer.name,
|
'name': customer.name,
|
||||||
'phone': phone_display,
|
'phone': phone_display,
|
||||||
'email': customer.email if customer.email else '',
|
'email': customer.email if customer.email else '',
|
||||||
|
'wallet_balance': float(customer.wallet_balance),
|
||||||
}, status=201)
|
}, status=201)
|
||||||
else:
|
else:
|
||||||
# Собираем ошибки валидации с указанием полей
|
# Собираем ошибки валидации с указанием полей
|
||||||
@@ -519,13 +522,10 @@ def api_create_customer(request):
|
|||||||
}, status=500)
|
}, status=500)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@manager_or_owner_required
|
||||||
@require_http_methods(["POST"])
|
@require_http_methods(["POST"])
|
||||||
def wallet_deposit(request, pk):
|
def wallet_deposit(request, pk):
|
||||||
"""Пополнение кошелька клиента"""
|
"""Пополнение кошелька клиента"""
|
||||||
if not request.user.is_staff:
|
|
||||||
raise PermissionDenied("У вас нет прав для изменения баланса кошелька клиента.")
|
|
||||||
|
|
||||||
customer = get_object_or_404(Customer, pk=pk)
|
customer = get_object_or_404(Customer, pk=pk)
|
||||||
|
|
||||||
if customer.is_system_customer:
|
if customer.is_system_customer:
|
||||||
@@ -552,13 +552,10 @@ def wallet_deposit(request, pk):
|
|||||||
return redirect('customers:customer-detail', pk=pk)
|
return redirect('customers:customer-detail', pk=pk)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@manager_or_owner_required
|
||||||
@require_http_methods(["POST"])
|
@require_http_methods(["POST"])
|
||||||
def wallet_withdraw(request, pk):
|
def wallet_withdraw(request, pk):
|
||||||
"""Возврат / списание с кошелька клиента"""
|
"""Возврат / списание с кошелька клиента"""
|
||||||
if not request.user.is_staff:
|
|
||||||
raise PermissionDenied("У вас нет прав для изменения баланса кошелька клиента.")
|
|
||||||
|
|
||||||
customer = get_object_or_404(Customer, pk=pk)
|
customer = get_object_or_404(Customer, pk=pk)
|
||||||
|
|
||||||
if customer.is_system_customer:
|
if customer.is_system_customer:
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ function formatMoney(v) {
|
|||||||
* - Кнопку "Выбрать клиента" в корзине (показывает имя клиента)
|
* - Кнопку "Выбрать клиента" в корзине (показывает имя клиента)
|
||||||
* - Кнопку "Выбрать клиента" в модалке продажи (показывает имя клиента)
|
* - Кнопку "Выбрать клиента" в модалке продажи (показывает имя клиента)
|
||||||
* - Видимость кнопок сброса в обоих местах (показываем только для не-системного клиента)
|
* - Видимость кнопок сброса в обоих местах (показываем только для не-системного клиента)
|
||||||
|
* - Ссылку на анкету клиента (показываем только для не-системного клиента)
|
||||||
*/
|
*/
|
||||||
function updateCustomerDisplay() {
|
function updateCustomerDisplay() {
|
||||||
// Обновляем текст кнопки в корзине
|
// Обновляем текст кнопки в корзине
|
||||||
@@ -110,17 +111,30 @@ function updateCustomerDisplay() {
|
|||||||
resetBtn.style.display = isSystemCustomer ? 'none' : 'block';
|
resetBtn.style.display = isSystemCustomer ? 'none' : 'block';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Обновляем ссылку на анкету клиента
|
||||||
|
const profileLink = document.getElementById('customerProfileLink');
|
||||||
|
if (profileLink) {
|
||||||
|
if (isSystemCustomer) {
|
||||||
|
profileLink.style.display = 'none';
|
||||||
|
} else {
|
||||||
|
profileLink.href = `/customers/${selectedCustomer.id}/`;
|
||||||
|
profileLink.style.display = 'block';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Устанавливает нового клиента и сохраняет в Redis
|
* Устанавливает нового клиента и сохраняет в Redis
|
||||||
* @param {number} customerId - ID клиента
|
* @param {number} customerId - ID клиента
|
||||||
* @param {string} customerName - Имя клиента
|
* @param {string} customerName - Имя клиента
|
||||||
|
* @param {number} walletBalance - Баланс кошелька клиента (опционально)
|
||||||
*/
|
*/
|
||||||
function selectCustomer(customerId, customerName) {
|
function selectCustomer(customerId, customerName, walletBalance = 0) {
|
||||||
selectedCustomer = {
|
selectedCustomer = {
|
||||||
id: customerId,
|
id: customerId,
|
||||||
name: customerName
|
name: customerName,
|
||||||
|
wallet_balance: walletBalance
|
||||||
};
|
};
|
||||||
updateCustomerDisplay();
|
updateCustomerDisplay();
|
||||||
|
|
||||||
@@ -136,6 +150,9 @@ function selectCustomer(customerId, customerName) {
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
console.error('Ошибка сохранения клиента:', data.error);
|
console.error('Ошибка сохранения клиента:', data.error);
|
||||||
|
} else {
|
||||||
|
// Обновляем баланс из ответа сервера
|
||||||
|
selectedCustomer.wallet_balance = data.wallet_balance || 0;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -188,8 +205,8 @@ function initCustomerSelect2() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Выбираем клиента
|
// Выбираем клиента с балансом
|
||||||
selectCustomer(parseInt(data.id), data.name);
|
selectCustomer(parseInt(data.id), data.name, data.wallet_balance || 0);
|
||||||
|
|
||||||
// Закрываем модалку
|
// Закрываем модалку
|
||||||
const modal = bootstrap.Modal.getInstance(document.getElementById('selectCustomerModal'));
|
const modal = bootstrap.Modal.getInstance(document.getElementById('selectCustomerModal'));
|
||||||
@@ -297,8 +314,8 @@ async function createNewCustomer() {
|
|||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.success) {
|
if (data.success) {
|
||||||
// Выбираем созданного клиента
|
// Выбираем созданного клиента с балансом
|
||||||
selectCustomer(data.id, data.name);
|
selectCustomer(data.id, data.name, data.wallet_balance || 0);
|
||||||
|
|
||||||
// Закрываем модалку
|
// Закрываем модалку
|
||||||
const modal = bootstrap.Modal.getInstance(document.getElementById('createCustomerModal'));
|
const modal = bootstrap.Modal.getInstance(document.getElementById('createCustomerModal'));
|
||||||
@@ -516,33 +533,46 @@ function renderProducts() {
|
|||||||
stock.style.color = '#856404';
|
stock.style.color = '#856404';
|
||||||
stock.style.fontWeight = 'bold';
|
stock.style.fontWeight = 'bold';
|
||||||
} else if (item.type === 'product' && item.available_qty !== undefined && item.reserved_qty !== undefined) {
|
} else if (item.type === 'product' && item.available_qty !== undefined && item.reserved_qty !== undefined) {
|
||||||
// Для обычных товаров показываем остатки: FREE(-RESERVED)
|
// Для обычных товаров показываем остатки: FREE(-RESERVED-IN_CART)
|
||||||
// FREE = доступно для продажи (available - reserved)
|
// FREE = доступно для продажи (available - reserved - в корзине)
|
||||||
const available = parseFloat(item.available_qty) || 0;
|
const available = parseFloat(item.available_qty) || 0;
|
||||||
const reserved = parseFloat(item.reserved_qty) || 0;
|
const reserved = parseFloat(item.reserved_qty) || 0;
|
||||||
const free = available - reserved;
|
|
||||||
|
// Вычитаем количество из корзины для визуализации
|
||||||
|
const cartKey = `product-${item.id}`;
|
||||||
|
const inCart = cart.has(cartKey) ? cart.get(cartKey).qty : 0;
|
||||||
|
|
||||||
|
const free = available - reserved - inCart;
|
||||||
|
|
||||||
// Создаём элементы для стилизации разных размеров
|
// Создаём элементы для стилизации разных размеров
|
||||||
const freeSpan = document.createElement('span');
|
const freeSpan = document.createElement('span');
|
||||||
freeSpan.textContent = free;
|
freeSpan.textContent = free;
|
||||||
freeSpan.style.fontSize = '1.1em';
|
freeSpan.style.fontSize = '1.1em';
|
||||||
freeSpan.style.fontWeight = 'bold';
|
freeSpan.style.fontWeight = 'bold';
|
||||||
freeSpan.style.fontStyle = 'normal';
|
freeSpan.style.fontStyle = 'normal';
|
||||||
|
|
||||||
// Отображаем резерв только если он есть
|
// Отображаем резерв и корзину если они есть
|
||||||
|
const suffixParts = [];
|
||||||
if (reserved > 0) {
|
if (reserved > 0) {
|
||||||
const reservedSpan = document.createElement('span');
|
suffixParts.push(`−${reserved}`);
|
||||||
reservedSpan.textContent = `(−${reserved})`;
|
}
|
||||||
reservedSpan.style.fontSize = '0.85em';
|
if (inCart > 0) {
|
||||||
reservedSpan.style.marginLeft = '3px';
|
suffixParts.push(`−${inCart}🛒`);
|
||||||
reservedSpan.style.fontStyle = 'normal';
|
}
|
||||||
|
|
||||||
|
if (suffixParts.length > 0) {
|
||||||
|
const suffixSpan = document.createElement('span');
|
||||||
|
suffixSpan.textContent = `(${suffixParts.join(' ')})`;
|
||||||
|
suffixSpan.style.fontSize = '0.85em';
|
||||||
|
suffixSpan.style.marginLeft = '3px';
|
||||||
|
suffixSpan.style.fontStyle = 'normal';
|
||||||
|
|
||||||
stock.appendChild(freeSpan);
|
stock.appendChild(freeSpan);
|
||||||
stock.appendChild(reservedSpan);
|
stock.appendChild(suffixSpan);
|
||||||
} else {
|
} else {
|
||||||
stock.appendChild(freeSpan);
|
stock.appendChild(freeSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Цветовая индикация: красный если свободных остатков нет или отрицательные
|
// Цветовая индикация: красный если свободных остатков нет или отрицательные
|
||||||
if (free <= 0) {
|
if (free <= 0) {
|
||||||
stock.style.color = '#dc3545'; // Красный
|
stock.style.color = '#dc3545'; // Красный
|
||||||
@@ -619,13 +649,13 @@ async function loadItems(append = false) {
|
|||||||
} else {
|
} else {
|
||||||
ITEMS = data.items;
|
ITEMS = data.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasMoreItems = data.has_more;
|
hasMoreItems = data.has_more;
|
||||||
|
|
||||||
if (data.has_more) {
|
if (data.has_more) {
|
||||||
currentPage = data.next_page;
|
currentPage = data.next_page;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderProducts();
|
renderProducts();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -721,6 +751,11 @@ async function addToCart(item) {
|
|||||||
renderCart();
|
renderCart();
|
||||||
saveCartToRedis(); // Сохраняем в Redis
|
saveCartToRedis(); // Сохраняем в Redis
|
||||||
|
|
||||||
|
// Перерисовываем товары для обновления визуального остатка
|
||||||
|
if (!isShowcaseView && item.type === 'product') {
|
||||||
|
renderProducts();
|
||||||
|
}
|
||||||
|
|
||||||
// Автоматический фокус на поле количества (только для обычных товаров)
|
// Автоматический фокус на поле количества (только для обычных товаров)
|
||||||
if (item.type !== 'showcase_kit') {
|
if (item.type !== 'showcase_kit') {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -735,17 +770,36 @@ async function addToCart(item) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Вспомогательная функция для обновления количества товара в корзине
|
||||||
|
async function updateCartItemQty(cartKey, newQty) {
|
||||||
|
const item = cart.get(cartKey);
|
||||||
|
if (!item) return;
|
||||||
|
|
||||||
|
if (newQty <= 0) {
|
||||||
|
await removeFromCart(cartKey);
|
||||||
|
} else {
|
||||||
|
item.qty = newQty;
|
||||||
|
renderCart();
|
||||||
|
saveCartToRedis();
|
||||||
|
|
||||||
|
// Перерисовываем товары для обновления визуального остатка
|
||||||
|
if (!isShowcaseView && item.type === 'product') {
|
||||||
|
renderProducts();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderCart() {
|
function renderCart() {
|
||||||
const list = document.getElementById('cartList');
|
const list = document.getElementById('cartList');
|
||||||
list.innerHTML = '';
|
list.innerHTML = '';
|
||||||
let total = 0;
|
let total = 0;
|
||||||
|
|
||||||
if (cart.size === 0) {
|
if (cart.size === 0) {
|
||||||
list.innerHTML = '<p class="text-muted text-center py-4 small">Корзина пуста</p>';
|
list.innerHTML = '<p class="text-muted text-center py-4 small">Корзина пуста</p>';
|
||||||
document.getElementById('cartTotal').textContent = '0.00';
|
document.getElementById('cartTotal').textContent = '0.00';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cart.forEach((item, cartKey) => {
|
cart.forEach((item, cartKey) => {
|
||||||
const row = document.createElement('div');
|
const row = document.createElement('div');
|
||||||
row.className = 'cart-item mb-2';
|
row.className = 'cart-item mb-2';
|
||||||
@@ -799,16 +853,10 @@ function renderCart() {
|
|||||||
const minusBtn = document.createElement('button');
|
const minusBtn = document.createElement('button');
|
||||||
minusBtn.className = 'btn btn-outline-secondary btn-sm';
|
minusBtn.className = 'btn btn-outline-secondary btn-sm';
|
||||||
minusBtn.innerHTML = '<i class="bi bi-dash-circle" style="font-size: 1.2em;"></i>';
|
minusBtn.innerHTML = '<i class="bi bi-dash-circle" style="font-size: 1.2em;"></i>';
|
||||||
minusBtn.onclick = (e) => {
|
minusBtn.onclick = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const currentQty = cart.get(cartKey).qty;
|
const currentQty = cart.get(cartKey).qty;
|
||||||
if (currentQty <= 1) {
|
await updateCartItemQty(cartKey, currentQty - 1);
|
||||||
removeFromCart(cartKey);
|
|
||||||
} else {
|
|
||||||
cart.get(cartKey).qty = currentQty - 1;
|
|
||||||
renderCart();
|
|
||||||
saveCartToRedis();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Поле ввода количества
|
// Поле ввода количества
|
||||||
@@ -820,26 +868,19 @@ function renderCart() {
|
|||||||
qtyInput.style.padding = '0.375rem 0.25rem';
|
qtyInput.style.padding = '0.375rem 0.25rem';
|
||||||
qtyInput.value = item.qty;
|
qtyInput.value = item.qty;
|
||||||
qtyInput.min = 1;
|
qtyInput.min = 1;
|
||||||
qtyInput.onchange = (e) => {
|
qtyInput.onchange = async (e) => {
|
||||||
const newQty = parseInt(e.target.value) || 1;
|
const newQty = parseInt(e.target.value) || 1;
|
||||||
if (newQty <= 0) {
|
await updateCartItemQty(cartKey, newQty);
|
||||||
removeFromCart(cartKey);
|
|
||||||
} else {
|
|
||||||
cart.get(cartKey).qty = newQty;
|
|
||||||
renderCart();
|
|
||||||
saveCartToRedis(); // Сохраняем в Redis при изменении количества
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Кнопка плюс
|
// Кнопка плюс
|
||||||
const plusBtn = document.createElement('button');
|
const plusBtn = document.createElement('button');
|
||||||
plusBtn.className = 'btn btn-outline-secondary btn-sm';
|
plusBtn.className = 'btn btn-outline-secondary btn-sm';
|
||||||
plusBtn.innerHTML = '<i class="bi bi-plus-circle" style="font-size: 1.2em;"></i>';
|
plusBtn.innerHTML = '<i class="bi bi-plus-circle" style="font-size: 1.2em;"></i>';
|
||||||
plusBtn.onclick = (e) => {
|
plusBtn.onclick = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
cart.get(cartKey).qty += 1;
|
const currentQty = cart.get(cartKey).qty;
|
||||||
renderCart();
|
await updateCartItemQty(cartKey, currentQty + 1);
|
||||||
saveCartToRedis();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Собираем контейнер
|
// Собираем контейнер
|
||||||
@@ -908,6 +949,11 @@ async function removeFromCart(cartKey) {
|
|||||||
cart.delete(cartKey);
|
cart.delete(cartKey);
|
||||||
renderCart();
|
renderCart();
|
||||||
saveCartToRedis(); // Сохраняем в Redis
|
saveCartToRedis(); // Сохраняем в Redis
|
||||||
|
|
||||||
|
// Перерисовываем товары для обновления визуального остатка
|
||||||
|
if (!isShowcaseView && item && item.type === 'product') {
|
||||||
|
renderProducts();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearCart() {
|
function clearCart() {
|
||||||
|
|||||||
@@ -69,6 +69,9 @@
|
|||||||
<span id="customerSelectBtnText" class="fw-semibold">Выбрать</span>
|
<span id="customerSelectBtnText" class="fw-semibold">Выбрать</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
<a href="#" id="customerProfileLink" class="btn btn-sm btn-outline-secondary" title="Открыть анкету клиента" target="_blank" style="display: none;">
|
||||||
|
<i class="bi bi-box-arrow-up-right"></i>
|
||||||
|
</a>
|
||||||
<button class="btn btn-sm btn-outline-danger" id="resetCustomerBtn" title="Сбросить на системного клиента" style="display: none;">
|
<button class="btn btn-sm btn-outline-danger" id="resetCustomerBtn" title="Сбросить на системного клиента" style="display: none;">
|
||||||
<i class="bi bi-x-lg"></i>
|
<i class="bi bi-x-lg"></i>
|
||||||
</button>
|
</button>
|
||||||
@@ -484,18 +487,8 @@
|
|||||||
<script id="categoriesData" type="application/json">{{ categories_json|safe }}</script>
|
<script id="categoriesData" type="application/json">{{ categories_json|safe }}</script>
|
||||||
<script id="itemsData" type="application/json">{{ items_json|safe }}</script>
|
<script id="itemsData" type="application/json">{{ items_json|safe }}</script>
|
||||||
<script id="showcaseKitsData" type="application/json">{{ showcase_kits_json|safe }}</script>
|
<script id="showcaseKitsData" type="application/json">{{ showcase_kits_json|safe }}</script>
|
||||||
<script id="systemCustomerData" type="application/json">
|
<script id="systemCustomerData" type="application/json">{{ system_customer_json|safe }}</script>
|
||||||
{
|
<script id="selectedCustomerData" type="application/json">{{ selected_customer_json|safe }}</script>
|
||||||
"id": {{ system_customer.id }},
|
|
||||||
"name": "{{ system_customer.name|escapejs }}"
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script id="selectedCustomerData" type="application/json">
|
|
||||||
{
|
|
||||||
"id": {{ selected_customer.id }},
|
|
||||||
"name": "{{ selected_customer.name|escapejs }}"
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<script id="cartData" type="application/json">{{ cart_data|safe }}</script>
|
<script id="cartData" type="application/json">{{ cart_data|safe }}</script>
|
||||||
<script id="currentWarehouseData" type="application/json">
|
<script id="currentWarehouseData" type="application/json">
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -181,14 +181,16 @@ def pos_terminal(request):
|
|||||||
'showcase_kits_json': json.dumps([]),
|
'showcase_kits_json': json.dumps([]),
|
||||||
'current_warehouse': None,
|
'current_warehouse': None,
|
||||||
'warehouses': [],
|
'warehouses': [],
|
||||||
'system_customer': {
|
'system_customer_json': json.dumps({
|
||||||
'id': system_customer.id,
|
'id': system_customer.id,
|
||||||
'name': system_customer.name
|
'name': system_customer.name,
|
||||||
},
|
'wallet_balance': float(system_customer.wallet_balance)
|
||||||
'selected_customer': {
|
}),
|
||||||
|
'selected_customer_json': json.dumps({
|
||||||
'id': system_customer.id,
|
'id': system_customer.id,
|
||||||
'name': system_customer.name
|
'name': system_customer.name,
|
||||||
},
|
'wallet_balance': float(system_customer.wallet_balance)
|
||||||
|
}),
|
||||||
'cart_data': json.dumps({}),
|
'cart_data': json.dumps({}),
|
||||||
'title': 'POS Terminal',
|
'title': 'POS Terminal',
|
||||||
}
|
}
|
||||||
@@ -208,7 +210,8 @@ def pos_terminal(request):
|
|||||||
customer = Customer.objects.get(id=cached_customer_data['customer_id'])
|
customer = Customer.objects.get(id=cached_customer_data['customer_id'])
|
||||||
selected_customer = {
|
selected_customer = {
|
||||||
'id': customer.id,
|
'id': customer.id,
|
||||||
'name': customer.name
|
'name': customer.name,
|
||||||
|
'wallet_balance': float(customer.wallet_balance)
|
||||||
}
|
}
|
||||||
except Customer.DoesNotExist:
|
except Customer.DoesNotExist:
|
||||||
# Клиент был удален - очищаем кэш
|
# Клиент был удален - очищаем кэш
|
||||||
@@ -218,7 +221,8 @@ def pos_terminal(request):
|
|||||||
if not selected_customer:
|
if not selected_customer:
|
||||||
selected_customer = {
|
selected_customer = {
|
||||||
'id': system_customer.id,
|
'id': system_customer.id,
|
||||||
'name': system_customer.name
|
'name': system_customer.name,
|
||||||
|
'wallet_balance': float(system_customer.wallet_balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Пытаемся получить сохраненную корзину из Redis
|
# Пытаемся получить сохраненную корзину из Redis
|
||||||
@@ -280,11 +284,12 @@ def pos_terminal(request):
|
|||||||
'name': current_warehouse.name
|
'name': current_warehouse.name
|
||||||
},
|
},
|
||||||
'warehouses': warehouses_list,
|
'warehouses': warehouses_list,
|
||||||
'system_customer': {
|
'system_customer_json': json.dumps({
|
||||||
'id': system_customer.id,
|
'id': system_customer.id,
|
||||||
'name': system_customer.name
|
'name': system_customer.name,
|
||||||
},
|
'wallet_balance': float(system_customer.wallet_balance)
|
||||||
'selected_customer': selected_customer, # Текущий выбранный клиент (из Redis или системный)
|
}),
|
||||||
|
'selected_customer_json': json.dumps(selected_customer), # Текущий выбранный клиент (из Redis или системный)
|
||||||
'cart_data': json.dumps(cart_data), # Сохраненная корзина из Redis
|
'cart_data': json.dumps(cart_data), # Сохраненная корзина из Redis
|
||||||
'title': 'POS Terminal',
|
'title': 'POS Terminal',
|
||||||
}
|
}
|
||||||
@@ -355,14 +360,16 @@ def set_customer(request, customer_id):
|
|||||||
redis_key = f'pos:customer:{request.user.id}:{current_warehouse.id}'
|
redis_key = f'pos:customer:{request.user.id}:{current_warehouse.id}'
|
||||||
customer_data = {
|
customer_data = {
|
||||||
'customer_id': customer.id,
|
'customer_id': customer.id,
|
||||||
'customer_name': customer.name
|
'customer_name': customer.name,
|
||||||
|
'wallet_balance': float(customer.wallet_balance)
|
||||||
}
|
}
|
||||||
cache.set(redis_key, customer_data, timeout=7200) # 2 часа
|
cache.set(redis_key, customer_data, timeout=7200) # 2 часа
|
||||||
|
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'success': True,
|
'success': True,
|
||||||
'customer_id': customer.id,
|
'customer_id': customer.id,
|
||||||
'customer_name': customer.name
|
'customer_name': customer.name,
|
||||||
|
'wallet_balance': float(customer.wallet_balance)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user