mirror of
https://github.com/ferdzo/serviceCRM.git
synced 2026-04-04 21:06:24 +00:00
Working prototype with tailwind, added uv, updated models, added docker.
This commit is contained in:
1
.env.example
Normal file
1
.env.example
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SECRET_KEY=django-insecure-change-me-in-production-!@#$%^&*()
|
||||||
58
.github/copilot-instructions.md
vendored
Normal file
58
.github/copilot-instructions.md
vendored
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# ServiceCRM Copilot Instructions
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
ServiceCRM is a simple Service Ticket Management System built with Django. It manages repair tickets ("Inserts") for service customers, tracking details like customer info, defect description, repair status, and notes.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
- **Framework**: Django 4.x (compatible with 5.x/6.x in dependencies).
|
||||||
|
- **Structure**: Single-app project where the `serviceCRM` package contains both project settings and application logic (views, models, urls).
|
||||||
|
- **Patterns**: Model-View-Template (MVT).
|
||||||
|
- **Frontend**: Server-side rendered templates using Bootstrap 5 and `django-crispy-forms`.
|
||||||
|
|
||||||
|
## Key Components
|
||||||
|
|
||||||
|
### Data Model
|
||||||
|
- **Primary Model**: `Insert` (defined in `serviceCRM/models.py`).
|
||||||
|
- Represents a service ticket.
|
||||||
|
- Key fields: `name`, `phone`, `description` (defect), `repair` (resolution), `done` (status), `date`.
|
||||||
|
|
||||||
|
### Views & Controllers
|
||||||
|
- **Location**: `serviceCRM/views.py`.
|
||||||
|
- **Pattern**: Mix of Class-Based Views (CBV) and Function-Based Views (FBV).
|
||||||
|
- List views use `django_tables2.SingleTableView`.
|
||||||
|
- Create/Update use `generic.View` and `generic.UpdateView`.
|
||||||
|
- **Note**: Some views use a non-standard static method pattern (e.g., `InsertNew.insert` referenced in URLs).
|
||||||
|
|
||||||
|
### UI/UX
|
||||||
|
- **Tables**: `django-tables2` is used for listing records (`serviceCRM/tables.py`).
|
||||||
|
- **Forms**: `django-crispy-forms` with `crispy-bootstrap5` pack (`serviceCRM/forms.py`).
|
||||||
|
- **Templates**: Located in `templates/` and `templates/serviceCRM/`.
|
||||||
|
|
||||||
|
## Configuration & Environment
|
||||||
|
- **Settings**: `serviceCRM/settings.py` uses `django-environ`.
|
||||||
|
- **Environment**: Configuration reads from `.env` file (see `.env.example`).
|
||||||
|
- **Database**:
|
||||||
|
- Local/Dev: SQLite (configured via `settings.py` overriding env vars if needed).
|
||||||
|
- Production: Configurable via `DATABASE_URL` or individual env vars (PostgreSQL supported).
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Setup
|
||||||
|
1. Create `.env` from `.env.example`.
|
||||||
|
2. Install dependencies: `pip install -r requirements.txt`.
|
||||||
|
3. Run migrations: `python manage.py migrate`.
|
||||||
|
|
||||||
|
### Common Commands
|
||||||
|
- **Run Server**: `python manage.py runserver`
|
||||||
|
- **Make Migrations**: `python manage.py makemigrations` (Required when changing `models.py`)
|
||||||
|
- **Migrate**: `python manage.py migrate`
|
||||||
|
|
||||||
|
## Coding Conventions
|
||||||
|
- **Imports**: standard library -> third party -> django -> local apps.
|
||||||
|
- **Urls**: Defined in `serviceCRM/urls.py` (which is the root URLconf).
|
||||||
|
- **Routing**: Routes are mixed between CBV (`.as_view()`) and function references. Maintain consistency with existing patterns when adding new routes.
|
||||||
|
|
||||||
|
## Critical Patterns to Respect
|
||||||
|
1. **Forms**: Always use `InputForm` in `serviceCRM/forms.py` for ticket creation/editing to ensure consistent widget rendering with Bootstrap classes.
|
||||||
|
2. **Tables**: When adding lists, subclass `tables.Table` in `serviceCRM/tables.py` and use `SingleTableView` for display/sorting features.
|
||||||
|
3. **Template Inheritance**: All templates should extend `base.html`.
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -2,7 +2,7 @@
|
|||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
# Ignore __pycache__ folders and .pyc files
|
# Ignore __pycache__ folders and .pyc files
|
||||||
__pycache__/
|
*__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
# Ignore .env files
|
# Ignore .env files
|
||||||
@@ -14,7 +14,9 @@ __pycache__/
|
|||||||
# Ignore .DS_Store files
|
# Ignore .DS_Store files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
.vscode\
|
.vscode
|
||||||
|
|
||||||
|
db.sqlite3
|
||||||
|
|
||||||
pyvenv.cfg
|
pyvenv.cfg
|
||||||
|
|
||||||
|
|||||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.14
|
||||||
19
.vscode/launch.json
vendored
19
.vscode/launch.json
vendored
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Python Debugger: Django",
|
|
||||||
"type": "debugpy",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${workspaceFolder}\\manage.py",
|
|
||||||
"args": [
|
|
||||||
"runserver"
|
|
||||||
],
|
|
||||||
"django": true,
|
|
||||||
"autoStartBrowser": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
4
.vscode/settings.json
vendored
4
.vscode/settings.json
vendored
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"cmake.configureOnOpen": false,
|
|
||||||
"python.pythonPath": ".venv\\Scripts\\python.exe"
|
|
||||||
}
|
|
||||||
38
Dockerfile
Normal file
38
Dockerfile
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Django Backend Dockerfile
|
||||||
|
FROM ghcr.io/astral-sh/uv:python3.13-alpine AS builder
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
ENV UV_COMPILE_BYTECODE=1
|
||||||
|
|
||||||
|
COPY pyproject.toml uv.lock ./
|
||||||
|
|
||||||
|
RUN uv sync --frozen --no-dev --no-install-project
|
||||||
|
|
||||||
|
COPY serviceCRM/ ./serviceCRM/
|
||||||
|
COPY manage.py ./
|
||||||
|
|
||||||
|
RUN uv sync --frozen --no-dev
|
||||||
|
|
||||||
|
|
||||||
|
FROM python:3.13-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apk add --no-cache postgresql-client
|
||||||
|
|
||||||
|
COPY --from=builder /app/.venv /app/.venv
|
||||||
|
COPY --from=builder /app/serviceCRM/ /app/serviceCRM/
|
||||||
|
COPY --from=builder /app/manage.py /app/
|
||||||
|
|
||||||
|
RUN adduser -D -u 1000 appuser && \
|
||||||
|
chown -R appuser:appuser /app
|
||||||
|
|
||||||
|
USER appuser
|
||||||
|
|
||||||
|
ENV PATH="/app/.venv/bin:$PATH"
|
||||||
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
|
|
||||||
|
CMD ["python", "-m", "uvicorn", "serviceCRM.asgi:application", "--host", "0.0.0.0", "--port", "8000"]
|
||||||
14
docker-compose.yml
Normal file
14
docker-compose.yml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
- DEBUG=False
|
||||||
|
- ALLOWED_HOSTS=*
|
||||||
35
pyproject.toml
Normal file
35
pyproject.toml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
[project]
|
||||||
|
name = "servicecrm"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A CRM application built with Django."
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.14"
|
||||||
|
dependencies = [
|
||||||
|
"asgiref>=3.11.0",
|
||||||
|
"cffi>=2.0.0",
|
||||||
|
"crispy-bootstrap5>=2025.6",
|
||||||
|
"crispy-tailwind>=1.0.3",
|
||||||
|
"cryptography>=46.0.3",
|
||||||
|
"django>=6.0.1",
|
||||||
|
"django-crispy-forms>=2.5",
|
||||||
|
"django-datatable-view>=2.1.6",
|
||||||
|
"django-environ>=0.12.0",
|
||||||
|
"django-filter>=25.2",
|
||||||
|
"django-pipeline>=4.1.0",
|
||||||
|
"django-tables2>=2.8.0",
|
||||||
|
"dnspython>=2.8.0",
|
||||||
|
"feedparser>=6.0.12",
|
||||||
|
"nanoid>=2.0.0",
|
||||||
|
"pillow>=12.1.0",
|
||||||
|
"publicsuffix>=1.1.1",
|
||||||
|
"pycparser>=2.23",
|
||||||
|
"python-dateutil>=2.9.0.post0",
|
||||||
|
"pytz>=2025.2",
|
||||||
|
"sgmllib3k>=1.0.0",
|
||||||
|
"six>=1.17.0",
|
||||||
|
"sqlparse>=0.5.5",
|
||||||
|
"tablib>=3.9.0",
|
||||||
|
"typing-extensions>=4.15.0",
|
||||||
|
"tzdata>=2025.3",
|
||||||
|
"uvicorn>=0.40.0",
|
||||||
|
]
|
||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
@@ -1,6 +1,27 @@
|
|||||||
from .models import Insert
|
from .models import Insert
|
||||||
import django_filters as filters
|
import django_filters as filters
|
||||||
from django_filters import FilterSet
|
from django_filters import FilterSet
|
||||||
|
from django.db.models import Q
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
class InsertFilter(FilterSet):
|
||||||
|
start_date = filters.DateFilter(field_name="date", lookup_expr='gte', label='Од датум', widget=forms.TextInput(attrs={'type': 'date'}))
|
||||||
|
end_date = filters.DateFilter(field_name="date", lookup_expr='lte', label='До датум', widget=forms.TextInput(attrs={'type': 'date'}))
|
||||||
|
search = filters.CharFilter(method='filter_search', label='Пребарај (Име, Тел, ID, Опис)')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Insert
|
||||||
|
fields = ['search', 'done']
|
||||||
|
|
||||||
|
def filter_search(self, queryset, name, value):
|
||||||
|
return queryset.filter(
|
||||||
|
Q(name__icontains=value) |
|
||||||
|
Q(phone__icontains=value) |
|
||||||
|
Q(description__icontains=value) |
|
||||||
|
Q(plateno__icontains=value) |
|
||||||
|
Q(ticket_id__icontains=value) |
|
||||||
|
Q(id__icontains=value)
|
||||||
|
)
|
||||||
|
|
||||||
class DoneTable(FilterSet):
|
class DoneTable(FilterSet):
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -11,4 +32,22 @@ class DoneTable(FilterSet):
|
|||||||
if value:
|
if value:
|
||||||
return queryset.filter(done=True)
|
return queryset.filter(done=True)
|
||||||
else:
|
else:
|
||||||
return queryset.filter(done=False)
|
return queryset.filter(done=False)
|
||||||
|
class DoneFilter(FilterSet):
|
||||||
|
start_date = filters.DateFilter(field_name="date_close", lookup_expr='gte', label='Од датум (Затворено)', widget=forms.TextInput(attrs={'type': 'date'}))
|
||||||
|
end_date = filters.DateFilter(field_name="date_close", lookup_expr='lte', label='До датум (Затворено)', widget=forms.TextInput(attrs={'type': 'date'}))
|
||||||
|
search = filters.CharFilter(method='filter_search', label='Пребарај (Име, Тел, ID)')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Insert
|
||||||
|
fields = ['status']
|
||||||
|
|
||||||
|
def filter_search(self, queryset, name, value):
|
||||||
|
return queryset.filter(
|
||||||
|
Q(name__icontains=value) |
|
||||||
|
Q(phone__icontains=value) |
|
||||||
|
Q(description__icontains=value) |
|
||||||
|
Q(plateno__icontains=value) |
|
||||||
|
Q(ticket_id__icontains=value) |
|
||||||
|
Q(id__icontains=value)
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
from .models import Insert
|
from .models import Insert
|
||||||
|
|
||||||
|
|
||||||
@@ -9,17 +10,31 @@ class DateInput(forms.DateInput):
|
|||||||
class InputForm(forms.ModelForm):
|
class InputForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Insert
|
model = Insert
|
||||||
fields = {"name", "phone", "description", "date", "note"}
|
fields = ["name", "phone", "date", "description", "note"]
|
||||||
labels = {'name': "Name", 'phone': "Phone", 'date': "Date", 'description': "Description", 'note': "Note"}
|
labels = {
|
||||||
|
'name': "Име",
|
||||||
|
'phone': "Телефон",
|
||||||
|
'date': "Датум на прием",
|
||||||
|
'description': "Опис на проблем",
|
||||||
|
'note': "Забелешка"
|
||||||
|
}
|
||||||
widgets = {
|
widgets = {
|
||||||
'name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
||||||
'phone': forms.TextInput(attrs={'class': 'form-control'}),
|
|
||||||
'date': DateInput(),
|
'date': DateInput(),
|
||||||
'description': forms.Textarea(attrs={'class': 'form-control'}),
|
'description': forms.Textarea(attrs={'rows': 3}),
|
||||||
'note': forms.TextInput(attrs={'class': 'form-control'})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
field_order = ["name", "phone", "date", "description", "done"]
|
class CloseForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Insert
|
||||||
|
fields = ["repair", "plateno"]
|
||||||
|
labels = {
|
||||||
|
'repair': "Детали за поправка",
|
||||||
|
'plateno': "Наплатено"
|
||||||
|
}
|
||||||
|
widgets = {
|
||||||
|
'repair': forms.Textarea(attrs={'rows': 4}),
|
||||||
|
'plateno': forms.TextInput(attrs={'placeholder': 'пр. 1500 МКД'})
|
||||||
|
}
|
||||||
|
|
||||||
# class EditForm(forms.ModelForm):
|
# class EditForm(forms.ModelForm):
|
||||||
# class Meta:
|
# class Meta:
|
||||||
|
|||||||
18
serviceCRM/migrations/0006_insert_date_close.py
Normal file
18
serviceCRM/migrations/0006_insert_date_close.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-08 11:09
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('serviceCRM', '0005_alter_insert_done'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='insert',
|
||||||
|
name='date_close',
|
||||||
|
field=models.DateField(blank=True, null=True, verbose_name='date closed'),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
serviceCRM/migrations/0007_insert_status.py
Normal file
18
serviceCRM/migrations/0007_insert_status.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-08 11:14
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('serviceCRM', '0006_insert_date_close'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='insert',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('RECEIVED', 'Received'), ('DIAGNOSING', 'Diagnosing'), ('WAITING_PARTS', 'Waiting for Parts'), ('READY', 'Ready for Pickup'), ('COMPLETED', 'Completed')], default='RECEIVED', max_length=20),
|
||||||
|
),
|
||||||
|
]
|
||||||
19
serviceCRM/migrations/0008_insert_ticket_id.py
Normal file
19
serviceCRM/migrations/0008_insert_ticket_id.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-08 11:30
|
||||||
|
|
||||||
|
import serviceCRM.models
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('serviceCRM', '0007_insert_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='insert',
|
||||||
|
name='ticket_id',
|
||||||
|
field=models.CharField(default=serviceCRM.models.generate_nanoid, max_length=12, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
19
serviceCRM/migrations/0009_alter_insert_ticket_id.py
Normal file
19
serviceCRM/migrations/0009_alter_insert_ticket_id.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-08 11:32
|
||||||
|
|
||||||
|
import serviceCRM.models
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('serviceCRM', '0008_insert_ticket_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='ticket_id',
|
||||||
|
field=models.CharField(default=serviceCRM.models.generate_nanoid, editable=False, max_length=12, unique=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
27
serviceCRM/migrations/0010_ticketlog.py
Normal file
27
serviceCRM/migrations/0010_ticketlog.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-08 13:04
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('serviceCRM', '0009_alter_insert_ticket_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TicketLog',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('action', models.CharField(max_length=50)),
|
||||||
|
('details', models.TextField(blank=True, null=True)),
|
||||||
|
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='logs', to='serviceCRM.insert')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['-timestamp'],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Generated by Django 6.0.1 on 2026-01-08 14:37
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('serviceCRM', '0010_ticketlog'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='date',
|
||||||
|
field=models.DateField(verbose_name='Датум'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='date_close',
|
||||||
|
field=models.DateField(blank=True, null=True, verbose_name='Датум затворање'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='description',
|
||||||
|
field=models.CharField(max_length=300, verbose_name='Опис'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='done',
|
||||||
|
field=models.BooleanField(default=False, verbose_name='Завршено'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(max_length=50, verbose_name='Име'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='note',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=100, null=True, verbose_name='Забелешка'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='phone',
|
||||||
|
field=models.CharField(max_length=20, verbose_name='Телефон'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='plateno',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=10, null=True, verbose_name='Плаќање/Рег.'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='repair',
|
||||||
|
field=models.CharField(blank=True, default=None, max_length=300, null=True, verbose_name='Поправка'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='insert',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('RECEIVED', 'Примено'), ('DIAGNOSING', 'Дијагностика'), ('WAITING_PARTS', 'Чека делови'), ('READY', 'Готово за подигање'), ('COMPLETED', 'Завршено')], default='RECEIVED', max_length=20, verbose_name='Статус'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,19 +1,63 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import UserManager
|
from django.contrib.auth.models import UserManager
|
||||||
|
from nanoid import generate
|
||||||
|
|
||||||
|
def generate_nanoid():
|
||||||
|
return generate(size=12)
|
||||||
|
|
||||||
class Insert(models.Model):
|
class Insert(models.Model):
|
||||||
name = models.CharField(max_length=50)
|
ticket_id = models.CharField(max_length=12, default=generate_nanoid, unique=True, editable=False)
|
||||||
phone = models.CharField(max_length=20)
|
name = models.CharField(max_length=50, verbose_name="Име")
|
||||||
description = models.CharField(max_length=300)
|
phone = models.CharField(max_length=20, verbose_name="Телефон")
|
||||||
note= models.CharField(max_length=100, default=None, blank=True, null=True)
|
description = models.CharField(max_length=300, verbose_name="Опис")
|
||||||
date = models.DateField("date submitted")
|
note= models.CharField(max_length=100, default=None, blank=True, null=True, verbose_name="Забелешка")
|
||||||
done = models.BooleanField(default=False)
|
date = models.DateField("Датум")
|
||||||
repair = models.CharField(default=None, blank=True, null=True,max_length=300)
|
done = models.BooleanField(default=False, verbose_name="Завршено")
|
||||||
plateno = models.CharField(max_length=10, default=None, blank=True, null=True)
|
|
||||||
|
STATUS_CHOICES = [
|
||||||
|
('RECEIVED', 'Примено'),
|
||||||
|
('DIAGNOSING', 'Дијагностика'),
|
||||||
|
('WAITING_PARTS', 'Чека делови'),
|
||||||
|
('READY', 'Готово за подигање'),
|
||||||
|
('COMPLETED', 'Завршено'),
|
||||||
|
]
|
||||||
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='RECEIVED', verbose_name="Статус")
|
||||||
|
|
||||||
|
date_close = models.DateField("Датум затворање", null=True, blank=True)
|
||||||
|
repair = models.CharField(default=None, blank=True, null=True,max_length=300, verbose_name="Поправка")
|
||||||
|
plateno = models.CharField(max_length=10, default=None, blank=True, null=True, verbose_name="Плаќање/Рег.")
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
# Sync done and status fields
|
||||||
|
if self.status == 'COMPLETED':
|
||||||
|
self.done = True
|
||||||
|
if not self.date_close:
|
||||||
|
self.date_close = timezone.now().date()
|
||||||
|
elif self.done:
|
||||||
|
self.status = 'COMPLETED'
|
||||||
|
if not self.date_close:
|
||||||
|
self.date_close = timezone.now().date()
|
||||||
|
else:
|
||||||
|
self.done = False
|
||||||
|
|
||||||
|
super(Insert, self).save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Ime: " + self.name + " Telefonski broj: " + self.phone + "\nDefekt: " + self.description + "\nDatum: \n"
|
return "Ime: " + self.name + " Telefonski broj: " + self.phone + "\nDefekt: " + self.description + "\nDatum: \n"
|
||||||
|
|
||||||
def isDone(self):
|
def isDone(self):
|
||||||
return self.done
|
return self.done
|
||||||
|
|
||||||
|
class TicketLog(models.Model):
|
||||||
|
ticket = models.ForeignKey(Insert, on_delete=models.CASCADE, related_name='logs')
|
||||||
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
action = models.CharField(max_length=50)
|
||||||
|
details = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['-timestamp']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.ticket.ticket_id} - {self.action} at {self.timestamp}"
|
||||||
|
|||||||
@@ -25,9 +25,9 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
|||||||
SECRET_KEY = env('SECRET_KEY')
|
SECRET_KEY = env('SECRET_KEY')
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# SECURITY WARNING: don't run with debug turned on in production!
|
||||||
DEBUG = True
|
DEBUG = env.bool('DEBUG', default=False)
|
||||||
|
|
||||||
ALLOWED_HOSTS = ['*']
|
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=['*'])
|
||||||
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
@@ -39,22 +39,29 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
|
'whitenoise.runserver_nostatic', # Add whitenoise before staticfiles
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'crispy_forms',
|
'crispy_forms',
|
||||||
'crispy_bootstrap5',
|
'crispy_tailwind',
|
||||||
'django_tables2',
|
'django_tables2',
|
||||||
|
'django_filters',
|
||||||
]
|
]
|
||||||
CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5"
|
CRISPY_ALLOWED_TEMPLATE_PACKS = "tailwind"
|
||||||
CRISPY_TEMPLATE_PACK = "bootstrap5"
|
CRISPY_TEMPLATE_PACK = "tailwind"
|
||||||
DJANGO_TABLES2_TABLE_ATTRS = {
|
DJANGO_TABLES2_TABLE_ATTRS = {
|
||||||
'class': 'table table-hover',
|
'class': 'min-w-full divide-y divide-gray-200 border',
|
||||||
'thead': {
|
'thead': {
|
||||||
'class': 'table-light',
|
'class': 'bg-gray-50',
|
||||||
|
},
|
||||||
|
'tbody': {
|
||||||
|
'class': 'bg-white divide-y divide-gray-200',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'whitenoise.middleware.WhiteNoiseMiddleware', # Add whitenoise middleware
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
'django.middleware.locale.LocaleMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
||||||
'django.middleware.csrf.CsrfViewMiddleware',
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
@@ -88,12 +95,8 @@ WSGI_APPLICATION = 'serviceCRM.wsgi.application'
|
|||||||
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.postgresql',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': env('DATABASE_NAME'),
|
'NAME': BASE_DIR / 'db.sqlite3',
|
||||||
'USER': env('DATABASE_USER'),
|
|
||||||
'PASSWORD': env('DATABASE_PASS'),
|
|
||||||
'HOST': env('DATABASE_HOST'),
|
|
||||||
'PORT': env('DATABASE_PORT'),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,9 +123,20 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
# Internationalization
|
# Internationalization
|
||||||
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
# https://docs.djangoproject.com/en/4.2/topics/i18n/
|
||||||
|
|
||||||
LANGUAGE_CODE = 'en-us'
|
LANGUAGE_CODE = 'mk'
|
||||||
|
|
||||||
TIME_ZONE = 'UTC'
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
LANGUAGES = [
|
||||||
|
('mk', _('Macedonian')),
|
||||||
|
('en', _('English')),
|
||||||
|
]
|
||||||
|
|
||||||
|
LOCALE_PATHS = [
|
||||||
|
BASE_DIR / 'locale',
|
||||||
|
]
|
||||||
|
|
||||||
|
TIME_ZONE = 'Europe/Skopje'
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
@@ -133,8 +147,13 @@ USE_TZ = True
|
|||||||
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
# https://docs.djangoproject.com/en/4.2/howto/static-files/
|
||||||
|
|
||||||
STATIC_URL = 'static/'
|
STATIC_URL = 'static/'
|
||||||
|
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
||||||
|
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||||
|
|
||||||
# Default primary key field type
|
# Default primary key field type
|
||||||
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
LOGIN_REDIRECT_URL = '/'
|
||||||
|
LOGIN_URL = '/admin/login/'
|
||||||
|
|||||||
@@ -6,18 +6,24 @@ from .models import Insert
|
|||||||
|
|
||||||
class InsertTable(tables.Table):
|
class InsertTable(tables.Table):
|
||||||
|
|
||||||
actions = TemplateColumn(template_code='<a class="btn btn-secondary" href="{% url \'update\' record.id %}">Edit</a> <a class="btn btn-secondary" href="{% url \'done\' record.id %}">Details</a>')
|
actions = TemplateColumn(template_code='''
|
||||||
|
<a class="text-indigo-600 hover:text-indigo-900 mr-2" href="{% url 'nalog' ticket_id=record.ticket_id %}">Види</a>
|
||||||
|
<a class="text-gray-600 hover:text-gray-900 mr-2" href="{% url 'update' ticket_id=record.ticket_id %}">Уреди</a>
|
||||||
|
{% if not record.done %}
|
||||||
|
<a class="text-green-600 hover:text-green-900 font-medium" href="{% url 'close_ticket' ticket_id=record.ticket_id %}">Затвори</a>
|
||||||
|
{% endif %}
|
||||||
|
''')
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Insert
|
model = Insert
|
||||||
fields = ("id","name","phone","description","date","done")
|
fields = ("ticket_id","name","phone","description","date","status","done")
|
||||||
per_page = 5
|
per_page = 5
|
||||||
|
|
||||||
|
|
||||||
class DoneInsertTable(InsertTable):
|
class DoneInsertTable(InsertTable):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Insert
|
model = Insert
|
||||||
fields = ("id","name","phone","description","date","done")
|
fields = ("ticket_id","name","phone","description","date", "date_close", "done")
|
||||||
per_page = 5
|
per_page = 5
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -1,43 +1,92 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
{% load static i18n %}
|
||||||
<head>
|
<html lang="mk">
|
||||||
<meta charset="utf-8">
|
<head>
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
|
<meta charset="UTF-8">
|
||||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"/>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>Service </title>
|
<title>Service CRM</title>
|
||||||
</head>
|
<script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
|
||||||
<body>
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||||
<script src="http://code.jquery.com/jquery-3.3.1.min.js"></script>
|
<style>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"></script>
|
body { font-family: 'Inter', sans-serif; }
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"></script>
|
</style>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.20/jquery.datetimepicker.full.min.js"></script>
|
</head>
|
||||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css">
|
<body class="bg-gray-50 text-gray-900">
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
|
<nav class="bg-white shadow">
|
||||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js"></script>
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
<nav class="navbar navbar-inverse">
|
<div class="flex justify-between h-16">
|
||||||
<div class="container-fluid">
|
<div class="flex">
|
||||||
<div class="navbar-header">
|
<div class="flex-shrink-0 flex items-center">
|
||||||
<a class="navbar-brand" href="/">Service CRM</a>
|
{% if user.is_authenticated %}
|
||||||
|
<a href="{% url 'dashboard' %}">
|
||||||
|
<img src="{% static 'fer-logo.png' %}" alt="Logo" class="h-8 w-auto">
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url 'home' %}">
|
||||||
|
<img src="{% static 'fer-logo.png' %}" alt="Logo" class="h-8 w-auto">
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="hidden sm:ml-6 sm:flex sm:space-x-8">
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
<a href="{% url 'dashboard' %}" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||||
|
<svg class="-ml-1 mr-2 h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z" />
|
||||||
|
</svg>
|
||||||
|
Активни налози
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'done' %}" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||||
|
<svg class="-ml-1 mr-2 h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4" />
|
||||||
|
</svg>
|
||||||
|
Архива
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<a href="{% url 'track_ticket' %}" class="border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700 inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium">
|
||||||
|
<svg class="-ml-1 mr-2 h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
|
</svg>
|
||||||
|
Проверка на статус
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
<a href="{% url 'insert' %}" class="bg-gray-900 text-white hover:bg-gray-800 px-4 py-2 rounded-md text-sm font-medium transition duration-150 ease-in-out shadow-sm flex items-center">
|
||||||
|
<svg class="-ml-1 mr-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||||
|
</svg>
|
||||||
|
Нов Налог
|
||||||
|
</a>
|
||||||
|
<div class="ml-3 relative flex items-center space-x-3">
|
||||||
|
<span class="text-sm text-gray-500">Здраво, <strong>{{ user.username }}</strong></span>
|
||||||
|
<a href="{% url 'logout' %}" class="text-gray-400 hover:text-gray-500">
|
||||||
|
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="nav navbar-nav navbar-right">
|
|
||||||
<li><a href="/insert"><span class="glyphicon glyphicon-user"></span>Insert</a></li>
|
|
||||||
<li><a href="/admin"><span class="glyphicon glyphicon-log-in"></span> Admin</a></li>
|
|
||||||
<li><a href="/done"><span class="glyphicon glyphicon-log-in"></span> Done</a></li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</nav>
|
</nav>
|
||||||
<div class="container">
|
|
||||||
{% comment %} <h1 class="mt-2">Service CRM</h1>
|
<main class="max-w-7xl mx-auto py-8 sm:px-6 lg:px-8">
|
||||||
<a href='/insert'><button type="button" class="btn btn-dark">Insert</button></a>
|
<div class="px-4 py-4 sm:px-0">
|
||||||
<hr class="mt-0 mb-4"> {% endcomment %}
|
{% if messages %}
|
||||||
|
<div class="mb-6 space-y-2">
|
||||||
<div class="row justify-content-center">
|
{% for message in messages %}
|
||||||
<div class="col-8">
|
<div class="p-4 rounded-md shadow-sm {% if message.tags == 'success' %}bg-green-50 text-green-800 border-l-4 border-green-400{% elif message.tags == 'error' %}bg-red-50 text-red-800 border-l-4 border-red-400{% else %}bg-blue-50 text-blue-800 border-l-4 border-blue-400{% endif %}">
|
||||||
{% block content %}
|
{{ message }}
|
||||||
{% endblock %}
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
{% endblock %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</main>
|
||||||
</div>
|
</body>
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
34
serviceCRM/templates/serviceCRM/close_ticket.html
Normal file
34
serviceCRM/templates/serviceCRM/close_ticket.html
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
|
{% block title %}Затвори налог #{{ object.id }}{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="max-w-3xl mx-auto">
|
||||||
|
<div class="bg-white shadow sm:rounded-lg mb-6">
|
||||||
|
<div class="px-4 py-5 sm:p-6">
|
||||||
|
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-2">Затвори налог #{{ object.id }}</h3>
|
||||||
|
<p class="text-sm text-gray-500 mb-6">
|
||||||
|
Завршување на поправка за <strong>{{ object.name }}</strong> ({{ object.description|truncatechars:50 }})
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|crispy }}
|
||||||
|
|
||||||
|
<div class="mt-5 flex space-x-3">
|
||||||
|
<button type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-green-600 hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="-ml-1 mr-2 h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
Заврши и Затвори
|
||||||
|
</button>
|
||||||
|
<a href="{% url 'home' %}" class="inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||||
|
Откажи
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock%}
|
||||||
@@ -1,6 +1,31 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load render_table from django_tables2 %}
|
{% load render_table from django_tables2 %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% render_table table %}
|
<div class="mb-6 bg-white shadow sm:rounded-lg">
|
||||||
|
<div class="px-4 py-5 sm:p-6">
|
||||||
|
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">Пребарување Архива</h3>
|
||||||
|
<form method="get" class="space-y-4 md:space-y-0 md:grid md:grid-cols-12 md:gap-6 items-end">
|
||||||
|
<div class="md:col-span-3">
|
||||||
|
{{ filter.form.start_date|as_crispy_field }}
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-3">
|
||||||
|
{{ filter.form.end_date|as_crispy_field }}
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-4">
|
||||||
|
{{ filter.form.search|as_crispy_field }}
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-2">
|
||||||
|
<button type="submit" class="w-full inline-flex justify-center items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-gray-900 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||||
|
Филтрирај
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
||||||
|
{% render_table table %}
|
||||||
|
</div>
|
||||||
{%endblock%}
|
{%endblock%}
|
||||||
@@ -1,13 +1,17 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% block title %}Edit{% endblock %}
|
{% load crispy_forms_tags %}
|
||||||
|
{% block title %}Уреди{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% csrf_token %}
|
<div class="bg-white shadow sm:rounded-lg mb-6">
|
||||||
|
<div class="px-4 py-5 sm:p-6">
|
||||||
<form method="post">
|
<form method="post">
|
||||||
<div class="form-group">
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{form.as_p}}
|
{{ form|crispy }}
|
||||||
<input type="submit" class="btn btn-success" value="Update">
|
<div class="mt-5 flex space-x-3">
|
||||||
<input type="submit" class="btn btn-danger" value="Delete" formaction="{% url 'delete' id=object.id %}">
|
<input type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-gray-900 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 cursor-pointer" value="Ажурирај">
|
||||||
|
<input type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 cursor-pointer" value="Избриши" formaction="{% url 'delete' ticket_id=object.ticket_id %}">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock%}
|
{% endblock%}
|
||||||
@@ -1,11 +1,16 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% csrf_token %}
|
<div class="bg-white shadow sm:rounded-lg mb-6">
|
||||||
|
<div class="px-4 py-5 sm:p-6">
|
||||||
<form action="/insert/" method="post">
|
<form action="/insert/" method="post">
|
||||||
<div class="form-group">
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{form.as_p}}
|
{{ form|crispy }}
|
||||||
<input type="submit" class="btn btn-success" value="Submit">
|
<div class="mt-5">
|
||||||
|
<input type="submit" class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-gray-900 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 cursor-pointer" value="Внеси">
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock%}
|
{% endblock%}
|
||||||
@@ -1,5 +1,57 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load render_table from django_tables2 %}
|
{% load render_table from django_tables2 %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% render_table table %}
|
<!-- Dashboard Stats -->
|
||||||
|
<div class="grid grid-cols-1 gap-5 sm:grid-cols-3 mb-6">
|
||||||
|
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||||
|
<div class="px-4 py-5 sm:p-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500 truncate">Активни налози</dt>
|
||||||
|
<dd class="mt-1 text-3xl font-semibold text-gray-900">{{ stats_active }}</dd>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||||
|
<div class="px-4 py-5 sm:p-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500 truncate">Завршени денес</dt>
|
||||||
|
<dd class="mt-1 text-3xl font-semibold text-gray-900">{{ stats_today_closed }}</dd>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white overflow-hidden shadow rounded-lg">
|
||||||
|
<div class="px-4 py-5 sm:p-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500 truncate">Спремни за подигање</dt>
|
||||||
|
<dd class="mt-1 text-3xl font-semibold text-yellow-600">{{ stats_ready }}</dd>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white shadow sm:rounded-lg mb-6">
|
||||||
|
<div class="px-4 py-5 sm:p-6">
|
||||||
|
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4">Пребарај налози</h3>
|
||||||
|
<form method="get" class="space-y-4 md:space-y-0 md:grid md:grid-cols-12 md:gap-6 items-end">
|
||||||
|
<div class="md:col-span-3">
|
||||||
|
{{ filter.form.start_date|as_crispy_field }}
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-3">
|
||||||
|
{{ filter.form.end_date|as_crispy_field }}
|
||||||
|
</div>
|
||||||
|
<div class="md:col-span-4">
|
||||||
|
{{ filter.form.search|as_crispy_field }}
|
||||||
|
</div>
|
||||||
|
<!-- Hidden done field if needed, or included in search/other filters -->
|
||||||
|
<div class="md:col-span-2">
|
||||||
|
<button type="submit" class="w-full inline-flex justify-center items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-gray-900 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||||
|
<svg class="-ml-1 mr-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||||
|
<path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
Пребарај
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
||||||
|
{% render_table table %}
|
||||||
|
</div>
|
||||||
{% endblock%}
|
{% endblock%}
|
||||||
@@ -1,83 +1,107 @@
|
|||||||
{% block content %}
|
{% extends 'base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
<html lang="en">
|
{% block content %}
|
||||||
<head>
|
<div class="max-w-2xl mx-auto bg-white shadow overflow-hidden sm:rounded-lg">
|
||||||
<meta charset="UTF-8">
|
<div class="px-4 py-5 sm:px-6 flex justify-between items-center">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<div>
|
||||||
<title>Servicing Ticket</title>
|
<h3 class="text-lg leading-6 font-medium text-gray-900">Сервисен Налог #{{ data.ticket_id }}</h3>
|
||||||
<style>
|
<p class="mt-1 max-w-2xl text-sm text-gray-500">Креиран на {{ data.date }} (Интерен ID: {{ data.id }})</p>
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
margin: 0;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
.container {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.header img {
|
|
||||||
max-width: 150px;
|
|
||||||
}
|
|
||||||
.ticket {
|
|
||||||
border: 2px solid #000;
|
|
||||||
padding: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.ticket-info {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.ticket-info label {
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.ticket-description {
|
|
||||||
border-top: 2px solid #000;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
.center {
|
|
||||||
display: block;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
max-width: 100%;
|
|
||||||
max-height: 100%;
|
|
||||||
margin-bottom: 5 0px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="container">
|
|
||||||
<div class="header">
|
|
||||||
<img src="{% static 'fer-logo.png' %}" class="center" alt="Company Logo">
|
|
||||||
</div>
|
</div>
|
||||||
<div class="ticket">
|
<div class="flex space-x-3">
|
||||||
<div class="ticket-info">
|
<a href="{% url 'print_label' data.ticket_id %}" target="_blank" class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||||
<label>Име и презиме:</label> {{ name }}
|
<svg xmlns="http://www.w3.org/2000/svg" class="-ml-1 mr-2 h-5 w-5 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
</div>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" />
|
||||||
<div class="ticket-info">
|
</svg>
|
||||||
<label>Телефонски број:</label> {{phone}}
|
Печати Налепница
|
||||||
</div>
|
</a>
|
||||||
<div class="ticket-info">
|
<a href="{% url 'print_receipt' data.ticket_id %}" target="_blank" class="inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-gray-900 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||||
<label>Датум:</label> {{date}}
|
<svg xmlns="http://www.w3.org/2000/svg" class="-ml-1 mr-2 h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
</div>
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 17h2a2 2 0 002-2v-4a2 2 0 00-2-2H5a2 2 0 00-2 2v4a2 2 0 002 2h2m2 4h6a2 2 0 002-2v-4a2 2 0 00-2-2H9a2 2 0 00-2 2v4a2 2 0 002 2zm8-12V5a2 2 0 00-2-2H9a2 2 0 00-2 2v4h10z" />
|
||||||
<div class="ticket-description">
|
</svg>
|
||||||
<label><b>Опис на дефект:</b></label> {{ desc }}
|
Печати Потврда
|
||||||
</div>
|
</a>
|
||||||
</div>
|
|
||||||
<div style="height:150px;"></div>
|
|
||||||
<div class="ticket">
|
|
||||||
<div class="ticket-info">
|
|
||||||
<label>Име и презиме:</label> {{ name }}
|
|
||||||
</div>
|
|
||||||
<div class="ticket-info">
|
|
||||||
<label>Телефонски број:</label> {{ phone }}
|
|
||||||
</div>
|
|
||||||
<div class="ticket-info">
|
|
||||||
<label>Датум:</label> {{ date }}
|
|
||||||
</div>
|
|
||||||
<div class="ticket-description">
|
|
||||||
<label><b>Опис на дефект:</b></label> {{ desc }}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
<div class="border-t border-gray-200">
|
||||||
</html>
|
<dl>
|
||||||
|
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Име на клиент</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ data.name }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Телефонски број</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ data.phone }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Опис на дефект</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ data.description }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Статус</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ data.get_status_display }}</dd>
|
||||||
|
</div>
|
||||||
|
{% if data.note %}
|
||||||
|
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Интерна забелешка</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ data.note }}</dd>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if data.done %}
|
||||||
|
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Детали за поправка</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ data.repair }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Плаќање/Регистарски број</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ data.plateno }}</dd>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">Датум на затворање</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">{{ data.date_close }}</dd>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ticket History -->
|
||||||
|
<div class="bg-gray-50 px-4 py-5 sm:px-6 border-t border-gray-200">
|
||||||
|
<h4 class="text-md leading-6 font-medium text-gray-900 mb-4">Историја на активности</h4>
|
||||||
|
<div class="flow-root">
|
||||||
|
<ul role="list" class="-mb-8">
|
||||||
|
{% for log in data.logs.all %}
|
||||||
|
<li>
|
||||||
|
<div class="relative pb-8">
|
||||||
|
{% if not forloop.last %}
|
||||||
|
<span class="absolute top-4 left-4 -ml-px h-full w-0.5 bg-gray-200" aria-hidden="true"></span>
|
||||||
|
{% endif %}
|
||||||
|
<div class="relative flex space-x-3">
|
||||||
|
<div>
|
||||||
|
<span class="h-8 w-8 rounded-full bg-gray-500 flex items-center justify-center ring-8 ring-white">
|
||||||
|
<svg class="h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
|
||||||
|
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="min-w-0 flex-1 pt-1.5 flex justify-between space-x-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-sm text-gray-500">{{ log.details }} <span class="font-medium text-gray-900">({{ log.action }})</span></p>
|
||||||
|
</div>
|
||||||
|
<div class="text-right text-sm whitespace-nowrap text-gray-500">
|
||||||
|
<time datetime="{{ log.timestamp|date:'Y-m-d H:i' }}">{{ log.timestamp|date:"M d, H:i" }}</time>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{% empty %}
|
||||||
|
<li class="text-sm text-gray-500">Нема историја.</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="px-4 py-3 bg-white border-t border-gray-200 text-right sm:px-6">
|
||||||
|
<a href="{% url 'dashboard' %}" class="text-indigo-600 hover:text-indigo-900">Назад кон листа</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
41
serviceCRM/templates/serviceCRM/print_label.html
Normal file
41
serviceCRM/templates/serviceCRM/print_label.html
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="mk">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Налепница #{{ ticket.id }}</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
width: 300px; /* Adjust for label printer width (e.g., BROTHER QL) */
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.header { font-size: 1.2em; font-weight: bold; margin-bottom: 5px; }
|
||||||
|
.id { font-size: 2em; font-weight: bold; margin: 10px 0; border: 2px solid black; display: inline-block; padding: 5px 20px; }
|
||||||
|
.details { font-size: 0.9em; text-align: left; margin-top: 10px; }
|
||||||
|
@media print {
|
||||||
|
body { width: 100%; margin: 0; padding: 0; }
|
||||||
|
button { display: none; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body onload="window.print()">
|
||||||
|
<div class="header">
|
||||||
|
<img src="{% static 'fer-logo.png' %}" style="max-width: 150px;" alt="Logo">
|
||||||
|
</div>
|
||||||
|
<div>{{ ticket.date }}</div>
|
||||||
|
|
||||||
|
<div class="id">#{{ ticket.id }}</div>
|
||||||
|
<div style="font-size: 10px; font-family: monospace;">{{ ticket.ticket_id }}</div>
|
||||||
|
|
||||||
|
<div class="header">{{ ticket.name }}</div>
|
||||||
|
<div>{{ ticket.phone }}</div>
|
||||||
|
|
||||||
|
<div class="details">
|
||||||
|
<strong>Дефект:</strong><br>
|
||||||
|
{{ ticket.description|truncatechars:50 }}
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
184
serviceCRM/templates/serviceCRM/print_receipt.html
Normal file
184
serviceCRM/templates/serviceCRM/print_receipt.html
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
{% load static %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="mk">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Потврда #{{ ticket.id }}</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<style>
|
||||||
|
@media print {
|
||||||
|
@page {
|
||||||
|
size: A4 portrait;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
html, body {
|
||||||
|
width: 210mm;
|
||||||
|
height: 296mm; /* Slightly less than 297 to prevent overflow */
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.no-print { display: none !important; }
|
||||||
|
|
||||||
|
.page-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.a5-copy {
|
||||||
|
flex: 1;
|
||||||
|
height: 50%;
|
||||||
|
padding: 10mm; /* Reduced padding to fit better */
|
||||||
|
box-sizing: border-box;
|
||||||
|
border-bottom: 1px dashed #999;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.a5-copy:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cut-line { display: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Screen preview styles */
|
||||||
|
body { background: #f3f4f6; }
|
||||||
|
.page-container {
|
||||||
|
background: white;
|
||||||
|
width: 210mm;
|
||||||
|
min-height: 297mm;
|
||||||
|
margin: 20px auto;
|
||||||
|
box-shadow: 0 0 10px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
.a5-copy {
|
||||||
|
height: 148mm;
|
||||||
|
padding: 15mm;
|
||||||
|
position: relative;
|
||||||
|
background: white;
|
||||||
|
border-bottom: 1px dashed #eee;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body onload="window.print()">
|
||||||
|
<div class="page-container">
|
||||||
|
|
||||||
|
<!-- Copy 1: Customer -->
|
||||||
|
<div class="a5-copy">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="flex justify-between items-start border-b-2 border-gray-800 pb-2 mb-4">
|
||||||
|
<div>
|
||||||
|
<img src="{% static 'fer-logo.png' %}" alt="Logo" class="h-10 w-auto mb-1">
|
||||||
|
<h1 class="text-2xl font-bold text-gray-900">СЕРВИСНА ПОТВРДА</h1>
|
||||||
|
<span class="text-xs font-bold text-gray-500 uppercase tracking-widest">Примерок за Клиент</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<div class="text-xl font-bold">Налог #{{ ticket.id }}</div>
|
||||||
|
<div class="text-xs font-mono text-gray-500 mb-1">Реф: {{ ticket.ticket_id }}</div>
|
||||||
|
<div class="text-gray-600 text-sm">{{ ticket.date }}</div>
|
||||||
|
<div class="mt-1 inline-block px-2 py-0.5 bg-gray-200 rounded text-xs font-bold">{{ ticket.get_status_display }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content Grid -->
|
||||||
|
<div class="flex gap-6 mb-4">
|
||||||
|
<div class="w-1/3">
|
||||||
|
<h3 class="text-gray-500 text-xs uppercase tracking-wide border-b border-gray-200 mb-1">Клиент</h3>
|
||||||
|
<p class="font-bold text-gray-900">{{ ticket.name }}</p>
|
||||||
|
<p class="text-sm text-gray-800">{{ ticket.phone }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="w-2/3">
|
||||||
|
<h3 class="text-gray-500 text-xs uppercase tracking-wide border-b border-gray-200 mb-1">Детали за сервис</h3>
|
||||||
|
<div class="bg-gray-50 p-2 rounded text-sm">
|
||||||
|
<p class="font-bold text-xs text-gray-500">Пријавен дефект:</p>
|
||||||
|
<p class="mb-2">{{ ticket.description }}</p>
|
||||||
|
{% if ticket.plateno %}
|
||||||
|
<p class="font-bold text-xs text-gray-500">Проценка/Наплатено:</p>
|
||||||
|
<p class="font-mono font-bold">{{ ticket.plateno }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Terms (Condensed) -->
|
||||||
|
<div class="text-[10px] leading-tight text-justify text-gray-500 mb-8 border-t border-gray-100 pt-2 absolute bottom-[12mm] left-[10mm] right-[10mm]">
|
||||||
|
<p><strong>УСЛОВИ:</strong> Со потпис потврдувате дека сте согласни со условите. Сервисот не одговара за загуба на податоци. Дијагностика се наплаќа ако поправката е одбиена. Уреди неподигнати 30 дена по известувањето се отстрануваат.</p>
|
||||||
|
|
||||||
|
<!-- Signatures inline with terms to save space -->
|
||||||
|
<div class="grid grid-cols-2 gap-8 mt-2 pt-2">
|
||||||
|
<div class="border-t border-gray-400 pt-1">
|
||||||
|
<p class="text-xs text-gray-600">Потпис на клиент</p>
|
||||||
|
</div>
|
||||||
|
<div class="border-t border-gray-400 pt-1 text-right">
|
||||||
|
<p class="text-xs text-gray-600">Сервисер</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Copy 2: Service -->
|
||||||
|
<div class="a5-copy">
|
||||||
|
<!-- Header -->
|
||||||
|
<div class="flex justify-between items-start border-b-2 border-gray-800 pb-2 mb-4">
|
||||||
|
<div>
|
||||||
|
<img src="{% static 'fer-logo.png' %}" alt="Logo" class="h-10 w-auto mb-1">
|
||||||
|
<h1 class="text-2xl font-bold text-gray-900">СЕРВИСНА ПОТВРДА</h1>
|
||||||
|
<span class="text-xs font-bold text-gray-500 uppercase tracking-widest">Примерок за Сервис</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<div class="text-xl font-bold">Налог #{{ ticket.id }}</div>
|
||||||
|
<div class="text-xs font-mono text-gray-500 mb-1">Реф: {{ ticket.ticket_id }}</div>
|
||||||
|
<div class="text-gray-600 text-sm">{{ ticket.date }}</div>
|
||||||
|
<div class="mt-1 inline-block px-2 py-0.5 bg-gray-200 rounded text-xs font-bold">{{ ticket.get_status_display }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Content Grid -->
|
||||||
|
<div class="flex gap-6 mb-4">
|
||||||
|
<div class="w-1/3">
|
||||||
|
<h3 class="text-gray-500 text-xs uppercase tracking-wide border-b border-gray-200 mb-1">Клиент</h3>
|
||||||
|
<p class="font-bold text-gray-900">{{ ticket.name }}</p>
|
||||||
|
<p class="text-sm text-gray-800">{{ ticket.phone }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="w-2/3">
|
||||||
|
<h3 class="text-gray-500 text-xs uppercase tracking-wide border-b border-gray-200 mb-1">Детали за сервис</h3>
|
||||||
|
<div class="bg-gray-50 p-2 rounded text-sm">
|
||||||
|
<p class="font-bold text-xs text-gray-500">Пријавен дефект:</p>
|
||||||
|
<p class="mb-2">{{ ticket.description }}</p>
|
||||||
|
{% if ticket.plateno %}
|
||||||
|
<p class="font-bold text-xs text-gray-500">Проценка/Наплатено:</p>
|
||||||
|
<p class="font-mono font-bold">{{ ticket.plateno }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Terms (Condensed) -->
|
||||||
|
<div class="text-[10px] leading-tight text-justify text-gray-500 mb-8 border-t border-gray-100 pt-2 absolute bottom-[12mm] left-[10mm] right-[10mm]">
|
||||||
|
<p><strong>УСЛОВИ:</strong> Со потпис потврдувате дека сте согласни со условите. Сервисот не одговара за загуба на податоци. Дијагностика се наплаќа ако поправката е одбиена. Уреди неподигнати 30 дена по известувањето се отстрануваат.</p>
|
||||||
|
|
||||||
|
<!-- Signatures -->
|
||||||
|
<div class="grid grid-cols-2 gap-8 mt-2 pt-2">
|
||||||
|
<div class="border-t border-gray-400 pt-1">
|
||||||
|
<p class="text-xs text-gray-600">Потпис на клиент</p>
|
||||||
|
</div>
|
||||||
|
<div class="border-t border-gray-400 pt-1 text-right">
|
||||||
|
<p class="text-xs text-gray-600">Сервисер</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Print Control -->
|
||||||
|
<div class="fixed bottom-4 right-4 no-print flex gap-2">
|
||||||
|
<button onclick="window.print()" class="bg-gray-900 text-white px-4 py-2 rounded shadow hover:bg-gray-800">Печати</button>
|
||||||
|
<button onclick="window.close()" class="bg-gray-500 text-white px-4 py-2 rounded shadow hover:bg-gray-600">Затвори</button>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
35
serviceCRM/templates/serviceCRM/public_track_form.html
Normal file
35
serviceCRM/templates/serviceCRM/public_track_form.html
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="min-h-full flex flex-col justify-center py-12 sm:px-6 lg:px-8">
|
||||||
|
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
||||||
|
<h2 class="mt-6 text-center text-3xl font-extrabold text-gray-900">
|
||||||
|
Проверка на статус
|
||||||
|
</h2>
|
||||||
|
<p class="mt-2 text-center text-sm text-gray-600">
|
||||||
|
Внесете го бројот на вашиот налог
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
|
||||||
|
<div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
|
||||||
|
<form class="space-y-6" action="{% url 'track_ticket' %}" method="get">
|
||||||
|
<div>
|
||||||
|
<label for="ticket_id" class="block text-sm font-medium text-gray-700">
|
||||||
|
Број на налог
|
||||||
|
</label>
|
||||||
|
<div class="mt-1">
|
||||||
|
<input id="ticket_id" name="ticket_id" type="text" placeholder="на пр. 9aotIewrNmPl" required class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none focus:ring-gray-500 focus:border-gray-500 sm:text-sm">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<button type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-gray-900 hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500">
|
||||||
|
Провери статус
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
89
serviceCRM/templates/serviceCRM/public_track_result.html
Normal file
89
serviceCRM/templates/serviceCRM/public_track_result.html
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="max-w-3xl mx-auto py-12 px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="bg-white shadow overflow-hidden sm:rounded-lg">
|
||||||
|
<div class="px-4 py-5 sm:px-6 flex justify-between items-center bg-gray-100 border-b border-gray-200">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||||
|
Статус на поправка
|
||||||
|
</h3>
|
||||||
|
<p class="mt-1 max-w-2xl text-sm text-gray-500">
|
||||||
|
Рефернца: <span class="font-mono font-bold text-gray-900">{{ ticket.ticket_id }}</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="inline-flex items-center px-4 py-2 rounded-full text-base font-medium
|
||||||
|
{% if ticket.done %}
|
||||||
|
bg-green-100 text-green-800
|
||||||
|
{% else %}
|
||||||
|
bg-yellow-100 text-yellow-800
|
||||||
|
{% endif %}">
|
||||||
|
{% if ticket.done %}
|
||||||
|
ЗАВРШЕНО
|
||||||
|
{% else %}
|
||||||
|
ВО ИЗРАБОТКА
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="border-t border-gray-200">
|
||||||
|
<dl>
|
||||||
|
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">
|
||||||
|
Уред
|
||||||
|
</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
|
{{ ticket.name }}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">
|
||||||
|
Дефект / Проблем
|
||||||
|
</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
|
{{ ticket.description }}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">
|
||||||
|
Моментален статус
|
||||||
|
</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2 font-semibold">
|
||||||
|
{{ ticket.get_status_display }}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">
|
||||||
|
Примен на
|
||||||
|
</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
|
{{ ticket.date }}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
{% if ticket.done %}
|
||||||
|
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 border-t border-gray-200">
|
||||||
|
<dt class="text-sm font-medium text-gray-900">
|
||||||
|
Забелешки за сервисот
|
||||||
|
</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
|
{{ ticket.repair|default:"Нема забелешки." }}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
<dt class="text-sm font-medium text-gray-500">
|
||||||
|
Завршено на
|
||||||
|
</dt>
|
||||||
|
<dd class="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
|
{{ ticket.date_close }}
|
||||||
|
</dd>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
|
||||||
|
<a href="{% url 'track_ticket' %}" class="font-medium text-gray-900 hover:text-gray-700">Провери друг налог →</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
@@ -19,13 +19,24 @@ from django.urls import path
|
|||||||
import serviceCRM.views as view
|
import serviceCRM.views as view
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", view.InsertListView.as_view(), name="index"),
|
path("", view.home, name="home"),
|
||||||
|
path("dashboard/", view.InsertListView.as_view(), name="dashboard"),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path("insert/", view.InsertNew.insert, name="insert"),
|
path("insert/", view.InsertNew.insert, name="insert"),
|
||||||
path("edit/<int:pk>/", view.Update.as_view(), name="update"),
|
path("logout/", view.logout_view, name="logout"),
|
||||||
path("nalog/<int:id>/", view.Nalog, name="nalog"),
|
path("track/", view.track_ticket, name="track_ticket"),
|
||||||
path("delete/<int:id>/", view.Delete.delete, name="delete"),
|
path("ticket/<str:ticket_id>/", view.Nalog, name="nalog"),
|
||||||
|
|
||||||
|
# Secure public links
|
||||||
|
path("print/label/<str:ticket_id>/", view.print_label, name="print_label"),
|
||||||
|
path("print/receipt/<str:ticket_id>/", view.print_receipt, name="print_receipt"),
|
||||||
|
|
||||||
|
# Secure staff actions
|
||||||
|
path("edit/<str:ticket_id>/", view.Update.as_view(), name="update"),
|
||||||
|
path("close/<str:ticket_id>/", view.CloseTicketView.as_view(), name="close_ticket"),
|
||||||
|
path("delete/<str:ticket_id>/", view.Delete.delete, name="delete"),
|
||||||
|
|
||||||
path("done/", view.Done.as_view(), name="done"),
|
path("done/", view.Done.as_view(), name="done"),
|
||||||
path("done/<int:id>/", view.Done.done_by_id, name="done"),
|
path("done/<str:ticket_id>/", view.Done.done_by_id, name="done_detail"),
|
||||||
path("datatable/", view.DatatableView.as_view(), name="datatable"),
|
path("datatable/", view.DatatableView.as_view(), name="datatable"),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,74 +1,144 @@
|
|||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import get_object_or_404, render
|
from django.shortcuts import get_object_or_404, render, redirect
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.contrib.auth import logout
|
||||||
|
|
||||||
from .forms import InputForm
|
from .forms import InputForm, CloseForm
|
||||||
from .models import Insert
|
from .models import Insert, TicketLog
|
||||||
from .tables import DoneInsertTable, InsertTable
|
from .tables import DoneInsertTable, InsertTable
|
||||||
|
from .filter import InsertFilter, DoneFilter
|
||||||
|
|
||||||
from django_tables2 import SingleTableView
|
from django_tables2.views import SingleTableMixin,SingleTableView
|
||||||
|
from django_filters.views import FilterView
|
||||||
from datatableview.views import DatatableView
|
from datatableview.views import DatatableView
|
||||||
|
|
||||||
class InsertListView(SingleTableView):
|
class InsertListView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
||||||
model = Insert
|
model = Insert
|
||||||
table_class = InsertTable
|
table_class = InsertTable
|
||||||
template_name = 'serviceCRM/list.html'
|
template_name = 'serviceCRM/list.html'
|
||||||
|
filterset_class = InsertFilter
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Insert.objects.filter(done=False).order_by('-date', '-id')
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
# Dashboard Widgets Data
|
||||||
|
context['stats_active'] = Insert.objects.filter(done=False).count()
|
||||||
|
context['stats_today_closed'] = Insert.objects.filter(done=True, date_close=timezone.now().date()).count()
|
||||||
|
context['stats_ready'] = Insert.objects.filter(status='READY', done=False).count()
|
||||||
|
return context
|
||||||
|
|
||||||
class InsertNew(generic.View):
|
class InsertNew(generic.View):
|
||||||
model = Insert
|
model = Insert
|
||||||
template_name = "serviceCRM/form.html"
|
template_name = "serviceCRM/form.html"
|
||||||
|
|
||||||
|
@login_required
|
||||||
def insert(request):
|
def insert(request):
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
form = InputForm(request.POST)
|
form = InputForm(request.POST)
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
ticket = form.save()
|
ticket = form.save()
|
||||||
print("Raboti")
|
TicketLog.objects.create(ticket=ticket, action="Креирано", details="Налогот е креиран во системот.")
|
||||||
return HttpResponseRedirect(f"/nalog/{ticket.id}/")
|
return HttpResponseRedirect(f"/ticket/{ticket.ticket_id}/")
|
||||||
else:
|
else:
|
||||||
form = InputForm()
|
form = InputForm()
|
||||||
|
|
||||||
return render(request, InsertNew.template_name, {'form': form})
|
return render(request, InsertNew.template_name, {'form': form})
|
||||||
|
|
||||||
class Update(generic.UpdateView):
|
class Update(LoginRequiredMixin, generic.UpdateView):
|
||||||
model = Insert
|
model = Insert
|
||||||
template_name = "serviceCRM/edit.html"
|
template_name = "serviceCRM/edit.html"
|
||||||
fields = ["name", "phone", "description","note", "done", "repair", "plateno"]
|
fields = ["status", "name", "phone", "description","note", "repair", "plateno", "done"]
|
||||||
success_url = '/'
|
success_url = '/'
|
||||||
|
slug_field = 'ticket_id'
|
||||||
|
slug_url_kwarg = 'ticket_id'
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
response = super().form_valid(form)
|
||||||
|
if 'status' in form.changed_data:
|
||||||
|
TicketLog.objects.create(ticket=self.object, action="Промена на статус", details=f"Статусот е променет во {self.object.get_status_display()}")
|
||||||
|
else:
|
||||||
|
TicketLog.objects.create(ticket=self.object, action="Ажурирано", details="Деталите за налогот се ажурирани.")
|
||||||
|
return response
|
||||||
|
|
||||||
def Nalog(request, id):
|
class CloseTicketView(LoginRequiredMixin, generic.UpdateView):
|
||||||
try:
|
model = Insert
|
||||||
data = Insert.objects.get(id=id)
|
form_class = CloseForm
|
||||||
except:
|
template_name = "serviceCRM/close_ticket.html"
|
||||||
return HttpResponseRedirect("/")
|
success_url = '/done/'
|
||||||
template = "serviceCRM/nalog.html"
|
slug_field = 'ticket_id'
|
||||||
context = {"name": data.name, "phone": data.phone, "desc": data.description, "date": data.date}
|
slug_url_kwarg = 'ticket_id'
|
||||||
return render(request, template, context)
|
|
||||||
|
def form_valid(self, form):
|
||||||
class Done(SingleTableView):
|
form.instance.done = True
|
||||||
|
form.instance.date_close = timezone.now().date()
|
||||||
|
response = super().form_valid(form)
|
||||||
|
TicketLog.objects.create(ticket=self.object, action="Затворено", details="Налогот и поправката се завршени.")
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def Nalog(request, ticket_id):
|
||||||
|
data = get_object_or_404(Insert, ticket_id=ticket_id)
|
||||||
|
template = "serviceCRM/nalog.html"
|
||||||
|
context = {"data": data}
|
||||||
|
return render(request, template, context)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def print_label(request, ticket_id):
|
||||||
|
ticket = get_object_or_404(Insert, ticket_id=ticket_id)
|
||||||
|
return render(request, 'serviceCRM/print_label.html', {'ticket': ticket})
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def print_receipt(request, ticket_id):
|
||||||
|
ticket = get_object_or_404(Insert, ticket_id=ticket_id)
|
||||||
|
return render(request, 'serviceCRM/print_receipt.html', {'ticket': ticket})
|
||||||
|
|
||||||
|
class Done(LoginRequiredMixin, SingleTableMixin, FilterView):
|
||||||
model = Insert
|
model = Insert
|
||||||
table_data = Insert.objects.filter(done=True)
|
|
||||||
table_class = DoneInsertTable
|
table_class = DoneInsertTable
|
||||||
template_name = 'serviceCRM/done.html'
|
template_name = 'serviceCRM/done.html'
|
||||||
|
filterset_class = DoneFilter
|
||||||
|
|
||||||
def done_by_id(request, id):
|
def get_queryset(self):
|
||||||
try:
|
return Insert.objects.filter(done=True)
|
||||||
req = get_object_or_404(Insert, id=id)
|
|
||||||
except:
|
@login_required
|
||||||
return HttpResponseRedirect("/done/")
|
def done_by_id(request, ticket_id):
|
||||||
context = {"name": req.name, "phone": req.phone, "desc": req.description, "date": req.date}
|
req = get_object_or_404(Insert, ticket_id=ticket_id)
|
||||||
return HttpResponse(f"Report ID: {id} \nName: {req.name} \nPhone: {req.phone} \nDescription: {req.description} \n Note:{req.note} \nDate: {req.date} \nDone: {req.done} \nRepair: {req.repair} \n Plateno: {req.plateno} \n")
|
return HttpResponse(f"Report ID: {ticket_id} \nName: {req.name} \nPhone: {req.phone} \nDescription: {req.description} \n Note:{req.note} \nDate: {req.date} \nDone: {req.done} \nRepair: {req.repair} \n Plateno: {req.plateno} \n")
|
||||||
|
|
||||||
class Delete(generic.View):
|
class Delete(generic.View):
|
||||||
model = Insert
|
model = Insert
|
||||||
|
|
||||||
def delete(request, id):
|
@login_required
|
||||||
req = get_object_or_404(Insert, id=id)
|
def delete(request, ticket_id):
|
||||||
|
req = get_object_or_404(Insert, ticket_id=ticket_id)
|
||||||
req.delete()
|
req.delete()
|
||||||
return HttpResponseRedirect("/")
|
return HttpResponseRedirect("/")
|
||||||
|
|
||||||
class DatatableView(DatatableView):
|
class DatatableView(LoginRequiredMixin, DatatableView):
|
||||||
model = Insert
|
model = Insert
|
||||||
template_name = 'serviceCRM/Insert_list.html'
|
template_name = 'serviceCRM/Insert_list.html'
|
||||||
|
def track_ticket(request):
|
||||||
|
if request.method == 'GET' and 'ticket_id' in request.GET:
|
||||||
|
ticket_id = request.GET.get('ticket_id').strip()
|
||||||
|
try:
|
||||||
|
ticket = Insert.objects.get(ticket_id=ticket_id)
|
||||||
|
return render(request, 'serviceCRM/public_track_result.html', {'ticket': ticket})
|
||||||
|
except Insert.DoesNotExist:
|
||||||
|
return render(request, 'serviceCRM/public_track_form.html', {'error': 'Налогот не е пронајден. Проверете го бројот на налогот.'})
|
||||||
|
|
||||||
|
return render(request, 'serviceCRM/public_track_form.html')
|
||||||
|
def logout_view(request):
|
||||||
|
logout(request)
|
||||||
|
return redirect('/')
|
||||||
|
|
||||||
|
def home(request):
|
||||||
|
if request.user.is_authenticated:
|
||||||
|
return redirect('dashboard')
|
||||||
|
return track_ticket(request)
|
||||||
|
|||||||
488
uv.lock
generated
Normal file
488
uv.lock
generated
Normal file
@@ -0,0 +1,488 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.14"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "asgiref"
|
||||||
|
version = "3.11.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/76/b9/4db2509eabd14b4a8c71d1b24c8d5734c52b8560a7b1e1a8b56c8d25568b/asgiref-3.11.0.tar.gz", hash = "sha256:13acff32519542a1736223fb79a715acdebe24286d98e8b164a73085f40da2c4", size = 37969, upload-time = "2025-11-19T15:32:20.106Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/be/317c2c55b8bbec407257d45f5c8d1b6867abc76d12043f2d3d58c538a4ea/asgiref-3.11.0-py3-none-any.whl", hash = "sha256:1db9021efadb0d9512ce8ffaf72fcef601c7b73a8807a1bb2ef143dc6b14846d", size = 24096, upload-time = "2025-11-19T15:32:19.004Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cffi"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "click"
|
||||||
|
version = "8.3.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorama"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crispy-bootstrap5"
|
||||||
|
version = "2025.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
{ name = "django-crispy-forms" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/97/30/36cc4144b6dff91bb54490a3b474897b7469bcda9517bf9f54681ea91011/crispy_bootstrap5-2025.6.tar.gz", hash = "sha256:f1bde7cac074c650fc82f31777d4a4cfd0df2512c68bc4128f259c75d3daada4", size = 23950, upload-time = "2025-06-08T07:43:35.461Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d8/d4/8cf1ba773a91fc17bab1fd46b75bbdef780c4cccbbb8230e617980a0362c/crispy_bootstrap5-2025.6-py3-none-any.whl", hash = "sha256:a343aa128b4383f35f00295b94de2b10862f2a4f24eda21fa6ead45234c07050", size = 24794, upload-time = "2025-06-08T07:43:34.206Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crispy-tailwind"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
{ name = "django-crispy-forms" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ae/ac/a307ae5ce869d7151b90d4b8b042a48eb454a936dacc695f6418486e5bd8/crispy-tailwind-1.0.3.tar.gz", hash = "sha256:2bc9f616d406e4b003f25d46fcb0079f1c2522719d97adb107667271d849459a", size = 19172, upload-time = "2024-02-13T09:52:37.895Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/ca/11f65e24f3c182dfaf90fd3710d2dcca0fbc3026923e47b43f52a4a2349b/crispy_tailwind-1.0.3-py3-none-any.whl", hash = "sha256:31427f66b1c4fd0d6fb040f4197cfb97d104cdbe7641ea2dea940c0057c4db4b", size = 25700, upload-time = "2024-02-13T09:52:35.928Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cryptography"
|
||||||
|
version = "46.0.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/9f/33/c00162f49c0e2fe8064a62cb92b93e50c74a72bc370ab92f86112b33ff62/cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1", size = 749258, upload-time = "2025-10-15T23:18:31.74Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1d/42/9c391dd801d6cf0d561b5890549d4b27bafcc53b39c31a817e69d87c625b/cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a", size = 7225004, upload-time = "2025-10-15T23:16:52.239Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1c/67/38769ca6b65f07461eb200e85fc1639b438bdc667be02cf7f2cd6a64601c/cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc", size = 4296667, upload-time = "2025-10-15T23:16:54.369Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/49/498c86566a1d80e978b42f0d702795f69887005548c041636df6ae1ca64c/cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d", size = 4450807, upload-time = "2025-10-15T23:16:56.414Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4b/0a/863a3604112174c8624a2ac3c038662d9e59970c7f926acdcfaed8d61142/cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb", size = 4299615, upload-time = "2025-10-15T23:16:58.442Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/64/02/b73a533f6b64a69f3cd3872acb6ebc12aef924d8d103133bb3ea750dc703/cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849", size = 4016800, upload-time = "2025-10-15T23:17:00.378Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/25/d5/16e41afbfa450cde85a3b7ec599bebefaef16b5c6ba4ec49a3532336ed72/cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8", size = 4984707, upload-time = "2025-10-15T23:17:01.98Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/56/e7e69b427c3878352c2fb9b450bd0e19ed552753491d39d7d0a2f5226d41/cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec", size = 4482541, upload-time = "2025-10-15T23:17:04.078Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/78/f6/50736d40d97e8483172f1bb6e698895b92a223dba513b0ca6f06b2365339/cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91", size = 4299464, upload-time = "2025-10-15T23:17:05.483Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/00/de/d8e26b1a855f19d9994a19c702fa2e93b0456beccbcfe437eda00e0701f2/cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e", size = 4950838, upload-time = "2025-10-15T23:17:07.425Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8f/29/798fc4ec461a1c9e9f735f2fc58741b0daae30688f41b2497dcbc9ed1355/cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926", size = 4481596, upload-time = "2025-10-15T23:17:09.343Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/15/8d/03cd48b20a573adfff7652b76271078e3045b9f49387920e7f1f631d125e/cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71", size = 4426782, upload-time = "2025-10-15T23:17:11.22Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/b1/ebacbfe53317d55cf33165bda24c86523497a6881f339f9aae5c2e13e57b/cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac", size = 4698381, upload-time = "2025-10-15T23:17:12.829Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/96/92/8a6a9525893325fc057a01f654d7efc2c64b9de90413adcf605a85744ff4/cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018", size = 3055988, upload-time = "2025-10-15T23:17:14.65Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7e/bf/80fbf45253ea585a1e492a6a17efcb93467701fa79e71550a430c5e60df0/cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb", size = 3514451, upload-time = "2025-10-15T23:17:16.142Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2e/af/9b302da4c87b0beb9db4e756386a7c6c5b8003cd0e742277888d352ae91d/cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c", size = 2928007, upload-time = "2025-10-15T23:17:18.04Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/e2/a510aa736755bffa9d2f75029c229111a1d02f8ecd5de03078f4c18d91a3/cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217", size = 7158012, upload-time = "2025-10-15T23:17:19.982Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/73/dc/9aa866fbdbb95b02e7f9d086f1fccfeebf8953509b87e3f28fff927ff8a0/cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5", size = 4288728, upload-time = "2025-10-15T23:17:21.527Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c5/fd/bc1daf8230eaa075184cbbf5f8cd00ba9db4fd32d63fb83da4671b72ed8a/cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715", size = 4435078, upload-time = "2025-10-15T23:17:23.042Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/82/98/d3bd5407ce4c60017f8ff9e63ffee4200ab3e23fe05b765cab805a7db008/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54", size = 4293460, upload-time = "2025-10-15T23:17:24.885Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/26/e9/e23e7900983c2b8af7a08098db406cf989d7f09caea7897e347598d4cd5b/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459", size = 3995237, upload-time = "2025-10-15T23:17:26.449Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/15/af68c509d4a138cfe299d0d7ddb14afba15233223ebd933b4bbdbc7155d3/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422", size = 4967344, upload-time = "2025-10-15T23:17:28.06Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ca/e3/8643d077c53868b681af077edf6b3cb58288b5423610f21c62aadcbe99f4/cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7", size = 4466564, upload-time = "2025-10-15T23:17:29.665Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0e/43/c1e8726fa59c236ff477ff2b5dc071e54b21e5a1e51aa2cee1676f1c986f/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044", size = 4292415, upload-time = "2025-10-15T23:17:31.686Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/42/f9/2f8fefdb1aee8a8e3256a0568cffc4e6d517b256a2fe97a029b3f1b9fe7e/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665", size = 4931457, upload-time = "2025-10-15T23:17:33.478Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/30/9b54127a9a778ccd6d27c3da7563e9f2d341826075ceab89ae3b41bf5be2/cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3", size = 4466074, upload-time = "2025-10-15T23:17:35.158Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ac/68/b4f4a10928e26c941b1b6a179143af9f4d27d88fe84a6a3c53592d2e76bf/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20", size = 4420569, upload-time = "2025-10-15T23:17:37.188Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/49/3746dab4c0d1979888f125226357d3262a6dd40e114ac29e3d2abdf1ec55/cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de", size = 4681941, upload-time = "2025-10-15T23:17:39.236Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fd/30/27654c1dbaf7e4a3531fa1fc77986d04aefa4d6d78259a62c9dc13d7ad36/cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914", size = 3022339, upload-time = "2025-10-15T23:17:40.888Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f6/30/640f34ccd4d2a1bc88367b54b926b781b5a018d65f404d409aba76a84b1c/cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db", size = 3494315, upload-time = "2025-10-15T23:17:42.769Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ba/8b/88cc7e3bd0a8e7b861f26981f7b820e1f46aa9d26cc482d0feba0ecb4919/cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21", size = 2919331, upload-time = "2025-10-15T23:17:44.468Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fd/23/45fe7f376a7df8daf6da3556603b36f53475a99ce4faacb6ba2cf3d82021/cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936", size = 7218248, upload-time = "2025-10-15T23:17:46.294Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/27/32/b68d27471372737054cbd34c84981f9edbc24fe67ca225d389799614e27f/cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683", size = 4294089, upload-time = "2025-10-15T23:17:48.269Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/26/42/fa8389d4478368743e24e61eea78846a0006caffaf72ea24a15159215a14/cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d", size = 4440029, upload-time = "2025-10-15T23:17:49.837Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/eb/f483db0ec5ac040824f269e93dd2bd8a21ecd1027e77ad7bdf6914f2fd80/cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0", size = 4297222, upload-time = "2025-10-15T23:17:51.357Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fd/cf/da9502c4e1912cb1da3807ea3618a6829bee8207456fbbeebc361ec38ba3/cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc", size = 4012280, upload-time = "2025-10-15T23:17:52.964Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6b/8f/9adb86b93330e0df8b3dcf03eae67c33ba89958fc2e03862ef1ac2b42465/cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3", size = 4978958, upload-time = "2025-10-15T23:17:54.965Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/a0/5fa77988289c34bdb9f913f5606ecc9ada1adb5ae870bd0d1054a7021cc4/cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971", size = 4473714, upload-time = "2025-10-15T23:17:56.754Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/e5/fc82d72a58d41c393697aa18c9abe5ae1214ff6f2a5c18ac470f92777895/cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac", size = 4296970, upload-time = "2025-10-15T23:17:58.588Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/78/06/5663ed35438d0b09056973994f1aec467492b33bd31da36e468b01ec1097/cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04", size = 4940236, upload-time = "2025-10-15T23:18:00.897Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fc/59/873633f3f2dcd8a053b8dd1d38f783043b5fce589c0f6988bf55ef57e43e/cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506", size = 4472642, upload-time = "2025-10-15T23:18:02.749Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/39/8e71f3930e40f6877737d6f69248cf74d4e34b886a3967d32f919cc50d3b/cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963", size = 4423126, upload-time = "2025-10-15T23:18:04.85Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cd/c7/f65027c2810e14c3e7268353b1681932b87e5a48e65505d8cc17c99e36ae/cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4", size = 4686573, upload-time = "2025-10-15T23:18:06.908Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0a/6e/1c8331ddf91ca4730ab3086a0f1be19c65510a33b5a441cb334e7a2d2560/cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df", size = 3036695, upload-time = "2025-10-15T23:18:08.672Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/90/45/b0d691df20633eff80955a0fc7695ff9051ffce8b69741444bd9ed7bd0db/cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f", size = 3501720, upload-time = "2025-10-15T23:18:10.632Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e8/cb/2da4cc83f5edb9c3257d09e1e7ab7b23f049c7962cae8d842bbef0a9cec9/cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372", size = 2918740, upload-time = "2025-10-15T23:18:12.277Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django"
|
||||||
|
version = "6.0.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "asgiref" },
|
||||||
|
{ name = "sqlparse" },
|
||||||
|
{ name = "tzdata", marker = "sys_platform == 'win32'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b5/9b/016f7e55e855ee738a352b05139d4f8b278d0b451bd01ebef07456ef3b0e/django-6.0.1.tar.gz", hash = "sha256:ed76a7af4da21551573b3d9dfc1f53e20dd2e6c7d70a3adc93eedb6338130a5f", size = 11069565, upload-time = "2026-01-06T18:55:53.069Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/b5/814ed98bd21235c116fd3436a7ed44d47560329a6d694ec8aac2982dbb93/django-6.0.1-py3-none-any.whl", hash = "sha256:a92a4ff14f664a896f9849009cb8afaca7abe0d6fc53325f3d1895a15253433d", size = 8338791, upload-time = "2026-01-06T18:55:46.175Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-crispy-forms"
|
||||||
|
version = "2.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/79/a1/6a638d13717e4d4f8df169dade0fa51bdc65d9825df39d98ce709a776b49/django_crispy_forms-2.5.tar.gz", hash = "sha256:066e72a8f152a1334f1c811cc37740868efe3265e5a218f79079ef89f848c3d8", size = 1097999, upload-time = "2025-11-06T20:44:01.921Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/58/ac3a11950baaf75c1f3242e3af9dfe45201f6ee10c113dd37a9c000876d2/django_crispy_forms-2.5-py3-none-any.whl", hash = "sha256:adc99d5901baca09479c53bf536b3909e80a9f2bb299438a223de4c106ebf1f9", size = 31464, upload-time = "2025-11-06T20:44:00.795Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-datatable-view"
|
||||||
|
version = "2.1.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
{ name = "python-dateutil" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/05/cc/f26f6bf8cf70110fe40d86039ac01a6f407260eca1ea67ad56e529a39071/django-datatable-view-2.1.6.tar.gz", hash = "sha256:b84ba6809b35b1d018eb502b3fed5b9dec219ed87120ee54f04a601551a341af", size = 50474, upload-time = "2021-05-28T21:21:45.338Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3e/ae/026ee8aee1ad6b3d06407cccb2c3036d47a6b319b5b03986d221059e48bf/django_datatable_view-2.1.6-py2.py3-none-any.whl", hash = "sha256:74b4ac9e99ebba50f5558b62434a21fa28c681b0c9f40b875c96ff497aeba11b", size = 59862, upload-time = "2021-05-28T21:21:43.278Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-environ"
|
||||||
|
version = "0.12.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d6/04/65d2521842c42f4716225f20d8443a50804920606aec018188bbee30a6b0/django_environ-0.12.0.tar.gz", hash = "sha256:227dc891453dd5bde769c3449cf4a74b6f2ee8f7ab2361c93a07068f4179041a", size = 56804, upload-time = "2025-01-13T17:03:37.74Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/83/b3/0a3bec4ecbfee960f39b1842c2f91e4754251e0a6ed443db9fe3f666ba8f/django_environ-0.12.0-py2.py3-none-any.whl", hash = "sha256:92fb346a158abda07ffe6eb23135ce92843af06ecf8753f43adf9d2366dcc0ca", size = 19957, upload-time = "2025-01-13T17:03:32.918Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-filter"
|
||||||
|
version = "25.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/2c/e4/465d2699cd388c0005fb8d6ae6709f239917c6d8790ac35719676fffdcf3/django_filter-25.2.tar.gz", hash = "sha256:760e984a931f4468d096f5541787efb8998c61217b73006163bf2f9523fe8f23", size = 143818, upload-time = "2025-10-05T09:51:31.521Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/40/6a02495c5658beb1f31eb09952d8aa12ef3c2a66342331ce3a35f7132439/django_filter-25.2-py3-none-any.whl", hash = "sha256:9c0f8609057309bba611062fe1b720b4a873652541192d232dd28970383633e3", size = 94145, upload-time = "2025-10-05T09:51:29.728Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-pipeline"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "setuptools" },
|
||||||
|
{ name = "wheel" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/1c/7b/2f78ccdad9f45a3d4f709950160f9d7e5009b2b8bec8ef636025ec89b62e/django_pipeline-4.1.0.tar.gz", hash = "sha256:aa1d79df6f215b78396cdd50ed162f8741dc4993e9fba2c78483d9b6f1e722b4", size = 72180, upload-time = "2025-09-13T11:47:45.332Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9a/89/fd40adbf5cc16550007ad67bffd7493c9976f7e576bf431d1bc537cfa976/django_pipeline-4.1.0-py3-none-any.whl", hash = "sha256:bdb84feb8db73b9fe8298fd9d0f6e50f30d78eb28a8ed28f73ca5d154080c3d5", size = 75523, upload-time = "2025-09-13T11:42:55.754Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-tables2"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/28/ab/3533314e818b04a9e1f04ae167845fec509f4e78ebaa6cf507d12eabcce3/django_tables2-2.8.0.tar.gz", hash = "sha256:0dea3401bb99a0164ba09e20d59a7d90856fdc05e5ae2da9a14d0fa14d99257c", size = 129204, upload-time = "2025-11-21T10:17:43.813Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/8b/047bb29359953b1c76f8886377845538144033d87da622c6b743f597a50b/django_tables2-2.8.0-py3-none-any.whl", hash = "sha256:860633b0f448216af73fca6005c7e38dc9b46931dc36c08a9281a71ee250b1ee", size = 95968, upload-time = "2025-11-21T10:17:42.363Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dnspython"
|
||||||
|
version = "2.8.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/8c/8b/57666417c0f90f08bcafa776861060426765fdb422eb10212086fb811d26/dnspython-2.8.0.tar.gz", hash = "sha256:181d3c6996452cb1189c4046c61599b84a5a86e099562ffde77d26984ff26d0f", size = 368251, upload-time = "2025-09-07T18:58:00.022Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ba/5a/18ad964b0086c6e62e2e7500f7edc89e3faa45033c71c1893d34eed2b2de/dnspython-2.8.0-py3-none-any.whl", hash = "sha256:01d9bbc4a2d76bf0db7c1f729812ded6d912bd318d3b1cf81d30c0f845dbf3af", size = 331094, upload-time = "2025-09-07T18:57:58.071Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "feedparser"
|
||||||
|
version = "6.0.12"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "sgmllib3k" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/dc/79/db7edb5e77d6dfbc54d7d9df72828be4318275b2e580549ff45a962f6461/feedparser-6.0.12.tar.gz", hash = "sha256:64f76ce90ae3e8ef5d1ede0f8d3b50ce26bcce71dd8ae5e82b1cd2d4a5f94228", size = 286579, upload-time = "2025-09-10T13:33:59.486Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4e/eb/c96d64137e29ae17d83ad2552470bafe3a7a915e85434d9942077d7fd011/feedparser-6.0.12-py3-none-any.whl", hash = "sha256:6bbff10f5a52662c00a2e3f86a38928c37c48f77b3c511aedcd51de933549324", size = 81480, upload-time = "2025-09-10T13:33:58.022Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "h11"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nanoid"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/b7/9d/0250bf5935d88e214df469d35eccc0f6ff7e9db046fc8a9aeb4b2a192775/nanoid-2.0.0.tar.gz", hash = "sha256:5a80cad5e9c6e9ae3a41fa2fb34ae189f7cb420b2a5d8f82bd9d23466e4efa68", size = 3290, upload-time = "2018-11-20T14:45:51.578Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2e/0d/8630f13998638dc01e187fadd2e5c6d42d127d08aeb4943d231664d6e539/nanoid-2.0.0-py3-none-any.whl", hash = "sha256:90aefa650e328cffb0893bbd4c236cfd44c48bc1f2d0b525ecc53c3187b653bb", size = 5844, upload-time = "2018-11-20T14:45:50.165Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pillow"
|
||||||
|
version = "12.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d0/02/d52c733a2452ef1ffcc123b68e6606d07276b0e358db70eabad7e40042b7/pillow-12.1.0.tar.gz", hash = "sha256:5c5ae0a06e9ea030ab786b0251b32c7e4ce10e58d983c0d5c56029455180b5b9", size = 46977283, upload-time = "2026-01-02T09:13:29.892Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8c/87/bdf971d8bbcf80a348cc3bacfcb239f5882100fe80534b0ce67a784181d8/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:5cb7bc1966d031aec37ddb9dcf15c2da5b2e9f7cc3ca7c54473a20a927e1eb91", size = 4062533, upload-time = "2026-01-02T09:12:20.791Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ff/4f/5eb37a681c68d605eb7034c004875c81f86ec9ef51f5be4a63eadd58859a/pillow-12.1.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:97e9993d5ed946aba26baf9c1e8cf18adbab584b99f452ee72f7ee8acb882796", size = 4138546, upload-time = "2026-01-02T09:12:23.664Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/11/6d/19a95acb2edbace40dcd582d077b991646b7083c41b98da4ed7555b59733/pillow-12.1.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:414b9a78e14ffeb98128863314e62c3f24b8a86081066625700b7985b3f529bd", size = 3601163, upload-time = "2026-01-02T09:12:26.338Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fc/36/2b8138e51cb42e4cc39c3297713455548be855a50558c3ac2beebdc251dd/pillow-12.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6bdb408f7c9dd2a5ff2b14a3b0bb6d4deb29fb9961e6eb3ae2031ae9a5cec13", size = 5266086, upload-time = "2026-01-02T09:12:28.782Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/53/4b/649056e4d22e1caa90816bf99cef0884aed607ed38075bd75f091a607a38/pillow-12.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:3413c2ae377550f5487991d444428f1a8ae92784aac79caa8b1e3b89b175f77e", size = 4657344, upload-time = "2026-01-02T09:12:31.117Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/6b/c5742cea0f1ade0cd61485dc3d81f05261fc2276f537fbdc00802de56779/pillow-12.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e5dcbe95016e88437ecf33544ba5db21ef1b8dd6e1b434a2cb2a3d605299e643", size = 6232114, upload-time = "2026-01-02T09:12:32.936Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/8f/9f521268ce22d63991601aafd3d48d5ff7280a246a1ef62d626d67b44064/pillow-12.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d0a7735df32ccbcc98b98a1ac785cc4b19b580be1bdf0aeb5c03223220ea09d5", size = 8042708, upload-time = "2026-01-02T09:12:34.78Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1a/eb/257f38542893f021502a1bbe0c2e883c90b5cff26cc33b1584a841a06d30/pillow-12.1.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c27407a2d1b96774cbc4a7594129cc027339fd800cd081e44497722ea1179de", size = 6347762, upload-time = "2026-01-02T09:12:36.748Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/5a/8ba375025701c09b309e8d5163c5a4ce0102fa86bbf8800eb0d7ac87bc51/pillow-12.1.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15c794d74303828eaa957ff8070846d0efe8c630901a1c753fdc63850e19ecd9", size = 7039265, upload-time = "2026-01-02T09:12:39.082Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cf/dc/cf5e4cdb3db533f539e88a7bbf9f190c64ab8a08a9bc7a4ccf55067872e4/pillow-12.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c990547452ee2800d8506c4150280757f88532f3de2a58e3022e9b179107862a", size = 6462341, upload-time = "2026-01-02T09:12:40.946Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d0/47/0291a25ac9550677e22eda48510cfc4fa4b2ef0396448b7fbdc0a6946309/pillow-12.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b63e13dd27da389ed9475b3d28510f0f954bca0041e8e551b2a4eb1eab56a39a", size = 7165395, upload-time = "2026-01-02T09:12:42.706Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4f/4c/e005a59393ec4d9416be06e6b45820403bb946a778e39ecec62f5b2b991e/pillow-12.1.0-cp314-cp314-win32.whl", hash = "sha256:1a949604f73eb07a8adab38c4fe50791f9919344398bdc8ac6b307f755fc7030", size = 6431413, upload-time = "2026-01-02T09:12:44.944Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1c/af/f23697f587ac5f9095d67e31b81c95c0249cd461a9798a061ed6709b09b5/pillow-12.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f9f6a650743f0ddee5593ac9e954ba1bdbc5e150bc066586d4f26127853ab94", size = 7176779, upload-time = "2026-01-02T09:12:46.727Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b3/36/6a51abf8599232f3e9afbd16d52829376a68909fe14efe29084445db4b73/pillow-12.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:808b99604f7873c800c4840f55ff389936ef1948e4e87645eaf3fccbc8477ac4", size = 2543105, upload-time = "2026-01-02T09:12:49.243Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/82/54/2e1dd20c8749ff225080d6ba465a0cab4387f5db0d1c5fb1439e2d99923f/pillow-12.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bc11908616c8a283cf7d664f77411a5ed2a02009b0097ff8abbba5e79128ccf2", size = 5268571, upload-time = "2026-01-02T09:12:51.11Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/57/61/571163a5ef86ec0cf30d265ac2a70ae6fc9e28413d1dc94fa37fae6bda89/pillow-12.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:896866d2d436563fa2a43a9d72f417874f16b5545955c54a64941e87c1376c61", size = 4660426, upload-time = "2026-01-02T09:12:52.865Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5e/e1/53ee5163f794aef1bf84243f755ee6897a92c708505350dd1923f4afec48/pillow-12.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8e178e3e99d3c0ea8fc64b88447f7cac8ccf058af422a6cedc690d0eadd98c51", size = 6269908, upload-time = "2026-01-02T09:12:54.884Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/0b/b4b4106ff0ee1afa1dc599fde6ab230417f800279745124f6c50bcffed8e/pillow-12.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:079af2fb0c599c2ec144ba2c02766d1b55498e373b3ac64687e43849fbbef5bc", size = 8074733, upload-time = "2026-01-02T09:12:56.802Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/9f/80b411cbac4a732439e629a26ad3ef11907a8c7fc5377b7602f04f6fe4e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bdec5e43377761c5dbca620efb69a77f6855c5a379e32ac5b158f54c84212b14", size = 6381431, upload-time = "2026-01-02T09:12:58.823Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8f/b7/d65c45db463b66ecb6abc17c6ba6917a911202a07662247e1355ce1789e7/pillow-12.1.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:565c986f4b45c020f5421a4cea13ef294dde9509a8577f29b2fc5edc7587fff8", size = 7068529, upload-time = "2026-01-02T09:13:00.885Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/50/96/dfd4cd726b4a45ae6e3c669fc9e49deb2241312605d33aba50499e9d9bd1/pillow-12.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:43aca0a55ce1eefc0aefa6253661cb54571857b1a7b2964bd8a1e3ef4b729924", size = 6492981, upload-time = "2026-01-02T09:13:03.314Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4d/1c/b5dc52cf713ae46033359c5ca920444f18a6359ce1020dd3e9c553ea5bc6/pillow-12.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0deedf2ea233722476b3a81e8cdfbad786f7adbed5d848469fa59fe52396e4ef", size = 7191878, upload-time = "2026-01-02T09:13:05.276Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/53/26/c4188248bd5edaf543864fe4834aebe9c9cb4968b6f573ce014cc42d0720/pillow-12.1.0-cp314-cp314t-win32.whl", hash = "sha256:b17fbdbe01c196e7e159aacb889e091f28e61020a8abeac07b68079b6e626988", size = 6438703, upload-time = "2026-01-02T09:13:07.491Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b8/0e/69ed296de8ea05cb03ee139cee600f424ca166e632567b2d66727f08c7ed/pillow-12.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:27b9baecb428899db6c0de572d6d305cfaf38ca1596b5c0542a5182e3e74e8c6", size = 7182927, upload-time = "2026-01-02T09:13:09.841Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fc/f5/68334c015eed9b5cff77814258717dec591ded209ab5b6fb70e2ae873d1d/pillow-12.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:f61333d817698bdcdd0f9d7793e365ac3d2a21c1f1eb02b32ad6aefb8d8ea831", size = 2545104, upload-time = "2026-01-02T09:13:12.068Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "publicsuffix"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d5/70/8124bab47b4b83c5846e124e91e8958696200acabc7404d3765f44212f8d/publicsuffix-1.1.1.tar.gz", hash = "sha256:22ce1d65ab6af5e9b2122e2443facdb93fb5c4abf24138099cb10fe7989f43b6", size = 66870, upload-time = "2019-12-01T13:44:51.867Z" }
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pycparser"
|
||||||
|
version = "2.23"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-dateutil"
|
||||||
|
version = "2.9.0.post0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "six" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pytz"
|
||||||
|
version = "2025.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f8/bf/abbd3cdfb8fbc7fb3d4d38d320f2441b1e7cbe29be4f23797b4a2b5d8aac/pytz-2025.2.tar.gz", hash = "sha256:360b9e3dbb49a209c21ad61809c7fb453643e048b38924c765813546746e81c3", size = 320884, upload-time = "2025-03-25T02:25:00.538Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/81/c4/34e93fe5f5429d7570ec1fa436f1986fb1f00c3e0f43a589fe2bbcd22c3f/pytz-2025.2-py2.py3-none-any.whl", hash = "sha256:5ddf76296dd8c44c26eb8f4b6f35488f3ccbf6fbbd7adee0b7262d43f0ec2f00", size = 509225, upload-time = "2025-03-25T02:24:58.468Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "servicecrm"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "asgiref" },
|
||||||
|
{ name = "cffi" },
|
||||||
|
{ name = "crispy-bootstrap5" },
|
||||||
|
{ name = "crispy-tailwind" },
|
||||||
|
{ name = "cryptography" },
|
||||||
|
{ name = "django" },
|
||||||
|
{ name = "django-crispy-forms" },
|
||||||
|
{ name = "django-datatable-view" },
|
||||||
|
{ name = "django-environ" },
|
||||||
|
{ name = "django-filter" },
|
||||||
|
{ name = "django-pipeline" },
|
||||||
|
{ name = "django-tables2" },
|
||||||
|
{ name = "dnspython" },
|
||||||
|
{ name = "feedparser" },
|
||||||
|
{ name = "nanoid" },
|
||||||
|
{ name = "pillow" },
|
||||||
|
{ name = "publicsuffix" },
|
||||||
|
{ name = "pycparser" },
|
||||||
|
{ name = "python-dateutil" },
|
||||||
|
{ name = "pytz" },
|
||||||
|
{ name = "sgmllib3k" },
|
||||||
|
{ name = "six" },
|
||||||
|
{ name = "sqlparse" },
|
||||||
|
{ name = "tablib" },
|
||||||
|
{ name = "typing-extensions" },
|
||||||
|
{ name = "tzdata" },
|
||||||
|
{ name = "uvicorn" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [
|
||||||
|
{ name = "asgiref", specifier = ">=3.11.0" },
|
||||||
|
{ name = "cffi", specifier = ">=2.0.0" },
|
||||||
|
{ name = "crispy-bootstrap5", specifier = ">=2025.6" },
|
||||||
|
{ name = "crispy-tailwind", specifier = ">=1.0.3" },
|
||||||
|
{ name = "cryptography", specifier = ">=46.0.3" },
|
||||||
|
{ name = "django", specifier = ">=6.0.1" },
|
||||||
|
{ name = "django-crispy-forms", specifier = ">=2.5" },
|
||||||
|
{ name = "django-datatable-view", specifier = ">=2.1.6" },
|
||||||
|
{ name = "django-environ", specifier = ">=0.12.0" },
|
||||||
|
{ name = "django-filter", specifier = ">=25.2" },
|
||||||
|
{ name = "django-pipeline", specifier = ">=4.1.0" },
|
||||||
|
{ name = "django-tables2", specifier = ">=2.8.0" },
|
||||||
|
{ name = "dnspython", specifier = ">=2.8.0" },
|
||||||
|
{ name = "feedparser", specifier = ">=6.0.12" },
|
||||||
|
{ name = "nanoid", specifier = ">=2.0.0" },
|
||||||
|
{ name = "pillow", specifier = ">=12.1.0" },
|
||||||
|
{ name = "publicsuffix", specifier = ">=1.1.1" },
|
||||||
|
{ name = "pycparser", specifier = ">=2.23" },
|
||||||
|
{ name = "python-dateutil", specifier = ">=2.9.0.post0" },
|
||||||
|
{ name = "pytz", specifier = ">=2025.2" },
|
||||||
|
{ name = "sgmllib3k", specifier = ">=1.0.0" },
|
||||||
|
{ name = "six", specifier = ">=1.17.0" },
|
||||||
|
{ name = "sqlparse", specifier = ">=0.5.5" },
|
||||||
|
{ name = "tablib", specifier = ">=3.9.0" },
|
||||||
|
{ name = "typing-extensions", specifier = ">=4.15.0" },
|
||||||
|
{ name = "tzdata", specifier = ">=2025.3" },
|
||||||
|
{ name = "uvicorn", specifier = ">=0.40.0" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "setuptools"
|
||||||
|
version = "80.9.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sgmllib3k"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/9e/bd/3704a8c3e0942d711c1299ebf7b9091930adae6675d7c8f476a7ce48653c/sgmllib3k-1.0.0.tar.gz", hash = "sha256:7868fb1c8bfa764c1ac563d3cf369c381d1325d36124933a726f29fcdaa812e9", size = 5750, upload-time = "2010-08-24T14:33:52.445Z" }
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "six"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sqlparse"
|
||||||
|
version = "0.5.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/90/76/437d71068094df0726366574cf3432a4ed754217b436eb7429415cf2d480/sqlparse-0.5.5.tar.gz", hash = "sha256:e20d4a9b0b8585fdf63b10d30066c7c94c5d7a7ec47c889a2d83a3caa93ff28e", size = 120815, upload-time = "2025-12-19T07:17:45.073Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/49/4b/359f28a903c13438ef59ebeee215fb25da53066db67b305c125f1c6d2a25/sqlparse-0.5.5-py3-none-any.whl", hash = "sha256:12a08b3bf3eec877c519589833aed092e2444e68240a3577e8e26148acc7b1ba", size = 46138, upload-time = "2025-12-19T07:17:46.573Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tablib"
|
||||||
|
version = "3.9.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/11/00/416d2ba54d7d58a7f7c61bf62dfeb48fd553cf49614daf83312f2d2c156e/tablib-3.9.0.tar.gz", hash = "sha256:1b6abd8edb0f35601e04c6161d79660fdcde4abb4a54f66cc9f9054bd55d5fe2", size = 125565, upload-time = "2025-10-15T18:21:56.263Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/66/6b/32e51d847148b299088fc42d3d896845fd09c5247190133ea69dbe71ba51/tablib-3.9.0-py3-none-any.whl", hash = "sha256:eda17cd0d4dda614efc0e710227654c60ddbeb1ca92cdcfc5c3bd1fc5f5a6e4a", size = 49580, upload-time = "2025-10-15T18:21:44.185Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typing-extensions"
|
||||||
|
version = "4.15.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tzdata"
|
||||||
|
version = "2025.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/c202b344c5ca7daf398f3b8a477eeb205cf3b6f32e7ec3a6bac0629ca975/tzdata-2025.3.tar.gz", hash = "sha256:de39c2ca5dc7b0344f2eba86f49d614019d29f060fc4ebc8a417896a620b56a7", size = 196772, upload-time = "2025-12-13T17:45:35.667Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/b0/003792df09decd6849a5e39c28b513c06e84436a54440380862b5aeff25d/tzdata-2025.3-py2.py3-none-any.whl", hash = "sha256:06a47e5700f3081aab02b2e513160914ff0694bce9947d6b76ebd6bf57cfc5d1", size = 348521, upload-time = "2025-12-13T17:45:33.889Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uvicorn"
|
||||||
|
version = "0.40.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "click" },
|
||||||
|
{ name = "h11" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/c3/d1/8f3c683c9561a4e6689dd3b1d345c815f10f86acd044ee1fb9a4dcd0b8c5/uvicorn-0.40.0.tar.gz", hash = "sha256:839676675e87e73694518b5574fd0f24c9d97b46bea16df7b8c05ea1a51071ea", size = 81761, upload-time = "2025-12-21T14:16:22.45Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/d8/2083a1daa7439a66f3a48589a57d576aa117726762618f6bb09fe3798796/uvicorn-0.40.0-py3-none-any.whl", hash = "sha256:c6c8f55bc8bf13eb6fa9ff87ad62308bbbc33d0b67f84293151efe87e0d5f2ee", size = 68502, upload-time = "2025-12-21T14:16:21.041Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wheel"
|
||||||
|
version = "0.45.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/8a/98/2d9906746cdc6a6ef809ae6338005b3f21bb568bea3165cfc6a243fdc25c/wheel-0.45.1.tar.gz", hash = "sha256:661e1abd9198507b1409a20c02106d9670b2576e916d58f520316666abca6729", size = 107545, upload-time = "2024-11-23T00:18:23.513Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0b/2c/87f3254fd8ffd29e4c02732eee68a83a1d3c346ae39bc6822dcbcb697f2b/wheel-0.45.1-py3-none-any.whl", hash = "sha256:708e7481cc80179af0e556bbf0cc00b8444c7321e2700b8d8580231d13017248", size = 72494, upload-time = "2024-11-23T00:18:21.207Z" },
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user