Added ares to automatically add subjects and refactored named views
This commit is contained in:
parent
702877465c
commit
d2faa872ac
28 changed files with 274 additions and 88 deletions
|
@ -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 %}
|
|
@ -3,4 +3,4 @@ from django.apps import AppConfig
|
|||
|
||||
class AccountConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "account"
|
||||
name = "accounts"
|
|
@ -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'))
|
|
@ -5,7 +5,7 @@ from django.db import migrations, models
|
|||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("account", "0001_initial"),
|
||||
("accounts", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
14
accounts/templates/account/me.html
Normal file
14
accounts/templates/account/me.html
Normal file
|
@ -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 %}
|
|
@ -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"),
|
|
@ -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"
|
||||
|
|
|
@ -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),
|
||||
]
|
||||
|
|
|
@ -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"]
|
||||
|
||||
|
|
46
subjects/forms.py
Normal file
46
subjects/forms.py
Normal file
|
@ -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"]
|
||||
)
|
|
@ -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"
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
|
@ -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"),
|
||||
),
|
||||
]
|
|
@ -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
|
||||
|
|
|
@ -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 %}
|
9
subjects/templates/subjects/create.html
Normal file
9
subjects/templates/subjects/create.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% extends "facturio/base.html" %}
|
||||
|
||||
{% load crispy_forms_tags %}
|
||||
|
||||
{% block title %}Create Subjects{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% crispy form form.helper %}
|
||||
{% endblock %}
|
33
subjects/templates/subjects/index.html
Normal file
33
subjects/templates/subjects/index.html
Normal file
|
@ -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 %}
|
|
@ -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")
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
||||
def create_subject(req: HttpRequest) -> HttpResponse:
|
||||
if req.method == "POST":
|
||||
form = forms.CreateSubjectForm(data=req.POST)
|
||||
|
||||
if not form.is_valid():
|
||||
return render(req, "subjects/create.html", dict(form=form))
|
||||
|
||||
ares_data = call_ares(form.cleaned_data.get("cin"))
|
||||
|
||||
ares_address_data = ares_data["address"]
|
||||
ares_legal_data = ares_data["legal"]
|
||||
|
||||
address_line = build_address(
|
||||
ares_address_data["street"],
|
||||
ares_address_data["zip_code"],
|
||||
ares_address_data["city"],
|
||||
ares_address_data["city_part"]
|
||||
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"],
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
return redirect(reverse("subjects:list"))
|
||||
|
||||
return render(req, "subjects/ares.html", dict(error="", ares_data=important_data))
|
||||
elif req.method == "GET":
|
||||
form = forms.CreateSubjectForm()
|
||||
|
||||
return render(req, "subjects/create.html", dict(form=form))
|
||||
|
||||
return HttpResponse(status=405)
|
||||
|
|
|
@ -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>
|
||||
|
|
Reference in a new issue