add links between items and tickets /tickets endpoint
This commit is contained in:
parent
d8be7f09e4
commit
2ece0cefd8
5 changed files with 81 additions and 7 deletions
|
@ -1,6 +1,6 @@
|
|||
from django.contrib import admin
|
||||
|
||||
from tickets.models import IssueThread, Comment, StateChange, Assignment, ShippingVoucher
|
||||
from tickets.models import IssueThread, Comment, StateChange, Assignment, ItemRelation, ShippingVoucher
|
||||
|
||||
|
||||
class IssueThreadAdmin(admin.ModelAdmin):
|
||||
|
@ -19,6 +19,10 @@ class AssignmentAdmin(admin.ModelAdmin):
|
|||
pass
|
||||
|
||||
|
||||
class ItemRelationAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
||||
class ShippingVouchersAdmin(admin.ModelAdmin):
|
||||
pass
|
||||
|
||||
|
@ -27,4 +31,5 @@ admin.site.register(IssueThread, IssueThreadAdmin)
|
|||
admin.site.register(Comment, CommentAdmin)
|
||||
admin.site.register(StateChange, StateChangeAdmin)
|
||||
admin.site.register(Assignment, AssignmentAdmin)
|
||||
admin.site.register(ItemRelation, ItemRelationAdmin)
|
||||
admin.site.register(ShippingVoucher, ShippingVouchersAdmin)
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 4.2.7 on 2024-06-23 02:17
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0004_alter_event_created_at_alter_item_created_at'),
|
||||
('tickets', '0009_shippingvoucher'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='issuethread',
|
||||
name='event',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='issue_threads', to='inventory.event'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ItemRelation',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('timestamp', models.DateTimeField(auto_now_add=True)),
|
||||
('status', models.CharField(choices=[('possible', 'Possible'), ('confirmed', 'Confirmed'), ('discarded', 'Discarded'), ('provided', 'Provided')], default='possible', max_length=255)),
|
||||
('issue_thread', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='item_relations', to='tickets.issuethread')),
|
||||
('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='issues', to='inventory.item')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='issuethread',
|
||||
name='related_items',
|
||||
field=models.ManyToManyField(through='tickets.ItemRelation', to='inventory.item'),
|
||||
),
|
||||
]
|
|
@ -3,7 +3,7 @@ from django.utils import timezone
|
|||
from django_softdelete.models import SoftDeleteModel
|
||||
|
||||
from authentication.models import ExtendedUser
|
||||
from inventory.models import Event
|
||||
from inventory.models import Event, Item
|
||||
from django.db.models.signals import post_save, pre_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
|
@ -29,12 +29,21 @@ STATE_CHOICES = (
|
|||
('found_closed', 'Item Found and stored externally and closed'),
|
||||
)
|
||||
|
||||
RELATION_STATUS_CHOICES = (
|
||||
('possible', 'Possible'),
|
||||
('confirmed', 'Confirmed'),
|
||||
('discarded', 'Discarded'),
|
||||
('provided', 'Provided'),
|
||||
)
|
||||
|
||||
|
||||
class IssueThread(SoftDeleteModel):
|
||||
id = models.AutoField(primary_key=True)
|
||||
uuid = models.CharField(max_length=255, unique=True, null=False, blank=False)
|
||||
name = models.CharField(max_length=255)
|
||||
event = models.ForeignKey(Event, null=True, on_delete=models.SET_NULL, related_name='issue_threads')
|
||||
manually_created = models.BooleanField(default=False)
|
||||
related_items = models.ManyToManyField(Item, through='ItemRelation')
|
||||
|
||||
def short_uuid(self):
|
||||
return self.uuid[:8]
|
||||
|
@ -119,6 +128,17 @@ class Assignment(models.Model):
|
|||
return str(self.issue_thread) + ' assigned to ' + self.assigned_to.username
|
||||
|
||||
|
||||
class ItemRelation(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
issue_thread = models.ForeignKey(IssueThread, on_delete=models.CASCADE, related_name='item_relations')
|
||||
item = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='issues')
|
||||
timestamp = models.DateTimeField(auto_now_add=True)
|
||||
status = models.CharField(max_length=255, choices=RELATION_STATUS_CHOICES, default='possible')
|
||||
|
||||
def __str__(self):
|
||||
return str(self.issue_thread) + ' related to ' + str(self.item)
|
||||
|
||||
|
||||
class ShippingVoucher(models.Model):
|
||||
id = models.AutoField(primary_key=True)
|
||||
issue_thread = models.ForeignKey(IssueThread, on_delete=models.CASCADE, related_name='shipping_vouchers', null=True)
|
||||
|
|
|
@ -3,6 +3,7 @@ from rest_framework import serializers
|
|||
from authentication.models import ExtendedUser
|
||||
from mail.api_v2 import AttachmentSerializer
|
||||
from tickets.models import IssueThread, Comment, STATE_CHOICES, ShippingVoucher
|
||||
from inventory.serializers import ItemSerializer
|
||||
|
||||
|
||||
class CommentSerializer(serializers.ModelSerializer):
|
||||
|
@ -40,11 +41,12 @@ class IssueSerializer(serializers.ModelSerializer):
|
|||
last_activity = serializers.SerializerMethodField()
|
||||
assigned_to = serializers.SlugRelatedField(slug_field='username', queryset=ExtendedUser.objects.all(),
|
||||
allow_null=True, required=False)
|
||||
related_items = ItemSerializer(many=True, read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = IssueThread
|
||||
fields = ('id', 'timeline', 'name', 'state', 'assigned_to', 'last_activity', 'uuid')
|
||||
read_only_fields = ('id', 'timeline', 'last_activity', 'uuid')
|
||||
fields = ('id', 'timeline', 'name', 'state', 'assigned_to', 'last_activity', 'uuid', 'related_items')
|
||||
read_only_fields = ('id', 'timeline', 'last_activity', 'uuid', 'related_items')
|
||||
|
||||
def to_internal_value(self, data):
|
||||
ret = super().to_internal_value(data)
|
||||
|
@ -69,7 +71,9 @@ class IssueSerializer(serializers.ModelSerializer):
|
|||
last_mail = self.emails.order_by('-timestamp').first().timestamp if self.emails.count() > 0 else None
|
||||
last_assignment = self.assignments.order_by('-timestamp').first().timestamp if \
|
||||
self.assignments.count() > 0 else None
|
||||
args = [x for x in [last_state_change, last_comment, last_mail, last_assignment] if
|
||||
last_relation = self.item_relations.order_by('-timestamp').first().timestamp if \
|
||||
self.item_relations.count() > 0 else None
|
||||
args = [x for x in [last_state_change, last_comment, last_mail, last_assignment, last_relation] if
|
||||
x is not None]
|
||||
return max(args)
|
||||
except AttributeError:
|
||||
|
@ -110,6 +114,14 @@ class IssueSerializer(serializers.ModelSerializer):
|
|||
'timestamp': assignment.timestamp,
|
||||
'assigned_to': assignment.assigned_to.username,
|
||||
})
|
||||
for relation in obj.item_relations.all():
|
||||
timeline.append({
|
||||
'type': 'item_relation',
|
||||
'id': relation.id,
|
||||
'status': relation.status,
|
||||
'timestamp': relation.timestamp,
|
||||
'item': ItemSerializer(relation.item).data,
|
||||
})
|
||||
for shipping_voucher in obj.shipping_vouchers.all():
|
||||
timeline.append({
|
||||
'type': 'shipping_voucher',
|
||||
|
|
|
@ -23,13 +23,15 @@
|
|||
>
|
||||
<template #actions="{ item }">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-success" @click.stop="confirm('return Item?') && markItemReturned(item)" title="returned">
|
||||
<button class="btn btn-success"
|
||||
@click.stop="confirm('return Item?') && markItemReturned(item)" title="returned">
|
||||
<font-awesome-icon icon="check"/>
|
||||
</button>
|
||||
<button class="btn btn-secondary" @click.stop="openEditingModalWith(item)" title="edit">
|
||||
<font-awesome-icon icon="edit"/>
|
||||
</button>
|
||||
<button class="btn btn-danger" @click.stop="confirm('delete Item?') && deleteItem(item)" title="delete">
|
||||
<button class="btn btn-danger" @click.stop="confirm('delete Item?') && deleteItem(item)"
|
||||
title="delete">
|
||||
<font-awesome-icon icon="trash"/>
|
||||
</button>
|
||||
</div>
|
||||
|
|
Loading…
Reference in a new issue