adding devices
This commit is contained in:
parent
5c5625595e
commit
a123d9bb27
Binary file not shown.
Binary file not shown.
@ -191,12 +191,7 @@
|
|||||||
|
|
||||||
<div class="px-4 mt-2">
|
<div class="px-4 mt-2">
|
||||||
<div class="d-flex gap-2 mb-2">
|
<div class="d-flex gap-2 mb-2">
|
||||||
<select id="customerSelect" class="form-select form-select-sm shadow-none" onchange="onCustomerChange()">
|
<div class="position-relative w-100"><input type="hidden" id="customerSelect" value="" onchange="onCustomerChange()"><div class="input-group input-group-sm"><span class="input-group-text bg-white border-end-0"><i class="bi bi-search"></i></span><input type="text" id="customerSearchInput" class="form-control border-start-0 shadow-none" placeholder="{% trans 'Search Name / Phone...' %}" autocomplete="off"><button class="btn btn-outline-secondary border-start-0" type="button" onclick="clearCustomerSelection()" id="clearCustomerBtn" style="display:none;"><i class="bi bi-x"></i></button></div><div id="customerSearchResults" class="list-group position-absolute w-100 shadow-sm d-none" style="z-index: 1050; max-height: 250px; overflow-y: auto; top: 100%;"></div></div>
|
||||||
<option value="">{% trans "Walking Customer" %}</option>
|
|
||||||
{% for customer in customers %}
|
|
||||||
<option value="{{ customer.id }}">{{ customer.name }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
<button class="btn btn-sm btn-outline-primary shadow-none" data-bs-toggle="modal" data-bs-target="#addCustomerModal">
|
<button class="btn btn-sm btn-outline-primary shadow-none" data-bs-toggle="modal" data-bs-target="#addCustomerModal">
|
||||||
<i class="bi bi-person-plus"></i>
|
<i class="bi bi-person-plus"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -1013,6 +1008,8 @@
|
|||||||
if (data.success) {
|
if (data.success) {
|
||||||
cart = data.items;
|
cart = data.items;
|
||||||
document.getElementById('customerSelect').value = data.customer_id || "";
|
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();
|
renderCart();
|
||||||
onCustomerChange();
|
onCustomerChange();
|
||||||
updateHeldCount();
|
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 = `
|
||||||
|
<div>
|
||||||
|
<div class="fw-bold small">${c.name}</div>
|
||||||
|
<div class="text-muted" style="font-size: 0.7rem;">${c.phone || ''}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
item.onclick = () => selectCustomer(c.id, c.name);
|
||||||
|
resultsContainer.appendChild(item);
|
||||||
|
});
|
||||||
|
resultsContainer.classList.remove('d-none');
|
||||||
|
} else {
|
||||||
|
resultsContainer.innerHTML = '<div class="list-group-item text-muted small">{% trans "No results found" %}</div>';
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
{% endlocalize %}
|
{% endlocalize %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -35,6 +35,11 @@
|
|||||||
<i class="bi bi-credit-card me-2"></i>{% trans "Payment Methods" %}
|
<i class="bi bi-credit-card me-2"></i>{% trans "Payment Methods" %}
|
||||||
</button>
|
</button>
|
||||||
</li>
|
</li>
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link fw-bold px-4" id="devices-tab" data-bs-toggle="pill" data-bs-target="#devices" type="button" role="tab">
|
||||||
|
<i class="bi bi-printer me-2"></i>{% trans "Devices" %}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button class="nav-link fw-bold px-4" id="loyalty-tab" data-bs-toggle="pill" data-bs-target="#loyalty" type="button" role="tab">
|
<button class="nav-link fw-bold px-4" id="loyalty-tab" data-bs-toggle="pill" data-bs-target="#loyalty" type="button" role="tab">
|
||||||
<i class="bi bi-star me-2"></i>{% trans "Loyalty System" %}
|
<i class="bi bi-star me-2"></i>{% trans "Loyalty System" %}
|
||||||
@ -289,6 +294,147 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Devices Tab -->
|
||||||
|
<div class="tab-pane fade" id="devices" role="tabpanel">
|
||||||
|
<div class="card shadow-sm border-0 glassmorphism mb-4">
|
||||||
|
<div class="card-header bg-transparent border-0 py-3 d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="card-title mb-0 fw-bold">{% trans "Hardware Devices" %}</h5>
|
||||||
|
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addDeviceModal">
|
||||||
|
<i class="bi bi-plus-lg me-1"></i> {% trans "Add Device" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="bg-light">
|
||||||
|
<tr>
|
||||||
|
<th class="ps-4">{% trans "Name" %}</th>
|
||||||
|
<th>{% trans "Type" %}</th>
|
||||||
|
<th>{% trans "Connection" %}</th>
|
||||||
|
<th>{% trans "Status" %}</th>
|
||||||
|
<th class="text-end pe-4">{% trans "Actions" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for device in devices %}
|
||||||
|
<tr>
|
||||||
|
<td class="ps-4 fw-bold">{{ device.name }}</td>
|
||||||
|
<td>{{ device.get_device_type_display }}</td>
|
||||||
|
<td>
|
||||||
|
{{ device.get_connection_type_display }}
|
||||||
|
{% if device.ip_address %}
|
||||||
|
<small class="d-block text-muted">{{ device.ip_address }}{% if device.port %}:{{ device.port }}{% endif %}</small>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if device.is_active %}
|
||||||
|
<span class="badge bg-success-soft text-success">{% trans "Active" %}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge bg-danger-soft text-danger">{% trans "Inactive" %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-end pe-4">
|
||||||
|
<button class="btn btn-sm btn-light text-primary" data-bs-toggle="modal" data-bs-target="#editDeviceModal{{ device.id }}">
|
||||||
|
<i class="bi bi-pencil"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-light text-danger" data-bs-toggle="modal" data-bs-target="#deleteDeviceModal{{ device.id }}">
|
||||||
|
<i class="bi bi-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Edit Device Modal -->
|
||||||
|
<div class="modal fade" id="editDeviceModal{{ device.id }}" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content border-0">
|
||||||
|
<form action="{% url 'edit_device' device.id %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title fw-bold">{% trans "Edit Device" %}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Device Name" %}</label>
|
||||||
|
<input type="text" name="name" class="form-control" value="{{ device.name }}" required>
|
||||||
|
</div>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Type" %}</label>
|
||||||
|
<select name="device_type" class="form-select">
|
||||||
|
<option value="printer" {% if device.device_type == 'printer' %}selected{% endif %}>{% trans "Printer" %}</option>
|
||||||
|
<option value="scanner" {% if device.device_type == 'scanner' %}selected{% endif %}>{% trans "Scanner" %}</option>
|
||||||
|
<option value="scale" {% if device.device_type == 'scale' %}selected{% endif %}>{% trans "Weight Scale" %}</option>
|
||||||
|
<option value="display" {% if device.device_type == 'display' %}selected{% endif %}>{% trans "Customer Display" %}</option>
|
||||||
|
<option value="other" {% if device.device_type == 'other' %}selected{% endif %}>{% trans "Other" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Connection" %}</label>
|
||||||
|
<select name="connection_type" class="form-select">
|
||||||
|
<option value="network" {% if device.connection_type == 'network' %}selected{% endif %}>{% trans "Network (IP)" %}</option>
|
||||||
|
<option value="usb" {% if device.connection_type == 'usb' %}selected{% endif %}>{% trans "USB" %}</option>
|
||||||
|
<option value="bluetooth" {% if device.connection_type == 'bluetooth' %}selected{% endif %}>{% trans "Bluetooth" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<label class="form-label fw-semibold">{% trans "IP Address" %}</label>
|
||||||
|
<input type="text" name="ip_address" class="form-control" value="{{ device.ip_address|default:'' }}">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Port" %}</label>
|
||||||
|
<input type="number" name="port" class="form-control" value="{{ device.port|default:'' }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-switch mt-3">
|
||||||
|
<input class="form-check-input" type="checkbox" name="is_active" {% if device.is_active %}checked{% endif %}>
|
||||||
|
<label class="form-check-label">{% trans "Active" %}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer bg-light border-0">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||||
|
<button type="submit" class="btn btn-primary">{% trans "Save Changes" %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Delete Device Modal -->
|
||||||
|
<div class="modal fade" id="deleteDeviceModal{{ device.id }}" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content border-0">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title fw-bold">{% trans "Confirm Delete" %}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>{% trans "Are you sure you want to delete" %} <strong>{{ device.name }}</strong>?</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer bg-light border-0">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||||
|
<a href="{% url 'delete_device' device.id %}" class="btn btn-danger">{% trans "Delete" %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="5" class="text-center py-5">
|
||||||
|
<div class="text-muted">
|
||||||
|
<i class="bi bi-printer fs-1 d-block mb-3"></i>
|
||||||
|
{% trans "No devices configured." %}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Loyalty Tab -->
|
<!-- Loyalty Tab -->
|
||||||
<div class="tab-pane fade" id="loyalty" role="tabpanel">
|
<div class="tab-pane fade" id="loyalty" role="tabpanel">
|
||||||
<div class="card shadow-sm border-0 glassmorphism mb-4">
|
<div class="card shadow-sm border-0 glassmorphism mb-4">
|
||||||
@ -557,6 +703,63 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Add Device Modal -->
|
||||||
|
<div class="modal fade" id="addDeviceModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content border-0 shadow">
|
||||||
|
<form action="{% url 'add_device' %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title fw-bold">{% trans "Add New Device" %}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Device Name" %}</label>
|
||||||
|
<input type="text" name="name" class="form-control" required placeholder="e.g. Kitchen Printer">
|
||||||
|
</div>
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Type" %}</label>
|
||||||
|
<select name="device_type" class="form-select">
|
||||||
|
<option value="printer">{% trans "Printer" %}</option>
|
||||||
|
<option value="scanner">{% trans "Scanner" %}</option>
|
||||||
|
<option value="scale">{% trans "Weight Scale" %}</option>
|
||||||
|
<option value="display">{% trans "Customer Display" %}</option>
|
||||||
|
<option value="other">{% trans "Other" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Connection" %}</label>
|
||||||
|
<select name="connection_type" class="form-select">
|
||||||
|
<option value="network" selected>{% trans "Network (IP)" %}</option>
|
||||||
|
<option value="usb">{% trans "USB" %}</option>
|
||||||
|
<option value="bluetooth">{% trans "Bluetooth" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<label class="form-label fw-semibold">{% trans "IP Address" %}</label>
|
||||||
|
<input type="text" name="ip_address" class="form-control" placeholder="192.168.1.100">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Port" %}</label>
|
||||||
|
<input type="number" name="port" class="form-control" placeholder="9100">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-check form-switch mt-3">
|
||||||
|
<input class="form-check-input" type="checkbox" name="is_active" checked>
|
||||||
|
<label class="form-check-label">{% trans "Active" %}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer bg-light border-0">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
|
||||||
|
<button type="submit" class="btn btn-primary">{% trans "Save Device" %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
|
|||||||
@ -80,6 +80,7 @@ urlpatterns = [
|
|||||||
path('customers/edit/<int:pk>/', views.edit_customer, name='edit_customer'),
|
path('customers/edit/<int:pk>/', views.edit_customer, name='edit_customer'),
|
||||||
path('customers/delete/<int:pk>/', views.delete_customer, name='delete_customer'),
|
path('customers/delete/<int:pk>/', views.delete_customer, name='delete_customer'),
|
||||||
path('api/add-customer-ajax/', views.add_customer_ajax, name='add_customer_ajax'),
|
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
|
# Suppliers
|
||||||
path('suppliers/add/', views.add_supplier, name='add_supplier'),
|
path('suppliers/add/', views.add_supplier, name='add_supplier'),
|
||||||
@ -118,7 +119,13 @@ urlpatterns = [
|
|||||||
path('settings/loyalty/edit/<int:pk>/', views.edit_loyalty_tier, name='edit_loyalty_tier'),
|
path('settings/loyalty/edit/<int:pk>/', views.edit_loyalty_tier, name='edit_loyalty_tier'),
|
||||||
path('settings/loyalty/delete/<int:pk>/', views.delete_loyalty_tier, name='delete_loyalty_tier'),
|
path('settings/loyalty/delete/<int:pk>/', views.delete_loyalty_tier, name='delete_loyalty_tier'),
|
||||||
path('api/customer-loyalty/<int:pk>/', views.get_customer_loyalty_api, name='get_customer_loyalty_api'),
|
path('api/customer-loyalty/<int:pk>/', views.get_customer_loyalty_api, name='get_customer_loyalty_api'),
|
||||||
|
|
||||||
# WhatsApp
|
# WhatsApp
|
||||||
path('api/send-invoice-whatsapp/', views.send_invoice_whatsapp, name='send_invoice_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'),
|
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/<int:pk>/', views.edit_device, name='edit_device'),
|
||||||
|
path('settings/devices/delete/<int:pk>/', views.delete_device, name='delete_device'),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -23,7 +23,7 @@ from .models import ( Expense, ExpenseCategory,
|
|||||||
Quotation, QuotationItem,
|
Quotation, QuotationItem,
|
||||||
SaleReturn, SaleReturnItem, PurchaseReturn, PurchaseReturnItem,
|
SaleReturn, SaleReturnItem, PurchaseReturn, PurchaseReturnItem,
|
||||||
PaymentMethod, HeldSale, LoyaltyTier, LoyaltyTransaction
|
PaymentMethod, HeldSale, LoyaltyTier, LoyaltyTransaction
|
||||||
)
|
, Device)
|
||||||
import json
|
import json
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
@ -1019,11 +1019,13 @@ def settings_view(request):
|
|||||||
|
|
||||||
payment_methods = PaymentMethod.objects.all().order_by("name_en")
|
payment_methods = PaymentMethod.objects.all().order_by("name_en")
|
||||||
loyalty_tiers = LoyaltyTier.objects.all().order_by("min_points")
|
loyalty_tiers = LoyaltyTier.objects.all().order_by("min_points")
|
||||||
|
devices = Device.objects.all().order_by("name")
|
||||||
|
|
||||||
context = {
|
context = {
|
||||||
"settings": settings,
|
"settings": settings,
|
||||||
"payment_methods": payment_methods,
|
"payment_methods": payment_methods,
|
||||||
"loyalty_tiers": loyalty_tiers
|
"loyalty_tiers": loyalty_tiers,
|
||||||
|
"devices": devices
|
||||||
}
|
}
|
||||||
return render(request, "core/settings.html", context)
|
return render(request, "core/settings.html", context)
|
||||||
|
|
||||||
@ -1645,6 +1647,7 @@ def recall_held_sale_api(request, pk):
|
|||||||
data = {
|
data = {
|
||||||
'success': True,
|
'success': True,
|
||||||
'customer_id': held_sale.customer.id if held_sale.customer else None,
|
'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,
|
'items': held_sale.cart_data,
|
||||||
'total_amount': float(held_sale.total_amount),
|
'total_amount': float(held_sale.total_amount),
|
||||||
'notes': held_sale.notes
|
'notes': held_sale.notes
|
||||||
@ -2355,3 +2358,62 @@ def test_whatsapp_connection(request):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return JsonResponse({'success': False, 'error': str(e)})
|
return JsonResponse({'success': False, 'error': str(e)})
|
||||||
return JsonResponse({'success': False, 'error': _("Invalid request method.")})
|
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)})
|
||||||
@ -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')
|
|
||||||
38
debug_settings.py
Normal file
38
debug_settings.py
Normal file
@ -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">
|
||||||
|
<i class="bi bi-whatsapp me-2"></i>{% trans "WhatsApp Gateway" %}
|
||||||
|
</button>
|
||||||
|
</li>'
|
||||||
|
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('<!-- Add Tier Modal -->')
|
||||||
|
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.")
|
||||||
|
|
||||||
260
patch_settings_html.py
Normal file
260
patch_settings_html.py
Normal file
@ -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 <i class="bi bi-whatsapp me-2"></i>{% trans "WhatsApp Gateway" %}\n </button>\n li>'
|
||||||
|
|
||||||
|
insert_str = """
|
||||||
|
<li class="nav-item" role="presentation">
|
||||||
|
<button class="nav-link fw-bold px-4" id="devices-tab" data-bs-toggle="pill" data-bs-target="#devices" type="button" role="tab">
|
||||||
|
<i class="bi bi-hdd-network me-2"></i>{% trans "Devices" %}
|
||||||
|
</button>
|
||||||
|
</li>"
|
||||||
|
|
||||||
|
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 = """
|
||||||
|
<!-- Devices Tab -->
|
||||||
|
<div class="tab-pane fade" id="devices" role="tabpanel">
|
||||||
|
<div class="card shadow-sm border-0 glassmorphism mb-4">
|
||||||
|
<div class="card-header bg-transparent border-0 py-3 d-flex justify-content-between align-items-center">
|
||||||
|
<h5 class="card-title mb-0 fw-bold">{% trans "Connected Devices" %}</h5>
|
||||||
|
<button class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#addDeviceModal">
|
||||||
|
<i class="bi bi-plus-lg me-1"></i> {% trans "Add Device" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-hover align-middle mb-0">
|
||||||
|
<thead class="bg-light">
|
||||||
|
<tr>
|
||||||
|
<th class="ps-4">{% trans "Device Name" %}</th>
|
||||||
|
<th>{% trans "Type" %}</th>
|
||||||
|
<th>{% trans "Connection" %}</th>
|
||||||
|
<th>{% trans "IP / Port" %}</th>
|
||||||
|
<th>{% trans "Status" %}</th>
|
||||||
|
<th class="text-end pe-4">{% trans "Actions" %}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for device in devices %}
|
||||||
|
<tr>
|
||||||
|
<td class="ps-4 fw-bold">{{ device.name }}</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge bg-secondary-soft text-secondary">{{ device.get_device_type_display }}</span>
|
||||||
|
</td>
|
||||||
|
<td>{{ device.get_connection_type_display }}</td>
|
||||||
|
<td>
|
||||||
|
{% if device.ip_address %}
|
||||||
|
{{ device.ip_address }}{% if device.port %}:{{ device.port }}{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">-</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if device.is_active %}
|
||||||
|
<span class="badge bg-success-soft text-success">{% trans "Active" %}</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="badge bg-danger-soft text-danger">{% trans "Inactive" %}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="text-end pe-4">
|
||||||
|
<button class="btn btn-sm btn-light text-primary" data-bs-toggle="modal" data-bs-target="#editDeviceModal{{ device.id }}">
|
||||||
|
<i class="bi bi-pencil"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-sm btn-light text-danger" data-bs-toggle="modal" data-bs-target="#deleteDeviceModal{{ device.id }}">
|
||||||
|
<i class="bi bi-trash"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<!-- Edit Device Modal -->
|
||||||
|
<div class="modal fade" id="editDeviceModal{{ device.id }}" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content border-0">
|
||||||
|
<form action="{% url 'edit_device' device.id %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title fw-bold">{% trans "Edit Device" %}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Device Name" %}</label>
|
||||||
|
<input type="text" name="name" class="form-control" value="{{ device.name }}" required>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Device Type" %}</label>
|
||||||
|
<select name="device_type" class="form-select">
|
||||||
|
<option value="printer" {% if device.device_type == 'printer' %}selected{% endif %}>{% trans "Printer" %}</option>
|
||||||
|
<option value="scanner" {% if device.device_type == 'scanner' %}selected{% endif %}>{% trans "Scanner" %}</option>
|
||||||
|
<option value="scale" {% if device.device_type == 'scale' %}selected{% endif %}>{% trans "Weight Scale" %}</option>
|
||||||
|
<option value="display" {% if device.device_type == 'display' %}selected{% endif %}>{% trans "Customer Display" %}</option>
|
||||||
|
<option value="other" {% if device.device_type == 'other' %}selected{% endif %}>{% trans "Other" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Connection Type" %}</label>
|
||||||
|
<select name="connection_type" class="form-select">
|
||||||
|
<option value="network" {% if device.connection_type == 'network' %}selected{% endif %}>{% trans "Network (IP)" %}</option>
|
||||||
|
<option value="usb" {% if device.connection_type == 'usb' %}selected{% endif %}>{% trans "USB" %}</option>
|
||||||
|
<option value="bluetooth" {% if device.connection_type == 'bluetooth' %}selected{% endif %}>{% trans "Bluetooth" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<label class="form-label fw-semibold">{% trans "IP Address" %}</label>
|
||||||
|
<input type="text" name="ip_address" class="form-control" value="{{ device.ip_address|default:'' }}" placeholder="192.168.1.100">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Port" %}</label>
|
||||||
|
<input type="number" name="port" class="form-control" value="{{ device.port|default:'' }}" placeholder="9100">
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" name="is_active" {% if device.is_active %}checked{% endif %}>
|
||||||
|
<label class="form-check-label">{% trans "Active" %}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer bg-light border-0">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||||
|
<button type="submit" class="btn btn-primary">{% trans "Save Changes" %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Delete Device Modal -->
|
||||||
|
<div class="modal fade" id="deleteDeviceModal{{ device.id }}" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content border-0">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title fw-bold">{% trans "Delete Device" %}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>{% trans "Are you sure you want to delete" %} <strong>{{ device.name }}</strong>?</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer bg-light border-0">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Cancel" %}</button>
|
||||||
|
<a href="{% url 'delete_device' device.id %}" class="btn btn-danger">{% trans "Delete" %}</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% empty %}
|
||||||
|
<tr>
|
||||||
|
<td colspan="6" class="text-center py-5">
|
||||||
|
<div class="text-muted">
|
||||||
|
<i class="bi bi-hdd-network fs-1 d-block mb-3"></i>
|
||||||
|
{% trans "No devices configured." %}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"
|
||||||
|
|
||||||
|
parts = content.split('<!-- Add Tier Modal -->')
|
||||||
|
if len(parts) > 1:
|
||||||
|
last_div = parts[0].rfind('</div>')
|
||||||
|
second_last_div = parts[0].rfind('</div>', 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 + '<!-- Add Tier Modal -->' + 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 = """
|
||||||
|
<!-- Add Device Modal -->
|
||||||
|
<div class="modal fade" id="addDeviceModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content border-0 shadow">
|
||||||
|
<form action="{% url 'add_device' %}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title fw-bold">{% trans "Add New Device" %}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-12">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Device Name" %}</label>
|
||||||
|
<input type="text" name="name" class="form-control" required placeholder="e.g. Kitchen Printer">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Device Type" %}</label>
|
||||||
|
<select name="device_type" class="form-select">
|
||||||
|
<option value="printer">{% trans "Printer" %}</option>
|
||||||
|
<option value="scanner">{% trans "Scanner" %}</option>
|
||||||
|
<option value="scale">{% trans "Weight Scale" %}</option>
|
||||||
|
<option value="display">{% trans "Customer Display" %}</option>
|
||||||
|
<option value="other">{% trans "Other" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Connection Type" %}</label>
|
||||||
|
<select name="connection_type" class="form-select">
|
||||||
|
<option value="network" selected>{% trans "Network (IP)" %}</option>
|
||||||
|
<option value="usb">{% trans "USB" %}</option>
|
||||||
|
<option value="bluetooth">{% trans "Bluetooth" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-8">
|
||||||
|
<label class="form-label fw-semibold">{% trans "IP Address" %}</label>
|
||||||
|
<input type="text" name="ip_address" class="form-control" placeholder="192.168.1.100">
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label fw-semibold">{% trans "Port" %}</label>
|
||||||
|
<input type="number" name="port" class="form-control" placeholder="9100">
|
||||||
|
</div>
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" name="is_active" checked>
|
||||||
|
<label class="form-check-label">{% trans "Active" %}</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer bg-light border-0">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
|
||||||
|
<button type="submit" class="btn btn-primary">{% trans "Add Device" %}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"
|
||||||
|
content = content.replace('{% endblock %}', modal_content + '\n{% endblock %}')
|
||||||
|
print("Added Add Device Modal.")
|
||||||
|
|
||||||
|
with open(file_path, 'w') as f:
|
||||||
|
f.write(content)
|
||||||
85
patch_views.py
Normal file
85
patch_views.py
Normal file
@ -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)
|
||||||
Loading…
x
Reference in New Issue
Block a user