Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
TreyWW authored Apr 20, 2024
2 parents 8aebd81 + fdec63e commit 89417b2
Show file tree
Hide file tree
Showing 83 changed files with 2,130 additions and 918 deletions.
14 changes: 14 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,22 @@ name: "CodeQL"
on:
push:
branches: [ "main" ]
paths:
- 'backend/**'
- 'components/**'
- 'infrastructure/**'
- 'settings/**'
- 'tests/**'
- 'manage.py'
pull_request:
branches: [ "main" ]
paths:
- 'backend/**'
- 'components/**'
- 'infrastructure/**'
- 'settings/**'
- 'tests/**'
- 'manage.py'

jobs:
analyze:
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/run_black_linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@ on:
push:
branches:
- main
paths:
- '**.py'
pull_request:
paths:
- '**.py'

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: psf/black@stable
- uses: actions/checkout@v4
- uses: psf/black@stable
4 changes: 4 additions & 0 deletions .github/workflows/run_djlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ on:
push:
branches:
- main
paths:
- 'frontend/templates/**.html'
pull_request:
paths:
- 'frontend/templates/**.html'

jobs:
lint:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

Expand Down
24 changes: 6 additions & 18 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -143,24 +143,6 @@ dmypy.json
# Node
node_modules

# Terraform
**/.terraform/*
*.tfstate
*.tfstate.*

*.tfvars
*.tfvars.json

!sample.tfvars.json

override.tf
override.tf.json
*_override.tf
*_override.tf.json

.terraformrc
terraform.rc

# Temporary (WRITERSIDE)
Writerside/**

Expand All @@ -171,3 +153,9 @@ frontend/static/js/bundle.js.map

# Pulumi
infrastructure/aws/pulumi/Pulumi.dev.yaml
infrastructure/aws/pulumi/Pulumi.production.yaml
infrastructure/aws/pulumi/Pulumi.staging.yaml

Pulumi.*.yaml
Pulumi.*.yaml.bak
.pulumi/
4 changes: 3 additions & 1 deletion infrastructure/aws/pulumi/Pulumi.yaml → Pulumi.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
name: MyFinances
name: myfinances
main: /infrastructure/aws/pulumi/
stackConfigDir: /infrastructure/aws/pulumi/
runtime:
name: python
options:
Expand Down
3 changes: 2 additions & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ If you have found a security vulnerability in MyFinances please __**DO NOT**__ s

## Supported Versions

We are currently still in development and have not reached a public V1 release. We only support the most recent update for feature upgrades, bug reports and security patches.

| Version | Supported |
| ------- | ------------------ |
| 0.x.x | :white_check_mark: |
| < 0.2.1 | :x: |
23 changes: 20 additions & 3 deletions backend/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
Receipt,
ReceiptDownloadToken,
EmailSendStatus,
InvoiceReminder,
)

# from django.contrib.auth.models imp/ort User
Expand All @@ -52,11 +53,9 @@
VerificationCodes,
APIKey,
InvoiceOnetimeSchedule,
QuotaOverrides,
QuotaUsage,
QuotaIncreaseRequest,
Receipt,
ReceiptDownloadToken,
InvoiceReminder,
]
)

Expand All @@ -65,11 +64,29 @@ class QuotaLimitAdmin(admin.ModelAdmin):
readonly_fields = ["name", "slug"]


class QuotaOverridesAdmin(admin.ModelAdmin):
def get_queryset(self, request):
return super().get_queryset(request).select_related("quota_limit", "user")


class QuotaUsageAdmin(admin.ModelAdmin):
def get_queryset(self, request):
return super().get_queryset(request).select_related("quota_limit", "user")


class QuotaIncreaseRequestAdmin(admin.ModelAdmin):
def get_queryset(self, request):
return super().get_queryset(request).select_related("quota_limit", "user")


class EmailSendStatusAdmin(admin.ModelAdmin):
readonly_fields = ["aws_message_id"]


admin.site.register(QuotaLimit, QuotaLimitAdmin)
admin.site.register(QuotaUsage, QuotaUsageAdmin)
admin.site.register(QuotaOverrides, QuotaOverridesAdmin)
admin.site.register(QuotaIncreaseRequest, QuotaIncreaseRequestAdmin)
admin.site.register(EmailSendStatus, EmailSendStatusAdmin)

# admin.site.unregister(User)
Expand Down
40 changes: 39 additions & 1 deletion backend/api/base/modal.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
from __future__ import annotations

from django.contrib import messages
from django.http import HttpRequest
from django.http import HttpResponseBadRequest
from django.shortcuts import render

from backend.models import Client
from backend.models import Client, Receipt
from backend.models import Invoice
from backend.models import QuotaLimit
from backend.models import Team
from backend.models import UserSettings
from backend.utils import quota_usage_check_under


# Still working on
Expand All @@ -29,6 +31,23 @@ def open_modal(request: HttpRequest, modal_name, context_type=None, context_valu
elif context_type == "leave_team":
if request.user.teams_joined.filter(id=context_value).exists():
context["team"] = Team.objects.filter(id=context_value).first()
elif context_type == "edit_receipt":
try:
receipt = Receipt.objects.get(pk=context_value)
except Receipt.DoesNotExist:
return render(request, template_name, context)
receipt_date = receipt.date.strftime("%Y-%m-%d") if receipt.date else ""
context = {
"modal_id": f"modal_{receipt.id}_receipts_upload",
"receipt_id": context_value,
"receipt_name": receipt.name,
"receipt_date": receipt_date,
"merchant_store_name": receipt.merchant_store,
"purchase_category": receipt.purchase_category,
"total_price": receipt.total_price,
"has_receipt_image": True if receipt.image else False,
"edit_flag": True,
}
elif context_type == "edit_invoice_to":
invoice = context_value
try:
Expand Down Expand Up @@ -81,6 +100,25 @@ def open_modal(request: HttpRequest, modal_name, context_type=None, context_valu
print(context["quota_usage"])
except QuotaLimit.DoesNotExist:
...
elif context_type == "invoice_reminder":
try:
invoice = (
Invoice.objects.only("id", "client_email", "client_to__email").select_related("client_to").get(id=context_value)
)
except Invoice.DoesNotExist:
return render(request, template_name, context)

if invoice.has_access(request.user):
context["invoice"] = invoice
else:
messages.error(request, "You don't have access to this invoice")
return render(request, "base/toasts.html")

above_quota_usage = quota_usage_check_under(request, "invoices-schedules", api=True, htmx=True)

if not isinstance(above_quota_usage, bool):
context["above_quota_usage"] = True

else:
context[context_type] = context_value

Expand Down
2 changes: 1 addition & 1 deletion backend/api/invoices/create/set_destination.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def set_destination_to(request: HttpRequest):
client = Client.objects.get(user=request.user, id=selected_client)
context["existing_client"] = client
except Client.DoesNotExist:
messages.error("Client not found")
messages.error(request, "Client not found")

return render(request, "pages/invoices/create/_to_destination.html", context)

Expand Down
5 changes: 2 additions & 3 deletions backend/api/invoices/delete.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
from django.contrib import messages
from django.http import HttpRequest, JsonResponse, QueryDict, HttpResponse
from django.http import HttpRequest, JsonResponse, QueryDict, HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import resolve
from django.urls import resolve, reverse
from django.urls.exceptions import Resolver404
from django.views.decorators.http import require_http_methods

from backend.models import Invoice, QuotaLimit


Expand Down
95 changes: 95 additions & 0 deletions backend/api/invoices/reminders/create.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
from django.contrib import messages
from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.utils import timezone

from backend.models import Invoice, InvoiceReminder, QuotaUsage
from backend.utils import quota_usage_check_under
from infrastructure.aws.schedules.create_reminder import CreateReminderInputData, create_reminder_schedule

from backend.types.htmx import HtmxHttpRequest


def get_datetime_from_reminder(reminder: InvoiceReminder) -> str:
if reminder.reminder_type == "on_overdue":
return reminder.invoice.date_due.strftime("%Y-%m-%dT%H:%M")

if reminder.reminder_type == "before_due":
days = 0 - reminder.days
else:
days = reminder.days

date = (timezone.now() + timezone.timedelta(days=days)).replace(hour=12, minute=0, second=0, microsecond=0).strftime("%Y-%m-%dT%H:%M")
return date


def create_reminder_view(request: HtmxHttpRequest) -> HttpResponse:
if not request.htmx:
return redirect("invoices:dashboard")

check_usage = quota_usage_check_under(request, "invoices-schedules", api=True, htmx=True)

if not isinstance(check_usage, bool):
return check_usage

# Extract POST data
invoice_id = request.POST.get("invoice_id") or request.POST.get("invoice")
reminder_type = request.POST.get("reminder_type")
days = request.POST.get("days", "none")

# Check if invoice exists
try:
invoice = Invoice.objects.get(id=invoice_id)
except Invoice.DoesNotExist:
messages.error(request, "Invoice not found")
return render(request, "base/toast.html", {"autohide": False})

# Check user permission
if not invoice.has_access(user=request.user):
messages.error(request, "You do not have permission to create schedules for this invoice")
return render(request, "base/toasts.html")

# Check reminder type
if reminder_type not in InvoiceReminder.ReminderTypes.values:
messages.error(request, "Invalid reminder type")
return render(request, "base/toasts.html")

# Ensure days is set for non-overdue reminders
if reminder_type == "on_overdue":
days = 0

# Convert days to integer
try:
days = int(days)
if days <= 0 and reminder_type != "on_overdue":
raise ValueError
except ValueError:
messages.error(request, "Invalid days value. Make sure it's an integer from 1-31")
return render(request, "base/toasts.html")

# Create reminder object
reminder = InvoiceReminder(invoice=invoice, reminder_type=reminder_type)
if reminder_type != "on_overdue":
reminder.days = days

# Prepare data for creating reminder schedule
data = CreateReminderInputData(
reminder=reminder,
invoice=invoice,
datetime=get_datetime_from_reminder(reminder),
email_type="client_to_email", # TODO: Change to user inputted
)

# Create reminder schedule
REMINDER = create_reminder_schedule(data)

# Handle result of creating reminder schedule
if REMINDER.success:
reminder.save()
QuotaUsage.create_str(request.user, "invoices-schedules", REMINDER.reminder.id)
messages.success(request, "Schedule created!")
return render(request, "pages/invoices/schedules/reminders/_table_row.html", {"reminder": REMINDER.reminder})
else:
messages.error(request, REMINDER.message)
return render(request, "base/toasts.html")
Loading

0 comments on commit 89417b2

Please sign in to comment.