from rest_framework import serializers from apps.accounts.serializers import PkAsIdMixin from apps.farms.models import Cycle, Flock, Kandang, Site class SiteSerializer(PkAsIdMixin, serializers.ModelSerializer): class Meta: model = Site fields = ["id", "site_name", "user", "created_at", "updated_at"] read_only_fields = ["id", "created_at", "updated_at"] class FeedInButtonUrlSerializer(serializers.Serializer): label = serializers.CharField(max_length=50, required=False, allow_blank=True, default="") url = serializers.URLField(max_length=500) class KandangSerializer(PkAsIdMixin, serializers.ModelSerializer): feed_in_button_urls = FeedInButtonUrlSerializer(many=True, required=False) class Meta: model = Kandang fields = [ "id", "kandang_name", "feed_in_button_urls", "site", "created_at", "updated_at", ] read_only_fields = ["id", "created_at", "updated_at"] def validate_feed_in_button_urls(self, value): cleaned = [] for item in value or []: url = str(item.get("url", "")).strip() if not url: continue label = str(item.get("label", "") or "").strip()[:50] cleaned.append({"label": label, "url": url}) return cleaned def update(self, instance, validated_data): urls = validated_data.pop("feed_in_button_urls", None) instance = super().update(instance, validated_data) if urls is not None: instance.feed_in_button_urls = urls instance.save(update_fields=["feed_in_button_urls", "updated_at"]) return instance def to_representation(self, instance): data = super().to_representation(instance) urls = data.get("feed_in_button_urls") or [] if not isinstance(urls, list): urls = [] data["feed_in_button_urls"] = [ {"label": str(item.get("label", "") or ""), "url": str(item.get("url", "") or "")} for item in urls if isinstance(item, dict) and str(item.get("url", "") or "").strip() ] return data class FlockSerializer(PkAsIdMixin, serializers.ModelSerializer): class Meta: model = Flock fields = ["id", "flock_name", "kandang", "created_at", "updated_at"] read_only_fields = ["id", "created_at", "updated_at"] class CycleSerializer(PkAsIdMixin, serializers.ModelSerializer): kandang = serializers.PrimaryKeyRelatedField(queryset=Kandang.objects.all()) kandang_name = serializers.CharField(source="kandang.kandang_name", read_only=True) chick_in_weight = serializers.IntegerField(source="doc_in_weight") current_day = serializers.IntegerField(read_only=True) visible_through_date = serializers.DateField(read_only=True) soft_end_date = serializers.SerializerMethodField() class Meta: model = Cycle fields = [ "id", "kandang", "kandang_name", "total_days", "current_day", "visible_through_date", "soft_end_date", "start_date", "end_date", "chick_in_weight", "doc_in_count", "feed_initial_balance", "feed_initial_balance_date", "status", "proposed_end_date", "close_requested_at", "close_requested_by", "created_at", "updated_at", ] read_only_fields = [ "id", "kandang_name", "feed_initial_balance", "feed_initial_balance_date", "end_date", "status", "proposed_end_date", "close_requested_at", "close_requested_by", "created_at", "updated_at", "soft_end_date", ] def get_soft_end_date(self, obj: Cycle): return obj.soft_end_date() def validate(self, attrs): attrs.pop("status", None) # Defense in depth: end_date is read-only (GM approve only). attrs.pop("end_date", None) if self.instance is None: attrs.setdefault("total_days", 1) start = attrs.get("start_date", getattr(self.instance, "start_date", None)) end = getattr(self.instance, "end_date", None) if self.instance else None if start and end and end < start: raise serializers.ValidationError({"end_date": "Must be on or after start_date."}) return attrs def to_representation(self, instance): return super().to_representation(instance)