Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added representatives and the ability to import clients #155

Merged
merged 11 commits into from
Dec 15, 2023
25 changes: 24 additions & 1 deletion backend/api/base/modal.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.http import HttpRequest, HttpResponseBadRequest
from django.shortcuts import render

from backend.models import UserSettings
from backend.models import UserSettings, Invoice


# Still working on
Expand All @@ -19,6 +19,29 @@ def open_modal(request: HttpRequest, modal_name, context_type=None, context_valu
] = request.user.user_profile.profile_picture_url
except UserSettings.DoesNotExist:
pass
elif context_type == "edit_invoice_to":
invoice = context_value
try:
invoice = Invoice.objects.get(user=request.user, id=invoice)
except:
return render(request, template_name, context)

if invoice.client_to:
context["to_name"] = invoice.client_to.name
context["to_company"] = invoice.client_to.company
context["to_address"] = invoice.client_to.address
context["existing_client_id"] = invoice.client_to.id
# context["to_city"] = invoice.client_to.city
# context["to_county"] = invoice.client_to.county
# context["to_country"] = invoice.client_to.country
else:
context["to_name"] = invoice.client_name
context["to_company"] = invoice.client_company
context["to_address"] = invoice.client_address
# context["to_city"] = invoice.client_city
# context["to_county"] = invoice.client_county
# context["to_country"] = invoice.client_country

return render(request, template_name, context)
except:
return HttpResponseBadRequest("Something went wrong")
17 changes: 17 additions & 0 deletions backend/api/clients/fetch.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import time

from django.db.models import Q
from django.http import HttpRequest
from django.shortcuts import render, redirect
Expand All @@ -23,3 +25,18 @@ def fetch_all_clients(request: HttpRequest):
)

return render(request, "pages/clients/dashboard/_table.html", {"clients": clients})


@require_http_methods(["GET"])
def fetch_clients_dropdown(request: HttpRequest):
if not request.htmx:
return redirect("clients dashboard")

selected_client = request.GET.get("existing_client_id") or None
clients = Client.objects.filter(user=request.user, active=True)

return render(
request,
"pages/invoices/create/_view_clients_dropdown.html",
{"clients": clients, "selected_client": selected_client},
)
5 changes: 5 additions & 0 deletions backend/api/clients/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
fetch.fetch_all_clients,
name="fetch",
),
path(
"fetch/dropdown/",
fetch.fetch_clients_dropdown,
name="fetch dropdown",
),
]

app_name = "clients"
15 changes: 14 additions & 1 deletion backend/api/invoices/create/set_destination.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
from django.contrib import messages
from django.http import HttpRequest
from django.shortcuts import render
from django.views.decorators.http import require_http_methods

to_get = ["name", "address", "city", "country"]
from backend.models import Client

to_get = ["name", "address", "city", "country", "company", "is_representative"]


@require_http_methods(["POST"])
Expand All @@ -11,6 +14,16 @@ def set_destination_to(request: HttpRequest):

context.update({key: request.POST.get(key) for key in to_get})

use_existing = True if request.POST.get("use_existing") == "true" else False
selected_client = request.POST.get("selected_client") if use_existing else None

if selected_client:
try:
client = Client.objects.get(user=request.user, id=selected_client)
context["existing_client"] = client
except:
messages.error("Client not found")

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


Expand Down
1 change: 0 additions & 1 deletion backend/api/invoices/delete.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
@require_http_methods(["DELETE"])
def delete_invoice(request: HttpRequest):
delete_items = QueryDict(request.body)
print("del items", delete_items)

invoice = delete_items.get("invoice")

Expand Down
22 changes: 22 additions & 0 deletions backend/migrations/0003_client_company_client_is_representative.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.1.7 on 2023-12-12 14:37

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("backend", "0002_alter_receipt_date_uploaded"),
]

operations = [
migrations.AddField(
model_name="client",
name="company",
field=models.CharField(blank=True, max_length=100, null=True),
),
migrations.AddField(
model_name="client",
name="is_representative",
field=models.BooleanField(default=False),
),
]
17 changes: 17 additions & 0 deletions backend/migrations/0004_invoice_client_is_representative.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.1.7 on 2023-12-15 08:40

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("backend", "0003_client_company_client_is_representative"),
]

operations = [
migrations.AddField(
model_name="invoice",
name="client_is_representative",
field=models.BooleanField(default=False),
),
]
18 changes: 18 additions & 0 deletions backend/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@ class Client(models.Model):
name = models.CharField(max_length=64)
phone_number = models.CharField(max_length=100, blank=True, null=True)
email = models.EmailField(blank=True, null=True)
company = models.CharField(max_length=100, blank=True, null=True)
is_representative = models.BooleanField(default=False)

address = models.CharField(max_length=100, blank=True, null=True)
city = models.CharField(max_length=100, blank=True, null=True)
Expand Down Expand Up @@ -181,6 +183,7 @@ class Invoice(models.Model):
client_city = models.CharField(max_length=100, blank=True, null=True)
client_county = models.CharField(max_length=100, blank=True, null=True)
client_country = models.CharField(max_length=100, blank=True, null=True)
client_is_representative = models.BooleanField(default=False)

self_name = models.CharField(max_length=100, blank=True, null=True)
self_company = models.CharField(max_length=100, blank=True, null=True)
Expand Down Expand Up @@ -218,6 +221,21 @@ def dynamic_payment_status(self):
else:
return self.payment_status

@property
def get_to_details(self) -> tuple[str, dict[str, str]]:
"""
Returns the client details for the invoice
"client" and Client object if client_to
"manual" and dict of details if client_name
"""
if self.client_to:
return "client", self.client_to
else:
return "manual", {
"name": self.client_name,
"company": self.client_company,
}

def __str__(self):
invoice_id = self.invoice_id or self.id
if self.client_name:
Expand Down
45 changes: 32 additions & 13 deletions backend/views/core/clients/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,47 @@ def create_client(request: HttpRequest):
if request.method == "GET":
return render(request, "pages/clients/create/create.html")

client_name = request.POST.get("client_name")
client_email = request.POST.get("client_email")
client_address = request.POST.get("client_address")
client_phone = request.POST.get("client_phone")
client_details = {
"name": request.POST.get("client_name"),
"email": request.POST.get("client_email"),
"address": request.POST.get("client_address"),
"phone_number": request.POST.get("client_phone"),
"company": request.POST.get("company_name"),
"is_representative": True
if request.POST.get("is_representative") == "on"
else False,
}

if not client_name:
messages.error(request, "Please provide at least a client name")
return redirect("clients create")
error = validate_client_create(client_details)

if len(client_name) < 3:
messages.error(request, "Client name must be at least 3 characters")
if error:
messages.error(request, error)
return redirect("clients create")

client = Client.objects.create(
user=request.user,
name=client_name,
email=client_email,
address=client_address,
phone_number=client_phone,
)

for model_field, new_value in client_details.items():
setattr(client, model_field, new_value)

client.save()

if client:
messages.success(request, f"Client created successfully (#{client.id})")
else:
messages.error(request, "Failed to create client - an unknown error occurred")
return redirect("clients dashboard")


def validate_client_create(client_details):
if not client_details.get("name"):
return "Please provide at least a client name"

if len(client_details.get("name")) < 3:
return "Client name must be at least 3 characters"

if client_details.get("is_representiative") and not client_details.get("company"):
return "Please provide a company name if you are creating a representative"

return None
71 changes: 47 additions & 24 deletions backend/views/core/invoices/create.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from django.contrib import messages
from django.http import HttpRequest
from django.shortcuts import render, redirect
from django.urls import reverse_lazy
Expand All @@ -7,8 +8,9 @@


def invoice_page_get(request: HttpRequest):
context = {}

context = {
"clients": Client.objects.filter(user=request.user),
}
return render(request, "pages/invoices/create/create.html", context)


Expand All @@ -23,36 +25,57 @@ def invoice_page_post(request: HttpRequest):
request.POST.getlist("hours[]"),
request.POST.getlist("price_per_hour[]"),
)
] # TODO: add products to this for logic (so that they can be loaded without manually making each time)
]

invoice = Invoice.objects.create(
user=request.user,
date_due=datetime.strptime(request.POST.get("date_due"), "%Y-%m-%d").date(),
date_issued=request.POST.get("date_issued"),
client_name=request.POST.get("to_name"),
client_company=request.POST.get("to_company"),
client_address=request.POST.get("to_address"),
client_city=request.POST.get("to_city"),
client_county=request.POST.get("to_county"),
client_country=request.POST.get("to_country"),
self_name=request.POST.get("from_name"),
self_company=request.POST.get("from_company"),
self_address=request.POST.get("from_address"),
self_city=request.POST.get("from_city"),
self_county=request.POST.get("from_county"),
self_country=request.POST.get("from_country"),
notes=request.POST.get("notes"),
invoice_number=request.POST.get("invoice_number"),
vat_number=request.POST.get("vat_number"),
# logo = request.POST.get("logo"),
reference=request.POST.get("reference"),
sort_code=request.POST.get("sort_code"),
account_number=request.POST.get("account_number"),
account_holder_name=request.POST.get("account_holder_name"),
)

is_existing_client = True if request.POST.get("selected_client") else False

if is_existing_client:
try:
client = Client.objects.get(
user=request.user, id=request.POST.get("selected_client")
)
except:
messages.error(request, "Client not found")
invoice.delete()
return render(request, "pages/invoices/create/create.html")
invoice.client_to = client

else:
invoice.client_name = request.POST.get("to_name")
invoice.client_company = request.POST.get("to_company")
invoice.client_address = request.POST.get("to_address")
invoice.client_city = request.POST.get("to_city")
invoice.client_county = request.POST.get("to_county")
invoice.client_country = request.POST.get("to_country")
if request.POST.get("is_representative") == "on":
invoice.client_is_representative = True
else:
invoice.client_is_representative = False

invoice.self_name = request.POST.get("from_name")
invoice.self_company = request.POST.get("from_company")
invoice.self_address = request.POST.get("from_address")
invoice.self_city = request.POST.get("from_city")
invoice.self_county = request.POST.get("from_county")
invoice.self_country = request.POST.get("from_country")

invoice.notes = request.POST.get("notes")
invoice.invoice_number = request.POST.get("invoice_number")
invoice.vat_number = request.POST.get("vat_number")
invoice.logo = request.FILES.get("logo")
invoice.reference = request.POST.get("reference")
invoice.sort_code = request.POST.get("sort_code")
invoice.account_number = request.POST.get("account_number")
invoice.account_holder_name = request.POST.get("account_holder_name")

invoice.payment_status = invoice.dynamic_payment_status
print(invoice.date_due)

invoice.save()
invoice.items.set(invoice_items)

Expand Down
Loading
Loading