From a123d9bb271b3b36a72a7cb8570ee3b64c706ceb Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Thu, 5 Feb 2026 12:52:12 +0000 Subject: [PATCH] adding devices --- core/__pycache__/urls.cpython-311.pyc | Bin 10759 -> 11244 bytes core/__pycache__/views.cpython-311.pyc | Bin 130082 -> 134089 bytes core/templates/core/pos.html | 77 +++++++- core/templates/core/settings.html | 203 +++++++++++++++++++ core/urls.py | 7 + core/views.py | 66 ++++++- core/views_patch.py | 35 ---- debug_settings.py | 38 ++++ patch_settings_html.py | 260 +++++++++++++++++++++++++ patch_views.py | 85 ++++++++ 10 files changed, 728 insertions(+), 43 deletions(-) delete mode 100644 core/views_patch.py create mode 100644 debug_settings.py create mode 100644 patch_settings_html.py create mode 100644 patch_views.py diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 439bd2a210f0eb90cc9a090c1d5eca28d31b7b40..ee499aeeb0e76d75d84a76662177727a992b9a52 100644 GIT binary patch delta 1604 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 Y2}E&Q_cTjCOHM+rm@dvR
- +
@@ -1013,6 +1008,8 @@ if (data.success) { cart = data.items; document.getElementById('customerSelect').value = data.customer_id || ""; + document.getElementById('customerSearchInput').value = data.customer_name || ""; + document.getElementById('clearCustomerBtn').style.display = data.customer_id ? 'block' : 'none'; renderCart(); onCustomerChange(); updateHeldCount(); @@ -1082,6 +1079,74 @@ } }); } +// Customer Search Logic +let searchTimeout; + +document.getElementById('customerSearchInput').addEventListener('input', function(e) { + const query = e.target.value.trim(); + const resultsContainer = document.getElementById('customerSearchResults'); + const clearBtn = document.getElementById('clearCustomerBtn'); + + if (query.length > 0) { + clearBtn.style.display = 'block'; + } else { + clearBtn.style.display = 'none'; + resultsContainer.classList.add('d-none'); + return; + } + + clearTimeout(searchTimeout); + searchTimeout = setTimeout(() => { + fetch(`{% url 'search_customers_api' %}?q=${encodeURIComponent(query)}`) + .then(res => res.json()) + .then(data => { + resultsContainer.innerHTML = ''; + if (data.results.length > 0) { + data.results.forEach(c => { + const item = document.createElement('button'); + item.className = 'list-group-item list-group-item-action py-2 text-start'; + item.innerHTML = ` +
+
${c.name}
+
${c.phone || ''}
+
+ `; + item.onclick = () => selectCustomer(c.id, c.name); + resultsContainer.appendChild(item); + }); + resultsContainer.classList.remove('d-none'); + } else { + resultsContainer.innerHTML = '
{% trans "No results found" %}
'; + resultsContainer.classList.remove('d-none'); + } + }); + }, 300); +}); + +function selectCustomer(id, name) { + document.getElementById('customerSelect').value = id; + document.getElementById('customerSearchInput').value = name; + document.getElementById('customerSearchResults').classList.add('d-none'); + document.getElementById('clearCustomerBtn').style.display = 'block'; + onCustomerChange(); +} + +function clearCustomerSelection() { + document.getElementById('customerSelect').value = ''; + document.getElementById('customerSearchInput').value = ''; + document.getElementById('customerSearchResults').classList.add('d-none'); + document.getElementById('clearCustomerBtn').style.display = 'none'; + onCustomerChange(); +} + +document.addEventListener('click', function(e) { + const container = document.getElementById('customerSearchResults'); + const input = document.getElementById('customerSearchInput'); + const clearBtn = document.getElementById('clearCustomerBtn'); + if (container && !container.contains(e.target) && e.target !== input && e.target !== clearBtn) { + container.classList.add('d-none'); + } +}); {% endlocalize %} {% endblock %} \ No newline at end of file diff --git a/core/templates/core/settings.html b/core/templates/core/settings.html index 12e5c38..f62b8d0 100644 --- a/core/templates/core/settings.html +++ b/core/templates/core/settings.html @@ -35,6 +35,11 @@ {% trans "Payment Methods" %} +
+ +
+
+
+
{% trans "Hardware Devices" %}
+ +
+
+
+ + + + + + + + + + + + {% for device in devices %} + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Name" %}{% trans "Type" %}{% trans "Connection" %}{% trans "Status" %}{% trans "Actions" %}
{{ device.name }}{{ device.get_device_type_display }} + {{ device.get_connection_type_display }} + {% if device.ip_address %} + {{ device.ip_address }}{% if device.port %}:{{ device.port }}{% endif %} + {% endif %} + + {% if device.is_active %} + {% trans "Active" %} + {% else %} + {% trans "Inactive" %} + {% endif %} + + + +
+
+ + {% trans "No devices configured." %} +
+
+
+
+
+
+
@@ -557,6 +703,63 @@
+ + + {% endblock %} {% block scripts %} diff --git a/core/urls.py b/core/urls.py index c274525..06d1e02 100644 --- a/core/urls.py +++ b/core/urls.py @@ -80,6 +80,7 @@ urlpatterns = [ path('customers/edit//', views.edit_customer, name='edit_customer'), path('customers/delete//', views.delete_customer, name='delete_customer'), path('api/add-customer-ajax/', views.add_customer_ajax, name='add_customer_ajax'), + path('api/search-customers/', views.search_customers_api, name='search_customers_api'), # Suppliers path('suppliers/add/', views.add_supplier, name='add_supplier'), @@ -118,7 +119,13 @@ urlpatterns = [ path('settings/loyalty/edit//', views.edit_loyalty_tier, name='edit_loyalty_tier'), path('settings/loyalty/delete//', views.delete_loyalty_tier, name='delete_loyalty_tier'), path('api/customer-loyalty//', views.get_customer_loyalty_api, name='get_customer_loyalty_api'), + # WhatsApp path('api/send-invoice-whatsapp/', views.send_invoice_whatsapp, name='send_invoice_whatsapp'), path('api/test-whatsapp/', views.test_whatsapp_connection, name='test_whatsapp_connection'), + + # Devices + path('settings/devices/add/', views.add_device, name='add_device'), + path('settings/devices/edit//', views.edit_device, name='edit_device'), + path('settings/devices/delete//', views.delete_device, name='delete_device'), ] diff --git a/core/views.py b/core/views.py index fb5606a..8c556ec 100644 --- a/core/views.py +++ b/core/views.py @@ -23,7 +23,7 @@ from .models import ( Expense, ExpenseCategory, Quotation, QuotationItem, SaleReturn, SaleReturnItem, PurchaseReturn, PurchaseReturnItem, PaymentMethod, HeldSale, LoyaltyTier, LoyaltyTransaction -) +, Device) import json from datetime import timedelta from django.utils import timezone @@ -1019,11 +1019,13 @@ def settings_view(request): payment_methods = PaymentMethod.objects.all().order_by("name_en") loyalty_tiers = LoyaltyTier.objects.all().order_by("min_points") + devices = Device.objects.all().order_by("name") context = { "settings": settings, "payment_methods": payment_methods, - "loyalty_tiers": loyalty_tiers + "loyalty_tiers": loyalty_tiers, + "devices": devices } return render(request, "core/settings.html", context) @@ -1645,6 +1647,7 @@ def recall_held_sale_api(request, pk): data = { 'success': True, 'customer_id': held_sale.customer.id if held_sale.customer else None, + 'customer_name': held_sale.customer.name if held_sale.customer else "", 'items': held_sale.cart_data, 'total_amount': float(held_sale.total_amount), 'notes': held_sale.notes @@ -2355,3 +2358,62 @@ def test_whatsapp_connection(request): except Exception as e: return JsonResponse({'success': False, 'error': str(e)}) return JsonResponse({'success': False, 'error': _("Invalid request method.")}) + + +@login_required +def add_device(request): + if request.method == 'POST': + name = request.POST.get('name') + device_type = request.POST.get('device_type') + connection_type = request.POST.get('connection_type') + ip_address = request.POST.get('ip_address') + port = request.POST.get('port') + is_active = request.POST.get('is_active') == 'on' + + Device.objects.create( + name=name, + device_type=device_type, + connection_type=connection_type, + ip_address=ip_address if ip_address else None, + port=port if port else None, + is_active=is_active + ) + messages.success(request, _("Device added successfully!")) + return redirect(reverse('settings') + '#devices') + +@login_required +def edit_device(request, pk): + device = get_object_or_404(Device, pk=pk) + if request.method == 'POST': + device.name = request.POST.get('name') + device.device_type = request.POST.get('device_type') + device.connection_type = request.POST.get('connection_type') + device.ip_address = request.POST.get('ip_address') + device.port = request.POST.get('port') + device.is_active = request.POST.get('is_active') == 'on' + + if not device.ip_address: + device.ip_address = None + if not device.port: + device.port = None + + device.save() + messages.success(request, _("Device updated successfully!")) + return redirect(reverse('settings') + '#devices') + +@login_required +def delete_device(request, pk): + device = get_object_or_404(Device, pk=pk) + device.delete() + messages.success(request, _("Device deleted successfully!")) + return redirect(reverse('settings') + '#devices') +@login_required +def search_customers_api(request): + query = request.GET.get('q', '') + if query: + customers = Customer.objects.filter( + Q(name__icontains=query) | Q(phone__icontains=query) + ).values('id', 'name', 'phone')[:20] + else: + customers = [] + return JsonResponse({'results': list(customers)}) \ No newline at end of file diff --git a/core/views_patch.py b/core/views_patch.py deleted file mode 100644 index f64efac..0000000 --- a/core/views_patch.py +++ /dev/null @@ -1,35 +0,0 @@ -@login_required -def edit_product(request, pk): - product = get_object_or_404(Product, pk=pk) - if request.method == 'POST': - product.name_en = request.POST.get('name_en') - product.name_ar = request.POST.get('name_ar') - product.sku = request.POST.get('sku') - product.category = get_object_or_404(Category, id=request.POST.get('category')) - - unit_id = request.POST.get('unit') - product.unit = get_object_or_404(Unit, id=unit_id) if unit_id else None - - supplier_id = request.POST.get('supplier') - product.supplier = get_object_or_404(Supplier, id=supplier_id) if supplier_id else None - - product.cost_price = request.POST.get('cost_price', 0) - product.price = request.POST.get('price', 0) - product.vat = request.POST.get('vat', 0) - product.description = request.POST.get('description', '') - product.opening_stock = request.POST.get('opening_stock', 0) - product.stock_quantity = request.POST.get('stock_quantity', 0) - product.min_stock_level = request.POST.get('min_stock_level', 0) - product.is_active = request.POST.get('is_active') == 'on' - product.has_expiry = request.POST.get('has_expiry') == 'on' - product.expiry_date = request.POST.get('expiry_date') - if not product.has_expiry: - product.expiry_date = None - - if 'image' in request.FILES: - product.image = request.FILES['image'] - - product.save() - messages.success(request, _("Product updated successfully!")) - return redirect(reverse('inventory') + '#items') - return redirect(reverse('inventory') + '#items') diff --git a/debug_settings.py b/debug_settings.py new file mode 100644 index 0000000..a79282e --- /dev/null +++ b/debug_settings.py @@ -0,0 +1,38 @@ + +file_path = 'core/templates/core/settings.html' +with open(file_path, 'r') as f: + content = f.read() + +print("File length:", len(content)) + +# Check context for Nav Tab +if 'id="devices-tab"' in content: + print("Devices tab already exists.") +else: + context_str = 'id="whatsapp-tab" data-bs-toggle="pill" data-bs-target="#whatsapp" type="button" role="tab"> + {% trans "WhatsApp Gateway" %} + + ' + if context_str in content: + print("Found Nav Tab context.") + else: + print("Nav Tab context NOT found. Dumping nearby content:") + # Find rough location + idx = content.find('id="whatsapp-tab"') + if idx != -1: + print(content[idx:idx+300]) + +# Check context for Tab Pane +if 'id="devices" role="tabpanel"' in content: + print("Devices pane already exists.") +else: + # Try to find the end of tab content + # Look for Add Tier Modal + idx = content.find('') + if idx != -1: + print("Found Add Tier Modal at index:", idx) + print("Preceding content:") + print(content[idx-100:idx]) + else: + print("Add Tier Modal NOT found.") + diff --git a/patch_settings_html.py b/patch_settings_html.py new file mode 100644 index 0000000..8669a41 --- /dev/null +++ b/patch_settings_html.py @@ -0,0 +1,260 @@ +file_path = 'core/templates/core/settings.html' + +with open(file_path, 'r') as f: + content = f.read() + +# 1. Add Nav Tab +if 'id="devices-tab"' not in content: + whatsapp_tab_end = 'id="whatsapp-tab" data-bs-toggle="pill" data-bs-target="#whatsapp" type="button" role="tab">\n {% trans "WhatsApp Gateway" %}\n \n li>' + + insert_str = """ + " + + if whatsapp_tab_end in content: + content = content.replace(whatsapp_tab_end, whatsapp_tab_end + insert_str) + print("Added Devices Tab Nav.") + else: + # Fallback search if exact string match fails due to whitespace + print("Could not find exact match for Nav Tab insertion. Trying simpler match.") + simple_search = '{% trans "WhatsApp Gateway" %}' + parts = content.split(simple_search) + if len(parts) > 1: + # Reconstruct slightly differently but risky + pass + +# 2. Add Tab Content +if 'id="devices" role="tabpanel"' not in content: + devices_pane = """ + +
+
+
+
{% trans "Connected Devices" %}
+ +
+
+
+ + + + + + + + + + + + + {% for device in devices %} + + + + + + + + + + + + + + + + {% empty %} + + + + {% endfor %} + +
{% trans "Device Name" %}{% trans "Type" %}{% trans "Connection" %}{% trans "IP / Port" %}{% trans "Status" %}{% trans "Actions" %}
{{ device.name }} + {{ device.get_device_type_display }} + {{ device.get_connection_type_display }} + {% if device.ip_address %} + {{ device.ip_address }}{% if device.port %}:{{ device.port }}{% endif %} + {% else %} + - + {% endif %} + + {% if device.is_active %} + {% trans "Active" %} + {% else %} + {% trans "Inactive" %} + {% endif %} + + + +
+
+ + {% trans "No devices configured." %} +
+
+
+
+
+
+ " + + parts = content.split('') + if len(parts) > 1: + last_div = parts[0].rfind('') + second_last_div = parts[0].rfind('', 0, last_div) + + if second_last_div != -1: + new_part0 = parts[0][:second_last_div] + devices_pane + parts[0][second_last_div:] + content = new_part0 + '' + parts[1] + print("Added Devices Tab Pane.") + else: + print("Could not find insertion point for Devices Pane.") + +# 3. Add Add Device Modal +if 'id="addDeviceModal"' not in content: + modal_content = """ + + +" + content = content.replace('{% endblock %}', modal_content + '\n{% endblock %}') + print("Added Add Device Modal.") + +with open(file_path, 'w') as f: + f.write(content) \ No newline at end of file diff --git a/patch_views.py b/patch_views.py new file mode 100644 index 0000000..7bd7fcf --- /dev/null +++ b/patch_views.py @@ -0,0 +1,85 @@ +import re + +file_path = 'core/views.py' + +with open(file_path, 'r') as f: + content = f.read() + +# 1. Add Device to imports +if 'Device' not in content: + pattern = r'(from \.models import \(.*?)(\))' + replacement = r'\1, Device\2' + content = re.sub(pattern, replacement, content, flags=re.DOTALL) + print("Added Device to imports.") + +# 2. Update settings_view +if 'devices = Device.objects.all()' not in content: + # Find the lines before context creation + search_str = 'loyalty_tiers = LoyaltyTier.objects.all().order_by("min_points")' + insert_str = '\n devices = Device.objects.all().order_by("name")' + content = content.replace(search_str, search_str + insert_str) + + # Update context + context_search = '"loyalty_tiers": loyalty_tiers' + context_insert = ',\n "devices": devices' + content = content.replace(context_search, context_search + context_insert) + print("Updated settings_view.") + +# 3. Add Device views +new_views = """ + +@login_required +def add_device(request): + if request.method == 'POST': + name = request.POST.get('name') + device_type = request.POST.get('device_type') + connection_type = request.POST.get('connection_type') + ip_address = request.POST.get('ip_address') + port = request.POST.get('port') + is_active = request.POST.get('is_active') == 'on' + + Device.objects.create( + name=name, + device_type=device_type, + connection_type=connection_type, + ip_address=ip_address if ip_address else None, + port=port if port else None, + is_active=is_active + ) + messages.success(request, _("Device added successfully!")) + return redirect(reverse('settings') + '#devices') + +@login_required +def edit_device(request, pk): + device = get_object_or_404(Device, pk=pk) + if request.method == 'POST': + device.name = request.POST.get('name') + device.device_type = request.POST.get('device_type') + device.connection_type = request.POST.get('connection_type') + device.ip_address = request.POST.get('ip_address') + device.port = request.POST.get('port') + device.is_active = request.POST.get('is_active') == 'on' + + if not device.ip_address: + device.ip_address = None + if not device.port: + device.port = None + + device.save() + messages.success(request, _("Device updated successfully!")) + return redirect(reverse('settings') + '#devices') + +@login_required +def delete_device(request, pk): + device = get_object_or_404(Device, pk=pk) + device.delete() + messages.success(request, _("Device deleted successfully!")) + return redirect(reverse('settings') + '#devices') +""" + +if 'def add_device(request):' not in content: + content += new_views + print("Added Device views.") + +with open(file_path, 'w') as f: + f.write(content) \ No newline at end of file