class ResetPasswordSerializer(serializers.Serializer): password = serializers.CharField( required=True, allow_blank=False, max_length=63, style={'input_type': 'password'}, validators=[ MatchingDuelFieldsValidator( another_field='password_repeat', message=_("Inputted passwords fields do not match.")), EnhancedPasswordStrengthFieldValidator() ]) password_repeat = serializers.CharField(required=True, allow_blank=False, max_length=63, style={'input_type': 'password'}) pr_access_code = serializers.CharField(required=True, allow_blank=False, max_length=255, style={'input_type': 'password'}) def validate(self, clean_data): pr_access_code = clean_data['pr_access_code'] try: clean_data['me'] = SharedUser.objects.get( pr_access_code=pr_access_code) except SharedUser.DoesNotExist: raise serializers.ValidationError( _("Password reset access code does not exist.")) return clean_data
class StaffChangePasswordOperationSerializer(serializers.ModelSerializer): # Add password adding. password = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=63, style={'input_type': 'password'}, validators=[ MatchingDuelFieldsValidator( another_field='password_repeat', message=_("Inputted passwords fields do not match.")), EnhancedPasswordStrengthFieldValidator() ]) password_repeat = serializers.CharField(write_only=True, required=False, allow_blank=True, max_length=63, style={'input_type': 'password'}) # Meta Information. class Meta: model = Staff fields = ( 'password', 'password_repeat', ) def update(self, instance, validated_data): """ Override this function to include extra functionality. """ #--------------------------- # Update `SharedUser` object. #--------------------------- # Update the password if required. password = validated_data.get('password', None) if password: instance.owner.set_password(password) logger.info("Updated the password.") # Save our instance. instance.owner.save() logger.info("Updated the staff member.") # Return our validated data. return instance
class PartnerListCreateSerializer(serializers.ModelSerializer): # OVERRIDE THE MODEL FIELDS AND ENFORCE THE FOLLOWING CUSTOM VALIDATION RULES. given_name = serializers.CharField(required=True, allow_blank=False, validators=[]) last_name = serializers.CharField(required=True, allow_blank=False, validators=[]) gender = serializers.CharField( required=True, allow_blank=False, allow_null=False, ) address_country = serializers.CharField(required=True, allow_blank=False, validators=[]) address_region = serializers.CharField(required=True, allow_blank=False, validators=[]) address_locality = serializers.CharField(required=True, allow_blank=False, validators=[]) postal_code = serializers.CharField(required=True, allow_blank=False, validators=[]) street_address = serializers.CharField(required=True, allow_blank=False, validators=[]) # We are overriding the `email` field to include unique email validation. email = serializers.EmailField( validators=[ UniqueValidator(queryset=SharedUser.objects.all()), ], required=True, ) # All comments are created by our `create` function and not by # `django-rest-framework`. # comments = PartnerCommentSerializer(many=True, read_only=True, allow_null=True) # This is a field used in the `create` function if the user enters a # comment. This field is *ONLY* to be used during the POST creation and # will be blank during GET. extra_comment = serializers.CharField(write_only=True, allow_null=True) # Custom formatting of our telephone fields. fax_number = PhoneNumberField(allow_null=True, required=False) telephone = PhoneNumberField(allow_null=True, required=False) other_telephone = PhoneNumberField(allow_null=True, required=False) # Attach with our foreign keys. how_hear = serializers.PrimaryKeyRelatedField( many=False, required=True, allow_null=False, queryset=HowHearAboutUsItem.objects.all()) # Add password adding. password = serializers.CharField( write_only=True, required=True, allow_blank=False, max_length=63, style={'input_type': 'password'}, validators=[ MatchingDuelFieldsValidator( another_field='password_repeat', message=_("Inputted passwords fields do not match.")), EnhancedPasswordStrengthFieldValidator() ]) password_repeat = serializers.CharField(write_only=True, required=True, allow_blank=False, max_length=63, style={'input_type': 'password'}) is_active = serializers.BooleanField( write_only=True, required=True, error_messages={"invalid": "Please pick either 'Yes' or 'No' choice."}) # # Fields used for mapping to organizations. # organization_name = serializers.CharField( source="organization.name", write_only=True, required=True, allow_blank=False, max_length=63, validators=[UniqueValidator(queryset=Organization.objects.all(), )], ) organization_type_of = serializers.CharField( source="organization.type_of", write_only=True, required=True, allow_blank=True, max_length=63, ) organization_address_country = serializers.CharField( source="organization.address_country", write_only=True, required=False, allow_blank=True, max_length=127, ) organization_address_locality = serializers.CharField( source="organization.address_locality", write_only=True, required=False, allow_blank=True, max_length=127, ) organization_address_region = serializers.CharField( source="organization.address_region", write_only=True, required=False, allow_blank=True, max_length=127, ) organization_post_office_box_number = serializers.CharField( source="organization.post_office_box_number", write_only=True, required=False, allow_blank=True, max_length=255, ) organization_postal_code = serializers.CharField( source="organization.postal_code", write_only=True, required=False, allow_blank=True, max_length=127, ) organization_street_address = serializers.CharField( source="organization.street_address", write_only=True, required=False, allow_blank=True, max_length=255, ) organization_street_address_extra = serializers.CharField( source="organization.street_address_extra", write_only=True, required=False, allow_blank=True, max_length=255, ) # Meta Information. class Meta: model = Partner fields = ( # Thing 'id', 'created', 'last_modified', # Person 'given_name', 'middle_name', 'last_name', 'birthdate', 'join_date', 'gender', 'description', # Misc (Read/Write) 'is_active', 'is_ok_to_email', 'is_ok_to_text', 'how_hear', # Misc (Read Only) # 'comments', 'password', 'password_repeat', # Misc (Write Only) 'extra_comment', 'organization_name', 'organization_type_of', 'organization_address_country', 'organization_address_locality', 'organization_address_region', 'organization_post_office_box_number', 'organization_postal_code', 'organization_street_address', 'organization_street_address_extra', # Contact Point 'area_served', 'available_language', 'contact_type', 'email', 'fax_number', # 'hours_available', #TODO: FIX 'telephone', 'telephone_extension', 'telephone_type_of', 'other_telephone', 'other_telephone_extension', 'other_telephone_type_of', # Postal Address 'address_country', 'address_locality', 'address_region', 'post_office_box_number', 'postal_code', 'street_address', 'street_address_extra', # Geo-coordinate 'elevation', 'latitude', 'longitude', # 'location' #TODO: FIX ) extra_kwargs = { "is_ok_to_email": { "error_messages": { "invalid": "Please pick either 'Yes' or 'No' choice." } }, "is_ok_to_text": { "error_messages": { "invalid": "Please pick either 'Yes' or 'No' choice." } } } def validate_telephone(self, value): """ Include validation on no-blanks """ if value is None: raise serializers.ValidationError("This field may not be blank.") return value def validate_organization_type_of(self, value): """ Include validation on no-blanks or "null" types """ if value is None or value == "null": raise serializers.ValidationError("This field may not be blank.") return value def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related( 'owner', 'created_by', 'last_modified_by', # 'comments' ) return queryset @transaction.atomic def create(self, validated_data): """ Override the `create` function to add extra functinality: - Create a `User` object in the public database. - Create a `SharedUser` object in the public database. - Create a `Partner` object in the tenant database. - If user has entered text in the 'extra_comment' field then we will a `Comment` object and attach it to the `Partner` object. - We will attach the staff user whom created this `Partner` object. """ # Format our telephone(s) fax_number = validated_data.get('fax_number', None) if fax_number: fax_number = phonenumbers.parse(fax_number, "CA") telephone = validated_data.get('telephone', None) if telephone: telephone = phonenumbers.parse(telephone, "CA") other_telephone = validated_data.get('other_telephone', None) if other_telephone: other_telephone = phonenumbers.parse(other_telephone, "CA") validated_data['fax_number'] = fax_number validated_data['telephone'] = telephone validated_data['other_telephone'] = other_telephone #------------------- # Create our user. #------------------- email = validated_data.get('email', None) # Extract our "email" field. owner = SharedUser.objects.create( first_name=validated_data['given_name'], last_name=validated_data['last_name'], email=email, franchise=self.context['franchise'], was_email_activated=True, is_active=validated_data['is_active'], ) logger.info("Created shared user.") # Attach the user to the `Partner` group. owner.groups.add(ASSOCIATE_GROUP_ID) logger.info("Set shared user group.") # Update the password. password = validated_data.get('password', None) owner.set_password(password) owner.save() logger.info("Set shared user password.") #--------------------------------------------------- # Create our `Partner` object in our tenant schema. #--------------------------------------------------- # Create an "Partner". partner = Partner.objects.create( owner=owner, created_by=self.context['created_by'], last_modified_by=self.context['created_by'], description=validated_data['description'], # Profile given_name=validated_data['given_name'], last_name=validated_data['last_name'], middle_name=validated_data['middle_name'], birthdate=validated_data.get('birthdate', None), join_date=validated_data.get('join_date', None), gender=validated_data.get('gender', None), how_hear=validated_data.get('how_hear', None), # Misc is_ok_to_email=validated_data.get('is_ok_to_email', None), is_ok_to_text=validated_data.get('is_ok_to_text', None), created_from=self.context['created_from'], created_from_is_public=self.context['created_from_is_public'], # Contact Point area_served=validated_data.get('area_served', None), available_language=validated_data.get('available_language', None), contact_type=validated_data.get('contact_type', None), email=email, fax_number=fax_number, # 'hours_available', #TODO: IMPLEMENT. telephone=telephone, telephone_extension=validated_data.get('telephone_extension', None), telephone_type_of=validated_data.get('telephone_type_of', None), other_telephone=other_telephone, other_telephone_extension=validated_data.get( 'other_telephone_extension', None), other_telephone_type_of=validated_data.get( 'other_telephone_type_of', None), # Postal Address address_country=validated_data.get('address_country', None), address_locality=validated_data.get('address_locality', None), address_region=validated_data.get('address_region', None), post_office_box_number=validated_data.get('post_office_box_number', None), postal_code=validated_data.get('postal_code', None), street_address=validated_data.get('street_address', None), street_address_extra=validated_data.get('street_address_extra', None), # Geo-coordinate elevation=validated_data.get('elevation', None), latitude=validated_data.get('latitude', None), longitude=validated_data.get('longitude', None), # 'location' #TODO: IMPLEMENT. ) logger.info("Created partner.") #----------------------------------- # Create or update our Organization. #----------------------------------- organization_name = validated_data.get('organization_name', None) organization_type_of = validated_data.get('organization_type_of', None) organization_address_country = validated_data.get( 'organization_address_country', None) organization_address_locality = validated_data.get( 'organization_address_locality', None) organization_address_region = validated_data.get( 'organization_address_region', None) organization_post_office_box_number = validated_data.get( 'organization_post_office_box_number', None) organization_postal_code = validated_data.get( 'organization_postal_code', None) organization_street_address = validated_data.get( 'organization_street_address', None) organization_street_address_extra = validated_data.get( 'organization_street_address_extra', None) if organization_name and organization_type_of: organization, created = Organization.objects.update_or_create( name=organization_name, type_of=organization_type_of, defaults={ 'type_of': organization_type_of, 'name': organization_name, 'address_country': organization_address_country, 'address_locality': organization_address_locality, 'address_region': organization_address_region, 'post_office_box_number': organization_post_office_box_number, 'postal_code': organization_postal_code, 'street_address': organization_street_address, 'street_address_extra': organization_street_address_extra, }) if created: organization.owner = owner organization.save() logger.info("Created organization.") partner.organization = organization partner.save() logger.info("Attached created organization to partner.") #----------------------------- # Create our `Comment` object. #----------------------------- extra_comment = validated_data.get('extra_comment', None) if extra_comment is not None: comment = Comment.objects.create( created_by=self.context['created_by'], last_modified_by=self.context['created_by'], text=extra_comment, created_from=self.context['created_from'], created_from_is_public=self.context['created_from_is_public']) logger.info("Created comment.") PartnerComment.objects.create( about=partner, comment=comment, ) logger.info("Attached comment to partner.") # Update validation data. # validated_data['comments'] = PartnerComment.objects.filter(partner=partner) validated_data['created_by'] = self.context['created_by'] validated_data['last_modified_by'] = self.context['created_by'] # validated_data['extra_comment'] = None validated_data['id'] = partner.id # Return our validated data. return validated_data
class StaffRetrieveUpdateDestroySerializer(serializers.ModelSerializer): # owner = serializers.PrimaryKeyRelatedField(many=False, read_only=True) given_name = serializers.CharField( required=True, allow_blank=False, validators=[] ) last_name = serializers.CharField( required=True, allow_blank=False, validators=[] ) gender = serializers.CharField( required=True, allow_blank=False, allow_null=False, ) address_country = serializers.CharField( required=True, allow_blank=False, validators=[] ) address_region = serializers.CharField( required=True, allow_blank=False, validators=[] ) address_locality = serializers.CharField( required=True, allow_blank=False, validators=[] ) postal_code = serializers.CharField( required=True, allow_blank=False, validators=[] ) street_address = serializers.CharField( required=True, allow_blank=False, validators=[] ) # We are overriding the `email` field to include unique email validation. email = serializers.EmailField( validators=[ UniqueValidator(queryset=Staff.objects.all()), ], required=False ) personal_email = serializers.EmailField( validators=[ UniqueValidator(queryset=Staff.objects.all()), ], required=False ) # Add password adding. password = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=63, style={'input_type': 'password'}, validators = [ MatchingDuelFieldsValidator( another_field='password_repeat', message=_("Inputted passwords fields do not match.") ), EnhancedPasswordStrengthFieldValidator() ] ) password_repeat = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=63, style={'input_type': 'password'} ) is_active = serializers.BooleanField( write_only=True, required=True, error_messages={ "invalid": _("Please pick either 'Yes' or 'No' choice.") } ) # This field is used to assign the user to the group. account_type = serializers.CharField( write_only=True, allow_null=False, required=True ) # # Attach with our foreign keys. # how_hear = serializers.PrimaryKeyRelatedField( # many=False, # required=True, # allow_null=False, # queryset=HowHearAboutUsItem.objects.all() # ) # All comments are created by our `create` function and not by # # `django-rest-framework`. # comments = StaffCommentSerializer(many=True, read_only=True) # # # This is a field used in the `create` function if the user enters a # # comment. This field is *ONLY* to be used during the POST creation and # # will be blank during GET. # extra_comment = serializers.CharField(write_only=True, allow_null=True) # Custom formatting of our telephone fields. fax_number = PhoneNumberField(allow_null=True, required=False) telephone = PhoneNumberField(allow_null=True, required=False) other_telephone = PhoneNumberField(allow_null=True, required=False) emergency_contact_telephone = PhoneNumberField(allow_null=True, required=False) emergency_contact_alternative_telephone = PhoneNumberField(allow_null=True, required=False) # Meta Information. class Meta: model = Staff fields = ( # Thing 'id', 'created', 'last_modified', # 'owner', 'description', 'account_type', # Person 'given_name', 'middle_name', 'last_name', 'birthdate', 'join_date', 'gender', # Misc (Read/Write) 'tags', 'is_active', # # 'is_senior', # # 'is_support', # # 'job_info_read', # 'how_hear', # # # Misc (Read Only) # 'comments', # # # Misc (Write Only) 'password', 'password_repeat', # 'extra_comment', # Contact Point 'area_served', 'available_language', 'contact_type', 'email', 'personal_email', 'fax_number', # 'hours_available', #TODO: FIX 'telephone', 'telephone_extension', 'telephone_type_of', 'other_telephone', 'other_telephone_extension', 'other_telephone_type_of', # Postal Address 'address_country', 'address_locality', 'address_region', 'post_office_box_number', 'postal_code', 'street_address', 'street_address_extra', # Geo-coordinate 'elevation', 'latitude', 'longitude', # 'location' #TODO: FIX # Emergency Contact 'emergency_contact_name', 'emergency_contact_relationship', 'emergency_contact_telephone', 'emergency_contact_alternative_telephone' ) def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related( 'owner', 'created_by', 'last_modified_by', # 'comments' 'tags', ) return queryset def validate_account_type(self, value): """ Include validation for valid choices. """ account_type = int_or_none(value) if account_type is None: raise serializers.ValidationError("Please select a valid choice.") else: if account_type == FRONTLINE_GROUP_ID: return value last_modified_by = self.context['last_modified_by'] if last_modified_by.is_management_or_executive_staff(): return value raise serializers.ValidationError("You do not have permission to change the account type.") def validate_personal_email(self, value): """ Include validation for valid choices. """ if value is None or value == '': raise serializers.ValidationError("This field may not be blank.") return value @transaction.atomic def update(self, instance, validated_data): """ Override this function to include extra functionality. """ # For debugging purposes only. # print(validated_data) # Get our inputs. email = validated_data.get('email', instance.email) personal_email = validated_data.get('personal_email', None) #------------------------------------- # Bugfix: Created `SharedUser` object. #------------------------------------- if instance.owner is None: owner = SharedUser.objects.filter(email=email).first() if owner: instance.owner = owner instance.save() logger.info("BUGFIX: Attached existing shared user to staff.") else: instance.owner = SharedUser.objects.create( first_name=validated_data['given_name'], last_name=validated_data['last_name'], email=email, is_active=validated_data['is_active'], franchise=self.context['franchise'], was_email_activated=True ) instance.save() logger.info("BUGFIX: Created shared user and attached to staff.") #--------------------------- # Update `SharedUser` object. #--------------------------- # Update the password if required. password = validated_data.get('password', None) if password: instance.owner.set_password(password) logger.info("Updated the password.") # Update the account. if email: instance.owner.email = email instance.owner.username = get_unique_username_from_email(email) instance.owner.first_name = validated_data.get('given_name', instance.owner.first_name) instance.owner.last_name = validated_data.get('last_name', instance.owner.last_name) instance.owner.is_active = validated_data.get('is_active', instance.owner.is_active) instance.owner.save() logger.info("Updated the shared user.") # Attach the user to the `group` group. account_type = validated_data.get('account_type', None) if account_type != "NaN": account_type = int(account_type) instance.owner.groups.set([account_type]) logger.info("Updated the group membership.") #--------------------------- # Update `Staff` object. #--------------------------- # Person instance.description=validated_data.get('description', None) instance.given_name=validated_data.get('given_name', None) instance.last_name=validated_data.get('last_name', None) instance.middle_name=validated_data.get('middle_name', None) instance.birthdate=validated_data.get('birthdate', None) instance.join_date=validated_data.get('join_date', None) instance.gender=validated_data.get('gender', None) # Misc instance.hourly_salary_desired=validated_data.get('hourly_salary_desired', 0.00) instance.limit_special=validated_data.get('limit_special', None) instance.dues_date=validated_data.get('dues_date', None) instance.commercial_insurance_expiry_date=validated_data.get('commercial_insurance_expiry_date', None) instance.police_check=validated_data.get('police_check', None) instance.drivers_license_class=validated_data.get('drivers_license_class', None) # instance.how_hear=validated_data.get('how_hear', None) instance.last_modified_by = self.context['last_modified_by'] instance.last_modified_from = self.context['last_modified_from'] instance.last_modified_from_is_public = self.context['last_modified_from_is_public'] # 'organizations', #TODO: IMPLEMENT. # Contact Point instance.area_served=validated_data.get('area_served', None) instance.available_language=validated_data.get('available_language', None) instance.contact_type=validated_data.get('contact_type', None) instance.email=email instance.personal_email=personal_email instance.fax_number=validated_data.get('fax_number', None) # 'hours_available', #TODO: IMPLEMENT. instance.telephone=validated_data.get('telephone', None) instance.telephone_extension=validated_data.get('telephone_extension', None) instance.telephone_type_of=validated_data.get('telephone_type_of', None) instance.other_telephone=validated_data.get('other_telephone', None) instance.other_telephone_extension=validated_data.get('other_telephone_extension', None) instance.other_telephone_type_of=validated_data.get('other_telephone_type_of', None) # Postal Address instance.address_country=validated_data.get('address_country', None) instance.address_locality=validated_data.get('address_locality', None) instance.address_region=validated_data.get('address_region', None) instance.post_office_box_number=validated_data.get('post_office_box_number', None) instance.postal_code=validated_data.get('postal_code', None) instance.street_address=validated_data.get('street_address', None) instance.street_address_extra=validated_data.get('street_address_extra', None) # Geo-coordinate instance.elevation=validated_data.get('elevation', None) instance.latitude=validated_data.get('latitude', None) instance.longitude=validated_data.get('longitude', None) # 'location' #TODO: IMPLEMENT. # Emergency contact. instance.emergency_contact_name=validated_data.get('emergency_contact_name', None) instance.emergency_contact_relationship=validated_data.get('emergency_contact_relationship', None) instance.emergency_contact_telephone=validated_data.get('emergency_contact_telephone', None) instance.emergency_contact_alternative_telephone=validated_data.get('emergency_contact_alternative_telephone', None) # Save our instance. instance.save() logger.info("Updated the staff member.") #------------------------ # Set our `Tag` objects. #------------------------ tags = validated_data.get('tags', None) if tags is not None: if len(tags) > 0: instance.tags.set(tags) #--------------------------- # Attach our comment. #--------------------------- extra_comment = validated_data.get('extra_comment', None) if extra_comment is not None: comment = Comment.objects.create( created_by=self.context['last_modified_by'], last_modified_by=self.context['last_modified_by'], text=extra_comment, created_from = self.context['last_modified_from'], created_from_is_public = self.context['last_modified_from_is_public'] ) staff_comment = StaffComment.objects.create( staff=instance, comment=comment, ) #--------------------------- # Update validation data. #--------------------------- # validated_data['comments'] = StaffComment.objects.filter(staff=instance) validated_data['last_modified_by'] = self.context['last_modified_by'] # validated_data['extra_comment'] = None # Return our validated data. return validated_data
class StaffListCreateSerializer(serializers.ModelSerializer): # OVERRIDE THE MODEL FIELDS AND ENFORCE THE FOLLOWING CUSTOM VALIDATION RULES. given_name = serializers.CharField( required=True, allow_blank=False, validators=[] ) last_name = serializers.CharField( required=True, allow_blank=False, validators=[] ) gender = serializers.CharField( required=True, allow_blank=False, allow_null=False, ) address_country = serializers.CharField( required=True, allow_blank=False, validators=[] ) address_region = serializers.CharField( required=True, allow_blank=False, validators=[] ) address_locality = serializers.CharField( required=True, allow_blank=False, validators=[] ) postal_code = serializers.CharField( required=True, allow_blank=False, validators=[] ) street_address = serializers.CharField( required=True, allow_blank=False, validators=[] ) # We are overriding the `email` field to include unique email validation. email = serializers.EmailField( validators=[ UniqueValidator(queryset=SharedUser.objects.all()), ], required=True, ) # All comments are created by our `create` function and not by # `django-rest-framework`. # comments = StaffCommentSerializer(many=True, read_only=True, allow_null=True) # This is a field used in the `create` function if the user enters a # comment. This field is *ONLY* to be used during the POST creation and # will be blank during GET. extra_comment = serializers.CharField(write_only=True, allow_null=True) # This field is used to assign the user to the group. account_type = serializers.CharField( write_only=True, allow_null=False, required=True ) # Custom formatting of our telephone fields. fax_number = PhoneNumberField(allow_null=True, required=False) telephone = PhoneNumberField(allow_null=True, required=False) other_telephone = PhoneNumberField(allow_null=True, required=False) emergency_contact_telephone = PhoneNumberField(allow_null=True, required=False) emergency_contact_alternative_telephone = PhoneNumberField(allow_null=True, required=False) # Add password adding. password = serializers.CharField( write_only=True, required=True, allow_blank=False, max_length=63, style={'input_type': 'password'}, validators = [ MatchingDuelFieldsValidator( another_field='password_repeat', message=_("Inputted passwords fields do not match.") ), EnhancedPasswordStrengthFieldValidator() ] ) password_repeat = serializers.CharField( write_only=True, required=True, allow_blank=False, max_length=63, style={'input_type': 'password'} ) is_active = serializers.BooleanField( write_only=True, required=True, error_messages={ "invalid": _("Please pick either 'Yes' or 'No' choice.") } ) # Attach with our foreign keys. how_hear = serializers.PrimaryKeyRelatedField( many=False, required=True, allow_null=False, queryset=HowHearAboutUsItem.objects.all() ) # Useful for determining if the user is active or not. state = serializers.IntegerField(read_only=True,source="owner.is_active") # Meta Information. class Meta: model = Staff fields = ( # Thing 'id', 'created', 'last_modified', 'account_type', 'description', # Person 'given_name', 'middle_name', 'last_name', 'birthdate', 'join_date', 'gender', # Misc (Read/Write) 'tags', 'is_active', 'how_hear', 'state', # # Misc (Read Only) # 'comments', # Misc (Write Only) 'extra_comment', 'password', 'password_repeat', # Contact Point 'area_served', 'available_language', 'contact_type', 'email', 'personal_email', 'fax_number', # 'hours_available', #TODO: FIX 'telephone', 'telephone_extension', 'telephone_type_of', 'other_telephone', 'other_telephone_extension', 'other_telephone_type_of', # Postal Address 'address_country', 'address_locality', 'address_region', 'post_office_box_number', 'postal_code', 'street_address', 'street_address_extra', # Geo-coordinate 'elevation', 'latitude', 'longitude', # 'location' #TODO: FIX # Emergency Contact 'emergency_contact_name', 'emergency_contact_relationship', 'emergency_contact_telephone', 'emergency_contact_alternative_telephone' ) def validate_telephone(self, value): """ Include validation on no-blanks """ if value is None: raise serializers.ValidationError("This field may not be blank.") return value def validate_account_type(self, value): """ Include validation for valid choices. """ account_type = int_or_none(value) if account_type is None: raise serializers.ValidationError("Please select a valid choice.") else: if account_type == FRONTLINE_GROUP_ID: return value created_by = self.context['created_by'] if created_by.is_management_or_executive_staff(): return value raise serializers.ValidationError("You do not have permission to change the account type.") def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related( 'owner', 'created_by', 'last_modified_by', # 'comments' 'tags', ) return queryset @transaction.atomic def create(self, validated_data): """ Override the `create` function to add extra functinality: - Create a `User` object in the public database. - Create a `SharedUser` object in the public database. - Create a `Staff` object in the tenant database. - If user has entered text in the 'extra_comment' field then we will a `Comment` object and attach it to the `Staff` object. - We will attach the staff user whom created this `Staff` object. """ # Format our telephone(s) fax_number = validated_data.get('fax_number', None) if fax_number: fax_number = phonenumbers.parse(fax_number, "CA") telephone = validated_data.get('telephone', None) if telephone: telephone = phonenumbers.parse(telephone, "CA") other_telephone = validated_data.get('other_telephone', None) if other_telephone: other_telephone = phonenumbers.parse(other_telephone, "CA") validated_data['fax_number'] = fax_number validated_data['telephone'] = telephone validated_data['other_telephone'] = other_telephone # Extract our "email" field. email = validated_data.get('email', None) personal_email = validated_data.get('personal_email', None) #------------------- # Create our user. #------------------- owner = SharedUser.objects.create( first_name=validated_data['given_name'], last_name=validated_data['last_name'], email=email, is_active=validated_data['is_active'], franchise=self.context['franchise'], was_email_activated=True ) logger.info("Created shared user.") # Attach the user to the `group` group. account_type = int_or_none(validated_data.get('account_type', None)) if account_type: owner.groups.set([account_type]) # Update the password. password = validated_data.get('password', None) owner.set_password(password) owner.save() #--------------------------------------------------- # Create our `Staff` object in our tenant schema. #--------------------------------------------------- # Create an "Staff". staff = Staff.objects.create( created_by=self.context['created_by'], last_modified_by=self.context['created_by'], description=validated_data.get('description', None), # Person given_name=validated_data['given_name'], last_name=validated_data['last_name'], middle_name=validated_data['middle_name'], birthdate=validated_data.get('birthdate', None), join_date=validated_data.get('join_date', None), gender=validated_data.get('gender', None), # Misc created_from = self.context['created_from'], created_from_is_public = self.context['created_from_is_public'], # . . . # Contact Point area_served=validated_data.get('area_served', None), available_language=validated_data.get('available_language', None), contact_type=validated_data.get('contact_type', None), email=email, personal_email=personal_email, fax_number=fax_number, # 'hours_available', #TODO: IMPLEMENT. telephone=telephone, telephone_extension=validated_data.get('telephone_extension', None), telephone_type_of=validated_data.get('telephone_type_of', None), other_telephone=other_telephone, other_telephone_extension=validated_data.get('other_telephone_extension', None), other_telephone_type_of=validated_data.get('other_telephone_type_of', None), # Postal Address address_country=validated_data.get('address_country', None), address_locality=validated_data.get('address_locality', None), address_region=validated_data.get('address_region', None), post_office_box_number=validated_data.get('post_office_box_number', None), postal_code=validated_data.get('postal_code', None), street_address=validated_data.get('street_address', None), street_address_extra=validated_data.get('street_address_extra', None), # Geo-coordinate elevation=validated_data.get('elevation', None), latitude=validated_data.get('latitude', None), longitude=validated_data.get('longitude', None), # 'location' #TODO: IMPLEMENT. # Emergency contact emergency_contact_name=validated_data.get('emergency_contact_name', None), emergency_contact_relationship=validated_data.get('emergency_contact_relationship', None), emergency_contact_telephone=validated_data.get('emergency_contact_telephone', None), emergency_contact_alternative_telephone=validated_data.get('emergency_contact_alternative_telephone', None), ) logger.info("Created staff member.") # Update our staff again. staff.owner = owner staff.email = email staff.save() logger.info("Attached user object to staff member.") #------------------------ # Set our `Tag` objects. #------------------------ tags = validated_data.get('tags', None) if tags is not None: if len(tags) > 0: staff.tags.set(tags) #----------------------------- # Create our `Comment` object. #----------------------------- extra_comment = validated_data.get('extra_comment', None) if extra_comment is not None: comment = Comment.objects.create( created_by=self.context['created_by'], last_modified_by=self.context['created_by'], text=extra_comment, created_from = self.context['created_from'], created_from_is_public = self.context['created_from_is_public'] ) staff_comment = StaffComment.objects.create( about=staff, comment=comment, ) # Update validation data. # validated_data['comments'] = StaffComment.objects.filter(staff=staff) validated_data['created_by'] = self.context['created_by'] validated_data['last_modified_by'] = self.context['created_by'] validated_data['extra_comment'] = None validated_data['id'] = staff.id # Return our validated data. return validated_data
class AssociateListCreateSerializer(serializers.ModelSerializer): # OVERRIDE THE MODEL FIELDS AND ENFORCE THE FOLLOWING CUSTOM VALIDATION RULES. given_name = serializers.CharField(required=True, allow_blank=False, validators=[]) last_name = serializers.CharField(required=True, allow_blank=False, validators=[]) gender = serializers.CharField( required=True, allow_blank=False, allow_null=False, ) address_country = serializers.CharField(required=True, allow_blank=False, validators=[]) address_region = serializers.CharField(required=True, allow_blank=False, validators=[]) address_locality = serializers.CharField(required=True, allow_blank=False, validators=[]) postal_code = serializers.CharField(required=True, allow_blank=False, validators=[]) street_address = serializers.CharField(required=True, allow_blank=False, validators=[]) join_date = serializers.DateField(required=True, ) # We are overriding the `email` field to include unique email validation. email = serializers.EmailField( validators=[UniqueValidator(queryset=SharedUser.objects.all())], required=True, ) # All comments are created by our `create` function and not by # `django-rest-framework`. # comments = AssociateCommentSerializer(many=True, read_only=True, allow_null=True) # This is a field used in the `create` function if the user enters a # comment. This field is *ONLY* to be used during the POST creation and # will be blank during GET. extra_comment = serializers.CharField(write_only=True, allow_null=True) # # The skill_sets that this associate belongs to. We will return primary # # keys only. This field is read/write accessible. # skill_sets = serializers.PrimaryKeyRelatedField(many=True, queryset=SkillSet.objects.all(), allow_null=True) # assigned_skill_sets = SkillSetListCreateSerializer(many=True, read_only=True) # Custom formatting of our telephone fields. fax_number = PhoneNumberField(allow_null=True, required=False) telephone = PhoneNumberField( allow_null=False, required=True, ) other_telephone = PhoneNumberField(allow_null=True, required=False) emergency_contact_telephone = PhoneNumberField(allow_null=True, required=False) emergency_contact_alternative_telephone = PhoneNumberField(allow_null=True, required=False) # Add password adding. password = serializers.CharField( write_only=True, required=True, allow_blank=False, max_length=63, style={'input_type': 'password'}, validators=[ MatchingDuelFieldsValidator( another_field='password_repeat', message=_("Inputted passwords fields do not match.")), EnhancedPasswordStrengthFieldValidator() ]) password_repeat = serializers.CharField(write_only=True, required=True, allow_blank=False, max_length=63, style={'input_type': 'password'}) is_active = serializers.BooleanField( write_only=True, required=True, error_messages={"invalid": "Please pick either 'Yes' or 'No' choice."}) # Attach with our foreign keys. how_hear = serializers.PrimaryKeyRelatedField( many=False, required=True, allow_null=False, queryset=HowHearAboutUsItem.objects.all()) # Useful for determining if the user is active or not. state = serializers.IntegerField(read_only=True, source="owner.is_active") # Meta Information. class Meta: model = Associate fields = ( # Thing 'id', 'created', 'last_modified', # Person 'given_name', 'middle_name', 'last_name', 'birthdate', 'join_date', 'gender', 'description', 'tax_id', # Misc (Read/Write) 'is_active', 'is_ok_to_email', 'is_ok_to_text', 'hourly_salary_desired', 'limit_special', 'dues_date', 'commercial_insurance_expiry_date', 'auto_insurance_expiry_date', 'wsib_number', 'wsib_insurance_date', 'police_check', 'drivers_license_class', 'vehicle_types', # many-to-many 'how_hear', 'how_hear_other', 'skill_sets', # many-to-many 'tags', # many-to-many 'insurance_requirements', # many-to-many # Misc (Read Only) # 'comments', 'password', 'password_repeat', 'state', # # Misc (Write Only) 'extra_comment', # Contact Point 'area_served', 'available_language', 'contact_type', 'email', 'fax_number', # 'hours_available', #TODO: FIX 'telephone', 'telephone_extension', 'telephone_type_of', 'other_telephone', 'other_telephone_extension', 'other_telephone_type_of', # Postal Address 'address_country', 'address_locality', 'address_region', 'post_office_box_number', 'postal_code', 'street_address', 'street_address_extra', # Geo-coordinate 'elevation', 'latitude', 'longitude', # 'location' #TODO: FIX # Emergency Contact 'emergency_contact_name', 'emergency_contact_relationship', 'emergency_contact_telephone', 'emergency_contact_alternative_telephone') extra_kwargs = { "is_ok_to_email": { "error_messages": { "invalid": _("Please pick either 'Yes' or 'No' choice.") } }, "is_ok_to_text": { "error_messages": { "invalid": _("Please pick either 'Yes' or 'No' choice.") } }, "hourly_salary_desired": { "error_messages": { "min_value": _("Ensure this value is greater than or equal to 0."), "invalid": _("Please enter a value with no $, such as 20") } } } def validate_telephone(self, value): """ Include validation on no-blanks """ if value is None: raise serializers.ValidationError("This field may not be blank.") return value def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related( 'owner', 'created_by', 'last_modified_by', 'tags', 'skill_sets', 'vehicle_types' 'comments', 'insurance_requirements', ) return queryset @transaction.atomic def create(self, validated_data): """ Override the `create` function to add extra functinality: - Create a `User` object in the public database. - Create a `SharedUser` object in the public database. - Create a `Associate` object in the tenant database. - If user has entered text in the 'extra_comment' field then we will a `Comment` object and attach it to the `Associate` object. - We will attach the staff user whom created this `Associate` object. """ # Format our telephone(s) fax_number = validated_data.get('fax_number', None) if fax_number: fax_number = phonenumbers.parse(fax_number, "CA") telephone = validated_data.get('telephone', None) if telephone: telephone = phonenumbers.parse(telephone, "CA") other_telephone = validated_data.get('other_telephone', None) if other_telephone: other_telephone = phonenumbers.parse(other_telephone, "CA") validated_data['fax_number'] = fax_number validated_data['telephone'] = telephone validated_data['other_telephone'] = other_telephone #------------------- # Create our user. #------------------- email = validated_data.get('email', None) # Extract our "email" field. owner = SharedUser.objects.create( first_name=validated_data['given_name'], last_name=validated_data['last_name'], email=email, franchise=self.context['franchise'], was_email_activated=True, is_active=validated_data['is_active'], ) # Attach the user to the `Associate` group. owner.groups.add(ASSOCIATE_GROUP_ID) # Update the password. password = validated_data.get('password', None) owner.set_password(password) owner.save() logger.info("Created shared user.") #--------------------------------------------------- # Create our `Associate` object in our tenant schema. #--------------------------------------------------- # Create an "Associate". associate = Associate.objects.create( owner=owner, created_by=self.context['created_by'], last_modified_by=self.context['created_by'], description=validated_data['description'], # Profile given_name=validated_data['given_name'], last_name=validated_data['last_name'], middle_name=validated_data['middle_name'], birthdate=validated_data.get('birthdate', None), join_date=validated_data.get('join_date', None), gender=validated_data.get('gender', None), tax_id=validated_data.get('tax_id', None), # Misc is_ok_to_email=validated_data.get('is_ok_to_email', None), is_ok_to_text=validated_data.get('is_ok_to_text', None), hourly_salary_desired=validated_data.get('hourly_salary_desired', 0.00), limit_special=validated_data.get('limit_special', None), dues_date=validated_data.get('dues_date', None), commercial_insurance_expiry_date=validated_data.get( 'commercial_insurance_expiry_date', None), auto_insurance_expiry_date=validated_data.get( 'auto_insurance_expiry_date', None), wsib_number=validated_data.get('wsib_number', None), wsib_insurance_date=validated_data.get('wsib_insurance_date', None), police_check=validated_data.get('police_check', None), drivers_license_class=validated_data.get('drivers_license_class', None), how_hear=validated_data.get('how_hear', None), how_hear_other=validated_data.get('how_hear_other', None), created_from=self.context['created_from'], created_from_is_public=self.context['created_from_is_public'], # 'organizations', #TODO: IMPLEMENT. # Contact Point area_served=validated_data.get('area_served', None), available_language=validated_data.get('available_language', None), contact_type=validated_data.get('contact_type', None), email=email, fax_number=fax_number, # 'hours_available', #TODO: IMPLEMENT. telephone=telephone, telephone_extension=validated_data.get('telephone_extension', None), telephone_type_of=validated_data.get('telephone_type_of', None), other_telephone=other_telephone, other_telephone_extension=validated_data.get( 'other_telephone_extension', None), other_telephone_type_of=validated_data.get( 'other_telephone_type_of', None), # Postal Address address_country=validated_data.get('address_country', None), address_locality=validated_data.get('address_locality', None), address_region=validated_data.get('address_region', None), post_office_box_number=validated_data.get('post_office_box_number', None), postal_code=validated_data.get('postal_code', None), street_address=validated_data.get('street_address', None), street_address_extra=validated_data.get('street_address_extra', None), # Geo-coordinate elevation=validated_data.get('elevation', None), latitude=validated_data.get('latitude', None), longitude=validated_data.get('longitude', None), # 'location' #TODO: IMPLEMENT. # Emergency contact emergency_contact_name=validated_data.get('emergency_contact_name', None), emergency_contact_relationship=validated_data.get( 'emergency_contact_relationship', None), emergency_contact_telephone=validated_data.get( 'emergency_contact_telephone', None), emergency_contact_alternative_telephone=validated_data.get( 'emergency_contact_alternative_telephone', None), ) logger.info("Created associate.") #----------------------------- # Set our `SkillSet` objects. #----------------------------- skill_sets = validated_data.get('skill_sets', None) if skill_sets is not None: if len(skill_sets) > 0: associate.skill_sets.set(skill_sets) logger.info("Set associate skill sets.") #------------------------------- # Set our `VehicleType` objects. #------------------------------- vehicle_types = validated_data.get('vehicle_types', None) if vehicle_types is not None: if len(vehicle_types) > 0: associate.vehicle_types.set(vehicle_types) logger.info("Set associate vehicle types.") #------------------------ # Set our `Tag` objects. #------------------------ tags = validated_data.get('tags', None) if tags is not None: if len(tags) > 0: associate.tags.set(tags) logger.info("Set associate tags.") #----------------------------- # Create our `Comment` object. #----------------------------- extra_comment = validated_data.get('extra_comment', None) if extra_comment is not None: comment = Comment.objects.create( created_by=self.context['created_by'], last_modified_by=self.context['created_by'], text=extra_comment, created_from=self.context['created_from'], created_from_is_public=self.context['created_from_is_public']) AssociateComment.objects.create( about=associate, comment=comment, ) logger.info("Set associate comments.") #---------------------------------------- # Set our `InsuranceRequirement` objects. #---------------------------------------- insurance_requirements = validated_data.get('insurance_requirements', None) if insurance_requirements is not None: if len(insurance_requirements) > 0: associate.insurance_requirements.set(insurance_requirements) logger.info("Set associate insurance requirements.") # Update validation data. # validated_data['comments'] = AssociateComment.objects.filter(associate=associate) validated_data['created_by'] = self.context['created_by'] validated_data['last_modified_by'] = self.context['created_by'] validated_data['extra_comment'] = None validated_data['id'] = associate.id # validated_data['assigned_skill_sets'] = associate.skill_sets.all() # Return our validated data. return validated_data
class CustomerRetrieveUpdateDestroySerializer(serializers.ModelSerializer): # owner = serializers.PrimaryKeyRelatedField(many=False, read_only=True) given_name = serializers.CharField( required=True, allow_blank=False, validators=[] ) last_name = serializers.CharField( required=True, allow_blank=False, validators=[] ) gender = serializers.CharField( required=True, allow_blank=False, allow_null=False, ) address_country = serializers.CharField( required=True, allow_blank=False, validators=[] ) address_region = serializers.CharField( required=True, allow_blank=False, validators=[] ) address_locality = serializers.CharField( required=True, allow_blank=False, validators=[] ) postal_code = serializers.CharField( required=True, allow_blank=False, validators=[] ) street_address = serializers.CharField( required=True, allow_blank=False, validators=[] ) # We are overriding the `email` field to include unique email validation. email = serializers.EmailField( validators=[ UniqueValidator(queryset=Customer.objects.all()), ], required=False ) # All comments are created by our `create` function and not by # `django-rest-framework`. # comments = CustomerCommentSerializer(many=True, read_only=True) # This is a field used in the `create` function if the user enters a # comment. This field is *ONLY* to be used during the POST creation and # will be blank during GET. extra_comment = serializers.CharField(write_only=True, allow_null=True) # Custom formatting of our telephone fields. fax_number = PhoneNumberField(allow_null=True, required=False) telephone = PhoneNumberField(allow_null=True, required=False) other_telephone = PhoneNumberField(allow_null=True, required=False) # Add password adding. password = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=63, style={'input_type': 'password'}, validators = [ MatchingDuelFieldsValidator( another_field='password_repeat', message=_("Inputted passwords fields do not match.") ), EnhancedPasswordStrengthFieldValidator() ] ) password_repeat = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=63, style={'input_type': 'password'} ) # Attach with our foreign keys. how_hear = serializers.PrimaryKeyRelatedField( many=False, required=True, allow_null=False, queryset=HowHearAboutUsItem.objects.all() ) # Generate the full name of the associate. full_name = serializers.SerializerMethodField() address = serializers.SerializerMethodField() address_url = serializers.SerializerMethodField() full_address = serializers.SerializerMethodField() e164_telephone = serializers.SerializerMethodField() created_by = serializers.SerializerMethodField() last_modified_by = serializers.SerializerMethodField() how_hear_pretty = serializers.SerializerMethodField() # # Fields used for mapping to organizations. # organization_name = serializers.CharField( write_only=True, required=False, allow_blank=False, allow_null=False, max_length=63, validators=[ # UniqueValidator( # queryset=Organization.objects.all(), # ) ], ) organization_type_of = serializers.CharField( write_only=True, required=True, allow_blank=True, max_length=63, validators=[] ) organization_address_country = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=127, validators=[] ) organization_address_locality = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=127, validators=[] ) organization_address_region = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=127, validators=[] ) organization_post_office_box_number = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=255, validators=[] ) organization_postal_code = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=127, validators=[] ) organization_street_address = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=255, validators=[] ) organization_street_address_extra = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=255, validators=[] ) class Meta: model = Customer fields = ( # Thing 'id', 'created', 'created_by', 'last_modified', 'last_modified_by', # 'owner', 'description', # Person 'given_name', 'middle_name', 'last_name', 'birthdate', 'join_date', 'gender', # Misc (Read/Write) 'is_ok_to_email', 'is_ok_to_text', 'is_senior', 'is_support', 'job_info_read', 'type_of', 'tags', 'how_hear', 'how_hear_other', # Misc (Read Only) 'extra_comment', 'full_name', 'address', 'address_url', 'full_address', 'e164_telephone', 'how_hear_pretty', # Misc (Write Only) 'password', 'password_repeat', # Organization 'organization_name', 'organization_type_of', 'organization_address_country', 'organization_address_locality', 'organization_address_region', 'organization_post_office_box_number', 'organization_postal_code', 'organization_street_address', 'organization_street_address_extra', # Contact Point 'area_served', 'available_language', 'contact_type', 'email', 'fax_number', # 'hours_available', #TODO: FIX 'telephone', 'telephone_extension', 'telephone_type_of', 'other_telephone', 'other_telephone_extension', 'other_telephone_type_of', # Postal Address 'address_country', 'address_locality', 'address_region', 'post_office_box_number', 'postal_code', 'street_address', 'street_address_extra', # Geo-coordinate 'elevation', 'latitude', 'longitude', # 'location' #TODO: FIX ) def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related( 'owner', 'created_by', 'last_modified_by', 'tags', 'comments', 'organization' ) return queryset def validate(self, data): """ Override the validator to provide additional custom validation based on our custom logic. 1. If 'type_of' == Commercial then make sure 'email' was inputted. """ # CASE 1 - Other reason if data.get('type_of', None) == COMMERCIAL_CUSTOMER_TYPE_OF_ID: email = data.get('email', None) if email is None: raise serializers.ValidationError(_("Please provide an email if client is commercial.")) # Return our data. return data def get_full_name(self, obj): try: return str(obj) except Exception as e: return None def get_address(self, obj): try: return obj.get_postal_address_without_postal_code() except Exception as e: return None def get_address_url(self, obj): try: return obj.get_google_maps_url() except Exception as e: return None def get_full_address(self, obj): try: return obj.get_postal_address() except Exception as e: return None def get_created_by(self, obj): try: return str(obj.created_by) except Exception as e: return None def get_last_modified_by(self, obj): try: return str(obj.last_modified_by) except Exception as e: return None def get_e164_telephone(self, obj): """ Converts the "PhoneNumber" object into a "E164" format. See: https://github.com/daviddrysdale/python-phonenumbers """ try: if obj.telephone: return phonenumbers.format_number(obj.telephone, phonenumbers.PhoneNumberFormat.E164) else: return "-" except Exception as e: return None def get_how_hear_pretty(self, obj): try: return str(obj.how_hear) except Exception as e: return None def update(self, instance, validated_data): """ Override this function to include extra functionality. """ # Get our inputs. email = validated_data.get('email', instance.email) type_of_customer = validated_data.get('type_of', UNASSIGNED_CUSTOMER_TYPE_OF_ID) #----------------------------------------------------------- # Bugfix: Created `SharedUser` object if not created before. #----------------------------------------------------------- if instance.owner is None and email: owner = SharedUser.objects.filter(email=email).first() if owner: instance.owner = owner instance.save() logger.info("BUGFIX: Attached existing shared user to staff.") else: instance.owner = SharedUser.objects.create( first_name=validated_data['given_name'], last_name=validated_data['last_name'], email=email, is_active=True, franchise=self.context['franchise'], was_email_activated=True ) instance.save() logger.info("BUGFIX: Created shared user and attached to staff.") #--------------------------- # Update `SharedUser` object. #--------------------------- if instance.owner: # Update details. instance.owner.first_name = validated_data.get('given_name', instance.owner.first_name) instance.owner.last_name = validated_data.get('last_name', instance.owner.last_name) if email: instance.owner.email = email instance.owner.username = get_unique_username_from_email(email) # Update the password. password = validated_data.get('password', None) instance.owner.set_password(password) # Save the model to the database. instance.owner.save() logger.info("Updated shared user.") #--------------------------- # Update `Customer` object. #--------------------------- # Update email instance. instance.description = validated_data.get('description', instance.description) if email: instance.email = email # Profile instance.given_name = validated_data.get('given_name', instance.given_name) instance.middle_name = validated_data.get('middle_name', instance.middle_name) instance.last_name = validated_data.get('last_name', instance.last_name) instance.last_modified_by = self.context['last_modified_by'] instance.birthdate = validated_data.get('birthdate', instance.birthdate) instance.join_date = validated_data.get('join_date', instance.join_date) instance.gender = validated_data.get('gender', instance.gender) # Misc (Read/Write) instance.is_ok_to_email = validated_data.get('is_ok_to_email', instance.is_ok_to_email) instance.is_ok_to_text = validated_data.get('is_ok_to_text', instance.is_ok_to_text) instance.is_senior = validated_data.get('is_ok_to_text', instance.is_ok_to_text) instance.is_senior = validated_data.get('is_senior', instance.is_senior) instance.is_support = validated_data.get('is_support', instance.is_support) instance.job_info_read = validated_data.get('job_info_read', instance.job_info_read) instance.how_hear = validated_data.get('how_hear', instance.how_hear) instance.how_hear_other = validated_data.get('how_hear_other', instance.how_hear_other) instance.type_of=validated_data.get('type_of', instance.type_of) # # Misc (Read Only) instance.last_modified_by = self.context['last_modified_by'] instance.last_modified_from = self.context['last_modified_from'] instance.last_modified_from_is_public = self.context['last_modified_from_is_public'] # 'organizations', #TODO: FIX # Contact Point instance.area_served = validated_data.get('area_served', instance.area_served) instance.available_language = validated_data.get('available_language', instance.available_language) instance.contact_type = validated_data.get('contact_type', instance.contact_type) instance.email = validated_data.get('email', instance.contact_type) instance.fax_number = validated_data.get('fax_number', instance.fax_number) # 'hours_available', #TODO: FIX instance.telephone=validated_data.get('telephone', None) instance.telephone_extension=validated_data.get('telephone_extension', None) instance.telephone_type_of=validated_data.get('telephone_type_of', None) instance.other_telephone=validated_data.get('other_telephone', None) instance.other_telephone_extension=validated_data.get('other_telephone_extension', None) instance.other_telephone_type_of=validated_data.get('other_telephone_type_of', None) # Postal Address instance.address_country = validated_data.get('address_country', instance.address_country) instance.address_locality = validated_data.get('address_locality', instance.address_locality) instance.address_region = validated_data.get('address_region', instance.address_region) instance.post_office_box_number = validated_data.get('post_office_box_number', instance.post_office_box_number) instance.postal_code = validated_data.get('postal_code', instance.postal_code) instance.street_address = validated_data.get('street_address', instance.street_address) instance.street_address_extra = validated_data.get('street_address_extra', instance.street_address_extra) # Geo-coordinate instance.elevation = validated_data.get('elevation', instance.elevation) instance.latitude = validated_data.get('latitude', instance.latitude) instance.longitude = validated_data.get('longitude', instance.longitude) # 'location' #TODO: FIX # Save instance.save() logger.info("Updated the customer.") #------------------------ # Set our `Tag` objects. #------------------------ tags = validated_data.get('tags', instance.tags) if tags is not None: if len(tags) > 0: instance.tags.set(tags) #--------------------------- # Attach our comment. #--------------------------- extra_comment = validated_data.get('extra_comment', None) if extra_comment is not None: comment = Comment.objects.create( created_by=self.context['last_modified_by'], last_modified_by=self.context['last_modified_by'], text=extra_comment, created_from = self.context['last_modified_from'], created_from_is_public = self.context['last_modified_from_is_public'] ) CustomerComment.objects.create( about=instance, comment=comment, ) #---------------------------------------- # Update or create `Organization` object. #---------------------------------------- if type_of_customer == COMMERCIAL_CUSTOMER_TYPE_OF_ID: logger.info("Detected commercial customer...") organization_name = validated_data.get('organization_name', None) organization_type_of = validated_data.get('organization_type_of', None) organization_address_country = validated_data.get('organization_address_country', None) organization_address_locality = validated_data.get('organization_address_locality', None) organization_address_region = validated_data.get('organization_address_region', None) organization_post_office_box_number = validated_data.get('organization_post_office_box_number', None) organization_postal_code = validated_data.get('organization_postal_code', None) organization_street_address = validated_data.get('organization_street_address', None) organization_street_address_extra = validated_data.get('organization_street_address_extra', None) if organization_name and organization_type_of: organization, created = Organization.objects.update_or_create( name=organization_name, type_of=organization_type_of, defaults={ 'type_of': organization_type_of, 'name': organization_name, 'address_country': organization_address_country, 'address_locality': organization_address_locality, 'address_region': organization_address_region, 'post_office_box_number': organization_post_office_box_number, 'postal_code': organization_postal_code, 'street_address': organization_street_address, 'street_address_extra': organization_street_address_extra, } ) logger.info("Created organization.") if created: logger.info("Created organization.") organization.owner = instance.owner organization.save() instance.organization = organization instance.save() logger.info("Attached created organization to customer.") #--------------------------- # Update validation data. #--------------------------- # validated_data['comments'] = CustomerComment.objects.filter(customer=instance) validated_data['last_modified_by'] = self.context['last_modified_by'] validated_data['extra_comment'] = None # Return our validated data. return validated_data
class CustomerListCreateSerializer(serializers.ModelSerializer): # OVERRIDE THE MODEL FIELDS AND ENFORCE THE FOLLOWING CUSTOM VALIDATION RULES. given_name = serializers.CharField( required=True, allow_blank=False, validators=[] ) last_name = serializers.CharField( required=True, allow_blank=False, validators=[] ) gender = serializers.CharField( required=True, allow_blank=False, allow_null=False, ) address_country = serializers.CharField( required=True, allow_blank=False, validators=[] ) address_region = serializers.CharField( required=True, allow_blank=False, validators=[] ) address_locality = serializers.CharField( required=True, allow_blank=False, validators=[] ) postal_code = serializers.CharField( required=True, allow_blank=False, validators=[] ) street_address = serializers.CharField( required=True, allow_blank=False, validators=[] ) # We are overriding the `email` field to include unique email validation. email = serializers.EmailField( validators=[ UniqueValidator(queryset=SharedUser.objects.all()), ], required=False, ) # All comments are created by our `create` function and not by # `django-rest-framework`. # comments = CustomerCommentSerializer(many=True, read_only=True, allow_null=True) # This is a field used in the `create` function if the user enters a # comment. This field is *ONLY* to be used during the POST creation and # will be blank during GET. extra_comment = serializers.CharField(write_only=True, allow_null=True) # Custom formatting of our telephone fields. fax_number = PhoneNumberField(allow_null=True, required=False) telephone = PhoneNumberField(allow_null=False, required=True,) other_telephone = PhoneNumberField(allow_null=True, required=False) # Attach with our foreign keys. how_hear = serializers.PrimaryKeyRelatedField( many=False, required=True, allow_null=False, queryset=HowHearAboutUsItem.objects.all() ) # Add password adding. password = serializers.CharField( write_only=True, required=True, allow_blank=False, max_length=63, style={'input_type': 'password'}, validators = [ MatchingDuelFieldsValidator( another_field='password_repeat', message=_("Inputted passwords fields do not match.") ), EnhancedPasswordStrengthFieldValidator() ] ) password_repeat = serializers.CharField( write_only=True, required=True, allow_blank=False, max_length=63, style={'input_type': 'password'} ) # # Fields used for mapping to organizations. # organization_name = serializers.CharField( write_only=True, required=False, allow_blank=False, allow_null=False, max_length=63, validators=[ # UniqueValidator( # queryset=Organization.objects.all(), # ) ], ) organization_type_of = serializers.CharField( write_only=True, required=True, allow_blank=True, max_length=63, validators=[] ) organization_address_country = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=127, validators=[] ) organization_address_locality = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=127, validators=[] ) organization_address_region = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=127, validators=[] ) organization_post_office_box_number = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=255, validators=[] ) organization_postal_code = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=127, validators=[] ) organization_street_address = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=255, validators=[] ) organization_street_address_extra = serializers.CharField( write_only=True, required=False, allow_blank=True, max_length=255, validators=[] ) # Generate the full name of the associate. full_name = serializers.SerializerMethodField() address = serializers.SerializerMethodField() address_url = serializers.SerializerMethodField() full_address = serializers.SerializerMethodField() e164_telephone = serializers.SerializerMethodField() # Meta Information. class Meta: model = Customer fields = ( # Thing 'id', 'created', 'last_modified', # 'owner', 'description', # Person 'given_name', 'middle_name', 'last_name', 'birthdate', 'join_date', 'gender', 'nationality', # Misc (Read/Write) 'is_ok_to_email', 'is_ok_to_text', 'is_senior', 'is_support', 'job_info_read', 'type_of', 'tags', 'how_hear', 'how_hear_other', # Misc (Read Only) # 'comments', 'password', 'password_repeat', 'state', 'full_name', 'address', 'address_url', 'full_address', 'e164_telephone', # Organization 'organization_name', 'organization_type_of', 'organization_address_country', 'organization_address_locality', 'organization_address_region', 'organization_post_office_box_number', 'organization_postal_code', 'organization_street_address', 'organization_street_address_extra', # Misc (Write Only) 'extra_comment', # Contact Point 'area_served', 'available_language', 'contact_type', 'email', 'fax_number', # 'hours_available', #TODO: FIX 'telephone', 'telephone_extension', 'telephone_type_of', 'other_telephone', 'other_telephone_extension', 'other_telephone_type_of', # Postal Address 'address_country', 'address_locality', 'address_region', 'post_office_box_number', 'postal_code', 'street_address', 'street_address_extra', # Geo-coordinate 'elevation', 'latitude', 'longitude', # 'location' #TODO: FIX ) extra_kwargs = { "is_ok_to_email": { "error_messages": { "invalid": "Please pick either 'Yes' or 'No' choice." } }, "is_ok_to_text": { "error_messages": { "invalid": "Please pick either 'Yes' or 'No' choice." } } } def validate_telephone(self, value): """ Include validation on no-blanks """ if value is None: raise serializers.ValidationError("This field may not be blank.") return value def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ queryset = queryset.prefetch_related( 'owner', 'created_by', 'last_modified_by', 'tags', 'comments' ) return queryset def get_full_name(self, obj): try: return str(obj) except Exception as e: return None def get_address(self, obj): try: return obj.get_postal_address_without_postal_code() except Exception as e: return None def get_address_url(self, obj): try: return obj.get_google_maps_url() except Exception as e: return None def get_full_address(self, obj): try: return obj.get_postal_address() except Exception as e: return None def get_e164_telephone(self, obj): """ Converts the "PhoneNumber" object into a "NATIONAL" format. See: https://github.com/daviddrysdale/python-phonenumbers """ try: if obj.telephone: return phonenumbers.format_number(obj.telephone, phonenumbers.PhoneNumberFormat.E164) else: return "-" except Exception as e: return None def create(self, validated_data): """ Override the `create` function to add extra functinality. """ type_of_customer = validated_data.get('type_of', UNASSIGNED_CUSTOMER_TYPE_OF_ID) # Format our telephone(s) fax_number = validated_data.get('fax_number', None) if fax_number: fax_number = phonenumbers.parse(fax_number, "CA") telephone = validated_data.get('telephone', None) if telephone: telephone = phonenumbers.parse(telephone, "CA") other_telephone = validated_data.get('other_telephone', None) if other_telephone: other_telephone = phonenumbers.parse(other_telephone, "CA") #------------------- # Create our user. #------------------- # Extract our "email" field. email = validated_data.get('email', None) # If an email exists then owner = None if email: owner = SharedUser.objects.create( first_name=validated_data['given_name'], last_name=validated_data['last_name'], email=email, is_active=True, franchise=self.context['franchise'], was_email_activated=True ) # Attach the user to the `Customer` group. owner.groups.add(CUSTOMER_GROUP_ID) # Update the password. password = validated_data.get('password', None) owner.set_password(password) owner.save() logger.info("Created shared user.") #--------------------------------------------------- # Create our `Customer` object in our tenant schema. #--------------------------------------------------- customer = Customer.objects.create( owner=owner, created_by=self.context['created_by'], last_modified_by=self.context['created_by'], description=validated_data.get('description', None), # Profile given_name=validated_data['given_name'], last_name=validated_data['last_name'], middle_name=validated_data['middle_name'], birthdate=validated_data.get('birthdate', None), join_date=validated_data.get('join_date', None), gender=validated_data.get('gender', None), # Misc is_senior=validated_data.get('is_senior', False), is_support=validated_data.get('is_support', False), job_info_read=validated_data.get('job_info_read', False), how_hear=validated_data.get('how_hear', 1), how_hear_other=validated_data.get('how_hear_other', "Not answered"), type_of=type_of_customer, created_from = self.context['created_from'], created_from_is_public = self.context['created_from_is_public'], # Contact Point email=email, area_served=validated_data.get('area_served', None), available_language=validated_data.get('available_language', None), contact_type=validated_data.get('contact_type', None), fax_number=fax_number, # 'hours_available', #TODO: IMPLEMENT. telephone=telephone, telephone_extension=validated_data.get('telephone_extension', None), telephone_type_of=validated_data.get('telephone_type_of', None), other_telephone=other_telephone, other_telephone_extension=validated_data.get('other_telephone_extension', None), other_telephone_type_of=validated_data.get('other_telephone_type_of', None), # Postal Address address_country=validated_data.get('address_country', None), address_locality=validated_data.get('address_locality', None), address_region=validated_data.get('address_region', None), post_office_box_number=validated_data.get('post_office_box_number', None), postal_code=validated_data.get('postal_code', None), street_address=validated_data.get('street_address', None), street_address_extra=validated_data.get('street_address_extra', None), # Geo-coordinate elevation=validated_data.get('elevation', None), latitude=validated_data.get('latitude', None), longitude=validated_data.get('longitude', None), # 'location' #TODO: IMPLEMENT. ) logger.info("Created customer.") #----------------------------------- # Create or update our Organization. #----------------------------------- type_of_customer = validated_data.get('type_of', UNASSIGNED_CUSTOMER_TYPE_OF_ID) if type_of_customer == COMMERCIAL_CUSTOMER_TYPE_OF_ID: logger.info("Detected commercial customer...") organization_name = validated_data.get('organization_name', None) organization_type_of = validated_data.get('organization_type_of', None) organization_address_country = validated_data.get('organization_address_country', None) organization_address_locality = validated_data.get('organization_address_locality', None) organization_address_region = validated_data.get('organization_address_region', None) organization_post_office_box_number = validated_data.get('organization_post_office_box_number', None) organization_postal_code = validated_data.get('organization_postal_code', None) organization_street_address = validated_data.get('organization_street_address', None) organization_street_address_extra = validated_data.get('organization_street_address_extra', None) if organization_name and organization_type_of: organization, created = Organization.objects.update_or_create( name=organization_name, type_of=organization_type_of, defaults={ 'type_of': organization_type_of, 'name': organization_name, 'address_country': organization_address_country, 'address_locality': organization_address_locality, 'address_region': organization_address_region, 'post_office_box_number': organization_post_office_box_number, 'postal_code': organization_postal_code, 'street_address': organization_street_address, 'street_address_extra': organization_street_address_extra, } ) logger.info("Created organization.") if created: logger.info("Created organization.") organization.owner = owner organization.save() customer.organization = organization # DEPRECATED customer.organization_name = validated_data.get('organization_name', None) customer.organization_type_of = validated_data.get('organization_type_of', None) customer.save() logger.info("Attached created organization to customer.") #------------------------ # Set our `Tag` objects. #------------------------ tags = validated_data.get('tags', None) if tags is not None: if len(tags) > 0: customer.tags.set(tags) #----------------------------- # Create our `Comment` object. #----------------------------- extra_comment = validated_data.get('extra_comment', None) if extra_comment is not None: comment = Comment.objects.create( created_by=self.context['created_by'], last_modified_by=self.context['created_by'], text=extra_comment, created_from = self.context['created_from'], created_from_is_public = self.context['created_from_is_public'] ) CustomerComment.objects.create( about=customer, comment=comment, ) # Update validation data. # validated_data['comments'] = CustomerComment.objects.filter(customer=customer) validated_data['created_by'] = self.context['created_by'] validated_data['last_modified_by'] = self.context['created_by'] validated_data['extra_comment'] = None validated_data['telephone'] = telephone validated_data['fax_number'] = fax_number validated_data['other_telephone'] = other_telephone validated_data['id'] = customer.id # Return our validated data. return validated_data