Compare commits
11 commits
Author | SHA1 | Date | |
---|---|---|---|
228e592096 | |||
d324a1853b | |||
0c1fce561a | |||
0f12c3fa74 | |||
fb92e1f9c1 | |||
f99d53c824 | |||
6197abe7d7 | |||
084d35fdf4 | |||
52b2eb2529 | |||
1311fa677d | |||
e8a54dcbc7 |
72 changed files with 1681 additions and 326 deletions
8
.gitignore
vendored
8
.gitignore
vendored
|
@ -3,7 +3,11 @@ __pycache__
|
|||
.python_version
|
||||
*.egg-info
|
||||
|
||||
|
||||
media/
|
||||
./static/
|
||||
/static/
|
||||
media.tar.gz
|
||||
*.pg
|
||||
*.pg
|
||||
|
||||
override.env
|
||||
.env
|
21
Dockerfile
Normal file
21
Dockerfile
Normal file
|
@ -0,0 +1,21 @@
|
|||
FROM python:3.13-alpine
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
RUN pip install --no-cache-dir poetry
|
||||
|
||||
ENV INVENTORY_STATIC_FILES=/static INVENTORY_MEDIA_FILES=/media
|
||||
RUN mkdir -p "$INVENTORY_MEDIA_FILES" && mkdir -p "$INVENTORY_STATIC_FILES"
|
||||
|
||||
COPY pyproject.toml /usr/src/app
|
||||
COPY poetry.lock /usr/src/app
|
||||
|
||||
RUN poetry install --no-root --no-interaction --no-cache
|
||||
COPY manage.py ./manage.py
|
||||
COPY inventory ./inventory
|
||||
COPY inventory_project ./inventory_project
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
RUN poetry run python manage.py collectstatic
|
||||
|
||||
CMD [ "/bin/sh", "/entrypoint.sh" ]
|
115
Readme.md
115
Readme.md
|
@ -9,17 +9,19 @@ what area of your workshop you have to search for to find the part you
|
|||
currently need. It has been optimized to store information for electronics
|
||||
parts and small other hardware like screws, nuts and bolts.
|
||||
|
||||
### Prerequisites
|
||||
### Prerequisites for manual install or docker Standalone
|
||||
|
||||
As configured by default you will need the following:
|
||||
|
||||
- A postgres database named `inventory` with a postgres user `inventory` that
|
||||
may connect without password or by default with the password `inventory`
|
||||
|
||||
### Installation (manual)
|
||||
|
||||
You will need:
|
||||
- Python > 3.10
|
||||
- Poetry to install requirements and create a virtualenv
|
||||
|
||||
### Installation
|
||||
|
||||
This is a standard Django 5.1 application, if you know how to deploy those the
|
||||
following might sound familiar:
|
||||
|
||||
|
@ -27,16 +29,103 @@ following might sound familiar:
|
|||
- Github `git clone https://github.com/dunkelstern/inventory.git`
|
||||
- ForgeJo: `git clone https://git.dunkelstern.de/dunkelstern/inventory.git`
|
||||
2. Change to checkout: `cd inventory`
|
||||
3. Install virtualenv and dependencies: `poetry install --no-root`
|
||||
4. Migrate the Database: `poetry run python manage.py migrate`
|
||||
5. Create an admin user: `poetry run python manage.py createsuperuser`
|
||||
6. Run the server
|
||||
3. Install virtualenv and dependencies:
|
||||
```
|
||||
poetry install --no-root
|
||||
```
|
||||
4. If you want to use the system in another language than the default english set it
|
||||
up in the `inventory_project/settings.py`:
|
||||
```python
|
||||
LANGUAGE_CODE = 'en-us' # or something like 'de-de'
|
||||
```
|
||||
see the settings file for defined languages.
|
||||
5. If you changed the language rebuild the translation files:
|
||||
```
|
||||
poetry run python manage.py compilemessages
|
||||
```
|
||||
6. Migrate the Database:
|
||||
```
|
||||
poetry run python manage.py migrate
|
||||
```
|
||||
7. Optionally create an admin user. If not done manually the application will prompt you on first run.
|
||||
```
|
||||
poetry run python manage.py createsuperuser
|
||||
```
|
||||
8. Run the server
|
||||
- Development server (not for deployment!): `poetry run python manage.py runserver`
|
||||
- Deployment via `gunicorn` on port 8000: `poetry run gunicorn inventory_project.wsgi -b 0.0.0.0:8000`
|
||||
|
||||
Then login on `http://localhost:8000/admin/` for the Django admin interface or
|
||||
go to `http://localhost:8000` to enter the inventory management system directly
|
||||
|
||||
### Installation (Standalone Docker)
|
||||
|
||||
#### Building yourself
|
||||
|
||||
1. Checkout repository:
|
||||
- Github `git clone https://github.com/dunkelstern/inventory.git`
|
||||
- ForgeJo: `git clone https://git.dunkelstern.de/dunkelstern/inventory.git`
|
||||
2. Change to checkout: `cd inventory`
|
||||
3. Build Docker image: `docker build -t 'dunkelstern/inventory:latest' .`
|
||||
|
||||
next steps below
|
||||
|
||||
#### Pulling from docker hub
|
||||
|
||||
1. Pull Docker image: `docker pull 'dunkelstern/inventory:latest'`
|
||||
|
||||
next steps below
|
||||
|
||||
#### Next steps
|
||||
|
||||
1. Install a PostgreSQL DB somewhere and create a user and DB.
|
||||
2. Setup environment (put everything in a `.env` file):
|
||||
```
|
||||
INVENTORY_DB_HOST=
|
||||
INVENTORY_DB_NAME=
|
||||
INVENTORY_DB_USER=
|
||||
INVENTORY_DB_PASSWORD=
|
||||
|
||||
INVENTORY_SECRET_KEY=
|
||||
INVENTORY_EXTERNAL_URL=http://localhost:8000
|
||||
INVENTORY_TRUSTED_ORIGINS=localhost:8000
|
||||
INVENTORY_DEBUG=FALSE
|
||||
|
||||
INVENTORY_LANGUAGE=en-us
|
||||
INVENTORY_TIMEZONE=UTC
|
||||
|
||||
INVENTORY_PAGE_SIZE=25
|
||||
```
|
||||
3. Create a media directory for uploaded files: `mkdir -p media`
|
||||
4. Run the container:
|
||||
```
|
||||
docker run \
|
||||
--name inventory \
|
||||
-d \
|
||||
--restart=always \
|
||||
--env-file=.env \
|
||||
-p 8000:8000 \
|
||||
--volume ./media:/media \
|
||||
dunkelstern/inventory:latest
|
||||
```
|
||||
5. The onboarding process will start on first call of the application and prompt to create an admin user.
|
||||
|
||||
### Installation (Docker compose)
|
||||
|
||||
1. Checkout repository:
|
||||
- Github `git clone https://github.com/dunkelstern/inventory.git`
|
||||
- ForgeJo: `git clone https://git.dunkelstern.de/dunkelstern/inventory.git`
|
||||
2. Change to checkout: `cd inventory`
|
||||
3. Copy `default.env` to `override.env` and check settings. Use a long random string for `INVENTORY_SECRET_KEY`!
|
||||
4. Build the stack: `docker-compose up --build -d`
|
||||
5. You can reach the application on port 8000
|
||||
6. The onboarding process will start on first call of the application and prompt to create an admin user.
|
||||
|
||||
The compose stack will create two volumes:
|
||||
|
||||
- `inventory_dbdata` which contains the PostgreSQL database directory
|
||||
- `inventory_mediafiles` which will contain any uploaded file
|
||||
|
||||
### Additional information
|
||||
|
||||
1. The initial DB migration pre-populates the database with some useful defaults
|
||||
|
@ -51,6 +140,18 @@ go to `http://localhost:8000` to enter the inventory management system directly
|
|||
[the service file](inventory.service) in the root of this repository for an
|
||||
example.
|
||||
|
||||
### Changelog
|
||||
|
||||
#### 1.1
|
||||
|
||||
- Part count can be configured to be available in settings
|
||||
- Currency can be configured in settings
|
||||
- Complete system is translateable (English and German are provided)
|
||||
- Onboarding process so you do not have to create a superuser on the command line
|
||||
- Docker and Docker-Compose files
|
||||
|
||||

|
||||
|
||||
### Screenshots
|
||||
|
||||
#### Login
|
||||
|
|
22
default.env
Normal file
22
default.env
Normal file
|
@ -0,0 +1,22 @@
|
|||
# override this with a long random string (used for CSRF protection)
|
||||
INVENTORY_SECRET_KEY=""
|
||||
|
||||
# override with URL the service will be available under
|
||||
INVENTORY_EXTERNAL_URL="https://inventory.example.com"
|
||||
|
||||
# override with URLs that are used to send POST requests here,
|
||||
# the EXTERNAL_URL will be in there already, http and https
|
||||
# will be added automatically, separate multiple origins with commas
|
||||
INVENTORY_TRUSTED_ORIGINS="localhost"
|
||||
|
||||
# keep this to FALSE for deployments
|
||||
INVENTORY_DEBUG="FALSE"
|
||||
|
||||
# if you want to run the service in another language, override this
|
||||
INVENTORY_LANGUAGE="en-us"
|
||||
|
||||
# if you want the service to use local time then override this
|
||||
INVENTORY_TIMEZONE="UTC"
|
||||
|
||||
# This is the default pagination size
|
||||
INVENTORY_PAGE_SIZE="25"
|
46
docker-compose.yaml
Normal file
46
docker-compose.yaml
Normal file
|
@ -0,0 +1,46 @@
|
|||
name: inventory
|
||||
|
||||
services:
|
||||
db:
|
||||
image: postgres:17-alpine
|
||||
restart: always
|
||||
shm_size: 128mb
|
||||
environment:
|
||||
POSTGRES_PASSWORD: inventory
|
||||
POSTGRES_USER: inventory
|
||||
POSTGRES_DB: inventory
|
||||
volumes:
|
||||
- dbdata:/var/lib/postgresql/data
|
||||
|
||||
inventory:
|
||||
image: dunkelstern/inventory
|
||||
build: .
|
||||
restart: always
|
||||
depends_on:
|
||||
- db
|
||||
env_file:
|
||||
- path: ./default.env
|
||||
required: true
|
||||
- path: ./override.env
|
||||
required: false
|
||||
environment:
|
||||
INVENTORY_DB_HOST: db
|
||||
INVENTORY_DB_NAME: inventory
|
||||
INVENTORY_DB_USER: inventory
|
||||
INVENTORY_DB_PASSWORD: inventory
|
||||
ports:
|
||||
- name: web
|
||||
target: 8000
|
||||
host_ip: 127.0.0.1
|
||||
published: "8000"
|
||||
protocol: tcp
|
||||
app_protocol: http
|
||||
mode: host
|
||||
volumes:
|
||||
- mediafiles:/media
|
||||
links:
|
||||
- db
|
||||
|
||||
volumes:
|
||||
dbdata:
|
||||
mediafiles:
|
BIN
docs/settings.jpeg
Normal file
BIN
docs/settings.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
8
entrypoint.sh
Normal file
8
entrypoint.sh
Normal file
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
|
||||
cd /usr/src/app
|
||||
|
||||
# poetry run python manage.py collectstatic --noinput
|
||||
poetry run python manage.py migrate --noinput
|
||||
|
||||
exec poetry run gunicorn inventory_project.wsgi -b 0.0.0.0:8000
|
|
@ -3,7 +3,7 @@ from django.shortcuts import redirect
|
|||
from django.contrib import admin
|
||||
from django.conf import settings
|
||||
|
||||
from inventory.models import Item, Documentation
|
||||
from inventory.models import Item, Documentation, Settings
|
||||
|
||||
|
||||
class DocumentationAdmin(admin.ModelAdmin):
|
||||
|
@ -19,6 +19,12 @@ class ItemAdmin(admin.ModelAdmin):
|
|||
readonly_fields = ['created_at', 'changed_at']
|
||||
filter_horizontal = ('tags', 'documentation')
|
||||
|
||||
def get_exclude(self, request, obj=None):
|
||||
s = Settings.objects.first()
|
||||
if (s.track_amount):
|
||||
return self.exclude
|
||||
return (self.exclude or tuple()) + ('count', 'low_count')
|
||||
|
||||
def view_on_site(self, obj):
|
||||
url = reverse('item-detail', kwargs={'pk': obj.id})
|
||||
return settings.SERVER_URL + url
|
||||
|
|
BIN
inventory/locale/de/LC_MESSAGES/django.mo
Normal file
BIN
inventory/locale/de/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
629
inventory/locale/de/LC_MESSAGES/django.po
Normal file
629
inventory/locale/de/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,629 @@
|
|||
# Translation for Inventory management system
|
||||
# Copyright (C) 2025 Johannes Schriewer
|
||||
# This file is distributed under the same license as the Inventory package.
|
||||
# Johannes Schriewer <hallo@dunkelstern.de>, 2025
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: 1.0\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-01-11 03:20+0100\n"
|
||||
"PO-Revision-Date: 2025-01-07 23:00+0100\n"
|
||||
"Last-Translator: Johannes Schriewer <hallo@dunkelstern.de>\n"
|
||||
"Language: German\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: .\inventory\models\area.py:8 .\inventory\models\box.py:10
|
||||
#: .\inventory\models\distributor.py:6 .\inventory\models\documentation.py:6
|
||||
#: .\inventory\models\form_factor.py:6 .\inventory\models\item.py:8
|
||||
#: .\inventory\models\layout.py:6 .\inventory\models\manufacturer.py:6
|
||||
#: .\inventory\models\tag.py:6 .\inventory\models\workshop.py:8
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:26
|
||||
#: .\inventory\templates\inventory\item_detail.html:27
|
||||
#: .\inventory\templates\inventory\item_list.html:7
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:26
|
||||
msgid "Name"
|
||||
msgstr "Name"
|
||||
|
||||
#: .\inventory\models\area.py:9 .\inventory\models\box.py:11
|
||||
#: .\inventory\models\distributor.py:7 .\inventory\models\form_factor.py:7
|
||||
#: .\inventory\models\item.py:9 .\inventory\models\layout.py:7
|
||||
#: .\inventory\models\manufacturer.py:7 .\inventory\models\tag.py:7
|
||||
#: .\inventory\models\workshop.py:9
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:30
|
||||
#: .\inventory\templates\inventory\item_detail.html:31
|
||||
#: .\inventory\templates\inventory\item_list.html:9
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:30
|
||||
msgid "Description"
|
||||
msgstr "Beschreibung"
|
||||
|
||||
#: .\inventory\models\area.py:11
|
||||
msgid "Show sub area"
|
||||
msgstr "Unterbereiche anzeigen"
|
||||
|
||||
#: .\inventory\models\area.py:13
|
||||
msgid "Allow sub-areas to be defined in this area"
|
||||
msgstr "Erlaube Unterbereiche in diesem Bereich"
|
||||
|
||||
#: .\inventory\models\area.py:15 .\inventory\models\box.py:14
|
||||
#: .\inventory\models\distributor.py:16 .\inventory\models\documentation.py:7
|
||||
#: .\inventory\models\form_factor.py:13 .\inventory\models\item.py:76
|
||||
#: .\inventory\models\layout.py:8 .\inventory\models\manufacturer.py:13
|
||||
#: .\inventory\models\tag.py:8 .\inventory\models\workshop.py:17
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:89
|
||||
#: .\inventory\templates\inventory\item_detail.html:128
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:57
|
||||
msgid "Created at"
|
||||
msgstr "Erstellt am"
|
||||
|
||||
#: .\inventory\models\area.py:16 .\inventory\models\box.py:15
|
||||
#: .\inventory\models\distributor.py:17 .\inventory\models\documentation.py:8
|
||||
#: .\inventory\models\form_factor.py:14 .\inventory\models\item.py:77
|
||||
#: .\inventory\models\layout.py:9 .\inventory\models\manufacturer.py:14
|
||||
#: .\inventory\models\tag.py:9 .\inventory\models\workshop.py:18
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:91
|
||||
#: .\inventory\templates\inventory\item_detail.html:130
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:59
|
||||
msgid "Changed at"
|
||||
msgstr "Geändert am"
|
||||
|
||||
#: .\inventory\models\area.py:24
|
||||
msgid "Area"
|
||||
msgstr "Bereich"
|
||||
|
||||
#: .\inventory\models\area.py:25
|
||||
#: .\inventory\templates\inventory\area_detail.html:25
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:23
|
||||
msgid "Areas"
|
||||
msgstr "Bereiche"
|
||||
|
||||
#: .\inventory\models\box.py:12 .\inventory\models\distributor.py:14
|
||||
#: .\inventory\models\form_factor.py:11 .\inventory\models\item.py:58
|
||||
#: .\inventory\models\manufacturer.py:11 .\inventory\models\tag.py:17
|
||||
#: .\inventory\models\workshop.py:15 .\inventory\templates\base.html:58
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:77
|
||||
#: .\inventory\templates\inventory\item_detail.html:54
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:45
|
||||
#: .\inventory\templates\inventory\tag_list.html:9
|
||||
msgid "Tags"
|
||||
msgstr "Tags"
|
||||
|
||||
#: .\inventory\models\box.py:35
|
||||
msgid "Box"
|
||||
msgstr "Kisten"
|
||||
|
||||
#: .\inventory\models\box.py:36
|
||||
#: .\inventory\templates\inventory\box-generic.html:7
|
||||
#: .\inventory\templates\inventory\tag_detail.html:43
|
||||
msgid "Boxes"
|
||||
msgstr "Kisten"
|
||||
|
||||
#: .\inventory\models\container.py:9 .\inventory\models\layout.py:18
|
||||
msgid "Layout"
|
||||
msgstr "Layout"
|
||||
|
||||
#: .\inventory\models\container.py:35 .\inventory\models\container.py:45
|
||||
#: .\inventory\models\container.py:51
|
||||
#: .\inventory\templates\inventory\item_list.html:10
|
||||
msgid "Container"
|
||||
msgstr "Behälter"
|
||||
|
||||
#: .\inventory\models\container.py:46
|
||||
#: .\inventory\templates\inventory\area_detail.html:51
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:49
|
||||
msgid "Containers"
|
||||
msgstr "Behälter"
|
||||
|
||||
#: .\inventory\models\container.py:56
|
||||
msgid "Index of compartment in layout"
|
||||
msgstr "Index des Abteils im Layout"
|
||||
|
||||
#: .\inventory\models\distributor.py:9 .\inventory\models\manufacturer.py:9
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:40
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:40
|
||||
msgid "Web link"
|
||||
msgstr "Web link"
|
||||
|
||||
#: .\inventory\models\distributor.py:10
|
||||
#: .\inventory\templates\inventory\item_detail.html:121
|
||||
msgid "Search link"
|
||||
msgstr "Such-link"
|
||||
|
||||
#: .\inventory\models\distributor.py:10
|
||||
msgid "Use {} for search placeholder"
|
||||
msgstr "Benutze {} oder {0} als Platzhalter"
|
||||
|
||||
#: .\inventory\models\distributor.py:11
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:65
|
||||
msgid "Phone"
|
||||
msgstr "Telefon"
|
||||
|
||||
#: .\inventory\models\distributor.py:12
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:71
|
||||
msgid "E-Mail"
|
||||
msgstr "E-Mail"
|
||||
|
||||
#: .\inventory\models\distributor.py:13 .\inventory\models\form_factor.py:9
|
||||
#: .\inventory\models\manufacturer.py:10
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:35
|
||||
#: .\inventory\templates\inventory\distributor_list.html:15
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:35
|
||||
#: .\inventory\templates\inventory\manufacturer_list.html:15
|
||||
msgid "Icon"
|
||||
msgstr "Symbol"
|
||||
|
||||
#: .\inventory\models\distributor.py:24 .\inventory\models\item.py:31
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:6
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:10
|
||||
#: .\inventory\templates\inventory\distributor_list.html:16
|
||||
#: .\inventory\templates\inventory\item_detail.html:96
|
||||
#: .\inventory\templates\inventory\item_list.html:15
|
||||
msgid "Distributor"
|
||||
msgstr "Händler"
|
||||
|
||||
#: .\inventory\models\distributor.py:25 .\inventory\templates\base.html:55
|
||||
#: .\inventory\templates\inventory\distributor_list.html:5
|
||||
#: .\inventory\templates\inventory\distributor_list.html:8
|
||||
#: .\inventory\templates\inventory\tag_detail.html:63
|
||||
msgid "Distributors"
|
||||
msgstr "Händler"
|
||||
|
||||
#: .\inventory\models\documentation.py:17
|
||||
#: .\inventory\models\documentation.py:18 .\inventory\models\item.py:52
|
||||
#: .\inventory\templates\inventory\item_detail.html:135
|
||||
msgid "Documentation"
|
||||
msgstr "Dokumentation"
|
||||
|
||||
#: .\inventory\models\form_factor.py:10
|
||||
#: .\inventory\templates\inventory\cell.html:16
|
||||
#: .\inventory\templates\inventory\item_list.html:25
|
||||
#: .\inventory\templates\inventory\search_result_item.html:6
|
||||
msgid "Datasheet"
|
||||
msgstr "Datenblatt"
|
||||
|
||||
#: .\inventory\models\form_factor.py:23 .\inventory\models\item.py:17
|
||||
#: .\inventory\templates\inventory\item_detail.html:68
|
||||
msgid "Form factor"
|
||||
msgstr "Formfaktor"
|
||||
|
||||
#: .\inventory\models\form_factor.py:24
|
||||
#: .\inventory\templates\inventory\tag_detail.html:121
|
||||
msgid "Form factors"
|
||||
msgstr "Formfaktoren"
|
||||
|
||||
#: .\inventory\models\item.py:11
|
||||
msgid "Size"
|
||||
msgstr "Größe"
|
||||
|
||||
#: .\inventory\models\item.py:13
|
||||
msgid "Number of sub-compartments this item takes up"
|
||||
msgstr "Anzahl der Unterteilungen die dieses Teil benötigt"
|
||||
|
||||
#: .\inventory\models\item.py:24 .\inventory\models\manufacturer.py:21
|
||||
#: .\inventory\templates\inventory\item_detail.html:85
|
||||
#: .\inventory\templates\inventory\item_list.html:12
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:6
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:10
|
||||
#: .\inventory\templates\inventory\manufacturer_list.html:16
|
||||
msgid "Manufacturer"
|
||||
msgstr "Hersteller"
|
||||
|
||||
#: .\inventory\models\item.py:37
|
||||
msgid "Distributor item no."
|
||||
msgstr "Händler Bestellnummer"
|
||||
|
||||
#: .\inventory\models\item.py:43
|
||||
#: .\inventory\templates\inventory\item_detail.html:107
|
||||
msgid "Price"
|
||||
msgstr "Preis"
|
||||
|
||||
#: .\inventory\models\item.py:49
|
||||
msgid "Last ordered on"
|
||||
msgstr "Zuletzt bestellt am"
|
||||
|
||||
#: .\inventory\models\item.py:63
|
||||
msgid "Count"
|
||||
msgstr "Anzahl"
|
||||
|
||||
#: .\inventory\models\item.py:66
|
||||
msgid "Number of parts available"
|
||||
msgstr "Anzahl der verfügbaren Teile"
|
||||
|
||||
#: .\inventory\models\item.py:69
|
||||
#: .\inventory\templates\inventory\item_detail.html:49
|
||||
msgid "Low watermark"
|
||||
msgstr "Bestellgrenze"
|
||||
|
||||
#: .\inventory\models\item.py:72
|
||||
msgid "Low watermark on which to alert ordering more"
|
||||
msgstr "Untere Grenze ab der das Teil nachbestellt werden soll"
|
||||
|
||||
#: .\inventory\models\item.py:75
|
||||
msgid "Custom metadata, used by templates"
|
||||
msgstr "Metadaten die in der Vorlage verwendet werden können"
|
||||
|
||||
#: .\inventory\models\item.py:98
|
||||
msgid "Item"
|
||||
msgstr "Teil"
|
||||
|
||||
#: .\inventory\models\item.py:99
|
||||
#: .\inventory\templates\inventory\box-generic.html:16
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:64
|
||||
#: .\inventory\templates\inventory\tag_detail.html:113
|
||||
msgid "Items"
|
||||
msgstr "Teile"
|
||||
|
||||
#: .\inventory\models\layout.py:10
|
||||
msgid "Data"
|
||||
msgstr "Konfiguration"
|
||||
|
||||
#: .\inventory\models\layout.py:11
|
||||
msgid "Template name"
|
||||
msgstr "Vorlage"
|
||||
|
||||
#: .\inventory\models\layout.py:19
|
||||
msgid "Layouts"
|
||||
msgstr "Layouts"
|
||||
|
||||
#: .\inventory\models\manufacturer.py:22 .\inventory\templates\base.html:52
|
||||
#: .\inventory\templates\inventory\manufacturer_list.html:5
|
||||
#: .\inventory\templates\inventory\manufacturer_list.html:8
|
||||
#: .\inventory\templates\inventory\tag_detail.html:88
|
||||
msgid "Manufacturers"
|
||||
msgstr "Hersteller"
|
||||
|
||||
#: .\inventory\models\settings.py:13
|
||||
msgid "Default container to display when calling the index page"
|
||||
msgstr "Standardbehälter der beim Aufruf der Index-Seite gezeigt werden soll"
|
||||
|
||||
#: .\inventory\models\settings.py:17
|
||||
msgid "Show item count in overview and warn on low watermarks"
|
||||
msgstr "Aktiviere die Mengenangaben und Nachbestellgrenzen"
|
||||
|
||||
#: .\inventory\models\settings.py:19
|
||||
msgid "Currency"
|
||||
msgstr "Währung"
|
||||
|
||||
#: .\inventory\models\settings.py:19
|
||||
msgid "Currency name"
|
||||
msgstr "Name der Währung"
|
||||
|
||||
#: .\inventory\models\settings.py:20
|
||||
msgid "Currency symbol"
|
||||
msgstr "Währungssymbol"
|
||||
|
||||
#: .\inventory\models\settings.py:22
|
||||
msgid "Currency symbol at end"
|
||||
msgstr "Währungssymbol am Ende"
|
||||
|
||||
#: .\inventory\models\settings.py:24
|
||||
msgid "Currency symbol after amount"
|
||||
msgstr "Zeige das Währungssymbol nach der Zahl an"
|
||||
|
||||
#: .\inventory\models\settings.py:28 .\inventory\models\settings.py:31
|
||||
#: .\inventory\models\settings.py:32
|
||||
msgid "Settings"
|
||||
msgstr "Einstellungen"
|
||||
|
||||
#: .\inventory\models\tag.py:16
|
||||
#: .\inventory\templates\inventory\tag_detail.html:5
|
||||
msgid "Tag"
|
||||
msgstr "Tag"
|
||||
|
||||
#: .\inventory\models\workshop.py:11
|
||||
msgid "Show boxes"
|
||||
msgstr "Behälter anzeigen"
|
||||
|
||||
#: .\inventory\models\workshop.py:13
|
||||
msgid "Allow boxes to be defined directly in this workshop"
|
||||
msgstr "Erlaube Behälter in dieser Werkstatt"
|
||||
|
||||
#: .\inventory\models\workshop.py:26 .\inventory\templates\base.html:61
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:5
|
||||
msgid "Workshop"
|
||||
msgstr "Werkstatt"
|
||||
|
||||
#: .\inventory\models\workshop.py:27
|
||||
#: .\inventory\templates\inventory\tag_detail.html:23
|
||||
#: .\inventory\templates\inventory\workshop_list.html:5
|
||||
#: .\inventory\templates\inventory\workshop_list.html:8
|
||||
msgid "Workshops"
|
||||
msgstr "Werkstätten"
|
||||
|
||||
#: .\inventory\templates\base.html:43 .\inventory\templates\base.html:46
|
||||
#: .\inventory\templates\base.html:47
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:45
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:58
|
||||
#: .\inventory\templates\inventory\search.html:5
|
||||
#: .\inventory\templates\inventory\search.html:8
|
||||
#: .\inventory\templates\inventory\search.html:13
|
||||
#: .\inventory\templates\inventory\search.html:14
|
||||
#: .\inventory\templates\inventory\search_result.html:6
|
||||
#: .\inventory\templates\inventory\search_result.html:9
|
||||
#: .\inventory\templates\inventory\search_result.html:14
|
||||
#: .\inventory\templates\inventory\search_result.html:15
|
||||
#: .\inventory\templates\inventory\tag_list.html:43
|
||||
#: .\inventory\templates\inventory\tag_list.html:44
|
||||
msgid "Search"
|
||||
msgstr "Suchen"
|
||||
|
||||
#: .\inventory\templates\base.html:68
|
||||
msgid "Logout"
|
||||
msgstr "Ausloggen"
|
||||
|
||||
#: .\inventory\templates\inventory\area_detail.html:16
|
||||
#: .\inventory\templates\inventory\area_detail.html:36
|
||||
#: .\inventory\templates\inventory\area_detail.html:62
|
||||
#: .\inventory\templates\inventory\box-detail.html:20
|
||||
#: .\inventory\templates\inventory\cell.html:19
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:17
|
||||
#: .\inventory\templates\inventory\distributor_list.html:33
|
||||
#: .\inventory\templates\inventory\item_detail.html:18
|
||||
#: .\inventory\templates\inventory\item_list.html:28
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:17
|
||||
#: .\inventory\templates\inventory\manufacturer_list.html:33
|
||||
#: .\inventory\templates\inventory\tag_detail.html:16
|
||||
#: .\inventory\templates\inventory\tag_detail.html:33
|
||||
#: .\inventory\templates\inventory\tag_detail.html:53
|
||||
#: .\inventory\templates\inventory\tag_detail.html:78
|
||||
#: .\inventory\templates\inventory\tag_detail.html:103
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:16
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:34
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:59
|
||||
#: .\inventory\templates\inventory\workshop_list.html:21
|
||||
msgid "Edit"
|
||||
msgstr "Bearbeiten"
|
||||
|
||||
#: .\inventory\templates\inventory\area_detail.html:41
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:39
|
||||
msgid "No areas defined"
|
||||
msgstr "Keine Bereiche definiert"
|
||||
|
||||
#: .\inventory\templates\inventory\area_detail.html:47
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:45
|
||||
msgid "Create new area..."
|
||||
msgstr "Neuen Bereich anlegen..."
|
||||
|
||||
#: .\inventory\templates\inventory\area_detail.html:67
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:64
|
||||
msgid "No containers defined"
|
||||
msgstr "Keine Behälter definiert..."
|
||||
|
||||
#: .\inventory\templates\inventory\area_detail.html:73
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:70
|
||||
msgid "Create new container..."
|
||||
msgstr "Neuen Behälter anlegen..."
|
||||
|
||||
#: .\inventory\templates\inventory\box-detail.html:12
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:9
|
||||
#: .\inventory\templates\inventory\item_detail.html:10
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:9
|
||||
#: .\inventory\templates\inventory\tag_detail.html:8
|
||||
#: .\inventory\templates\inventory\workshop_detail.html:8
|
||||
msgid "Back"
|
||||
msgstr "Zurück"
|
||||
|
||||
#: .\inventory\templates\inventory\cell.html:21
|
||||
msgid "Details"
|
||||
msgstr "Details"
|
||||
|
||||
#: .\inventory\templates\inventory\cell.html:26
|
||||
#: .\inventory\templates\inventory\item_detail.html:42
|
||||
msgid "Low stock"
|
||||
msgstr "Wenig Vorrat"
|
||||
|
||||
#: .\inventory\templates\inventory\cell.html:43
|
||||
msgid "New item..."
|
||||
msgstr "Neues Teil..."
|
||||
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:57
|
||||
msgid "Search query"
|
||||
msgstr "Suchanfrage"
|
||||
|
||||
#: .\inventory\templates\inventory\distributor_detail.html:83
|
||||
#: .\inventory\templates\inventory\item_detail.html:60
|
||||
#: .\inventory\templates\inventory\manufacturer_detail.html:51
|
||||
#: .\inventory\templates\inventory\tag_list.html:50
|
||||
msgid "No tags"
|
||||
msgstr "Keine Tags"
|
||||
|
||||
#: .\inventory\templates\inventory\distributor_list.html:8
|
||||
#: .\inventory\templates\inventory\tag_list.html:9
|
||||
#: .\inventory\templates\inventory\workshop_list.html:8
|
||||
#: .\inventory\templates\registration\login.html:5
|
||||
msgid "Inventory management"
|
||||
msgstr "Inventarverwaltung"
|
||||
|
||||
#: .\inventory\templates\inventory\distributor_list.html:43
|
||||
msgid "Create new distributor..."
|
||||
msgstr "Neuen Händler anlegen..."
|
||||
|
||||
#: .\inventory\templates\inventory\item_detail.html:36
|
||||
#: .\inventory\templates\inventory\item_detail.html:45
|
||||
msgid "Amount"
|
||||
msgstr "Anzahl"
|
||||
|
||||
#: .\inventory\templates\inventory\item_detail.html:46
|
||||
msgid "Update"
|
||||
msgstr "Speichern"
|
||||
|
||||
#: .\inventory\templates\inventory\item_detail.html:108
|
||||
msgid "Sum"
|
||||
msgstr "Summe"
|
||||
|
||||
#: .\inventory\templates\inventory\item_detail.html:114
|
||||
msgid "Last ordered"
|
||||
msgstr "Zuletzt bestellt"
|
||||
|
||||
#: .\inventory\templates\inventory\manufacturer_list.html:8
|
||||
msgid "Inventory Management"
|
||||
msgstr "Inventarverwaltung"
|
||||
|
||||
#: .\inventory\templates\inventory\manufacturer_list.html:42
|
||||
msgid "Create new manufacturer..."
|
||||
msgstr "Neuen Hersteller anlegen..."
|
||||
|
||||
#: .\inventory\templates\inventory\onboarding.html:5
|
||||
#: .\inventory\templates\inventory\onboarding.html:8
|
||||
#: .\inventory\templates\inventory\onboarding_success.html:5
|
||||
#: .\inventory\templates\inventory\onboarding_success.html:8
|
||||
msgid "Inventory Setup"
|
||||
msgstr "Inventarverwaltung Setup"
|
||||
|
||||
#: .\inventory\templates\inventory\onboarding.html:12
|
||||
msgid "Welcome to the Inventory Management setup"
|
||||
msgstr "Willkommen zur Einrichtung der Inventarverwaltung"
|
||||
|
||||
#: .\inventory\templates\inventory\onboarding.html:15
|
||||
msgid ""
|
||||
"\n"
|
||||
" Currently no admin user is defined in the database.\n"
|
||||
" To use the inventory management system you need at least one admin "
|
||||
"user...\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Aktuell ist kein Admin Benutzer in der Datenbank angelegt.\n"
|
||||
" Um die Inventarverwaltung nutzen zu können muss mindestens\n"
|
||||
" ein Administrator angelegt werden...\n"
|
||||
" "
|
||||
|
||||
#: .\inventory\templates\inventory\onboarding.html:22
|
||||
msgid ""
|
||||
"\n"
|
||||
" Please verify that the following settings are correct and then fill "
|
||||
"out the\n"
|
||||
" form at the end and click\n"
|
||||
" "
|
||||
msgstr ""
|
||||
" Bitte überprüfe die folgenden Einstellungen und klicke dann auf\n"
|
||||
" "
|
||||
|
||||
#: .\inventory\templates\inventory\onboarding.html:26
|
||||
#: .\inventory\templates\inventory\onboarding.html:38
|
||||
#: .\inventory\templates\inventory\onboarding.html:51
|
||||
msgid "Create user"
|
||||
msgstr "Benutzer anlegen"
|
||||
|
||||
#: .\inventory\templates\inventory\onboarding.html:29
|
||||
msgid "Current settings"
|
||||
msgstr "Aktuelle Einstellungen"
|
||||
|
||||
#: .\inventory\templates\inventory\onboarding.html:42
|
||||
msgid ""
|
||||
"\n"
|
||||
" Please correct the errors below.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Bitte die unten angezeigten Fehler korrigieren.\n"
|
||||
" "
|
||||
|
||||
#: .\inventory\templates\inventory\pagination.html:6
|
||||
#: .\inventory\templates\inventory\search_result.html:33
|
||||
msgid "First page"
|
||||
msgstr "Erste Seite"
|
||||
|
||||
#: .\inventory\templates\inventory\pagination.html:7
|
||||
#: .\inventory\templates\inventory\search_result.html:34
|
||||
msgid "Previous page"
|
||||
msgstr "Vorherige Seite"
|
||||
|
||||
#: .\inventory\templates\inventory\pagination.html:13
|
||||
#: .\inventory\templates\inventory\search_result.html:38
|
||||
msgid "Next page"
|
||||
msgstr "Nächste Seite"
|
||||
|
||||
#: .\inventory\templates\inventory\pagination.html:14
|
||||
#: .\inventory\templates\inventory\search_result.html:39
|
||||
msgid "Last page"
|
||||
msgstr "Letzte Seite"
|
||||
|
||||
#: .\inventory\templates\inventory\search_result.html:18
|
||||
msgid "Search result for"
|
||||
msgstr "Suchergebnisse für"
|
||||
|
||||
#: .\inventory\templates\inventory\search_result.html:27
|
||||
msgid "Nothing found"
|
||||
msgstr "Nichts gefunden"
|
||||
|
||||
#: .\inventory\templates\inventory\search_result_item.html:11
|
||||
msgid "Contained in"
|
||||
msgstr "Im Behälter"
|
||||
|
||||
#: .\inventory\templates\inventory\set_index.html:10
|
||||
msgid "Currently set as index page"
|
||||
msgstr "Momentan als Index-Seite definiert"
|
||||
|
||||
#: .\inventory\templates\inventory\set_index.html:12
|
||||
msgid "Promote to index page"
|
||||
msgstr "Als Index-Seite definieren"
|
||||
|
||||
#: .\inventory\templates\inventory\tag_list.html:55
|
||||
msgid "Create new tag..."
|
||||
msgstr "Neues Tag anlegen..."
|
||||
|
||||
#: .\inventory\templates\inventory\workshop_list.html:30
|
||||
msgid "Create new workshop..."
|
||||
msgstr "Neue Werkstatt anlegen..."
|
||||
|
||||
#: .\inventory\templates\registration\login.html:5
|
||||
msgid "Login"
|
||||
msgstr "Einloggen"
|
||||
|
||||
#: .\inventory\templates\registration\login.html:11
|
||||
msgid "Your username and password didn't match. Please try again."
|
||||
msgstr ""
|
||||
"Benutzername und Passwort passen nicht zusammen, bitte versuche es noch "
|
||||
"einmal."
|
||||
|
||||
#: .\inventory\templates\registration\login.html:17
|
||||
msgid ""
|
||||
"\n"
|
||||
" Your account doesn't have access to this page. To proceed,\n"
|
||||
" please login with an account that has access.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Dein Zugang hat keinen Zugriff auf diese Seite.\n"
|
||||
" Zum Fortfahren bitte mit einem Zugang einloggen der die\n"
|
||||
" entsprechenden Rechte besitzt!\n"
|
||||
" "
|
||||
|
||||
#: .\inventory\templates\registration\login.html:24
|
||||
msgid ""
|
||||
"\n"
|
||||
" Please login to see this page.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Bitte einloggen um auf diese Seite Zugriff zu bekommen.\n"
|
||||
" "
|
||||
|
||||
#: .\inventory\templates\registration\login.html:49
|
||||
msgid "Lost password?"
|
||||
msgstr "Passwort vergessen?"
|
||||
|
||||
#: .\inventory\views\onboarding.py:14
|
||||
msgid "Username"
|
||||
msgstr "Benutzername"
|
||||
|
||||
#: .\inventory\views\onboarding.py:15
|
||||
msgid "Email"
|
||||
msgstr "E-Mail"
|
||||
|
||||
#: .\inventory\views\onboarding.py:16
|
||||
msgid "Password"
|
||||
msgstr "Passwort"
|
||||
|
||||
#: .\inventory_project\settings.py:130
|
||||
msgid "German"
|
||||
msgstr "Deutsch"
|
||||
|
||||
#: .\inventory_project\settings.py:131
|
||||
msgid "English"
|
||||
msgstr "Englisch"
|
23
inventory/migrations/0006_item_count_item_low_count.py
Normal file
23
inventory/migrations/0006_item_count_item_low_count.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 5.1.4 on 2025-01-07 17:20
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0005_alter_sorting_collation'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='item',
|
||||
name='count',
|
||||
field=models.PositiveIntegerField(default=1, help_text='Number of parts available'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='item',
|
||||
name='low_count',
|
||||
field=models.PositiveIntegerField(default=0, help_text='Low watermark on which to alert ordering more'),
|
||||
),
|
||||
]
|
18
inventory/migrations/0007_settings_track_amount.py
Normal file
18
inventory/migrations/0007_settings_track_amount.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.1.4 on 2025-01-07 17:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0006_item_count_item_low_count'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='settings',
|
||||
name='track_amount',
|
||||
field=models.BooleanField(default=False, help_text='Show item count in overview and warn on low watermarks'),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 5.1.4 on 2025-01-07 18:19
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0007_settings_track_amount'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='settings',
|
||||
name='currency',
|
||||
field=models.CharField(default='Euro', help_text='Currency name', max_length=30),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='settings',
|
||||
name='currency_symbol',
|
||||
field=models.CharField(default='€', help_text='Currency symbol', max_length=20),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='settings',
|
||||
name='currency_symbol_position',
|
||||
field=models.BooleanField(default=True, help_text='Currency symbol after amount'),
|
||||
),
|
||||
]
|
|
@ -1,14 +1,19 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.urls import reverse
|
||||
from django.db import models
|
||||
from .container import Container, CanBeContained
|
||||
|
||||
|
||||
class Area(CanBeContained, Container):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
description = models.CharField(max_length=4096)
|
||||
show_sub_area = models.BooleanField(default=True, help_text="Allow sub-areas to be defined in this area")
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
changed_at = models.DateTimeField(auto_now=True)
|
||||
name = models.CharField(_("Name"), max_length=255, unique=True)
|
||||
description = models.CharField(_("Description"), max_length=4096)
|
||||
show_sub_area = models.BooleanField(
|
||||
_("Show sub area"),
|
||||
default=True,
|
||||
help_text=_("Allow sub-areas to be defined in this area")
|
||||
)
|
||||
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
|
||||
changed_at = models.DateTimeField(_("Changed at"), auto_now=True)
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
|
@ -16,3 +21,5 @@ class Area(CanBeContained, Container):
|
|||
|
||||
class Meta:
|
||||
ordering = ("name", )
|
||||
verbose_name = _("Area")
|
||||
verbose_name_plural = _("Areas")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.urls import reverse
|
||||
from django.utils.text import slugify
|
||||
from django.template.loader import get_template, TemplateDoesNotExist
|
||||
|
@ -6,15 +7,12 @@ from .container import CanBeContained, Container
|
|||
|
||||
|
||||
class Box(CanBeContained, Container):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
description = models.CharField(max_length=4096)
|
||||
tags = models.ManyToManyField('inventory.Tag', blank=True)
|
||||
name = models.CharField(_("Name"), max_length=255, unique=True)
|
||||
description = models.CharField(_("Description"), max_length=4096)
|
||||
tags = models.ManyToManyField('inventory.Tag', verbose_name=_("Tags"), blank=True)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
changed_at = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'Boxes'
|
||||
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
|
||||
changed_at = models.DateTimeField(_("Changed at"), auto_now=True)
|
||||
|
||||
@property
|
||||
def template_name(self):
|
||||
|
@ -34,3 +32,5 @@ class Box(CanBeContained, Container):
|
|||
|
||||
class Meta:
|
||||
ordering = ("name", )
|
||||
verbose_name = _("Box")
|
||||
verbose_name_plural = _("Boxes")
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db import models
|
||||
from django.apps import apps
|
||||
|
||||
|
||||
class Container(models.Model):
|
||||
layout = models.ForeignKey('inventory.Layout', on_delete=models.PROTECT, null=True, blank=True)
|
||||
layout = models.ForeignKey(
|
||||
'inventory.Layout',
|
||||
verbose_name=_("Layout"),
|
||||
on_delete=models.PROTECT,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
|
||||
@property
|
||||
def subclass(self):
|
||||
|
@ -25,7 +32,7 @@ class Container(models.Model):
|
|||
_, obj = self.subclass
|
||||
if obj is not None:
|
||||
return obj.name
|
||||
return 'Container'
|
||||
return _("Container")
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
|
@ -34,15 +41,19 @@ class Container(models.Model):
|
|||
return obj.url
|
||||
return None
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Container")
|
||||
verbose_name_plural = _("Containers")
|
||||
|
||||
class CanBeContained(models.Model):
|
||||
container = models.ForeignKey(
|
||||
'inventory.Container',
|
||||
verbose_name=_("Container"),
|
||||
related_name="%(class)s_related",
|
||||
null=True,
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
index = models.PositiveIntegerField('Index of compartment in layout')
|
||||
index = models.PositiveIntegerField(_("Index of compartment in layout"))
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Distributor(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
description = models.CharField(max_length=4096)
|
||||
name = models.CharField(_("Name"), max_length=255, unique=True)
|
||||
description = models.CharField(_("Description"), max_length=4096)
|
||||
|
||||
web_link = models.URLField(null=True, blank=True)
|
||||
search_link = models.URLField(help_text='Use {} for search placeholder', null=True, blank=True)
|
||||
phone = models.CharField(max_length=128, null=True, blank=True)
|
||||
email = models.EmailField(null=True, blank=True, default=None)
|
||||
icon = models.ImageField(null=True, blank=True)
|
||||
tags = models.ManyToManyField('inventory.Tag', blank=True)
|
||||
web_link = models.URLField(_("Web link"), null=True, blank=True)
|
||||
search_link = models.URLField(_("Search link"), help_text=_("Use {} for search placeholder"), null=True, blank=True)
|
||||
phone = models.CharField(_("Phone"), max_length=128, null=True, blank=True)
|
||||
email = models.EmailField(_("E-Mail"), null=True, blank=True, default=None)
|
||||
icon = models.ImageField(_("Icon"), null=True, blank=True)
|
||||
tags = models.ManyToManyField('inventory.Tag', verbose_name=_("Tags"), blank=True)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
changed_at = models.DateTimeField(auto_now=True)
|
||||
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
|
||||
changed_at = models.DateTimeField(_("Changed at"), auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
ordering = ("name", )
|
||||
ordering = ("name", )
|
||||
verbose_name = _("Distributor")
|
||||
verbose_name_plural = _("Distributors")
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Documentation(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
changed_at = models.DateTimeField(auto_now=True)
|
||||
name = models.CharField(_("Name"), max_length=255, unique=True)
|
||||
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
|
||||
changed_at = models.DateTimeField(_("Changed at"), auto_now=True)
|
||||
|
||||
file = models.FileField()
|
||||
|
||||
|
@ -13,3 +14,5 @@ class Documentation(models.Model):
|
|||
|
||||
class Meta:
|
||||
ordering = ("name", )
|
||||
verbose_name = _("Documentation")
|
||||
verbose_name_plural = _("Documentation")
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db import models
|
||||
|
||||
|
||||
class FormFactor(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True, db_collation="numeric")
|
||||
description = models.CharField(max_length=4096, db_collation="numeric")
|
||||
name = models.CharField(_("Name"), max_length=255, unique=True, db_collation="numeric")
|
||||
description = models.CharField(_("Description"), max_length=4096, db_collation="numeric")
|
||||
|
||||
icon = models.ImageField(null=True, blank=True)
|
||||
datasheet = models.FileField(null=True, blank=True)
|
||||
tags = models.ManyToManyField('inventory.Tag', blank=True)
|
||||
icon = models.ImageField(_("Icon"), null=True, blank=True)
|
||||
datasheet = models.FileField(_("Datasheet"), null=True, blank=True)
|
||||
tags = models.ManyToManyField('inventory.Tag', verbose_name=_("Tags"), blank=True)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
changed_at = models.DateTimeField(auto_now=True)
|
||||
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
|
||||
changed_at = models.DateTimeField(_("Changed at"), auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
items = [self.name]
|
||||
|
@ -19,3 +20,5 @@ class FormFactor(models.Model):
|
|||
|
||||
class Meta:
|
||||
ordering = ("name", )
|
||||
verbose_name = _("Form factor")
|
||||
verbose_name_plural = _("Form factors")
|
||||
|
|
|
@ -1,24 +1,80 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db import models
|
||||
|
||||
from .container import CanBeContained
|
||||
|
||||
|
||||
class Item(CanBeContained):
|
||||
name = models.TextField(max_length=255, db_collation="numeric")
|
||||
description = models.CharField(max_length=4096, db_collation="numeric")
|
||||
size = models.PositiveIntegerField(default=1, help_text="Number of sub-compartments this item takes up")
|
||||
form_factor = models.ForeignKey('inventory.FormFactor', null=True, blank=True, on_delete=models.PROTECT)
|
||||
manufacturer = models.ForeignKey('inventory.Manufacturer', null=True, blank=True, on_delete=models.PROTECT)
|
||||
distributor = models.ForeignKey('inventory.Distributor', null=True, blank=True, on_delete=models.PROTECT)
|
||||
distributor_item_no = models.CharField(max_length=255, null=True, blank=True)
|
||||
price = models.DecimalField(decimal_places=3, max_digits=7, null=True, blank=True)
|
||||
last_ordered_on = models.DateField(null=True, blank=True)
|
||||
documentation = models.ManyToManyField('inventory.Documentation', related_name='items', blank=True)
|
||||
tags = models.ManyToManyField('inventory.Tag', blank=True)
|
||||
name = models.TextField(_("Name"), max_length=255, db_collation="numeric")
|
||||
description = models.CharField(_("Description"), max_length=4096, db_collation="numeric")
|
||||
size = models.PositiveIntegerField(
|
||||
_("Size"),
|
||||
default=1,
|
||||
help_text=_("Number of sub-compartments this item takes up")
|
||||
)
|
||||
form_factor = models.ForeignKey(
|
||||
'inventory.FormFactor',
|
||||
verbose_name=_("Form factor"),
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.PROTECT
|
||||
)
|
||||
manufacturer = models.ForeignKey(
|
||||
'inventory.Manufacturer',
|
||||
verbose_name=_("Manufacturer"),
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.PROTECT
|
||||
)
|
||||
distributor = models.ForeignKey(
|
||||
'inventory.Distributor',
|
||||
verbose_name=_("Distributor"),
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.PROTECT
|
||||
)
|
||||
distributor_item_no = models.CharField(
|
||||
_("Distributor item no."),
|
||||
max_length=255,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
price = models.DecimalField(
|
||||
_("Price"),
|
||||
decimal_places=3,
|
||||
max_digits=7,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
last_ordered_on = models.DateField(_("Last ordered on"), null=True, blank=True)
|
||||
documentation = models.ManyToManyField(
|
||||
'inventory.Documentation',
|
||||
verbose_name=_("Documentation"),
|
||||
related_name='items',
|
||||
blank=True
|
||||
)
|
||||
tags = models.ManyToManyField(
|
||||
'inventory.Tag',
|
||||
verbose_name=_("Tags"),
|
||||
blank=True
|
||||
)
|
||||
|
||||
metadata = models.JSONField('Custom metadata, used by templates', blank=True, null=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
changed_at = models.DateTimeField(auto_now=True)
|
||||
count = models.PositiveIntegerField(
|
||||
_("Count"),
|
||||
default=1,
|
||||
null=False,
|
||||
help_text=_("Number of parts available")
|
||||
)
|
||||
low_count = models.PositiveIntegerField(
|
||||
_("Low watermark"),
|
||||
default=0,
|
||||
null=False,
|
||||
help_text=_("Low watermark on which to alert ordering more")
|
||||
)
|
||||
|
||||
metadata = models.JSONField(_("Custom metadata, used by templates"), blank=True, null=True)
|
||||
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
|
||||
changed_at = models.DateTimeField(_("Changed at"), auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
items = [self.name, self.description]
|
||||
|
@ -32,6 +88,12 @@ class Item(CanBeContained):
|
|||
return list(self.tags.all()) + list(self.form_factor.tags.all())
|
||||
else:
|
||||
return list(self.tags.all())
|
||||
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.count * self.price
|
||||
|
||||
class Meta:
|
||||
ordering = ("name", )
|
||||
verbose_name = _("Item")
|
||||
verbose_name_plural = _("Items")
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Layout(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
description = models.CharField(max_length=4096)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
changed_at = models.DateTimeField(auto_now=True)
|
||||
data = models.JSONField()
|
||||
template_name = models.CharField(max_length=255, null=True, blank=True)
|
||||
name = models.CharField(_("Name"), max_length=255, unique=True)
|
||||
description = models.CharField(_("Description"), max_length=4096)
|
||||
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
|
||||
changed_at = models.DateTimeField(_("Changed at"), auto_now=True)
|
||||
data = models.JSONField(_("Data"))
|
||||
template_name = models.CharField(_("Template name"), max_length=255, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
ordering = ("name", )
|
||||
verbose_name = _("Layout")
|
||||
verbose_name_plural = _("Layouts")
|
||||
|
|
|
@ -1,19 +1,22 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Manufacturer(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
description = models.CharField(max_length=4096, blank=True)
|
||||
name = models.CharField(_("Name"), max_length=255, unique=True)
|
||||
description = models.CharField(_("Description"), max_length=4096, blank=True)
|
||||
|
||||
web_link = models.URLField(null=True, blank=True)
|
||||
icon = models.ImageField(null=True, blank=True)
|
||||
tags = models.ManyToManyField('inventory.Tag', blank=True)
|
||||
web_link = models.URLField(_("Web link"), null=True, blank=True)
|
||||
icon = models.ImageField(_("Icon"), null=True, blank=True)
|
||||
tags = models.ManyToManyField('inventory.Tag', verbose_name=_("Tags"), blank=True)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
changed_at = models.DateTimeField(auto_now=True)
|
||||
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
|
||||
changed_at = models.DateTimeField(_("Changed at"), auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
ordering = ("name", )
|
||||
verbose_name = _("Manufacturer")
|
||||
verbose_name_plural = _("Manufacturers")
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.utils.translation import gettext
|
||||
from django.db import models
|
||||
|
||||
|
||||
|
@ -8,11 +10,23 @@ class Settings(models.Model):
|
|||
default=None,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text='Default container to display when calling the index page'
|
||||
help_text=_("Default container to display when calling the index page")
|
||||
)
|
||||
track_amount = models.BooleanField(
|
||||
default=False,
|
||||
help_text=_("Show item count in overview and warn on low watermarks")
|
||||
)
|
||||
currency = models.CharField(_("Currency"), max_length=30, help_text=_("Currency name"), default="Euro")
|
||||
currency_symbol = models.CharField(_("Currency symbol"), max_length=20, default="€")
|
||||
currency_symbol_position = models.BooleanField(
|
||||
_("Currency symbol at end"),
|
||||
default=True,
|
||||
help_text=_("Currency symbol after amount")
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return 'Settings'
|
||||
return gettext("Settings")
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'Settings'
|
||||
verbose_name = _("Settings")
|
||||
verbose_name_plural = _("Settings")
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Tag(models.Model):
|
||||
name = models.CharField(max_length=255, unique=True, db_collation="numeric")
|
||||
description = models.CharField(max_length=4096, db_collation="numeric")
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
changed_at = models.DateTimeField(auto_now=True)
|
||||
name = models.CharField(_("Name"), max_length=255, unique=True, db_collation="numeric")
|
||||
description = models.CharField(_("Description"), max_length=4096, db_collation="numeric")
|
||||
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
|
||||
changed_at = models.DateTimeField(_("Changed at"), auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
ordering = ['name', 'pk']
|
||||
verbose_name = _("Tag")
|
||||
verbose_name_plural = _("Tags")
|
||||
|
|
|
@ -1,16 +1,21 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
from django.urls import reverse
|
||||
from django.db import models
|
||||
from .container import Container
|
||||
|
||||
|
||||
class Workshop(Container):
|
||||
name = models.CharField(max_length=255, unique=True)
|
||||
description = models.CharField(max_length=4096)
|
||||
show_boxes = models.BooleanField(default=True, help_text="Allow boxes to be defined directly in this workshop")
|
||||
tags = models.ManyToManyField('inventory.Tag', blank=True)
|
||||
name = models.CharField(_("Name"), max_length=255, unique=True)
|
||||
description = models.CharField(_("Description"), max_length=4096)
|
||||
show_boxes = models.BooleanField(
|
||||
_("Show boxes"),
|
||||
default=True,
|
||||
help_text=_("Allow boxes to be defined directly in this workshop")
|
||||
)
|
||||
tags = models.ManyToManyField('inventory.Tag', verbose_name=_("Tags"), blank=True)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
changed_at = models.DateTimeField(auto_now=True)
|
||||
created_at = models.DateTimeField(_("Created at"), auto_now_add=True)
|
||||
changed_at = models.DateTimeField(_("Changed at"), auto_now=True)
|
||||
|
||||
@property
|
||||
def url(self):
|
||||
|
@ -18,3 +23,5 @@ class Workshop(Container):
|
|||
|
||||
class Meta:
|
||||
ordering = ("name", )
|
||||
verbose_name = _("Workshop")
|
||||
verbose_name_plural = _("Workshops")
|
||||
|
|
|
@ -89,6 +89,24 @@ td.disabled {
|
|||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.cell .stock {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
left: 5px;
|
||||
display: inline;
|
||||
font-size: xx-small;
|
||||
color: #808080;
|
||||
line-height: 10px;
|
||||
}
|
||||
|
||||
.cell .stock .icon {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin: 0 2px 0 0;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.cell .form_factor {
|
||||
position: absolute;
|
||||
display: inline;
|
||||
|
@ -153,7 +171,13 @@ td.disabled {
|
|||
top: 3px;
|
||||
right: 7px;
|
||||
}
|
||||
|
||||
|
||||
.cell .stock {
|
||||
font-size: 75%;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
}
|
||||
|
||||
.cell .price {
|
||||
font-size: 75%;
|
||||
bottom: 3px;
|
||||
|
|
|
@ -26,6 +26,11 @@ table.attribute-list th {
|
|||
border-style: solid;
|
||||
}
|
||||
|
||||
table.attribute-list .small {
|
||||
color: #808080;
|
||||
font-size: xx-small;
|
||||
}
|
||||
|
||||
table.box {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
@ -158,8 +163,7 @@ img.icon {
|
|||
margin-right: 10px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
img.logo {
|
||||
|
@ -277,9 +281,6 @@ table.list thead th {
|
|||
padding: 10px;
|
||||
}
|
||||
|
||||
table.list tbody tr {
|
||||
}
|
||||
|
||||
table.list tbody td {
|
||||
border-bottom: 1px solid black;
|
||||
padding: 10px;
|
||||
|
@ -322,3 +323,26 @@ table.list tbody td {
|
|||
font-weight: normal;
|
||||
color: #808080;
|
||||
}
|
||||
|
||||
div.icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.warning-icon {
|
||||
background-color: #c00000;
|
||||
-webkit-mask: url(/static/inventory/img/warn.svg) no-repeat center;
|
||||
mask: url(/static/inventory/img/warn.svg) no-repeat center;
|
||||
}
|
||||
|
||||
form label {
|
||||
display: inline-block;
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: bold;
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
}
|
1
inventory/static/inventory/img/warn.svg
Normal file
1
inventory/static/inventory/img/warn.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
|
After Width: | Height: | Size: 542 B |
|
@ -1,6 +1,8 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html lang="{{ LANGUAGE_CODE }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
|
||||
|
@ -38,32 +40,32 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
<a href="{% url 'search' %}" onclick="showSearch(event)"><img class="icon" title="Search" src="{% static "inventory/img/search.svg" %}"></a>
|
||||
<a href="{% url 'search' %}" onclick="showSearch(event)"><img class="icon" title="{% translate 'Search' %}" src="{% static "inventory/img/search.svg" %}"></a>
|
||||
<div id="search-container">
|
||||
<form action="{% url 'search' %}" method="get">
|
||||
<input name="q" id="search" type="text" placeholder="Search">
|
||||
<button type="submit">Search</button>
|
||||
<input name="q" id="search" type="text" placeholder="{% translate 'Search' %}">
|
||||
<button type="submit">{% translate 'Search' %}</button>
|
||||
</form>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'manufacturer-list' %}"><img class="icon" title="Manufacturers" src="{% static "inventory/img/manufacturer.svg" %}"></a>
|
||||
<a href="{% url 'manufacturer-list' %}"><img class="icon" title="{% translate 'Manufacturers' %}" src="{% static "inventory/img/manufacturer.svg" %}"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'distributor-list' %}"><img class="icon" title="Distributors" src="{% static "inventory/img/distributor.svg" %}"></a>
|
||||
<a href="{% url 'distributor-list' %}"><img class="icon" title="{% translate 'Distributors' %}" src="{% static "inventory/img/distributor.svg" %}"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'tag-list' %}"><img class="icon" title="Tags" src="{% static "inventory/img/tags.svg" %}"></a>
|
||||
<a href="{% url 'tag-list' %}"><img class="icon" title="{% translate 'Tags' %}" src="{% static "inventory/img/tags.svg" %}"></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'index' %}"><img class="icon" title="Workshops" src="{% static "inventory/img/workshop.svg" %}"></a>
|
||||
<a href="{% url 'index' %}"><img class="icon" title="{% translate 'Workshop' %}" src="{% static "inventory/img/workshop.svg" %}"></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<form method="POST" action="{% url "logout" %}">
|
||||
{% csrf_token %}
|
||||
<button><img class="icon" src="{% static "inventory/img/logout.svg" %}"> Logout</button>
|
||||
<button type="submit"><img class="icon" src="{% static "inventory/img/logout.svg" %}" title="{% translate 'Logout' %}"> {% translate 'Logout' %}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</nav>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{{ area.name }}{% endblock %}
|
||||
|
||||
|
@ -12,7 +13,7 @@
|
|||
{% block header_icons %}
|
||||
{% if user.is_staff %}
|
||||
<li>
|
||||
<a href="{% url "admin:inventory_area_change" object_id=area.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||
<a href="{% url "admin:inventory_area_change" object_id=area.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}" title="{% translate 'Edit' %}"></a>
|
||||
</li>
|
||||
{% include 'inventory/set_index.html' with item=area is_index=is_index %}
|
||||
{% endif %}
|
||||
|
@ -21,7 +22,7 @@
|
|||
{% block content %}
|
||||
|
||||
{% if area.show_sub_area %}
|
||||
<h3>Areas</h3>
|
||||
<h3>{% translate 'Areas' %}</h3>
|
||||
|
||||
<table class="list">
|
||||
<tbody>
|
||||
|
@ -32,22 +33,22 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_area_change" object_id=a.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_area_change" object_id=a.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td>No areas defined</td></tr>
|
||||
<tr><td>{% translate 'No areas defined' %}</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% if user.is_staff %}
|
||||
<p><a href="{% url "admin:inventory_area_add" %}?container={{ area.id }}&index=0">Create new area...</a></p>
|
||||
<p><a href="{% url "admin:inventory_area_add" %}?container={{ area.id }}&index=0">{% translate 'Create new area...' %}</a></p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<h3>Containers</h3>
|
||||
<h3>{% translate 'Containers' %}</h3>
|
||||
|
||||
<table class="list">
|
||||
<tbody>
|
||||
|
@ -58,17 +59,17 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_box_change" object_id=box.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_box_change" object_id=box.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td>No containers defined</td></tr>
|
||||
<tr><td>{% translate 'No containers defined' %}</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% if user.is_staff %}
|
||||
<p><a href="{% url "admin:inventory_box_add" %}?container={{ area.id }}&index=0">Create new container...</a></p>
|
||||
<p><a href="{% url "admin:inventory_box_add" %}?container={{ area.id }}&index=0">{% translate 'Create new container...' %}</a></p>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -1,5 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static "inventory/css/cell.css" %}">
|
||||
|
@ -8,7 +9,7 @@
|
|||
{% block title %}{{ object.name }}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
<a href="{{ object.container_url }}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
||||
<a href="{{ object.container_url }}"><img class="icon" src="{% static "inventory/img/back.svg" %}" title="{% translate 'Back' %}"></a>
|
||||
{{ object.name }}
|
||||
<span class="small">{{ object.description}}</span></h2>
|
||||
{% endblock %}
|
||||
|
@ -16,7 +17,7 @@
|
|||
{% block header_icons %}
|
||||
{% if user.is_staff %}
|
||||
<li>
|
||||
<a href="{% url "admin:inventory_box_change" object_id=object.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||
<a href="{% url "admin:inventory_box_change" object_id=object.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}" title="{% translate 'Edit' %}"></a>
|
||||
</li>
|
||||
{% include 'inventory/set_index.html' with item=object is_index=is_index %}
|
||||
{% endif %}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
{% extends "inventory/box-detail.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if box.box_related.exists %}
|
||||
<h3>Boxes</h3>
|
||||
<h3>{% translate 'Boxes' %}</h3>
|
||||
<ul>
|
||||
{% for b in box.box_related.all %}
|
||||
<li><a href="{% url 'box-detail' b.id %}" title="{{ b.description }}">{{ b.name }} ({{ b.layout.name }})</a></li>
|
||||
|
@ -12,7 +13,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if box.item_related.exists %}
|
||||
<h3>Items</h3>
|
||||
<h3>{% translate 'Items' %}</h3>
|
||||
<ul>
|
||||
{% for item in box.item_related.all %}
|
||||
<li {% if hilight == item.id %}class="hilighted"{% endif %}><a href="{% url 'item-detail' item.id %}" title="{{ item.description }}">{{ item.name }}</a></li>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<td rowspan="2">
|
||||
<div class="compartments-vertical">
|
||||
{% for compartment in layouted.0.0 %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -17,7 +17,7 @@
|
|||
<td class="triple-height" colspan="3">
|
||||
<div class="compartments">
|
||||
{% for compartment in layouted.0.1 %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -27,7 +27,7 @@
|
|||
<td class="triple-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<td>
|
||||
<div class="compartments">
|
||||
{% for compartment in layouted.0 %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -20,7 +20,7 @@
|
|||
<td rowspan="2">
|
||||
<div class="compartments-vertical">
|
||||
{% for compartment in layouted.1.0 %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -40,7 +40,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<td>
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -27,7 +27,7 @@
|
|||
<td>
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -42,7 +42,7 @@
|
|||
<td>
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -27,7 +27,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -42,7 +42,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<td>
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -27,7 +27,7 @@
|
|||
<td>
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -42,7 +42,7 @@
|
|||
<td>
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<td>
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<td>
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<td>
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -21,7 +21,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -32,7 +32,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -45,7 +45,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -21,7 +21,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -32,7 +32,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
@ -45,7 +45,7 @@
|
|||
<td class="double-height">
|
||||
<div class="compartments">
|
||||
{% for compartment in item %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight %}
|
||||
{% include "inventory/cell.html" with item=compartment hilight=hilight settings=settings %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load admin_urls %}
|
||||
{% load currency %}
|
||||
|
||||
<div class="cell{% if hilight == item.id %} search-hilight{% endif %}">
|
||||
{% if item.name %}
|
||||
{% if item.metadata.package %}
|
||||
|
@ -10,16 +13,23 @@
|
|||
</a>
|
||||
<div class="cell-buttons">
|
||||
{% if item.documentation.all %}
|
||||
<a class="datasheet" href="{{ item.documentation.all.0.file.url }}"><img class="icon" src="{% static "inventory/img/datasheet.svg" %}"></a>
|
||||
<a class="datasheet" href="{{ item.documentation.all.0.file.url }}"><img class="icon" src="{% static "inventory/img/datasheet.svg" %}" title="{% translate 'Datasheet' %}"></a>
|
||||
{% endif %}
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_item_change" object_id=item.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_item_change" object_id=item.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
<a class="details" href="{% url "item-detail" pk=item.pk %}"><img class="icon" src="{% static "inventory/img/detail.svg" %}"></a>
|
||||
<a class="details" href="{% url "item-detail" pk=item.pk %}"><img class="icon" src="{% static "inventory/img/detail.svg" %}" title="{% translate 'Details' %}"></a>
|
||||
</div>
|
||||
|
||||
{% if settings.track_amount %}
|
||||
{% if item.count <= item.low_count %}
|
||||
<div class="stock"><div class="icon warning-icon" title="{% translate 'Low stock' %}"></div>{{ item.count }}</div>
|
||||
{% else %}
|
||||
<div class="stock">{{ item.count }}</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if item.price %}
|
||||
<div class="price">{{ item.price | floatformat:2 }} €</div>
|
||||
<div class="price">{{ item.price | currency:"short" }}</div>
|
||||
{% endif %}
|
||||
{% if item.form_factor %}
|
||||
<div class="form_factor">{{ item.form_factor.name }}</div>
|
||||
|
@ -30,7 +40,7 @@
|
|||
{% else %}
|
||||
<div class="cell-buttons">
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_item_add" %}?index={{ item.index }}&container={{ item.container_id }}"><img class="icon" src="{% static "inventory/img/add.svg" %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_item_add" %}?index={{ item.index }}&container={{ item.container_id }}"><img class="icon" src="{% static "inventory/img/add.svg" %}" title="{% translate 'New item...' %}"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load formatstring %}
|
||||
|
||||
{% block title %}Distributor: {{ distributor }}{% endblock %}
|
||||
{% block title %}{% translate 'Distributor' %}: {{ distributor }}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
<a href="{% url 'distributor-list' %}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
||||
Distributor: {{ distributor.name }}
|
||||
<a href="{% url 'distributor-list' %}"><img class="icon" src="{% static "inventory/img/back.svg" %}" title="{% translate 'Back' %}"></a>
|
||||
{% translate 'Distributor' %}: {{ distributor.name }}
|
||||
<span class="small">{{ distributor.description }}</span></h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block header_icons %}
|
||||
{% if user.is_staff %}
|
||||
<li>
|
||||
<a href="{% url "admin:inventory_distributor_change" object_id=distributor.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||
<a href="{% url "admin:inventory_distributor_change" object_id=distributor.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}" title="{% translate 'Edit' %}"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
@ -22,26 +23,26 @@
|
|||
<table class="attribute-list">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>{% translate 'Name' %}</th>
|
||||
<td>{{ distributor.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th>{% translate 'Description' %}</th>
|
||||
<td>{{ distributor.description }}</td>
|
||||
</tr>
|
||||
{% if distributor.icon %}
|
||||
<tr>
|
||||
<th>Icon</th>
|
||||
<th>{% translate 'Icon' %}</th>
|
||||
<td><img src="{{ distributor.icon.url }}" title="{{ distributor.name }}" style="max-height: 20px;"></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th>Link</th>
|
||||
<th>{% translate 'Web link' %}</th>
|
||||
<td>{% if distributor.web_link %}<a href="{{ distributor.web_link }}" title="{{ distributor.name }}">{{ distributor.web_link }}{% else %}-{% endif %}</td>
|
||||
</tr>
|
||||
{% if distributor.search_link %}
|
||||
<tr>
|
||||
<th>Search</th>
|
||||
<th>{% translate 'Search' %}</th>
|
||||
<td>
|
||||
<script>
|
||||
function distro_search(e) {
|
||||
|
@ -53,42 +54,41 @@
|
|||
}
|
||||
</script>
|
||||
<form>
|
||||
<label for="distributor_search">Search query:</label>
|
||||
<input name="distributor_search" id="distributor_search" type="text">
|
||||
<button onclick="distro_search(event)" type="submit">Search</button>
|
||||
<input name="distributor_search" id="distributor_search" type="text" placeholder="{% translate 'Search query' %}">
|
||||
<button onclick="distro_search(event)" type="submit">{% translate 'Search' %}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if distributor.phone %}
|
||||
<tr>
|
||||
<th>Phone</th>
|
||||
<th>{% translate 'Phone' %}</th>
|
||||
<td>{{ distributor.phone }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% if distributor.email %}
|
||||
<tr>
|
||||
<th>Email</th>
|
||||
<th>{% translate 'E-Mail' %}</th>
|
||||
<td><a href="mailto:{{ distributor.email }}">{{ distributor.email }}</a></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr>
|
||||
<th>Tags</th>
|
||||
<th>{% translate 'Tags' %}</th>
|
||||
<td>
|
||||
<ul class="tag-list">
|
||||
{% for tag in distributor.tags.all %}
|
||||
<li><a href="{% url 'tag-detail' tag.id %}" title="{{ tag.name }}">{{ tag.name }}</a></li>
|
||||
{% empty %}
|
||||
No tags
|
||||
{% translate 'No tags' %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><th>Created at</th><td>{{ distributor.created_at }}</td></tr>
|
||||
<tr><th>{% translate 'Created at' %}</th><td>{{ distributor.created_at }}</td></tr>
|
||||
{% if distributor.created_at != distributor.changed_at %}
|
||||
<tr><th>Last change</th><td>{{ distributor.changed_at }}</td></tr>
|
||||
<tr><th>{% translate 'Changed at' %}</th><td>{{ distributor.changed_at }}</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}Distributors{% endblock %}
|
||||
{% block title %}{% translate 'Distributors' %}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
Inventory management - Distributors
|
||||
{% translate 'Inventory management' %} - {% translate 'Distributors' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<table class="list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="icon">Logo</th>
|
||||
<th>Distributor</th>
|
||||
<th class="icon">{% translate 'Icon' %}</th>
|
||||
<th>{% translate 'Distributor' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -29,7 +30,7 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_distributor_change" object_id=distributor.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_distributor_change" object_id=distributor.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -39,7 +40,7 @@
|
|||
|
||||
|
||||
{% if user.is_staff %}
|
||||
<p><a href="{% url "admin:inventory_distributor_add" %}?layout=1">Create new distributor...</a></p>
|
||||
<p><a href="{% url "admin:inventory_distributor_add" %}?layout=1">{% translate 'Create new distributor...' %}</a></p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load formatstring %}
|
||||
{% load currency %}
|
||||
|
||||
{% block title %}{{ item }}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
<a href="{{ item.container_url }}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
||||
<a href="{{ item.container_url }}"><img class="icon" src="{% static "inventory/img/back.svg" %}" title="{% translate 'Back' %}"></a>
|
||||
{{ item.name }}
|
||||
<span class="small">{{ item.description }}{% if item.form_factor %}, {{ item.form_factor }}{% endif %}</span></h2>
|
||||
{% endblock %}
|
||||
|
@ -13,7 +15,7 @@
|
|||
{% block header_icons %}
|
||||
{% if user.is_staff %}
|
||||
<li>
|
||||
<a href="{% url "admin:inventory_item_change" object_id=item.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||
<a href="{% url "admin:inventory_item_change" object_id=item.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}" title="{% translate 'Edit' %}"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
@ -22,21 +24,40 @@
|
|||
<table class="attribute-list">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>{% translate 'Name' %}</th>
|
||||
<td>{{ item.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th>{% translate 'Description' %}</th>
|
||||
<td>{{ item.description }}</td>
|
||||
</tr>
|
||||
{% if settings.track_amount %}
|
||||
<tr>
|
||||
<th>Tags</th>
|
||||
<th>{% translate 'Amount' %}</th>
|
||||
<td>
|
||||
<form method="post" action="{% url 'item-detail' item.pk %}">
|
||||
{% csrf_token %}
|
||||
<label for="amount">
|
||||
{% if item.count <= item.low_count %}
|
||||
<div class="icon warning-icon" title="{% translate 'Low stock' %}"></div>
|
||||
{% endif %}
|
||||
</label>
|
||||
<input type="text" name="amount" id="amount" value="{{ item.count }}" placeholder="{% translate 'Amount' %}">
|
||||
<button name="save" type="submit">{% translate 'Update' %}</button>
|
||||
<button name="dec" type="submit">-1</button>
|
||||
</form>
|
||||
<span class="small right">{% translate 'Low watermark' %}: {{ item.low_count }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th>{% translate 'Tags' %}</th>
|
||||
<td>
|
||||
<ul class="tag-list">
|
||||
{% for tag in item.all_tags %}
|
||||
<li><a href="{% url 'tag-detail' tag.id %}" title="{{ tag.name }}">{{ tag.name }}</a></li>
|
||||
{% empty %}
|
||||
No tags
|
||||
{% translate 'No tags' %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
|
@ -44,7 +65,7 @@
|
|||
|
||||
{% if item.form_factor %}
|
||||
<tr>
|
||||
<th>Form factor</th>
|
||||
<th>{% translate 'Form factor' %}</th>
|
||||
<td>
|
||||
{% if item.form_factor.datasheet %}
|
||||
<a href="{% url 'distributor-detail' item.distributor.id %}">
|
||||
|
@ -61,7 +82,7 @@
|
|||
|
||||
{% if item.manufacturer %}
|
||||
<tr>
|
||||
<th>Manufacturer</th>
|
||||
<th>{% translate 'Manufacturer' %}</th>
|
||||
<td>
|
||||
<a href="{% url 'manufacturer-detail' item.manufacturer.id %}">
|
||||
{% if item.manufacturer.icon %}<img src="{{ item.manufacturer.icon.url }}" class="icon">{% endif %}{{ item.manufacturer.name }}
|
||||
|
@ -72,7 +93,7 @@
|
|||
|
||||
{% if item.distributor %}
|
||||
<tr>
|
||||
<th>Distributor</th>
|
||||
<th>{% translate 'Distributor' %}</th>
|
||||
<td>
|
||||
<a href="{% url 'distributor-detail' item.distributor.id %}">
|
||||
{% if item.distributor.icon %}<img src="{{ item.distributor.icon.url }}" class="icon">{% endif %}{{ item.distributor.name }}
|
||||
|
@ -83,35 +104,35 @@
|
|||
|
||||
{% if item.price %}
|
||||
<tr>
|
||||
<th>Price</th>
|
||||
<td>{{ item.price }} Euro</td>
|
||||
<th>{% translate 'Price' %}</th>
|
||||
<td>{{ item.price | currency:"detail" }} <span class="small">{% if settings.track_amount %}({% translate 'Sum' %}: {{ item.value | currency:"long" }}){% endif %}</span></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if item.last_ordered_on %}
|
||||
<tr>
|
||||
<th>Last ordered</th>
|
||||
<th>{% translate 'Last ordered' %}</th>
|
||||
<td>{{ item.last_ordered_on }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if item.distributor_item_no %}
|
||||
<tr>
|
||||
<th>Link</th>
|
||||
<th>{% translate 'Search link' %}</th>
|
||||
<td>
|
||||
<a href="{% formatstring item.distributor.search_link item.distributor_item_no %}">{% formatstring item.distributor.search_link item.distributor_item_no %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr><th>Created at</th><td>{{ item.created_at }}</td></tr>
|
||||
<tr><th>{% translate 'Created at' %}</th><td>{{ item.created_at }}</td></tr>
|
||||
{% if item.created_at != item.changed_at %}
|
||||
<tr><th>Last change</th><td>{{ item.changed_at }}</td></tr>
|
||||
<tr><th>{% translate 'Changed at' %}</th><td>{{ item.changed_at }}</td></tr>
|
||||
{% endif %}
|
||||
|
||||
{% if item.documentation.exists %}
|
||||
<tr>
|
||||
<th>Datasheets</th>
|
||||
<th>{% translate 'Documentation' %}</th>
|
||||
<td>
|
||||
<ul>
|
||||
{% for doc in item.documentation.all %}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
{% load i18n %}
|
||||
{% load static %}
|
||||
|
||||
<table class="list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>{% translate 'Name' %}</th>
|
||||
<th></th>
|
||||
<th>Description</th>
|
||||
<th>Container</th>
|
||||
<th>{% translate 'Description' %}</th>
|
||||
<th>{% translate 'Container' %}</th>
|
||||
{% if show_manufacturer %}
|
||||
<th>Manufacturer</th>
|
||||
<th>{% translate 'Manufacturer' %}</th>
|
||||
{% endif %}
|
||||
{% if show_distributor %}
|
||||
<th>Distributor</th>
|
||||
<th>{% translate 'Distributor' %}</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -21,10 +22,10 @@
|
|||
<td><a href="{% url 'item-detail' item.id %}">{{ item.name }}</a></td>
|
||||
<td class="right">
|
||||
{% if item.documentation.all %}
|
||||
<a class="datasheet" href="{{ item.documentation.all.0.file.url }}"><img class="icon" src="{% static "inventory/img/datasheet.svg" %}"></a>
|
||||
<a class="datasheet" href="{{ item.documentation.all.0.file.url }}"><img class="icon" src="{% static "inventory/img/datasheet.svg" %}" title="{% translate 'Datasheet' %}"></a>
|
||||
{% endif %}
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_item_change" object_id=item.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_item_change" object_id=item.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ item.description }}</td>
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load formatstring %}
|
||||
|
||||
{% block title %}Manufacturer: {{ manufacturer }}{% endblock %}
|
||||
{% block title %}{% translate 'Manufacturer' %}: {{ manufacturer }}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
<a href="{% url 'manufacturer-list' %}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
||||
Manufacturer: {{ manufacturer.name }}
|
||||
<a href="{% url 'manufacturer-list' %}"><img class="icon" src="{% static "inventory/img/back.svg" %}" title="{% translate 'Back' %}"></a>
|
||||
{% translate 'Manufacturer' %}: {{ manufacturer.name }}
|
||||
<span class="small">{{ manufacturer.description }}</span></h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block header_icons %}
|
||||
{% if user.is_staff %}
|
||||
<li>
|
||||
<a href="{% url "admin:inventory_manufacturer_change" object_id=manufacturer.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||
<a href="{% url "admin:inventory_manufacturer_change" object_id=manufacturer.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}" title="{% translate 'Edit' %}"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
@ -22,45 +23,45 @@
|
|||
<table class="attribute-list">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>{% translate 'Name' %}</th>
|
||||
<td>{{ manufacturer.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<th>{% translate 'Description' %}</th>
|
||||
<td>{{ manufacturer.description }}</td>
|
||||
</tr>
|
||||
{% if manufacturer.icon %}
|
||||
<tr>
|
||||
<th>Icon</th>
|
||||
<th>{% translate 'Icon' %}</th>
|
||||
<td><img src="{{ manufacturer.icon.url }}" title="{{ manufacturer.name }}" style="max-height: 20px;"></td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
<tr>
|
||||
<th>Link</th>
|
||||
<th>{% translate 'Web link' %}</th>
|
||||
<td>{% if manufacturer.web_link %}<a href="{{ manufacturer.web_link }}" title="{{ manufacturer.name }}">{{ manufacturer.web_link }}{% else %}-{% endif %}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Tags</th>
|
||||
<th>{% translate 'Tags' %}</th>
|
||||
<td>
|
||||
<ul class="tag-list">
|
||||
{% for tag in manufacturer.tags.all %}
|
||||
<li><a href="{% url 'tag-detail' tag.id %}" title="{{ tag.name }}">{{ tag.name }}</a></li>
|
||||
{% empty %}
|
||||
No tags
|
||||
{% translate 'No tags' %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr><th>Created at</th><td>{{ manufacturer.created_at }}</td></tr>
|
||||
<tr><th>{% translate 'Created at' %}</th><td>{{ manufacturer.created_at }}</td></tr>
|
||||
{% if manufacturer.created_at != manufacturer.changed_at %}
|
||||
<tr><th>Last change</th><td>{{ manufacturer.changed_at }}</td></tr>
|
||||
<tr><th>{% translate 'Changed at' %}</th><td>{{ manufacturer.changed_at }}</td></tr>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Items</h2>
|
||||
<h2>{% translate 'Items' %}</h2>
|
||||
|
||||
{% url 'manufacturer-detail' manufacturer.id as this_url %}
|
||||
{% include "inventory/pagination.html" with url=this_url id="paginator_top" param="item" paginator=items %}
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Manufacturers{% endblock %}
|
||||
{% block title %}{% translate 'Manufacturers' %}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
Inventory management - Manufacturers
|
||||
{% translate 'Inventory Management' %} - {% translate 'Manufacturers' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<table class="list">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Logo</th>
|
||||
<th>Manufacturer</th>
|
||||
<th>{% translate 'Icon' %}</th>
|
||||
<th>{% translate 'Manufacturer' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -29,7 +30,7 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_manufacturer_change" object_id=manufacturer.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_manufacturer_change" object_id=manufacturer.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -38,7 +39,7 @@
|
|||
</table>
|
||||
|
||||
{% if user.is_staff %}
|
||||
<p><a href="{% url "admin:inventory_manufacturer_add" %}?layout=1">Create new manufacturer...</a></p>
|
||||
<p><a href="{% url "admin:inventory_manufacturer_add" %}?layout=1">{% translate 'Create new manufacturer...' %}</a></p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
|
53
inventory/templates/inventory/onboarding.html
Normal file
53
inventory/templates/inventory/onboarding.html
Normal file
|
@ -0,0 +1,53 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate 'Inventory Setup' %}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
{% translate 'Inventory Setup' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{% translate 'Welcome to the Inventory Management setup' %}</h2>
|
||||
|
||||
<p>
|
||||
{% blocktranslate %}
|
||||
Currently no admin user is defined in the database.
|
||||
To use the inventory management system you need at least one admin user...
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{% blocktranslate %}
|
||||
Please verify that the following settings are correct and then fill out the
|
||||
form at the end and click
|
||||
{% endblocktranslate %}
|
||||
<em>{% translate 'Create user' %}</em>
|
||||
</p>
|
||||
|
||||
<h2>{% translate 'Current settings' %}</h2>
|
||||
|
||||
<dl>
|
||||
{% for key, value in settings.items %}
|
||||
<dt>{{ key }}</dt>
|
||||
<dd>{{ value }}</dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
|
||||
<h2>{% translate 'Create user' %}</h2>
|
||||
|
||||
{% if form.errors %}
|
||||
<p>
|
||||
{% blocktranslate %}
|
||||
Please correct the errors below.
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<form action="{% url 'onboarding' %}" method="post">
|
||||
{% csrf_token %}
|
||||
{{ form }}
|
||||
<input type="submit" value="{% translate 'Create user' %}">
|
||||
</form>
|
||||
{% endblock %}
|
35
inventory/templates/inventory/onboarding_success.html
Normal file
35
inventory/templates/inventory/onboarding_success.html
Normal file
|
@ -0,0 +1,35 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% translate 'Inventory Setup' %}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
{% translate 'Inventory Setup' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h2>Onboarding completed!</h2>
|
||||
|
||||
<p>Congratulations, you have successfully setup the Inventory management system</p>
|
||||
|
||||
<p>You may now log in with the password you just set up.</p>
|
||||
|
||||
<form method="post" action="{% url 'login' %}">
|
||||
{% csrf_token %}
|
||||
<table>
|
||||
<tr>
|
||||
<td>{{ form.username.label_tag }}</td>
|
||||
<td>{{ form.username }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ form.password.label_tag }}</td>
|
||||
<td>{{ form.password }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<input type="submit" value="login">
|
||||
<input type="hidden" name="next" value="{{ next }}">
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
|
@ -1,15 +1,16 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
<div class="pagination" id="{{ id }}">
|
||||
{% if paginator.has_previous %}
|
||||
<a href="{{ url }}?{{ param }}_page=1#{{ id }}"><img src="{% static 'inventory/img/first.svg' %}" class="icon" title="First page"></a>
|
||||
<a href="{{ url }}?{{ param }}_page={{ paginator.previous_page_number }}#{{ id }}"><img src="{% static 'inventory/img/previous.svg' %}" class="icon" title="Previous page"></a>
|
||||
<a href="{{ url }}?{{ param }}_page=1#{{ id }}"><img src="{% static 'inventory/img/first.svg' %}" class="icon" title="{% translate 'First page' %}"></a>
|
||||
<a href="{{ url }}?{{ param }}_page={{ paginator.previous_page_number }}#{{ id }}"><img src="{% static 'inventory/img/previous.svg' %}" class="icon" title="{% translate 'Previous page' %}"></a>
|
||||
{% endif %}
|
||||
{% if paginator.paginator.num_pages > 1 %}
|
||||
{{ paginator.number }}/{{ paginator.paginator.num_pages }}
|
||||
{% endif %}
|
||||
{% if paginator.has_next %}
|
||||
<a href="{{ url }}?{{ param }}_page={{ paginator.next_page_number }}#{{ id }}"><img src="{% static 'inventory/img/next.svg' %}" class="icon" title="Next page"></a>
|
||||
<a href="{{ url }}?{{ param }}_page={{ paginator.paginator.num_pages}}#{{ id }}"><img src="{% static 'inventory/img/last.svg' %}" class="icon" title="Last page"></a>
|
||||
<a href="{{ url }}?{{ param }}_page={{ paginator.next_page_number }}#{{ id }}"><img src="{% static 'inventory/img/next.svg' %}" class="icon" title="{% translate 'Next page' %}"></a>
|
||||
<a href="{{ url }}?{{ param }}_page={{ paginator.paginator.num_pages}}#{{ id }}"><img src="{% static 'inventory/img/last.svg' %}" class="icon" title="{% translate 'Last page' %}"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Search{% endblock %}
|
||||
{% block title %}{% translate 'Search' %}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
Search
|
||||
{% translate 'Search' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="get" action="{% url 'search' %}">
|
||||
<input type="text" id="search" name="q">
|
||||
<button type="submit">Search</button>
|
||||
<input type="text" id="search" name="q" placeholder="{% translate 'Search' %}">
|
||||
<button type="submit">{% translate 'Search' %}</button>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -1,20 +1,21 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load hilight %}
|
||||
|
||||
{% block title %}Search{% endblock %}
|
||||
{% block title %}{% translate 'Search' %}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
Search
|
||||
{% translate 'Search' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="get" action="{% url 'search' %}">
|
||||
<input type="text" id="search" name="q" value="{{ q }}">
|
||||
<button type="submit">Search</button>
|
||||
<input type="text" id="search" name="q" value="{{ q }}" placeholder="{% translate 'Search' %}">
|
||||
<button type="submit">{% translate 'Search' %}</button>
|
||||
</form>
|
||||
|
||||
<h2>Search result for '{{ q }}'</h2>
|
||||
<h2>{% translate 'Search result for' %} '{{ q }}'</h2>
|
||||
|
||||
{% for result in results %}
|
||||
<div class="search-result">
|
||||
|
@ -23,19 +24,19 @@
|
|||
<hr>
|
||||
</div>
|
||||
{% empty %}
|
||||
<p>Noting found</p>
|
||||
<p>{% translate 'Nothing found' %}</p>
|
||||
{% endfor %}
|
||||
|
||||
{% if pages > 1 %}
|
||||
<div class="pagination">
|
||||
{% if page > 1 %}
|
||||
<a href="{% url 'search' %}?q={{ q }}&page=1"><img src="{% static 'inventory/img/first.svg' %}" class="icon" title="First page"></a>
|
||||
<a href="{% url 'search' %}?q={{ q }}&page={{ page | add:'-1'}}"><img src="{% static 'inventory/img/previous.svg' %}" class="icon" title="Previous page"></a>
|
||||
<a href="{% url 'search' %}?q={{ q }}&page=1"><img src="{% static 'inventory/img/first.svg' %}" class="icon" title="{% translate 'First page' %}"></a>
|
||||
<a href="{% url 'search' %}?q={{ q }}&page={{ page | add:'-1'}}"><img src="{% static 'inventory/img/previous.svg' %}" class="icon" title="{% translate 'Previous page' %}"></a>
|
||||
{% endif %}
|
||||
{{ page }}/{{ pages }}
|
||||
{% if page < pages %}
|
||||
<a href="{% url 'search' %}?q={{ q }}&page={{ page | add:'1'}}"><img src="{% static 'inventory/img/next.svg' %}" class="icon" title="Next page"></a>
|
||||
<a href="{% url 'search' %}?q={{ q }}&page={{ pages }}"><img src="{% static 'inventory/img/last.svg' %}" class="icon" title="Last page"></a>
|
||||
<a href="{% url 'search' %}?q={{ q }}&page={{ page | add:'1'}}"><img src="{% static 'inventory/img/next.svg' %}" class="icon" title="{% translate 'Next page' %}"></a>
|
||||
<a href="{% url 'search' %}?q={{ q }}&page={{ pages }}"><img src="{% static 'inventory/img/last.svg' %}" class="icon" title="{% translate 'Last page' %}"></a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load hilight %}
|
||||
<p>
|
||||
{% if item.documentation.all %}
|
||||
<a class="datasheet" href="{{ item.documentation.all.0.file.url }}"><img class="icon" src="{% static "inventory/img/datasheet.svg" %}"></a>
|
||||
<a class="datasheet" href="{{ item.documentation.all.0.file.url }}"><img class="icon" src="{% static "inventory/img/datasheet.svg" %}" title="{% translate 'Datasheet' %}"></a>
|
||||
{% endif %}
|
||||
{{ item | hilight:tokens }}
|
||||
<span class="small">{{item.form_factor.name}}</span>
|
||||
</p>
|
||||
<p>Contained in: <a href="{{ item.container_url }}?hilight={{ item.id }}">{{ item.container.display_name }}</a></p>
|
||||
<p>{% translate 'Contained in' %}: <a href="{{ item.container_url }}?hilight={{ item.id }}">{{ item.container.display_name }}</a></p>
|
||||
<ul class="tag-list">
|
||||
{% for tag in item.all_tags %}
|
||||
<li><a href="{% url 'tag-detail' tag.id %}" title="{{ tag.name }}">{{ tag.name }}</a></li>
|
||||
{% empty %}
|
||||
No tags
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
<li>
|
||||
<form method="POST" action="{% url "index" %}">
|
||||
|
@ -6,9 +7,9 @@
|
|||
<input type="hidden" name="index_id" value='{{ item.id }}'>
|
||||
<button class="icon-only-button">
|
||||
{% if is_index %}
|
||||
<img class="icon" src="{% static "inventory/img/star-filled.svg" %}">
|
||||
<img class="icon" src="{% static "inventory/img/star-filled.svg" %}" title="{% translate 'Currently set as index page' %}">
|
||||
{% else %}
|
||||
<img class="icon" src="{% static "inventory/img/star-outline.svg" %}">
|
||||
<img class="icon" src="{% static "inventory/img/star-outline.svg" %}" title="{% translate 'Promote to index page' %}">
|
||||
{% endif %}
|
||||
</button>
|
||||
</form>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Tag {{ tag.name }}{% endblock %}
|
||||
{% block title %}{% translate 'Tag' %} {{ tag.name }}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
<a href="{% url "tag-list" %}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
||||
<a href="{% url "tag-list" %}"><img class="icon" src="{% static "inventory/img/back.svg" %}" title="{% translate 'Back' %}"></a>
|
||||
{{ tag.name }}
|
||||
<span class="small">{{ tag.description}}</span>
|
||||
{% endblock %}
|
||||
|
@ -12,14 +13,14 @@
|
|||
{% block header_icons %}
|
||||
{% if user.is_staff %}
|
||||
<li>
|
||||
<a href="{% url "admin:inventory_tag_change" object_id=tag.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||
<a href="{% url "admin:inventory_tag_change" object_id=tag.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}" title="{% translate 'Edit' %}"></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if tag.workshop_set.count > 0 %}
|
||||
<h3>Workshops</h3>
|
||||
<h3>{% translate 'Workshops' %}</h3>
|
||||
<table class="list">
|
||||
<tbody>
|
||||
{% for workshop in tag.workshop_set.all %}
|
||||
|
@ -29,7 +30,7 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_workshop_change" object_id=workshop.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_workshop_change" object_id=workshop.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -39,7 +40,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if tag.box_set.count > 0 %}
|
||||
<h3>Boxes</h3>
|
||||
<h3>{% translate 'Boxes' %}</h3>
|
||||
<table class="list">
|
||||
<tbody>
|
||||
{% for box in tag.box_set.all %}
|
||||
|
@ -49,7 +50,7 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_workshop_change" object_id=box.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_workshop_change" object_id=box.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -59,7 +60,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if tag.distributor_set.count > 0 %}
|
||||
<h3>Distributors</h3>
|
||||
<h3>{% translate 'Distributors' %}</h3>
|
||||
<table class="list">
|
||||
<tbody>
|
||||
{% for distributor in tag.distributor_set.all %}
|
||||
|
@ -74,7 +75,7 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_workshop_change" object_id=distributor.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_workshop_change" object_id=distributor.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -84,7 +85,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if tag.manufacturer_set.count > 0 %}
|
||||
<h3>Manufacturers</h3>
|
||||
<h3>{% translate 'Manufacturers' %}</h3>
|
||||
<table class="list">
|
||||
<tbody>
|
||||
{% for manufacturer in tag.manufacturer_set.all %}
|
||||
|
@ -99,7 +100,7 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_workshop_change" object_id=manufacturer.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_workshop_change" object_id=manufacturer.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -109,7 +110,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if items %}
|
||||
<h3>Items</h3>
|
||||
<h3>{% translate 'Items' %}</h3>
|
||||
{% url 'tag-detail' tag.id as this_url %}
|
||||
{% include "inventory/pagination.html" with url=this_url id="items_paginator_top" param="item" paginator=items %}
|
||||
{% include "inventory/item_list.html" with items=items show_manufacturer=1 show_distributor=1 %}
|
||||
|
@ -117,7 +118,7 @@
|
|||
{% endif %}
|
||||
|
||||
{% if tag.formfactor_set.count > 0 %}
|
||||
<h3>Form factors</h3>
|
||||
<h3>{% translate 'Form factors' %}</h3>
|
||||
<ul class="compact-list">
|
||||
{% for formfactor in tag.formfactor_set.all %}
|
||||
<li>{{ formfactor.name }}</li>
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load formatstring %}
|
||||
|
||||
{% block title %}Tags{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
Inventory Management - Tags
|
||||
{% translate 'Inventory management' %} - {% translate 'Tags' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -39,19 +40,19 @@
|
|||
}
|
||||
</script>
|
||||
<form>
|
||||
<input type="text" id="tag-search" name="tag-search" placeholder="Search" oninput="updateSearch(event)">
|
||||
<button onclick="updateSearch(event)">Search</button>
|
||||
<input type="text" id="tag-search" name="tag-search" placeholder="{% translate 'Search' %}" oninput="updateSearch(event)">
|
||||
<button onclick="updateSearch(event)">{% translate 'Search' %}</button>
|
||||
</form>
|
||||
<ul class="tag-list" id="tagcloud">
|
||||
{% for tag in object_list %}
|
||||
<li data-search="{{ tag.name | lower }} {{ tag.description | lower }}"><a href="{% url 'tag-detail' tag.id %}" title="{{ tag.name }}">{{ tag.name }}</a></li>
|
||||
{% empty %}
|
||||
No tags
|
||||
{% translate 'No tags' %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
{% if user.is_staff %}
|
||||
<p><a href="{% url "admin:inventory_tag_add" %}?layout=1">Create new tag...</a></p>
|
||||
<p><a href="{% url "admin:inventory_tag_add" %}?layout=1">{% translate 'Create new tag...' %}</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
|
@ -1,10 +1,11 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Workshop {{ workshop.name }}{% endblock %}
|
||||
{% block title %}{% translate 'Workshop' %} {{ workshop.name }}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
<a href="{% url "workshop-list" %}"><img class="icon" src="{% static "inventory/img/back.svg" %}"></a>
|
||||
<a href="{% url "workshop-list" %}"><img class="icon" src="{% static "inventory/img/back.svg" %}" title="{% translate 'Back' %}"></a>
|
||||
{{ workshop.name }}
|
||||
<span class="small">{{ workshop.description}}</span>
|
||||
{% endblock %}
|
||||
|
@ -12,14 +13,14 @@
|
|||
{% block header_icons %}
|
||||
{% if user.is_staff %}
|
||||
<li>
|
||||
<a href="{% url "admin:inventory_workshop_change" object_id=workshop.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}"></a>
|
||||
<a href="{% url "admin:inventory_workshop_change" object_id=workshop.pk %}"><img class="icon" src="{% static "inventory/img/edit.svg" %}" title="{% translate 'Edit' %}"></a>
|
||||
</li>
|
||||
{% include 'inventory/set_index.html' with item=workshop is_index=is_index %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3>Areas</h3>
|
||||
<h3>{% translate 'Areas' %}</h3>
|
||||
|
||||
<table class="list">
|
||||
<tbody>
|
||||
|
@ -30,22 +31,22 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_area_change" object_id=area.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_area_change" object_id=area.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td>No areas defined</td></tr>
|
||||
<tr><td>{% translate 'No areas defined' %}</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% if user.is_staff %}
|
||||
<p><a href="{% url "admin:inventory_area_add" %}?container={{ workshop.id }}&index=0">Create new area...</a></p>
|
||||
<p><a href="{% url "admin:inventory_area_add" %}?container={{ workshop.id }}&index=0">{% translate 'Create new area...' %}</a></p>
|
||||
{% endif %}
|
||||
|
||||
{% if workshop.show_boxes %}
|
||||
<h3>Containers</h3>
|
||||
<h3>{% translate 'Containers' %}</h3>
|
||||
<table class="list">
|
||||
<tbody>
|
||||
{% for box in workshop.box_related.all %}
|
||||
|
@ -55,18 +56,18 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_box_change" object_id=box.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_box_change" object_id=box.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr><td>No containers defined</td></tr>
|
||||
<tr><td>{% translate 'No containers defined' %}</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% if user.is_staff %}
|
||||
<p><a href="{% url "admin:inventory_box_add" %}?container={{ workshop.id }}&index=0">Create new container...</a></p>
|
||||
<p><a href="{% url "admin:inventory_box_add" %}?container={{ workshop.id }}&index=0">{% translate 'Create new container...' %}</a></p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -1,10 +1,11 @@
|
|||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Workshops{% endblock %}
|
||||
{% block title %}{% translate 'Workshops' %}{% endblock %}
|
||||
|
||||
{% block header_bar %}
|
||||
Inventory management - Workshops
|
||||
{% translate 'Inventory management' %} - {% translate 'Workshops' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
@ -17,7 +18,7 @@
|
|||
</td>
|
||||
<td>
|
||||
{% if user.is_staff %}
|
||||
<a class="edit" href="{% url "admin:inventory_workshop_change" object_id=workshop.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}"></a>
|
||||
<a class="edit" href="{% url "admin:inventory_workshop_change" object_id=workshop.pk %}"><img class="icon" src="{% static 'inventory/img/edit.svg' %}" title="{% translate 'Edit' %}"></a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -26,7 +27,7 @@
|
|||
</table>
|
||||
|
||||
{% if user.is_staff %}
|
||||
<p><a href="{% url "admin:inventory_workshop_add" %}?layout=1">Create new workshop...</a></p>
|
||||
<p><a href="{% url "admin:inventory_workshop_add" %}?layout=1">{% translate 'Create new workshop...' %}</a></p>
|
||||
{% endif %}
|
||||
|
||||
|
||||
|
|
|
@ -1,21 +1,30 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n%}
|
||||
|
||||
{% block header_bar %}
|
||||
Inventory management - Login
|
||||
{% translate 'Inventory management' %} - {% translate 'Login' %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if form.errors %}
|
||||
<p>Your username and password didn't match. Please try again.</p>
|
||||
<p>{% blocktranslate %}Your username and password didn't match. Please try again.{% endblocktranslate %}</p>
|
||||
{% endif %}
|
||||
|
||||
{% if next %}
|
||||
{% if user.is_authenticated %}
|
||||
<p>Your account doesn't have access to this page. To proceed,
|
||||
please login with an account that has access.</p>
|
||||
<p>
|
||||
{% blocktranslate %}
|
||||
Your account doesn't have access to this page. To proceed,
|
||||
please login with an account that has access.
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p>Please login to see this page.</p>
|
||||
<p>
|
||||
{% blocktranslate %}
|
||||
Please login to see this page.
|
||||
{% endblocktranslate %}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
@ -37,6 +46,6 @@
|
|||
</form>
|
||||
|
||||
{# Assumes you setup the password_reset view in your URLconf #}
|
||||
<p><a href="{% url 'password_reset' %}">Lost password?</a></p>
|
||||
<p><a href="{% url 'password_reset' %}">{% translate 'Lost password?' %}</a></p>
|
||||
|
||||
{% endblock %}
|
32
inventory/templatetags/currency.py
Normal file
32
inventory/templatetags/currency.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
from django import template
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.formats import number_format
|
||||
|
||||
from inventory.models import Settings
|
||||
|
||||
register = template.Library()
|
||||
s = None
|
||||
|
||||
@register.filter(name='currency')
|
||||
def currency(value, format):
|
||||
global s
|
||||
if s is None:
|
||||
s = Settings.objects.first()
|
||||
value = float(value)
|
||||
|
||||
if format == 'detail':
|
||||
value = number_format(round(value, 3), 3, use_l10n=True)
|
||||
else:
|
||||
value = number_format(round(value, 2), 2, use_l10n=True)
|
||||
|
||||
if format == 'long' or format == 'detail':
|
||||
symbol = s.currency
|
||||
else:
|
||||
symbol = s.currency_symbol
|
||||
|
||||
if s.currency_symbol_position:
|
||||
result = f"{value} {symbol}"
|
||||
else:
|
||||
result = f"{symbol} {value}"
|
||||
|
||||
return mark_safe(result)
|
|
@ -34,7 +34,8 @@ from .views import (
|
|||
ManufacturerView,
|
||||
IndexView,
|
||||
TagView,
|
||||
SearchView
|
||||
SearchView,
|
||||
OnboardingView
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
|
@ -54,5 +55,6 @@ urlpatterns = [
|
|||
path('tags', TagListView.as_view(), name='tag-list'),
|
||||
path('tag/<int:pk>', TagView.as_view(), name='tag-detail'),
|
||||
path('search', SearchView.as_view(), name='search'),
|
||||
path('onboarding', OnboardingView.as_view(), name='onboarding'),
|
||||
path('', IndexView.as_view(), name='index')
|
||||
]
|
||||
|
|
|
@ -7,6 +7,7 @@ from .workshop import WorkshopView, WorkshopListView
|
|||
from .index import IndexView
|
||||
from .tag import TagListView, TagView
|
||||
from .search import SearchView
|
||||
from .onboarding import OnboardingView
|
||||
|
||||
__all__ = [
|
||||
AreaView, AreaListView,
|
||||
|
@ -17,5 +18,6 @@ __all__ = [
|
|||
WorkshopView, WorkshopListView,
|
||||
IndexView,
|
||||
TagView, TagListView,
|
||||
SearchView
|
||||
SearchView,
|
||||
OnboardingView
|
||||
]
|
|
@ -4,7 +4,7 @@ from django.utils.decorators import method_decorator
|
|||
from django.views.generic import ListView, DetailView
|
||||
from django.db.models import QuerySet
|
||||
|
||||
from inventory.models import Box
|
||||
from inventory.models import Box, Settings
|
||||
|
||||
from .utils import CanBeIndexMixin
|
||||
|
||||
|
@ -54,6 +54,7 @@ class BoxView(CanBeIndexMixin, DetailView):
|
|||
context = self.get_context_data(object=self.object)
|
||||
context['layouted'], _ = self.layout(self.object.item_related.all().order_by('index'), self.object.layout.data)
|
||||
context['hilight'] = hilighted
|
||||
context['settings'] = Settings.objects.first()
|
||||
return self.render_to_response(context)
|
||||
|
||||
|
||||
|
|
|
@ -3,14 +3,24 @@ from django.shortcuts import redirect
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import View
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.views import redirect_to_login
|
||||
|
||||
from inventory.models import Settings
|
||||
|
||||
|
||||
@method_decorator(login_required, name='dispatch')
|
||||
@method_decorator(login_required, name='post')
|
||||
class IndexView(View):
|
||||
|
||||
def get(self, request):
|
||||
User = get_user_model()
|
||||
if User.objects.all().count() == 0:
|
||||
# redirect to onboarding
|
||||
return redirect(reverse('onboarding'))
|
||||
if not request.user.is_authenticated:
|
||||
path = request.get_full_path()
|
||||
return redirect_to_login(path, reverse('login'))
|
||||
# check settings for correct starred index page
|
||||
settings = Settings.objects.first()
|
||||
if settings.default_container is not None:
|
||||
return redirect(settings.default_container.url)
|
||||
|
|
|
@ -1,15 +1,30 @@
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views.generic import ListView, DetailView
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from inventory.models import Item
|
||||
from inventory.models import Item, Settings
|
||||
|
||||
|
||||
@method_decorator(login_required, name='dispatch')
|
||||
class ItemView(DetailView):
|
||||
context_object_name = 'item'
|
||||
queryset = Item.objects.all().select_related('container', 'container__layout', 'manufacturer', 'distributor').prefetch_related('documentation')
|
||||
def get_context_data(self, *args, object_list=None, **kwargs):
|
||||
result = super().get_context_data(*args, object_list=object_list, **kwargs)
|
||||
result['settings'] = Settings.objects.first()
|
||||
return result
|
||||
|
||||
def post(self, request, pk):
|
||||
item = get_object_or_404(Item.objects.filter(pk=pk))
|
||||
if request.POST.get('dec', None) is not None:
|
||||
if item.count > 0:
|
||||
item.count -= 1
|
||||
item.save()
|
||||
if request.POST.get('save', None) is not None:
|
||||
item.count = request.POST.get('amount', 0)
|
||||
item.save()
|
||||
return self.get(request)
|
||||
|
||||
@method_decorator(login_required, name='dispatch')
|
||||
class ItemListView(ListView):
|
||||
|
|
60
inventory/views/onboarding.py
Normal file
60
inventory/views/onboarding.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from django.urls import reverse
|
||||
from django.template.response import TemplateResponse
|
||||
from django.shortcuts import redirect
|
||||
from django.views.generic import View
|
||||
from django import forms
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class OnboardingForm(forms.Form):
|
||||
username = forms.CharField(label=_("Username"), max_length=150, required=True)
|
||||
email = forms.EmailField(label=_("Email"), max_length=254, required=True)
|
||||
password = forms.CharField(label=_("Password"), max_length=1024, min_length=8, widget=forms.PasswordInput(), required=True)
|
||||
|
||||
|
||||
class OnboardingView(View):
|
||||
|
||||
def get(self, request):
|
||||
User = get_user_model()
|
||||
if User.objects.all().count() != 0:
|
||||
# redirect to index
|
||||
return redirect(reverse('index'))
|
||||
|
||||
return TemplateResponse(request, "inventory/onboarding.html", {
|
||||
"settings": {
|
||||
"SERVER_URL": settings.SERVER_URL,
|
||||
"DEBUG": settings.DEBUG,
|
||||
"ALLOWED_HOSTS": settings.ALLOWED_HOSTS,
|
||||
"DATABASE_HOST": settings.DATABASES['default']['HOST'],
|
||||
"DATABASE_NAME": settings.DATABASES['default']['NAME'],
|
||||
"DATABASE_USER": settings.DATABASES['default']['USER'],
|
||||
"DATABASE_PASSWORD": settings.DATABASES['default']['PASSWORD'],
|
||||
"LANGUAGE_CODE": settings.LANGUAGE_CODE,
|
||||
"TIME_ZONE": settings.TIME_ZONE,
|
||||
"STATIC_ROOT": settings.STATIC_ROOT,
|
||||
"MEDIA_ROOT": settings.MEDIA_ROOT,
|
||||
"PAGE_SIZE": settings.PAGE_SIZE,
|
||||
},
|
||||
"form": OnboardingForm()
|
||||
})
|
||||
|
||||
def post(self, request):
|
||||
# validate we have everything
|
||||
form = OnboardingForm(request.POST)
|
||||
if form.is_valid():
|
||||
# create superuser
|
||||
User = get_user_model()
|
||||
User.objects.create_superuser(
|
||||
form.cleaned_data['username'],
|
||||
form.cleaned_data['email'],
|
||||
form.cleaned_data['password']
|
||||
)
|
||||
|
||||
# show success screen
|
||||
login_form = AuthenticationForm(data={"username": form.cleaned_data['username'] })
|
||||
return TemplateResponse(request, "inventory/onboarding_success.html", {"form": login_form, "next": reverse('index')})
|
||||
return TemplateResponse(request, "inventory/onboarding.html", {"settings": settings, "form": form})
|
|
@ -10,10 +10,13 @@ For the full list of settings and their values, see
|
|||
https://docs.djangoproject.com/en/3.0/ref/settings/
|
||||
"""
|
||||
|
||||
from typing import List
|
||||
import os
|
||||
import sys
|
||||
import asyncio
|
||||
import socket
|
||||
from urllib.parse import urlparse
|
||||
from uuid import uuid4
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
if sys.platform == 'win32':
|
||||
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
|
||||
|
@ -22,19 +25,34 @@ if sys.platform == 'win32':
|
|||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# Externally visible URL of the server
|
||||
SERVER_URL = "http://127.0.0.1:8000"
|
||||
SERVER_URL = os.environ.get('INVENTORY_EXTERNAL_URL', "http://127.0.0.1:8000")
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'nqo*a(^g$8#0%&+*_7#b_7ybn-znk4#=45_(qy-lq-^v675pqk'
|
||||
SECRET_KEY = os.environ.get('INVENTORY_SECRET_KEY', uuid4().hex)
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
DEBUG = (os.environ.get('INVENTORY_DEBUG', "FALSE") == "TRUE")
|
||||
|
||||
ALLOWED_HOSTS: List[str] = []
|
||||
parsed_url = urlparse(SERVER_URL)
|
||||
ALLOWED_HOSTS: list[str] = [
|
||||
'.localhost',
|
||||
'127.0.0.1',
|
||||
'[::1]',
|
||||
parsed_url.hostname,
|
||||
socket.gethostbyname('localhost')
|
||||
]
|
||||
|
||||
CSRF_TRUSTED_ORIGINS: list[str] = [
|
||||
*[
|
||||
f'{prot}://{host}'
|
||||
for host in os.environ.get("INVENTORY_TRUSTED_ORIGINS", "localhost").split(',')
|
||||
for prot in ('http', 'https')
|
||||
],
|
||||
f'{SERVER_URL}'
|
||||
]
|
||||
|
||||
# Application definition
|
||||
|
||||
|
@ -88,9 +106,10 @@ ASGI_APPLICATION = 'inventory_project.asgi.application'
|
|||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.postgresql',
|
||||
'NAME': 'inventory',
|
||||
'USER': 'inventory',
|
||||
'PASSWORD': 'inventory'
|
||||
'HOST': os.environ.get('INVENTORY_DB_HOST', 'localhost'),
|
||||
'NAME': os.environ.get('INVENTORY_DB_NAME', 'inventory'),
|
||||
'USER': os.environ.get('INVENTORY_DB_USER', 'inventory'),
|
||||
'PASSWORD': os.environ.get('INVENTORY_DB_PASSWORD', 'inventory')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,10 +135,14 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.0/topics/i18n/
|
||||
LANGUAGES = [
|
||||
("de", _("German")),
|
||||
("en", _("English")),
|
||||
]
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGE_CODE = os.environ.get('INVENTORY_LANGUAGE', 'en-us')
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
TIME_ZONE = os.environ.get('INVENTORY_TIMEZONE', 'UTC')
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
|
@ -132,13 +155,13 @@ USE_TZ = True
|
|||
# https://docs.djangoproject.com/en/3.0/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
|
||||
STATIC_ROOT = os.environ.get('INVENTORY_STATIC_FILES', os.path.join(BASE_DIR, 'static'))
|
||||
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||
|
||||
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
SERVE_MEDIA_FILES = DEBUG
|
||||
MEDIA_ROOT = os.environ.get('INVENTORY_MEDIA_FILES', os.path.join(BASE_DIR, 'media'))
|
||||
SERVE_MEDIA_FILES = True
|
||||
|
||||
# Default page size for paginated content
|
||||
PAGE_SIZE = 25
|
||||
PAGE_SIZE = int(os.environ.get('INVENTORY_PAGE_SIZE', "25"))
|
||||
|
|
46
poetry.lock
generated
46
poetry.lock
generated
|
@ -126,22 +126,23 @@ pyflakes = ">=3.2.0,<3.3.0"
|
|||
|
||||
[[package]]
|
||||
name = "gunicorn"
|
||||
version = "20.1.0"
|
||||
version = "23.0.0"
|
||||
description = "WSGI HTTP Server for UNIX"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "gunicorn-20.1.0-py3-none-any.whl", hash = "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e"},
|
||||
{file = "gunicorn-20.1.0.tar.gz", hash = "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8"},
|
||||
{file = "gunicorn-23.0.0-py3-none-any.whl", hash = "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d"},
|
||||
{file = "gunicorn-23.0.0.tar.gz", hash = "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
setuptools = ">=3.0"
|
||||
packaging = "*"
|
||||
|
||||
[package.extras]
|
||||
eventlet = ["eventlet (>=0.24.1)"]
|
||||
eventlet = ["eventlet (>=0.24.1,!=0.36.0)"]
|
||||
gevent = ["gevent (>=1.4.0)"]
|
||||
setproctitle = ["setproctitle"]
|
||||
testing = ["coverage", "eventlet", "gevent", "pytest", "pytest-cov"]
|
||||
tornado = ["tornado (>=0.2)"]
|
||||
|
||||
[[package]]
|
||||
|
@ -249,6 +250,17 @@ files = [
|
|||
{file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "24.2"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"},
|
||||
{file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "10.4.0"
|
||||
|
@ -521,26 +533,6 @@ files = [
|
|||
{file = "python_monkey_business-1.1.0-py2.py3-none-any.whl", hash = "sha256:15b4f603c749ba9a7b4f1acd36af023a6c5ba0f7e591c945f8253f0ef44bf389"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "75.6.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"},
|
||||
{file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"]
|
||||
core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
|
||||
cover = ["pytest-cov"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
|
||||
enabler = ["pytest-enabler (>=2.2)"]
|
||||
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
|
||||
type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"]
|
||||
|
||||
[[package]]
|
||||
name = "sqlparse"
|
||||
version = "0.5.3"
|
||||
|
@ -647,4 +639,4 @@ brotli = ["brotli"]
|
|||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "9fa0905357cc8444a092294c75ee8b373078c78723c6c345cf56a59091cf0ce7"
|
||||
content-hash = "41ac47a86e2c341e2862f4309757b703ab5b6d4939a664ec1289611a0191228b"
|
||||
|
|
|
@ -7,11 +7,11 @@ authors = ["Johannes Schriewer <hallo@dunkelstern.de>"]
|
|||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
django = "^5"
|
||||
gunicorn = "^20"
|
||||
gunicorn = "^23"
|
||||
whitenoise = "^6.6"
|
||||
commentjson = "^0.9"
|
||||
psycopg = { version = "^3", extras = [ "binary" ] }
|
||||
django-nested-admin = "^4.0.0"
|
||||
django-nested-admin = "^4"
|
||||
pillow = "^10"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
|
|
Loading…
Reference in a new issue