From 6b85437c6d103a78c7f28414979796f52f2f3570 Mon Sep 17 00:00:00 2001 From: Strelix Date: Tue, 12 Dec 2023 11:12:42 +0000 Subject: [PATCH 01/10] Added invoice icons --- backend/views/core/invoices/create.py | 2 +- frontend/static/src/output.css | 30 ++++- .../pages/invoices/create/create.html | 120 ++++++++++-------- .../pages/invoices/view/invoice.html | 22 +++- 4 files changed, 110 insertions(+), 64 deletions(-) diff --git a/backend/views/core/invoices/create.py b/backend/views/core/invoices/create.py index d78514e7..6eabfda1 100644 --- a/backend/views/core/invoices/create.py +++ b/backend/views/core/invoices/create.py @@ -43,7 +43,7 @@ def invoice_page_post(request: HttpRequest): notes=request.POST.get("notes"), invoice_number=request.POST.get("invoice_number"), vat_number=request.POST.get("vat_number"), - # logo = request.POST.get("logo"), + logo=request.FILES.get("logo"), reference=request.POST.get("reference"), sort_code=request.POST.get("sort_code"), account_number=request.POST.get("account_number"), diff --git a/frontend/static/src/output.css b/frontend/static/src/output.css index 48ab3999..4125fef9 100644 --- a/frontend/static/src/output.css +++ b/frontend/static/src/output.css @@ -4697,6 +4697,11 @@ html { background-color: rgb(0 0 0 / var(--tw-bg-opacity)); } +.bg-error { + --tw-bg-opacity: 1; + background-color: var(--fallback-er,oklch(var(--er)/var(--tw-bg-opacity))); +} + .bg-gray-200 { --tw-bg-opacity: 1; background-color: rgb(229 231 235 / var(--tw-bg-opacity)); @@ -4727,11 +4732,6 @@ html { background-color: var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity))); } -.bg-success { - --tw-bg-opacity: 1; - background-color: var(--fallback-su,oklch(var(--su)/var(--tw-bg-opacity))); -} - .bg-white { --tw-bg-opacity: 1; background-color: rgb(255 255 255 / var(--tw-bg-opacity)); @@ -4742,6 +4742,16 @@ html { background-color: rgb(133 77 14 / var(--tw-bg-opacity)); } +.bg-success { + --tw-bg-opacity: 1; + background-color: var(--fallback-su,oklch(var(--su)/var(--tw-bg-opacity))); +} + +.bg-gray-500 { + --tw-bg-opacity: 1; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)); +} + .bg-opacity-50 { --tw-bg-opacity: 0.5; } @@ -5114,6 +5124,16 @@ html { color: rgb(250 204 21 / var(--tw-text-opacity)); } +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} + +.text-gray-200 { + --tw-text-opacity: 1; + color: rgb(229 231 235 / var(--tw-text-opacity)); +} + .underline { text-decoration-line: underline; } diff --git a/frontend/templates/pages/invoices/create/create.html b/frontend/templates/pages/invoices/create/create.html index 672f6a54..609b8d11 100644 --- a/frontend/templates/pages/invoices/create/create.html +++ b/frontend/templates/pages/invoices/create/create.html @@ -3,7 +3,7 @@ {% load markdownify %} {% block content %} {% include 'components/modal.html' with modals=modal_data %} -
+ {% csrf_token %}
STEP 1 - DESTINATIONS
@@ -171,56 +171,74 @@
-{#
STEP 5 - NOTES [OPTIONAL]
#} -{#
#} -{#
#} -{#
#} -{#
#} -{# #} -{#
#} -{#
#} -{#
#} -{#
#} -{##} -{# #} -{# #} + +
STEP 5 - CUSTOM DESIGNS [OPTIONAL]
+
+
+
+
+
+ + +
+
+
+
+
+ + {#
STEP 5 - NOTES [OPTIONAL]
#} + {#
#} + {#
#} + {#
#} + {#
#} + {# #} + {#
#} + {#
#} + {#
#} + {#
#} + {##} + {# #} + {# #}

- - - - {% with ps=invoice.payment_status %} + + + + {% if ps == "paid" %} This invoice has been paid {% elif ps == "pending" %} @@ -32,9 +39,9 @@ {% elif ps == "overdue" %} This invoice is overdue {% endif %} - {% endwith %} -

+

+ {% endwith %}
@@ -59,7 +66,8 @@
- company-logo

From cce975459ffe166b6ae05ceae93180554223b991 Mon Sep 17 00:00:00 2001 From: Strelix Date: Wed, 13 Dec 2023 08:09:25 +0000 Subject: [PATCH 02/10] Started adding representatives --- ...client_company_client_is_representative.py | 22 +++ backend/models.py | 5 +- backend/views/core/clients/create.py | 28 +++- frontend/static/src/output.css | 157 +++++++++++++++--- .../modals/invoices_to_destination.html | 15 ++ .../pages/clients/create/create.html | 93 +++++++---- .../pages/invoices/view/invoice.html | 34 +++- 7 files changed, 290 insertions(+), 64 deletions(-) create mode 100644 backend/migrations/0003_client_company_client_is_representative.py diff --git a/backend/migrations/0003_client_company_client_is_representative.py b/backend/migrations/0003_client_company_client_is_representative.py new file mode 100644 index 00000000..8e6bc3a6 --- /dev/null +++ b/backend/migrations/0003_client_company_client_is_representative.py @@ -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), + ), + ] diff --git a/backend/models.py b/backend/models.py index bb17767a..bf723c13 100644 --- a/backend/models.py +++ b/backend/models.py @@ -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) @@ -206,9 +208,6 @@ class Invoice(models.Model): date_created = models.DateTimeField(auto_now_add=True) date_due = models.DateField() date_issued = models.DateField(blank=True, null=True) - payment_status = models.CharField( - max_length=10, choices=STATUS_CHOICES, default="pending" - ) def __str__(self): invoice_id = self.invoice_id or self.id diff --git a/backend/views/core/clients/create.py b/backend/views/core/clients/create.py index b53c1374..fa8686d8 100644 --- a/backend/views/core/clients/create.py +++ b/backend/views/core/clients/create.py @@ -13,13 +13,13 @@ def create_client(request: HttpRequest): client_email = request.POST.get("client_email") client_address = request.POST.get("client_address") client_phone = request.POST.get("client_phone") + client_company = request.POST.get("company_name") + is_representiative = request.POST.get("is_representative") - if not client_name: - messages.error(request, "Please provide at least a client name") - return redirect("clients create") + error = validate_client_create(client_name, client_company, is_representiative) - 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( @@ -29,8 +29,26 @@ def create_client(request: HttpRequest): address=client_address, phone_number=client_phone, ) + + if is_representiative: + client.is_representative = True + client.company = client_company + 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_name, client_company, is_representiative): + if not client_name: + return "Please provide at least a client name" + + if len(client_name) < 3: + return "Client name must be at least 3 characters" + + if is_representiative and not client_company: + return "Please provide a company name if you are creating a representative" + + return None diff --git a/frontend/static/src/output.css b/frontend/static/src/output.css index 4125fef9..8044a9af 100644 --- a/frontend/static/src/output.css +++ b/frontend/static/src/output.css @@ -2003,6 +2003,30 @@ html { background-color: var(--fallback-b1,oklch(var(--b1)/var(--tw-bg-opacity))); } +.toggle { + flex-shrink: 0; + --tglbg: var(--fallback-b1,oklch(var(--b1)/1)); + --handleoffset: 1.5rem; + --handleoffsetcalculator: calc(var(--handleoffset) * -1); + --togglehandleborder: 0 0; + height: 1.5rem; + width: 3rem; + cursor: pointer; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border-radius: var(--rounded-badge, 1.9rem); + border-width: 1px; + border-color: currentColor; + background-color: currentColor; + color: var(--fallback-bc,oklch(var(--bc)/0.5)); + transition: background, + box-shadow var(--animation-input, 0.2s) ease-out; + box-shadow: var(--handleoffsetcalculator) 0 0 2px var(--tglbg) inset, + 0 0 0 2px var(--tglbg) inset, + var(--togglehandleborder); +} + .alert-info { border-color: var(--fallback-in,oklch(var(--in)/0.2)); --tw-text-opacity: 1; @@ -2736,6 +2760,18 @@ html { outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); } +.input-primary { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); +} + +.input-primary:focus, + .input-primary:focus-within { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + .input-disabled, .input:disabled, .input[disabled] { @@ -2769,6 +2805,10 @@ html { margin-inline-start: -1px; } +.join-item:focus { + isolation: isolate; +} + .link-primary { --tw-text-opacity: 1; color: var(--fallback-p,oklch(var(--p)/var(--tw-text-opacity))); @@ -3119,6 +3159,17 @@ html { outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); } +.select-primary { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); +} + +.select-primary:focus { + --tw-border-opacity: 1; + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + .select-disabled, .select:disabled, .select[disabled] { @@ -3321,6 +3372,73 @@ html { } } +[dir="rtl"] .toggle { + --handleoffsetcalculator: calc(var(--handleoffset) * 1); +} + +.toggle:focus-visible { + outline-style: solid; + outline-width: 2px; + outline-offset: 2px; + outline-color: var(--fallback-bc,oklch(var(--bc)/0.2)); +} + +.toggle:hover { + background-color: currentColor; +} + +.toggle:checked, + .toggle[checked="true"], + .toggle[aria-checked="true"] { + background-image: none; + --handleoffsetcalculator: var(--handleoffset); + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); +} + +[dir="rtl"] .toggle:checked, [dir="rtl"] .toggle[checked="true"], [dir="rtl"] .toggle[aria-checked="true"] { + --handleoffsetcalculator: calc(var(--handleoffset) * -1); +} + +.toggle:indeterminate { + --tw-text-opacity: 1; + color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); + box-shadow: calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset, + calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset, + 0 0 0 2px var(--tglbg) inset; +} + +[dir="rtl"] .toggle:indeterminate { + box-shadow: calc(var(--handleoffset) / 2) 0 0 2px var(--tglbg) inset, + calc(var(--handleoffset) / -2) 0 0 2px var(--tglbg) inset, + 0 0 0 2px var(--tglbg) inset; +} + +.toggle-primary:focus-visible { + outline-color: var(--fallback-p,oklch(var(--p)/1)); +} + +.toggle-primary:checked, + .toggle-primary[checked="true"], + .toggle-primary[aria-checked="true"] { + border-color: var(--fallback-p,oklch(var(--p)/var(--tw-border-opacity))); + --tw-border-opacity: 0.1; + --tw-bg-opacity: 1; + background-color: var(--fallback-p,oklch(var(--p)/var(--tw-bg-opacity))); + --tw-text-opacity: 1; + color: var(--fallback-pc,oklch(var(--pc)/var(--tw-text-opacity))); +} + +.toggle:disabled { + cursor: not-allowed; + --tw-border-opacity: 1; + border-color: var(--fallback-bc,oklch(var(--bc)/var(--tw-border-opacity))); + background-color: transparent; + opacity: 0.3; + --togglehandleborder: 0 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset, + var(--handleoffsetcalculator) 0 0 3px var(--fallback-bc,oklch(var(--bc)/1)) inset; +} + .glass, .glass.btn-active { border: none; @@ -4712,6 +4830,11 @@ html { background-color: rgb(249 250 251 / var(--tw-bg-opacity)); } +.bg-gray-500 { + --tw-bg-opacity: 1; + background-color: rgb(107 114 128 / var(--tw-bg-opacity)); +} + .bg-purple-600 { --tw-bg-opacity: 1; background-color: rgb(147 51 234 / var(--tw-bg-opacity)); @@ -4732,6 +4855,11 @@ html { background-color: var(--fallback-s,oklch(var(--s)/var(--tw-bg-opacity))); } +.bg-success { + --tw-bg-opacity: 1; + background-color: var(--fallback-su,oklch(var(--su)/var(--tw-bg-opacity))); +} + .bg-white { --tw-bg-opacity: 1; background-color: rgb(255 255 255 / var(--tw-bg-opacity)); @@ -4742,16 +4870,6 @@ html { background-color: rgb(133 77 14 / var(--tw-bg-opacity)); } -.bg-success { - --tw-bg-opacity: 1; - background-color: var(--fallback-su,oklch(var(--su)/var(--tw-bg-opacity))); -} - -.bg-gray-500 { - --tw-bg-opacity: 1; - background-color: rgb(107 114 128 / var(--tw-bg-opacity)); -} - .bg-opacity-50 { --tw-bg-opacity: 0.5; } @@ -5039,6 +5157,11 @@ html { color: var(--fallback-bc,oklch(var(--bc)/var(--tw-text-opacity))); } +.text-black { + --tw-text-opacity: 1; + color: rgb(0 0 0 / var(--tw-text-opacity)); +} + .text-error { --tw-text-opacity: 1; color: var(--fallback-er,oklch(var(--er)/var(--tw-text-opacity))); @@ -5124,16 +5247,6 @@ html { color: rgb(250 204 21 / var(--tw-text-opacity)); } -.text-black { - --tw-text-opacity: 1; - color: rgb(0 0 0 / var(--tw-text-opacity)); -} - -.text-gray-200 { - --tw-text-opacity: 1; - color: rgb(229 231 235 / var(--tw-text-opacity)); -} - .underline { text-decoration-line: underline; } @@ -6439,6 +6552,10 @@ button.btn.loading-htmx.htmx-request { width: 50%; } + .md\:w-1\/3 { + width: 33.333333%; + } + .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } diff --git a/frontend/templates/modals/invoices_to_destination.html b/frontend/templates/modals/invoices_to_destination.html index 81c3f3b8..0aa90bf3 100644 --- a/frontend/templates/modals/invoices_to_destination.html +++ b/frontend/templates/modals/invoices_to_destination.html @@ -1,9 +1,24 @@ {% component_block "modal" id="modal_invoices_to_destination" start_open="true" title="Update TO info" %} {% fill "content" %} +

+ + +
+
OR
{% csrf_token %} +
+ + +
{% csrf_token %}
REQUIRED DETAILS
+
+ +
{# Client Name Card #}
@@ -40,46 +46,77 @@
+ {# Client Company Card #} +
+
+
+ + + +
+
+
OPTIONAL DETAILS
- {# Client Address Card #} -
-
-
-
- - -