feat: initial commit - Band Management application

This commit is contained in:
2026-01-06 03:11:46 +01:00
commit 34e12e00b3
24543 changed files with 3991790 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
from django.contrib import admin
from .models import Transaction
# Register your models here.
class TransactionAdmin(admin.ModelAdmin):
list_display = ('customer', 'transaction_date', 'due_date', 'total', 'status')
list_filter = ('customer', 'transaction_date', 'due_date', 'total', 'status')
search_fields = ('customer', 'transaction_date', 'due_date', 'total', 'status')
ordering = ('customer', 'transaction_date', 'due_date', 'total', 'status')
list_per_page = 10
admin.site.register(Transaction, TransactionAdmin)

View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class TransactionsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'apps.transactions'

View File

@@ -0,0 +1,79 @@
[
{
"model": "transactions.transaction",
"pk": 1,
"fields": {
"customer": "John Doe",
"transaction_date": "2022-01-01",
"due_date": "2022-01-31",
"total": "100.00",
"status": "Paid"
}
},
{
"model": "transactions.transaction",
"pk": 2,
"fields": {
"customer": "Jane Smith",
"transaction_date": "2022-02-01",
"due_date": "2022-02-28",
"total": "200.00",
"status": "Due"
}
},
{
"model": "transactions.transaction",
"pk": 3,
"fields": {
"customer": "Bob Johnson",
"transaction_date": "2022-03-01",
"due_date": "2022-03-31",
"total": "300.00",
"status": "Canceled"
}
},
{
"model": "transactions.transaction",
"pk": 4,
"fields": {
"customer": "Alice Lee",
"transaction_date": "2022-04-01",
"due_date": "2022-04-30",
"total": "400.00",
"status": "Paid"
}
},
{
"model": "transactions.transaction",
"pk": 5,
"fields": {
"customer": "David Kim",
"transaction_date": "2022-05-01",
"due_date": "2022-05-31",
"total": "500.00",
"status": "Due"
}
},
{
"model": "transactions.transaction",
"pk": 6,
"fields": {
"customer": "Emily Chen",
"transaction_date": "2022-06-01",
"due_date": "2022-06-30",
"total": "600.00",
"status": "Canceled"
}
},
{
"model": "transactions.transaction",
"pk": 7,
"fields": {
"customer": "Michael Brown",
"transaction_date": "2022-07-01",
"due_date": "2022-07-31",
"total": "700.00",
"status": "Paid"
}
}
]

View File

@@ -0,0 +1,7 @@
from django import forms
from .models import Transaction
class TransactionForm(forms.ModelForm):
class Meta:
model = Transaction
fields = '__all__'

View File

@@ -0,0 +1,25 @@
# Generated by Django 5.0 on 2023-12-08 05:27
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Transaction',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('customer', models.CharField(max_length=150)),
('transaction_date', models.DateField()),
('due_date', models.DateField()),
('total', models.DecimalField(decimal_places=2, max_digits=20)),
('status', models.CharField(choices=[('Paid', 'Paid'), ('Due', 'Due'), ('Canceled', 'Canceled')], max_length=20)),
],
),
]

View File

@@ -0,0 +1,11 @@
from django.db import models
class Transaction(models.Model):
customer = models.CharField(max_length=150)
transaction_date = models.DateField()
due_date = models.DateField()
total = models.DecimalField(max_digits=20, decimal_places=2)
status = models.CharField(max_length=20, choices=[("Paid", "Paid"), ("Due", "Due"), ("Canceled", "Canceled")])
def __str__(self):
return self.customer

View File

@@ -0,0 +1,66 @@
{% extends layout_path %}
{% load static %}
{% load i18n %}
{% block title %}Transactions{% endblock title %}
{% block vendor_css %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'vendor/libs/select2/select2.css' %}" />
<link rel="stylesheet" href="{% static 'vendor/libs/@form-validation/form-validation.css' %}" />
<link rel="stylesheet" href="{% static 'vendor/libs/flatpickr/flatpickr.css' %}" />
{% endblock vendor_css %}
{% block vendor_js %}
{{ block.super }}
<script src="{% static 'vendor/libs/select2/select2.js' %}"></script>
<script src="{% static 'vendor/libs/@form-validation/popular.js' %}"></script>
<script src="{% static 'vendor/libs/@form-validation/bootstrap5.js' %}"></script>
<script src="{% static 'vendor/libs/@form-validation/auto-focus.js' %}"></script>
<script src="{% static 'vendor/libs/flatpickr/flatpickr.js' %}"></script>
{% endblock vendor_js %}
{% block page_js %}
{{ block.super }}
<script src="{% static 'js/transactions-add-update.js' %}"></script>
{% endblock page_js %}
{% block content %}
<!-- Add Transactions Form -->
<div class="card">
<div class="card-body">
<form class="add-transactions pt-0" id="addTransactionForm" action="{% url 'transactions-add' %}" method="post">
{% csrf_token %}
<div class="mb-4 form-control-validation">
<label class="form-label" for="customer-name">Customer</label>
<input type="text" class="form-control" id="customer-name" name="customer" placeholder="Full Name" />
</div>
<div class="mb-4 form-control-validation">
<label class="form-label" for="transaction-date">Transaction date</label>
<input type="text" id="transaction-date" class="form-control" name="transaction_date" value="{{ current_date }}" placeholder="Transaction Date" />
</div>
<div class="mb-4 form-control-validation">
<label class="form-label" for="due-date">Due date</label>
<input type="text" id="due-date" class="form-control" name="due_date" placeholder="Due Date" />
</div>
<div class="mb-4 form-control-validation">
<label class="form-label" for="total-amount">Total Amount</label>
<input type="number" id="total-amount" name="total" class="form-control" placeholder="Total Amount" />
</div>
<div class="mb-4 form-control-validation">
<label class="form-label" for="customer-status">Status</label>
<select id="customer-status" class="select2 form-select" name="status">
<option value="" disabled selected class="d-none"></option>
<option value="Paid">Paid</option>
<option value="Due">Due</option>
<option value="Canceled">Canceled</option>
</select>
</div>
<button type="submit" class="btn btn-primary me-sm-3 me-1" name="submitButton">Submit</button>
<a href="{% url 'transactions' %}" class="btn btn-secondary">Back</a>
</form>
</div>
</div>
<!--/ Add Transactions Form -->
{% endblock %}

View File

@@ -0,0 +1,180 @@
{% extends layout_path %}
{% load static %}
{% load i18n %}
{% block title %}Transactions{% endblock title %}
{% block vendor_css %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'vendor/libs/datatables-bs5/datatables.bootstrap5.css' %}" />
<link rel="stylesheet" href="{% static 'vendor/libs/datatables-responsive-bs5/responsive.bootstrap5.css' %}" />
<link rel="stylesheet" href="{% static 'vendor/libs/datatables-buttons-bs5/buttons.bootstrap5.css' %}" />
<link rel="stylesheet" href="{% static 'vendor/libs/sweetalert2/sweetalert2.css' %}" />
{% endblock vendor_css %}
{% block vendor_js %}
{{ block.super }}
<script src="{% static 'vendor/libs/moment/moment.js' %}"></script>
<script src="{% static 'vendor/libs/datatables-bs5/datatables-bootstrap5.js' %}"></script>
<script src="{% static 'vendor/libs/sweetalert2/sweetalert2.js' %}"></script>
{% endblock vendor_js %}
{% block page_js %}
{{ block.super }}
<script src="{% static 'js/transactions-list.js' %}"></script>
<script src="{% static 'js/transactions-delete.js' %}"></script>
{% endblock page_js %}
{% block content %}
<!-- Transactions Toast -->
{% if messages %}
<div class="toast-container end-0 m-1 me-4">
{% for message in messages %}
<div class="toast {% if message.tags == 'success' %}bg-success{% elif message.tags == 'error' %}bg-danger{% else %}bg-warning{% endif %}" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="true" data-bs-delay="2000">
<div class="toast-body text-center text-white fw-medium">
{{ message }}
</div>
</div>
{% endfor %}
</div>
{% endif %}
<!--/ Transactions Toast -->
<!-- Transactions Table -->
<!-- permission required: view_transaction -->
{% if perms.transactions.view_transaction %}
<div class="row g-4 mb-4">
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<div class="d-flex align-items-end">
<h3 class="mb-0 me-2">{{ transactions_count }}</h3>
<small class="text-primary"></small>
</div>
<small>Total Transactions</small>
</div>
<span class="badge bg-label-primary rounded p-2">
<i class="icon-base ti tabler-arrows-transfer-down"></i>
</span>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<div class="d-flex align-items-end">
<h3 class="mb-0 me-2">$ {{ paid_count }}</h3>
<small class="text-primary"></small>
</div>
<small>Total Paid</small>
</div>
<span class="badge bg-label-success rounded p-2">
<i class="icon-base ti tabler-check"></i>
</span>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<div class="d-flex align-items-end">
<h3 class="mb-0 me-2">$ {{ due_count }}</h3>
<small class="text-primary"></small>
</div>
<small>Total Due</small>
</div>
<span class="badge bg-label-warning rounded p-2">
<i class="icon-base ti tabler-clock-down"></i>
</span>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-3">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<div class="d-flex align-items-end">
<h3 class="mb-0 me-2">$ {{ canceled_count }}</h3>
<small class="text-primary"></small>
</div>
<small>Total Canceled</small>
</div>
<span class="badge bg-label-danger rounded p-2">
<i class="icon-base ti tabler-x"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-datatable tabler-responsive">
<table class="datatables-transaction table">
<thead class="border-top">
<tr class="text-nowrap">
<th></th>
<th>Id</th>
<th>Customer</th>
<th>Transaction Date</th>
<th>Due Date</th>
<th>Total</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for transaction in transactions %}
<tr>
<td></td>
<td>{{ transaction.id }}</td>
<td class="text-nowrap fw-medium text-heading">{{ transaction.customer|capfirst }}</td>
<td class="text-nowrap">{{ transaction.transaction_date }}</td>
<td class="text-nowrap">{{ transaction.due_date }}</td>
<td class="text-nowrap">$ {{ transaction.total }}</td>
<td>
<div class="badge bg-label-{% if transaction.status == 'Paid' %}success{% elif transaction.status == 'Due' %}warning{% elif transaction.status == 'Canceled' %}danger{% endif %}">
{{transaction.status}}
</div>
</td>
<td>
<div class="d-flex align-items-center gap-1">
<!-- permission required: edit_transaction -->
{% if perms.transactions.edit_transaction %}
<a href="{% url 'transactions-update' transaction.id %}" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill"><i class='icon-base ti tabler-edit icon-md'></i></a>
{% endif %}
<!-- permission required: delete_transaction -->
{% if perms.transactions.delete_transaction %}
<a href="{% url 'transactions-delete' transaction.id %}" class="btn btn-icon btn-text-secondary waves-effect waves-light rounded-pill delete-transaction" data-transaction-username="{{ transaction.customer|capfirst }}"><i class="icon-base ti tabler-trash icon-md"></i></a>
{% endif %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
<!--/ Transactions Table -->
<script>
var addTransactionBtn = '{% if perms.transactions.add_transaction %}' + true + '{% endif %}';
</script>
{% endblock %}

View File

@@ -0,0 +1,72 @@
{% extends layout_path %}
{% load static %}
{% load i18n %}
{% block title %}Transactions{% endblock title %}
{% block vendor_css %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'vendor/libs/select2/select2.css' %}" />
<link rel="stylesheet" href="{% static 'vendor/libs/@form-validation/form-validation.css' %}" />
<link rel="stylesheet" href="{% static 'vendor/libs/flatpickr/flatpickr.css' %}" />
<link rel="stylesheet" href="{% static 'vendor/libs/sweetalert2/sweetalert2.css' %}" />
{% endblock vendor_css %}
{% block vendor_js %}
{{ block.super }}
<script src="{% static 'vendor/libs/select2/select2.js' %}"></script>
<script src="{% static 'vendor/libs/@form-validation/popular.js' %}"></script>
<script src="{% static 'vendor/libs/@form-validation/bootstrap5.js' %}"></script>
<script src="{% static 'vendor/libs/@form-validation/auto-focus.js' %}"></script>
<script src="{% static 'vendor/libs/flatpickr/flatpickr.js' %}"></script>
<script src="{% static 'vendor/libs/sweetalert2/sweetalert2.js' %}"></script>
{% endblock vendor_js %}
{% block page_js %}
{{ block.super }}
<script src="{% static 'js/transactions-add-update.js' %}"></script>
<script src="{% static 'js/transactions-delete.js' %}"></script>
{% endblock page_js %}
{% block content %}
<!-- Update Transactions Form -->
<div class="card">
<div class="card-body">
<form class="update-transaction pt-0" id="UpdateTransactionForm" action="{% url 'transactions-update' transaction_id.id %}" method="post">
{% csrf_token %}
<input type="hidden" name="id" id="user_id">
<div class="mb-4 form-control-validation">
<label class="form-label" for="customer-name">Customer</label>
<input type="text" class="form-control" id="customer-name" name="customer" value="{{ transaction_id.customer }}" placeholder="Full Name" />
</div>
<div class="mb-4 form-control-validation">
<label class="form-label" for="transaction-date">Transaction date</label>
<input type="text" id="transaction-date" class="form-control" name="transaction_date" value="{{ transaction_id.transaction_date|date:'c' }}" placeholder="Transaction Date" />
</div>
<div class="mb-4 form-control-validation">
<label class="form-label" for="current_date">Due date</label>
<input type="text" id="due-date" class="form-control" name="due_date" value="{{ transaction_id.due_date|date:'c' }}" placeholder="Due Date" />
</div>
<div class="mb-4 form-control-validation">
<label class="form-label" for="total-amount">Total Amount</label>
<input type="number" id="total-amount" name="total" class="form-control" value="{{ transaction_id.total }}" placeholder="Total Amount" />
</div>
<div class="mb-4 form-control-validation">
<label class="form-label" for="customer-status">Status</label>
<select id="customer-status" class="select2 form-select" name="status">
<option value="Paid" {% if transaction_id.status == 'Paid' %} selected {% endif %}>Paid</option>
<option value="Due" {% if transaction_id.status == 'Due' %} selected {% endif %}>Due</option>
<option value="Canceled" {% if transaction_id.status == 'Canceled' %} selected {% endif %}>Canceled</option>
</select>
</div>
<button type="submit" class="btn btn-primary me-sm-3 me-1 data-submit">Save</button>
<a href="{% url 'transactions-delete' transaction_id.id %}" class="btn btn-danger me-sm-3 me-1 delete-transaction" data-transaction-username="{{ transaction_id.customer }}">Delete</a>
<a href="{% url 'transactions' %}" class="btn btn-secondary me-sm-3 me-1">Back</a>
</form>
</div>
</div>
<!--/ Update Transactions Form -->
{% endblock %}

View File

@@ -0,0 +1,36 @@
from datetime import date
from django.shortcuts import redirect
from django.contrib import messages
from django.views.generic import TemplateView
from web_project import TemplateLayout
from apps.transactions.models import Transaction
from apps.transactions.forms import TransactionForm
from django.contrib.auth.mixins import PermissionRequiredMixin
class TransactionAddView(PermissionRequiredMixin, TemplateView):
permission_required = ("transactions.add_transaction")
def get_context_data(self, **kwargs):
context = TemplateLayout.init(self, super().get_context_data(**kwargs))
context['current_date'] = date.today().strftime("%Y-%m-%d")
return context
def post(self, request):
form = TransactionForm(request.POST)
if form.is_valid():
if not self.transaction_exists(form.cleaned_data):
form.save()
messages.success(request, 'Transaction Added')
else:
messages.error(request, 'Transaction already exists')
else:
messages.error(request, 'Transaction Failed')
return redirect('transactions')
def transaction_exists(self, cleaned_data):
return Transaction.objects.filter(
customer__iexact=cleaned_data['customer'],
transaction_date=cleaned_data['transaction_date'],
due_date=cleaned_data['due_date'],
total=cleaned_data['total'],
status=cleaned_data['status']
).exists()

View File

@@ -0,0 +1,15 @@
from django.shortcuts import redirect, get_object_or_404
from django.views.generic import DeleteView
from django.contrib import messages
from apps.transactions.models import Transaction
from django.contrib.auth.mixins import PermissionRequiredMixin
class TransactionDeleteView(PermissionRequiredMixin, DeleteView):
permission_required = ("transactions.delete_transaction")
def get(self, request, pk):
transaction = get_object_or_404(Transaction, id=pk)
transaction.delete()
messages.success(request, 'Transaction Deleted')
return redirect('transactions')

View File

@@ -0,0 +1,41 @@
from django.views.generic import TemplateView
from django.db.models import Sum
from web_project.template_helpers.theme import TemplateHelper
from web_project import TemplateLayout
from apps.transactions.models import Transaction
class TransactionListView(TemplateView):
def get_context_data(self, **kwargs):
context = TemplateLayout.init(self, super().get_context_data(**kwargs))
transactions = self.get_annotated_transactions()
# Update the context
context.update(
{
"transactions": transactions,
"transactions_count": Transaction.objects.count(),
"due_count": self.get_total_due(),
"paid_count": self.get_total_paid(),
"canceled_count": self.get_total_canceled(),
}
)
TemplateHelper.map_context(context)
return context
def get_annotated_transactions(self):
# Get all transactions and order them by ID
return Transaction.objects.all().order_by('id')
def get_total_due(self):
total_due_amount = Transaction.objects.filter(status='Due').aggregate(total_due=Sum('total'))
return float(total_due_amount['total_due']) if total_due_amount['total_due'] is not None else 0.0
def get_total_paid(self):
# Calculate the total sum of 'total' field for 'Paid' transactions
total_paid_amount = Transaction.objects.filter(status='Paid').aggregate(total_paid=Sum('total'))
return float(total_paid_amount['total_paid']) if total_paid_amount['total_paid'] is not None else 0.0
def get_total_canceled(self):
total_canceled_amount = Transaction.objects.filter(status='Canceled').aggregate(total_canceled=Sum('total'))
return float(total_canceled_amount['total_canceled']) if total_canceled_amount['total_canceled'] is not None else 0.0

View File

@@ -0,0 +1,42 @@
from django.shortcuts import redirect
from django.contrib import messages
from django.db.models import Q
from django.views.generic import TemplateView
from web_project import TemplateLayout
from apps.transactions.forms import TransactionForm
from apps.transactions.models import Transaction
from django.contrib.auth.mixins import PermissionRequiredMixin
class TransactionUpdateView(PermissionRequiredMixin, TemplateView):
permission_required = ("transactions.update_transaction")
def get_context_data(self, **kwargs):
context = TemplateLayout.init(self, super().get_context_data(**kwargs))
context['transaction_id'] = self.get_transaction(self.kwargs['pk'])
return context
def post(self, request, pk):
transaction = self.get_transaction(pk)
form = TransactionForm(request.POST, instance=transaction)
if form.is_valid():
if not self.transaction_exists(form.cleaned_data, transaction):
form.save()
messages.success(request, 'Transaction Updated')
else:
messages.error(request, 'Transaction Already Exists')
else:
messages.error(request, 'Transaction Failed')
return redirect('transactions')
def get_transaction(self, pk):
return Transaction.objects.get(pk=pk)
def transaction_exists(self, cleaned_data, current_transaction):
matching_transactions = Transaction.objects.filter(
Q(customer__iexact=cleaned_data['customer']) &
Q(transaction_date=cleaned_data['transaction_date']) &
Q(due_date=cleaned_data['due_date']) &
Q(total=cleaned_data['total']) &
Q(status=cleaned_data['status'])
).exclude(pk=current_transaction.pk)
return matching_transactions.exists()

View File

@@ -0,0 +1,30 @@
from django.urls import path
from django.contrib.auth.decorators import login_required
from apps.transactions.transaction_list.views import TransactionListView
from apps.transactions.transaction_add.views import TransactionAddView
from apps.transactions.transaction_update.views import TransactionUpdateView
from apps.transactions.transaction_delete.views import TransactionDeleteView
urlpatterns = [
path(
"transactions/list/",
login_required(TransactionListView.as_view(template_name="transactions_list.html")),
name="transactions",
),
path(
"transactions/add/",
login_required(TransactionAddView.as_view(template_name="transactions_add.html")),
name="transactions-add",
),
path (
"transactions/update/<int:pk>",
login_required(TransactionUpdateView.as_view(template_name="transactions_update.html")),
name="transactions-update",
),
path (
"transactions/delete/<int:pk>/",
login_required(TransactionDeleteView.as_view()),
name="transactions-delete",
),
]