203 lines
7.0 KiB
Python
203 lines
7.0 KiB
Python
"""
|
|
DRF Serializers for Finance app.
|
|
"""
|
|
|
|
from rest_framework import serializers
|
|
from .models import Invoice, InvoiceLineItem, Payment, Service, Package, PackagePurchase, Payer
|
|
from core.serializers import PatientListSerializer
|
|
|
|
|
|
class ServiceSerializer(serializers.ModelSerializer):
|
|
"""Serializer for Service model."""
|
|
|
|
clinic_name = serializers.CharField(source='clinic.name_en', read_only=True)
|
|
|
|
class Meta:
|
|
model = Service
|
|
fields = [
|
|
'id', 'code', 'name_en', 'name_ar',
|
|
'clinic', 'clinic_name', 'base_price',
|
|
'duration_minutes', 'is_active'
|
|
]
|
|
read_only_fields = ['id']
|
|
|
|
|
|
class PackageSerializer(serializers.ModelSerializer):
|
|
"""Serializer for Package model."""
|
|
|
|
services_display = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Package
|
|
fields = [
|
|
'id', 'name_en', 'name_ar', 'services', 'services_display',
|
|
'total_sessions', 'price', 'validity_days', 'is_active'
|
|
]
|
|
read_only_fields = ['id']
|
|
|
|
def get_services_display(self, obj):
|
|
"""Return list of service names."""
|
|
return [service.name_en for service in obj.services.all()]
|
|
|
|
|
|
class PayerSerializer(serializers.ModelSerializer):
|
|
"""Serializer for Payer model."""
|
|
|
|
patient_name = serializers.CharField(source='patient.get_full_name', read_only=True)
|
|
type_display = serializers.CharField(source='get_type_display', read_only=True)
|
|
|
|
class Meta:
|
|
model = Payer
|
|
fields = [
|
|
'id', 'patient', 'patient_name', 'name',
|
|
'type', 'type_display', 'policy_number',
|
|
'coverage_percentage', 'is_active'
|
|
]
|
|
read_only_fields = ['id']
|
|
|
|
|
|
class InvoiceLineItemSerializer(serializers.ModelSerializer):
|
|
"""Serializer for InvoiceLineItem model."""
|
|
|
|
service_name = serializers.CharField(source='service.name_en', read_only=True)
|
|
package_name = serializers.CharField(source='package.name_en', read_only=True)
|
|
|
|
class Meta:
|
|
model = InvoiceLineItem
|
|
fields = [
|
|
'id', 'service', 'service_name', 'package', 'package_name',
|
|
'description', 'quantity', 'unit_price', 'total'
|
|
]
|
|
read_only_fields = ['id', 'total']
|
|
|
|
def validate(self, data):
|
|
"""Calculate total."""
|
|
quantity = data.get('quantity', 1)
|
|
unit_price = data.get('unit_price', 0)
|
|
data['total'] = quantity * unit_price
|
|
return data
|
|
|
|
|
|
class InvoiceListSerializer(serializers.ModelSerializer):
|
|
"""Lightweight serializer for invoice lists."""
|
|
|
|
patient_name = serializers.CharField(source='patient.get_full_name', read_only=True)
|
|
status_display = serializers.CharField(source='get_status_display', read_only=True)
|
|
|
|
class Meta:
|
|
model = Invoice
|
|
fields = [
|
|
'id', 'invoice_number', 'patient', 'patient_name',
|
|
'issue_date', 'due_date', 'total',
|
|
'status', 'status_display'
|
|
]
|
|
read_only_fields = ['id', 'invoice_number']
|
|
|
|
|
|
class InvoiceDetailSerializer(serializers.ModelSerializer):
|
|
"""Detailed serializer for invoice detail views."""
|
|
|
|
patient = PatientListSerializer(read_only=True)
|
|
line_items = InvoiceLineItemSerializer(many=True, read_only=True)
|
|
status_display = serializers.CharField(source='get_status_display', read_only=True)
|
|
amount_paid = serializers.SerializerMethodField()
|
|
amount_due = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Invoice
|
|
fields = [
|
|
'id', 'invoice_number', 'patient', 'appointment',
|
|
'issue_date', 'due_date',
|
|
'subtotal', 'tax', 'discount', 'total',
|
|
'status', 'status_display',
|
|
'amount_paid', 'amount_due',
|
|
'line_items', 'notes',
|
|
'created_at', 'updated_at'
|
|
]
|
|
read_only_fields = ['id', 'invoice_number', 'created_at', 'updated_at']
|
|
|
|
def get_amount_paid(self, obj):
|
|
"""Calculate total amount paid."""
|
|
return sum(payment.amount for payment in obj.payments.filter(status='COMPLETED'))
|
|
|
|
def get_amount_due(self, obj):
|
|
"""Calculate amount due."""
|
|
amount_paid = self.get_amount_paid(obj)
|
|
return obj.total - amount_paid
|
|
|
|
|
|
class InvoiceCreateSerializer(serializers.ModelSerializer):
|
|
"""Serializer for creating invoices."""
|
|
|
|
line_items = InvoiceLineItemSerializer(many=True)
|
|
|
|
class Meta:
|
|
model = Invoice
|
|
fields = [
|
|
'patient', 'appointment', 'issue_date', 'due_date',
|
|
'subtotal', 'tax', 'discount', 'notes', 'line_items'
|
|
]
|
|
|
|
def create(self, validated_data):
|
|
"""Create invoice with line items."""
|
|
line_items_data = validated_data.pop('line_items')
|
|
|
|
# Calculate total
|
|
subtotal = validated_data.get('subtotal', 0)
|
|
tax = validated_data.get('tax', 0)
|
|
discount = validated_data.get('discount', 0)
|
|
total = subtotal + tax - discount
|
|
validated_data['total'] = total
|
|
validated_data['status'] = 'DRAFT'
|
|
|
|
invoice = Invoice.objects.create(**validated_data)
|
|
|
|
# Create line items
|
|
for item_data in line_items_data:
|
|
InvoiceLineItem.objects.create(invoice=invoice, **item_data)
|
|
|
|
return invoice
|
|
|
|
|
|
class PaymentSerializer(serializers.ModelSerializer):
|
|
"""Serializer for Payment model."""
|
|
|
|
invoice_number = serializers.CharField(source='invoice.invoice_number', read_only=True)
|
|
patient_name = serializers.CharField(source='invoice.patient.get_full_name', read_only=True)
|
|
method_display = serializers.CharField(source='get_method_display', read_only=True)
|
|
status_display = serializers.CharField(source='get_status_display', read_only=True)
|
|
processed_by_name = serializers.CharField(source='processed_by.get_full_name', read_only=True)
|
|
|
|
class Meta:
|
|
model = Payment
|
|
fields = [
|
|
'id', 'invoice', 'invoice_number', 'patient_name',
|
|
'payment_date', 'amount',
|
|
'method', 'method_display',
|
|
'transaction_id', 'reference',
|
|
'status', 'status_display',
|
|
'processed_by', 'processed_by_name',
|
|
'created_at'
|
|
]
|
|
read_only_fields = ['id', 'created_at']
|
|
|
|
|
|
class PackagePurchaseSerializer(serializers.ModelSerializer):
|
|
"""Serializer for PackagePurchase model."""
|
|
|
|
patient_name = serializers.CharField(source='patient.get_full_name', read_only=True)
|
|
package_name = serializers.CharField(source='package.name_en', read_only=True)
|
|
invoice_number = serializers.CharField(source='invoice.invoice_number', read_only=True)
|
|
|
|
class Meta:
|
|
model = PackagePurchase
|
|
fields = [
|
|
'id', 'patient', 'patient_name',
|
|
'package', 'package_name',
|
|
'invoice', 'invoice_number',
|
|
'purchase_date', 'expiry_date',
|
|
'total_sessions', 'used_sessions', 'remaining_sessions',
|
|
'is_active'
|
|
]
|
|
read_only_fields = ['id', 'remaining_sessions']
|