9 Commits

Author SHA1 Message Date
gitea 16c3c60a6f fixed some errors 2025-08-05 23:40:06 +03:00
gitea f16ea7c748 some fields validation and model cleaning 2025-08-05 21:38:16 +03:00
gitea 06a3b105d3 some tests 2025-08-05 18:34:54 +03:00
gitea 8294db9189 settings split for development and production.py 2025-08-04 19:26:38 +03:00
gitea f501be9794 fixed client dashboard
fixed employee sidebar nomenclatures buttons
2025-08-03 17:25:23 +03:00
gitea 9167092f27 fix date filter
employee dashboard fully working
2025-08-03 13:30:28 +03:00
gitea 75b3adfc71 added missing/incomplete permissions to views 2025-08-03 11:52:01 +03:00
gitea 13c4c324fc added some filters to templates 2025-08-03 10:50:59 +03:00
gitea e824e87953 development branch created to preserve working deploy project 2025-08-02 17:37:16 +03:00
696 changed files with 91313 additions and 627 deletions
+33
View File
@@ -0,0 +1,33 @@
# 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/
+1 -1
View File
@@ -16,7 +16,7 @@
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.13 (DepoT)" jdkType="Python SDK" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">
+20
View File
@@ -0,0 +1,20 @@
<?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>
+12
View File
@@ -0,0 +1,12 @@
<?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
@@ -0,0 +1,2 @@
#n:information_schema
!<md> [null, 0, null, null, -2147483648, -2147483648]
@@ -0,0 +1,2 @@
#n:pg_catalog
!<md> [null, 0, null, null, -2147483648, -2147483648]
@@ -0,0 +1,2 @@
#n:public
!<md> [1114, 0, null, null, -2147483648, -2147483648]
+1
View File
@@ -3,4 +3,5 @@
<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>
+211 -75
View File
@@ -5,39 +5,12 @@
</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" />
@@ -69,49 +42,167 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<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;
<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"
},
&quot;keyToStringList&quot;: {
&quot;DatabaseDriversLRU&quot;: [
&quot;postgresql&quot;
"keyToStringList": {
"DatabaseDriversLRU": [
"postgresql"
]
}
}</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">
<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>
<configuration name="DepoT" type="Python.DjangoServer" factoryName="Django server">
<module name="DepoT" />
<option name="ENV_FILES" value="" />
@@ -119,9 +210,10 @@
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
<env name="DJANGO_SETTINGS_MODULE" value="DepoT.settings" />
<env name="DJANGO_SETTINGS_MODULE" value="DepoT.settings.development" />
</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" />
@@ -137,12 +229,29 @@
<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-09060db00ec0-JavaScript-PY-251.26927.74" />
<option value="bundled-python-sdk-657d8234b839-64d779b69b7a-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-251.26927.74" />
<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" />
</set>
</attachedChunks>
</component>
@@ -161,7 +270,10 @@
<workItem from="1753175068058" duration="4635000" />
<workItem from="1753179863298" duration="8357000" />
<workItem from="1753197869497" duration="98156000" />
<workItem from="1753637487803" duration="47474000" />
<workItem from="1753637487803" duration="71422000" />
<workItem from="1754046655407" duration="19056000" />
<workItem from="1754068089083" duration="32822000" />
<workItem from="1754408990802" duration="13021000" />
</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" />
@@ -282,19 +394,34 @@
<option name="timeStamp" value="55" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/common/views/client_views.py</url>
<line>118</line>
<option name="timeStamp" value="61" />
<url>file://$PROJECT_DIR$/preinfo/test_views.py</url>
<line>50</line>
<option name="timeStamp" value="130" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/payments/views.py</url>
<line>41</line>
<option name="timeStamp" value="98" />
<url>file://$PROJECT_DIR$/preinfo/test_views.py</url>
<line>94</line>
<option name="timeStamp" value="138" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/payments/views.py</url>
<line>56</line>
<option name="timeStamp" value="99" />
<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" />
</line-breakpoint>
<line-breakpoint enabled="true" type="javascript">
<url>file://$PROJECT_DIR$/static/js/container_validation.js</url>
@@ -311,4 +438,13 @@
</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>
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
View File
View File
@@ -14,10 +14,10 @@ from pathlib import Path
import os
import environ
BASE_DIR = Path(__file__).resolve().parent.parent
BASE_DIR = Path(__file__).resolve().parent.parent.parent
env = environ.Env()
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))
env.read_env(os.path.join(BASE_DIR, '.env'))
SECRET_KEY = "django-insecure-g%187p84o9^rr)3#9@r3n^o2v1i%@6=+puxm7hlodg+kbsk%n#"
+247
View File
@@ -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()
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")}/'
+5
View File
@@ -0,0 +1,5 @@
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"]
+5
View File
@@ -0,0 +1,5 @@
FROM alpine/git
WORKDIR /build_context
COPY . .
RUN ls -la
CMD ["echo", "Build context listed."]
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-3
View File
@@ -5,7 +5,6 @@ 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',
@@ -16,12 +15,10 @@ 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',
-1
View File
@@ -11,6 +11,5 @@ 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,6 +9,8 @@ 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)
+2
View File
@@ -37,6 +37,8 @@ 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):
+11
View File
@@ -0,0 +1,11 @@
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)
+6 -2
View File
@@ -1,14 +1,18 @@
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('change-password/', views.UserChangePasswordView.as_view(), name='change_password'),
# 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('<int:pk>/update/', views.UserUpdateView.as_view(), name='user_update'),
path('<int:pk>/active/', views.UserActiveView.as_view(), name='user_active'),
])),
+29 -37
View File
@@ -1,5 +1,5 @@
from django.contrib.auth import get_user_model
from django.contrib.auth.views import LoginView
from django.contrib.auth.views import LoginView, PasswordChangeView
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
from accounts.forms import LoginForm, RegisterForm, UserChangePasswordForm, UserEditForm
from accounts.models import DepotUser
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin
from django.contrib.auth.mixins import AccessMixin, LoginRequiredMixin, UserPassesTestMixin
# Create your views here.
@@ -28,27 +28,21 @@ class DepotLoginView(LoginView):
def is_company_admin(user):
return user.is_authenticated and user.is_company_admin
@method_decorator(login_required, name='dispatch')
class RegisterView(AccessMixin, FormView):
class RegisterView(LoginRequiredMixin, UserPassesTestMixin, FormView):
template_name = 'registration/register.html'
form_class = RegisterForm
# model = get_user_model()
success_url = reverse_lazy('dashboard')
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 test_func(self):
user = self.request.user
return user.is_superuser or user.user_type == DepotUser.UserType.COMPANY_ADMIN
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'])
@@ -90,17 +84,15 @@ class RegisterView(AccessMixin, FormView):
return form
class UserListView(ListView):
class UserListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
template_name = 'registration/user-list.html'
model = get_user_model()
context_object_name = 'objects'
paginate_by = 30 # Number of containers per page
# base_template = 'employee-base.html'
paginate_by = 20
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# context['base_template'] = self.base_template
# return context
def test_func(self):
user = self.request.user
return user.is_superuser or user.user_type == DepotUser.UserType.COMPANY_ADMIN
def get_queryset(self):
queryset = super().get_queryset()
@@ -111,7 +103,6 @@ class UserListView(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:
@@ -119,18 +110,15 @@ class UserListView(ListView):
else:
return queryset.none()
class UserUpdateView(UpdateView):
class UserUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
template_name = 'registration/register.html'
form_class = RegisterForm
form_class = UserEditForm
model = get_user_model()
success_url = reverse_lazy('user_list')
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 test_func(self):
user = self.request.user
return user.is_superuser or user.user_type == DepotUser.UserType.COMPANY_ADMIN
def form_valid(self, form):
user = form.save(commit=False)
@@ -175,14 +163,16 @@ class UserUpdateView(UpdateView):
return form
class UserActiveView(LoginRequiredMixin, View):
class UserActiveView(LoginRequiredMixin, UserPassesTestMixin, 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.")
@@ -192,7 +182,9 @@ class UserActiveView(LoginRequiredMixin, View):
return JsonResponse({'success': True, 'is_active': target_user.is_active})
class UserChangePasswordView(LoginRequiredMixin, View):
class CustomPasswordChangeView(LoginRequiredMixin, PasswordChangeView):
template_name = 'registration/change_password.html'
form_class = UserChangePasswordForm
success_url = reverse_lazy('home')
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')
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+19 -8
View File
@@ -1,4 +1,8 @@
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.
@@ -10,15 +14,15 @@ class Booking(models.Model):
('canceled', 'Canceled'),
]
number = models.CharField(max_length=50, unique=True)
vehicles = models.CharField(blank=True, null=True)
number = UpperCaseCharField(max_length=50)
vehicles = UpperCaseCharField(blank=True, null=True)
container_type = models.ForeignKey(
ContainerTypeModel,
on_delete=models.CASCADE,
related_name='booking_container_types',
)
container_count = models.IntegerField()
container_expedited_count = models.IntegerField(default=0)
container_count = models.PositiveIntegerField()
container_expedited_count = models.PositiveIntegerField(default=0)
carrier = models.CharField(max_length=100, blank=True, null=True)
line = models.ForeignKey(
LinesModel,
@@ -27,12 +31,12 @@ class Booking(models.Model):
)
visible = models.BooleanField(default=True)
is_new = models.BooleanField(default=True)
container_number = models.CharField(max_length=11, blank=True, null=True)
container_number = ContainerNumberField(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.IntegerField()
created_by = models.ForeignKey(DepotUser, related_name='booking_created_user', on_delete=models.CASCADE)
updated_on = models.DateTimeField(auto_now=True)
updated_by = models.IntegerField()
updated_by = models.ForeignKey(DepotUser, related_name='booking_updated_user', on_delete=models.CASCADE)
status = models.CharField(
max_length=10,
choices=STATUS_CHOICES,
@@ -41,4 +45,11 @@ class Booking(models.Model):
@property
def containers_left(self):
return self.container_count - (self.container_expedited_count or 0)
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.')
+98 -2
View File
@@ -1,3 +1,99 @@
from django.test import TestCase
from django.test import TestCase, Client
from django.urls import reverse, reverse_lazy
from django.contrib.auth import get_user_model
# Create your tests here.
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')
Binary file not shown.
Binary file not shown.
+16 -16
View File
@@ -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
from django.views.generic import CreateView, ListView, UpdateView
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 = 4
paginate_by = 20
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
result = filter_queryset_by_user(queryset, user)
return result
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
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.id
form.instance.updated_by = self.request.user.id
form.instance.created_by = self.request.user
form.instance.updated_by = self.request.user
form.instance.vehicles_left = form.cleaned_data.get('vehicles')
return super().form_valid(form)
def test_func(self):
return True # 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):
class ClientBookingUpdateView(LoginRequiredMixin, UserPassesTestMixin, LineFilterFormMixin, UpdateView):
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.id
form.instance.updated_by = self.request.user
return super().form_valid(form)
def test_func(self):
return True # 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'
+6 -7
View File
@@ -1,19 +1,18 @@
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import ListView
from booking.models import Booking
class BookingListView(ListView):
class BookingListView(LoginRequiredMixin, UserPassesTestMixin, ListView):
template_name = 'employee/booking-list.html'
model = Booking
context_object_name = 'objects'
paginate_by = 30 # Number of containers per page
# base_template = 'employee-base.html'
paginate_by = 20
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# context['base_template'] = self.base_template
# return context
def test_func(self):
user = self.request.user
return self.request.user.user_type == 'EM' or user.is_superuser
def get_queryset(self):
queryset = super().get_queryset()

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