From d2faa872ace5ce449a7bd5e2a8a858c05106470c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Krop=C3=A1=C4=8Dek?= <kropikuba@gmail.com> Date: Thu, 21 Dec 2023 00:39:13 +0100 Subject: [PATCH] Added ares to automatically add subjects and refactored named views --- account/templates/account/me.html | 16 ----- {account => accounts}/__init__.py | 0 {account => accounts}/admin.py | 0 {account => accounts}/apps.py | 2 +- {account => accounts}/forms.py | 4 +- .../migrations/0001_initial.py | 0 ...er_email_alter_user_first_name_and_more.py | 2 +- {account => accounts}/migrations/__init__.py | 0 {account => accounts}/models.py | 0 .../templates/account/login.html | 0 accounts/templates/account/me.html | 14 +++++ .../templates/account/register.html | 0 {account => accounts}/tests.py | 0 {account => accounts}/urls.py | 5 +- {account => accounts}/views.py | 0 facturio/settings.py | 6 +- facturio/urls.py | 2 +- subjects/admin.py | 6 +- subjects/forms.py | 46 +++++++++++++++ subjects/migrations/0001_initial.py | 18 +++++- ...t_city_alter_subject_city_part_and_more.py | 53 +++++++++++++++++ subjects/models.py | 39 +++++++++++- subjects/templates/subjects/ares.html | 23 -------- subjects/templates/subjects/create.html | 9 +++ subjects/templates/subjects/index.html | 33 +++++++++++ subjects/urls.py | 5 +- subjects/views.py | 59 +++++++++++-------- templates/facturio/base.html | 20 +++---- 28 files changed, 274 insertions(+), 88 deletions(-) delete mode 100644 account/templates/account/me.html rename {account => accounts}/__init__.py (100%) rename {account => accounts}/admin.py (100%) rename {account => accounts}/apps.py (85%) rename {account => accounts}/forms.py (88%) rename {account => accounts}/migrations/0001_initial.py (100%) rename {account => accounts}/migrations/0002_alter_user_email_alter_user_first_name_and_more.py (95%) rename {account => accounts}/migrations/__init__.py (100%) rename {account => accounts}/models.py (100%) rename {account => accounts}/templates/account/login.html (100%) create mode 100644 accounts/templates/account/me.html rename {account => accounts}/templates/account/register.html (100%) rename {account => accounts}/tests.py (100%) rename {account => accounts}/urls.py (79%) rename {account => accounts}/views.py (100%) create mode 100644 subjects/forms.py create mode 100644 subjects/migrations/0002_alter_subject_city_alter_subject_city_part_and_more.py delete mode 100644 subjects/templates/subjects/ares.html create mode 100644 subjects/templates/subjects/create.html create mode 100644 subjects/templates/subjects/index.html diff --git a/account/templates/account/me.html b/account/templates/account/me.html deleted file mode 100644 index 5887c40..0000000 --- a/account/templates/account/me.html +++ /dev/null @@ -1,16 +0,0 @@ -{% extends "facturio/base.html" %} - -{% block title %}About Me{% endblock %} - -{% block content %} - <div class="container mt-4"> - <h2 class="mb-4">About Me</h2> - <div class="card"> - <h5 class="card-header">Welcome, {{ request.user.first_name }} {{ request.user.last_name }}!</h5> - <ul class="list-group list-group-flush"> - <li class="list-group-item">Username: {{ request.user.username }}</li> - <li class="list-group-item">Email: {{ request.user.email }}</li> - </ul> - </div> - </div> -{% endblock %} diff --git a/account/__init__.py b/accounts/__init__.py similarity index 100% rename from account/__init__.py rename to accounts/__init__.py diff --git a/account/admin.py b/accounts/admin.py similarity index 100% rename from account/admin.py rename to accounts/admin.py diff --git a/account/apps.py b/accounts/apps.py similarity index 85% rename from account/apps.py rename to accounts/apps.py index 2c684a9..ec4e357 100644 --- a/account/apps.py +++ b/accounts/apps.py @@ -3,4 +3,4 @@ from django.apps import AppConfig class AccountConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" - name = "account" + name = "accounts" diff --git a/account/forms.py b/accounts/forms.py similarity index 88% rename from account/forms.py rename to accounts/forms.py index e85403f..0e3f646 100644 --- a/account/forms.py +++ b/accounts/forms.py @@ -11,7 +11,7 @@ class LoginForm(AuthenticationForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = helper.FormHelper() - self.helper.form_action = "login" + self.helper.form_action = "accounts:login" self.helper.form_method = "post" self.helper.add_input(layout.Submit('submit', 'Login')) @@ -24,6 +24,6 @@ class RegisterForm(UserCreationForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = helper.FormHelper() - self.helper.form_action = "register" + self.helper.form_action = "accounts:register" self.helper.form_method = "post" self.helper.add_input(layout.Submit('submit', 'Register')) diff --git a/account/migrations/0001_initial.py b/accounts/migrations/0001_initial.py similarity index 100% rename from account/migrations/0001_initial.py rename to accounts/migrations/0001_initial.py diff --git a/account/migrations/0002_alter_user_email_alter_user_first_name_and_more.py b/accounts/migrations/0002_alter_user_email_alter_user_first_name_and_more.py similarity index 95% rename from account/migrations/0002_alter_user_email_alter_user_first_name_and_more.py rename to accounts/migrations/0002_alter_user_email_alter_user_first_name_and_more.py index b8f4201..be9e06f 100644 --- a/account/migrations/0002_alter_user_email_alter_user_first_name_and_more.py +++ b/accounts/migrations/0002_alter_user_email_alter_user_first_name_and_more.py @@ -5,7 +5,7 @@ from django.db import migrations, models class Migration(migrations.Migration): dependencies = [ - ("account", "0001_initial"), + ("accounts", "0001_initial"), ] operations = [ diff --git a/account/migrations/__init__.py b/accounts/migrations/__init__.py similarity index 100% rename from account/migrations/__init__.py rename to accounts/migrations/__init__.py diff --git a/account/models.py b/accounts/models.py similarity index 100% rename from account/models.py rename to accounts/models.py diff --git a/account/templates/account/login.html b/accounts/templates/account/login.html similarity index 100% rename from account/templates/account/login.html rename to accounts/templates/account/login.html diff --git a/accounts/templates/account/me.html b/accounts/templates/account/me.html new file mode 100644 index 0000000..37caf86 --- /dev/null +++ b/accounts/templates/account/me.html @@ -0,0 +1,14 @@ +{% extends "facturio/base.html" %} + +{% block title %}About Me{% endblock %} + +{% block content %} + <h2 class="mb-4">About Me</h2> + <div class="card"> + <h5 class="card-header">Welcome, {{ request.user.first_name }} {{ request.user.last_name }}!</h5> + <ul class="list-group list-group-flush"> + <li class="list-group-item">Username: {{ request.user.username }}</li> + <li class="list-group-item">Email: {{ request.user.email }}</li> + </ul> + </div> +{% endblock %} diff --git a/account/templates/account/register.html b/accounts/templates/account/register.html similarity index 100% rename from account/templates/account/register.html rename to accounts/templates/account/register.html diff --git a/account/tests.py b/accounts/tests.py similarity index 100% rename from account/tests.py rename to accounts/tests.py diff --git a/account/urls.py b/accounts/urls.py similarity index 79% rename from account/urls.py rename to accounts/urls.py index 2184e52..8e7a9ac 100644 --- a/account/urls.py +++ b/accounts/urls.py @@ -1,8 +1,9 @@ -from django.http import HttpResponse -from django.urls import path, re_path +from django.urls import path from . import views +app_name = "accounts" + urlpatterns = [ path("", views.me, name="me"), path("me/", views.me, name="me_exp"), diff --git a/account/views.py b/accounts/views.py similarity index 100% rename from account/views.py rename to accounts/views.py diff --git a/facturio/settings.py b/facturio/settings.py index fbc69af..b92181f 100644 --- a/facturio/settings.py +++ b/facturio/settings.py @@ -37,7 +37,7 @@ INSTALLED_APPS = [ "django.contrib.staticfiles", "crispy_forms", "crispy_bootstrap5", - "account.apps.AccountConfig", + "accounts.apps.AccountConfig", "subjects.apps.SubjectsConfig" ] @@ -121,12 +121,12 @@ STATIC_URL = "static/" DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" # Auth configuration -LOGIN_BASE_URL = "/account" +LOGIN_BASE_URL = "/accounts" LOGIN_URL = f"{LOGIN_BASE_URL}/login" LOGOUT_REDIRECT_URL = f"/" LOGIN_REDIRECT_URL = f"{LOGIN_BASE_URL}/me" -AUTH_USER_MODEL = "account.User" +AUTH_USER_MODEL = "accounts.User" # Crispy config CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" diff --git a/facturio/urls.py b/facturio/urls.py index 9a5aecd..a932a8d 100644 --- a/facturio/urls.py +++ b/facturio/urls.py @@ -26,7 +26,7 @@ def landing_page(req: HttpRequest) -> HttpResponse: urlpatterns = [ path("", landing_page, name="main-page"), - path("account/", include("account.urls")), + path("accounts/", include("accounts.urls")), path("subjects/", include("subjects.urls")), path("admin/", admin.site.urls), ] diff --git a/subjects/admin.py b/subjects/admin.py index dd45906..17549f6 100644 --- a/subjects/admin.py +++ b/subjects/admin.py @@ -2,4 +2,8 @@ from django.contrib import admin from . import models -admin.site.register(models.Subject) + +@admin.register(models.Subject) +class SubjectAdmin(admin.ModelAdmin): + list_display = ["id", "name", "city", "city_part", "zip_code"] + diff --git a/subjects/forms.py b/subjects/forms.py new file mode 100644 index 0000000..13f4b8e --- /dev/null +++ b/subjects/forms.py @@ -0,0 +1,46 @@ +from ares_util.ares import validate_czech_company_id +from ares_util.exceptions import InvalidCompanyIDError +from crispy_forms import helper, layout +from django import forms +from django.core.exceptions import ValidationError +from django.forms import fields +from django.utils.translation import gettext_lazy as _ + +from . import models + + +class CreateSubjectForm(forms.Form): + error_messages = { + "invalid_cin": _("Your provided CIN is not correct."), + "already_existing": _("Subject with provided CIN already exists.") + } + cin = fields.CharField( + label=_("CIN"), + max_length=8, + help_text=_("Enter the subject CIN, rest will be generated automatically") + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.helper = helper.FormHelper() + self.helper.form_action = "subjects:create" + self.helper.form_method = "post" + self.helper.add_input(layout.Submit('submit', 'Add')) + + def clean_cin(self): + cin = self.cleaned_data.get("cin") + try: + validate_czech_company_id(cin) + except InvalidCompanyIDError as ex: + raise ValidationError( + self.error_messages["invalid_cin"] + ) + + try: + models.Subject.objects.get(id=cin) + except models.Subject.DoesNotExist: + return cin + + raise ValidationError( + self.error_messages["already_existing"] + ) diff --git a/subjects/migrations/0001_initial.py b/subjects/migrations/0001_initial.py index e5f9dd5..28c57bb 100644 --- a/subjects/migrations/0001_initial.py +++ b/subjects/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.0 on 2023-12-19 13:38 +# Generated by Django 5.0 on 2023-12-20 22:36 from django.db import migrations, models @@ -21,6 +21,22 @@ class Migration(migrations.Migration): verbose_name="cin", ), ), + ("name", models.CharField(max_length=128, verbose_name="name")), + ( + "vat_id", + models.CharField( + blank=True, max_length=12, null=True, verbose_name="vat_id" + ), + ), + ("street", models.CharField(max_length=64, verbose_name="street")), + ("zip_code", models.CharField(max_length=6, verbose_name="zip_code")), + ("city", models.CharField(max_length=64, verbose_name="city")), + ( + "city_part", + models.CharField( + blank=True, max_length=64, null=True, verbose_name="city_part" + ), + ), ], ), ] diff --git a/subjects/migrations/0002_alter_subject_city_alter_subject_city_part_and_more.py b/subjects/migrations/0002_alter_subject_city_alter_subject_city_part_and_more.py new file mode 100644 index 0000000..fdb6fb1 --- /dev/null +++ b/subjects/migrations/0002_alter_subject_city_alter_subject_city_part_and_more.py @@ -0,0 +1,53 @@ +# Generated by Django 5.0 on 2023-12-20 22:47 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("subjects", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="subject", + name="city", + field=models.CharField(max_length=64, verbose_name="City"), + ), + migrations.AlterField( + model_name="subject", + name="city_part", + field=models.CharField( + blank=True, max_length=64, null=True, verbose_name="City part" + ), + ), + migrations.AlterField( + model_name="subject", + name="id", + field=models.CharField( + max_length=8, primary_key=True, serialize=False, verbose_name="CIN" + ), + ), + migrations.AlterField( + model_name="subject", + name="name", + field=models.CharField(max_length=128, verbose_name="Name"), + ), + migrations.AlterField( + model_name="subject", + name="street", + field=models.CharField(max_length=64, verbose_name="Street"), + ), + migrations.AlterField( + model_name="subject", + name="vat_id", + field=models.CharField( + blank=True, max_length=12, null=True, verbose_name="VAT ID" + ), + ), + migrations.AlterField( + model_name="subject", + name="zip_code", + field=models.CharField(max_length=6, verbose_name="Zip Code"), + ), + ] diff --git a/subjects/models.py b/subjects/models.py index d3a9805..78ad294 100644 --- a/subjects/models.py +++ b/subjects/models.py @@ -5,7 +5,44 @@ from django.utils.translation import gettext_lazy as _ class Subject(models.Model): id = models.CharField( - _("cin"), + _("CIN"), max_length=8, primary_key=True ) + + name = models.CharField( + _("Name"), + max_length=128 + ) + + vat_id = models.CharField( + _("VAT ID"), + max_length=12, + null=True, + blank=True + ) + + street = models.CharField( + _("Street"), + max_length=64, + ) + + zip_code = models.CharField( + _("Zip Code"), + max_length=6, + ) + + city = models.CharField( + _("City"), + max_length=64, + ) + + city_part = models.CharField( + _("City part"), + max_length=64, + null=True, + blank=True + ) + + def __str__(self): + return self.name diff --git a/subjects/templates/subjects/ares.html b/subjects/templates/subjects/ares.html deleted file mode 100644 index eae6f1a..0000000 --- a/subjects/templates/subjects/ares.html +++ /dev/null @@ -1,23 +0,0 @@ -{% extends "facturio/base.html" %} - -{% block title %}AresTest{% endblock %} - -{% block content %} - <div class="container mt-4"> - <h2 class="mb-4">Ares data</h2> - <div class="card {% if error %}bg-danger{% endif %}"> - {% if error %} - <div class="card-body"> - <h5 class="card-title">Ares search failed!</h5> - <p class="card-text">{{ error }}</p> - </div> - {% endif %} - {% if ares_data %} - <div class="card-body"> - <h5 class="card-title">Ares data</h5> - <pre class="card-text">{{ ares_data|pprint }}</pre> - </div> - {% endif %} - </div> - </div> -{% endblock %} diff --git a/subjects/templates/subjects/create.html b/subjects/templates/subjects/create.html new file mode 100644 index 0000000..cd4f345 --- /dev/null +++ b/subjects/templates/subjects/create.html @@ -0,0 +1,9 @@ +{% extends "facturio/base.html" %} + +{% load crispy_forms_tags %} + +{% block title %}Create Subjects{% endblock %} + +{% block content %} + {% crispy form form.helper %} +{% endblock %} \ No newline at end of file diff --git a/subjects/templates/subjects/index.html b/subjects/templates/subjects/index.html new file mode 100644 index 0000000..62d8d90 --- /dev/null +++ b/subjects/templates/subjects/index.html @@ -0,0 +1,33 @@ +{% extends "facturio/base.html" %} + +{% block title %}Subjects{% endblock %} + +{% block content %} + <a href="{% url "subjects:create" %}" class="btn btn-primary" role="button">Add new</a> + <table class="table"> + <thead> + <tr> + <th>CIN</th> + <th>Name</th> + <th>VAT ID</th> + <th>Street</th> + <th>Zip Code</th> + <th>City</th> + <th>City part</th> + </tr> + </thead> + <tbody> + {% for subject in subjects %} + <tr> + <td>{{ subject.id }}</td> + <td>{{ subject.name }}</td> + <td>{{ subject.vat_id }}</td> + <td>{{ subject.street }}</td> + <td>{{ subject.zip_code }}</td> + <td>{{ subject.city }}</td> + <td>{{ subject.city_part }}</td> + </tr> + {% endfor %} + </tbody> + </table> +{% endblock %} diff --git a/subjects/urls.py b/subjects/urls.py index b2a4c93..799d82a 100644 --- a/subjects/urls.py +++ b/subjects/urls.py @@ -2,6 +2,9 @@ from django.urls import path from . import views +app_name = "subjects" + urlpatterns = [ - path("<str:ico>", views.test, name="ares_test") + path("", views.main_page, name="list"), + path("new/", views.create_subject, name="create") ] diff --git a/subjects/views.py b/subjects/views.py index ee36ee3..0c8cab5 100644 --- a/subjects/views.py +++ b/subjects/views.py @@ -1,38 +1,47 @@ -from ares_util.ares import call_ares, validate_czech_company_id -from ares_util.exceptions import InvalidCompanyIDError +from ares_util.ares import call_ares from django.http import HttpRequest, HttpResponse -from django.shortcuts import render +from django.shortcuts import render, redirect +from django.urls import reverse -ARES_BASE_URL = "https://wwwinfo.mfcr.cz/cgi-bin/ares/darv_rzp.cgi?ico=27074358&xml=0&ver=1.0.4" +from . import models, forms def build_address(street: str, zip_code: int | str, city: str, city_part: str) -> str: return f"{street}, {zip_code}, {city} - {city_part}" -def test(req: HttpRequest, ico: str) -> HttpResponse: - try: - validate_czech_company_id(ico) - except InvalidCompanyIDError as ex: - return render(req, "subjects/ares.html", dict(error=ex, ares_data={})) +def main_page(req: HttpRequest) -> HttpResponse: + subjects = models.Subject.objects.all() + return render(req, "subjects/index.html", dict(subjects=subjects)) - ares_data = call_ares(ico) - ares_address_data = ares_data["address"] - ares_legal_data = ares_data["legal"] +def create_subject(req: HttpRequest) -> HttpResponse: + if req.method == "POST": + form = forms.CreateSubjectForm(data=req.POST) - address_line = build_address( - ares_address_data["street"], - ares_address_data["zip_code"], - ares_address_data["city"], - ares_address_data["city_part"] - ) + if not form.is_valid(): + return render(req, "subjects/create.html", dict(form=form)) - important_data = dict( - copmany_name=ares_legal_data["company_name"], - company_id=ares_legal_data["company_id"], - company_vat_id=ares_legal_data["company_vat_id"], - address_line=address_line, - ) + ares_data = call_ares(form.cleaned_data.get("cin")) - return render(req, "subjects/ares.html", dict(error="", ares_data=important_data)) + ares_address_data = ares_data["address"] + ares_legal_data = ares_data["legal"] + + models.Subject.objects.create( + id=ares_legal_data["company_id"], + vat_id=ares_legal_data["company_vat_id"], + name=ares_legal_data["company_name"], + street=ares_address_data["street"], + zip_code=ares_address_data["zip_code"], + city=ares_address_data["city"], + city_part=ares_address_data["city_part"], + ) + + return redirect(reverse("subjects:list")) + + elif req.method == "GET": + form = forms.CreateSubjectForm() + + return render(req, "subjects/create.html", dict(form=form)) + + return HttpResponse(status=405) diff --git a/templates/facturio/base.html b/templates/facturio/base.html index e1d98ec..9296e0b 100644 --- a/templates/facturio/base.html +++ b/templates/facturio/base.html @@ -2,20 +2,20 @@ <html lang="cs"> <head> <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>Facturio - {% block title %}App{% endblock %}</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous"> </head> <body> -<nav class="navbar navbar-expand-lg navbar-dark bg-dark"> +<nav class="navbar navbar-expand-lg navbar-dark bg-primary"> <div class="container-fluid"> <a class="navbar-brand" href="{% url "main-page" %}">Facturio</a> - - <div class="collapse navbar-collapse" id="navbarLeft"> + <div class="collapse navbar-collapse"> <ul class="navbar-nav"> - {#<li class="nav-item">#} - {# <a class="nav-link" href="#">Item 1</a>#} - {#</li>#} + <li class="nav-item"> + <a class="nav-link" href="{% url "subjects:list" %}">Subjects</a> + </li> {#<li class="nav-item">#} {# <a class="nav-link" href="#">Item 2</a>#} {#</li>#} @@ -26,17 +26,17 @@ <ul class="navbar-nav"> {% if request.user.is_authenticated %} <li class="nav-item"> - <a class="nav-link" href="{% url 'me' %}">Profile</a> + <a class="nav-link" href="{% url 'accounts:me' %}">Profile</a> </li> <li class="nav-item"> - <a class="nav-link" href="{% url 'logout' %}">Logout</a> + <a class="nav-link" href="{% url 'accounts:logout' %}">Logout</a> </li> {% else %} <li class="nav-item"> - <a class="nav-link" href="{% url 'login' %}">Login</a> + <a class="nav-link" href="{% url 'accounts:login' %}">Login</a> </li> <li class="nav-item"> - <a class="nav-link" href="{% url 'register' %}">Register</a> + <a class="nav-link" href="{% url 'accounts:register' %}">Register</a> </li> {% endif %} </ul>