"""Schema for payment_allocation serialization.""" from abacus_common_logic.marshalling.custom_fields import ma from marshmallow import validates_schema, ValidationError from payment.constants.constants import ( DEFAULT_BULK_MAX_LIMIT, PAYEE_TYPES, PAYMENT_ALLOCATION_FLOWTHROUGH_STATUS_TRANSITIONS, PAYMENT_ALLOCATION_LEDGER_STATUSES, PAYMENT_ALLOCATION_STATUSES, PAYMENT_ALLOCATION_TYPES, ) from payment.constants.error import ( ERROR_ALLOCATION_DUPLICATE, ERROR_BULK_MAX_LIMIT, ERROR_BULK_REQUIRED_FIELDS, ) class PaymentAllocationFlowthroughSchema(ma.Schema): """Payment Allocation Flowthrough schema.""" payment_allocation_id = ma.IntegerId(required=True) contract_id = ma.NonNegativeInteger(required=True) payee_type = ma.Enum(options=PAYEE_TYPES, required=True) payee_id = ma.NonNegativeInteger(required=True) statement_period_id = ma.NonNegativeInteger(required=True) payment_allocation_type = ma.Enum(options=PAYMENT_ALLOCATION_TYPES, required=True) amount_to_payment = ma.Decimal(as_string=True, required=True) payment_status = ma.Enum(options=PAYMENT_ALLOCATION_STATUSES, required=True) payment_status_modified = ma.DateTime(allow_none=True) amount_to_ledger = ma.Decimal(as_string=True, required=True) ledger_status = ma.Enum(options=PAYMENT_ALLOCATION_LEDGER_STATUSES, required=True) ledger_status_modified = ma.DateTime(allow_none=True) currency_code = ma.NonemptyString(required=True) description = ma.String(allow_none=True) created_at = ma.FormattedDateTime(required=True) created_by = ma.String(required=True) last_modified = ma.FormattedDateTime(required=True) last_modified_by = ma.String(required=True) class PaymentAllocationFlowthroughListSchema(ma.Schema): """Payment Allocation Flowthrough list schema.""" items = ma.Nested(PaymentAllocationFlowthroughSchema, many=True) total_count = ma.NonNegativeInteger(required=True) class PaymentAllocationFlowthroughBulkRequestSchema(ma.Schema): """Bulk request schema for payment allocation flowthrough retrieval.""" payment_allocation_ids = ma.List( ma.NonNegativeInteger(), allow_none=True, load_default=None ) contract_ids = ma.List(ma.NonNegativeInteger(), allow_none=True, load_default=None) payment_statuses = ma.List( ma.Enum(options=PAYMENT_ALLOCATION_STATUSES), allow_none=True, load_default=None ) ledger_statuses = ma.List( ma.Enum(options=PAYMENT_ALLOCATION_LEDGER_STATUSES), allow_none=True, load_default=None, ) @validates_schema def validate_filters(self, data, **kwargs): """Validate that at least one filter is provided.""" filters = [ data.get('payment_allocation_ids'), data.get('contract_ids'), data.get('payment_statuses'), data.get('ledger_statuses'), ] if not any(filters): raise ValidationError( 'At least one filter must be provided: ' 'payment_allocation_ids, contract_ids, payment_statuses, or ledger_statuses' ) for field_name in ['payment_allocation_ids', 'contract_ids']: field_value = data.get(field_name) if field_value and len(set(field_value)) != len(field_value): raise ValidationError(f'{field_name} contains duplicate values') class PaymentAllocationFlowthroughBulkUpdateSchema(ma.Schema): """Schema for bulk updating payment allocation flowthrough.""" payment_allocation_id = ma.NonNegativeInteger(required=True) payment_status = ma.Enum( options=PAYMENT_ALLOCATION_STATUSES, required=False, metadata={ 'description': 'Allowed transitions: ' + ' | '.join( [ f"{source} -> {', '.join(targets)}" for source, targets in PAYMENT_ALLOCATION_FLOWTHROUGH_STATUS_TRANSITIONS.items() ] ) }, ) ledger_status = ma.Enum(options=PAYMENT_ALLOCATION_LEDGER_STATUSES, required=False) @validates_schema(pass_many=True) def validate_many_items(self, data, many, **kwargs): if not many: return data if len(data) > DEFAULT_BULK_MAX_LIMIT: raise ValidationError(ERROR_BULK_MAX_LIMIT.format(DEFAULT_BULK_MAX_LIMIT)) ids = [d['payment_allocation_id'] for d in data] if len(ids) != len(set(ids)): raise ValidationError(ERROR_ALLOCATION_DUPLICATE) if no_data := [ d['payment_allocation_id'] for d in data if not any((d.get('payment_status'), d.get('ledger_status'))) ]: raise ValidationError(ERROR_BULK_REQUIRED_FIELDS.format(no_data)) return data class PaymentAllocationFlowthroughBulkDeleteSchema(ma.Schema): """Schema for bulk deleting payment allocation flowthrough.""" payment_allocation_ids = ma.List( ma.NonNegativeInteger(required=True), required=True ) @validates_schema() def validate_schema(self, data, **kwargs): if len(data.get('payment_allocation_ids')) > DEFAULT_BULK_MAX_LIMIT: raise ValidationError(ERROR_BULK_MAX_LIMIT.format(DEFAULT_BULK_MAX_LIMIT)) if len(data.get('payment_allocation_ids')) != len( set(data.get('payment_allocation_ids')) ): raise ValidationError(ERROR_ALLOCATION_DUPLICATE) return data