diff --git a/app/apps/calendar_view/__init__.py b/app/apps/calendar_view/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/apps/calendar_view/apps.py b/app/apps/calendar_view/apps.py new file mode 100644 index 0000000..43934c9 --- /dev/null +++ b/app/apps/calendar_view/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CalendarViewConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "apps.calendar_view" diff --git a/app/apps/calendar_view/migrations/__init__.py b/app/apps/calendar_view/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/apps/calendar_view/urls.py b/app/apps/calendar_view/urls.py new file mode 100644 index 0000000..9d5c747 --- /dev/null +++ b/app/apps/calendar_view/urls.py @@ -0,0 +1,27 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path("calendar/", views.index, name="calendar_index"), + path( + "calendar///list/", + views.calendar_list, + name="calendar_list", + ), + path( + "calendar////transactions/", + views.calendar_transactions_list, + name="calendar_transactions_list", + ), + path( + "calendar///", + views.calendar, + name="calendar", + ), + # path( + # "calendar/available_dates/", + # views.month_year_picker, + # name="available_dates", + # ), +] diff --git a/app/apps/calendar_view/utils/__init__.py b/app/apps/calendar_view/utils/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/apps/calendar_view/utils/calendar.py b/app/apps/calendar_view/utils/calendar.py new file mode 100644 index 0000000..ffdfb9c --- /dev/null +++ b/app/apps/calendar_view/utils/calendar.py @@ -0,0 +1,65 @@ +from datetime import datetime, date +import calendar + +from apps.transactions.models import Transaction + + +# def get_transactions_by_day(year, month): +# # Get all transactions for the month +# transactions = Transaction.objects.filter( +# date__year=year, date__month=month +# ).order_by("date") +# +# # Create a dictionary with all days of the month +# all_days = { +# day: {"day": day, "date": date(year, month, day), "transactions": []} +# for day in range(1, calendar.monthrange(year, month)[1] + 1) +# } +# +# # Group transactions by day +# for transaction in transactions: +# day = transaction.date.day +# all_days[day]["transactions"].append(transaction) +# +# # Convert to list and sort by day +# result = list(all_days.values()) +# +# return result + + +def get_transactions_by_day(year, month): + # Configure calendar to start on Monday + calendar.setfirstweekday(calendar.MONDAY) + + # Get the first and last day of the month + first_day = date(year, month, 1) + + # Get all transactions for the month + transactions = Transaction.objects.filter( + date__year=year, date__month=month + ).order_by("date") + + # Calculate padding days needed + padding_days = first_day.weekday() # Monday is 0, Sunday is 6 + + # Create padding days as empty dicts + padding_dates = [{}] * padding_days + + # Create current month days + current_month_dates = [ + {"day": day, "date": date(year, month, day), "transactions": []} + for day in range(1, calendar.monthrange(year, month)[1] + 1) + ] + + # Group transactions by day + for transaction in transactions: + day = transaction.date.day + for day_data in current_month_dates: + if day_data["day"] == day: + day_data["transactions"].append(transaction) + break + + # Combine padding and current month dates + result = padding_dates + current_month_dates + + return result diff --git a/app/apps/calendar_view/views.py b/app/apps/calendar_view/views.py new file mode 100644 index 0000000..a38f050 --- /dev/null +++ b/app/apps/calendar_view/views.py @@ -0,0 +1,78 @@ +import datetime + +from django.contrib.auth.decorators import login_required +from django.shortcuts import redirect, render +from django.utils import timezone +from django.views.decorators.http import require_http_methods + +from apps.calendar_view.utils.calendar import get_transactions_by_day +from apps.transactions.models import Transaction + + +@login_required +def index(request): + now = timezone.localdate(timezone.now()) + + return redirect(to="calendar", month=now.month, year=now.year) + + +@login_required +@require_http_methods(["GET"]) +def calendar(request, month: int, year: int): + if month < 1 or month > 12: + from django.http import Http404 + + raise Http404("Month is out of range") + + next_month = 1 if month == 12 else month + 1 + next_year = year + 1 if next_month == 1 and month == 12 else year + + previous_month = 12 if month == 1 else month - 1 + previous_year = year - 1 if previous_month == 12 and month == 1 else year + + return render( + request, + "calendar_view/pages/calendar.html", + context={ + "month": month, + "year": year, + "next_month": next_month, + "next_year": next_year, + "previous_month": previous_month, + "previous_year": previous_year, + }, + ) + + +@login_required +@require_http_methods(["GET"]) +def calendar_list(request, month: int, year: int): + today = timezone.localdate(timezone.now()) + dates = get_transactions_by_day(month=month, year=year) + + return render( + request, + "calendar_view/fragments/list.html", + context={ + "month": month, + "year": year, + "today": today, + "dates": dates, + }, + ) + + +@login_required +@require_http_methods(["GET"]) +def calendar_transactions_list(request, day: int, month: int, year: int): + date = datetime.date(year=year, month=month, day=day) + transactions = Transaction.objects.filter(date=date) + + return render( + request, + "calendar_view/fragments/list_transactions.html", + context={ + "date": date, + "transactions": transactions, + }, + ) diff --git a/app/templates/calendar_view/fragments/list.html b/app/templates/calendar_view/fragments/list.html new file mode 100644 index 0000000..f7c37bc --- /dev/null +++ b/app/templates/calendar_view/fragments/list.html @@ -0,0 +1,69 @@ +{% load natural %} +{% load i18n %} + +
+
+
+ {% translate 'MON' %} +
+
+ {% translate 'TUE' %} +
+
+ {% translate 'WED' %} +
+
+ {% translate 'THU' %} +
+
+ {% translate 'FRI' %} +
+
+ {% translate 'SAT' %} +
+
+ {% translate 'SUN' %} +
+
+
+ {% for date in dates %} + {% if date %} +
+
+
{{ date.date|date:"l"|lower }}
+
{{ date.day }}
+
+
+ {% for transaction in date.transactions %} + {% if transaction.is_paid %} + {% if transaction.type == "IN" and not transaction.account.is_asset %} + + {% elif transaction.type == "IN" and transaction.account.is_asset %} + + {% elif transaction.type == "EX" and not transaction.account.is_asset %} + + {% elif transaction.type == "EX" and transaction.account.is_asset %} + + {% endif %} + {% else %} + {% if transaction.type == "IN" and not transaction.account.is_asset %} + + {% elif transaction.type == "IN" and transaction.account.is_asset %} + + {% elif transaction.type == "EX" and not transaction.account.is_asset %} + + {% elif transaction.type == "EX" and transaction.account.is_asset %} + + {% endif %} + {% endif %} + {% endfor %} +
+
+ {% else %} +
+ {% endif %} + {% endfor %} +
+
diff --git a/app/templates/calendar_view/fragments/list_transactions.html b/app/templates/calendar_view/fragments/list_transactions.html new file mode 100644 index 0000000..ba1bd65 --- /dev/null +++ b/app/templates/calendar_view/fragments/list_transactions.html @@ -0,0 +1,18 @@ +{% extends 'extends/offcanvas.html' %} +{% load i18n %} +{% load crispy_forms_tags %} + +{% block title %}{% translate 'Transactions on' %} {{ date|date:"SHORT_DATE_FORMAT" }}{% endblock %} + +{% block body %} +
+{% for transaction in transactions %} + +{% empty %} + +{% endfor %} +
+{% endblock %} diff --git a/app/templates/calendar_view/pages/calendar.html b/app/templates/calendar_view/pages/calendar.html new file mode 100644 index 0000000..562f3ec --- /dev/null +++ b/app/templates/calendar_view/pages/calendar.html @@ -0,0 +1,100 @@ +{% extends "layouts/base.html" %} +{% load crispy_forms_tags %} +{% load i18n %} +{% load month_name %} +{% load static %} +{% load webpack_loader %} + +{% block title %}{% translate 'Monthly Overview' %} :: {{ month|month_name }}/{{ year }}{% endblock %} + +{% block body_hyperscript %} + on keyup[code is 'ArrowLeft' and target.nodeName is 'BODY'] from body trigger 'previous_month' end + on keyup[code is 'ArrowRight' and target.nodeName is 'BODY'] from body trigger 'next_month' end +{% endblock %} + +{% block content %} +
+
+{# Date picker#} +
+
+ +
+
+ {{ month|month_name }} {{ year }} +
+
+ + + +
+
+{# Action buttons#} +
+
+ + + + + + +
+
+
+
+
+
+
+{% endblock %}