Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion config/django/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
'open_schools_platform.ticket_management.tickets.apps.TicketsConfig',
'open_schools_platform.organization_management.teachers.apps.TeachersConfig',
'open_schools_platform.testing.apps.TestingConfig',
'open_schools_platform.sms.apps.SmsConfig'
'open_schools_platform.sms.apps.SmsConfig',
'open_schools_platform.receipt_management.receipts.apps.ReceiptsConfig'
]

THIRD_PARTY_APPS = [
Expand Down
1 change: 1 addition & 0 deletions open_schools_platform/api/swagger_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ class SwaggerTags:
PHOTO_MANAGEMENT_PHOTOS = "Photos management. Photos"
HISTORY_MANAGEMENT = "History management"
TICKET_MANAGEMENT_TICKET = "Ticket management. Tickets"
RECEIPT_MANAGEMENT_RECEIPTS = "Receipt management. Receipts"
7 changes: 6 additions & 1 deletion open_schools_platform/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
path('ticket', include(('open_schools_platform.ticket_management.tickets.urls', 'ticket'))),
]

receipt_management_urls = [
path('receipts', include(('open_schools_platform.receipt_management.receipts.urls', 'receipts'))),
]

urlpatterns = [
path('user-management/', include((user_management_urls, 'user-management'))),
path('organization-management/', include((organization_management_urls, 'organization-management'))),
Expand All @@ -53,7 +57,8 @@
path('students-management/', include((students_management_urls, 'students-management'))),
path('photos-management/', include((photos_management_urls, 'photo-management'))),
path('history-management/', include((history_management_urls, 'history-management'))),
path('ticket-management/', include((ticket_management_urls, 'ticket-management')))
path('ticket-management/', include((ticket_management_urls, 'ticket-management'))),
path('receipt-management/', include((receipt_management_urls, 'receipt-management')))
]

if settings.SWAGGER_ENABLED:
Expand Down
Empty file.
Empty file.
105 changes: 105 additions & 0 deletions open_schools_platform/receipt_management/receipts/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from django.contrib import admin
from django.utils.html import format_html

from .models import Receipt, ReceiptNotification


@admin.register(Receipt)
class ReceiptAdmin(admin.ModelAdmin):
list_display = [
'internal_receipt_number', 'recipient_full_name', 'student_profile_name',
'institution_name', 'total_amount', 'payment_due_date',
'created_at', 'pdf_file_link'
]
list_filter = [
'payment_due_date', 'created_at', 'institution_name',
'service_category'
]
search_fields = [
'internal_receipt_number', 'recipient_full_name', 'payer_full_name',
'institution_name', 'service_name', 'student_profile__user__first_name',
'student_profile__user__last_name' ]
readonly_fields = [
'id', 'created_at', 'updated_at'
]
fieldsets = (
('Student Information', {
'fields': ('student_profile', 'recipient_full_name', 'payer_full_name')
}),
('Receipt Details', {
'fields': ('internal_receipt_number', 'institution_name', 'service_name',
'service_category', 'payment_purpose')
}),
('Financial Information', {
'fields': ('debt_at_month_start', 'recalculation_amount', 'paid_amount',
'debt_at_next_month_start', 'prepayment', 'service_amount', 'total_amount')
}),
('Dates', {
'fields': ('receipt_date', 'payment_due_date')
}),
('Files & QR Codes', {
'fields': ('pdf_file', 'qr_code_data') }),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
})
)

def student_profile_name(self, obj):
"""Display student name from profile"""
if obj.student_profile:
return f"{obj.student_profile.user.first_name} {obj.student_profile.user.last_name}"
return "N/A"

student_profile_name.short_description = "Student Name"

def pdf_file_link(self, obj):
if obj.pdf_file:
return format_html(
'<a href="{}" target="_blank">Download PDF</a>',
obj.pdf_file.url
)
return "No PDF"

pdf_file_link.short_description = "PDF File"

def get_queryset(self, request):
return super().get_queryset(request).select_related('student_profile__user')


@admin.register(ReceiptNotification)
class ReceiptNotificationAdmin(admin.ModelAdmin):
list_display = [
'id', 'receipt_number', 'notification_type',
'is_delivered', 'sent_at', 'created_at'
]
list_filter = [
'notification_type', 'is_delivered', 'sent_at', 'created_at'
]
search_fields = [
'receipt__internal_receipt_number', 'receipt__recipient_full_name',
'receipt__payer_full_name', 'receipt__institution_name'
]
readonly_fields = ['id', 'created_at', 'updated_at', 'sent_at']

fieldsets = (
('Notification Details', {
'fields': ('receipt', 'notification_type')
}),
('Status', {
'fields': ('is_delivered', 'sent_at')
}),
('Timestamps', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
})
)

def receipt_number(self, obj):
"""Display receipt number"""
return obj.receipt.internal_receipt_number if obj.receipt else "N/A"

receipt_number.short_description = "Receipt Number"

def get_queryset(self, request):
return super().get_queryset(request).select_related('receipt')
6 changes: 6 additions & 0 deletions open_schools_platform/receipt_management/receipts/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class ReceiptsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'open_schools_platform.receipt_management.receipts'
68 changes: 68 additions & 0 deletions open_schools_platform/receipt_management/receipts/filters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
from django_filters import CharFilter, BooleanFilter, DateFilter, ChoiceFilter, NumberFilter

from open_schools_platform.common.filters import BaseFilterSet, UUIDInFilter, filter_by_ids, MetaCharIContainsMixin
from open_schools_platform.receipt_management.receipts.models import Receipt, ReceiptNotification


class ReceiptFilter(BaseFilterSet):
ids = CharFilter(method=filter_by_ids)
or_search = CharFilter(field_name="or_search", method="OR")

payer_full_name = CharFilter(field_name="payer_full_name", lookup_expr="icontains")
recipient_full_name = CharFilter(field_name="recipient_full_name", lookup_expr="icontains")
student_profile = UUIDInFilter(field_name="student_profile", lookup_expr="in")

institution_name = CharFilter(field_name="institution_name", lookup_expr="icontains")
service_name = CharFilter(field_name="service_name", lookup_expr="icontains")
service_category = CharFilter(field_name="service_category", lookup_expr="icontains")

total_amount_min = NumberFilter(field_name="total_amount", lookup_expr="gte")
total_amount_max = NumberFilter(field_name="total_amount", lookup_expr="lte")
service_amount_min = NumberFilter(field_name="service_amount", lookup_expr="gte")
service_amount_max = NumberFilter(field_name="service_amount", lookup_expr="lte")

payment_due_date_from = DateFilter(field_name="payment_due_date", lookup_expr="gte")
payment_due_date_to = DateFilter(field_name="payment_due_date", lookup_expr="lte")
receipt_date_from = DateFilter(field_name="receipt_date", lookup_expr="gte")
receipt_date_to = DateFilter(field_name="receipt_date", lookup_expr="lte")
created_at_from = DateFilter(field_name="created_at", lookup_expr="gte")
created_at_to = DateFilter(field_name="created_at", lookup_expr="lte")
internal_receipt_number = CharFilter(field_name="internal_receipt_number", lookup_expr="icontains")

payment_purpose = CharFilter(field_name="payment_purpose", lookup_expr="icontains")

class Meta(MetaCharIContainsMixin):
model = Receipt
fields = [
'id', 'payer_full_name', 'recipient_full_name', 'student_profile',
'institution_name', 'service_name', 'service_category',
'total_amount', 'service_amount', 'payment_due_date', 'receipt_date',
'internal_receipt_number', 'payment_purpose',
'created_at', 'updated_at'
]


class ReceiptNotificationFilter(BaseFilterSet):
or_search = CharFilter(field_name="or_search", method="OR")

receipt_recipient_name = CharFilter(field_name="receipt__recipient_full_name", lookup_expr="icontains")
receipt_institution = CharFilter(field_name="receipt__institution_name", lookup_expr="icontains")
receipt_service = CharFilter(field_name="receipt__service_name", lookup_expr="icontains")

notification_type = ChoiceFilter(
field_name="notification_type",
choices=ReceiptNotification.NOTIFICATION_TYPES
)
is_delivered = BooleanFilter(field_name="is_delivered")

created_at_from = DateFilter(field_name="created_at", lookup_expr="gte")
created_at_to = DateFilter(field_name="created_at", lookup_expr="lte")
sent_at_from = DateFilter(field_name="sent_at", lookup_expr="gte")
sent_at_to = DateFilter(field_name="sent_at", lookup_expr="lte")

class Meta(MetaCharIContainsMixin):
model = ReceiptNotification
fields = [
'id', 'receipt', 'notification_type', 'is_delivered',
'created_at', 'updated_at', 'sent_at'
]
Loading