mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-04-26 02:28:35 +02:00
feat: replace action row with a FAB
This commit is contained in:
@@ -8,35 +8,45 @@ from django.contrib.auth import get_user_model
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils import timezone
|
||||
from decimal import Decimal
|
||||
import datetime # Import was missing
|
||||
import datetime # Import was missing
|
||||
|
||||
from apps.transactions.models import (
|
||||
TransactionCategory,
|
||||
TransactionTag,
|
||||
TransactionEntity, # Added
|
||||
TransactionEntity, # Added
|
||||
Transaction,
|
||||
InstallmentPlan,
|
||||
RecurringTransaction,
|
||||
)
|
||||
from apps.accounts.models import Account, AccountGroup
|
||||
from apps.currencies.models import Currency, ExchangeRate
|
||||
from apps.common.models import SharedObject
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class BaseTransactionAppTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create_user(email="testuser@example.com", password="password")
|
||||
self.other_user = User.objects.create_user(email="otheruser@example.com", password="password")
|
||||
self.user = User.objects.create_user(
|
||||
email="testuser@example.com", password="password"
|
||||
)
|
||||
self.other_user = User.objects.create_user(
|
||||
email="otheruser@example.com", password="password"
|
||||
)
|
||||
self.client = Client()
|
||||
self.client.login(email="testuser@example.com", password="password")
|
||||
|
||||
self.currency = Currency.objects.create(
|
||||
code="USD", name="US Dollar", decimal_places=2, prefix="$ "
|
||||
)
|
||||
self.account_group = AccountGroup.objects.create(name="Test Group", owner=self.user)
|
||||
self.account_group = AccountGroup.objects.create(
|
||||
name="Test Group", owner=self.user
|
||||
)
|
||||
self.account = Account.objects.create(
|
||||
name="Test Account", group=self.account_group, currency=self.currency, owner=self.user
|
||||
name="Test Account",
|
||||
group=self.account_group,
|
||||
currency=self.currency,
|
||||
owner=self.user,
|
||||
)
|
||||
|
||||
|
||||
@@ -50,13 +60,24 @@ class TransactionCategoryTests(BaseTransactionAppTest):
|
||||
self.assertEqual(category.owner, self.user)
|
||||
|
||||
def test_category_creation_view(self):
|
||||
response = self.client.post(reverse("category_add"), {"name": "Utilities", "active": "on"})
|
||||
self.assertEqual(response.status_code, 204) # HTMX success, no content
|
||||
self.assertTrue(TransactionCategory.objects.filter(name="Utilities", owner=self.user).exists())
|
||||
response = self.client.post(
|
||||
reverse("category_add"), {"name": "Utilities", "active": "on"}
|
||||
)
|
||||
self.assertEqual(response.status_code, 204) # HTMX success, no content
|
||||
self.assertTrue(
|
||||
TransactionCategory.objects.filter(
|
||||
name="Utilities", owner=self.user
|
||||
).exists()
|
||||
)
|
||||
|
||||
def test_category_edit_view(self):
|
||||
category = TransactionCategory.objects.create(name="Initial Name", owner=self.user)
|
||||
response = self.client.post(reverse("category_edit", args=[category.id]), {"name": "Updated Name", "mute": "on", "active": "on"})
|
||||
category = TransactionCategory.objects.create(
|
||||
name="Initial Name", owner=self.user
|
||||
)
|
||||
response = self.client.post(
|
||||
reverse("category_edit", args=[category.id]),
|
||||
{"name": "Updated Name", "mute": "on", "active": "on"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
category.refresh_from_db()
|
||||
self.assertEqual(category.name, "Updated Name")
|
||||
@@ -66,25 +87,38 @@ class TransactionCategoryTests(BaseTransactionAppTest):
|
||||
category = TransactionCategory.objects.create(name="To Delete", owner=self.user)
|
||||
response = self.client.delete(reverse("category_delete", args=[category.id]))
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertFalse(TransactionCategory.all_objects.filter(id=category.id).exists()) # all_objects to check even if soft deleted by mistake
|
||||
self.assertFalse(
|
||||
TransactionCategory.all_objects.filter(id=category.id).exists()
|
||||
) # all_objects to check even if soft deleted by mistake
|
||||
|
||||
def test_other_user_cannot_edit_category(self):
|
||||
category = TransactionCategory.objects.create(name="User1s Category", owner=self.user)
|
||||
category = TransactionCategory.objects.create(
|
||||
name="User1s Category", owner=self.user
|
||||
)
|
||||
self.client.logout()
|
||||
self.client.login(email="otheruser@example.com", password="password")
|
||||
response = self.client.post(reverse("category_edit", args=[category.id]), {"name": "Attempted Update"})
|
||||
response = self.client.post(
|
||||
reverse("category_edit", args=[category.id]), {"name": "Attempted Update"}
|
||||
)
|
||||
# This should return a 204 with a message, not a 403, as per view logic for owned objects
|
||||
self.assertEqual(response.status_code, 204)
|
||||
category.refresh_from_db()
|
||||
self.assertEqual(category.name, "User1s Category") # Name should not change
|
||||
self.assertEqual(category.name, "User1s Category") # Name should not change
|
||||
|
||||
def test_category_sharing_and_visibility(self):
|
||||
category = TransactionCategory.objects.create(name="Shared Cat", owner=self.user, visibility=TransactionCategory.Visibility.SHARED)
|
||||
category = TransactionCategory.objects.create(
|
||||
name="Shared Cat",
|
||||
owner=self.user,
|
||||
visibility=SharedObject.Visibility.private,
|
||||
)
|
||||
category.shared_with.add(self.other_user)
|
||||
|
||||
# Other user should be able to see it (though not directly tested here, view logic would permit)
|
||||
# Test that owner can still edit
|
||||
response = self.client.post(reverse("category_edit", args=[category.id]), {"name": "Owner Edited Shared Cat", "active":"on"})
|
||||
response = self.client.post(
|
||||
reverse("category_edit", args=[category.id]),
|
||||
{"name": "Owner Edited Shared Cat", "active": "on"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
category.refresh_from_db()
|
||||
self.assertEqual(category.name, "Owner Edited Shared Cat")
|
||||
@@ -92,7 +126,9 @@ class TransactionCategoryTests(BaseTransactionAppTest):
|
||||
# Test other user cannot delete if not owner
|
||||
self.client.logout()
|
||||
self.client.login(email="otheruser@example.com", password="password")
|
||||
response = self.client.delete(reverse("category_delete", args=[category.id])) # This removes user from shared_with
|
||||
response = self.client.delete(
|
||||
reverse("category_delete", args=[category.id])
|
||||
) # This removes user from shared_with
|
||||
self.assertEqual(response.status_code, 204)
|
||||
category.refresh_from_db()
|
||||
self.assertTrue(TransactionCategory.all_objects.filter(id=category.id).exists())
|
||||
@@ -108,13 +144,19 @@ class TransactionTagTests(BaseTransactionAppTest):
|
||||
self.assertEqual(tag.owner, self.user)
|
||||
|
||||
def test_tag_creation_view(self):
|
||||
response = self.client.post(reverse("tag_add"), {"name": "Vacation", "active": "on"})
|
||||
response = self.client.post(
|
||||
reverse("tag_add"), {"name": "Vacation", "active": "on"}
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertTrue(TransactionTag.objects.filter(name="Vacation", owner=self.user).exists())
|
||||
self.assertTrue(
|
||||
TransactionTag.objects.filter(name="Vacation", owner=self.user).exists()
|
||||
)
|
||||
|
||||
def test_tag_edit_view(self):
|
||||
tag = TransactionTag.objects.create(name="Old Tag", owner=self.user)
|
||||
response = self.client.post(reverse("tag_edit", args=[tag.id]), {"name": "New Tag", "active": "on"})
|
||||
response = self.client.post(
|
||||
reverse("tag_edit", args=[tag.id]), {"name": "New Tag", "active": "on"}
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
tag.refresh_from_db()
|
||||
self.assertEqual(tag.name, "New Tag")
|
||||
@@ -135,39 +177,54 @@ class TransactionEntityTests(BaseTransactionAppTest):
|
||||
self.assertEqual(entity.owner, self.user)
|
||||
|
||||
def test_entity_creation_view(self):
|
||||
response = self.client.post(reverse("entity_add"), {"name": "Online Store", "active": "on"})
|
||||
response = self.client.post(
|
||||
reverse("entity_add"), {"name": "Online Store", "active": "on"}
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertTrue(TransactionEntity.objects.filter(name="Online Store", owner=self.user).exists())
|
||||
self.assertTrue(
|
||||
TransactionEntity.objects.filter(
|
||||
name="Online Store", owner=self.user
|
||||
).exists()
|
||||
)
|
||||
|
||||
def test_entity_edit_view(self):
|
||||
entity = TransactionEntity.objects.create(name="Local Shop", owner=self.user)
|
||||
response = self.client.post(reverse("entity_edit", args=[entity.id]), {"name": "Local Shop Inc.", "active": "on"})
|
||||
response = self.client.post(
|
||||
reverse("entity_edit", args=[entity.id]),
|
||||
{"name": "Local Shop Inc.", "active": "on"},
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
entity.refresh_from_db()
|
||||
self.assertEqual(entity.name, "Local Shop Inc.")
|
||||
|
||||
def test_entity_delete_view(self):
|
||||
entity = TransactionEntity.objects.create(name="To Be Removed Entity", owner=self.user)
|
||||
entity = TransactionEntity.objects.create(
|
||||
name="To Be Removed Entity", owner=self.user
|
||||
)
|
||||
response = self.client.delete(reverse("entity_delete", args=[entity.id]))
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertFalse(TransactionEntity.all_objects.filter(id=entity.id).exists())
|
||||
|
||||
|
||||
class TransactionTests(BaseTransactionAppTest): # Inherit from BaseTransactionAppTest
|
||||
class TransactionTests(BaseTransactionAppTest): # Inherit from BaseTransactionAppTest
|
||||
def setUp(self):
|
||||
super().setUp() # Call BaseTransactionAppTest's setUp
|
||||
super().setUp() # Call BaseTransactionAppTest's setUp
|
||||
"""Set up test data"""
|
||||
# self.category is already created in BaseTransactionAppTest if needed,
|
||||
# or create specific ones here.
|
||||
self.category = TransactionCategory.objects.create(name="Test Category", owner=self.user)
|
||||
self.category = TransactionCategory.objects.create(
|
||||
name="Test Category", owner=self.user
|
||||
)
|
||||
self.tag = TransactionTag.objects.create(name="Test Tag", owner=self.user)
|
||||
self.entity = TransactionEntity.objects.create(name="Test Entity", owner=self.user)
|
||||
self.entity = TransactionEntity.objects.create(
|
||||
name="Test Entity", owner=self.user
|
||||
)
|
||||
|
||||
def test_transaction_creation(self):
|
||||
"""Test basic transaction creation with required fields"""
|
||||
transaction = Transaction.objects.create(
|
||||
account=self.account,
|
||||
owner=self.user, # Assign owner
|
||||
owner=self.user, # Assign owner
|
||||
type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(),
|
||||
amount=Decimal("100.00"),
|
||||
@@ -184,7 +241,6 @@ class TransactionTests(BaseTransactionAppTest): # Inherit from BaseTransactionAp
|
||||
self.assertIn(self.tag, transaction.tags.all())
|
||||
self.assertIn(self.entity, transaction.entities.all())
|
||||
|
||||
|
||||
def test_transaction_creation_view(self):
|
||||
data = {
|
||||
"account": self.account.id,
|
||||
@@ -194,90 +250,122 @@ class TransactionTests(BaseTransactionAppTest): # Inherit from BaseTransactionAp
|
||||
"amount": "250.75",
|
||||
"description": "Freelance Gig",
|
||||
"category": self.category.id,
|
||||
"tags": [self.tag.name], # Dynamic fields expect names for creation/selection
|
||||
"entities": [self.entity.name]
|
||||
"tags": [
|
||||
self.tag.name
|
||||
], # Dynamic fields expect names for creation/selection
|
||||
"entities": [self.entity.name],
|
||||
}
|
||||
response = self.client.post(reverse("transaction_add"), data)
|
||||
self.assertEqual(response.status_code, 204, response.content.decode() if response.content else "No content")
|
||||
self.assertEqual(
|
||||
response.status_code,
|
||||
204,
|
||||
response.content.decode() if response.content else "No content",
|
||||
)
|
||||
self.assertTrue(
|
||||
Transaction.objects.filter(description="Freelance Gig", owner=self.user, amount=Decimal("250.75")).exists()
|
||||
Transaction.objects.filter(
|
||||
description="Freelance Gig", owner=self.user, amount=Decimal("250.75")
|
||||
).exists()
|
||||
)
|
||||
# Check that tag and entity were associated (or created if DynamicModel...Field handled it)
|
||||
created_transaction = Transaction.objects.get(description="Freelance Gig")
|
||||
self.assertIn(self.tag, created_transaction.tags.all())
|
||||
self.assertIn(self.entity, created_transaction.entities.all())
|
||||
|
||||
|
||||
def test_transaction_edit_view(self):
|
||||
transaction = Transaction.objects.create(
|
||||
account=self.account, owner=self.user, type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(), amount=Decimal("50.00"), description="Initial"
|
||||
account=self.account,
|
||||
owner=self.user,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(),
|
||||
amount=Decimal("50.00"),
|
||||
description="Initial",
|
||||
)
|
||||
updated_description = "Updated Description"
|
||||
updated_amount = "75.25"
|
||||
response = self.client.post(
|
||||
reverse("transaction_edit", args=[transaction.id]),
|
||||
{
|
||||
"account": self.account.id, "type": Transaction.Type.EXPENSE, "is_paid": "on",
|
||||
"date": transaction.date.isoformat(), "amount": updated_amount,
|
||||
"description": updated_description, "category": self.category.id
|
||||
}
|
||||
"account": self.account.id,
|
||||
"type": Transaction.Type.EXPENSE,
|
||||
"is_paid": "on",
|
||||
"date": transaction.date.isoformat(),
|
||||
"amount": updated_amount,
|
||||
"description": updated_description,
|
||||
"category": self.category.id,
|
||||
},
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
transaction.refresh_from_db()
|
||||
self.assertEqual(transaction.description, updated_description)
|
||||
self.assertEqual(transaction.amount, Decimal(updated_amount))
|
||||
|
||||
|
||||
def test_transaction_soft_delete_view(self):
|
||||
transaction = Transaction.objects.create(
|
||||
account=self.account, owner=self.user, type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(), amount=Decimal("10.00"), description="To Soft Delete"
|
||||
account=self.account,
|
||||
owner=self.user,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(),
|
||||
amount=Decimal("10.00"),
|
||||
description="To Soft Delete",
|
||||
)
|
||||
response = self.client.delete(
|
||||
reverse("transaction_delete", args=[transaction.id])
|
||||
)
|
||||
response = self.client.delete(reverse("transaction_delete", args=[transaction.id]))
|
||||
self.assertEqual(response.status_code, 204)
|
||||
transaction.refresh_from_db()
|
||||
self.assertTrue(transaction.deleted)
|
||||
self.assertIsNotNone(transaction.deleted_at)
|
||||
self.assertTrue(Transaction.deleted_objects.filter(id=transaction.id).exists())
|
||||
self.assertFalse(Transaction.objects.filter(id=transaction.id).exists()) # Default manager should not find it
|
||||
self.assertFalse(
|
||||
Transaction.objects.filter(id=transaction.id).exists()
|
||||
) # Default manager should not find it
|
||||
|
||||
def test_transaction_hard_delete_after_soft_delete(self):
|
||||
# First soft delete
|
||||
transaction = Transaction.objects.create(
|
||||
account=self.account, owner=self.user, type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(), amount=Decimal("15.00"), description="To Hard Delete"
|
||||
account=self.account,
|
||||
owner=self.user,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(),
|
||||
amount=Decimal("15.00"),
|
||||
description="To Hard Delete",
|
||||
)
|
||||
transaction.delete() # Soft delete via model method
|
||||
transaction.delete() # Soft delete via model method
|
||||
self.assertTrue(Transaction.deleted_objects.filter(id=transaction.id).exists())
|
||||
|
||||
# Then hard delete via view (which calls model's delete again on an already soft-deleted item)
|
||||
response = self.client.delete(reverse("transaction_delete", args=[transaction.id]))
|
||||
response = self.client.delete(
|
||||
reverse("transaction_delete", args=[transaction.id])
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertFalse(Transaction.all_objects.filter(id=transaction.id).exists())
|
||||
|
||||
|
||||
def test_transaction_undelete_view(self):
|
||||
transaction = Transaction.objects.create(
|
||||
account=self.account, owner=self.user, type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(), amount=Decimal("20.00"), description="To Undelete"
|
||||
account=self.account,
|
||||
owner=self.user,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(),
|
||||
amount=Decimal("20.00"),
|
||||
description="To Undelete",
|
||||
)
|
||||
transaction.delete() # Soft delete
|
||||
transaction.delete() # Soft delete
|
||||
transaction.refresh_from_db()
|
||||
self.assertTrue(transaction.deleted)
|
||||
|
||||
response = self.client.get(reverse("transaction_undelete", args=[transaction.id]))
|
||||
response = self.client.get(
|
||||
reverse("transaction_undelete", args=[transaction.id])
|
||||
)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
transaction.refresh_from_db()
|
||||
self.assertFalse(transaction.deleted)
|
||||
self.assertIsNone(transaction.deleted_at)
|
||||
self.assertTrue(Transaction.objects.filter(id=transaction.id).exists())
|
||||
|
||||
|
||||
def test_transaction_with_exchange_currency(self):
|
||||
"""Test transaction with exchange currency"""
|
||||
eur = Currency.objects.create(
|
||||
code="EUR", name="Euro", decimal_places=2, prefix="€", owner=self.user
|
||||
code="EUR", name="Euro", decimal_places=2, prefix="€"
|
||||
)
|
||||
self.account.exchange_currency = eur
|
||||
self.account.save()
|
||||
@@ -287,8 +375,8 @@ class TransactionTests(BaseTransactionAppTest): # Inherit from BaseTransactionAp
|
||||
from_currency=self.currency,
|
||||
to_currency=eur,
|
||||
rate=Decimal("0.85"),
|
||||
date=timezone.now().date(), # Ensure date matches transaction or is general
|
||||
owner=self.user
|
||||
date=timezone.now().date(), # Ensure date matches transaction or is general
|
||||
owner=self.user,
|
||||
)
|
||||
|
||||
transaction = Transaction.objects.create(
|
||||
@@ -352,39 +440,56 @@ class TransactionTests(BaseTransactionAppTest): # Inherit from BaseTransactionAp
|
||||
|
||||
def test_transaction_transfer_view(self):
|
||||
other_account = Account.objects.create(
|
||||
name="Other Account", group=self.account_group, currency=self.currency, owner=self.user
|
||||
name="Other Account",
|
||||
group=self.account_group,
|
||||
currency=self.currency,
|
||||
owner=self.user,
|
||||
)
|
||||
data = {
|
||||
"from_account": self.account.id,
|
||||
"to_account": other_account.id,
|
||||
"from_amount": "100.00",
|
||||
"to_amount": "100.00", # Assuming same currency for simplicity
|
||||
"to_amount": "100.00", # Assuming same currency for simplicity
|
||||
"date": timezone.now().date().isoformat(),
|
||||
"description": "Test Transfer",
|
||||
}
|
||||
response = self.client.post(reverse("transactions_transfer"), data)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
self.assertTrue(
|
||||
Transaction.objects.filter(account=self.account, type=Transaction.Type.EXPENSE, amount="100.00").exists()
|
||||
Transaction.objects.filter(
|
||||
account=self.account, type=Transaction.Type.EXPENSE, amount="100.00"
|
||||
).exists()
|
||||
)
|
||||
self.assertTrue(
|
||||
Transaction.objects.filter(account=other_account, type=Transaction.Type.INCOME, amount="100.00").exists()
|
||||
Transaction.objects.filter(
|
||||
account=other_account, type=Transaction.Type.INCOME, amount="100.00"
|
||||
).exists()
|
||||
)
|
||||
|
||||
def test_transaction_bulk_edit_view(self):
|
||||
t1 = Transaction.objects.create(
|
||||
account=self.account, owner=self.user, type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(), amount=Decimal("10.00"), description="Bulk 1"
|
||||
account=self.account,
|
||||
owner=self.user,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(),
|
||||
amount=Decimal("10.00"),
|
||||
description="Bulk 1",
|
||||
)
|
||||
t2 = Transaction.objects.create(
|
||||
account=self.account, owner=self.user, type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(), amount=Decimal("20.00"), description="Bulk 2"
|
||||
account=self.account,
|
||||
owner=self.user,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
date=timezone.now().date(),
|
||||
amount=Decimal("20.00"),
|
||||
description="Bulk 2",
|
||||
)
|
||||
new_category = TransactionCategory.objects.create(
|
||||
name="Bulk Category", owner=self.user
|
||||
)
|
||||
new_category = TransactionCategory.objects.create(name="Bulk Category", owner=self.user)
|
||||
data = {
|
||||
"transactions": [t1.id, t2.id],
|
||||
"category": new_category.id,
|
||||
"is_paid": "true", # NullBoolean can be 'true', 'false', or empty for no change
|
||||
"is_paid": "true", # NullBoolean can be 'true', 'false', or empty for no change
|
||||
}
|
||||
response = self.client.post(reverse("transactions_bulk_edit"), data)
|
||||
self.assertEqual(response.status_code, 204)
|
||||
@@ -396,18 +501,21 @@ class TransactionTests(BaseTransactionAppTest): # Inherit from BaseTransactionAp
|
||||
self.assertTrue(t2.is_paid)
|
||||
|
||||
|
||||
class InstallmentPlanTests(BaseTransactionAppTest): # Inherit from BaseTransactionAppTest
|
||||
class InstallmentPlanTests(
|
||||
BaseTransactionAppTest
|
||||
): # Inherit from BaseTransactionAppTest
|
||||
def setUp(self):
|
||||
super().setUp() # Call BaseTransactionAppTest's setUp
|
||||
super().setUp() # Call BaseTransactionAppTest's setUp
|
||||
# self.currency and self.account are available from base
|
||||
self.category = TransactionCategory.objects.create(name="Installments", owner=self.user)
|
||||
self.category = TransactionCategory.objects.create(
|
||||
name="Installments", owner=self.user
|
||||
)
|
||||
|
||||
def test_installment_plan_creation_and_transaction_generation(self):
|
||||
"""Test basic installment plan creation and its transaction generation."""
|
||||
start_date = timezone.now().date()
|
||||
plan = InstallmentPlan.objects.create(
|
||||
account=self.account,
|
||||
owner=self.user,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
description="Test Plan",
|
||||
number_of_installments=3,
|
||||
@@ -416,10 +524,10 @@ class InstallmentPlanTests(BaseTransactionAppTest): # Inherit from BaseTransacti
|
||||
recurrence=InstallmentPlan.Recurrence.MONTHLY,
|
||||
category=self.category,
|
||||
)
|
||||
plan.create_transactions() # Manually call as it's not in save in the form
|
||||
plan.create_transactions() # Manually call as it's not in save in the form
|
||||
|
||||
self.assertEqual(plan.transactions.count(), 3)
|
||||
first_transaction = plan.transactions.order_by('date').first()
|
||||
first_transaction = plan.transactions.order_by("date").first()
|
||||
self.assertEqual(first_transaction.amount, Decimal("100.00"))
|
||||
self.assertEqual(first_transaction.date, start_date)
|
||||
self.assertEqual(first_transaction.category, self.category)
|
||||
@@ -427,52 +535,64 @@ class InstallmentPlanTests(BaseTransactionAppTest): # Inherit from BaseTransacti
|
||||
def test_installment_plan_update_transactions(self):
|
||||
start_date = timezone.now().date()
|
||||
plan = InstallmentPlan.objects.create(
|
||||
account=self.account, owner=self.user, type=Transaction.Type.EXPENSE,
|
||||
description="Initial Plan", number_of_installments=2, start_date=start_date,
|
||||
installment_amount=Decimal("50.00"), recurrence=InstallmentPlan.Recurrence.MONTHLY,
|
||||
account=self.account,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
description="Initial Plan",
|
||||
number_of_installments=2,
|
||||
start_date=start_date,
|
||||
installment_amount=Decimal("50.00"),
|
||||
recurrence=InstallmentPlan.Recurrence.MONTHLY,
|
||||
)
|
||||
plan.create_transactions()
|
||||
self.assertEqual(plan.transactions.count(), 2)
|
||||
|
||||
plan.description = "Updated Plan Description"
|
||||
plan.installment_amount = Decimal("60.00")
|
||||
plan.number_of_installments = 3 # Increase installments
|
||||
plan.save() # This should trigger _calculate_end_date and _calculate_installment_total_number
|
||||
plan.update_transactions() # Manually call as it's not in save in the form
|
||||
plan.number_of_installments = 3 # Increase installments
|
||||
plan.save() # This should trigger _calculate_end_date and _calculate_installment_total_number
|
||||
plan.update_transactions() # Manually call as it's not in save in the form
|
||||
|
||||
self.assertEqual(plan.transactions.count(), 3)
|
||||
updated_transaction = plan.transactions.order_by('date').first()
|
||||
updated_transaction = plan.transactions.order_by("date").first()
|
||||
self.assertEqual(updated_transaction.description, "Updated Plan Description")
|
||||
# Amount should not change if already paid, but these are created as unpaid
|
||||
self.assertEqual(updated_transaction.amount, Decimal("60.00"))
|
||||
|
||||
|
||||
def test_installment_plan_delete_with_transactions(self):
|
||||
plan = InstallmentPlan.objects.create(
|
||||
account=self.account, owner=self.user, type=Transaction.Type.EXPENSE,
|
||||
description="Plan to Delete", number_of_installments=2, start_date=timezone.now().date(),
|
||||
installment_amount=Decimal("25.00"), recurrence=InstallmentPlan.Recurrence.MONTHLY,
|
||||
account=self.account,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
description="Plan to Delete",
|
||||
number_of_installments=2,
|
||||
start_date=timezone.now().date(),
|
||||
installment_amount=Decimal("25.00"),
|
||||
recurrence=InstallmentPlan.Recurrence.MONTHLY,
|
||||
)
|
||||
plan.create_transactions()
|
||||
plan_id = plan.id
|
||||
self.assertTrue(Transaction.objects.filter(installment_plan_id=plan_id).exists())
|
||||
self.assertTrue(
|
||||
Transaction.objects.filter(installment_plan_id=plan_id).exists()
|
||||
)
|
||||
|
||||
plan.delete() # This should also delete related transactions as per model's delete
|
||||
plan.delete() # This should also delete related transactions as per model's delete
|
||||
self.assertFalse(InstallmentPlan.all_objects.filter(id=plan_id).exists())
|
||||
self.assertFalse(Transaction.all_objects.filter(installment_plan_id=plan_id).exists())
|
||||
self.assertFalse(
|
||||
Transaction.all_objects.filter(installment_plan_id=plan_id).exists()
|
||||
)
|
||||
|
||||
|
||||
class RecurringTransactionTests(BaseTransactionAppTest): # Inherit
|
||||
class RecurringTransactionTests(BaseTransactionAppTest): # Inherit
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.category = TransactionCategory.objects.create(name="Recurring Category", owner=self.user)
|
||||
self.category = TransactionCategory.objects.create(
|
||||
name="Recurring Category", owner=self.user
|
||||
)
|
||||
|
||||
def test_recurring_transaction_creation_and_upcoming_generation(self):
|
||||
"""Test basic recurring transaction creation and initial upcoming transaction generation."""
|
||||
start_date = timezone.now().date()
|
||||
recurring = RecurringTransaction.objects.create(
|
||||
account=self.account,
|
||||
owner=self.user,
|
||||
type=Transaction.Type.INCOME,
|
||||
amount=Decimal("200.00"),
|
||||
description="Monthly Salary",
|
||||
@@ -481,20 +601,26 @@ class RecurringTransactionTests(BaseTransactionAppTest): # Inherit
|
||||
recurrence_interval=1,
|
||||
category=self.category,
|
||||
)
|
||||
recurring.create_upcoming_transactions() # Manually call
|
||||
recurring.create_upcoming_transactions() # Manually call
|
||||
|
||||
# It should create a few transactions (e.g., for next 5 occurrences or up to end_date)
|
||||
self.assertTrue(recurring.transactions.count() > 0)
|
||||
first_upcoming = recurring.transactions.order_by('date').first()
|
||||
first_upcoming = recurring.transactions.order_by("date").first()
|
||||
self.assertEqual(first_upcoming.amount, Decimal("200.00"))
|
||||
self.assertEqual(first_upcoming.date, start_date) # First one should be on start_date
|
||||
self.assertEqual(
|
||||
first_upcoming.date, start_date
|
||||
) # First one should be on start_date
|
||||
self.assertFalse(first_upcoming.is_paid)
|
||||
|
||||
def test_recurring_transaction_update_unpaid(self):
|
||||
recurring = RecurringTransaction.objects.create(
|
||||
account=self.account, owner=self.user, type=Transaction.Type.EXPENSE,
|
||||
amount=Decimal("30.00"), description="Subscription", start_date=timezone.now().date(),
|
||||
recurrence_type=RecurringTransaction.RecurrenceType.MONTH, recurrence_interval=1
|
||||
account=self.account,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
amount=Decimal("30.00"),
|
||||
description="Subscription",
|
||||
start_date=timezone.now().date(),
|
||||
recurrence_type=RecurringTransaction.RecurrenceType.MONTH,
|
||||
recurrence_interval=1,
|
||||
)
|
||||
recurring.create_upcoming_transactions()
|
||||
unpaid_transaction = recurring.transactions.filter(is_paid=False).first()
|
||||
@@ -503,7 +629,7 @@ class RecurringTransactionTests(BaseTransactionAppTest): # Inherit
|
||||
recurring.amount = Decimal("35.00")
|
||||
recurring.description = "Updated Subscription"
|
||||
recurring.save()
|
||||
recurring.update_unpaid_transactions() # Manually call
|
||||
recurring.update_unpaid_transactions() # Manually call
|
||||
|
||||
unpaid_transaction.refresh_from_db()
|
||||
self.assertEqual(unpaid_transaction.amount, Decimal("35.00"))
|
||||
@@ -511,13 +637,21 @@ class RecurringTransactionTests(BaseTransactionAppTest): # Inherit
|
||||
|
||||
def test_recurring_transaction_delete_unpaid(self):
|
||||
recurring = RecurringTransaction.objects.create(
|
||||
account=self.account, owner=self.user, type=Transaction.Type.EXPENSE,
|
||||
amount=Decimal("40.00"), description="Service Fee", start_date=timezone.now().date() + timedelta(days=5), # future start
|
||||
recurrence_type=RecurringTransaction.RecurrenceType.MONTH, recurrence_interval=1
|
||||
account=self.account,
|
||||
type=Transaction.Type.EXPENSE,
|
||||
amount=Decimal("40.00"),
|
||||
description="Service Fee",
|
||||
start_date=timezone.now().date() + timedelta(days=5), # future start
|
||||
recurrence_type=RecurringTransaction.RecurrenceType.MONTH,
|
||||
recurrence_interval=1,
|
||||
)
|
||||
recurring.create_upcoming_transactions()
|
||||
self.assertTrue(recurring.transactions.filter(is_paid=False).exists())
|
||||
|
||||
recurring.delete_unpaid_transactions() # Manually call
|
||||
recurring.delete_unpaid_transactions() # Manually call
|
||||
# This method in the model deletes transactions with date > today
|
||||
self.assertFalse(recurring.transactions.filter(is_paid=False, date__gt=timezone.now().date()).exists())
|
||||
self.assertFalse(
|
||||
recurring.transactions.filter(
|
||||
is_paid=False, date__gt=timezone.now().date()
|
||||
).exists()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user