I’m creating a POS application in Django. Currently I’m using the template bellow for adding items to a current order, modifying quantities, deletes some items that are unecessary to be in the current order, and finaly, after adding all needed items, to click on the Checkout button for validation.
{% load multiply %}
<!DOCTYPE html>
<html>
<head>
<title>Order Details</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<a class="navbar-brand" href="{% url 'posApp:home' %}">POS System</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="{% url 'posApp:category_list' %}">Categories</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'posApp:order_list' %}">Order list</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'posApp:create_order' %}">Create Order</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'posApp:add_category' %}">Add Category</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'posApp:product_list' %}">Products list</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'posApp:add_product' %}">Add Product</a>
</li>
</ul>
</div>
</nav>
<div class="container mt-5">
<h1>Order {{ order.id }}</h1>
<div class="row">
<div class="col-md-6">
<form id="add-item-form" method="post">
{% csrf_token %}
<div class="form-group">
<label for="barcode">Barcode</label>
<input type="text" class="form-control" id="barcode" name="barcode" placeholder="Enter Barcode" required>
</div>
<div class="form-group">
<label for="quantity">Quantity</label>
<input type="number" class="form-control" id="quantity" name="quantity" placeholder="Quantity" min="1" value="1" required>
</div>
<button type="submit" class="btn btn-primary">Add Product</button>
</form>
<div id="error-message" class="text-danger mt-2"></div>
</div>
<div class="col-md-6">
<h2>Current Order</h2>
<ul class="list-group" id="order-items">
{% for item in order.items.all %}
<li class="list-group-item" id="item-{{ item.id }}">
{{ item.product.name }} -
<input type="number" class="form-control d-inline item-quantity" style="width: 70px;" value="{{ item.quantity }}" min="1" data-item-id="{{ item.id }}" data-order-id="{{ order.id }}">
x {{ item.product.price }} + Tax: {{ item.tax_amount }} = <span class="item-total">{{ item.price|add:item.tax_amount }}</span>
<button class="btn btn-danger btn-sm remove-item" data-order-id="{{ order.id }}" data-item-id="{{ item.id }}">Remove</button>
</li>
{% endfor %}
</ul>
<p class="mt-3"><strong>Total (before tax):</strong> <span id="total-amount">{{ order.total_amount }}</span></p>
<p class="mt-3"><strong>Total Tax:</strong> <span id="total-tax">{{ order.total_tax_amount }}</span></p>
<p class="mt-3"><strong>Total (with tax):</strong> <span id="total-with-tax">{{ order.total_amount_with_tax }}</span></p>
<a href="{% url 'posApp:checkout' order.id %}" class="btn btn-success">Checkout</a>
</div>
</div>
</div>
<script>
$(document).ready(function() {
function updateOrderTotals(totalWithoutTax, totalTax, totalWithTax) {
$('#total-amount').text(totalWithoutTax.toFixed(2));
$('#total-tax').text(totalTax.toFixed(2));
$('#total-with-tax').text(totalWithTax.toFixed(2));
}
$('#add-item-form').on('submit', function(e) {
e.preventDefault();
var barcode = $('#barcode').val();
var quantity = parseInt($('#quantity').val());
$.ajax({
type: 'POST',
url: "{% url 'posApp:add_to_order' order.id %}",
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'barcode': barcode,
'quantity': quantity
},
success: function(response) {
if (response.success) {
var itemElement = $('#item-' + response.order_item_id);
if (itemElement.length) {
itemElement.find('.item-quantity').val(response.quantity);
itemElement.find('.item-total').text((response.quantity * parseFloat(response.price)).toFixed(2));
} else {
var itemHTML = '<li class="list-group-item" id="item-' + response.order_item_id + '">' +
response.product_name + ' - ' +
'<input type="number" class="form-control d-inline item-quantity" style="width: 70px;" value="' + response.quantity + '" min="1" data-item-id="' + response.order_item_id + '" data-order-id="{{ order.id }}">' +
' x ' + parseFloat(response.price).toFixed(2) + ' + Tax: ' + parseFloat(response.tax_amount).toFixed(2) + ' = <span class="item-total">' + (response.quantity * parseFloat(response.price)).toFixed(2) + '</span>' +
'<button class="btn btn-danger btn-sm remove-item" data-order-id="{{ order.id }}" data-item-id="' + response.order_item_id + '">Remove</button>' +
'</li>';
$('#order-items').append(itemHTML);
}
updateOrderTotals(parseFloat(response.total_without_tax), parseFloat(response.total_tax), parseFloat(response.total_with_tax));
$('#barcode').val('');
$('#quantity').val(1);
$('#error-message').text('');
} else {
$('#error-message').text(response.error);
}
},
error: function(xhr, status, error) {
$('#error-message').text('An error occurred: ' + error);
}
});
});
$(document).on('change', 'input.item-quantity', function() {
var itemId = $(this).data('item-id');
var orderId = $(this).data('order-id');
var quantity = parseInt($(this).val());
$.ajax({
type: 'POST',
url: "{% url 'posApp:update_order_item_quantity' order.id 0 %}".slice(0, -2) + itemId + "/",
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'quantity': quantity
},
success: function(response) {
if (response.success) {
var itemElement = $('#item-' + response.order_item_id);
var totalPriceWithTax = parseFloat(response.total_price_with_tax);
var totalWithoutTax = parseFloat(response.total_without_tax);
var totalTax = parseFloat(response.total_tax);
var totalWithTax = parseFloat(response.total_with_tax);
itemElement.find('.item-total').text(totalPriceWithTax.toFixed(2));
$('#total-amount').text(totalWithoutTax.toFixed(2));
$('#total-tax').text(totalTax.toFixed(2));
$('#total-with-tax').text(totalWithTax.toFixed(2));
} else {
alert('Error updating item quantity.');
}
},
error: function(xhr, status, error) {
alert('An error occurred: ' + error);
}
});
});
$(document).on('click', '.remove-item', function() {
var itemId = $(this).data('item-id');
var orderId = $(this).data('order-id');
$.ajax({
type: 'POST',
url: "{% url 'posApp:remove_from_order' order.id 0 %}".slice(0, -2) + itemId + "/",
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
$('#item-' + response.order_item_id).remove();
updateOrderTotals(parseFloat(response.total_without_tax), parseFloat(response.total_tax), parseFloat(response.total_with_tax));
} else {
alert('Error removing item.');
}
},
error: function(xhr, status, error) {
alert('An error occurred: ' + error);
}
});
});
});
</script>
</body>
</html>
I decided to change the template to the Django template format, as it is bellow:
{% extends 'base.html' %}
{% load multiply %}
{% load static %}
{% block title %}
<title>Order Details</title>
{% endblock title %}
{% block content %}
<div class="container mt-5">
<h1>Order {{ order.id }}</h1>
<div class="row">
<div class="col-md-6">
<form id="add-item-form" method="post">
{% csrf_token %}
<div class="form-group">
<label for="barcode">Barcode</label>
<input type="text" class="form-control" id="barcode" name="barcode" placeholder="Enter Barcode" required>
</div>
<div class="form-group">
<label for="quantity">Quantity</label>
<input type="number" class="form-control" id="quantity" name="quantity" placeholder="Quantity" min="1" value="1" required>
</div>
<button type="submit" class="btn btn-primary">Add Product</button>
</form>
<div id="error-message" class="text-danger mt-2"></div>
</div>
<div class="col-md-6">
<h2>Current Order</h2>
<ul class="list-group" id="order-items">
{% for item in order.items.all %}
<li class="list-group-item" id="item-{{ item.id }}">
{{ item.product.name }} -
<input type="number" class="form-control d-inline item-quantity" style="width: 70px;" value="{{ item.quantity }}" min="1" data-item-id="{{ item.id }}" data-order-id="{{ order.id }}">
x {{ item.product.price }} + Tax: {{ item.tax_amount }} = <span class="item-total">{{ item.price|add:item.tax_amount }}</span>
<button class="btn btn-danger btn-sm remove-item" data-order-id="{{ order.id }}" data-item-id="{{ item.id }}">Remove</button>
</li>
{% endfor %}
</ul>
<p class="mt-3"><strong>Total (before tax):</strong> <span id="total-amount">{{ order.total_amount }}</span></p>
<p class="mt-3"><strong>Total Tax:</strong> <span id="total-tax">{{ order.total_tax_amount }}</span></p>
<p class="mt-3"><strong>Total (with tax):</strong> <span id="total-with-tax">{{ order.total_amount_with_tax }}</span></p>
<form action="{% url 'posApp:checkout' order.id %}" method="post">
{% csrf_token %}
<button type="submit" class="btn btn-success">Checkout</button>
</form>
</div>
</div>
</div>
<script>
$(document).ready(function() {
// Function to update order totals in the UI
function updateOrderTotals(totalWithoutTax, totalTax, totalWithTax) {
$('#total-amount').text(totalWithoutTax.toFixed(2));
$('#total-tax').text(totalTax.toFixed(2));
$('#total-with-tax').text(totalWithTax.toFixed(2));
}
// Add product to order
$('#add-item-form').on('submit', function(e) {
e.preventDefault();
var barcode = $('#barcode').val();
var quantity = parseInt($('#quantity').val());
$.ajax({
type: 'POST',
url: "{% url 'posApp:add_to_order' order.id %}",
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'barcode': barcode,
'quantity': quantity
},
success: function(response) {
if (response.success) {
var itemElement = $('#item-' + response.order_item_id);
if (itemElement.length) {
// Update existing item
itemElement.find('.item-quantity').val(response.quantity);
itemElement.find('.item-total').text((response.quantity * parseFloat(response.price)).toFixed(2));
} else {
// Add new item
var itemHTML = '<li class="list-group-item" id="item-' + response.order_item_id + '">' +
response.product_name + ' - ' +
'<input type="number" class="form-control d-inline item-quantity" style="width: 70px;" value="' + response.quantity + '" min="1" data-item-id="' + response.order_item_id + '" data-order-id="{{ order.id }}">' +
' x ' + parseFloat(response.price).toFixed(2) + ' + Tax: ' + parseFloat(response.tax_amount).toFixed(2) + ' = <span class="item-total">' + (response.quantity * parseFloat(response.price)).toFixed(2) + '</span>' +
'<button class="btn btn-danger btn-sm remove-item" data-order-id="{{ order.id }}" data-item-id="' + response.order_item_id + '">Remove</button>' +
'</li>';
$('#order-items').append(itemHTML);
}
updateOrderTotals(parseFloat(response.total_without_tax), parseFloat(response.total_tax), parseFloat(response.total_with_tax));
$('#barcode').val('');
$('#quantity').val(1);
$('#error-message').text('');
} else {
$('#error-message').text(response.error);
}
},
error: function(xhr, status, error) {
$('#error-message').text('An error occurred: ' + error);
}
});
});
// Update item quantity
$(document).on('change', 'input.item-quantity', function() {
var itemId = $(this).data('item-id');
var orderId = $(this).data('order-id');
var quantity = parseInt($(this).val());
$.ajax({
type: 'POST',
url: "{% url 'posApp:update_order_item_quantity' order.id 0 %}".slice(0, -2) + itemId + "/",
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}',
'quantity': quantity
},
success: function(response) {
if (response.success) {
var itemElement = $('#item-' + response.order_item_id);
var totalPriceWithTax = parseFloat(response.total_price_with_tax);
var totalWithoutTax = parseFloat(response.total_without_tax);
var totalTax = parseFloat(response.total_tax);
var totalWithTax = parseFloat(response.total_with_tax);
itemElement.find('.item-total').text(totalPriceWithTax.toFixed(2));
updateOrderTotals(totalWithoutTax, totalTax, totalWithTax);
} else {
alert('Error updating item quantity.');
}
},
error: function(xhr, status, error) {
alert('An error occurred: ' + error);
}
});
});
// Remove item from order
$(document).on('click', '.remove-item', function() {
var itemId = $(this).data('item-id');
var orderId = $(this).data('order-id');
$.ajax({
type: 'POST',
url: "{% url 'posApp:remove_from_order' order.id 0 %}".slice(0, -2) + itemId + "/",
data: {
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
success: function(response) {
if (response.success) {
$('#item-' + response.order_item_id).remove();
updateOrderTotals(parseFloat(response.total_without_tax), parseFloat(response.total_tax), parseFloat(response.total_with_tax));
} else {
alert('Error removing item.');
}
},
error: function(xhr, status, error) {
alert('An error occurred: ' + error);
}
});
});
});
</script>
{% endblock content %}
Unfortunately, this template does not working. It’s throwing this error message:Method Not Allowed (POST): /order/57/
Method Not Allowed: /order/57/
For more details, here is my views for the order details:
class AddToOrderView(View):
def post(self, request, order_id):
order = get_object_or_404(Order, id=order_id)
barcode = request.POST.get('barcode')
quantity = int(request.POST.get('quantity', 1))
try:
product = Product.objects.get(barcode=barcode)
except Product.DoesNotExist:
return JsonResponse({'success': False, 'error': 'Product does not exist in the database.'})
# Check if the product already exists in the order
order_item, created = OrderItem.objects.get_or_create(order=order, product=product)
if created:
order_item.quantity = quantity
else:
order_item.quantity += quantity
order_item.save()
# Recalculate totals
total_without_tax = sum(item.price for item in order.items.all())
total_tax = sum(item.tax_amount for item in order.items.all())
total_with_tax = total_without_tax + total_tax
total_price_with_tax = order_item.price + order_item.tax_amount
return JsonResponse({
'success': True,
'order_item_id': order_item.id,
'product_name': product.name,
'quantity': order_item.quantity,
'price': product.price,
'tax_amount': order_item.tax_amount,
'total_without_tax': total_without_tax,
'total_tax': total_tax,
'total_with_tax': total_with_tax,
'total_price_with_tax': total_price_with_tax,
'is_existing_item': not created
})
Actually,the existing solutions for similar case does not solving my issue.
Thanks