<repo_name>kwabena-aboah/POSsystem<file_sep>/catalog/migrations/0002_auto_20201218_1301.py
# Generated by Django 3.1.1 on 2020-12-18 13:01

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('catalog', '0001_initial'),
    ]

    operations = [
        migrations.RemoveField(
            model_name='product',
            name='quantity',
        ),
        migrations.AddField(
            model_name='product',
            name='selling_price',
            field=models.DecimalField(decimal_places=2, default=1, max_digits=7),
            preserve_default=False,
        ),
    ]
<file_sep>/customer/templates/customer/index.html
{% extends 'inventory/base.html' %}

{% block content %}
<div class="container">
    <div class="col-md-12 col-sm-12 col-lg-12">
    	<h2>{{ title }}</h2>
		<br>
		<form action="{% url 'customer' %}" method="post" class="form">
		  	{% csrf_token %}
		  	{{ form.non_field_errors }}
		  	<div class="wrapper form-control">
		  		<label for="{{ form.name.id_for_label}}">Name</label>
			  	{{ form.name }}<br>
			  	<label for="{{ form.phone.id_for_label}}">Phone</label>
			  	{{ form.phone }}<br>
			  	<label for="{{ form.email.id_for_label}}">Email</label>
			  	{{ form.email }}<br>
			  	<label for="{{ form.orders.id_for_label}}">Orders</label>
			  	{{ form.orders }}<br>
			  	<label for="{{ form.amount_paid.id_for_label}}">Amount paid</label>
			  	{{ form.amount_paid }}<br>
			  	<label for="{{ form.amount_to_pay.id_for_label}}">Amount to pay</label>
			  	{{ form.amount_to_pay }}<br>
			  	<label for="{{ form.payment_status.id_for_label}}">Payment Status</label>
			  	{{ form.payment_status }}<br>
			  	<button name="submit" class="btn btn-danger">Submit</button>
		  	</div>
		  	
		  </form>
		</div>
	</div>
{% endblock %}<file_sep>/customer/admin.py
from django.contrib import admin
from . models import Customer
from . forms import CustomerForm


class CustomerAdmin(admin.ModelAdmin):
    list_display = ('id', 'name', 'phone', 'email', 'amount_paid', 'amount_to_pay', 'payment_status',)
    list_filter = ('payment_status',)
    search_fields = ('name',)
    form = CustomerForm
    list_per_page = 10

    # def save_model(self, request, obj, form, change):
    #     recieve_customer = Customer.objects.create(id=id)
    #     if getattr(obj, 'created_by', True) is not None:
    #         obj.created_by = request.user
    #         obj.save()
    #     recieve_customer.save()
    #     super().save_model(request, obj, form, change)


admin.site.register(Customer, CustomerAdmin)
<file_sep>/catalog/urls.py
from django.urls import path
from django.conf.urls import url
from django.views.generic import RedirectView
from . import views
from . import apiviews

# app_name = "catalog"
urlpatterns = [
    url(r'^$', RedirectView.as_view(pattern_name='order')),
    path('', views.order, name='order'),
    path('addition/', views.addition, name='addition'),
    url(r'^order/add/(?P<product_id>[0-9A-Za-z_-]*/?$)',
        views.order_add_product, name='order_add_product'),
    url(r'^order/remove/(?P<product_id>[0-9]*)/?$',
        views.order_remove_product, name="order_remove_product"),
    url(r'^order/reset/?$', views.reset_order, name='reset_order'),
    url(r'^order/amount/?$', views.order_amount, name='order_get_amount'),
    url(r'^order/?$', views.order, name='order'),

    url(r'^pay/momo/$', views.payment_momo, name='payment_momo'),
    url(r'^pay/cash/$', views.payment_cash, name='payment_cash'),

    path('cash/<int:amount>/', views.cash, name='cash'),

    url(r'^view-order/(?P<order_id>[0-9]+)/?$', views.view_order,
        name='view_order'),
    url(r'^print-order/(?P<order_id>[0-9]+)/?$', views.print_order,
        name='print_order'),
    url(r'^print-current-order/?$', views.print_current_order,
        name='print_current_order'),

    url(r'^stock/?$', views.view_stock, name='view_stock'),

    # API URL's
    path('api/orders/current/', apiviews.current_order,
         name='api_current_order'),
    path('api/orders/current/items/', apiviews.current_order_items,
         name='api_current_order_items'),
    path('api/orders/current/items/<int:item_id>/',
         apiviews.current_order_item,
         name='api_product'),
    path('api/orders/current/items/patch/<int:item_id>/',
         apiviews.current_order_patch, name='api_current_order_patch'),
    path('api/orders/current/items/search/<str:item_id>/',
         apiviews.search_order_item, name='api_search_product'),
    path('api/pay/momo/', apiviews.momo_payment, name='api_momo_payment'),
    path('api/pay/cash/', apiviews.cash_payment, name='api_cash_payment'),
]
<file_sep>/catalog/tasks.py
# from celery import shared_task
from celery.task.schedules import crontab
from celery.decorators import periodic_task
from celery.utils.log import get_task_logger
import time
from datetime import timedelta, date
# import datetime
from plyer import notification

from .models import Product

logger = get_task_logger(__name__)


@periodic_task(
    run_every=(crontab(minute='*/30')),
    name="task_save_10_days_to_expire_product",
    ignore_result=True
)
def expiry_date():
    try:
        products = Product.objects.all()
        for product in products:
            dt = date.today() + timedelta(10)
            print(dt)
            if product.expiring_date > dt:
                logger.info("{pro} expires on {dt}. 10 days before the expiring date.".format(
                    pro=product.name, dt=dt))
                return notification.notify(
                    title="URGENT INFORMATION ON PRODUCT!",
                    message="{pro} expires on {dt}. 10 days before the expiring date.".format(
                        pro=product.name, dt=dt),
                    app_name="JKF-VICTORY LIMITED",
                    ticker='URGENT PRODUCT INFORMATION',
                    timeout=20
                )
                time.sleep(15)
            else:
                return notification.notify(
                    title="INVENTORY INFORMATION!",
                    message="Inventory up to Date!!!",
                    app_name="JKF-VICTORY LIMITED",
                    ticker='PRODUCT INFORMATION',
                    timeout=20
                )
                time.sleep(15)
    except Exception:
        return 'Field "name" does not exist.'
    return


@periodic_task(
    run_every=(crontab(minute='*/30')),
    name="task_save_expired_products",
    ignore_result=True
)
def expired():
    try:
        products = Product.objects.all()
        for product in products:
            dt = date.today()
            if product.expiring_date == dt:
                logger.info("{pro} has expired.".format(
                    pro=product.name))
                return notification.notify(
                    title="EXPIRED PRODUCT NOTIFICATION!",
                    message="{pro} has expired.".format(
                        pro=product.name),
                    app_name="JKF-VICTORY LIMITED",
                    ticker='URGENT PRODUCT INFORMATION',
                    timeout=15
                )
                time.sleep(30)
    except Exception:
        return 'Field "name" does not exist'
    return


@periodic_task(
    run_every=(crontab(minute='*/30')),
    name="task_stock_level",
    ignore_result=True
)
def stock_level():
    try:
        products = Product.objects.all()
        for product in products:
            if product.stock == product.minimum_stock:
                logger.info("{pro} has reached it's minimum stock level.".format(
                    pro=product.name))
                return notification.notify(
                    title="STOCK LEVEL NOTIFICATION!",
                    message="{pro} has reached it's minimum stock level.".format(
                        pro=product.name),
                    app_name="JKF-VICTORY LIMITED",
                    ticker='INVENTORY INFORMATION',
                    timeout=15
                )
                time.sleep(30)
    except Exception:
        return 'Field "name" does not exist'
    return
<file_sep>/catalog/admin.py
from datetime import date
import datetime
from django.contrib import admin
from django.contrib.admin import SimpleListFilter
from django.db.models import F
from import_export import resources
from . models import Store, Supplier, Category, Product, Order, Cash, Setting
from import_export.admin import ImportExportModelAdmin
from . forms import ProductForm


admin.site.disable_action('delete_selected')

admin.site.site_header = "JKF-VICTORY LIMITED"
admin.site.site_title = "JKF-VICTORY LIMITED ADMINISTRATION"
admin.site.index_title = "JKF-VICTORY LIMITED ADMINISTRATION"
admin.site.site_url = "/"


class ProductResource(resources.ModelResource):

    class Meta:
        model = Product
        fields = (
            'name',
            'price',
            'selling_price',
            'stock',
            'stock_applies',
            'minimum_stock',
            'expiring_date',
            'stock_level'
        )


class LowStockFilter(SimpleListFilter):
    title = 'Available Stock'
    parameter_name = 'available_stock'

    def lookups(self, request, model_admin):
        return (
            ('low', 'Low Stock'),
            ('high', 'High Stock')
        )

    def queryset(self, request, queryset):
        if self.value() == 'low':
            return queryset.filter(stock__lte=F('minimum_stock'))
        if self.value() == 'high':
            return queryset.filter(stock__gte=F('minimum_stock'))


class ExpiredStockFilter(SimpleListFilter):
    title = 'Expired Stock'
    parameter_name = 'expired_stock'

    def lookups(self, request, model_admin):
        return (
            ('expired', 'Expired Product'),
        )

    def queryset(self, request, queryset):
        if self.value() == 'expired':
            return queryset.filter(expiring_date__lte=datetime.datetime.today())


class SettingAdmin(admin.ModelAdmin):
    list_display = ('key', 'value')


admin.site.register(Setting, SettingAdmin)


class ProductAdmin(ImportExportModelAdmin, admin.ModelAdmin):
    actions = ['delete_selected']
    resource_class = ProductResource
    list_display = (
        'expired_field',
        'name',
        'price',
        'selling_price',
        'stock',
        'stock_applies',
        'minimum_stock',
        'expiring_date',
        'stock_level'
    )

    list_display_links = ('name',)
    search_fileds = ('name',)
    date_hierarchy = 'created_on'
    form = ProductForm
    list_per_page = 10

    list_filter = (LowStockFilter, ExpiredStockFilter,)

    def save_model(self, request, obj, form, change):
        if getattr(obj, 'created_by', True) is not None:
            obj.created_by = request.user
            obj.save()
        super().save_model(request, obj, form, change)


admin.site.register(Product, ProductAdmin)


class StoreAdmin(admin.ModelAdmin):
    list_display = ('name',)
    list_display_links = ('name',)
    search_fileds = ('code',)
    actions = ['delete_selected']
    list_per_page = 5


admin.site.register(Store, StoreAdmin)

admin.site.register(Category)


class SupplierAdmin(admin.ModelAdmin):
    list_display = ('name', 'email', 'tel', 'location', 'comments')
    list_display_links = ('name',)
    search_fileds = ('name', 'email',)
    actions = ['delete_selected']
    list_per_page = 10


admin.site.register(Supplier, SupplierAdmin)

# Safe deletion of orders


def safe_delete_order(modeladmin, request, queryset):
    queryset.filter(done=True).delete()


safe_delete_order.short_description = "Delete completed orders"


class OrderAdmin(admin.ModelAdmin):
    ''' Admin site configuration for Order Model'''
    # Add custom delete-if-not-done action
    actions = [safe_delete_order]
    # List page display configuration
    list_display = ('user', 'total_price', 'done', 'last_change')
    # sidebar filter configuration
    list_filter = ('done',)
    date_hierarchy = 'last_change'
    # order by not Done
    ordering = ('done',)
    list_per_page = 10


admin.site.register(Order, OrderAdmin)

admin.site.register(Cash)
<file_sep>/catalog/apiviews.py
import logging
import decimal
from django.shortcuts import get_object_or_404
from django.views.decorators.csrf import csrf_exempt
from django.http import (HttpResponse, JsonResponse, HttpResponseBadRequest)
from rest_framework import status
from rest_framework.response import Response
from django.contrib import messages
from .serializers import OrderSerializer, OrderItemSerializer
from . helper import (get_current_user_order,
                      get_can_negative_stock, product_list_from_order)
from . models import Product, Order_Item, Order, Cash


@csrf_exempt
def current_order(request):
    if not request.user.is_authenticated:
        return HttpResponse(status=status.HTTP_401_UNAUTHORIZED)

    order = get_current_user_order(request.user.username)

    if request.method == 'DELETE':
        for item in Order_Item.objects.filter(order=order):
            if item.product.stock_applies:
                item.product.stock += 1
                item.product.save()
            item.delete()

        order.total_price = 0
        order.save()
    serializer = OrderSerializer(order)
    return JsonResponse(serializer.data, safe=False)


def current_order_items(request):
    if not request.user.is_authenticated:
        return HttpResponse(status=status.HTTP_401_UNAUTHORIZED)

    order = get_current_user_order(request.user.username)

    order_items = Order_Item.objects.filter(order=order)
    serializer = OrderItemSerializer(order_items, many=True)
    return JsonResponse(serializer.data, safe=False)


@csrf_exempt
def current_order_item(request, item_id):
    if not request.user.is_authenticated:
        return HttpResponse(status=status.HTTP_401_UNAUTHORIZED)

    order = get_current_user_order(request.user.username)

    if request.method == 'DELETE':
        order_item = get_object_or_404(Order_Item, id=item_id)
        order_product = order_item.product

        if order_product.stock_applies:
            order_product.stock += 1
            order_product.save()

        order.total_price = (
            order.total_price -
            order_item.price).quantize(
                decimal.Decimal('0.01'))

        if order.total_price < 0:
            logging.error("prices below 0! "
                          "You might be running in to the "
                          "10 digit total order price limit")
            order.total_price = 0

        order.save()
        order_item.delete()

        order_items = Order_Item.objects.filter(order=order)
        serializer = OrderItemSerializer(order_items, many=True)
        return JsonResponse(serializer.data, safe=False)

    elif request.method == 'PUT':
        product = get_object_or_404(Product, id=item_id)

        if product.stock_applies:
            if product.stock < 1 and not get_can_negative_stock():
                # messages.info(request, "Insufficient stock")
                return JsonResponse('{message: "Insufficient stock"}',
                                    status=status.HTTP_400_BAD_REQUEST)

            product.stock -= 1
            product.save()

        Order_Item.objects.create(order=order, product=product,
                                  price=product.price, name=product.name)
        # Order_Item.objects.filter(id=item_id).update(quantity=order.quantity)
        order.total_price = order.get_total_cost()
        order.save()

        order_items = Order_Item.objects.filter(order=order)
        serializer = OrderItemSerializer(order_items, many=True)
        return JsonResponse(serializer.data, safe=False)


@csrf_exempt
def current_order_patch(request, item_id):
    if not request.user.is_authenticated:
        return HttpResponse(status=status.HTTP_401_UNAUTHORIZED)

    order = get_current_user_order(request.user.username)
    # new codes
    if request.method == 'DELETE':
        order_item = get_object_or_404(Order_Item, id=item_id)
        order_product = order_item.product

        if order_product.stock_applies:
            order_product.stock += 1
            order_product.save()

        order.total_price = (
            order.total_price -
            order_item.price).quantize(
                decimal.Decimal('0.01'))

        if order.total_price < 0:
            logging.error("prices below 0! "
                          "You might be running in to the "
                          "10 digit total order price limit")
            order.total_price = 0

        order.save()
        order_item.delete()

        order_items = Order_Item.objects.filter(order=order)
        serializer = OrderItemSerializer(order_items, many=True)
        return JsonResponse(serializer.data, safe=False)

    elif request.method == 'PATCH':
        # Set item_id to a count function
        item_quantity = order.count_quantity()

        product = get_object_or_404(Product, id=item_id)

        # product.quantity = product.id

        if product.stock_applies:
            if product.stock < 1 and not get_can_negative_stock():
                # messages.info(request, "Insufficient stock")
                return JsonResponse('{message: "Insufficient stock"}',
                                    status=status.HTTP_400_BAD_REQUEST, safe=False)

            product.stock -= 1
            product.save()

        Order_Item.objects.update_or_create(
            order=order, product=product, price=product.price, name=product.name, quantity=item_quantity)

        order.total_price = order.get_total_cost()
        order.save()

        order_items = Order_Item.objects.filter(order=order)
        serializer = OrderItemSerializer(order_items, many=True)
        return JsonResponse(serializer.data, safe=False)


@csrf_exempt
def search_order_item(request, item_id):
    if not request.user.is_authenticated:
        return HttpResponse(status=status.HTTP_401_UNAUTHORIZED)

    order = get_current_user_order(request.user.username)

    if request.method == 'DELETE':
        order_item = get_object_or_404(Order_Item, name=item_id)
        order_product = order_item.product

        if order_product.stock_applies:
            order_product.stock += 1
            order_product.save()

        order.total_price = (
            order.total_price -
            order_item.price).quantize(
                decimal.Decimal('0.01'))

        if order.total_price < 0:
            logging.error("prices below 0! "
                          "You might be running in to the "
                          "10 digit total order price limit")
            order.total_price = 0

        order.save()
        order_item.delete()

        order_items = Order_Item.objects.filter(order=order)
        serializer = OrderItemSerializer(order_items, many=True)
        return JsonResponse(serializer.data, safe=False)

    elif request.method == 'PUT':
        product = get_object_or_404(Product, name=item_id)

        if product.stock_applies:
            if product.stock < 1 and not get_can_negative_stock():
                # messages.info(request, "Insufficient stock")
                return JsonResponse('{message: "Insufficient stock"}',
                                    status=status.HTTP_400_BAD_REQUEST)

            product.stock -= 1
            product.save()

        Order_Item.objects.create(order=order, product=product,
                                  price=product.price, name=product.name)
        order.total_price = order.get_total_cost()
        order.save()

        order_items = Order_Item.objects.filter(order=order)
        serializer = OrderItemSerializer(order_items, many=True)
        return JsonResponse(serializer.data, safe=False)

    else:
        return HttpResponseBadRequest()


def cash_payment(request):
    if not request.user.is_authenticated:
        return HttpResponse(status=status.HTTP_401_UNAUTHORIZED)

    order = get_current_user_order(request.user.username)
    cash, _ = Cash.objects.get_or_create(id=0)

    amount_added = 0

    for product in product_list_from_order(order):
        cash.amount += product.price
        amount_added += product.price
        cash.save()

    order.done = True
    order.save()
    Order.objects.create(user=request.user)

    return JsonResponse({'added': amount_added})


def momo_payment(request):
    if not request.user.is_authenticated:
        return HttpResponse(status=status.HTTP_401_UNAUTHORIZED)

    order = get_current_user_order(request.user.username)

    order.done = True
    order.save()
    Order.objects.create(user=request.user)

    return HttpResponse()
<file_sep>/customer/urls.py
from django.urls import path
from django.conf.urls import url
from django.views.generic import RedirectView
from  . import views

urlpatterns = [
    path('', views.listCustomer, name='list-client'),
    path('create/', views.createCustomer, name='customer'),
]<file_sep>/customer/migrations/0001_initial.py
# Generated by Django 3.1.1 on 2020-12-18 12:31

import customer.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        ('catalog', '0001_initial'),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='Customer',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=200, null=True, validators=[customer.models.validate_name])),
                ('phone', models.CharField(blank=True, max_length=17, null=True, validators=[customer.models.validate_phone])),
                ('email', models.EmailField(blank=True, max_length=254, null=True)),
                ('amount_to_pay', models.DecimalField(decimal_places=2, default=0, max_digits=10)),
                ('amount_paid', models.DecimalField(decimal_places=2, default=0, max_digits=10)),
                ('payment_status', models.CharField(blank=True, choices=[('Paid', 'Paid'), ('Part Payment', 'Part Payment')], max_length=14, null=True)),
                ('date_created', models.DateTimeField(auto_now=True)),
                ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
                ('orders', models.ManyToManyField(to='catalog.Order_Item')),
            ],
        ),
    ]
<file_sep>/catalog/migrations/0001_initial.py
# Generated by Django 3.1.1 on 2020-12-18 12:31

import catalog.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

    initial = True

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='Cash',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('amount', models.DecimalField(decimal_places=2, default=0, max_digits=7)),
            ],
            options={
                'verbose_name': 'Cash',
                'verbose_name_plural': 'Cash',
            },
        ),
        migrations.CreateModel(
            name='Category',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=100, validators=[catalog.models.validate_name])),
            ],
            options={
                'verbose_name': 'Category',
                'verbose_name_plural': 'Categories',
                'ordering': ('id',),
            },
        ),
        migrations.CreateModel(
            name='Order',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('total_price', models.DecimalField(decimal_places=2, default=0, max_digits=10)),
                ('done', models.BooleanField(default=False)),
                ('last_change', models.DateTimeField(auto_now=True)),
                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
            ],
        ),
        migrations.CreateModel(
            name='Setting',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('key', models.CharField(max_length=50)),
                ('value', models.CharField(max_length=100)),
            ],
        ),
        migrations.CreateModel(
            name='Store',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=150, validators=[catalog.models.validate_name])),
                ('description', models.TextField(blank=True, null=True)),
            ],
            options={
                'verbose_name': 'Store',
                'verbose_name_plural': 'Stores',
                'ordering': ('name',),
            },
        ),
        migrations.CreateModel(
            name='Supplier',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=100, validators=[catalog.models.validate_name])),
                ('email', models.EmailField(blank=True, max_length=254)),
                ('tel', models.CharField(blank=True, max_length=17, validators=[catalog.models.validate_phone])),
                ('location', models.CharField(blank=True, max_length=200)),
                ('comments', models.TextField(blank=True)),
            ],
        ),
        migrations.CreateModel(
            name='Product',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=200, validators=[catalog.models.validate_name])),
                ('price', models.DecimalField(decimal_places=2, max_digits=7)),
                ('stock_applies', models.BooleanField()),
                ('quantity', models.IntegerField(default=1, help_text='Number of items to purchase. Only update this field at the sales page for clients buying more than 1 items.')),
                ('minimum_stock', models.PositiveSmallIntegerField(default=0)),
                ('stock', models.IntegerField(default=0)),
                ('expiring_date', models.DateField()),
                ('colour', models.CharField(choices=[('blue', 'Blue'), ('green', 'Green'), ('yellow', 'Yellow'), ('orange', 'Orange'), ('purple', 'Purple'), ('black', 'Black'), ('pink', 'Pink'), ('cyan', 'Cyan')], default='blue', max_length=10)),
                ('created_on', models.DateTimeField(auto_now_add=True)),
                ('category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_category', to='catalog.category')),
                ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
                ('store', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_store', to='catalog.store')),
                ('supplier', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='product_supplier', to='catalog.supplier')),
            ],
            options={
                'verbose_name': 'Product',
                'verbose_name_plural': 'Products',
                'ordering': ('name',),
            },
        ),
        migrations.CreateModel(
            name='Order_Item',
            fields=[
                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('price', models.DecimalField(decimal_places=2, max_digits=7)),
                ('quantity', models.IntegerField(default=1)),
                ('name', models.CharField(max_length=100)),
                ('timestamp', models.DateTimeField(auto_now=True)),
                ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='catalog.order')),
                ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='catalog.product')),
            ],
        ),
    ]
<file_sep>/catalog/templates/inventory/index.html
{% extends 'inventory/base.html' %}

{% block content %}
	
<div class="container-fluid">
    <div class="row">
        <div class="col-sm-6">
            <div class="row">
                {% if list %}
                {% for product in list %}
                <div class="col-sm-6 col-md-4" style="padding-top: 5px; padding-bottom: 5px;">
                    <a role="button" class="btn btn-primary btn-block {{ product.colour }}"
                       href="#"
                       onclick="app.addProduct({{ product.id }});">
                        {{ product.name }}<br>{{ currency }}{{ product.price }}
                    </a>
                </div>
                {% endfor %}
                {% endif %}
            </div>
        </div>
        <div class="col-sm-6">
            <table class="table">
                <thead>
                    <tr>
                        <th scope="col">Name</th>
                        <th scope="col">Price</th>
                        <th scope="col">Quantity</th>
                    </tr>
                </thead>
                <tbody>
                    <tr v-for="item in items" :key="item.id">
                        <td>[[ item.name ]]</td>
                        <td>[[ item.price ]]</td>
                        <td data-th="Quantity">
                            <input type="number" class="form-control" v-model.number="item.quantity"
                                @blur="apdateProduct(item.quantity)"
                                @change="apdateProduct(item.quantity)"
                                step="1"
                                min="1"
                                max="99"/>
                        </td>
                        <td><button class="btn btn-danger" @click="removeProduct(item.id)">X</button></td>
                    </tr>
                    <tr>
                        <th scope="row">Total</th>
                        <td>[[ order.total_price ]]</td>
                        <td></td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</div>
    
{% endblock %}<file_sep>/catalog/forms.py
from django import forms
from . models import Product  # , Order_Item


class ProductForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    class Meta:
        model = Product
        fields = ('name', 'price', 'selling_price', 'stock_applies', 'minimum_stock',
                  'stock', 'expiring_date', 'supplier', 'store', 'category', 'colour')


# class Order_ItemForm(forms.ModelForm):
#     class Meta:
#         model = Order_Item
#         fields = ['quantity']
<file_sep>/summary/models.py
from django.db import models
from catalog.models import Order, Order_Item


class OrderSummary(Order):
    class Meta:
        proxy = True
        verbose_name = 'Order Report'
        verbose_name_plural = 'Order Reports'


class OrderItemSummary(Order_Item):
    class Meta:
        proxy = True
        verbose_name = "Product Report"
        verbose_name_plural = "Product Reports"
<file_sep>/README.md
POSsystem based on EpPOS system
<file_sep>/catalog/models.py
import re
import time
from datetime import date, timedelta
from django.db import models
from django.dispatch import receiver
from plyer import notification
from django.utils.html import format_html
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.urls import reverse


def validate_name(names):
    regex_string = r'^\w[\w ]*$'
    search = re.compile(regex_string).search
    result = bool(search(names))
    if not result:
        raise ValidationError("Please only use letters, "
                              "numbers and underscores or spaces."
                              "The name cannot start with space.")


def validate_phone(phone):
    phone_regex = r'^\+?1?\d{9,15}$'
    search = re.compile(phone_regex).search
    result = bool(search(phone))
    if not result:
        raise RegexValidator("Phone numbers must be entered in the format:"
                             "'+999999999.' Up to 15 digits allowed.")


class Store(models.Model):
    """This class manages Store divisions, warehouses, diffrent branches etc. """
    name = models.CharField(max_length=150, validators=[validate_name])
    description = models.TextField(blank=True, null=True)

    class Meta:
        ordering = ('name',)
        verbose_name = 'Store'
        verbose_name_plural = 'Stores'

    def __str__(self):
        return self.name


class Supplier(models.Model):
    """This class manages the suppliers of the products"""
    name = models.CharField(max_length=100, validators=[validate_name])
    email = models.EmailField(blank=True)
    tel = models.CharField(max_length=17, validators=[
                           validate_phone], blank=True)
    location = models.CharField(max_length=200, blank=True)
    comments = models.TextField(blank=True)

    def __str__(self):
        return self.name


class Category(models.Model):
    name = models.CharField(max_length=100, validators=[validate_name])

    def __str__(self):
        return self.name

    class Meta:
        ordering = ('id',)
        verbose_name = 'Category'
        verbose_name_plural = 'Categories'


class Product(models.Model):
    """ This class manages information related to the products."""
    # The different colours, to use as Product.<colour>
    BLUE = "blue"
    GREEN = "green"
    YELLOW = "yellow"
    ORANGE = "orange"
    PURPLE = "purple"
    BLACK = "black"
    PINK = "pink"
    CYAN = "cyan"

    # Colour choices
    COLOUR_CHOICES = (
        (BLUE, "Blue"),
        (GREEN, "Green"),
        (YELLOW, "Yellow"),
        (ORANGE, "Orange"),
        (PURPLE, "Purple"),
        (BLACK, "Black"),
        (PINK, "Pink"),
        (CYAN, "Cyan"),
    )
    name = models.CharField(max_length=200, validators=[validate_name])
    price = models.DecimalField(max_digits=7, decimal_places=2)
    stock_applies = models.BooleanField()
    selling_price = models.DecimalField(max_digits=7, decimal_places=2)
    minimum_stock = models.PositiveSmallIntegerField(default=0)
    stock = models.IntegerField(default=0)
    expiring_date = models.DateField()
    supplier = models.ForeignKey(
        Supplier, on_delete=models.CASCADE, blank=True, null=True, related_name='product_supplier')
    store = models.ForeignKey(
        Store, on_delete=models.CASCADE, blank=True, null=True, related_name="product_store")
    category = models.ForeignKey(
        Category, on_delete=models.CASCADE, blank=True, null=True, related_name="product_category")
    colour = models.CharField(
        max_length=10, choices=COLOUR_CHOICES, default="blue")
    created_on = models.DateTimeField(auto_now_add=True)
    created_by = models.ForeignKey(User, on_delete=models.CASCADE)

    class Meta:
        ordering = ('name',)
        verbose_name = 'Product'
        verbose_name_plural = 'Products'

    def __str__(self):
        return self.name

    def expired_field(self):
        if date.today() >= self.expiring_date:
            return format_html(u'<span style="color: red;">Expired</span>')
    expired_field.allow_tags = True

    def stock_level(self):
        if self.stock <= self.minimum_stock:
            return format_html(u"<span style='color: red;'>Low</span>")
        else:
            return format_html(u"<span style='color: green;'>High</span>")
    stock_level.allow_tags = True


class Order(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    total_price = models.DecimalField(
        max_digits=10, decimal_places=2, default=0)
    done = models.BooleanField(default=False)
    last_change = models.DateTimeField(auto_now=True)

    def get_absolute_url(self):
        return reverse('view_order', args=[self.id])

    def get_total_cost(self):
        """ Calculate total order cost for orders"""
        return sum(item.get_cost() for item in self.items.all())

    def count_quantity(self):
        """ Count for the quantity of order_items needed by customer."""
        result = 0
        for item in self.items.all():
            result += 1 * item.quantity
        return result


class Cash(models.Model):
    amount = models.DecimalField(max_digits=7, decimal_places=2, default=0)

    def __str__(self):
        return self.amount

    class Meta:
        verbose_name = 'Cash'
        verbose_name_plural = 'Cash'


class Order_Item(models.Model):
    product = models.ForeignKey(Product, on_delete=models.CASCADE)
    order = models.ForeignKey(
        Order, related_name='items', on_delete=models.CASCADE)
    price = models.DecimalField(max_digits=7, decimal_places=2)
    quantity = models.IntegerField(default=1)
    name = models.CharField(max_length=100)
    timestamp = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.product.name

    def get_cost(self):
        return self.price * self.quantity


class Setting(models.Model):
    key = models.CharField(max_length=50)
    value = models.CharField(max_length=100)

    def __str__(self):
        return self.key

    def __bool__(self):
        return bool(self.value)

    __nonzero__ = __bool__
<file_sep>/summary/templatetags/mathtags.py
from django import template

register = template.Library()


@register.filter()
def percentof(n1, n2):
    try:
        return (float(n1) / float(n2)) * 100
    except (ZeroDivisionError, TypeError):
        return None
    # return '{0:.2%}'.format(value)
<file_sep>/summary/admin.py
from django.contrib import admin
from django.db.models import Count, Sum, Min, Max, DateTimeField
from django.db.models.functions import Trunc

from . import models


def get_next_in_date_hierarchy(request, date_hierarchy):
    if date_hierarchy + '__day' in request.GET:
        return 'hour'
    if date_hierarchy + '__month' in request.GET:
        return 'day'
    if date_hierarchy + '__year' in request.GET:
        return 'week'
    return 'month'


@admin.register(models.OrderSummary)
class LoadOrderSummaryAdmin(admin.ModelAdmin):
    change_list_template = 'admin/order_change_list.html'
    actions = None
    date_hierarchy = 'last_change'
    show_full_result_count = False

    list_filter = (
        'user__username',
    )

    def has_add_permission(self, request):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

    def has_change_permission(self, request, obj=None):
        return True

    def has_module_permission(self, request):
        return True

    def changelist_view(self, request, extra_context=None):
        response = super().changelist_view(request, extra_context=extra_context)
        try:
            qs = response.context_data['cl'].queryset
        except (AttributeError, KeyError):
            return response

        # list view
        metrics = {
            'total': Count('id'),
            'total_cost': Sum('total_price')
        }

        response.context_data['summary'] = list(
            qs
                .values('id', 'total_price')
                .annotate(**metrics)
                .order_by('-last_change')
        )

        # list view summary
        response.context_data['summary_total'] = dict(qs.aggregate(**metrics))

        # Chart
        period = get_next_in_date_hierarchy(request, self.date_hierarchy)
        response.context_data['period'] = period
        summary_over_time = qs.annotate(
            period=Trunc('last_change', 'day', output_field=DateTimeField()),
        ).values('id').annotate(total=Sum('total_price')).order_by('last_change')

        summary_range = summary_over_time.aggregate(
            low=Min('total'),
            high=Max('total'),
        )
        high = summary_range.get('high', 0)
        low = summary_range.get('low', 0)

        response.context_data['summary_over_time'] = [{
            'period': x['id'],
            'total': x['total'] or 0,
            'pct': ((x['total'] or 0) - low) / (high - low) * 100
            if high > low else 0,
        } for x in summary_over_time]

        return response


@admin.register(models.OrderItemSummary)
class LoadOrderItemSummaryAdmin(admin.ModelAdmin):
    change_list_template = 'admin/orderitem_change_list.html'
    actions = None
    date_hierarchy = 'timestamp'
    show_full_result_count = False

    def has_add_permission(self, request):
        return False

    def has_delete_permission(self, request, obj=None):
        return False

    def has_change_permission(self, request, obj=None):
        return True

    def has_module_permission(self, request):
        return True

    def changelist_view(self, request, extra_context=None):
        response = super().changelist_view(request, extra_context=extra_context)
        try:
            qs = response.context_data['cl'].queryset
        except (AttributeError, KeyError):
            return response

        # list view
        metrics = {
            'total_product': Count('product__name'),
            'total_price': Sum('price')
        }

        response.context_data['summary'] = list(
            qs
            .values('product__name', 'price')
            .annotate(**metrics)
            .order_by('-timestamp')
        )

        # list view summary
        response.context_data['summary_total'] = dict(qs.aggregate(**metrics))

        # Chart
        period = get_next_in_date_hierarchy(request, self.date_hierarchy)
        response.context_data['period'] = period
        summary_over_time = qs.annotate(
            period=Trunc('timestamp', 'day', output_field=DateTimeField()),
        ).values('product__name').annotate(total=Sum('price')).order_by('timestamp')

        summary_range = summary_over_time.aggregate(
            low=Min('total'),
            high=Max('total'),
        )
        high = summary_range.get('high', 0)
        low = summary_range.get('low', 0)

        response.context_data['summary_over_time'] = [{
            'period': x['product__name'],
            'total': x['total'] or 0,
            'pct': ((x['total'] or 0) - low) / (high - low) * 100
            if high > low else 0,
        } for x in summary_over_time]

        return response
<file_sep>/customer/views.py
from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect, Http404, HttpResponse
from django.urls import reverse
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from .models import Customer
# from catalog import helper
from . forms import CustomerForm

@login_required
def createCustomer(request):
    if request.method != 'POST':
        form = CustomerForm()
    else:
        form = CustomerForm(request.POST)
        if form.is_valid():
            new_customer = form.save(commit=False)
            new_customer.created_by = request.user
            new_customer.save()
            messages.success(request, "Record successfully added!")
            return redirect('/')
    context = {'form': form, 'title': 'Create Customer'}
    return render(request, 'customer/index.html', context)

@login_required
def listCustomer(request):
    customers = Customer.objects.all()
    title = "Manage Creditors"
    context = {'customers': customers, 'title': title}
    return render(request, 'customer/creditor.html', context)
<file_sep>/catalog/views.py
import logging
import decimal
from django.core.exceptions import ObjectDoesNotExist
from django.shortcuts import render, get_object_or_404#, redirect
from django.http import (HttpResponse, HttpResponseForbidden,
                         HttpResponseBadRequest, HttpResponseRedirect)
from django.urls import reverse
from django.contrib import messages
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.contrib.auth import login as auth_login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.decorators import user_passes_test
from . models import Product, Order, Cash, Order_Item
from . tasks import expiry_date, expired, stock_level
from . import helper


def login(request):
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('<PASSWORD>')
        user = authenticate(username=username, password=password)
        if user:
            if user.is_active:
                messages.success(request, "Login Successful")
                auth_login(request, user)
                return HttpResponseRedirect(reverse('order'))
            else:
                return messages.info(request, "Your account was inactive.")
        else:
            print("Someone tried to login and failed.")
            print("They used username: {} and password: {}".format(
                username, password))
            messages.error(request, "Invalid login details given")
            return HttpResponseRedirect('/')
    else:
        return render(request, 'registration/login.html', {'title': 'LOGIN PAGE'})


@login_required
def order(request):
    # Sort products by code, then by name
    list = Product.objects.order_by('name')
    currency = helper.get_currency()
    expiry_day = expiry_date()
    expired_today = expired()
    stock_level_info = stock_level()
    context = {
        'list': list,
        'currency': currency,
        'expiry_day': expiry_day,
        'expired_today': expired_today,
        'stock_level_info': stock_level_info,
        'title': 'Orders',
    }
    return render(request, 'inventory/index.html', context=context)


def _addition_no_stock(request):
    cash, current_order, currency = helper.setup_handling(request)

    total_price = current_order.total_price
    list = Order_Item.objects.filter(order=current_order)
    context = {
        'list': list,
        'total_price': total_price,
        'cash': cash,
        'succesfully_payed': False,
        'payment_error': False,
        'amount_added': 0,
        'currency': currency,
        'stock_error': True,
    }
    return render(request, 'inventory/addition.html', context=context)


@login_required
def addition(request):
    cash, current_order, currency = helper.setup_handling(request)

    total_price = current_order.total_price
    list = Order_Item.objects.filter(order=current_order)
    context = {
        'list': list,
        'total_price': total_price,
        'cash': cash,
        'succesfully_payed': False,
        'payment_error': False,
        'amount_added': 0,
        'currency': currency,
        'stock_error': False,
    }
    return render(request, 'inventory/addition.html', context=context)


def _show_order(request, order_id, should_print):
    currency = helper.get_currency()
    order = get_object_or_404(Order, id=order_id)
    items = Order_Item.objects.filter(order=order)
    company = helper.get_company()

    context = {
        'list': items,
        'order': order,
        'currency': currency,
        'company': company,
        'print': should_print
    }

    return render(request, 'inventory/view_order.html', context=context)


@login_required
def view_order(request, order_id):
    return _show_order(request, order_id, False)


@login_required
def print_order(request, order_id):
    return _show_order(request, order_id, True)


@login_required
def print_current_order(request):
    # Get the user. This is quite a roundabout, sorry
    usr = User.objects.get_by_natural_key(request.user.username)
    q = Order.objects.filter(user=usr)\
                     .order_by('-last_change')

    # This is an edge case that would pretty much never occur
    if q.count() < 1:
        return HttpResponseBadRequest
    return _show_order(request, q[0].id, True)


@login_required
def order_amount(request):
    _, current_order, _ = helper.setup_handling(request)
    return HttpResponse(current_order.total_price)


@login_required
def order_add_product(request, product_id):
    _, current_order, _ = helper.setup_handling(request)

    if product_id.isdigit():
        try:
            to_add = Product.objects.get(id=product_id)
        except ObjectDoesNotExist:
            to_add = get_object_or_404(Product, code=product_id)

    else:
        to_add = get_object_or_404(Product, code=product_id)

    # Make sure we can't go under 0 stock
    if to_add.stock_applies:
        if to_add.stock < 1 and not helper.get_can_negative_stock():
            return _addition_no_stock(request)

        to_add.stock -= 1
        to_add.save()

    Order_Item.objects.create(order=current_order, product=to_add,
                              price=to_add.price, name=to_add.name)
    current_order.total_price = current_order.get_total_cost()
    current_order.save()

    return addition(request)


@login_required
def order_remove_product(request, product_id):
    cash, current_order, _ = helper.setup_handling(request)
    order_item = get_object_or_404(Order_Item, id=product_id)
    order_product = order_item.product

    if order_product.stock_applies:
        order_product.stock += 1
        order_product.save()

    current_order.total_price = current_order.get_total_cost()

    if current_order.total_price < 0:
        logging.error("prices below 0! "
                      "You might be running in to the "
                      "10 digit total order price limit")
        current_order.total_price = 0

    current_order.save()
    order_item.delete()

    # I only need default values.
    return addition(request)


@login_required
@user_passes_test(lambda u: u.is_superuser)
def reset_order(request):
    cash, current_order, _ = helper.setup_handling(request)

    for item in Order_Item.objects.filter(order=current_order):
        if item.product.stock_applies:
            item.product.stock += item.product.quantity
            item.product.save()
        item.delete()

    current_order.total_price = 0
    current_order.save()

    # I just need default values. Quite useful
    return addition(request)


@login_required
def payment_cash(request):
    succesfully_payed = False
    payment_error = False
    amount_added = 0
    cash, current_order, currency = helper.setup_handling(request)

    for product in helper.product_list_from_order(current_order):
        cash.amount += product.price * product.quantity
        amount_added += product.price * product.quantity
        cash.save()

    current_order.done = True
    current_order.save()
    current_order = Order.objects.create(user=request.user)
    succesfully_payed = True

    total_price = current_order.total_price
    list = Order_Item.objects.filter(order=current_order)
    context = {
        'list': list,
        'total_price': total_price,
        'cash': cash,
        'succesfully_payed': succesfully_payed,
        'payment_error': payment_error,
        'amount_added': amount_added,
        'currency': currency,
    }
    return render(request, 'inventory/addition.html', context=context)


@login_required
def payment_momo(request):
    succesfully_payed = False
    payment_error = False
    cash, current_order, currency = helper.setup_handling(request)

    current_order.done = True
    current_order.save()
    current_order = Order.objects.create(user=request.user)
    succesfully_payed = True

    total_price = current_order.total_price
    list = Order_Item.objects.filter(order=current_order)
    context = {
        'list': list,
        'total_price': total_price,
        'cash': cash,
        'succesfully_payed': succesfully_payed,
        'payment_error': payment_error,
        'amount_added': 0,
        'currency': currency,
    }
    return render(request, 'inventory/addition.html', context=context)


# We can't use the `@login_required` decorator here because this
# page is never shown to the user and only used in AJAX requests
def cash(request, amount):
    if (request.user.is_authenticated):
        cash, _ = Cash.objects.get_or_create(id=0)
        cash.amount = amount
        cash.save()
        return HttpResponse('')
    else:
        return HttpResponseForbidden('403 Forbidden')


@login_required
@user_passes_test(lambda u: u.is_superuser)
def view_stock(request):
    stock_products = Product.objects.filter(stock_applies=True)
    company = helper.get_company()

    context = {
        'list': stock_products,
        'company': company
    }

    return render(request, 'inventory/stock.html', context=context)
<file_sep>/customer/models.py
import re
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.db import models
from catalog.models import Order_Item
from django.contrib.auth.models import User


def validate_name(names):
    regex_string = r'^\w[\w ]*$'
    search = re.compile(regex_string).search
    result = bool(search(names))
    if not result:
        raise ValidationError("Please only use letters, "
                              "numbers and underscores or spaces."
                              "The name cannot start with space.")


def validate_phone(phone):
    phone_regex = r'^\+?1?\d{9,15}$'
    search = re.compile(phone_regex).search
    result = bool(search(phone))
    if not result:
        raise RegexValidator("Phone numbers must be entered in the format:"
                             "'+999999999.' Up to 15 digits allowed.")


class Customer(models.Model):
    PAID = 'Paid'
    PART = 'Part Payment'
    STATUS = (
        (PAID, 'Paid'),
        (PART, 'Part Payment'),
    )
    name = models.CharField(max_length=200, null=True,
                            validators=[validate_name])
    phone = models.CharField(max_length=17, null=True,
                             validators=[validate_phone], blank=True)
    email = models.EmailField(blank=True, null=True)
    orders = models.ManyToManyField(Order_Item)
    amount_to_pay = models.DecimalField(max_digits=10, decimal_places=2, default=0)
    amount_paid = models.DecimalField(
        max_digits=10, decimal_places=2, default=0)
    payment_status = models.CharField(
        max_length=14, choices=STATUS, blank=True, null=True)
    created_by = models.ForeignKey(User, on_delete=models.CASCADE)
    date_created = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name

    # def save(self, *args, **kwargs):
    #     for order in self.orders:
    #         if self.amount == order.price:
    #             return self.payment_status.Paid
    #         else:
    #             return self.payment_status.PART
    #     return super(Customer, self).save(*args, **kwargs)
<file_sep>/staticfiles/js/app.js
$(document).ready(function() {
     $('#resetcashpopover').popover({html:true});
 });

 $('#CodeModal').on('shown.bs.modal', function (e) {
     $('#codeInput').focus();
 })

 var app = new Vue({
     delimiters: ['[[', ']]'],
     el: '#app',
     data: {
         order: {},
         items: [],
         cashgot: 0.0
     },
     methods: {
         update_order: function() {
             fetch("/api/orders/current/")
                 .then(res => res.json())
                 .then((out) => {
                     this.order = out;
                 });
         },
         update_items: function() {
             fetch("/api/orders/current/items/")
                 .then(res => res.json())
                 .then((out) => {
                     this.items = out;
                 });
         },
         cashmodal: function() {
             $('#cashgot').focus();
             console.log(this.order.total_price);
             document.getElementById('topay').value = this.order.total_price;
         },
         removeProduct: function(product_id) {
             fetch("/api/orders/current/items/" + encodeURIComponent(product_id) + "/", {method: 'DELETE'})
                 .then(response => {
                     if (response.status == 200) {
                         response.json().then(json => {
                             this.items = json;
                         });
                         this.update_order();
                     } else {
                         window.alert("Can't remove product");
                         console.log(response);
                     }
                 });
         },
         addProduct: function(product_id) {
             fetch("/api/orders/current/items/" + encodeURIComponent(product_id) + "/", {method: 'PUT'})
                 .then(response => {
                     if (response.status == 404) {
                         window.alert("Product not found");
                     } else if (response.status != 200) {
                         console.log(response);
                         window.alert("Product out of stock. Refill please!");
                     } else {
                         this.update_order();
                         // this must be a promise
                         response.json().then(json => {
                             this.items = json;
                         });
                     }
                 });
         },
         resetCash: function() {
             var value = document.getElementById('resetcashinput').value;
             fetch("/cash/" + encodeURIComponent(value))
                 .then(response => {
                     if (response.status != 200) {
                         window.alert("Can't reset cash");
                         console.log(response);
                     } else {
                         $('#cashresetamount').text(encodeURIComponent(value));
                         $('#cashresetdiv').show()
                         $('#resetcashpopover').popover('hide');
                     }
                 });
         },
         payed: function(method) {
             if (confirm("Do you want to print the addition?")) {
                 console.log(this.order.id);
                 window.open("/print-order/" + this.order.id, "_blank").focus();
             }

             if (method == "cash") {
                 fetch("/api/pay/cash/").then(response => {
                     if (response.status != 200) {
                         window.alert("Couldn't pay");
                         console.log(response);
                     } else {
                         response.json().then(json => {
                             window.alert("Added " + json.added + " to the register");
                         });
                         this.update_order();
                         this.update_items();
                     }
                 });
             } else if (method == "momo") {
                 if (!confirm('Are you sure?'))
                     return;
                 fetch("/api/pay/momo/").then(response => {
                     if (response.status != 200) {
                         window.alert("Couldn't pay");
                         console.log(response);
                     } else {
                         window.alert("Successfully paid.");
                         this.update_order();
                         this.update_items();
                     }
                 });
             }
             console.log("Person payed");
         },
         scanCode: function() {
             this.addProduct(document.getElementById('codeInput').value);
             document.getElementById('codeInput').value = "";
             document.getElementById('codeInput').focus();
         },
         reset: function() {
             fetch("/api/orders/current/", {method: 'delete'})
                 .then(response => {
                     if (response.status == 200) {
                         window.alert("Order cleared");
                     } else {
                         window.alert("Can't clear order");
                         console.log(response);
                     }
                 });
             this.update_order();
             this.update_items();
         }
     },
     created() {
         this.update_order();
         this.update_items();
     }
 });

 $('#cashModal').on('shown.bs.modal', function() {
     app.cashmodal();
 });

 $(document).ready(function() {
     // This shallst load /addition in its div
     $('#resetcashpopover').popover({html:true});
 });

 $('#CodeModal').on('shown.bs.modal', function (e) {
     $('#codeInput').focus();
 })<file_sep>/Project/urls.py
"""Project URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.contrib.auth import views as auth_views
from django.views.generic.base import RedirectView
from catalog.views import login
from django.urls import path, re_path, include
from django.conf import settings
from django.conf.urls import url
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.conf.urls.static import static
from django.views.static import serve

urlpatterns = [
    re_path(r'^admin/', admin.site.urls),
    # path('', RedirectView.as_view(url='/', permanent=True)),
    path('', include('catalog.urls')),
    path('customers/', include('customer.urls')),
    path('accounts/logout/', auth_views.LogoutView.as_view(), {'next_page': '/'}, name='logout'),
    path('accounts/login/', login, name='login'),
    path('accounts/', include('django.contrib.auth.urls')),
    path('api-auth/', include('rest_framework.urls')),
    url(r'^static/(?P<path>.*)$', serve,
        {'document_root': settings.STATIC_ROOT}),
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += staticfiles_urlpatterns()
