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 %} -
-

About Me

-
-
Welcome, {{ request.user.first_name }} {{ request.user.last_name }}!
- -
-
-{% 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 %} +

About Me

+
+
Welcome, {{ request.user.first_name }} {{ request.user.last_name }}!
+ +
+{% 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 %} -
-

Ares data

-
- {% if error %} -
-
Ares search failed!
-

{{ error }}

-
- {% endif %} - {% if ares_data %} -
-
Ares data
-
{{ ares_data|pprint }}
-
- {% endif %} -
-
-{% 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 %} + Add new + + + + + + + + + + + + + + {% for subject in subjects %} + + + + + + + + + + {% endfor %} + +
CINNameVAT IDStreetZip CodeCityCity part
{{ subject.id }}{{ subject.name }}{{ subject.vat_id }}{{ subject.street }}{{ subject.zip_code }}{{ subject.city }}{{ subject.city_part }}
+{% 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("", 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 @@ + Facturio - {% block title %}App{% endblock %} -