Compare commits

..

1 Commits

Author SHA1 Message Date
gitea 6a6d103a26 Updated readme
Updated readme file with a Deployment section
7 months ago

@ -1,33 +0,0 @@
# Git
.git
.gitignore
# Docker
.dockerignore
docker-compose.yml
docker-compose.dev.yml
docker-compose-deploy.yml
docker-compose with volume.yml
dockerfile
*.tar
# Python
.venv
__pycache__
*.pyc
# Database
db.sqlite3
*.bak
# IDE
.idea
# Environment files
.env
production.env
# Other
gemini.cmd - Shortcut.lnk
images/

@ -16,7 +16,7 @@
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="jdk" jdkName="Python 3.13 (DepoT)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="dataSourceStorageLocal" created-in="PY-242.23339.19">
<data-source name="depot@localhost" uuid="2186be09-0cb1-4210-bad0-d279af5e6702">
<database-info product="PostgreSQL" version="17.4" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.7.3" dbms="POSTGRES" exact-version="17.4" exact-driver-version="42.7">
<identifier-quote-string>&quot;</identifier-quote-string>
</database-info>
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />
<secret-storage>master_key</secret-storage>
<user-name>postgres</user-name>
<schema-mapping>
<introspection-scope>
<node kind="database" qname="@">
<node kind="schema" qname="@" />
</node>
</introspection-scope>
</schema-mapping>
</data-source>
</component>
</project>

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="depot@localhost" uuid="2186be09-0cb1-4210-bad0-d279af5e6702">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://localhost:5432/depot</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

File diff suppressed because it is too large Load Diff

@ -3,5 +3,4 @@
<component name="Black">
<option name="sdkName" value="Python 3.13 (DepoT)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (depot_django)" project-jdk-type="Python SDK" />
</project>

@ -5,12 +5,39 @@
</component>
<component name="ChangeListManager">
<list default="true" id="7410a44d-51b9-408b-85ad-4fa46776b372" name="Changes" comment="commit unversioned files ;)">
<change afterPath="$PROJECT_DIR$/accounts/migrations/0001_initial.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/accounts/migrations/0002_clientpermission_codename_clientpermission_name.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/accounts/migrations/0003_remove_depotuser_is_company_admin_and_more.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/accounts/migrations/0004_employeepermission_depotuser_employee_permissions.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/accounts/migrations/0005_alter_depotuser_managers_alter_depotuser_user_type.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/accounts/migrations/0006_alter_clientpermission_options.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/accounts/migrations/0007_auto_20250725_1920.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/booking/migrations/0001_initial.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/booking/migrations/0002_rename_bookingmodel_booking.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/booking/migrations/0003_booking_container_expedited_count.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/booking/migrations/0004_booking_status.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/booking/migrations/0005_alter_booking_vehicles_left.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/booking/views/common.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/common/migrations/0001_initial.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/common/migrations/0002_alter_companymodel_short_name_and_more.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/common/migrations/0003_auto_20250725_1920.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/common/migrations/0005_companymodel_active_containerkindmodel_active_and_more.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/containers/migrations/0001_initial.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/containers/migrations/0002_rename_receive_vehicles_container_receive_vehicle.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/containers/migrations/0003_alter_container_booking.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/containers/migrations/0004_rename_line_id_container_line.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/containers/migrations/0005_alter_container_expedited_by_and_more.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/containers/migrations/0006_containerphotos.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/containers/migrations/0007_container_preinfo.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/damages_api/__init__.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/payments/migrations/0001_initial.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/payments/migrations/0002_additionalfees_containertariffperiod.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/payments/migrations/0003_auto_20250725_1920.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/payments/migrations/__init__.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/preinfo/migrations/0001_initial.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/preinfo/migrations/0002_alter_preinfomodel_deleted_by_and_more.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/preinfo/migrations/0003_rename_preinfomodel_preinfo.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/booking/models.py" beforeDir="false" afterPath="$PROJECT_DIR$/booking/models.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/booking/tests.py" beforeDir="false" afterPath="$PROJECT_DIR$/booking/tests.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/booking/views/client_views.py" beforeDir="false" afterPath="$PROJECT_DIR$/booking/views/client_views.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/containers/tests.py" beforeDir="false" afterPath="$PROJECT_DIR$/containers/tests.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/readme.md" beforeDir="false" afterPath="$PROJECT_DIR$/readme.md" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -42,167 +69,49 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ASKED_MARK_IGNORED_FILES_AS_EXCLUDED": "true",
"DefaultHtmlFileTemplate": "HTML File",
"Django Server.DepoT.executor": "Run",
"Django tests.Test: booking.tests.BookingViewsTestCase.executor": "Run",
"Django tests.Test: booking.tests.BookingViewsTestCase.test_booking_list_view.executor": "Debug",
"Django tests.Test: booking.tests.BookingViewsTestCase.test_booking_list_view__user_all_rights__expect_OK.executor": "Profiler",
"Django tests.Test: booking.tests.BookingViewsTestCase.test_booking_list_view__user_no_rights__expect_forbidden.executor": "Debug",
"Django tests.Test: booking.tests.BookingViewsTestCase.test_booking_update_view.executor": "Debug",
"Django tests.Test: preinfo.test_views.PreinfoViewsTest.executor": "Run",
"RunOnceActivity.OpenDjangoStructureViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.pycharm.django.structure.promotion.once.per.project": "true",
"django.template.preview.state": "SHOW_EDITOR",
"git-widget-placeholder": "deploy__branch",
"ignore.virus.scanning.warn.message": "true",
"last_opened_file_path": "C:/dev_projects/python/django/depot_django/containers",
"list.type.of.created.stylesheet": "CSS",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "preferences.editor",
"vue.rearranger.settings.migration": "true"
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;DefaultHtmlFileTemplate&quot;: &quot;HTML File&quot;,
&quot;Django Server.DepoT.executor&quot;: &quot;Debug&quot;,
&quot;RunOnceActivity.OpenDjangoStructureViewOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;RunOnceActivity.pycharm.django.structure.promotion.once.per.project&quot;: &quot;true&quot;,
&quot;django.template.preview.state&quot;: &quot;SHOW_EDITOR_AND_PREVIEW&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;C:/dev_projects/python/Django/DepoT&quot;,
&quot;list.type.of.created.stylesheet&quot;: &quot;CSS&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
},
"keyToStringList": {
"DatabaseDriversLRU": [
"postgresql"
&quot;keyToStringList&quot;: {
&quot;DatabaseDriversLRU&quot;: [
&quot;postgresql&quot;
]
}
}]]></component>
}</component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="C:\dev_projects\python\django\depot_django\containers" />
<recent name="C:\dev_projects\python\Django\DepoT\DepoT\settings" />
<recent name="C:\dev_projects\python\Django\DepoT" />
<recent name="C:\dev_projects\python\Django\DepoT\minio_backend" />
<recent name="C:\dev_projects\python\Django\DepoT\templates\employee" />
<recent name="C:\dev_projects\python\Django\DepoT\templates\client" />
<recent name="C:\dev_projects\python\Django\DepoT\templates" />
<recent name="C:\dev_projects\python\Django\DepoT\templates\registration" />
</key>
<key name="MoveFile.RECENT_KEYS">
<recent name="C:\dev_projects\python\Django\DepoT\images" />
<recent name="C:\dev_projects\python\Django\DepoT\unused templates" />
<recent name="C:\dev_projects\python\Django\DepoT\DepoT" />
<recent name="C:\dev_projects\python\Django\DepoT" />
<recent name="C:\dev_projects\python\Django\DepoT\templates\employee" />
<recent name="C:\dev_projects\python\Django\DepoT\templates\client" />
<recent name="C:\dev_projects\python\Django\DepoT\common\views" />
<recent name="C:\dev_projects\python\Django\DepoT\booking\views" />
</key>
</component>
<component name="RunManager" selected="Django tests.Test: preinfo.test_views.PreinfoViewsTest">
<configuration name="Test: booking.tests.BookingViewsTestCase" type="DjangoTestsConfigurationType" temporary="true">
<module name="DepoT" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
<env name="DJANGO_SETTINGS_MODULE" value="DepoT.settings.development" />
</envs>
<option name="SDK_HOME" value="$PROJECT_DIR$/.venv/Scripts/python.exe" />
<option name="SDK_NAME" value="Python 3.13 (depot_django)" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="TARGET" value="booking.tests.BookingViewsTestCase" />
<option name="SETTINGS_FILE" value="" />
<option name="CUSTOM_SETTINGS" value="false" />
<option name="USE_OPTIONS" value="false" />
<option name="OPTIONS" value="" />
<method v="2" />
</configuration>
<configuration name="Test: booking.tests.BookingViewsTestCase.test_booking_list_view__user_all_rights__expect_OK" type="DjangoTestsConfigurationType" temporary="true">
<module name="DepoT" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="TARGET" value="booking.tests.BookingViewsTestCase.test_booking_list_view__user_all_rights__expect_OK" />
<option name="SETTINGS_FILE" value="" />
<option name="CUSTOM_SETTINGS" value="false" />
<option name="USE_OPTIONS" value="false" />
<option name="OPTIONS" value="" />
<method v="2" />
</configuration>
<configuration name="Test: booking.tests.BookingViewsTestCase.test_booking_list_view__user_no_rights__expect_forbidden" type="DjangoTestsConfigurationType" temporary="true">
<module name="DepoT" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
<env name="DJANGO_SETTINGS_MODULE" value="DepoT.settings.development" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="TARGET" value="booking.tests.BookingViewsTestCase.test_booking_list_view__user_no_rights__expect_forbidden" />
<option name="SETTINGS_FILE" value="" />
<option name="CUSTOM_SETTINGS" value="false" />
<option name="USE_OPTIONS" value="false" />
<option name="OPTIONS" value="" />
<method v="2" />
</configuration>
<configuration name="Test: booking.tests.BookingViewsTestCase.test_booking_update_view" type="DjangoTestsConfigurationType" temporary="true">
<module name="DepoT" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
<env name="DJANGO_SETTINGS_MODULE" value="DepoT.settings.development" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="TARGET" value="booking.tests.BookingViewsTestCase.test_booking_update_view" />
<option name="SETTINGS_FILE" value="" />
<option name="CUSTOM_SETTINGS" value="false" />
<option name="USE_OPTIONS" value="false" />
<option name="OPTIONS" value="" />
<method v="2" />
</configuration>
<configuration name="Test: preinfo.test_views.PreinfoViewsTest" type="DjangoTestsConfigurationType" temporary="true">
<module name="DepoT" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
<env name="DJANGO_SETTINGS_MODULE" value="DepoT.settings.development" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="TARGET" value="preinfo.test_views.PreinfoViewsTest" />
<option name="SETTINGS_FILE" value="" />
<option name="CUSTOM_SETTINGS" value="false" />
<option name="USE_OPTIONS" value="false" />
<option name="OPTIONS" value="" />
<method v="2" />
</configuration>
<component name="RunManager">
<configuration name="DepoT" type="Python.DjangoServer" factoryName="Django server">
<module name="DepoT" />
<option name="ENV_FILES" value="" />
@ -210,10 +119,9 @@
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
<env name="DJANGO_SETTINGS_MODULE" value="DepoT.settings.development" />
<env name="DJANGO_SETTINGS_MODULE" value="DepoT.settings" />
</envs>
<option name="SDK_HOME" value="" />
<option name="SDK_NAME" value="Python 3.13 (depot_django)" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
@ -229,29 +137,12 @@
<option name="customRunCommand" value="" />
<method v="2" />
</configuration>
<list>
<item itemvalue="Django Server.DepoT" />
<item itemvalue="Django tests.Test: preinfo.test_views.PreinfoViewsTest" />
<item itemvalue="Django tests.Test: booking.tests.BookingViewsTestCase.test_booking_update_view" />
<item itemvalue="Django tests.Test: booking.tests.BookingViewsTestCase.test_booking_list_view__user_all_rights__expect_OK" />
<item itemvalue="Django tests.Test: booking.tests.BookingViewsTestCase.test_booking_list_view__user_no_rights__expect_forbidden" />
<item itemvalue="Django tests.Test: booking.tests.BookingViewsTestCase" />
</list>
<recent_temporary>
<list>
<item itemvalue="Django tests.Test: preinfo.test_views.PreinfoViewsTest" />
<item itemvalue="Django tests.Test: booking.tests.BookingViewsTestCase" />
<item itemvalue="Django tests.Test: booking.tests.BookingViewsTestCase.test_booking_update_view" />
<item itemvalue="Django tests.Test: booking.tests.BookingViewsTestCase.test_booking_list_view__user_all_rights__expect_OK" />
<item itemvalue="Django tests.Test: booking.tests.BookingViewsTestCase.test_booking_list_view__user_no_rights__expect_forbidden" />
</list>
</recent_temporary>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-js-predefined-d6986cc7102b-5c90d61e3bab-JavaScript-PY-242.23339.19" />
<option value="bundled-python-sdk-0029f7779945-399fe30bd8c1-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-242.23339.19" />
<option value="bundled-js-predefined-d6986cc7102b-09060db00ec0-JavaScript-PY-251.26927.74" />
<option value="bundled-python-sdk-657d8234b839-64d779b69b7a-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-251.26927.74" />
</set>
</attachedChunks>
</component>
@ -270,10 +161,7 @@
<workItem from="1753175068058" duration="4635000" />
<workItem from="1753179863298" duration="8357000" />
<workItem from="1753197869497" duration="98156000" />
<workItem from="1753637487803" duration="71422000" />
<workItem from="1754046655407" duration="19056000" />
<workItem from="1754068089083" duration="32822000" />
<workItem from="1754408990802" duration="13021000" />
<workItem from="1753637487803" duration="47474000" />
</task>
<task id="LOCAL-00001" summary="Add IntelliJ IDEA project configuration files&#10;&#10;This commit adds IntelliJ IDEA-specific configuration files for the project, including module setup, version control integration, inspection profiles, and workspace settings. These files facilitate development environment configuration for contributors using IntelliJ IDEA.">
<option name="closed" value="true" />
@ -394,34 +282,19 @@
<option name="timeStamp" value="55" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/preinfo/test_views.py</url>
<line>50</line>
<option name="timeStamp" value="130" />
<url>file://$PROJECT_DIR$/common/views/client_views.py</url>
<line>118</line>
<option name="timeStamp" value="61" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/preinfo/test_views.py</url>
<line>94</line>
<option name="timeStamp" value="138" />
<url>file://$PROJECT_DIR$/payments/views.py</url>
<line>41</line>
<option name="timeStamp" value="98" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/preinfo/test_views.py</url>
<line>101</line>
<option name="timeStamp" value="139" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/preinfo/test_views.py</url>
<line>107</line>
<option name="timeStamp" value="140" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/preinfo/test_views.py</url>
<line>111</line>
<option name="timeStamp" value="141" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/preinfo/test_views.py</url>
<line>117</line>
<option name="timeStamp" value="142" />
<url>file://$PROJECT_DIR$/payments/views.py</url>
<line>56</line>
<option name="timeStamp" value="99" />
</line-breakpoint>
<line-breakpoint enabled="true" type="javascript">
<url>file://$PROJECT_DIR$/static/js/container_validation.js</url>
@ -438,13 +311,4 @@
</default-breakpoints>
</breakpoint-manager>
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/DepoT$Test__booking_tests_BookingViewsTestCase_test_booking_list_view__user_no_rights__expect_forbidden.coverage" NAME="Test: booking.tests.BookingViewsTestCase.test_booking_list_view__user_no_rights__expect_forbidden Coverage Results" MODIFIED="1754372596969" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
<SUITE FILE_PATH="coverage/depot_django$Test__booking_tests_BookingViewsTestCase_test_booking_update_view.coverage" NAME="Test: booking.tests.BookingViewsTestCase.test_booking_update_view Coverage Results" MODIFIED="1754420837053" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
<SUITE FILE_PATH="coverage/depot_django$Test__booking_tests_BookingViewsTestCase.coverage" NAME="Test: booking.tests.BookingViewsTestCase Coverage Results" MODIFIED="1754420864513" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
<SUITE FILE_PATH="coverage/depot_django$Test__preinfo_test_views_PreinfoViewsTest.coverage" NAME="Test: preinfo.test_views.PreinfoViewsTest Coverage Results" MODIFIED="1754425958620" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
<SUITE FILE_PATH="coverage/DepoT$Test__booking_tests_BookingViewsTestCase_test_booking_list_view.coverage" NAME="Test: booking.tests.BookingViewsTestCase.test_booking_list_view Coverage Results" MODIFIED="1754333059580" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
<SUITE FILE_PATH="coverage/DepoT$Test__booking_tests_BookingViewsTestCase.coverage" NAME="Test: booking.tests.BookingViewsTestCase Coverage Results" MODIFIED="1754386070100" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
<SUITE FILE_PATH="coverage/DepoT$Test__booking_tests_BookingViewsTestCase_test_booking_list_view__user_all_rights__expect_OK.coverage" NAME="Test: booking.tests.BookingViewsTestCase.test_booking_list_view__user_all_rights__expect_OK Coverage Results" MODIFIED="1754372722226" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
</component>
</project>

@ -14,10 +14,10 @@ from pathlib import Path
import os
import environ
BASE_DIR = Path(__file__).resolve().parent.parent.parent
BASE_DIR = Path(__file__).resolve().parent.parent
env = environ.Env()
env.read_env(os.path.join(BASE_DIR, '.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#"

@ -1,247 +0,0 @@
"""
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()
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")}/'

@ -1,5 +0,0 @@
FROM minio/mc:latest
RUN test -f init_minio_buckets.sh && echo "init_minio_buckets.sh found in build context!" || (echo "init_minio_buckets.sh NOT found in build context!" && exit 1)
COPY init_minio_buckets.sh /usr/local/bin/init_minio_buckets.sh
RUN chmod +x /usr/local/bin/init_minio_buckets.sh
ENTRYPOINT ["/usr/local/bin/init_minio_buckets.sh"]

@ -1,5 +0,0 @@
FROM alpine/git
WORKDIR /build_context
COPY . .
RUN ls -la
CMD ["echo", "Build context listed."]

@ -5,6 +5,7 @@ from .models import DepotUser
@admin.register(DepotUser)
class DepotUserAdmin(UserAdmin):
# Add your custom fields to the fieldsets
fieldsets = UserAdmin.fieldsets + (
('Additional Info', {'fields': (
'phone_number',
@ -15,10 +16,12 @@ class DepotUserAdmin(UserAdmin):
)}),
)
# Add fields to display in list view
list_display = ('username', 'email', 'user_type', 'company', 'line', 'is_active', 'is_staff', 'is_superuser')
search_fields = ('username', 'email')
list_filter = ('user_type', 'is_active', 'is_staff', 'is_superuser')
# Add fields to the add form
add_fieldsets = UserAdmin.add_fieldsets + (
('Additional Info', {'fields': (
'email',

@ -11,5 +11,6 @@ class CompanyUserBackend(ModelBackend):
perms = super().get_user_permissions(user_obj, obj)
if user_obj.company and user_obj.company.is_client:
# Filter permissions based on client company context
perms = {p for p in perms if p.startswith('accounts.client_')}
return perms

@ -9,8 +9,6 @@ def create_permissions(apps, schema_editor):
(1, 'can_manage_containers', 'Can manage containers'),
(2, 'can_view_reports', 'Can view reports'),
(3, 'can_handle_operations', 'Can handle operations'),
(4, 'can_view_payments', 'Can view payments'),
(5, 'can_manage_payments', 'Can Manage payments'),
]
for _id, codename, name in employee_permissions:
EmployeePermission.objects.create(id=_id, codename=codename, name=name)

@ -37,8 +37,6 @@ class EmployeePermission(models.Model):
('can_manage_containers', 'Can manage containers'),
('can_view_reports', 'Can view reports'),
('can_handle_operations', 'Can handle operations'),
('can_view_payments', 'Can view payments'),
('can_manage_payments', 'Can manage payments'),
)
def __str__(self):

@ -1,11 +0,0 @@
from django import template
register = template.Library()
@register.filter
def has_company_perm(user, perm_codename):
return user.has_company_perm(perm_codename)
@register.filter
def has_employee_perm(user, perm_codename):
return user.has_employee_perm(perm_codename)

@ -1,18 +1,14 @@
from django.contrib.auth.views import LogoutView
from django.urls import path, include
from django.contrib.auth import views as auth_views
from accounts import views
from accounts.views import CustomPasswordChangeView
urlpatterns = [
path('', include([
path('', views.UserListView.as_view(), name='user_list'),
path('register/', views.RegisterView.as_view(), name='user_register'),
path('login/', views.DepotLoginView.as_view(), name='login'),
# path('relogin/', auth_views.logout_then_login, name='relogin'),
path('relogin/', LogoutView.as_view(next_page='login'), name='relogin'),
path('change-password/', CustomPasswordChangeView.as_view(), name='change_password'),
path('relogin/', auth_views.logout_then_login, name='relogin'),
path('change-password/', views.UserChangePasswordView.as_view(), name='change_password'),
path('<int:pk>/update/', views.UserUpdateView.as_view(), name='user_update'),
path('<int:pk>/active/', views.UserActiveView.as_view(), name='user_active'),
])),

@ -1,5 +1,5 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.views import LoginView, PasswordChangeView
from django.contrib.auth.views import LoginView
from django.http import HttpResponseForbidden, JsonResponse
from django.shortcuts import render
from django.urls import reverse_lazy
@ -8,11 +8,11 @@ from django.views import View
from django.views.generic import TemplateView, FormView, ListView, UpdateView
from rest_framework.generics import get_object_or_404
from accounts.forms import LoginForm, RegisterForm, UserChangePasswordForm, UserEditForm
from accounts.forms import LoginForm, RegisterForm, UserChangePasswordForm
from accounts.models import DepotUser
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin, UserPassesTestMixin
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
# Create your views here.
@ -28,21 +28,27 @@ class DepotLoginView(LoginView):
def is_company_admin(user):
return user.is_authenticated and user.is_company_admin
class RegisterView(LoginRequiredMixin, UserPassesTestMixin, FormView):
@method_decorator(login_required, name='dispatch')
class RegisterView(AccessMixin, FormView):
template_name = 'registration/register.html'
form_class = RegisterForm
# model = get_user_model()
success_url = reverse_lazy('dashboard')
def test_func(self):
user = self.request.user
return user.is_superuser or user.user_type == DepotUser.UserType.COMPANY_ADMIN
def dispatch(self, request, *args, **kwargs):
user: DepotUser = request.user
if not (user.is_superuser or user.user_type == DepotUser.UserType.COMPANY_ADMIN):
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
# Create user from form data
user = form.save(commit=False)
user_type = form.cleaned_data['user_type']
user.save()
# Clear irrelevant permissions based on user type
if user_type == DepotUser.UserType.CLIENT:
user.employee_permissions.clear()
user.company_permissions.set(form.cleaned_data['company_permissions'])
@ -84,15 +90,17 @@ class RegisterView(LoginRequiredMixin, UserPassesTestMixin, FormView):
return form
class UserListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
class UserListView(ListView):
template_name = 'registration/user-list.html'
model = get_user_model()
context_object_name = 'objects'
paginate_by = 20
paginate_by = 30 # Number of containers per page
# base_template = 'employee-base.html'
def test_func(self):
user = self.request.user
return user.is_superuser or user.user_type == DepotUser.UserType.COMPANY_ADMIN
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# context['base_template'] = self.base_template
# return context
def get_queryset(self):
queryset = super().get_queryset()
@ -103,6 +111,7 @@ class UserListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
if data_filter != 'all':
queryset = queryset.filter(is_active=True)
# Filter users based on permissions
if user.is_superuser:
return queryset.all()
elif user.user_type == DepotUser.UserType.COMPANY_ADMIN:
@ -110,15 +119,18 @@ class UserListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
else:
return queryset.none()
class UserUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
class UserUpdateView(UpdateView):
template_name = 'registration/register.html'
form_class = UserEditForm
form_class = RegisterForm
model = get_user_model()
success_url = reverse_lazy('user_list')
def test_func(self):
user = self.request.user
return user.is_superuser or user.user_type == DepotUser.UserType.COMPANY_ADMIN
def dispatch(self, request, *args, **kwargs):
user: DepotUser = request.user
if not (user.is_superuser or user.user_type == DepotUser.UserType.COMPANY_ADMIN):
return self.handle_no_permission()
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
user = form.save(commit=False)
@ -163,16 +175,14 @@ class UserUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
return form
class UserActiveView(LoginRequiredMixin, UserPassesTestMixin, View):
class UserActiveView(LoginRequiredMixin, View):
success_url = reverse_lazy('user_list')
def test_func(self):
user = self.request.user
return user.is_superuser or user.user_type == DepotUser.UserType.COMPANY_ADMIN
def post(self, request, pk, *args, **kwargs):
user = request.user
if not (user.is_superuser or getattr(user, 'user_type', None) == DepotUser.UserType.COMPANY_ADMIN):
return HttpResponseForbidden("You do not have permission to perform this action.")
target_user = get_object_or_404(get_user_model(), pk=pk)
if target_user == user:
return HttpResponseForbidden("You cannot change your own active status.")
@ -182,9 +192,7 @@ class UserActiveView(LoginRequiredMixin, UserPassesTestMixin, View):
return JsonResponse({'success': True, 'is_active': target_user.is_active})
class CustomPasswordChangeView(LoginRequiredMixin, PasswordChangeView):
class UserChangePasswordView(LoginRequiredMixin, View):
template_name = 'registration/change_password.html'
def get_success_url(self):
next_url = self.request.GET.get('next') or self.request.POST.get('next')
return next_url or reverse_lazy('dashboard')
form_class = UserChangePasswordForm
success_url = reverse_lazy('home')

@ -1,8 +1,4 @@
from django.core.exceptions import ValidationError
from django.db import models
from accounts.models import DepotUser
from common.fields import ContainerNumberField, UpperCaseCharField
from common.models import ContainerTypeModel, LinesModel, OperationModel
# Create your models here.
@ -14,15 +10,15 @@ class Booking(models.Model):
('canceled', 'Canceled'),
]
number = UpperCaseCharField(max_length=50)
vehicles = UpperCaseCharField(blank=True, null=True)
number = models.CharField(max_length=50, unique=True)
vehicles = models.CharField(blank=True, null=True)
container_type = models.ForeignKey(
ContainerTypeModel,
on_delete=models.CASCADE,
related_name='booking_container_types',
)
container_count = models.PositiveIntegerField()
container_expedited_count = models.PositiveIntegerField(default=0)
container_count = models.IntegerField()
container_expedited_count = models.IntegerField(default=0)
carrier = models.CharField(max_length=100, blank=True, null=True)
line = models.ForeignKey(
LinesModel,
@ -31,12 +27,12 @@ class Booking(models.Model):
)
visible = models.BooleanField(default=True)
is_new = models.BooleanField(default=True)
container_number = ContainerNumberField(max_length=11, blank=True, null=True)
container_number = models.CharField(max_length=11, blank=True, null=True)
vehicles_left = models.CharField(blank=True, null=True)
created_on = models.DateTimeField(auto_now_add=True)
created_by = models.ForeignKey(DepotUser, related_name='booking_created_user', on_delete=models.CASCADE)
created_by = models.IntegerField()
updated_on = models.DateTimeField(auto_now=True)
updated_by = models.ForeignKey(DepotUser, related_name='booking_updated_user', on_delete=models.CASCADE)
updated_by = models.IntegerField()
status = models.CharField(
max_length=10,
choices=STATUS_CHOICES,
@ -46,10 +42,3 @@ class Booking(models.Model):
@property
def containers_left(self):
return self.container_count - (self.container_expedited_count or 0)
class Meta:
app_label = 'booking'
def clean(self):
if Booking.objects.filter(number=self.number, status='active').exclude(id=self.id).exists():
raise ValidationError(f'Booking with number {self.number} already exists and is active.')

@ -1,99 +1,3 @@
from django.test import TestCase, Client
from django.urls import reverse, reverse_lazy
from django.contrib.auth import get_user_model
from django.test import TestCase
from accounts.models import ClientPermission
from booking.models import Booking
from common.models import LinesModel, ContainerTypeModel, CompanyModel
class BookingViewsTestCase(TestCase):
def setUp(self):
self.company = CompanyModel.objects.create(name='Test Company')
self.line = LinesModel.objects.create(name='Test Line', company_id=self.company.id)
self.container_type = ContainerTypeModel.objects.get(name='20GP')
self.user_password = 'password'
self.client = Client()
DepotUser = get_user_model()
self.user_client_no_rights = DepotUser.objects.create_user(username='user_client_no_rights', password=self.user_password, user_type='CL', email='user_client_no_rights@gmail.com')
self.user_client_all_rights = DepotUser.objects.create_user(username='user_client_all_rights', password=self.user_password, user_type='CL', email='user_client_all_rights@gmail.com', line=self.line, company=self.company)
self.user_client_all_rights.company_permissions.add(ClientPermission.objects.get(codename='can_view_booking').id)
self.user_client_all_rights.company_permissions.add(ClientPermission.objects.get(codename='can_manage_booking').id)
self.user_client_all_rights.company_permissions.add(ClientPermission.objects.get(codename='can_view_preinfo').id)
self.user_client_all_rights.company_permissions.add(ClientPermission.objects.get(codename='can_manage_preinfo').id)
self.user_client_all_rights.company_permissions.add(ClientPermission.objects.get(codename='can_view_payment').id)
self.user_client_all_rights.company_permissions.add(ClientPermission.objects.get(codename='can_manage_payment').id)
self.user_client_all_rights.company_permissions.add(ClientPermission.objects.get(codename='can_manage_company_users').id)
self.user_employee_no_rights = DepotUser.objects.create_user(username='user_employee_no_rights', password=self.user_password, user_type='EM', email='user_employee_no_rights@gmail.com')
self.user_employee_all_rights = DepotUser.objects.create_user(username='user_employee_all_rights', password=self.user_password, user_type='EM', email='user_employee_all_rights@gmail.com')
self.user_employee_all_rights.employee_permissions.add(1)
self.user_employee_all_rights.employee_permissions.add(2)
self.user_employee_all_rights.employee_permissions.add(3)
self.user_employee_all_rights.employee_permissions.add(4)
self.user_employee_all_rights.employee_permissions.add(5)
self.booking = Booking.objects.create(
number='BOOK123',
container_type=self.container_type,
container_count=10,
line=self.line,
created_by=self.user_client_all_rights,
updated_by=self.user_client_all_rights,
vehicles='K1,J2,K3',
vehicles_left='K1,J2,K3',
)
def test_employee_booking_list_view__anonymouse_user__expect_redirect_302(self):
response = self.client.get(reverse_lazy('employee_bookings'))
self.assertEqual(response.status_code, 302)
def test_employee_booking_list_view__client_user_login__expect_forbidden_403(self):
self.client.login(username=self.user_client_no_rights.username, password=self.user_password)
response = self.client.get(reverse_lazy('employee_bookings'))
self.assertEqual(response.status_code, 403)
def test_employee_booking_list_view__user_login__expect_redirect_200(self):
self.client.login(username=self.user_employee_no_rights.username, password=self.user_password)
response = self.client.get(reverse_lazy('employee_bookings'))
self.assertEqual(response.status_code, 200)
def test_booking_list_view__user_all_rights__expect_OK(self):
self.client.login(username=self.user_employee_all_rights.username, password=self.user_password)
response = self.client.get(reverse_lazy('employee_bookings'))
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'employee/booking-list.html')
self.assertContains(response, self.booking.number)
def test_client_create_booking__expect_redirect_to_booking_list_and_booking_in_db(self):
self.assertTrue( self.user_client_all_rights.has_company_perm('can_manage_booking'))
self.client.login(username=self.user_client_all_rights.username, password=self.user_password)
response = self.client.post(reverse('client_booking_create'), {
'number': 'BOOK456',
'container_type': self.container_type.id,
'container_count': 5,
'line': self.line.id,
'vehicles': 'Truck1,Truck2',
'vehicles_left': 'Truck1,Truck2',
})
self.assertEqual(response.status_code, 302)
self.assertTrue(Booking.objects.filter(number='BOOK456').exists())
def test_booking_update_view__expect_redirect_and_booking_updated_in_db(self):
self.client.login(username=self.user_client_all_rights.username, password=self.user_password)
response = self.client.post(reverse('client_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')
# Create your tests here.

@ -1,6 +1,6 @@
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.urls import reverse_lazy
from django.views.generic import CreateView, ListView, UpdateView
from django.views.generic import CreateView, ListView
from DepoT.mixins.LineFiltweFormMixin import LineFilterFormMixin
from booking.forms import BookingCreateForm, BookingUpdateForm
@ -11,23 +11,23 @@ from common.utils.utils import filter_queryset_by_user
class ClientBookingView(LoginRequiredMixin, UserPassesTestMixin, ListView):
model = Booking
template_name = 'client/booking-list.html'
paginate_by = 20
paginate_by = 4
context_object_name = 'objects'
# base_template = 'client-base.html'
def test_func(self):
return self.request.user.has_company_perm('can_view_booking') or self.request.user.user_type == 'CA'
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# context['base_template'] = self.base_template
# return context
def get_queryset(self):
queryset = super().get_queryset()
user = self.request.user
queryset = filter_queryset_by_user(queryset, user)
data_filter = self.request.GET.get('filter')
if data_filter != 'all':
queryset = queryset.filter(status='active')
queryset = queryset.order_by('-created_on')
return queryset
result = filter_queryset_by_user(queryset, user)
return result
class CreateBookingView(LoginRequiredMixin, UserPassesTestMixin, LineFilterFormMixin, CreateView):
@ -38,16 +38,16 @@ class CreateBookingView(LoginRequiredMixin, UserPassesTestMixin, LineFilterFormM
def form_valid(self, form):
# todo more validation
form.instance.created_by = self.request.user
form.instance.updated_by = self.request.user
form.instance.created_by = self.request.user.id
form.instance.updated_by = self.request.user.id
form.instance.vehicles_left = form.cleaned_data.get('vehicles')
return super().form_valid(form)
def test_func(self):
return self.request.user.has_company_perm('can_manage_booking') or self.request.user.user_type == 'CA'
return True # self.request.user.has_company_perm('can_edit_preinfo') or self.request.user.user_type == 'CA'
class ClientBookingUpdateView(LoginRequiredMixin, UserPassesTestMixin, LineFilterFormMixin, UpdateView):
class ClientBookingUpdateView(LoginRequiredMixin, UserPassesTestMixin, LineFilterFormMixin, CreateView):
template_name = 'client/booking-edit.html'
model = Booking
form_class = BookingUpdateForm
@ -55,8 +55,8 @@ class ClientBookingUpdateView(LoginRequiredMixin, UserPassesTestMixin, LineFilte
def form_valid(self, form):
# todo more validation
form.instance.updated_by = self.request.user
form.instance.updated_by = self.request.user.id
return super().form_valid(form)
def test_func(self):
return self.request.user.has_company_perm('can_manage_booking') or self.request.user.user_type == 'CA'
return True # self.request.user.has_company_perm('can_edit_preinfo') or self.request.user.user_type == 'CA'

@ -1,18 +1,19 @@
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import ListView
from booking.models import Booking
class BookingListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
class BookingListView(ListView):
template_name = 'employee/booking-list.html'
model = Booking
context_object_name = 'objects'
paginate_by = 20
paginate_by = 30 # Number of containers per page
# base_template = 'employee-base.html'
def test_func(self):
user = self.request.user
return self.request.user.user_type == 'EM' or user.is_superuser
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# context['base_template'] = self.base_template
# return context
def get_queryset(self):
queryset = super().get_queryset()

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save