Skip to content

Commit 71900ca

Browse files
authored
feature: Invoice onetime schedule sends (#220)
Added onetime schedule sending of emails
1 parent 4d08664 commit 71900ca

File tree

83 files changed

+3204
-775
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+3204
-775
lines changed

.dockerignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ app/*/*/*/__pycache__/
1313
.env/
1414
.venv/
1515
venv/
16-
env/
16+
env/
17+
18+
# Node
19+
node_modules

.env.github_actions

+4-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,7 @@ URL=127.0.0.1
44
PROXY_IP=localhost
55
BRANCH=debug
66

7-
DATABASE_TYPE=sqlite3
7+
DATABASE_TYPE=sqlite3
8+
9+
SITE_URL=http://myfinances.example.com
10+
SITE_NAME=myfinances-ghactions

.env.sample

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ URL=127.0.0.1
44
PROXY_IP=localhost
55
BRANCH=debug
66

7+
SITE_URL=https://myfinances.example.com
8+
SITE_NAME=myfinances
9+
710
DATABASE_HOST=127.0.0.1
811
DATABASE_NAME=myfinances_development
912
DATABASE_USER=root
@@ -21,7 +24,4 @@ GITHUB_SECRET=
2124

2225
GOOGLE_CLIENT_ID=
2326
GOOGLE_CLIENT_SECRET=
24-
GOOGLE_CLIENT_URI=
25-
26-
OCR_API_TEST=True
27-
OCR_API_KEY=
27+
GOOGLE_CLIENT_URI=

.gitignore

+26-1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ celerybeat.pid
104104

105105
# Environments
106106
.env
107+
*.env
108+
109+
!.env.sample
110+
107111
.venv
108112
env/
109113
venv/
@@ -136,4 +140,25 @@ dmypy.json
136140
.vscode
137141

138142
# Node
139-
node_modules
143+
node_modules
144+
145+
# Terraform
146+
**/.terraform/*
147+
*.tfstate
148+
*.tfstate.*
149+
150+
*.tfvars
151+
*.tfvars.json
152+
153+
!sample.tfvars.json
154+
155+
override.tf
156+
override.tf.json
157+
*_override.tf
158+
*_override.tf.json
159+
160+
.terraformrc
161+
terraform.rc
162+
163+
# Temporary (WRITERSIDE)
164+
Writerside/**

Dockerfile

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
FROM python:3.12-alpine
2+
3+
RUN pip install poetry==1.7.1
4+
5+
ENV POETRY_NO_INTERACTION=1 \
6+
POETRY_VIRTUALENVS_IN_PROJECT=1 \
7+
POETRY_VIRTUALENVS_CREATE=false \
8+
POETRY_CACHE_DIR=/tmp/poetry_cache
9+
10+
WORKDIR /MyFinances
11+
12+
COPY . .
13+
14+
#RUN #apk --no-cache --update add \
15+
# mariadb-connector-c-dev \
16+
# py-pip \
17+
# musl-dev \
18+
# gcc \
19+
# mariadb-dev \
20+
# libffi-dev
21+
22+
23+
# Install build dependencies
24+
RUN apk add --virtual .build-deps py-pip musl-dev gcc
25+
26+
# Install MySQL dependencies and packages if DATABASE_TYPE is mysql
27+
RUN if [ "${DATABASE_TYPE}" = "mysql" ]; then \
28+
apk add --no-cache mariadb-dev && \
29+
poetry install --only mysql; \
30+
fi
31+
32+
# Install PostgreSQL dependencies and packages if TESTING is not true or DATABASE_TYPE is postgres
33+
RUN if [ "${TESTING}" != "true" ] || [ "${DATABASE_TYPE}" = "postgres" ]; then \
34+
apk add --no-cache postgresql-dev && \
35+
poetry install --only postgres; \
36+
fi
37+
38+
# Clean up build dependencies
39+
RUN apk del .build-deps
40+
41+
RUN poetry install --without dev,mysql,postgres --no-root && rm -rf $POETRY_CACHE_DIR
42+
43+
RUN chmod +x infrastructure/backend/scripts/*
44+
RUN chmod +x infrastructure/backend/scripts/tests/*
45+
ENTRYPOINT ["sh", "infrastructure/backend/scripts/entrypoint.sh"]
46+
47+
EXPOSE 10012
48+
EXPOSE 9012

backend/admin.py

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
InvoiceProduct,
2020
FeatureFlags,
2121
VerificationCodes,
22+
APIKey,
23+
InvoiceOnetimeSchedule,
2224
)
2325

2426
# from django.contrib.auth.models imp/ort User
@@ -41,6 +43,8 @@
4143
InvoiceProduct,
4244
FeatureFlags,
4345
VerificationCodes,
46+
APIKey,
47+
InvoiceOnetimeSchedule,
4448
]
4549
)
4650

backend/api/admin/api_keys.py

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from django.contrib import messages
2+
from django.http import HttpRequest, HttpResponseBadRequest, HttpResponse
3+
from django.shortcuts import render, redirect
4+
5+
from backend.models import APIKey
6+
7+
8+
# Still working on
9+
10+
11+
def generate_api_key(request: HttpRequest) -> HttpResponse():
12+
if not request.htmx:
13+
return redirect("user settings")
14+
if not request.user.is_staff or not request.user.is_superuser:
15+
messages.error(request, "You don't have permission to view this page.")
16+
return redirect("dashboard")
17+
18+
service = request.POST.get("service")
19+
if not service:
20+
return HttpResponseBadRequest("Missing service")
21+
22+
if service == "aws_api_destination":
23+
service = APIKey.ServiceTypes.AWS_API_DESTINATION
24+
else:
25+
return HttpResponseBadRequest("Invalid service")
26+
27+
token = APIKey.objects.create(service=service)
28+
key = f"{token.id}:{token.key}"
29+
30+
token.hash()
31+
32+
return render(request, "pages/admin/api_keys/generate_response.html", {"key": key})

backend/api/admin/urls.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from django.urls import path
2+
3+
from . import api_keys
4+
5+
urlpatterns = [
6+
path(
7+
"api_keys/generate/",
8+
api_keys.generate_api_key,
9+
name="api-keys generate",
10+
),
11+
]
12+
13+
app_name = "admin"
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from django.db import connection, OperationalError
2+
from django.http import HttpRequest, HttpResponse
3+
from login_required import login_not_required
4+
5+
6+
@login_not_required
7+
def ping(request: HttpRequest) -> HttpResponse:
8+
return HttpResponse("pong")
9+
10+
11+
@login_not_required
12+
def healthcheck(request: HttpRequest) -> HttpResponse:
13+
try:
14+
status = connection.ensure_connection()
15+
except OperationalError:
16+
status = "error"
17+
18+
if not status: # good
19+
return HttpResponse(status=200, content="All operations are up and running!")
20+
return HttpResponse(status=503, content="Service Unavailable")

backend/api/healthcheck/urls.py

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from django.urls import path
2+
3+
from . import healthcheck
4+
5+
urlpatterns = [
6+
path(
7+
"ping/",
8+
healthcheck.ping,
9+
name="ping",
10+
),
11+
path(
12+
"healthcheck/",
13+
healthcheck.healthcheck,
14+
name="healthcheck",
15+
),
16+
]
17+
18+
app_name = "healthcheck"

backend/api/invoices/edit.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def change_status(request: HttpRequest, invoice_id: int, status: str) -> HttpRes
6767
status = status.lower() if status else ""
6868

6969
if not request.htmx:
70-
return redirect("invoices dashboard")
70+
return redirect("invoices:dashboard")
7171

7272
try:
7373
invoice = Invoice.objects.get(id=invoice_id)

backend/api/invoices/fetch.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
def fetch_all_invoices(request: HttpRequest):
2323
# Redirect if not an HTMX request
2424
if not request.htmx:
25-
return redirect("invoices dashboard")
25+
return redirect("invoices:dashboard")
2626

2727
context = {}
2828

0 commit comments

Comments
 (0)