Initial commit

This commit is contained in:
Jakub Kropáček 2023-12-18 21:00:37 +01:00
commit 3901201460
34 changed files with 649 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
.idea/

0
account/__init__.py Normal file
View file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

3
account/admin.py Normal file
View file

@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
account/apps.py Normal file
View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class AccountConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "account"

View file

3
account/models.py Normal file
View file

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View file

@ -0,0 +1,20 @@
{% extends "facturio/base.html" %}
{% block title %}Login{% endblock %}
{% block content %}
<div class="container mt-5">
<form class="col-md-6 offset-md-3" action="{% url 'login' %}" method="post">
{% csrf_token %}
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" name="username" id="username" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" name="password" id="password" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
</div>
{% endblock %}

View file

@ -0,0 +1,15 @@
{% 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">
<div class="card-body">
<h5 class="card-title">Welcome, {{ request.user.username }}!</h5>
<p class="card-text">Email: {{ request.user.email }}</p>
</div>
</div>
</div>
{% endblock %}

View file

@ -0,0 +1,5 @@
{% extends "facturio/base.html" %}
{% block title %}Register{% endblock %}
{% block content %}
<h1>TODO: register</h1>
{% endblock %}

3
account/tests.py Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

12
account/urls.py Normal file
View file

@ -0,0 +1,12 @@
from django.http import HttpResponse
from django.urls import path, re_path
from . import views
urlpatterns = [
path("", views.me, name="me"),
path("me/", views.me, name="me_exp"),
path("login/", views.auth_login, name="login"),
path("logout/", views.auth_logout, name="logout"),
path("register/", views.auth_register, name="register"),
]

40
account/views.py Normal file
View file

@ -0,0 +1,40 @@
from django.conf import settings
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.http import HttpRequest, HttpResponse, JsonResponse
from django.shortcuts import redirect, render
def auth_login(req: HttpRequest) -> HttpResponse:
if req.method == "POST":
username = req.POST["username"]
password = req.POST["password"]
user = authenticate(req, username=username, password=password)
if not user:
return JsonResponse(
{
"message": "Invalid cretendials"
},
status=401
)
else:
login(req, user)
redirect_url = getattr(req.GET, "next", settings.LOGIN_REDIRECT_URL)
return redirect(redirect_url)
elif req.method == "GET":
return render(req, "account/login.html")
return HttpResponse(status=405)
@login_required
def auth_logout(req: HttpRequest) -> HttpResponse:
logout(req)
return redirect(settings.LOGOUT_REDIRECT_URL)
def auth_register(req: HttpRequest) -> HttpResponse:
return render(req, "account/register.html")
@login_required
def me(req: HttpRequest) -> HttpResponse:
return render(req, "account/me.html")

BIN
db.sqlite3 Normal file

Binary file not shown.

0
facturio/__init__.py Normal file
View file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

16
facturio/asgi.py Normal file
View file

@ -0,0 +1,16 @@
"""
ASGI config for facturio project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "facturio.settings")
application = get_asgi_application()

128
facturio/settings.py Normal file
View file

@ -0,0 +1,128 @@
"""
Django settings for facturio project.
Generated by 'django-admin startproject' using Django 5.0.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = "django-insecure-+l+g3)q1zz2bz7=mz4ys6lhu5uj+=ucj34flm^clo4vb3(wmdp"
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"account.apps.AccountConfig"
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "facturio.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
STATICFILES_DIRS = [
BASE_DIR / "static",
]
WSGI_APPLICATION = "facturio.wsgi.application"
# Database
# https://docs.djangoproject.com/en/5.0/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.0/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = "static/"
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
# Login configuration
LOGIN_BASE_URL = "/account"
LOGIN_URL = f"{LOGIN_BASE_URL}/login"
LOGOUT_REDIRECT_URL = f"/"
LOGIN_REDIRECT_URL = f"{LOGIN_BASE_URL}/me"

34
facturio/urls.py Normal file
View file

@ -0,0 +1,34 @@
"""
URL configuration for facturio project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/5.0/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
# TODO: remove
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
from django.urls import path, include
# TODO: remove
def demo(req: HttpRequest) -> HttpResponse:
return render(req, "facturio/invoice.html")
urlpatterns = [
path("", demo, name="main-page"),
path("account/", include("account.urls")),
path("admin/", admin.site.urls),
]

16
facturio/wsgi.py Normal file
View file

@ -0,0 +1,16 @@
"""
WSGI config for facturio project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "facturio.settings")
application = get_wsgi_application()

22
manage.py Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "facturio.settings")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == "__main__":
main()

67
poetry.lock generated Normal file
View file

@ -0,0 +1,67 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]]
name = "asgiref"
version = "3.7.2"
description = "ASGI specs, helper code, and adapters"
optional = false
python-versions = ">=3.7"
files = [
{file = "asgiref-3.7.2-py3-none-any.whl", hash = "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e"},
{file = "asgiref-3.7.2.tar.gz", hash = "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"},
]
[package.extras]
tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"]
[[package]]
name = "django"
version = "5.0"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
optional = false
python-versions = ">=3.10"
files = [
{file = "Django-5.0-py3-none-any.whl", hash = "sha256:3a9fd52b8dbeae335ddf4a9dfa6c6a0853a1122f1fb071a8d5eca979f73a05c8"},
{file = "Django-5.0.tar.gz", hash = "sha256:7d29e14dfbc19cb6a95a4bd669edbde11f5d4c6a71fdaa42c2d40b6846e807f7"},
]
[package.dependencies]
asgiref = ">=3.7.0"
sqlparse = ">=0.3.1"
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
[package.extras]
argon2 = ["argon2-cffi (>=19.1.0)"]
bcrypt = ["bcrypt"]
[[package]]
name = "sqlparse"
version = "0.4.4"
description = "A non-validating SQL parser."
optional = false
python-versions = ">=3.5"
files = [
{file = "sqlparse-0.4.4-py3-none-any.whl", hash = "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3"},
{file = "sqlparse-0.4.4.tar.gz", hash = "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"},
]
[package.extras]
dev = ["build", "flake8"]
doc = ["sphinx"]
test = ["pytest", "pytest-cov"]
[[package]]
name = "tzdata"
version = "2023.3"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
files = [
{file = "tzdata-2023.3-py2.py3-none-any.whl", hash = "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"},
{file = "tzdata-2023.3.tar.gz", hash = "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a"},
]
[metadata]
lock-version = "2.0"
python-versions = "^3.12"
content-hash = "1c6ab57fc89858a0923620d3307e9173662bad9b58d9dde4f567267506e03587"

16
pyproject.toml Normal file
View file

@ -0,0 +1,16 @@
[tool.poetry]
name = "facturio"
version = "0.1.0"
description = ""
authors = ["Jakub Kropáček <kropikuba@gmail.com>"]
license = "MIT"
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
django = "^5.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8">
<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">
<div class="container-fluid">
<a class="navbar-brand" href="#">Facturio</a>
<!-- Left-aligned items -->
<div class="collapse navbar-collapse" id="navbarLeft">
<ul class="navbar-nav">
<!-- Additional items on the left -->
<li class="nav-item">
<a class="nav-link" href="#">Item 1</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Item 2</a>
</li>
<!-- Add more items as needed -->
</ul>
</div>
<!-- Right-aligned login/logout links -->
<div class="collapse navbar-collapse justify-content-end" id="navbarRight">
<ul class="navbar-nav">
{% if request.user.is_authenticated %}
<li class="nav-item">
<a class="nav-link" href="{% url 'logout' %}">Logout</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">Login</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'register' %}">Register</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
{% block content %} {% endblock %}
</div>
</body>
</html>

View file

@ -0,0 +1,189 @@
<!-- Vaše faktura s aktualizacemi -->
<!DOCTYPE html>
<html lang="cs">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Faktura</title>
<style>
@import url("https://fonts.googleapis.com/css2?family=Ubuntu&display=swap");
:root {
--font-family: "Ubuntu", sans-serif;
--main-color: #333;
--secondary-color: #888;
--border-color: #ddd;
--background-color: #fff;
}
body {
font-family: var(--font-family);
margin: 20px;
color: var(--main-color);
background-color: var(--background-color);
}
header {
text-align: left;
margin-bottom: 20px;
}
.parties ul {
list-style-type: none;
padding: 0;
margin: 0;
}
.parties li {
margin-top: 5px;
}
.parties h2 {
border-bottom: 1px solid var(--border-color);
padding-bottom: 8px;
font-size: 1.2rem;
}
.invoice-id {
color: var(--secondary-color);
}
#invoice {
border-collapse: collapse;
width: 100%;
}
#invoice th,
#invoice td {
padding: 12px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
.invoice-total {
margin-top: 20px;
text-align: right;
}
.parties-section {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
.parties {
width: 48%;
}
#invoice tbody tr {
border-bottom: 1px solid var(--border-color);
}
.small-col {
width: 5%;
}
.medium-col {
width: 15%;
}
.big-col {
width: 25%;
}
.right-align {
text-align: right;
}
footer {
position: fixed;
bottom: 0;
width: 100%;
text-align: center;
left: 0;
right: 0;
padding: 0;
margin-top: 20px;
color: var(--main-color);
background-color: var(--background-color);
}
@media print {
@page {
size: auto;
margin: 0;
}
body {
margin: 10mm 10mm;
}
}
</style>
</head>
<body>
<header>
<h1>Faktura <span class="invoice-id">2023-0001</span></h1>
</header>
<section class="parties-section">
<div class="parties">
<h2>Dodavatel</h2>
<ul>
<li>Název Vaší společnosti nebo jméno</li>
<li>Vaše adresa</li>
<li>Vaše město, PSČ</li>
<p></p>
<li>IČO: 123456789</li>
<li>DIČ: CZ123456789 jestli jste plátce DPH</li>
<p></p>
<li>Bankovní účet: 123-456789</li>
</ul>
</div>
<div class="parties">
<h2>Odběratel</h2>
<ul>
<li>Název společnosti nebo jméno zákazníka</li>
<li>Adresa zákazníka</li>
<li>Město zákazníka, PSČ</li>
<p></p>
<li>IČO zákazníka: 987654321</li>
<li>DIČ zákazníka: CZ987654321 jestli je plátce DPH</li>
<p></p>
<li><strong>Datum vystavení:</strong> 16. prosince 2023</li>
<li><strong>Datum splatnosti:</strong> 16. prosince 2023</li>
</ul>
</div>
</section>
<section>
<h2>Detaily faktury</h2>
<table id="invoice">
<thead>
<tr>
<th class="small-col">#</th>
<th class="small-col">Jednotka</th>
<th class="big-col">Popis položky</th>
<th class="medium-col right-align">Cena za MJ</th>
<th class="medium-col right-align">Celkem</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>ks</td>
<td>Položka 1</td>
<td class="right-align">50,00 Kč</td>
<td class="right-align">50,00 Kč</td>
</tr>
</tbody>
</table>
<div class="invoice-total">
<p><strong>Celkem: 100,00 Kč</strong></p>
</div>
</section>
<footer>
<p>Fyzická osoba zapsaná v živnostenském rejstříku.</p>
<p>Fakturu vygenerovala aplikace Facturio</p>
</footer>
</body>
</html>