import typing from django.contrib.auth.hashers import make_password from django.db.models import Q from rest_framework import serializers from apps.authentication.exceptions import UserExistException, OrganizationNationalUniqueIDException, \ OrganizationTypeRepeatableException from apps.authentication.models import ( User, City, Province, Organization, OrganizationType, OrganizationStats, BankAccountInformation ) from apps.authorization import models as authorize_models from apps.authorization.api.v1.serializers import ( UserRelationSerializer ) class CitySerializer(serializers.ModelSerializer): """ Serialize city data """ class Meta: model = City fields = [ 'id', 'name', 'province' ] class ProvinceSerializer(serializers.ModelSerializer): """ Serialize province data """ class Meta: model = Province fields = [ 'id', 'name', ] class BankAccountSerializer(serializers.ModelSerializer): """ Serialize bank account data """ class Meta: model = BankAccountInformation fields = '__all__' def validate(self, attrs): account = attrs['account'] card = attrs['card'] sheba = attrs['sheba'] # if not self.instance: # if self.Meta.model.objects.filter(Q(account=account) | Q(card=card) | Q(sheba=sheba)).exists(): # raise BankAccountExistException() # elif self.instance: # if self.instance.account != account: # if self.Meta.model.objects.filter(Q(account=account)).exists(): # raise BankAccountNumberAccountException() # # elif self.instance.card != card: # if self.Meta.model.objects.filter(Q(card=card)).exists(): # raise BankAccountCardException() # # elif self.instance.sheba != sheba: # if self.Meta.model.objects.filter(Q(sheba=sheba)).exists(): # raise BankAccountShebaException() return attrs def update(self, instance, validated_data): """ update user bank account information """ instance.name = validated_data.get('name', instance.name) instance.account = validated_data.get('account', instance.account) instance.organization = validated_data.get('organization', instance.organization) instance.account_type = validated_data.get('account_type', instance.account_type) instance.card = validated_data.get('card', instance.card) instance.sheba = validated_data.get('sheba', instance.sheba) instance.save() return instance class UserSerializer(serializers.ModelSerializer): """ Serialize user data """ class Meta: model = User fields = [ 'id', 'username', 'password', 'first_name', 'last_name', 'is_active', 'mobile', 'phone', 'national_code', 'birthdate', 'nationality', 'ownership', 'address', 'unit_name', 'unit_national_id', 'photo', 'province', 'city', 'otp_status', 'is_herd_owner', ] extra_kwargs = { 'password': { 'required': False }, 'username': { 'required': False }, "national_code": { 'required': False }, "unit_name": { 'required': False }, "unit_national_id": { 'required': False } } def validate(self, attrs): mobile = attrs['mobile'] if not self.instance: if self.Meta.model.objects.filter( Q(mobile=mobile) | Q(username=attrs['username']), trash=False ).exists(): raise UserExistException() elif self.instance: if self.instance.mobile != mobile: if self.Meta.model.objects.filter(trash=False, mobile=mobile).exists(): raise UserExistException() if 'username' in attrs.keys(): if self.instance.username != attrs['username']: if self.Meta.model.objects.filter(trash=False, username=attrs['username']).exists(): raise UserExistException() return attrs def to_representation(self, instance): """ Custom output """ representation = super().to_representation(instance) if isinstance(instance, User): if instance.bank_information.filter().exists(): representation['bank_account'] = BankAccountSerializer( instance.bank_information.all(), many=True ).data if instance.city: representation['city_name'] = instance.city.name if instance.province: representation['province_name'] = instance.province.name return representation def update(self, instance, validated_data): """ update user instance """ instance.username = validated_data.get('username', instance.username) # control password if validated_data.get('password'): instance.password = make_password(validated_data.get('password', instance.password)) instance.first_name = validated_data.get('first_name') instance.last_name = validated_data.get('last_name') instance.is_active = validated_data.get('is_active') instance.mobile = validated_data.get('mobile') instance.phone = validated_data.get('phone') instance.national_code = validated_data.get('national_code') instance.birthdate = validated_data.get('birthdate') instance.nationality = validated_data.get('nationality') instance.ownership = validated_data.get('ownership') instance.address = validated_data.get('address') instance.unit_name = validated_data.get('unit_name') instance.unit_national_id = validated_data.get('unit_national_id') instance.photo = validated_data.get('photo') instance.province = validated_data.get('province', instance.province) instance.city = validated_data.get('city', instance.city) instance.otp_status = validated_data.get('otp_status') instance.save() return instance @staticmethod def update_relations(user: object, relation_data: dict, bank_data: dict) -> typing.Any: """ update user relations & bank account for user """ user_relation = UserRelationSerializer(data=relation_data) # Create user relation if user_relation.is_valid(raise_exception=True): user_relation_obj = user_relation.update( authorize_models.UserRelations.objects.get(user=user), validated_data=relation_data ) bank_info = BankAccountSerializer(data=bank_data) # Create user bank information if bank_info.is_valid(raise_exception=True): bank_obj = bank_info.update( BankAccountInformation.objects.get(id=bank_data['id']), validated_data=bank_data ) return user_relation_obj, bank_obj # noqa class OrganizationTypeSerializer(serializers.ModelSerializer): """ Serialize organization type data """ class Meta: model = OrganizationType fields = [ 'id', 'parent', 'key', 'name', 'org_type_field', 'is_repeatable' ] def to_representation(self, instance): representation = super().to_representation(instance) if instance.parent: representation['parent'] = { 'name': instance.parent.name, 'id': instance.parent.id } return representation class OrganizationSerializer(serializers.ModelSerializer): """ Serialize organization data """ class Meta: model = Organization fields = [ 'id', 'name', 'type', 'province', 'city', 'address', 'parent_organization', 'national_unique_id', 'company_code', 'field_of_activity', 'free_visibility_by_scope', 'service_area', # noqa 'purchase_policy' ] extra_kwargs = { 'service_area': { # noqa 'required': False }, 'purchase_policy': { 'purchase_policy': False } } def validate(self, attrs): """ @ validate national_unique_code to be unique @ validate to organization type field """ # check national_unique_id national_unique_id = attrs['national_unique_id'] if not self.instance: if self.Meta.model.objects.filter(national_unique_id=national_unique_id).exists(): raise OrganizationNationalUniqueIDException() if self.instance: if self.instance.national_unique_id != national_unique_id: if self.Meta.model.objects.filter(national_unique_id=national_unique_id).exists(): raise OrganizationNationalUniqueIDException() # check organization type field # if is repeatable of type, organization will not be recreating org_type = attrs['type'] if not self.instance: if not org_type.is_repeatable: if org_type.org_type_field == 'PR' and self.Meta.model.objects.filter( type=org_type, province=attrs['province'] ).exists(): raise OrganizationTypeRepeatableException() if org_type.org_type_field == 'CI' and self.Meta.model.objects.filter( type=org_type, city=attrs['city'] ).exists(): raise OrganizationTypeRepeatableException() if org_type.org_type_field == 'CO' and self.Meta.model.objects.filter( type=org_type, province=attrs['province'], ).exists(): raise OrganizationTypeRepeatableException() # check organization type field when updating elif self.instance: if not org_type.is_repeatable: if org_type.org_type_field == 'PR' and self.instance.province != attrs[ 'province'] and self.Meta.model.objects.filter( type=org_type, province=attrs['province'], ).exists(): raise OrganizationTypeRepeatableException() if org_type.org_type_field == 'CI' and self.instance.city != attrs[ 'city'] and self.Meta.model.objects.filter( type=org_type, city=attrs['city'], ).exists(): raise OrganizationTypeRepeatableException() if org_type.org_type_field == 'CO' and self.instance.province != attrs[ 'province'] and self.Meta.model.objects.filter( type=org_type, province=attrs['province'], ).exists(): raise OrganizationTypeRepeatableException() return attrs def to_representation(self, instance): """ Custom output """ representation = super().to_representation(instance) if isinstance(instance, Organization): representation['province'] = ProvinceSerializer(instance.province).data representation['city'] = CitySerializer(instance.city).data representation['type'] = OrganizationTypeSerializer(instance.type).data if instance.parent_organization: representation['parent_organization'] = OrganizationSerializer( instance.parent_organization, ).data if instance.bank_information.exists(): representation['bank_account'] = BankAccountSerializer( instance.bank_information.all(), many=True ).data if instance.service_area: representation['service_area'] = [ { 'id': city.id, 'name': city.name, } for city in instance.service_area.all() ] return representation def update(self, instance, validated_data): """ update user organization information """ # noqa instance.name = validated_data.get('name', instance.name) instance.type = validated_data.get('type', instance.type) instance.province = validated_data.get('province', instance.province) instance.city = validated_data.get('city', instance.city) instance.address = validated_data.get('address', instance.address) instance.parent_organization = validated_data.get('parent_organization', instance.parent_organization) instance.national_unique_id = validated_data.get('national_unique_id', instance.national_unique_id) instance.purchase_policy = validated_data.get('purchase_policy', instance.purchase_policy) instance.free_visibility_by_scope = validated_data.get( 'free_visibility_by_scope', instance.free_visibility_by_scope ) instance.save() if validated_data.get('service_area'): instance.service_area.clear() instance.service_area.add(*(validated_data.get('service_area'))) return instance class OrganizationStatsSerializer(serializers.ModelSerializer): class Meta: model = OrganizationStats fields = '__all__'