diff --git a/core/core/settings.py b/core/core/settings.py index 805a27b..90e026b 100644 --- a/core/core/settings.py +++ b/core/core/settings.py @@ -70,6 +70,7 @@ INSTALLED_APPS = [ 'inventory', 'mail', 'notify_sessions', + 'shipments' ] REST_FRAMEWORK = { diff --git a/core/shipments/README.md b/core/shipments/README.md new file mode 100644 index 0000000..b224fe5 --- /dev/null +++ b/core/shipments/README.md @@ -0,0 +1,18 @@ +# Shipment Management +## Functional Description +This module handles shipments that are processed by the lost&found team. + +**Feature List** +- Shipment can contain *n* (often one, but a lostee can also be sent multiple items) items +- Shipment is linked to *n* tickets (for informative purposes) +- Shipment holds the address of the parcel +- Shipment holds the dhl voucher used +- On creation of a shipment, the agent can activate the features "adress retrieval", "item validation" and/or tracking + - **address retrieval**: the lostee recieves a email with publicly accessible link, on this page the user can enter his address. + - address sanitation + - address validation + - automatic state change after successful address entry (-> waiting for shipping) + - **item validation**: the lostee recieves a email with publicly accessible link, on this page the user see the images and confirm the items + - automatic state change after confirmation + - **tracking**: the lostee recieves a email with publicly accessible link, on this page the user can enter his address +- Returning parcels could be managed to (e.g. if a shipment is marked as returned, the items will be *unfound* again) \ No newline at end of file diff --git a/core/shipments/__init__.py b/core/shipments/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/shipments/api_v2.py b/core/shipments/api_v2.py new file mode 100644 index 0000000..e69de29 diff --git a/core/shipments/migrations/0001_initial.py b/core/shipments/migrations/0001_initial.py new file mode 100644 index 0000000..eb889e2 --- /dev/null +++ b/core/shipments/migrations/0001_initial.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.7 on 2025-03-15 21:37 + +from django.db import migrations, models +import django.db.models.manager +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('inventory', '0007_rename_container_item_container_old_itemplacement_and_more'), + ('tickets', '0013_alter_statechange_state'), + ] + + operations = [ + migrations.CreateModel( + name='Shipment', + fields=[ + ('is_deleted', models.BooleanField(default=False)), + ('deleted_at', models.DateTimeField(blank=True, null=True)), + ('id', models.AutoField(primary_key=True, serialize=False)), + ('public_secret', models.UUIDField(default=uuid.uuid4)), + ('created_at', models.DateTimeField(auto_now_add=True, null=True)), + ('updated_at', models.DateTimeField(blank=True, null=True)), + ('related_items', models.ManyToManyField(to='inventory.item')), + ('related_tickets', models.ManyToManyField(to='tickets.issuethread')), + ], + options={ + 'abstract': False, + }, + managers=[ + ('all_objects', django.db.models.manager.Manager()), + ], + ), + ] diff --git a/core/shipments/migrations/__init__.py b/core/shipments/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/shipments/models.py b/core/shipments/models.py new file mode 100644 index 0000000..b60f56a --- /dev/null +++ b/core/shipments/models.py @@ -0,0 +1,21 @@ +import uuid + +from django.db import models +from django_softdelete.models import SoftDeleteModel, SoftDeleteManager +from inventory.models import Item +from tickets.models import IssueThread + +class Shipment(SoftDeleteModel): + id = models.AutoField(primary_key=True) + public_secret = models.UUIDField(default = uuid.uuid4) + + related_items = models.ManyToManyField(Item) + related_tickets = models.ManyToManyField(IssueThread) + + created_at = models.DateTimeField(null=True, auto_now_add=True) + updated_at = models.DateTimeField(blank=True, null=True) + + all_objects = models.Manager() + + def __str__(self): + return '[' + str(self.id) + ']' + self.description \ No newline at end of file diff --git a/core/tickets/migrations/0013_alter_statechange_state.py b/core/tickets/migrations/0013_alter_statechange_state.py new file mode 100644 index 0000000..6a99ce5 --- /dev/null +++ b/core/tickets/migrations/0013_alter_statechange_state.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2025-03-15 21:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0012_remove_issuethread_related_items_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='statechange', + name='state', + field=models.CharField(choices=[('pending_new', 'New'), ('pending_open', 'Open'), ('pending_shipping', 'Needs to be shipped'), ('pending_physical_confirmation', 'Needs to be confirmed physically'), ('pending_return', 'Needs to be returned'), ('pending_postponed', 'Postponed'), ('pending_suspected_spam', 'Suspected Spam'), ('waiting_details', 'Waiting for details'), ('waiting_pre_shipping', 'Waiting for Address/Shipping Info'), ('closed_returned', 'Closed: Returned'), ('closed_shipped', 'Closed: Shipped'), ('closed_not_found', 'Closed: Not found'), ('closed_not_our_problem', 'Closed: Not our problem'), ('closed_duplicate', 'Closed: Duplicate'), ('closed_timeout', 'Closed: Timeout'), ('closed_spam', 'Closed: Spam'), ('closed_nothing_missing', 'Closed: Nothing missing'), ('closed_wtf', 'Closed: WTF'), ('found_open', 'Item Found and stored externally'), ('found_closed', 'Item Found and stored externally and closed')], default='pending_new', max_length=255), + ), + ] diff --git a/deploy/testdata.py b/deploy/testdata.py index dca385f..6cb3b33 100644 --- a/deploy/testdata.py +++ b/deploy/testdata.py @@ -3,7 +3,7 @@ import os def setup(): from authentication.models import ExtendedUser, EventPermission - from inventory.models import Event + from inventory.models import Event, Container, Item from django.contrib.auth.models import Permission, Group permissions = ['add_item', 'view_item', 'view_file', 'delete_item', 'change_item'] if not ExtendedUser.objects.filter(username='admin').exists(): @@ -38,6 +38,16 @@ def setup(): start='2024-12-18 00:00:00.000000', end='2024-12-27 00:00:00.000000', pre_start='2024-12-31 00:00:00.000000', post_end='2025-01-04 00:00:00.000000')[ 0] + + container1 ,_= Container.objects.get_or_create(id=1, name='Box1') + container2 ,_= Container.objects.get_or_create(id=2, name='Box2') + + testitem1 ,_= Item.objects.get_or_create(id=1, event=event1, description="Test item 1",uid_deprecated=111) + testitem1.container = container1 + testitem2 ,_= Item.objects.get_or_create(id=2, event=event2, description="Test item 2",uid_deprecated=112) + testitem2.container = container1 + testitem3 ,_= Item.objects.get_or_create(id=3, event=event2, description="Test item 3",uid_deprecated=113) + testitem3.container = container2 # for permission in permissions: # EventPermission.objects.create(event=event_37c3, user=foo,