Compare commits
13 Commits
66edabab04
...
filter-lis
| Author | SHA1 | Date | |
|---|---|---|---|
| 33ae8fe124 | |||
| d3471d54ea | |||
| b1ecdd1f0c | |||
| 163f59844f | |||
| 929e16e2ec | |||
| f06b72343b | |||
| 76e81be6bf | |||
| 84a34d8049 | |||
| 126caff1ba | |||
| b33ed21949 | |||
| 1b1d0f1b5c | |||
| 5e1989a60f | |||
| ad14f2fe12 |
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@
|
||||
/.venv
|
||||
__pycache__
|
||||
/db.sqlite3
|
||||
/project/settings_local.py
|
||||
|
||||
0
db.template.sqlite3
Normal file
0
db.template.sqlite3
Normal file
6775
media/postanski-brojevi.csv
Normal file
6775
media/postanski-brojevi.csv
Normal file
File diff suppressed because it is too large
Load Diff
33
poetry.lock
generated
33
poetry.lock
generated
@ -52,6 +52,26 @@ files = [
|
||||
django = ">=3.2"
|
||||
jinja2 = ">=3"
|
||||
|
||||
[[package]]
|
||||
name = "gunicorn"
|
||||
version = "21.2.0"
|
||||
description = "WSGI HTTP Server for UNIX"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
files = [
|
||||
{file = "gunicorn-21.2.0-py3-none-any.whl", hash = "sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0"},
|
||||
{file = "gunicorn-21.2.0.tar.gz", hash = "sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
packaging = "*"
|
||||
|
||||
[package.extras]
|
||||
eventlet = ["eventlet (>=0.24.1)"]
|
||||
gevent = ["gevent (>=1.4.0)"]
|
||||
setproctitle = ["setproctitle"]
|
||||
tornado = ["tornado (>=0.2)"]
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.1.3"
|
||||
@ -138,6 +158,17 @@ files = [
|
||||
{file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.0"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"},
|
||||
{file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.4.4"
|
||||
@ -179,4 +210,4 @@ files = [
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "5221e53a0bd37605e95d6feb1c511e8a66ea71fbc94c47d0651a1846aea3a6dc"
|
||||
content-hash = "6c86d5721314c92afa919983780e664a1573ebb7c25a8b54622393b97990e509"
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import random
|
||||
from typing import Any
|
||||
|
||||
from jinja2 import Environment
|
||||
@ -11,10 +12,17 @@ def conditional_cls(conditions: dict[str, Any]) -> str:
|
||||
return " ".join(result)
|
||||
|
||||
|
||||
def random_id() -> str:
|
||||
return f"_{random.randint(0, 10**8)}"
|
||||
|
||||
|
||||
def environment(**options):
|
||||
env = Environment(**options)
|
||||
|
||||
env.globals.update({
|
||||
"conditional_cls": conditional_cls,
|
||||
})
|
||||
env.globals.update(
|
||||
{
|
||||
"conditional_cls": conditional_cls,
|
||||
"random_id": random_id,
|
||||
}
|
||||
)
|
||||
return env
|
||||
|
||||
@ -3,4 +3,4 @@ from django.apps import AppConfig
|
||||
|
||||
class MainConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "main"
|
||||
name = "project.main"
|
||||
|
||||
84
project/main/migrations/0001_initial.py
Normal file
84
project/main/migrations/0001_initial.py
Normal file
@ -0,0 +1,84 @@
|
||||
# Generated by Django 5.0.4 on 2024-04-10 19:31
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="County",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=100)),
|
||||
],
|
||||
options={
|
||||
"db_table": "counties",
|
||||
"ordering": ["name"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="PostOffice",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("zip", models.PositiveIntegerField()),
|
||||
("name", models.CharField(max_length=100)),
|
||||
],
|
||||
options={
|
||||
"db_table": "post_offices",
|
||||
"ordering": ["zip"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="Area",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("name", models.CharField(max_length=100)),
|
||||
(
|
||||
"county",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE, to="main.county"
|
||||
),
|
||||
),
|
||||
(
|
||||
"post_office",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="main.postoffice",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"db_table": "areas",
|
||||
"ordering": ["name"],
|
||||
},
|
||||
),
|
||||
]
|
||||
48
project/main/migrations/0002_auto_20240410_1942.py
Normal file
48
project/main/migrations/0002_auto_20240410_1942.py
Normal file
@ -0,0 +1,48 @@
|
||||
# Generated by Django 5.0.4 on 2024-04-10 19:42
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
counties = [
|
||||
'Koprivničko-križevačka',
|
||||
'Međimurska',
|
||||
'Dubrovačko-neretvanska',
|
||||
'Zagrebačka',
|
||||
'Primorsko-goranska',
|
||||
'Požeško-slavonska',
|
||||
'Vukovarsko-srijemska',
|
||||
'Karlovačka',
|
||||
'Zadarska',
|
||||
'Bjelovarsko-bilogorska',
|
||||
'Splitsko-dalmatinska',
|
||||
'Virovitičko-podravska',
|
||||
'Ličko-senjska',
|
||||
'Grad Zagreb',
|
||||
'Brodsko-posavska',
|
||||
'Osječko-baranjska',
|
||||
'Sisačko-moslavačka',
|
||||
'Krapinsko-zagorska',
|
||||
'Istarska',
|
||||
'Šibensko-kninska',
|
||||
'Varaždinska',
|
||||
]
|
||||
|
||||
|
||||
def create_counties(apps, schema_editor):
|
||||
County = apps.get_model("main", "County")
|
||||
|
||||
bulk = []
|
||||
|
||||
for county in counties:
|
||||
bulk.append(County(name=county))
|
||||
|
||||
County.objects.bulk_create(bulk)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("main", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_counties),
|
||||
]
|
||||
333
project/main/migrations/0003_auto_20240410_1959.py
Normal file
333
project/main/migrations/0003_auto_20240410_1959.py
Normal file
@ -0,0 +1,333 @@
|
||||
# Generated by Django 5.0.4 on 2024-04-10 19:59
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
post_offices = {
|
||||
10000: 'Zagreb',
|
||||
10010: 'Zagreb-Sloboština',
|
||||
10020: 'Zagreb-Novi Zagreb',
|
||||
10040: 'Zagreb-Dubrava',
|
||||
10090: 'Zagreb-Susedgrad',
|
||||
10104: 'Zagreb',
|
||||
10105: 'Zagreb',
|
||||
10108: 'Zagreb',
|
||||
10109: 'Zagreb',
|
||||
10110: 'Zagreb',
|
||||
10135: 'Zagreb',
|
||||
10172: 'Zagreb',
|
||||
10250: 'Lučko',
|
||||
10255: 'Gornji Stupnik',
|
||||
10257: 'Brezovica',
|
||||
10290: 'Zaprešić',
|
||||
10291: 'Prigorje Brdovečko',
|
||||
10292: 'Šenkovec',
|
||||
10295: 'Kupljenovo',
|
||||
10297: 'Jakovlje',
|
||||
10298: 'Bistra',
|
||||
10310: 'Ivanić Grad',
|
||||
10315: 'Novoselec',
|
||||
10340: 'Vrbovec',
|
||||
10342: 'Dubrava',
|
||||
10360: 'Sesvete',
|
||||
10361: 'Sesvete Kraljevec',
|
||||
10362: 'Kašina',
|
||||
10370: 'Dugo Selo',
|
||||
10380: 'Sveti Ivan Zelina',
|
||||
10410: 'Velika Gorica',
|
||||
10413: 'Kravarsko',
|
||||
10414: 'Pokupsko',
|
||||
10430: 'Samobor',
|
||||
10431: 'Sveta Nedelja',
|
||||
10435: 'Sveti Martin pod Okićem',
|
||||
10437: 'Bestovje',
|
||||
10450: 'Jastrebarsko',
|
||||
10451: 'Pisarovina',
|
||||
10454: 'Krašić',
|
||||
20000: 'Dubrovnik',
|
||||
20207: 'Mlini',
|
||||
20210: 'Cavtat',
|
||||
20215: 'Gruda',
|
||||
20221: 'Koločep',
|
||||
20222: 'Lopud',
|
||||
20223: 'Šipanska Luka',
|
||||
20225: 'Babino Polje',
|
||||
20230: 'Ston',
|
||||
20232: 'Slano',
|
||||
20235: 'Zaton Veliki',
|
||||
20236: 'Mokošica',
|
||||
20240: 'Trpanj',
|
||||
20244: 'Potomje',
|
||||
20246: 'Janjina',
|
||||
20250: 'Orebić',
|
||||
20260: 'Korčula',
|
||||
20270: 'Vela Luka',
|
||||
20271: 'Blato',
|
||||
20290: 'Lastovo',
|
||||
20340: 'Ploče',
|
||||
20350: 'Metković',
|
||||
20355: 'Opuzen',
|
||||
21000: 'Split',
|
||||
21203: 'Donji Muć',
|
||||
21204: 'Dugopolje',
|
||||
21210: 'Solin',
|
||||
21212: 'Kaštel Sućurac',
|
||||
21214: 'Kaštel Kambelovac',
|
||||
21217: 'Kaštel Štafilić',
|
||||
21220: 'Trogir',
|
||||
21222: 'Marina',
|
||||
21225: 'Drvenik Veliki',
|
||||
21230: 'Sinj',
|
||||
21240: 'Trilj',
|
||||
21232: 'Dicmo',
|
||||
21233: 'Hrvace',
|
||||
21236: 'Vrlika',
|
||||
21238: 'Otok (Dalmacija)',
|
||||
21250: 'Šestanovac',
|
||||
21256: 'Cista Provo',
|
||||
21260: 'Imotski',
|
||||
21270: 'Zagvozd',
|
||||
21276: 'Vrgorac',
|
||||
21300: 'Makarska',
|
||||
21310: 'Omiš',
|
||||
21311: 'Stobreč',
|
||||
21315: 'Dugi Rat',
|
||||
21320: 'Baška Voda',
|
||||
21327: 'Podgora',
|
||||
21330: 'Gradac',
|
||||
21400: 'Supetar',
|
||||
21405: 'Milna',
|
||||
21412: 'Pučišća',
|
||||
21420: 'Bol',
|
||||
21425: 'Selca',
|
||||
21430: 'Grohote',
|
||||
21450: 'Hvar',
|
||||
21460: 'Stari Grad',
|
||||
21465: 'Jelsa',
|
||||
21469: 'Sućuraj',
|
||||
21480: 'Vis',
|
||||
21485: 'Komiža',
|
||||
22000: 'Šibenik',
|
||||
22202: 'Primošten',
|
||||
22211: 'Vodice',
|
||||
22213: 'Pirovac',
|
||||
22222: 'Skradin',
|
||||
22232: 'Zlarin',
|
||||
22233: 'Prvić Luka',
|
||||
22234: 'Prvić Šepurine',
|
||||
22235: 'Kaprije',
|
||||
22236: 'Žirje',
|
||||
22240: 'Tisno',
|
||||
22243: 'Murter',
|
||||
22300: 'Knin',
|
||||
22320: 'Drniš',
|
||||
22323: 'Unešić',
|
||||
23000: 'Zadar',
|
||||
23205: 'Bibinje',
|
||||
23206: 'Sukošan',
|
||||
23210: 'Biograd na Moru',
|
||||
23212: 'Tkon',
|
||||
23222: 'Zemunik',
|
||||
23223: 'Škabrnja',
|
||||
23232: 'Nin',
|
||||
23233: 'Privlaka',
|
||||
23234: 'Vir',
|
||||
23235: 'Vrsi',
|
||||
23241: 'Poličnik',
|
||||
23242: 'Posedarje',
|
||||
23244: 'Starigrad Paklenica',
|
||||
23248: 'Ražanac',
|
||||
23250: 'Pag',
|
||||
23271: 'Kukljica',
|
||||
23273: 'Preko',
|
||||
23281: 'Sali',
|
||||
23283: 'Rava',
|
||||
23284: 'Veli Iž',
|
||||
23286: 'Božava',
|
||||
23287: 'Veli Rat',
|
||||
23291: 'Sestrunj',
|
||||
23292: 'Molat',
|
||||
23293: 'Ist',
|
||||
23294: 'Premuda',
|
||||
23295: 'Silba',
|
||||
23296: 'Olib',
|
||||
23312: 'Novigrad (Dalmacija)',
|
||||
23420: 'Benkovac',
|
||||
23422: 'Stankovci',
|
||||
23440: 'Gračac',
|
||||
23450: 'Obrovac',
|
||||
31000: 'Osijek',
|
||||
31200: 'Osijek',
|
||||
31207: 'Tenja',
|
||||
31226: 'Dalj',
|
||||
31300: 'Beli Manastir',
|
||||
31326: 'Darda',
|
||||
31400: 'Đakovo',
|
||||
31431: 'Čepin',
|
||||
31500: 'Našice',
|
||||
31540: 'Donji Miholjac',
|
||||
31550: 'Valpovo',
|
||||
31551: 'Belišće',
|
||||
32000: 'Vukovar',
|
||||
32100: 'Vinkovci',
|
||||
32236: 'Ilok',
|
||||
32242: 'Slakovci',
|
||||
32249: 'Tovarnik',
|
||||
32252: 'Otok',
|
||||
32257: 'Drenovci',
|
||||
32270: 'Županja',
|
||||
32284: 'Stari Mikanovci',
|
||||
33000: 'Virovitica',
|
||||
33405: 'Pitomača',
|
||||
33410: 'Suhopolje',
|
||||
33515: 'Orahovica',
|
||||
33520: 'Slatina',
|
||||
34000: 'Požega',
|
||||
34310: 'Pleternica',
|
||||
34340: 'Kutjevo',
|
||||
34550: 'Pakrac',
|
||||
35000: 'Slavonski Brod',
|
||||
35212: 'Garčin',
|
||||
35214: 'Donji Andrijevci',
|
||||
35220: 'Slavonski Šamac',
|
||||
35222: 'Gundinci',
|
||||
35250: 'Oriovac',
|
||||
35252: 'Sibinj',
|
||||
35400: 'Nova Gradiška',
|
||||
35410: 'Nova Kapela',
|
||||
35420: 'Staro Petrovo Selo',
|
||||
35430: 'Okučani',
|
||||
40000: 'Čakovec',
|
||||
40313: 'Sveti Martin na Muri',
|
||||
40315: 'Mursko Središče',
|
||||
40320: 'Donji Kraljevec',
|
||||
40323: 'Prelog',
|
||||
42000: 'Varaždin',
|
||||
42202: 'Trnovec Bartolovečki',
|
||||
42204: 'Turčin',
|
||||
42208: 'Cestica',
|
||||
42220: 'Novi Marof',
|
||||
42223: 'Varaždinske Toplice',
|
||||
42230: 'Ludbreg',
|
||||
42240: 'Ivanec',
|
||||
42243: 'Maruševec',
|
||||
42250: 'Lepoglava',
|
||||
43000: 'Bjelovar',
|
||||
43240: 'Čazma',
|
||||
43270: 'Veliki Grđevac',
|
||||
43280: 'Garešnica',
|
||||
43290: 'Grubišno Polje',
|
||||
43500: 'Daruvar',
|
||||
44000: 'Sisak',
|
||||
44010: 'Sisak-Caprag',
|
||||
44210: 'Sunja',
|
||||
44250: 'Petrinja',
|
||||
44317: 'Popovača',
|
||||
44320: 'Kutina',
|
||||
44330: 'Novska',
|
||||
44400: 'Glina',
|
||||
44410: 'Vrginmost',
|
||||
44430: 'Hrvatska Kostajnica',
|
||||
44440: 'Dvor',
|
||||
47000: 'Karlovac',
|
||||
47220: 'Vojnić',
|
||||
47240: 'Slunj',
|
||||
47250: 'Duga Resa',
|
||||
47280: 'Ozalj',
|
||||
47300: 'Ogulin',
|
||||
48000: 'Koprivnica',
|
||||
48214: 'Sveti Ivan Žabno',
|
||||
48260: 'Križevci',
|
||||
48316: 'Đelekovec',
|
||||
48350: 'Đurđevac',
|
||||
49000: 'Krapina',
|
||||
49210: 'Zabok',
|
||||
49216: 'Desinić',
|
||||
49217: 'Krapinske Toplice',
|
||||
49218: 'Pregrada',
|
||||
49221: 'Bedekovčina',
|
||||
49223: 'Sveti Križ Začretje',
|
||||
49225: 'Đurmanec',
|
||||
49231: 'Hum na Sutli',
|
||||
49240: 'Donja Stubica',
|
||||
49243: 'Oroslavje',
|
||||
49246: 'Marija Bistrica',
|
||||
49250: 'Zlatar',
|
||||
49252: 'Mihovljan',
|
||||
49282: 'Konjščina',
|
||||
49284: 'Budinščina',
|
||||
49290: 'Klanjec',
|
||||
51000: 'Rijeka',
|
||||
51211: 'Matulji',
|
||||
51215: 'Kastav',
|
||||
51216: 'Viškovo',
|
||||
51217: 'Klana',
|
||||
51218: 'Dražice',
|
||||
51224: 'Krasica',
|
||||
51226: 'Hreljin',
|
||||
51250: 'Novi Vinodolski',
|
||||
51260: 'Crikvenica',
|
||||
51262: 'Kraljevica',
|
||||
51280: 'Rab',
|
||||
51300: 'Delnice',
|
||||
51306: 'Čabar',
|
||||
51326: 'Vrbovsko',
|
||||
51410: 'Opatija',
|
||||
51415: 'Lovran',
|
||||
51417: 'Mošćenička Draga',
|
||||
51500: 'Krk',
|
||||
51511: 'Malinska',
|
||||
51523: 'Baška',
|
||||
51550: 'Mali Lošinj',
|
||||
51552: 'Ilovik',
|
||||
51556: 'Martinšćica',
|
||||
51557: 'Cres',
|
||||
51561: 'Susak',
|
||||
51562: 'Unije',
|
||||
52000: 'Pazin',
|
||||
52100: 'Pula (Pola)',
|
||||
52207: 'Barban',
|
||||
52210: 'Rovinj (Rovigno)',
|
||||
52220: 'Labin',
|
||||
52333: 'Podpićan',
|
||||
52341: 'Žminj',
|
||||
52420: 'Buzet',
|
||||
52424: 'Motovun (Montona)',
|
||||
52440: 'Poreč (Parenzo)',
|
||||
52460: 'Buje (Buie)',
|
||||
52466: 'Novigrad-Cittanova',
|
||||
52470: 'Umag (Umago)',
|
||||
53000: 'Gospić',
|
||||
53202: 'Perušić',
|
||||
53220: 'Otočac',
|
||||
53230: 'Korenica',
|
||||
53234: 'Udbina',
|
||||
53250: 'Donji Lapac',
|
||||
53260: 'Brinje',
|
||||
53270: 'Senj',
|
||||
53288: 'Karlobag',
|
||||
53291: 'Novalja',
|
||||
}
|
||||
|
||||
|
||||
def create_post_offices(apps, schema_editor):
|
||||
PostOffice = apps.get_model("main", "PostOffice")
|
||||
|
||||
bulk = []
|
||||
|
||||
for key, name in post_offices.items():
|
||||
bulk.append(PostOffice(
|
||||
zip=key,
|
||||
name=name,
|
||||
))
|
||||
|
||||
PostOffice.objects.bulk_create(bulk)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("main", "0002_auto_20240410_1942"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_post_offices),
|
||||
]
|
||||
6863
project/main/migrations/0004_auto_20240411_0605.py
Normal file
6863
project/main/migrations/0004_auto_20240411_0605.py
Normal file
File diff suppressed because it is too large
Load Diff
3
project/main/models/__init__.py
Normal file
3
project/main/models/__init__.py
Normal file
@ -0,0 +1,3 @@
|
||||
from .area import Area
|
||||
from .county import County
|
||||
from .post_office import PostOffice
|
||||
11
project/main/models/area.py
Normal file
11
project/main/models/area.py
Normal file
@ -0,0 +1,11 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Area(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
post_office = models.ForeignKey("PostOffice", on_delete=models.CASCADE)
|
||||
county = models.ForeignKey("County", on_delete=models.CASCADE)
|
||||
|
||||
class Meta:
|
||||
db_table = "areas"
|
||||
ordering = ["name"]
|
||||
9
project/main/models/county.py
Normal file
9
project/main/models/county.py
Normal file
@ -0,0 +1,9 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class County(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
class Meta:
|
||||
db_table = "counties"
|
||||
ordering = ["name"]
|
||||
10
project/main/models/post_office.py
Normal file
10
project/main/models/post_office.py
Normal file
@ -0,0 +1,10 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
class PostOffice(models.Model):
|
||||
zip = models.PositiveIntegerField()
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
class Meta:
|
||||
db_table = "post_offices"
|
||||
ordering = ["zip"]
|
||||
@ -5,6 +5,7 @@
|
||||
<meta name="viewport"
|
||||
content="width=100%, user-scalable=yes, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.min.js" integrity="sha384-BBtl+eGJRgqQAUMxJ7pMwbEyER4l1g+O15P+16Ep7Q9Q+zqX6gSbd85u4mG4QzX+" crossorigin="anonymous"></script>
|
||||
<script src="https://unpkg.com/htmx.org@1.9.11" integrity="sha384-0gxUXCCR8yv9FM2b+U3FDbsKthCI66oH5IA9fHppQq9DDMHuMauqq1ZHBpJxQ0J0" crossorigin="anonymous"></script>
|
||||
<title>{{ title or "Django-html demo" }}</title>
|
||||
</head>
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
{% extends "main/base/layout.html" %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
This is some complex form content bellow.
|
||||
</p>
|
||||
{% endblock %}
|
||||
47
project/main/templates/main/complex_form/container.html
Normal file
47
project/main/templates/main/complex_form/container.html
Normal file
@ -0,0 +1,47 @@
|
||||
{% extends "main/base/layout.html" %}
|
||||
{% from "main/components/js_alert.html" import no_js_alert %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{{ no_js_alert() }}
|
||||
|
||||
<form>
|
||||
<div class="row mb-4">
|
||||
<div
|
||||
class="col-md-6"
|
||||
hx-post="{{ url("complex-form-handle-route-module") }}"
|
||||
hx-trigger="change"
|
||||
hx-target="#route-module-content"
|
||||
>
|
||||
<div id="route-module-content" class="h-100">
|
||||
{% set state = form_state.route_module %}
|
||||
{% include "main/complex_form/route_module.html" %}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="col"
|
||||
hx-post="{{ url("complex-form-handle-reports") }}"
|
||||
hx-trigger="change"
|
||||
hx-target="#reports-content"
|
||||
>
|
||||
<div id="reports-content">
|
||||
{% set state = form_state.reports %}
|
||||
{% include "main/complex_form/reports.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div
|
||||
class="col-md-6"
|
||||
hx-post="{{ url("complex-form-handle-warehouse-management") }}"
|
||||
hx-trigger="change"
|
||||
hx-target="#warehouse-management-content"
|
||||
>
|
||||
<div id="warehouse-management-content">
|
||||
{% set state = form_state.warehouse_management %}
|
||||
{% include "main/complex_form/warehouse_management.html" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
||||
45
project/main/templates/main/complex_form/reports.html
Normal file
45
project/main/templates/main/complex_form/reports.html
Normal file
@ -0,0 +1,45 @@
|
||||
{% from "main/components/inputs.html" import checkbox, select %}
|
||||
|
||||
{% set indent_1 = "ms-4" %}
|
||||
{% set indent_2 = "ms-5" %}
|
||||
|
||||
<div class="card p-3 h-100">
|
||||
{{ checkbox(title="Enable Reports", name="reports", state=state.enabled) }}
|
||||
|
||||
<hr>
|
||||
|
||||
{{ checkbox(title="Allow empty cashbag", name="allow_empty_cashbag", state=state.allow_empty_cashbag) }}
|
||||
{{ checkbox(title="Reports builder", name="reports_builder", state=state.reports_builder) }}
|
||||
{{ checkbox(title="Tax reports", name="tax_reports", state=state.tax_reports) }}
|
||||
{{ checkbox(title="Transaction list", name="transaction_list", state=state.transaction_list) }}
|
||||
{{ checkbox(title="Reports generator", name="reports_generator", state=state.reports_generator) }}
|
||||
{{ checkbox(title="Technical center reports", name="technical_center_reports", state=state.technical_center_reports) }}
|
||||
{{ checkbox(title="Dispense list", name="dispense_list", state=state.dispense_list) }}
|
||||
|
||||
{{ checkbox(title="Cash conformity", name="cash_conformity", state=state.cash_conformity) }}
|
||||
{{ checkbox(title="Scan 2nd barcode", name="scan_2nd_bardcode", state=state.scan_2nd_bardcode, cls=indent_1) }}
|
||||
|
||||
{{ select(
|
||||
title="Days between CC",
|
||||
name="days_between_cc",
|
||||
options={
|
||||
"1": "1",
|
||||
"2": "2",
|
||||
"3": "3",
|
||||
"4": "4",
|
||||
"5": "5",
|
||||
"6": "6",
|
||||
"7": "7",
|
||||
"8": "8",
|
||||
"9": "9",
|
||||
"14": "14",
|
||||
"30": "30",
|
||||
"31": "31",
|
||||
"60": "60",
|
||||
"90": "90",
|
||||
"365": "365",
|
||||
},
|
||||
state=state.days_between_cc,
|
||||
cls=indent_1
|
||||
) }}
|
||||
</div>
|
||||
35
project/main/templates/main/complex_form/route_module.html
Normal file
35
project/main/templates/main/complex_form/route_module.html
Normal file
@ -0,0 +1,35 @@
|
||||
{% from "main/components/inputs.html" import checkbox, select %}
|
||||
|
||||
{% set indent_1 = "ms-4" %}
|
||||
{% set indent_2 = "ms-5" %}
|
||||
|
||||
<div class="card p-3 h-100">
|
||||
{{ checkbox(title="Enable Route module", name="route_module", state=state.enabled) }}
|
||||
|
||||
<hr>
|
||||
|
||||
{{ checkbox(title="Smart routing", name="smart_routing", state=state.smart_routing) }}
|
||||
{{ checkbox(title="Predictive pickup", name="predictive_pickup", state=state.predictive_pickup, cls=indent_1) }}
|
||||
{{ checkbox(title="Automatic planning", name="automatic_planning", state=state.automatic_planning, cls=indent_1) }}
|
||||
{{ checkbox(title="Geo routing", name="geo_routing", state=state.geo_routing) }}
|
||||
{{ checkbox(title="Warehouse", name="warehouse", state=state.warehouse) }}
|
||||
{{ checkbox(title="End warehouse tracking", name="end_warehouse_tracking", state=state.end_warehouse_tracking, cls=indent_1) }}
|
||||
{{ checkbox(title="Pick&Pack application", name="pick_and_pack_application", state=state.pick_and_pack_application, cls=indent_1) }}
|
||||
{{ checkbox(title="Custom forms in routing", name="custom_forms_in_routing", state=state.custom_forms_in_routing) }}
|
||||
{{ checkbox(title="Money bag tracking", name="money_bag_tracking", state=state.money_bag_tracking) }}
|
||||
|
||||
{{ select(
|
||||
title="Packing model",
|
||||
name="packing_model",
|
||||
options={
|
||||
"": "No packing model",
|
||||
"per_route": "Packing model per route",
|
||||
"per_machine": "Packing model per machine",
|
||||
},
|
||||
state=state.packing_model
|
||||
) }}
|
||||
|
||||
{{ checkbox(title="Prekitting to box", name="prekitting_to_box", state=state.prekitting_to_box, cls=indent_1) }}
|
||||
{{ checkbox(title="Prekitting to pallet", name="prekitting_to_pallet", state=state.prekitting_to_pallet, cls=indent_1) }}
|
||||
{{ checkbox(title="Real time stock", name="real_time_stock", state=state.real_time_stock, cls=indent_1) }}
|
||||
</div>
|
||||
@ -0,0 +1,24 @@
|
||||
{% from "main/components/inputs.html" import checkbox, select %}
|
||||
|
||||
{% set indent_1 = "ms-4" %}
|
||||
{% set indent_2 = "ms-5" %}
|
||||
|
||||
<div class="card p-3 h-100">
|
||||
{{ checkbox(title="Product warehouse", name="product_warehouse", state=state.product_warehouse) }}
|
||||
{{ select(
|
||||
title="Delivery option",
|
||||
name="delivery_option",
|
||||
options={
|
||||
"direct": "Direct",
|
||||
"vehicle": "Vehicle",
|
||||
},
|
||||
state=state.delivery_option,
|
||||
cls=indent_1
|
||||
) }}
|
||||
{{ checkbox(title="Allow negative stock", name="allow_negative_stock", state=state.allow_negative_stock, cls=indent_1) }}
|
||||
{{ checkbox(title="Route reserve stock", name="route_reserve_stock", state=state.route_reserve_stock, cls=indent_1) }}
|
||||
{{ checkbox(title="Refill log", name="refill_log", state=state.refill_log, cls=indent_1) }}
|
||||
{{ checkbox(title="Product order", name="product_order", state=state.product_order, cls=indent_1) }}
|
||||
{{ checkbox(title="Spare parts warehouse", name="spare_parts_warehouse", state=state.spare_parts_warehouse) }}
|
||||
{{ checkbox(title="Purchase module", name="purchase_module", state=state.purchase_module) }}
|
||||
</div>
|
||||
46
project/main/templates/main/components/inputs.html
Normal file
46
project/main/templates/main/components/inputs.html
Normal file
@ -0,0 +1,46 @@
|
||||
{% macro checkbox(title, name, state, cls="") %}
|
||||
{% if state.visible %}
|
||||
{% set id = random_id() %}
|
||||
|
||||
<div class="form-check {{ cls }} mb-2">
|
||||
<input
|
||||
class="form-check-input"
|
||||
type="checkbox"
|
||||
id="{{ id }}"
|
||||
name="{{ name }}"
|
||||
{% if state.checked and state.enabled %}checked {% endif %}
|
||||
{% if not state.enabled %}disabled {% endif %}
|
||||
>
|
||||
<label class="form-check-label" for="{{ id }}">
|
||||
{{ title }}
|
||||
</label>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro select(title, name, options, state, cls="") %}
|
||||
{% if state.visible %}
|
||||
<div class="{{ cls }}">
|
||||
{% if title %}
|
||||
<label class="form-label {% if not state.enabled %}text-secondary{% endif %}">
|
||||
{{ title }}
|
||||
</label>
|
||||
{% endif %}
|
||||
<select
|
||||
name="{{ name }}"
|
||||
class="form-select mb-2"
|
||||
{% if not state.enabled %}disabled{% endif %}
|
||||
>
|
||||
{% for value, title in options.items() %}
|
||||
<option
|
||||
value="{{ value }}"
|
||||
{% if value == state.value %}selected{% endif %}
|
||||
>
|
||||
{{ title }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
25
project/main/templates/main/components/js_alert.html
Normal file
25
project/main/templates/main/components/js_alert.html
Normal file
@ -0,0 +1,25 @@
|
||||
{% macro no_js_alert() %}
|
||||
<div class="alert alert-success d-flex align-items-center mb-5" role="alert">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="text-success me-3" viewBox="0 0 16 16">
|
||||
<path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/>
|
||||
</svg>
|
||||
<div>
|
||||
No JavaScript is used creating this page.
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro js_alert(content) %}
|
||||
<div class="alert alert-warning d-flex align-items-center mb-5" role="alert">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="text-warning me-3" viewBox="0 0 16 16">
|
||||
<path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/>
|
||||
</svg>
|
||||
<div>
|
||||
{{ content }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro js_alert_work_in_progress() %}
|
||||
{{ js_alert("Work in progress.") }}
|
||||
{% endmacro %}
|
||||
53
project/main/templates/main/components/pagination.html
Normal file
53
project/main/templates/main/components/pagination.html
Normal file
@ -0,0 +1,53 @@
|
||||
{% macro pagination(page) %}
|
||||
{% macro render_link(title, page_number) %}
|
||||
<a
|
||||
class="page-link"
|
||||
href="#"
|
||||
hx-get="{{ url("filter-list-filter") }}"
|
||||
hx-trigger="click"
|
||||
hx-target="#filter-list-container"
|
||||
hx-include="[name='zip'],[name='area'],[name='county']"
|
||||
hx-vals='{"page": "{{ page_number }}"}'
|
||||
>
|
||||
{{ title }}
|
||||
</a>
|
||||
{% endmacro %}
|
||||
|
||||
{% if page.has_other_pages() %}
|
||||
<nav aria-label="Page navigation example">
|
||||
<ul class="pagination">
|
||||
{% if page.has_previous() %}
|
||||
<li class="page-item">
|
||||
{{ render_link(title="Previous", page_number=page.number - 1) }}
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#">Previous</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
{% for p in page.paginator.get_elided_page_range(page.number, on_each_side=2, on_ends=3) %}
|
||||
{% if p != page.paginator.ELLIPSIS %}
|
||||
<li class="page-item {% if page.number == p %}active{% endif %}">
|
||||
{{ render_link(title=p, page_number=p) }}
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item">
|
||||
<span class="page-link border-top-0 border-bottom-0">{{ page.paginator.ELLIPSIS }}</span>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if page.has_next() %}
|
||||
<li class="page-item">
|
||||
{{ render_link(title="Next", page_number=page.number + 1) }}
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="page-item disabled">
|
||||
<a class="page-link" href="#">Next</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
9
project/main/templates/main/components/toast.html
Normal file
9
project/main/templates/main/components/toast.html
Normal file
@ -0,0 +1,9 @@
|
||||
{% macro toast(title, id) %}
|
||||
<div class="toast-container position-absolute top-0 start-50 translate-middle-x mt-2">
|
||||
<div id="{{ id }}" class="toast text-bg-success">
|
||||
<div class="toast-body">
|
||||
{{ title }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@ -1,8 +1,68 @@
|
||||
{% extends "main/base/layout.html" %}
|
||||
{% from "main/components/js_alert.html" import js_alert_work_in_progress %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
This is some filter list content bellow.
|
||||
</p>
|
||||
<div class="card mb-2">
|
||||
<div class="card-body">
|
||||
<form id="filter-form"
|
||||
class="row g-2 align-items-center"
|
||||
>
|
||||
<div class="col-auto">
|
||||
<label class="form-label mb-0">ZIP:</label>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<input
|
||||
type="number"
|
||||
name="zip"
|
||||
class="form-control"
|
||||
hx-get="{{ url("filter-list-filter") }}"
|
||||
hx-trigger="keyup"
|
||||
hx-target="#filter-list-container"
|
||||
hx-include="[name='area'],[name='county']"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label class="form-label mb-0">Area:</label>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<input
|
||||
type="text"
|
||||
name="area"
|
||||
class="form-control"
|
||||
hx-get="{{ url("filter-list-filter") }}"
|
||||
hx-trigger="keyup"
|
||||
hx-target="#filter-list-container"
|
||||
hx-include="[name='zip'],[name='county']"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<label class="form-label mb-0">County:</label>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<select
|
||||
name="county"
|
||||
class="form-select"
|
||||
hx-get="{{ url("filter-list-filter") }}"
|
||||
hx-trigger="change"
|
||||
hx-target="#filter-list-container"
|
||||
hx-include="[name='zip'],[name='area']"
|
||||
>
|
||||
<option value="">
|
||||
- All -
|
||||
</option>
|
||||
{% for county in counties %}
|
||||
<option value="{{ county.id }}">
|
||||
{{ county.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="filter-list-container">
|
||||
{% include "main/filter_list_content.html" %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
25
project/main/templates/main/filter_list_content.html
Normal file
25
project/main/templates/main/filter_list_content.html
Normal file
@ -0,0 +1,25 @@
|
||||
{% from "main/components/pagination.html" import pagination %}
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Zip Code</th>
|
||||
<th>Post Office</th>
|
||||
<th>Area</th>
|
||||
<th>County</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for area in page.object_list %}
|
||||
<tr>
|
||||
<td>{{ area.post_office.zip }}</td>
|
||||
<td>{{ area.post_office.name }}</td>
|
||||
<td>{{ area.name }}</td>
|
||||
<td>{{ area.county.name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
{{ pagination(page) }}
|
||||
@ -1,19 +1,37 @@
|
||||
{% extends "main/base/layout.html" %}
|
||||
{% from "main/components/toast.html" import toast %}
|
||||
{% from "main/components/js_alert.html" import js_alert %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<form
|
||||
hx-post="{{ url("form-validation") }}"
|
||||
hx-target="#validation-form-content"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<div class="card p-4">
|
||||
{% include "main/form_validation_content.html" %}
|
||||
<div class="d-flex">
|
||||
<button type="submit" class="btn btn-success ms-auto">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{{ js_alert("Just few lines of JavaScript used to pop toast.") }}
|
||||
|
||||
<form
|
||||
hx-post="{{ url("form-validation") }}"
|
||||
hx-target="#validation-form-content"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<div class="card p-4">
|
||||
{% include "main/form_validation_content.html" %}
|
||||
<div class="d-flex">
|
||||
<button type="submit" class="btn btn-success ms-auto">
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{{ toast(title="Form validated and saved successfully. Sorry, but I used few lines of JS code for this toast.", id="toast-success") }}
|
||||
|
||||
|
||||
<script>
|
||||
let toastSuccess = null;
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const toastElement = document.querySelector("#toast-success");
|
||||
toastSuccess = new bootstrap.Toast(toastElement, {
|
||||
delay: 3000,
|
||||
})
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -57,3 +57,10 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% if validation.is_valid %}
|
||||
<script>
|
||||
toastSuccess.show();
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
{% extends "main/base/layout.html" %}
|
||||
{% from "main/components/js_alert.html" import no_js_alert %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
@ -13,6 +14,8 @@
|
||||
</button>
|
||||
{% endmacro %}
|
||||
|
||||
{{ no_js_alert() }}
|
||||
|
||||
{{ render_btn(title="Initial", cls="btn-outline-secondary", content="initial") }}
|
||||
{{ render_btn(title="Swap to content 1", cls="btn-info", content="info") }}
|
||||
{{ render_btn(title="Swap to content 2", cls="btn-warning", content="warning") }}
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
{% extends "main/base/layout.html" %}
|
||||
{% from "main/components/js_alert.html" import js_alert_work_in_progress %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
This is some table inline edit content bellow.
|
||||
</p>
|
||||
{{ js_alert_work_in_progress() }}
|
||||
{% endblock %}
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
from .complex_form import ComplexFormView
|
||||
from .filter_list import FilterListView
|
||||
from .complex_form.views import (
|
||||
ComplexFormView,
|
||||
ReportsHandleView,
|
||||
RouteModuleHandleView,
|
||||
WarehouseManagementHandleView,
|
||||
)
|
||||
from .filter_list import FilterListFilterView, FilterListView
|
||||
from .form_validation import FormValidationView
|
||||
from .home import HomeView
|
||||
from .swap import SwapView
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
from project.main.views.demo_view_base import DemoViewBase
|
||||
|
||||
|
||||
class ComplexFormView(DemoViewBase):
|
||||
template_name = "main/complex_form.html"
|
||||
active_section = "complex-form"
|
||||
title = "Complex Form"
|
||||
0
project/main/views/complex_form/__init__.py
Normal file
0
project/main/views/complex_form/__init__.py
Normal file
46
project/main/views/complex_form/reports_state.py
Normal file
46
project/main/views/complex_form/reports_state.py
Normal file
@ -0,0 +1,46 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from project.main.views.complex_form.state_models import CheckboxState, SelectState
|
||||
|
||||
|
||||
@dataclass
|
||||
class ReportsState:
|
||||
enabled: CheckboxState
|
||||
cash_conformity: CheckboxState
|
||||
days_between_cc: SelectState
|
||||
scan_2nd_bardcode: CheckboxState
|
||||
allow_empty_cashbag: CheckboxState
|
||||
reports_builder: CheckboxState
|
||||
tax_reports: CheckboxState
|
||||
transaction_list: CheckboxState
|
||||
reports_generator: CheckboxState
|
||||
technical_center_reports: CheckboxState
|
||||
dispense_list: CheckboxState
|
||||
|
||||
@staticmethod
|
||||
def from_form(values: dict[str, str]) -> "ReportsState":
|
||||
return ReportsState(
|
||||
enabled=CheckboxState(checked=values.get("reports") == "on"),
|
||||
cash_conformity=CheckboxState(checked=values.get("cash_conformity") == "on"),
|
||||
days_between_cc=SelectState(value=values.get("days_between_cc")),
|
||||
scan_2nd_bardcode=CheckboxState(checked=values.get("scan_2nd_bardcode") == "on"),
|
||||
allow_empty_cashbag=CheckboxState(checked=values.get("allow_empty_cashbag") == "on"),
|
||||
reports_builder=CheckboxState(checked=values.get("reports_builder") == "on"),
|
||||
tax_reports=CheckboxState(checked=values.get("tax_reports") == "on"),
|
||||
transaction_list=CheckboxState(checked=values.get("transaction_list") == "on"),
|
||||
reports_generator=CheckboxState(checked=values.get("reports_generator") == "on"),
|
||||
technical_center_reports=CheckboxState(checked=values.get("technical_center_reports") == "on"),
|
||||
dispense_list=CheckboxState(checked=values.get("dispense_list") == "on"),
|
||||
)
|
||||
|
||||
def control_state(self) -> None:
|
||||
self.cash_conformity.enabled = self.enabled.checked
|
||||
self.days_between_cc.visible = self.cash_conformity.enabled and self.cash_conformity.checked
|
||||
self.scan_2nd_bardcode.enabled = self.cash_conformity.enabled and self.cash_conformity.checked
|
||||
self.allow_empty_cashbag.enabled = self.enabled.checked
|
||||
self.reports_builder.enabled = self.enabled.checked
|
||||
self.tax_reports.enabled = self.enabled.checked
|
||||
self.transaction_list.enabled = self.enabled.checked
|
||||
self.reports_generator.enabled = self.enabled.checked
|
||||
self.technical_center_reports.enabled = self.enabled.checked
|
||||
self.dispense_list.enabled = self.enabled.checked
|
||||
92
project/main/views/complex_form/route_module_state.py
Normal file
92
project/main/views/complex_form/route_module_state.py
Normal file
@ -0,0 +1,92 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from project.main.views.complex_form.state_models import CheckboxState, SelectState
|
||||
|
||||
|
||||
@dataclass
|
||||
class RouteModuleState:
|
||||
enabled: CheckboxState
|
||||
smart_routing: CheckboxState
|
||||
predictive_pickup: CheckboxState
|
||||
automatic_planning: CheckboxState
|
||||
geo_routing: CheckboxState
|
||||
packing_model: SelectState
|
||||
prekitting_to_box: CheckboxState
|
||||
prekitting_to_pallet: CheckboxState
|
||||
real_time_stock: CheckboxState
|
||||
warehouse: CheckboxState
|
||||
end_warehouse_tracking: CheckboxState
|
||||
pick_and_pack_application: CheckboxState
|
||||
custom_forms_in_routing: CheckboxState
|
||||
money_bag_tracking: CheckboxState
|
||||
|
||||
@staticmethod
|
||||
def from_form(values: dict[str, str]) -> "RouteModuleState":
|
||||
return RouteModuleState(
|
||||
enabled=CheckboxState(checked=values.get("route_module") == "on"),
|
||||
smart_routing=CheckboxState(checked=values.get("smart_routing") == "on"),
|
||||
predictive_pickup=CheckboxState(
|
||||
checked=values.get("predictive_pickup") == "on"
|
||||
),
|
||||
automatic_planning=CheckboxState(
|
||||
checked=values.get("automatic_planning") == "on"
|
||||
),
|
||||
geo_routing=CheckboxState(checked=values.get("geo_routing") == "on"),
|
||||
packing_model=SelectState(value=values.get("packing_model")),
|
||||
prekitting_to_box=CheckboxState(
|
||||
checked=values.get("prekitting_to_box") == "on"
|
||||
),
|
||||
prekitting_to_pallet=CheckboxState(
|
||||
checked=values.get("prekitting_to_pallet") == "on"
|
||||
),
|
||||
real_time_stock=CheckboxState(
|
||||
checked=values.get("real_time_stock") == "on"
|
||||
),
|
||||
warehouse=CheckboxState(checked=values.get("warehouse") == "on"),
|
||||
end_warehouse_tracking=CheckboxState(
|
||||
checked=values.get("end_warehouse_tracking") == "on"
|
||||
),
|
||||
pick_and_pack_application=CheckboxState(
|
||||
checked=values.get("pick_and_pack_application") == "on"
|
||||
),
|
||||
custom_forms_in_routing=CheckboxState(
|
||||
checked=values.get("custom_forms_in_routing") == "on"
|
||||
),
|
||||
money_bag_tracking=CheckboxState(
|
||||
checked=values.get("money_bag_tracking") == "on"
|
||||
),
|
||||
)
|
||||
|
||||
def control_state(self) -> None:
|
||||
self.smart_routing.enabled = self.enabled.checked
|
||||
self.geo_routing.enabled = self.enabled.checked
|
||||
self.warehouse.enabled = self.enabled.checked
|
||||
self.custom_forms_in_routing.enabled = self.enabled.checked
|
||||
|
||||
self.predictive_pickup.visible = (
|
||||
self.smart_routing.checked and self.smart_routing.enabled
|
||||
)
|
||||
self.automatic_planning.visible = (
|
||||
self.smart_routing.checked and self.smart_routing.enabled
|
||||
)
|
||||
self.end_warehouse_tracking.visible = (
|
||||
self.warehouse.checked and self.warehouse.enabled
|
||||
)
|
||||
self.pick_and_pack_application.visible = (
|
||||
self.warehouse.checked and self.warehouse.enabled
|
||||
)
|
||||
|
||||
if not self.enabled.checked:
|
||||
self.packing_model.value = None
|
||||
|
||||
self.packing_model.enabled = self.enabled.checked
|
||||
self.prekitting_to_box.visible = (
|
||||
self.packing_model.enabled and self.packing_model.value
|
||||
)
|
||||
self.prekitting_to_pallet.visible = (
|
||||
self.packing_model.enabled and self.packing_model.value
|
||||
)
|
||||
self.real_time_stock.visible = (
|
||||
self.packing_model.enabled and self.packing_model.value
|
||||
)
|
||||
self.money_bag_tracking.enabled = self.enabled.checked
|
||||
15
project/main/views/complex_form/state_models.py
Normal file
15
project/main/views/complex_form/state_models.py
Normal file
@ -0,0 +1,15 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class CheckboxState:
|
||||
checked: bool = False
|
||||
visible: bool = True
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
@dataclass
|
||||
class SelectState:
|
||||
value: str = None
|
||||
visible: bool = True
|
||||
enabled: bool = True
|
||||
93
project/main/views/complex_form/views.py
Normal file
93
project/main/views/complex_form/views.py
Normal file
@ -0,0 +1,93 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from django.core.handlers.wsgi import WSGIRequest
|
||||
from django.http import HttpResponse
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
from project.main.views.complex_form.reports_state import ReportsState
|
||||
from project.main.views.complex_form.route_module_state import RouteModuleState
|
||||
from project.main.views.complex_form.warehouse_management_state import (
|
||||
WarehouseManagementState,
|
||||
)
|
||||
from project.main.views.demo_view_base import DemoViewBase
|
||||
|
||||
|
||||
@dataclass
|
||||
class FormState:
|
||||
route_module: RouteModuleState
|
||||
reports: ReportsState
|
||||
warehouse_management: WarehouseManagementState
|
||||
|
||||
def control_state(self) -> None:
|
||||
self.route_module.control_state()
|
||||
self.reports.control_state()
|
||||
self.warehouse_management.control_state()
|
||||
|
||||
|
||||
class ComplexFormView(DemoViewBase):
|
||||
template_name = "main/complex_form/container.html"
|
||||
active_section = "complex-form"
|
||||
title = "Complex Form"
|
||||
|
||||
def get_context_data(self, **kwargs) -> dict[str, Any]:
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
state = FormState(
|
||||
route_module=RouteModuleState.from_form(values={}),
|
||||
reports=ReportsState.from_form(values={}),
|
||||
warehouse_management=WarehouseManagementState.from_form(values={}),
|
||||
)
|
||||
state.control_state()
|
||||
|
||||
context.update(
|
||||
{
|
||||
"form_state": state,
|
||||
}
|
||||
)
|
||||
return context
|
||||
|
||||
def post(self, request: WSGIRequest, *args, **kwargs) -> HttpResponse:
|
||||
...
|
||||
|
||||
|
||||
class RouteModuleHandleView(TemplateView):
|
||||
template_name = "main/complex_form/route_module.html"
|
||||
|
||||
def post(self, request: WSGIRequest, *args, **kwargs) -> HttpResponse:
|
||||
state = RouteModuleState.from_form(values=request.POST)
|
||||
state.control_state()
|
||||
|
||||
return self.render_to_response(
|
||||
context={
|
||||
"state": state,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class ReportsHandleView(TemplateView):
|
||||
template_name = "main/complex_form/reports.html"
|
||||
|
||||
def post(self, request: WSGIRequest, *args, **kwargs) -> HttpResponse:
|
||||
state = ReportsState.from_form(values=request.POST)
|
||||
state.control_state()
|
||||
|
||||
return self.render_to_response(
|
||||
context={
|
||||
"state": state,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class WarehouseManagementHandleView(TemplateView):
|
||||
template_name = "main/complex_form/warehouse_management.html"
|
||||
|
||||
def post(self, request: WSGIRequest, *args, **kwargs) -> HttpResponse:
|
||||
state = WarehouseManagementState.from_form(values=request.POST)
|
||||
state.control_state()
|
||||
|
||||
return self.render_to_response(
|
||||
context={
|
||||
"state": state,
|
||||
}
|
||||
)
|
||||
@ -0,0 +1,48 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
from project.main.views.complex_form.state_models import CheckboxState, SelectState
|
||||
|
||||
|
||||
@dataclass
|
||||
class WarehouseManagementState:
|
||||
product_warehouse: CheckboxState
|
||||
delivery_option: SelectState
|
||||
allow_negative_stock: CheckboxState
|
||||
route_reserve_stock: CheckboxState
|
||||
refill_log: CheckboxState
|
||||
product_order: CheckboxState
|
||||
spare_parts_warehouse: CheckboxState
|
||||
purchase_module: CheckboxState
|
||||
|
||||
@staticmethod
|
||||
def from_form(values: dict[str, str]) -> "WarehouseManagementState":
|
||||
return WarehouseManagementState(
|
||||
product_warehouse=CheckboxState(
|
||||
checked=values.get("product_warehouse") == "on"
|
||||
),
|
||||
delivery_option=SelectState(value=values.get("delivery_option")),
|
||||
allow_negative_stock=CheckboxState(
|
||||
checked=values.get("allow_negative_stock") == "on"
|
||||
),
|
||||
route_reserve_stock=CheckboxState(
|
||||
checked=values.get("route_reserve_stock") == "on"
|
||||
),
|
||||
refill_log=CheckboxState(checked=values.get("refill_log") == "on"),
|
||||
product_order=CheckboxState(checked=values.get("product_order") == "on"),
|
||||
spare_parts_warehouse=CheckboxState(
|
||||
checked=values.get("spare_parts_warehouse") == "on"
|
||||
),
|
||||
purchase_module=CheckboxState(
|
||||
checked=values.get("purchase_module") == "on"
|
||||
),
|
||||
)
|
||||
|
||||
def control_state(self) -> None:
|
||||
self.delivery_option.visible = self.product_warehouse.checked
|
||||
self.allow_negative_stock.visible = self.product_warehouse.checked
|
||||
self.route_reserve_stock.visible = self.product_warehouse.checked
|
||||
self.refill_log.visible = self.product_warehouse.checked
|
||||
self.product_order.visible = self.product_warehouse.checked
|
||||
self.purchase_module.enabled = (
|
||||
self.product_warehouse.checked or self.spare_parts_warehouse.checked
|
||||
)
|
||||
@ -1,7 +1,76 @@
|
||||
from typing import Any
|
||||
|
||||
from django.core.paginator import Page, Paginator
|
||||
from django.db.models import Q, QuerySet
|
||||
|
||||
from project.main.models import Area, County
|
||||
from project.main.views.demo_view_base import DemoViewBase
|
||||
|
||||
PAGE_SIZE = 20
|
||||
|
||||
class FilterListView(DemoViewBase):
|
||||
|
||||
def get_all_counties() -> QuerySet[County]:
|
||||
return County.objects.all()
|
||||
|
||||
|
||||
class FilterListViewBase(DemoViewBase):
|
||||
def paginate(self, qs: QuerySet[Area]) -> Page:
|
||||
page = int(self.request.GET.get("page", 1))
|
||||
page_size = int(self.request.GET.get("page_size", PAGE_SIZE))
|
||||
|
||||
paginator = Paginator(
|
||||
object_list=qs,
|
||||
per_page=page_size,
|
||||
)
|
||||
return paginator.get_page(page)
|
||||
|
||||
|
||||
class FilterListView(FilterListViewBase):
|
||||
template_name = "main/filter_list.html"
|
||||
active_section = "filter-list"
|
||||
title = "Filter List"
|
||||
|
||||
def get_context_data(self, **kwargs) -> dict[str, Any]:
|
||||
context_data = super().get_context_data(**kwargs)
|
||||
|
||||
page = self.paginate(
|
||||
qs=Area.objects.select_related("county", "post_office").all(),
|
||||
)
|
||||
context_data.update(
|
||||
{
|
||||
"page": page,
|
||||
"counties": get_all_counties(),
|
||||
}
|
||||
)
|
||||
return context_data
|
||||
|
||||
|
||||
class FilterListFilterView(FilterListViewBase):
|
||||
template_name = "main/filter_list_content.html"
|
||||
|
||||
def get_context_data(self, **kwargs) -> dict[str, Any]:
|
||||
context_data = super().get_context_data(**kwargs)
|
||||
|
||||
q = Q()
|
||||
zip = self.request.GET.get("zip", None)
|
||||
area = self.request.GET.get("area", None)
|
||||
county = self.request.GET.get("county", None)
|
||||
|
||||
if zip:
|
||||
q &= Q(post_office__zip=zip)
|
||||
if area:
|
||||
q &= Q(name__icontains=area)
|
||||
if county:
|
||||
q &= Q(county_id=county)
|
||||
|
||||
page = self.paginate(
|
||||
qs=Area.objects.select_related("county", "post_office").filter(q),
|
||||
)
|
||||
|
||||
context_data.update(
|
||||
{
|
||||
"page": page,
|
||||
"counties": get_all_counties(),
|
||||
}
|
||||
)
|
||||
return context_data
|
||||
|
||||
@ -11,6 +11,7 @@ from project.main.views.demo_view_base import DemoViewBase
|
||||
@dataclass
|
||||
class Validation:
|
||||
validated: bool = False
|
||||
is_valid: bool = False
|
||||
|
||||
name: Optional[str] = None
|
||||
consent: Optional[bool] = None
|
||||
@ -57,6 +58,10 @@ class FormValidationView(DemoViewBase):
|
||||
if not validation.consent:
|
||||
validation.consent_error = "You should consent"
|
||||
|
||||
validation.is_valid = not (
|
||||
validation.name_error or validation.age_error or validation.consent_error
|
||||
)
|
||||
|
||||
return render(
|
||||
context={
|
||||
"validation": validation,
|
||||
|
||||
@ -37,6 +37,7 @@ INSTALLED_APPS = [
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"project.main",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
@ -146,3 +147,5 @@ STATIC_URL = "static/"
|
||||
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
|
||||
|
||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
||||
|
||||
from project.settings_local import *
|
||||
|
||||
2
project/settings_local.template.py
Normal file
2
project/settings_local.template.py
Normal file
@ -0,0 +1,2 @@
|
||||
ALLOWED_HOSTS = ["*"]
|
||||
DEBUG = True
|
||||
@ -1,3 +1,7 @@
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import path
|
||||
|
||||
from project.main import views
|
||||
@ -6,7 +10,35 @@ urlpatterns = [
|
||||
path("", views.HomeView.as_view(), name="home"),
|
||||
path("swap", views.SwapView.as_view(), name="swap"),
|
||||
path("filter-list", views.FilterListView.as_view(), name="filter-list"),
|
||||
path(
|
||||
"filter-list/filter", views.FilterListFilterView.as_view(), name="filter-list-filter"
|
||||
),
|
||||
path("form-validation", views.FormValidationView.as_view(), name="form-validation"),
|
||||
path("complex-form", views.ComplexFormView.as_view(), name="complex-form"),
|
||||
path("table-inline-edit", views.TableInlineEditView.as_view(), name="table-inline-edit"),
|
||||
path(
|
||||
"complex-form/handle/route-module",
|
||||
views.RouteModuleHandleView.as_view(),
|
||||
name="complex-form-handle-route-module",
|
||||
),
|
||||
path(
|
||||
"complex-form/handle/reports",
|
||||
views.ReportsHandleView.as_view(),
|
||||
name="complex-form-handle-reports",
|
||||
),
|
||||
path(
|
||||
"complex-form/handle/warehouse-management",
|
||||
views.WarehouseManagementHandleView.as_view(),
|
||||
name="complex-form-handle-warehouse-management",
|
||||
),
|
||||
path(
|
||||
"table-inline-edit",
|
||||
views.TableInlineEditView.as_view(),
|
||||
name="table-inline-edit",
|
||||
),
|
||||
]
|
||||
|
||||
# check if db exists, and copy from template if not
|
||||
if not Path.exists(settings.DATABASES["default"]["NAME"]):
|
||||
template_filename = settings.BASE_DIR / "db.template.sqlite3"
|
||||
shutil.copyfile(template_filename, settings.DATABASES["default"]["NAME"])
|
||||
print("Database copied from template.")
|
||||
|
||||
@ -9,6 +9,7 @@ readme = "README.md"
|
||||
python = "^3.10"
|
||||
django = "^5.0.4"
|
||||
django-jinja = "^2.11.0"
|
||||
gunicorn = "^21.2.0"
|
||||
|
||||
|
||||
[build-system]
|
||||
|
||||
Reference in New Issue
Block a user