+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -99,9 +137,10 @@
-
+
+
@@ -117,6 +156,15 @@
+
+
+
+
+
+
+
+
+
@@ -263,11 +311,6 @@
7
-
- file://$PROJECT_DIR$/DepoT/settings.py
- 12
-
-
file://$PROJECT_DIR$/static/js/container_validation.js
4
@@ -283,4 +326,7 @@
+
+
+
\ No newline at end of file
diff --git a/DepoT/__pycache__/settings.cpython-311.pyc b/DepoT/__pycache__/settings.cpython-311.pyc
deleted file mode 100644
index 6862117..0000000
Binary files a/DepoT/__pycache__/settings.cpython-311.pyc and /dev/null differ
diff --git a/DepoT/__pycache__/settings.cpython-313.pyc b/DepoT/__pycache__/settings.cpython-313.pyc
deleted file mode 100644
index d76232a..0000000
Binary files a/DepoT/__pycache__/settings.cpython-313.pyc and /dev/null differ
diff --git a/DepoT/settings/__init__.py b/DepoT/settings/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/DepoT/settings/base.py b/DepoT/settings/base.py
new file mode 100644
index 0000000..e69de29
diff --git a/DepoT/settings/development.py b/DepoT/settings/development.py
new file mode 100644
index 0000000..73c3455
--- /dev/null
+++ b/DepoT/settings/development.py
@@ -0,0 +1,188 @@
+"""
+Django settings for DepoT project.
+
+Generated by 'django-admin startproject' using Django 5.2.3.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.2/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/5.2/ref/settings/
+"""
+
+from pathlib import Path
+import os
+import environ
+
+BASE_DIR = Path(__file__).resolve().parent.parent.parent
+
+env = environ.Env()
+environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
+
+SECRET_KEY = "django-insecure-g%187p84o9^rr)3#9@r3n^o2v1i%@6=+puxm7hlodg+kbsk%n#"
+
+DEBUG = True
+
+ALLOWED_HOSTS = ['192.168.24.43', '127.0.0.1', 'localhost', ]
+
+
+PROJECT_APPS = [
+ 'accounts',
+ "booking",
+ "common",
+ "containers",
+ 'preinfo',
+ 'payments',
+]
+
+
+INSTALLED_APPS = [
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.messages",
+ "django.contrib.staticfiles",
+ "rest_framework",
+ "damages_api",
+] + PROJECT_APPS
+
+MIDDLEWARE = [
+ "django.middleware.security.SecurityMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
+]
+
+ROOT_URLCONF = "DepoT.urls"
+
+TEMPLATES = [
+ {
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": [BASE_DIR / 'templates']
+ ,
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": [
+ "django.template.context_processors.request",
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = "DepoT.wsgi.application"
+
+
+# Database
+# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
+
+# DATABASES = {
+# "default": {
+# "ENGINE": "django.db.backends.sqlite3",
+# "NAME": BASE_DIR / "db.sqlite3",
+# }
+# }
+
+DATABASES = {
+ "default": {
+ "ENGINE": "django.db.backends.postgresql",
+ "NAME": env("DB_NAME"),
+ "USER": env("DB_USER"),
+ "PASSWORD": env("DB_PASSWORD"),
+ "HOST": env("DB_HOST"),
+ "PORT": env("DB_PORT"),
+ }
+}
+# Password validation
+# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
+
+AUTH_USER_MODEL = 'accounts.DepotUser'
+
+LOGIN_URL = '/user/login/'
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
+ },
+ {
+ "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
+ },
+ {
+ "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
+ },
+ {
+ "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
+ },
+]
+
+AUTHENTICATION_BACKENDS = [
+ 'django.contrib.auth.backends.ModelBackend',
+]
+
+# Internationalization
+# https://docs.djangoproject.com/en/5.2/topics/i18n/
+
+LANGUAGE_CODE = "en-us"
+
+TIME_ZONE = "UTC"
+
+USE_I18N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/5.2/howto/static-files/
+
+STATIC_URL = "static/"
+
+STATICFILES_DIRS = [
+ BASE_DIR / 'static'
+]
+
+TEMP_FILE_FOLDER = "/tmp/damages_photos"
+# Default primary key field type
+# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+
+
+OWNCLOUD_URL = env('OWNCLOUD_URL')
+OWNCLOUD_USER = env('OWNCLOUD_USER')
+OWNCLOUD_PASSWORD = env('OWNCLOUD_PASSWORD')
+OWNCLOUD_DAMAGES_FOLDER = env('OWNCLOUD_DAMAGES_FOLDER')
+
+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+EMAIL_HOST = env("EMAIL_HOST", cast=str, default=None)
+EMAIL_PORT = env("EMAIL_PORT", cast=str, default='587') # Recommended
+EMAIL_HOST_USER = env("EMAIL_HOST_USER", cast=str, default=None)
+EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD", cast=str, default=None)
+EMAIL_USE_TLS = env("EMAIL_USE_TLS", cast=bool, default=True) # Use EMAIL_PORT 587 for TLS
+EMAIL_USE_SSL = env("EMAIL_USE_SSL", cast=bool, default=False) # EUse MAIL_PORT 465 for SSL
+
+ADMIN_USER_NAME=env("ADMIN_USER_NAME")
+ADMIN_USER_PASSWORD=env("ADMIN_USER_PASSWORD")
+ADMIN_USER_EMAIL=env("ADMIN_USER_EMAIL")
+
+MANAGERS=[]
+ADMINS=[]
+if all([ADMIN_USER_NAME, ADMIN_USER_EMAIL]):
+ ADMINS +=[
+ (f'{ADMIN_USER_NAME}', f'{ADMIN_USER_EMAIL}')
+ ]
+ MANAGERS=ADMINS
+
+
+MINIO_ENDPOINT = env('MINIO_ENDPOINT')
+AWS_S3_CUSTOM_DOMAIN = env('AWS_S3_CUSTOM_DOMAIN')
+MINIO_SERVER_URL = env('MINIO_SERVER_URL')
+AWS_S3_URL_PROTOCOL = env('AWS_S3_URL_PROTOCOL')
+MINIO_ACCESS_KEY = env('MINIO_ACCESS_KEY')
+MINIO_SECRET_KEY = env('MINIO_SECRET_KEY')
+MINIO_BUCKET_NAME = env('MINIO_BUCKET_NAME')
+MINIO_SECURE = False # Set to True if using HTTPS
\ No newline at end of file
diff --git a/DepoT/settings/production.py b/DepoT/settings/production.py
new file mode 100644
index 0000000..92c0fc8
--- /dev/null
+++ b/DepoT/settings/production.py
@@ -0,0 +1,247 @@
+"""
+Django settings for DepoT project.
+
+Generated by 'django-admin startproject' using Django 5.2.3.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/5.2/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/5.2/ref/settings/
+"""
+
+from pathlib import Path
+import os
+import environ
+from minio_backend.storage import MinioStaticStorage, MinioMediaStorage
+
+BASE_DIR = Path(__file__).resolve().parent.parent.parent
+
+env = environ.Env()
+environ.Env.read_env(os.path.join(BASE_DIR, 'production.env'))
+
+SECRET_KEY = "django-insecure-g%187p84o9^rr)3#9@r3n^o2v1i%@6=+puxm7hlodg+kbsk%n#"
+
+DEBUG = False
+
+ALLOWED_HOSTS = ['192.168.24.43', '127.0.0.1', 'localhost', 'depot.kikimor.com', ]
+
+CSRF_TRUSTED_ORIGINS = ['https://depot.kikimor.com']
+SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
+USE_X_FORWARDED_HOST = True
+
+
+PROJECT_APPS = [
+ 'accounts',
+ "booking",
+ "common",
+ "containers",
+ 'preinfo',
+ 'payments',
+]
+
+
+INSTALLED_APPS = [
+ "django.contrib.admin",
+ "django.contrib.auth",
+ "django.contrib.contenttypes",
+ "django.contrib.sessions",
+ "django.contrib.messages",
+ "django.contrib.staticfiles",
+ "rest_framework",
+ "damages_api",
+ "django_minio_backend",
+ "minio_backend",
+] + PROJECT_APPS
+
+MIDDLEWARE = [
+ "django.middleware.security.SecurityMiddleware",
+ "django.contrib.sessions.middleware.SessionMiddleware",
+ "django.middleware.common.CommonMiddleware",
+ "django.middleware.csrf.CsrfViewMiddleware",
+ "django.contrib.auth.middleware.AuthenticationMiddleware",
+ "django.contrib.messages.middleware.MessageMiddleware",
+ "django.middleware.clickjacking.XFrameOptionsMiddleware",
+]
+
+ROOT_URLCONF = "DepoT.urls"
+
+TEMPLATES = [
+ {
+ "BACKEND": "django.template.backends.django.DjangoTemplates",
+ "DIRS": [BASE_DIR / 'templates']
+ ,
+ "APP_DIRS": True,
+ "OPTIONS": {
+ "context_processors": [
+ "django.template.context_processors.request",
+ "django.contrib.auth.context_processors.auth",
+ "django.contrib.messages.context_processors.messages",
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = "DepoT.wsgi.application"
+
+
+# Database
+# https://docs.djangoproject.com/en/5.2/ref/settings/#databases
+
+# DATABASES = {
+# "default": {
+# "ENGINE": "django.db.backends.sqlite3",
+# "NAME": BASE_DIR / "db.sqlite3",
+# }
+# }
+
+DATABASES = {
+ "default": {
+ "ENGINE": "django.db.backends.postgresql",
+ "NAME": env("DB_NAME"),
+ "USER": env("DB_USER"),
+ "PASSWORD": env("DB_PASSWORD"),
+ "HOST": env("DB_HOST"),
+ "PORT": env("DB_PORT"),
+ }
+}
+# Password validation
+# https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators
+
+AUTH_USER_MODEL = 'accounts.DepotUser'
+
+LOGIN_URL = '/user/login/'
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
+ },
+ {
+ "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
+ },
+ {
+ "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
+ },
+ {
+ "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
+ },
+]
+
+AUTHENTICATION_BACKENDS = [
+ 'django.contrib.auth.backends.ModelBackend',
+]
+
+# Internationalization
+# https://docs.djangoproject.com/en/5.2/topics/i18n/
+
+LANGUAGE_CODE = "en-us"
+
+TIME_ZONE = "UTC"
+
+USE_I18N = True
+
+USE_TZ = True
+
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/5.2/howto/static-files/
+
+STATIC_URL = "static/"
+
+STATICFILES_DIRS = [
+ BASE_DIR / 'static'
+]
+
+STATIC_ROOT = BASE_DIR / 'staticfiles'
+
+TEMP_FILE_FOLDER = "/tmp/damages_photos"
+# Default primary key field type
+# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
+
+
+OWNCLOUD_URL = env('OWNCLOUD_URL')
+OWNCLOUD_USER = env('OWNCLOUD_USER')
+OWNCLOUD_PASSWORD = env('OWNCLOUD_PASSWORD')
+OWNCLOUD_DAMAGES_FOLDER = env('OWNCLOUD_DAMAGES_FOLDER')
+
+EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
+EMAIL_HOST = env("EMAIL_HOST", cast=str, default=None)
+EMAIL_PORT = env("EMAIL_PORT", cast=str, default='587') # Recommended
+EMAIL_HOST_USER = env("EMAIL_HOST_USER", cast=str, default=None)
+EMAIL_HOST_PASSWORD = env("EMAIL_HOST_PASSWORD", cast=str, default=None)
+EMAIL_USE_TLS = env("EMAIL_USE_TLS", cast=bool, default=True) # Use EMAIL_PORT 587 for TLS
+EMAIL_USE_SSL = env("EMAIL_USE_SSL", cast=bool, default=False) # EUse MAIL_PORT 465 for SSL
+
+ADMIN_USER_NAME=env("ADMIN_USER_NAME")
+ADMIN_USER_PASSWORD=env("ADMIN_USER_PASSWORD")
+ADMIN_USER_EMAIL=env("ADMIN_USER_EMAIL")
+
+MANAGERS=[]
+ADMINS=[]
+if all([ADMIN_USER_NAME, ADMIN_USER_EMAIL]):
+ ADMINS +=[
+ (f'{ADMIN_USER_NAME}', f'{ADMIN_USER_EMAIL}')
+ ]
+ MANAGERS=ADMINS
+
+
+MINIO_ENDPOINT = env('MINIO_ENDPOINT')
+AWS_S3_CUSTOM_DOMAIN = env('AWS_S3_CUSTOM_DOMAIN')
+MINIO_SERVER_URL = env('MINIO_SERVER_URL')
+MINIO_EXTERNAL_ENDPOINT = AWS_S3_CUSTOM_DOMAIN
+MINIO_EXTERNAL_ENDPOINT_USE_HTTPS = True
+AWS_S3_URL_PROTOCOL = env('AWS_S3_URL_PROTOCOL')
+MINIO_ACCESS_KEY = env('MINIO_ACCESS_KEY')
+MINIO_SECRET_KEY = env('MINIO_SECRET_KEY')
+MINIO_BUCKET_NAME = env('MINIO_BUCKET_NAME')
+MINIO_SECURE = False # Set to True if using HTTPS
+MINIO_USE_HTTPS = False # Add this line
+MINIO_STATIC_BUCKET = env('MINIO_STATIC_BUCKET_NAME')
+
+# django-minio-backend settings
+MINIO_STORAGE_ENDPOINT = AWS_S3_CUSTOM_DOMAIN
+MINIO_STORAGE_PORT = 443 if MINIO_SECURE else 80 # Add this line
+MINIO_STORAGE_ACCESS_KEY = env('MINIO_ACCESS_KEY')
+MINIO_STORAGE_SECRET_KEY = env('MINIO_SECRET_KEY')
+MINIO_STORAGE_USE_HTTPS = True
+MINIO_STORAGE_MEDIA_BUCKET_NAME = env('MINIO_BUCKET_NAME') # For user-uploaded media
+MINIO_STORAGE_STATIC_BUCKET_NAME = env('MINIO_STATIC_BUCKET_NAME') # For static files
+MINIO_STORAGE_AUTO_CREATE_MEDIA_BUCKET = True
+MINIO_STORAGE_AUTO_CREATE_STATIC_BUCKET = True
+MINIO_STORAGE_AUTO_CREATE_POLICY = True
+MINIO_STORAGE_MEDIA_BASE_URL = f'{AWS_S3_URL_PROTOCOL}://{AWS_S3_CUSTOM_DOMAIN}/{env("MINIO_BUCKET_NAME")}/'
+MINIO_STORAGE_STATIC_BASE_URL = f'{AWS_S3_URL_PROTOCOL}://{AWS_S3_CUSTOM_DOMAIN}/{env("MINIO_STATIC_BUCKET_NAME")}/'
+
+MINIO_PRIVATE_BUCKETS = [] # If you have any private buckets
+
+MINIO_PUBLIC_BUCKETS = [
+ env('MINIO_BUCKET_NAME'), # Just the bucket name as a string
+ env('MINIO_STATIC_BUCKET_NAME') # Just the bucket name as a string
+]
+
+MINIO_POLICY_ACTIONS = {
+ 'GET': ['get_object'],
+ 'PUT': ['put_object'],
+ 'DELETE': ['delete_object'],
+ 'LIST': ['list_multipart_uploads',
+ 'list_parts',
+ 'list_objects'],
+}
+
+STORAGES = {
+ "default": {
+ "BACKEND": "minio_backend.storage.MinioMediaStorage",
+ },
+ "staticfiles": {
+ "BACKEND": "minio_backend.storage.MinioStaticStorage",
+ },
+}
+
+DEFAULT_FILE_STORAGE = 'minio_backend.storage.MinioMediaStorage'
+STATICFILES_STORAGE = 'minio_backend.storage.MinioStaticStorage'
+STATIC_URL = f'{AWS_S3_URL_PROTOCOL}://{AWS_S3_CUSTOM_DOMAIN}/{env("MINIO_STATIC_BUCKET_NAME")}/'
+STATICFILES_LOCATION = 'static'
+MEDIA_ROOT = BASE_DIR / 'mediafiles'
+MEDIA_URL = f'{AWS_S3_URL_PROTOCOL}://{AWS_S3_CUSTOM_DOMAIN}/{env("MINIO_BUCKET_NAME")}/'
diff --git a/accounts/__pycache__/models.cpython-313.pyc b/accounts/__pycache__/models.cpython-313.pyc
index 5ac3fed..d8757d1 100644
Binary files a/accounts/__pycache__/models.cpython-313.pyc and b/accounts/__pycache__/models.cpython-313.pyc differ
diff --git a/booking/__pycache__/models.cpython-313.pyc b/booking/__pycache__/models.cpython-313.pyc
index 03801b4..9ad9cc0 100644
Binary files a/booking/__pycache__/models.cpython-313.pyc and b/booking/__pycache__/models.cpython-313.pyc differ
diff --git a/booking/models.py b/booking/models.py
index 76d0fa9..0f55f47 100644
--- a/booking/models.py
+++ b/booking/models.py
@@ -41,4 +41,7 @@ class Booking(models.Model):
@property
def containers_left(self):
- return self.container_count - (self.container_expedited_count or 0)
\ No newline at end of file
+ return self.container_count - (self.container_expedited_count or 0)
+
+ class Meta:
+ app_label = 'booking'
\ No newline at end of file
diff --git a/booking/tests.py b/booking/tests.py
index 7ce503c..d6f2ff2 100644
--- a/booking/tests.py
+++ b/booking/tests.py
@@ -1,3 +1,49 @@
-from django.test import TestCase
+from django.test import TestCase, Client
+from django.urls import reverse
+from django.contrib.auth import get_user_model
+from booking.models import Booking
+from common.models import LinesModel, ContainerTypeModel
-# Create your tests here.
+class BookingViewsTestCase(TestCase):
+ def setUp(self):
+ self.client = Client()
+ DepotUser = get_user_model()
+ self.user = DepotUser.objects.create_user(username='testuser', password='password', user_type='EM')
+ self.client.login(username='testuser', password='password')
+ self.line = LinesModel.objects.create(name='Test Line')
+ self.container_type = ContainerTypeModel.objects.create(name='20ft')
+ self.booking = Booking.objects.create(
+ number='BOOK123',
+ container_type=self.container_type,
+ container_count=10,
+ line=self.line,
+ created_by=self.user.id,
+ updated_by=self.user.id
+ )
+
+ def test_booking_list_view(self):
+ response = self.client.get(reverse('booking-list'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'booking/booking-list.html')
+ self.assertContains(response, self.booking.number)
+
+ def test_booking_create_view(self):
+ response = self.client.post(reverse('booking-create'), {
+ 'number': 'BOOK456',
+ 'container_type': self.container_type.id,
+ 'container_count': 5,
+ 'line': self.line.id,
+ })
+ self.assertEqual(response.status_code, 302)
+ self.assertTrue(Booking.objects.filter(number='BOOK456').exists())
+
+ def test_booking_update_view(self):
+ response = self.client.post(reverse('booking-update', args=[self.booking.id]), {
+ 'number': 'BOOK123-updated',
+ 'container_type': self.container_type.id,
+ 'container_count': 15,
+ 'line': self.line.id,
+ })
+ self.assertEqual(response.status_code, 302)
+ self.booking.refresh_from_db()
+ self.assertEqual(self.booking.number, 'BOOK123-updated')
\ No newline at end of file
diff --git a/booking/views/client_views.py b/booking/views/client_views.py
index c182da7..b61bf76 100644
--- a/booking/views/client_views.py
+++ b/booking/views/client_views.py
@@ -44,7 +44,7 @@ class CreateBookingView(LoginRequiredMixin, UserPassesTestMixin, LineFilterFormM
return super().form_valid(form)
def test_func(self):
- self.request.user.has_company_perm('can_edit_preinfo') or self.request.user.user_type == 'CA'
+ return self.request.user.has_company_perm('can_manage_booking') or self.request.user.user_type == 'CA'
class ClientBookingUpdateView(LoginRequiredMixin, UserPassesTestMixin, LineFilterFormMixin, CreateView):
diff --git a/common/__pycache__/models.cpython-313.pyc b/common/__pycache__/models.cpython-313.pyc
index ea73d8d..63bcfc9 100644
Binary files a/common/__pycache__/models.cpython-313.pyc and b/common/__pycache__/models.cpython-313.pyc differ
diff --git a/common/models.py b/common/models.py
index abf922c..12482f4 100644
--- a/common/models.py
+++ b/common/models.py
@@ -15,7 +15,8 @@ class NomenclatureBaseModel(models.Model):
class CompanyModel(NomenclatureBaseModel):
- ...
+ class Meta:
+ app_label = 'common'
class LinesModel(NomenclatureBaseModel):
@@ -25,8 +26,12 @@ class LinesModel(NomenclatureBaseModel):
related_name='line_company'
)
+ class Meta:
+ app_label = 'common'
+
class OperationModel(NomenclatureBaseModel):
- ...
+ class Meta:
+ app_label = 'common'
class ContainerKindModel(NomenclatureBaseModel):
...
diff --git a/containers/tests.py b/containers/tests.py
index 7ce503c..60ebdf7 100644
--- a/containers/tests.py
+++ b/containers/tests.py
@@ -1,3 +1,48 @@
-from django.test import TestCase
+from django.test import TestCase, Client
+from django.urls import reverse
+from django.contrib.auth import get_user_model
+from containers.models import Container
+from common.models import LinesModel, ContainerTypeModel
+from preinfo.models import Preinfo
-# Create your tests here.
+class ContainerViewsTestCase(TestCase):
+ def setUp(self):
+ self.client = Client()
+ DepotUser = get_user_model()
+ self.user = DepotUser.objects.create_user(username='testuser', password='password', user_type='E')
+ self.client.login(username='testuser', password='password')
+ self.line = LinesModel.objects.create(name='Test Line')
+ self.container_type = ContainerTypeModel.objects.create(name='20ft')
+ self.preinfo = Preinfo.objects.create(number='PRE123', line=self.line, container_type=self.container_type)
+ self.container = Container.objects.create(
+ number='TEST1234567',
+ line=self.line,
+ container_type=self.container_type,
+ received_by=self.user,
+ preinfo=self.preinfo
+ )
+
+ def test_container_list_view(self):
+ response = self.client.get(reverse('container-list'))
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, 'containers/container-list.html')
+ self.assertContains(response, self.container.number)
+
+ def test_container_receive_view(self):
+ response = self.client.post(reverse('container-receive'), {
+ 'number': 'TEST7654321',
+ 'line': self.line.id,
+ 'container_type': self.container_type.id,
+ 'preinfo': self.preinfo.id,
+ })
+ self.assertEqual(response.status_code, 302)
+ self.assertTrue(Container.objects.filter(number='TEST7654321').exists())
+
+ def test_container_expedition_view(self):
+ response = self.client.post(reverse('container-expedition', args=[self.container.id]), {
+ 'expedition_vehicle': 'TRUCK123',
+ })
+ self.assertEqual(response.status_code, 302)
+ self.container.refresh_from_db()
+ self.assertTrue(self.container.expedited)
+ self.assertEqual(self.container.expedition_vehicle, 'TRUCK123')
\ No newline at end of file
diff --git a/damages_api/tests.py b/damages_api/tests.py
index 7ce503c..3440085 100644
--- a/damages_api/tests.py
+++ b/damages_api/tests.py
@@ -1,3 +1,49 @@
-from django.test import TestCase
+import base64
+from unittest.mock import patch
+from django.test import TestCase, Client
+from django.urls import reverse
+from rest_framework import status
+from django.contrib.auth import get_user_model
+from containers.models import Container, ContainerPhotos
+from common.models import LinesModel, ContainerTypeModel
+from preinfo.models import Preinfo
-# Create your tests here.
+class DamagesAPITestCase(TestCase):
+ def setUp(self):
+ self.client = Client()
+ DepotUser = get_user_model()
+ self.user = DepotUser.objects.create_user(username='testuser', password='password', user_type='E')
+ self.client.login(username='testuser', password='password')
+ self.line = LinesModel.objects.create(name='Test Line')
+ self.container_type = ContainerTypeModel.objects.create(name='20ft')
+ self.preinfo = Preinfo.objects.create(number='PRE123', line=self.line, container_type=self.container_type)
+ self.container = Container.objects.create(
+ number='TEST1234567',
+ line=self.line,
+ container_type=self.container_type,
+ received_by=self.user,
+ preinfo=self.preinfo
+ )
+
+ @patch('damages_api.views.upload_damage_photo')
+ def test_damage_photo_upload(self, mock_upload):
+ mock_upload.return_value = 'http://mock-url.com/photo.jpg'
+ photo_data = base64.b64encode(b'test photo data').decode('utf-8')
+ response = self.client.post(reverse('damages-api', args=[self.container.id]), {
+ 'photo': photo_data,
+ 'photo_extension': 'jpg',
+ }, content_type='application/json')
+ self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+ self.assertTrue(ContainerPhotos.objects.filter(container=self.container).exists())
+
+ @patch('damages_api.views.boto3.client')
+ def test_damage_photo_retrieval(self, mock_boto_client):
+ mock_s3 = mock_boto_client.return_value
+ mock_s3.list_objects_v2.return_value = {
+ 'Contents': [
+ {'Key': f'{self.container.id}/test.jpg'}
+ ]
+ }
+ response = self.client.get(reverse('damages-api', args=[self.container.id]))
+ self.assertEqual(response.status_code, status.HTTP_200_OK)
+ self.assertIn('url', response.data[0])
\ No newline at end of file
diff --git a/docker-compose-deploy.yml b/docker-compose-deploy.yml
index bf34a59..6c97453 100644
--- a/docker-compose-deploy.yml
+++ b/docker-compose-deploy.yml
@@ -38,8 +38,7 @@ services:
networks:
- app-network
command: >
- bash -c "sleep 10 &&\
- python manage.py collectstatic --noinput --verbosity 3 &&\
+ bash -c "python manage.py collectstatic --noinput --verbosity 3 &&\
python manage.py migrate &&\
gunicorn DepoT.wsgi:application --bind 0.0.0.0:8000 --workers 3"
@@ -70,7 +69,6 @@ services:
condition: service_healthy
entrypoint: >
/bin/sh -c "
- sleep 10;
mc alias set myminio http://minio:9000 kikimor shushunka1;
mc mb myminio/damages;
mc mb myminio/static;
diff --git a/dockerfile b/dockerfile
index c4a5b18..6e73e67 100644
--- a/dockerfile
+++ b/dockerfile
@@ -2,6 +2,7 @@ FROM python:3.11-slim
ENV PYTHONUNBUFFERED=1
WORKDIR /DepoT
ENV PYTHONPATH="${PYTHONPATH}:/DepoT:/."
+ENV DJANGO_SETTINGS_MODULE=DepoT.settings.production
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
diff --git a/createbuckets_depot.tar b/images/createbuckets_depot.tar
similarity index 100%
rename from createbuckets_depot.tar
rename to images/createbuckets_depot.tar
diff --git a/depot-web.tar b/images/depot-web.tar
similarity index 100%
rename from depot-web.tar
rename to images/depot-web.tar
diff --git a/depot_image.tar b/images/depot_image.tar
similarity index 100%
rename from depot_image.tar
rename to images/depot_image.tar
diff --git a/minio.tar b/images/minio.tar
similarity index 100%
rename from minio.tar
rename to images/minio.tar
diff --git a/postgres.tar b/images/postgres.tar
similarity index 100%
rename from postgres.tar
rename to images/postgres.tar
diff --git a/web_depot.tar b/images/web_depot.tar
similarity index 91%
rename from web_depot.tar
rename to images/web_depot.tar
index 6c36f62..80245d5 100644
Binary files a/web_depot.tar and b/images/web_depot.tar differ
diff --git a/manage.py b/manage.py
index 5bf2659..fe147b1 100644
--- a/manage.py
+++ b/manage.py
@@ -6,7 +6,7 @@ import sys
def main():
"""Run administrative tasks."""
- os.environ.setdefault("DJANGO_SETTINGS_MODULE", "DepoT.settings")
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "DepoT.settings.development")
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
diff --git a/minio_backend/__pycache__/__init__.cpython-313.pyc b/minio_backend/__pycache__/__init__.cpython-313.pyc
index e40c124..add708a 100644
Binary files a/minio_backend/__pycache__/__init__.cpython-313.pyc and b/minio_backend/__pycache__/__init__.cpython-313.pyc differ
diff --git a/minio_backend/__pycache__/storage.cpython-313.pyc b/minio_backend/__pycache__/storage.cpython-313.pyc
index da7b73f..d01d116 100644
Binary files a/minio_backend/__pycache__/storage.cpython-313.pyc and b/minio_backend/__pycache__/storage.cpython-313.pyc differ
diff --git a/payments/services.py b/payments/services.py
index 6ea1990..156724f 100644
--- a/payments/services.py
+++ b/payments/services.py
@@ -5,9 +5,10 @@ import urllib.parse
from datetime import datetime, timedelta
import requests
+import environ
-from DepoT.settings import env
-
+# from DepoT.settings import env
+env = environ.Env()
class EPay:
payment_types = {
@@ -33,8 +34,8 @@ class EPay:
datetime.today() + timedelta(days=int(env("INVOICE_EXPIRE_PERIOD")))
).strftime("%d.%m.%Y")
- if amount == 0:
- amount = 1000
+ # if amount == 0:
+ # amount = 1000 # for
message = f"MIN={client_id}\nINVOICE={invoice}\nAMOUNT={amount}\nCURRENCY={currency}\nEXP_TIME={expiry}\nDESCR={description}"
encoded_data, checksum = EPay.encode_message(message)
diff --git a/templates/client/booking-list.html b/templates/client/booking-list.html
index a67b757..5afe303 100644
--- a/templates/client/booking-list.html
+++ b/templates/client/booking-list.html
@@ -1,6 +1,7 @@
{% extends 'list-crud.html' %}
{% load static %}
{% load filters %}
+{% load custom_filters %}
{% block filter %}