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
2 changes: 1 addition & 1 deletion .idea/jpydzr7-team-1.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

54 changes: 36 additions & 18 deletions Budget_Project/Budget_Application/forms.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from django.db.models import Q
from django.utils import timezone

from django import forms
from django.contrib.auth.password_validation import validate_password
from django.core.exceptions import ValidationError
Expand Down Expand Up @@ -449,17 +452,26 @@ def __init__(self, *args, **kwargs):
form_type = kwargs.pop('form_type', None)
super().__init__(*args, **kwargs)
family = getattr(user, 'family', None)
if family:
if family is not None and user.role !="kid":
self.fields['id_user'].queryset = User.objects.filter(family=family)
else:
self.fields['id_user'].queryset = User.objects.filter(pk=user.pk)
self.fields['id_user'].initial = user.pk
self.fields['income'].required = True
self.fields['expense'].required = True
self.fields['category'].queryset = Categories.objects.filter(
category_type=form_type,
user_id__family=user.family
)
if hasattr(user, 'family') and user.family:
# Filtrujemy po rodzinie i stosujemy distinct, aby uniknąć duplikatów
self.fields['category'].queryset = Categories.objects.filter(
category_type=form_type,
user_id__family=user.family
).distinct()
else:
# Filtrujemy po użytkowniku i stosujemy distinct, aby uniknąć duplikatów
self.fields['category'].queryset = Categories.objects.filter(
category_type=form_type,
user_id=user
).distinct()
self.fields['transaction_date'].initial = timezone.now().date().isoformat()
self.fields['description'].required = False
self.fields['transaction_type'].required = True
if form_type == 'income':
Expand All @@ -485,7 +497,7 @@ def clean_expense(self):

class Meta:
model = DataTransaction
fields = ['id_user','transaction_date', 'income', 'expense', 'category', 'description', 'transaction_type']
fields = ['category', 'id_user','transaction_date', 'income', 'expense', 'description', 'transaction_type']
widgets = {
'transaction_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'income': forms.NumberInput(attrs={'class': 'form-control'}),
Expand All @@ -505,36 +517,42 @@ class Meta:
}

class AddCategory(forms.ModelForm):
#TODO: dodajmy walidację na dublowanie kategorii

def __init__(self, *args, **kwargs):# pobieramy usera z widoku
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
form_type = kwargs.pop('form_type', None)
super().__init__(*args, **kwargs)

def clean_category_name(self):
category_name = self.cleaned_data['category_name']
family = self.user.family
# Sprawdzamy, czy kategoria o takiej nazwie już istnieje dla danego użytkownika
qs = Categories.objects.filter(category_name__iexact=category_name, user_id__family=family)

# Jeśli edycja, to wykluczamy obecną instancję
# Domyślny filtr: ignorujemy wielkość liter
query = Q(category_name__iexact=category_name)

# Jeżeli użytkownik ma rodzinę – szukaj po rodzinie
if hasattr(self.user, 'family') and self.user.family:
query &= Q(user_id__family=self.user.family)
else:
# Jeżeli nie ma rodziny – szukaj tylko po nim
query &= Q(user_id=self.user)

qs = Categories.objects.filter(query)

# Jeśli edycja, wyklucz bieżącą instancję
if self.instance.pk:
qs = qs.exclude(pk=self.instance.pk)

if qs.exists():
raise ValidationError("Ta kategoria już istnieje w Twojej rodzinie.")
return category_name
raise ValidationError("Ta kategoria już istnieje (dla Ciebie lub w Twojej rodzinie).")

return category_name

class Meta:
model = Categories
fields = ['category_name']
widgets = {
'category_name': forms.TextInput(attrs={'class': 'form-control'}),
'category_name': forms.TextInput(attrs={'class': 'form-control'}),
}
labels = {
'category_name': 'Nazwa kategorii'

'category_name': 'Nazwa kategorii'
}

94 changes: 90 additions & 4 deletions Budget_Project/Budget_Application/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,18 +250,63 @@ class Meta:
def __str__(self):
return self.category_name

from django.utils import timezone

class Budget(models.Model):
budget_initial_amount = models.FloatField(blank=True, null=True)
user_id = models.ForeignKey('User', on_delete=models.CASCADE)
budget_init_date = models.DateField()

class Meta:
db_table = 'BUDGET'

def __str__(self):
return f"Budżet użytkownika {self.user_id} z dnia {self.budget_init_date}"

@property
def current_amount(self):
# Pobierz dzisiejszą datę
today = timezone.now().date()

# Sprawdź, czy użytkownik należy do rodziny
if self.user_id.family is not None and self.user_id.role != "kid":
# Jeśli tak, weź wszystkich członków rodziny
family_members = User.objects.filter(family=self.user_id.family)
# Zbierz transakcje dla wszystkich członków rodziny, do dzisiejszej daty
transactions = DataTransaction.objects.filter(
id_user__in=family_members,
transaction_date__gte=self.budget_init_date,
transaction_date__lte=today # Tylko transakcje do dzisiejszej daty
)
else:
# Jeśli użytkownik nie ma rodziny, tylko jego transakcje, do dzisiejszej daty
transactions = DataTransaction.objects.filter(
id_user=self.user_id,
transaction_date__gte=self.budget_init_date,
transaction_date__lte=today # Tylko transakcje do dzisiejszej daty
)

# Zsumuj przychody i wydatki
total_income = sum(t.income or 0 for t in transactions)
total_expense = sum(t.expense or 0 for t in transactions)

# Oblicz aktualny stan budżetu i zaokrąglij do dwóch miejsc po przecinku
current = (self.budget_initial_amount or 0) + total_income - total_expense
return round(current, 2)



import random
from datetime import date, timedelta
@receiver(post_save, sender=User)
def create_default_categories_for_user(sender, instance, created, **kwargs):
if created:
default_categories = [
# Przychody (income)
('Wynagrodzenie', 'income'),
('Prezenty', 'income'),
('Zwrot podatku', 'income'),
('Dochód pasywny', 'income'),
('Inne przychody', 'income'),

# Wydatki (expense)
('Zakupy', 'expense'),
('Transport', 'expense'),
('Czynsz', 'expense'),
Expand All @@ -280,4 +325,45 @@ def create_default_categories_for_user(sender, instance, created, **kwargs):
category_name=name,
category_type=type_,
user_id=instance
)
)

# @receiver(post_save, sender=User)
# def generate_sample_transactions_for_user(sender, instance, created, **kwargs):
# if not created:
# return # Nie generujemy danych dla istniejących użytkowników
#
# income_categories = list(Categories.objects.filter(user_id=instance, category_type='income'))
# expense_categories = list(Categories.objects.filter(user_id=instance, category_type='expense'))
#
# start_date = date(2025, 6, 1)
# end_date = date(2025, 7, 19)
# current_date = start_date
#
# while current_date <= end_date:
# for _ in range(random.randint(1, 3)):
# category = random.choice(income_categories)
# amount = round(random.uniform(10, 500), 2)
# DataTransaction.objects.create(
# id_user=instance,
# transaction_date=current_date,
# income=amount,
# expense=None,
# description=f"{category.category_name} - przychód",
# category=category,
# transaction_type='income'
# )
#
# for _ in range(random.randint(1, 2)):
# category = random.choice(expense_categories)
# amount = round(random.uniform(10, 1000), 2)
# DataTransaction.objects.create(
# id_user=instance,
# transaction_date=current_date,
# income=None,
# expense=amount,
# description=f"{category.category_name} - wydatek",
# category=category,
# transaction_type='expense'
# )
#
# current_date += timedelta(days=1)
2 changes: 2 additions & 0 deletions Budget_Project/Budget_Application/signals.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from django.conf import settings
from django.apps import apps
from django.db.models.signals import post_save
from django.dispatch import receiver

User = apps.get_model(settings.AUTH_USER_MODEL.split('.')[0], settings.AUTH_USER_MODEL.split('.')[1])

Expand Down
5 changes: 4 additions & 1 deletion Budget_Project/Budget_Application/static/css/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@
.datepicker table tr td.active:hover.active {
background-color: #0d6efd !important;
background-image: none;
}
}



Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ <h2 class="mb-4 text-center">
{{ field }}
{% endif %}
{% if field.name == 'category' %}

<a class="btn btn-outline-secondary" href="{% url 'add_category' form_type %}">Edytuj listę kategorii</a>
<br>
<br>
{% endif %}
{% endfor %}
<div class="text-center">
Expand Down
89 changes: 89 additions & 0 deletions Budget_Project/Budget_Application/templates/budget_status.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!DOCTYPE html>
{% load static %}
<html lang="pl">
<head>
<meta charset="UTF-8" />
<title>{% if is_family %}Budżet rodziny{% else %}Twój budżet{% endif %}</title>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
{% include 'navbar.html' %}

<div class="container mt-4">
<h2>{% if is_family and user.role != "kid"%}Budżet rodziny{% else %}Twój budżet{% endif %}</h2>

{% if budget %}
<div class="card mb-3">
<div class="card-body">
<p><strong>Kwota początkowa:</strong> {{ budget.budget_initial_amount }} PLN</p>
<p><strong>Data utworzenia:</strong> {{ budget.budget_init_date }}</p>
<p><strong>Aktualny stan budżetu:</strong>
{% if budget.current_amount < 0 %}
<span class="text-danger">{{ budget.current_amount }} PLN</span>
{% else %}
<span class="text-success">{{ budget.current_amount }} PLN</span>
{% endif %}
</p>
<form method="post" style="display:inline;">
{% csrf_token %}
<input type="hidden" name="action" value="delete">
<button type="submit" class="btn btn-secondary"
onclick="return confirm('Czy na pewno chcesz usunąć budżet?');">
Usuń budżet
</button>
</form>
</div>
</div>
{% else %}
<div class="alert alert-info">Nie masz jeszcze zdefiniowanego budżetu. Podaj początkową kwotę:</div>
<form method="post" class="card p-3">
{% csrf_token %}
<input type="hidden" name="action" value="create">
<div class="mb-3">
<label for="initial_amount" class="form-label">
Kwota początkowa
<span data-bs-toggle="popover" title="Co to jest?"
data-bs-content="Kwota którą dysponujesz w poniżej podanym dniu."
style="cursor: pointer; color: #0d6efd;">*</span>
</label>
<input type="number" step="0.01" name="initial_amount" id="initial_amount" class="form-control" required>
</div>
<div class="mb-3">
<label for="budget_init_date" class="form-label">Data rozpoczęcia budżetu</label>
<input type="date" name="budget_init_date" id="budget_init_date" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Utwórz budżet</button>
</form>
{% endif %}

{% if is_family and user.role != "kid"%}
<hr />
<h3>Średnie miesięczne przychody i wydatki rodziny i członków</h3>
<table class="table table-striped mt-3">
<thead>
<tr>
<th>Członek rodziny</th>
<th>Średnie miesięczne przychody (PLN)</th>
<th>Średnie miesięczne wydatki (PLN)</th>
</tr>
</thead>
<tbody>
{% for member in member_stats %}
<tr>
<td>{{ member.name }}</td>
<td class="text-success">{{ member.avg_income }}</td>
<td class="text-danger">{{ member.avg_expense }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<script>
const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
const popoverList = [...popoverTriggerList].map(el => new bootstrap.Popover(el))
</script>
</body>
</html>
Loading