Any authenticated user could PATCH a batch, and the write serializer accepted status, so a buyer could move a qc_fail batch to delivered. The buyer portal reads batches through the ORM, not this API, so the whole viewset is closed to ops/admin and the write serializer refuses a hand-set QC status and any transition out of qc_fail.
85 lines
3.1 KiB
Python
85 lines
3.1 KiB
Python
from rest_framework import serializers
|
|
from .models import ProcessingBatch
|
|
|
|
|
|
class ProcessingBatchListSerializer(serializers.ModelSerializer):
|
|
qc_status = serializers.SerializerMethodField()
|
|
shipment_status = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = ProcessingBatch
|
|
fields = ["id", "batch_reference", "origin_facility", "grade", "weight_kg", "harvest_date", "status", "qc_status", "shipment_status", "created_at"]
|
|
|
|
def get_qc_status(self, obj):
|
|
qc = obj.qc_results.first()
|
|
if qc:
|
|
return "pass" if qc.overall_pass else "fail"
|
|
return "pending"
|
|
|
|
def get_shipment_status(self, obj):
|
|
shipment = obj.shipments.first()
|
|
return shipment.status if shipment else None
|
|
|
|
|
|
class ProcessingBatchDetailSerializer(serializers.ModelSerializer):
|
|
created_by_name = serializers.CharField(source="created_by.full_name", read_only=True)
|
|
qc_results = serializers.SerializerMethodField()
|
|
shipments = serializers.SerializerMethodField()
|
|
documents = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = ProcessingBatch
|
|
fields = "__all__"
|
|
|
|
def get_qc_results(self, obj):
|
|
qc = obj.qc_results.first()
|
|
if not qc:
|
|
return None
|
|
from apps.qc.serializers import QCResultSerializer
|
|
return QCResultSerializer(qc).data
|
|
|
|
def get_shipments(self, obj):
|
|
shipments = obj.shipments.all()
|
|
from apps.shipments.serializers import ShipmentSerializer
|
|
return ShipmentSerializer(shipments, many=True).data
|
|
|
|
def get_documents(self, obj):
|
|
docs = obj.documents.all()
|
|
from apps.documents.serializers import DocumentSerializer
|
|
return DocumentSerializer(docs, many=True).data
|
|
|
|
|
|
# Mirrors apps/ops/forms.py: a QC status is set by recording a QC result, not by
|
|
# hand, and a failed batch is sticky — only a new result moves it. The portal
|
|
# enforces this in BatchForm; without the same rule here the write serializer is
|
|
# a way around the screen.
|
|
QC_SET_STATUSES = {
|
|
ProcessingBatch.Status.QC_PASS,
|
|
ProcessingBatch.Status.QC_FAIL,
|
|
}
|
|
|
|
|
|
class ProcessingBatchWriteSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = ProcessingBatch
|
|
fields = ["id", "batch_reference", "harvest_date", "weight_kg", "grade", "origin_facility", "notes", "status"]
|
|
read_only_fields = ["id"]
|
|
|
|
def validate_status(self, status):
|
|
instance = self.instance
|
|
unchanged = instance is not None and status == instance.status
|
|
if status in QC_SET_STATUSES and not unchanged:
|
|
raise serializers.ValidationError(
|
|
f"Batch status {ProcessingBatch.Status(status).label} is set by the "
|
|
"QC result, not by hand."
|
|
)
|
|
if (
|
|
instance is not None
|
|
and instance.status == ProcessingBatch.Status.QC_FAIL
|
|
and status != ProcessingBatch.Status.QC_FAIL
|
|
):
|
|
raise serializers.ValidationError(
|
|
"A batch that has failed QC cannot be moved by hand; a new QC "
|
|
"result is what moves it."
|
|
)
|
|
return status
|