예제 #1
0
class ActivitySheetItemListCreateSerializer(serializers.ModelSerializer):
    # text = serializers.CharField(
    #     required=True,
    #     allow_blank=False,
    #     allow_null=False,
    #     validators=[]
    # )
    associate_full_name = serializers.SerializerMethodField()
    associate_telephone = PhoneNumberField(read_only=True,
                                           source="associate.telephone")
    associate_e164_telephone = serializers.SerializerMethodField()
    associate_email = serializers.EmailField(read_only=True,
                                             source="associate.email")
    pretty_state = serializers.CharField(read_only=True,
                                         source="get_pretty_state")

    class Meta:
        model = ActivitySheetItem
        fields = (
            'id',
            'job',
            'ongoing_job',
            'associate',
            'associate_full_name',
            'associate_telephone',
            'associate_e164_telephone',
            'associate_email',
            'comment',
            'state',
            'pretty_state',
            'created_at',
            'created_by',
        )

    def get_associate_full_name(self, obj):
        try:
            if obj.associate:
                return str(obj.associate)
        except Exception as e:
            pass
        return None

    def get_associate_e164_telephone(self, obj):
        """
        Converts the "PhoneNumber" object into a "NATIONAL" format.
        See: https://github.com/daviddrysdale/python-phonenumbers
        """
        try:
            if obj.associate.telephone:
                return phonenumbers.format_number(
                    obj.associate.telephone,
                    phonenumbers.PhoneNumberFormat.E164)
            else:
                return "-"
        except Exception as e:
            print(e)
            return None
예제 #2
0
class AssociateRetrieveUpdateDestroySerializer(serializers.ModelSerializer):
    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=Associate.objects.all()),
        ],
        required=True,
        allow_blank=False,
    )

    # All comments are created by our `create` function and not by
    # `django-rest-framework`.
    # comments = AssociateCommentSerializer(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)

    # 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)
    insurance_requirements = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=InsuranceRequirement.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()
    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)

    is_active = serializers.BooleanField(
        write_only=True,
        required=True,
    )

    join_date = serializers.DateField(required=True, )

    # Attach with our foreign keys.
    how_hear = serializers.PrimaryKeyRelatedField(
        many=False,
        required=True,
        allow_null=False,
        queryset=HowHearAboutUsItem.objects.all())

    class Meta:
        model = Associate
        fields = (
            # Thing
            'id',
            'created',
            'last_modified',
            'description',

            # 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',
            # 'assigned_skill_sets',
            # 'organizations', #TODO: FIX

            # # 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 setup_eager_loading(cls, queryset):
        """ Perform necessary eager loading of data. """
        queryset = queryset.prefetch_related('owner', 'created_by',
                                             'last_modified_by', 'skill_sets',
                                             'tags', 'vehicle_types'
                                             'comments',
                                             'insurance_requirements')
        return queryset

    @transaction.atomic
    def update(self, instance, validated_data):
        """
        Override this function to include extra functionality.
        """
        # For debugging purposes only.
        # logger.info(validated_data)

        # Get our inputs.
        email = validated_data.get('email', instance.email)
        skill_sets = validated_data.get('skill_sets', None)
        vehicle_types = validated_data.get('vehicle_types', None)
        insurance_requirements = validated_data.get('insurance_requirements',
                                                    None)

        # Update telephone numbers.
        fax_number = validated_data.get('fax_number', instance.fax_number)
        if fax_number is not None:
            validated_data['fax_number'] = phonenumbers.parse(fax_number, "CA")
        telephone = validated_data.get('telephone', instance.telephone)
        if telephone is not None:
            validated_data['telephone'] = phonenumbers.parse(telephone, "CA")
        other_telephone = validated_data.get('other_telephone',
                                             instance.other_telephone)
        if other_telephone is not None:
            validated_data['other_telephone'] = phonenumbers.parse(
                other_telephone, "CA")

        #---------------------------
        # Update `SharedUser` object.
        #---------------------------
        instance.owner, created = SharedUser.objects.update_or_create(
            email=email,
            defaults={
                'email': email,
                'first_name': validated_data.get('given_name',
                                                 instance.given_name),
                'last_name': validated_data.get('last_name',
                                                instance.last_name),
                'is_active': validated_data.get('is_active', False)
            })
        logger.info("Updated shared user.")

        # Update the password.
        password = validated_data.get('password', None)
        if password:
            instance.owner.set_password(password)

            # Save the model.
            instance.owner.save()
            logger.info("Password was updated.")

        #---------------------------
        # Update `Associate` object.
        #---------------------------
        instance.email = email

        # Profile
        instance.given_name = validated_data.get('given_name',
                                                 instance.given_name)
        instance.last_name = validated_data.get('last_name',
                                                instance.last_name)
        instance.middle_name = validated_data.get('middle_name',
                                                  instance.middle_name)
        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)
        instance.description = validated_data.get('description',
                                                  instance.description)
        instance.tax_id = validated_data.get('tax_id', instance.tax_id)

        # Misc
        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.hourly_salary_desired = validated_data.get(
            'hourly_salary_desired', instance.hourly_salary_desired)
        instance.limit_special = validated_data.get('limit_special',
                                                    instance.limit_special)
        instance.dues_date = validated_data.get('dues_date',
                                                instance.dues_date)
        instance.commercial_insurance_expiry_date = validated_data.get(
            'commercial_insurance_expiry_date',
            instance.commercial_insurance_expiry_date)
        instance.auto_insurance_expiry_date = validated_data.get(
            'auto_insurance_expiry_date', instance.auto_insurance_expiry_date)
        instance.wsib_insurance_date = validated_data.get(
            'wsib_insurance_date', instance.wsib_insurance_date)
        instance.wsib_number = validated_data.get('wsib_number',
                                                  instance.wsib_number)
        instance.police_check = validated_data.get('police_check',
                                                   instance.police_check)
        instance.drivers_license_class = validated_data.get(
            'drivers_license_class', instance.drivers_license_class)
        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.last_modified_from = self.context['last_modified_from']
        instance.last_modified_from_is_public = self.context[
            'last_modified_from_is_public']
        instance.last_modified_by = self.context['last_modified_by']
        # 'organizations', #TODO: IMPLEMENT.

        # 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.fax_number = validated_data.get('fax_number',
                                                 instance.fax_number)
        # 'hours_available', #TODO: IMPLEMENT.
        instance.telephone = validated_data.get('telephone',
                                                instance.telephone)
        instance.telephone_extension = validated_data.get(
            'telephone_extension', instance.telephone_extension)
        instance.telephone_type_of = validated_data.get(
            'telephone_type_of', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)
        instance.other_telephone = validated_data.get('other_telephone',
                                                      instance.other_telephone)
        instance.other_telephone_extension = validated_data.get(
            'other_telephone_extension', instance.other_telephone_extension)
        instance.other_telephone_type_of = validated_data.get(
            'other_telephone_type_of', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)

        # 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: IMPLEMENT.

        # Emergency contact.
        instance.emergency_contact_name = validated_data.get(
            'emergency_contact_name', instance.emergency_contact_name)
        instance.emergency_contact_relationship = validated_data.get(
            'emergency_contact_relationship',
            instance.emergency_contact_relationship)
        instance.emergency_contact_telephone = validated_data.get(
            'emergency_contact_telephone',
            instance.emergency_contact_telephone)
        instance.emergency_contact_alternative_telephone = validated_data.get(
            'emergency_contact_alternative_telephone',
            instance.emergency_contact_alternative_telephone)

        # Save our instance.
        instance.save()
        logger.info("Updated the associate.")

        #-----------------------------
        # Set our `SkillSet` objects.
        #-----------------------------
        if skill_sets is not None:
            if len(skill_sets) > 0:
                instance.skill_sets.set(skill_sets)
                logger.info("Set associate skill sets.")

        #-------------------------------
        # Set our `VehicleType` objects.
        #-------------------------------
        if vehicle_types is not None:
            if len(vehicle_types) > 0:
                instance.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:
                instance.tags.set(tags)
                logger.info("Set associate 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'])
            AssociateComment.objects.create(
                about=instance,
                comment=comment,
            )
            logger.info("Set associate comments.")

        #----------------------------------------
        # Set our `InsuranceRequirement` objects.
        #----------------------------------------
        if insurance_requirements is not None:
            if len(insurance_requirements) > 0:
                instance.insurance_requirements.set(insurance_requirements)
                logger.info("Set associate insurance requirements.")

        #---------------------------
        # Update validation data.
        #---------------------------
        # validated_data['comments'] = AssociateComment.objects.filter(associate=instance)
        validated_data['last_modified_by'] = self.context['last_modified_by']
        # validated_data['extra_comment'] = None
        # validated_data['assigned_skill_sets'] = instance.skill_sets.all()

        # Return our validated data.
        return validated_data
예제 #3
0
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 CustomerContactUpdateSerializer(serializers.ModelSerializer):
    organization_name = serializers.CharField(
        required=False,
        allow_blank=True,
        validators=[
            UniqueValidator(queryset=Customer.objects.all()),
        ],
    )
    organization_type_of = serializers.IntegerField(required=False, )
    given_name = serializers.CharField(required=True,
                                       allow_blank=False,
                                       validators=[])
    last_name = 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,
        allow_null=True,
        allow_blank=True,
    )

    # Custom formatting of our telephone fields.
    primary_phone = PhoneNumberField(allow_null=False,
                                     required=True,
                                     source="telephone")
    primary_phone_type_of = serializers.IntegerField(
        required=True, validators=[], source="telephone_type_of")
    secondary_phone = PhoneNumberField(allow_null=True,
                                       required=False,
                                       source="other_telephone")
    secondary_phone_type_of = serializers.IntegerField(
        required=False, validators=[], source="other_telephone_type_of")

    class Meta:
        model = Customer
        fields = (
            'organization_name',
            'organization_type_of',
            'given_name',
            'last_name',
            'email',
            'primary_phone',
            'primary_phone_type_of',
            'secondary_phone',
            'secondary_phone_type_of',
            'is_ok_to_email',
            'is_ok_to_text',
        )

    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 get_organization_name(self, data):
        if data.get('type_of', None) == COMMERCIAL_CUSTOMER_TYPE_OF_ID:
            organization_name = data.get('organization_name', None)
            if organization_name is None:
                raise serializers.ValidationError(
                    _("Please provide the organization name."))
        return data

    def get_organization_type_of(self, data):
        if data.get('type_of', None) == COMMERCIAL_CUSTOMER_TYPE_OF_ID:
            organization_type_of = data.get('organization_type_of', None)
            if organization_type_of is None:
                raise serializers.ValidationError(
                    _("Please provide the organization type of."))
        return data

    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 update(self, instance, validated_data):
        """
        Override this function to include extra functionality.
        """
        # Get our inputs.
        email = validated_data.get('email', instance.email)

        #-----------------------------------------------------------
        # 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.
        #---------------------------
        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.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.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']
        instance.organization_name = validated_data.get(
            'organization_name', instance.organization_name)
        instance.organization_type_of = validated_data.get(
            'organization_type_of', instance.organization_type_of)

        instance.email = validated_data.get('email', instance.contact_type)
        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)

        instance.save()
        logger.info("Updated the customer.")
        return instance
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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
예제 #8
0
class StaffContactUpdateSerializer(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=[])

    # We are overriding the `email` field to include unique email validation.
    work_email = serializers.EmailField(validators=[
        UniqueValidator(queryset=Staff.objects.all()),
    ],
                                        required=False,
                                        source="email")
    personal_email = serializers.EmailField(validators=[
        UniqueValidator(queryset=Staff.objects.all()),
    ],
                                            required=False)

    # Custom formatting of our telephone fields.
    primary_phone = PhoneNumberField(allow_null=False,
                                     required=True,
                                     source="telephone")
    primary_phone_type_of = serializers.IntegerField(
        required=True, validators=[], source="telephone_type_of")
    secondary_phone = PhoneNumberField(allow_null=True,
                                       required=False,
                                       source="other_telephone")
    secondary_phone_type_of = serializers.IntegerField(
        required=False, validators=[], source="other_telephone_type_of")

    # Meta Information.
    class Meta:
        model = Staff
        fields = (
            'given_name',
            'last_name',
            'primary_phone',
            'primary_phone_type_of',
            'secondary_phone',
            'secondary_phone_type_of',
            'work_email',
            'personal_email',
        )

    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_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

    def update(self, instance, validated_data):
        """
        Override this function to include extra functionality.
        """
        # # For debugging purposes only.
        # print(validated_data)

        # Get our inputs.
        work_email = validated_data.get('work_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=work_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=work_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.
        #---------------------------
        # Update the account.
        if work_email:
            instance.owner.email = work_email
            instance.owner.username = get_unique_username_from_email(
                work_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.save()
        logger.info("Updated the shared user.")

        #---------------------------
        # Update `Staff` object.
        #---------------------------
        # Person
        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)
        # Misc
        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']

        # 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 = work_email
        instance.personal_email = personal_email
        # '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', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)

        # Save our instance.
        instance.save()
        logger.info("Updated the staff member.")

        # Return our validated data.
        return instance
class OngoingWorkOrderRetrieveSerializer(serializers.ModelSerializer):
    customer = serializers.PrimaryKeyRelatedField(many=False, read_only=True)
    associate = serializers.PrimaryKeyRelatedField(many=False, read_only=True)
    associate_full_name = serializers.SerializerMethodField()
    associate_telephone = PhoneNumberField(read_only=True, source="associate.telephone")
    associate_telephone_type_of = serializers.IntegerField(read_only=True, source="associate.telephone_type_of")
    associate_pretty_telephone_type_of = serializers.CharField(read_only=True, source="associate.get_pretty_telephone_type_of")
    associate_other_telephone = PhoneNumberField(read_only=True, source="associate.other_telephone")
    associate_other_telephone_type_of = serializers.IntegerField(read_only=True, source="associate.other_telephone_type_of")
    associate_pretty_other_telephone_type_of = serializers.CharField(read_only=True, source="associate.get_pretty_other_telephone_type_of")
    customer_full_name = serializers.SerializerMethodField()
    customer_telephone = PhoneNumberField(read_only=True, source="customer.telephone")
    customer_telephone_type_of = serializers.IntegerField(read_only=True, source="customer.telephone_type_of")
    customer_pretty_telephone_type_of = serializers.CharField(read_only=True, source="customer.get_pretty_telephone_type_of")
    customer_other_telephone = PhoneNumberField(read_only=True, source="customer.other_telephone")
    customer_other_telephone_type_of = serializers.IntegerField(read_only=True, source="customer.other_telephone_type_of")
    customer_pretty_other_telephone_type_of = serializers.CharField(read_only=True, source="customer.get_pretty_other_telephone_type_of")
    pretty_status = serializers.CharField(read_only=True, source="get_pretty_status")
    pretty_type_of = serializers.CharField(read_only=True, source="get_pretty_type_of")

    class Meta:
        model = OngoingWorkOrder
        fields = (
            # Read only fields.
            'id',

            # Read Only fields.
            'associate_full_name',
            'associate_telephone',
            'associate_telephone_type_of',
            'associate_pretty_telephone_type_of',
            'associate_other_telephone',
            'associate_other_telephone_type_of',
            'associate_pretty_other_telephone_type_of',
            'customer_full_name',
            'customer_telephone',
            'customer_telephone_type_of',
            'customer_pretty_telephone_type_of',
            'customer_other_telephone',
            'customer_other_telephone_type_of',
            'customer_pretty_other_telephone_type_of',
            'pretty_status',
            'pretty_type_of',
            'associate',
            'customer',

            # Read / write fields.
            'state'
        )

    def get_associate_full_name(self, obj):
        try:
            if obj.associate:
                return str(obj.associate)
        except Exception as e:
            pass
        return None

    def get_customer_full_name(self, obj):
        try:
            if obj.customer:
                return str(obj.customer)
        except Exception as e:
            pass
        return None

    # def get_pretty_skill_sets(self, obj):
    #     try:
    #         s = SkillSetListCreateSerializer(obj.skill_sets.all(), many=True)
    #         return s.data
    #     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
예제 #10
0
class WorkOrderRetrieveUpdateDestroySerializer(serializers.ModelSerializer):
    assigned_skill_sets = SkillSetListCreateSerializer(many=True, read_only=True)
    associate_first_name = serializers.ReadOnlyField(source='associate.owner.first_name')
    associate_last_name = serializers.ReadOnlyField(source='associate.owner.last_name')
    customer_first_name = serializers.ReadOnlyField(source='customer.owner.first_name')
    customer_last_name = serializers.ReadOnlyField(source='customer.owner.last_name')

    # created_by = serializers.ReadOnlyField()
    # last_modified_by = serializers.ReadOnlyField()
    tags = serializers.PrimaryKeyRelatedField(many=True, queryset=Tag.objects.all(), 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)

    invoice_ids = serializers.CharField(required=False, allow_null=True)

    assignment_date = serializers.DateField(required=False, allow_null=True)
    completion_date = serializers.DateField(required=False, allow_null=True)
    start_date = serializers.DateField(required=False, allow_null=True)
    invoice_service_fee_payment_date = serializers.DateField(required=False, allow_null=True)
    invoice_date = serializers.DateField(required=False, allow_null=True)

    associate_full_name = serializers.SerializerMethodField()
    associate_telephone = PhoneNumberField(read_only=True, source="associate.telephone")
    associate_telephone_type_of = serializers.IntegerField(read_only=True, source="associate.telephone_type_of")
    associate_pretty_telephone_type_of = serializers.CharField(read_only=True, source="associate.get_pretty_telephone_type_of")
    associate_other_telephone = PhoneNumberField(read_only=True, source="associate.other_telephone")
    associate_other_telephone_type_of = serializers.IntegerField(read_only=True, source="associate.other_telephone_type_of")
    associate_pretty_other_telephone_type_of = serializers.CharField(read_only=True, source="associate.get_pretty_other_telephone_type_of")
    associate_tax_id = serializers.ReadOnlyField(source='associate.tax_id')
    associate_service_fee = serializers.IntegerField(source='associate.service_fee.id', read_only=True,)
    associate_service_fee_label = serializers.CharField(source='associate.service_fee.title', read_only=True,)
    customer_address = serializers.SerializerMethodField()
    customer_email = serializers.EmailField(read_only=True, source="associate.email")
    customer_full_name = serializers.SerializerMethodField()
    customer_telephone = PhoneNumberField(read_only=True, source="customer.telephone")
    customer_telephone_type_of = serializers.IntegerField(read_only=True, source="customer.telephone_type_of")
    customer_pretty_telephone_type_of = serializers.CharField(read_only=True, source="customer.get_pretty_telephone_type_of")
    customer_other_telephone = PhoneNumberField(read_only=True, source="customer.other_telephone")
    customer_other_telephone_type_of = serializers.IntegerField(read_only=True, source="customer.other_telephone_type_of")
    customer_pretty_other_telephone_type_of = serializers.CharField(read_only=True, source="customer.get_pretty_other_telephone_type_of")
    pretty_status = serializers.CharField(read_only=True, source="get_pretty_status")
    pretty_type_of = serializers.CharField(read_only=True, source="get_pretty_type_of")
    pretty_skill_sets = serializers.SerializerMethodField()
    pretty_tags = serializers.SerializerMethodField()
    pretty_invoice_service_fee = serializers.SerializerMethodField()
    latest_pending_task = serializers.ReadOnlyField(source="latest_pending_task.id")
    latest_pending_task_type_of = serializers.SerializerMethodField()
    # ReadOnlyField(source="latest_pending_task.type_of")
    pretty_latest_pending_task = serializers.SerializerMethodField()
    created_at = serializers.DateTimeField(read_only=True, source="created")
    created_by = serializers.SerializerMethodField()
    last_modified_at = serializers.DateTimeField(read_only=True, source="last_modified")
    last_modified_by = serializers.SerializerMethodField()
    was_survey_conducted = serializers.BooleanField(read_only=True)
    no_survey_conducted_reason = serializers.IntegerField(read_only=True)
    no_survey_conducted_reason_other =serializers.CharField(read_only=True)
    score = serializers.FloatField(read_only=True)
    was_job_satisfactory = serializers.BooleanField(read_only=True)
    was_job_finished_on_time_and_on_budget = serializers.BooleanField(read_only=True)
    was_associate_punctual = serializers.BooleanField(read_only=True)
    was_associate_professional = serializers.BooleanField(read_only=True)
    would_customer_refer_our_organization = serializers.BooleanField(read_only=True)
    cloned_from = serializers.IntegerField(read_only=True, allow_null=False, source="cloned_from.id")
    invoice_id = serializers.IntegerField(read_only=True, allow_null=False, source="invoice.order.id")
    invoice_paid_to = serializers.IntegerField(read_only=True,)
    invoice_deposit_amount = serializers.CharField(read_only=True, source="invoice_deposit_amount.amount")
    invoice_sub_total_amount = serializers.CharField(read_only=True, source="invoice_sub_total_amount.amount")

    closing_reason = serializers.IntegerField(read_only=True,)
    closing_reason_other = serializers.CharField(read_only=True, allow_blank=False, allow_null=False,)
    closing_reason_comment = serializers.CharField(read_only=True, allow_blank=False, allow_null=False,)

    class Meta:
        model = WorkOrder
        fields = (
            # Read only field.
            'id',
            # 'comments',
            'closing_reason',
            'closing_reason_comment',
            'closing_reason_other',

            'assigned_skill_sets',
            'associate_first_name',
            'associate_last_name',
            'customer_first_name',
            'customer_last_name',
            'tags',

            # Write only fields.
            'extra_comment',

            # Read or write fields.
            'assignment_date',
            'associate',
            'completion_date',
            'customer',
            'hours',
            'is_home_support_service',
            # 'created_by',
            # 'last_modified_by',
            'skill_sets',
            'description',
            'start_date',
            'invoice_service_fee',
            'invoice_ids',
            'invoice_service_fee_payment_date',
            'invoice_date',
            'invoice_quote_amount',
            'invoice_labour_amount',
            'invoice_material_amount',
            'invoice_other_costs_amount',
            'invoice_quoted_labour_amount',
            'invoice_quoted_material_amount',
            'invoice_quoted_other_costs_amount',
            'invoice_total_quote_amount',
            'invoice_sub_total_amount',
            'invoice_tax_amount',
            'invoice_deposit_amount',
            'invoice_total_amount',
            'invoice_amount_due',
            'invoice_service_fee_amount',
            'invoice_actual_service_fee_amount_paid',
            'state',
            'invoice_balance_owing_amount',
            'visits',
            'cloned_from',
            'invoice_id',

            # Read Only fields.
            'associate_full_name',
            'associate_telephone',
            'associate_telephone_type_of',
            'associate_pretty_telephone_type_of',
            'associate_other_telephone',
            'associate_other_telephone_type_of',
            'associate_pretty_other_telephone_type_of',
            'associate_tax_id',
            'associate_service_fee_label',
            'customer_address',
            'associate_service_fee',
            'customer_email',
            'customer_full_name',
            'customer_telephone',
            'customer_telephone_type_of',
            'customer_pretty_telephone_type_of',
            'customer_other_telephone',
            'customer_other_telephone_type_of',
            'customer_pretty_other_telephone_type_of',
            'pretty_status',
            'pretty_type_of',
            'pretty_skill_sets',
            'pretty_tags',
            'pretty_invoice_service_fee',
            'pretty_latest_pending_task',
            'latest_pending_task',
            'latest_pending_task_type_of',
            'created_at',
            'created_by',
            'last_modified_at',
            'last_modified_by',
            'was_survey_conducted',
            'no_survey_conducted_reason',
            'no_survey_conducted_reason_other',
            'score',
            'was_job_satisfactory',
            'was_job_finished_on_time_and_on_budget',
            'was_associate_punctual',
            'was_associate_professional',
            'would_customer_refer_our_organization',
            'invoice_paid_to',
        )

    def setup_eager_loading(cls, queryset):
        """ Perform necessary eager loading of data. """
        queryset = queryset.prefetch_related(
            'associate',
            'tags',
            'created_by',
            'customer',
            'comments',
            'last_modified_by',
            'skill_sets',
            'invoice_service_fee'
        )
        return queryset

    def get_associate_full_name(self, obj):
        try:
            if obj.associate:
                return obj.associate.get_pretty_name()
        except Exception as e:
            pass
        return None

    def get_customer_full_name(self, obj):
        try:
            if obj.customer:
                return obj.customer.get_pretty_name()
        except Exception as e:
            pass
        return None

    def get_customer_address(self, obj):
        try:
            return obj.customer.get_postal_address()
        except Exception as e:
            return None

    def get_pretty_skill_sets(self, obj):
        try:
            s = SkillSetListCreateSerializer(obj.skill_sets.all(), many=True)
            return s.data
        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_latest_pending_task_type_of(self, obj):
        try:
            if obj.latest_pending_task.type_of == FOLLOW_UP_CUSTOMER_SURVEY_TASK_ITEM_TYPE_OF_ID:
                return FOLLOW_UP_DID_CUSTOMER_REVIEW_ASSOCIATE_AFTER_JOB_TASK_ITEM_TYPE_OF_ID

            return obj.latest_pending_task.type_of
        except Exception as e:
            return None

    def get_pretty_latest_pending_task(self, obj):
        try:
            return str(obj.latest_pending_task)
        except Exception as e:
            return None

    def validate_invoice_service_fee_payment_date(self, value):
        """
        Include validation on no-blanks if the state is set to be changed
        to ``completed_and_paid`` state of the work order.
        """
        state = self.context['state']
        if state:
            if state == WORK_ORDER_STATE.COMPLETED_AND_PAID:
                if value is None:
                    raise serializers.ValidationError("This field may not be blank when submitting a payment status.")
        return value

    def validate_invoice_date(self, value):
        """
        Include validation on no-blanks
        """
        if value is None:
            raise serializers.ValidationError("This field may not be blank.")
        return value

    def get_pretty_tags(self, obj):
        try:
            s = TagListCreateSerializer(obj.tags.all(), many=True)
            return s.data
        except Exception as e:
            return None

    def get_pretty_invoice_service_fee(self, obj):
        try:
            return obj.invoice_service_fee.title
        except Exception as e:
            return None

    @transaction.atomic
    def update(self, instance, validated_data):
        """
        Override this function to include extra functionality.
        """
        instance.assignment_date = validated_data.get('assignment_date', instance.assignment_date)
        instance.associate = validated_data.get('associate', instance.associate)
        instance.completion_date = validated_data.get('completion_date', instance.completion_date)
        instance.customer = validated_data.get('customer', instance.customer)
        instance.hours = validated_data.get('hours', instance.hours)
        instance.is_home_support_service = validated_data.get('is_home_support_service', instance.is_home_support_service)
        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']
        instance.description = validated_data.get('description', instance.description)
        instance.start_date = validated_data.get('start_date', instance.start_date)
        instance.state = validated_data.get('state', instance.state)

        # Financial information.
        instance.visits = validated_data.get('visits', instance.visits)
        instance.invoice_service_fee = validated_data.get('invoice_service_fee', instance.invoice_service_fee)
        instance.invoice_service_fee_payment_date = validated_data.get('invoice_service_fee_payment_date', instance.invoice_service_fee_payment_date)
        instance.invoice_ids = validated_data.get('invoice_ids', instance.invoice_ids)
        instance.invoice_date = validated_data.get('invoice_date', instance.invoice_date)
        instance.invoice_quote_amount = validated_data.get('invoice_quote_amount', instance.invoice_quote_amount)
        instance.invoice_labour_amount = validated_data.get('invoice_labour_amount', instance.invoice_labour_amount)
        instance.invoice_material_amount = validated_data.get('invoice_material_amount', instance.invoice_material_amount)
        instance.invoice_other_costs_amount = validated_data.get('invoice_other_costs_amount', instance.invoice_other_costs_amount)
        instance.invoice_quoted_material_amount = validated_data.get('invoice_quoted_material_amount', instance.invoice_quoted_material_amount)
        instance.invoice_quoted_labour_amount = validated_data.get('invoice_quoted_labour_amount', instance.invoice_quoted_labour_amount)
        instance.invoice_quoted_other_costs_amount = validated_data.get('invoice_quoted_other_costs_amount', instance.invoice_quoted_other_costs_amount)
        instance.invoice_total_quote_amount = validated_data.get('invoice_total_quote_amount', instance.invoice_total_quote_amount)
        instance.invoice_tax_amount = validated_data.get('invoice_tax_amount', instance.invoice_tax_amount)
        instance.invoice_total_amount = validated_data.get('invoice_total_amount', instance.invoice_total_amount)
        instance.invoice_amount_due = validated_data.get('invoice_amount_due', instance.invoice_amount_due)
        instance.invoice_service_fee_amount = validated_data.get('invoice_service_fee_amount', instance.invoice_service_fee_amount)
        instance.invoice_actual_service_fee_amount_paid = validated_data.get('invoice_actual_service_fee_amount_paid', instance.invoice_actual_service_fee_amount_paid)
        instance.invoice_balance_owing_amount = instance.invoice_service_fee_amount.amount - instance.invoice_actual_service_fee_amount_paid.amount

        # Update the job type based off of the customers type. This is done in
        # in case the new customer is either commercial or residential but
        # the job type was marked the opposite.
        if instance.customer:
            instance.job_type_of = UNASSIGNED_JOB_TYPE_OF_ID
            if instance.customer.type_of == RESIDENTIAL_CUSTOMER_TYPE_OF_ID:
                instance.job_type_of = RESIDENTIAL_JOB_TYPE_OF_ID
            if instance.customer.type_of == COMMERCIAL_CUSTOMER_TYPE_OF_ID:
                instance.job_type_of = COMMERCIAL_JOB_TYPE_OF_ID

        # Save the model.
        instance.save()
        logger.info("Updated order object.")

        #TODO: IMPLEMENT ASSOCIATE GLOBAL BALANCE OWING AMOUNT.

        #-----------------------------
        # Set our `Tags` objects.
        #-----------------------------
        tags = validated_data.get('tags', None)
        if tags is not None:
            if len(tags) > 0:
                instance.tags.set(tags)
                logger.info("Set tags with order.")

        #-----------------------------
        # Set our `SkillSet` objects.
        #-----------------------------
        skill_sets = validated_data.get('skill_sets', instance.skill_sets)
        if skill_sets is not None:
            if len(skill_sets) > 0:
                instance.skill_sets.set(skill_sets)
                logger.info("Set skill set with order.")

        #-----------------------------
        # 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['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']
            )
            WorkOrderComment.objects.create(
                about=instance,
                comment=comment,
            )
            logger.info("Created and set comment with order.")

        # Update validation data.
        # validated_data['comments'] = WorkOrderComment.objects.filter(order=instance)
        validated_data['created'] = instance.created
        validated_data['created_by'] = instance.created_by
        validated_data['last_modified_by'] = self.context['last_modified_by']
        validated_data['extra_comment'] = None
        validated_data['assigned_skill_sets'] = instance.skill_sets.all()

        #---------------------------------------------------------------------
        # Update the `Associate` object for the `balance_owing_amount` field.
        #---------------------------------------------------------------------
        if instance.associate:
            import django_rq
            from shared_etl.tasks import update_balance_owing_amount_for_associate_func
            django_rq.enqueue(update_balance_owing_amount_for_associate_func, {
                'franchise_schema_name': self.context['franchise'].schema_name,
                'associate_id': instance.associate.id
            })

        #----------------------------------------
        # Clear our cache for specific functions.
        #----------------------------------------
        if instance.associate:
            instance.associate.invalidate("latest_completed_and_paid_order")

        # Return our validated data.
        return validated_data
예제 #11
0
class StaffAccountUpdateSerializer(serializers.ModelSerializer):
    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=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 = (
            'description',
            'account_type',
            'tags',
            'is_active',
            'police_check',

            # 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 update(self, instance, validated_data):
        """
        Override this function to include extra functionality.
        """
        # For debugging purposes only.
        # print(validated_data)

        #-------------------------------------
        # Bugfix: Created `SharedUser` object.
        #-------------------------------------
        if instance.owner is None:
            owner = SharedUser.objects.filter(email=instance.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=instance.given_name,
                    last_name=instance.last_name,
                    email=instance.mail,
                    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.
        #---------------------------
        # Update the account.
        if instance.email:
            instance.owner.email = instance.email
            instance.owner.username = get_unique_username_from_email(
                instance.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.police_check = validated_data.get('police_check',
                                                   instance.police_check)
        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" and account_type != None:
            account_type = int(account_type)
            instance.owner.groups.set([account_type])
            logger.info("Updated the group membership.")

        #---------------------------
        # Update `Staff` object.
        #---------------------------

        # Misc
        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']

        # Emergency contact.
        instance.description = validated_data.get('description', None)
        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)

        # Return our validated data.
        return instance
class AssociateProfileSerializer(serializers.ModelSerializer):
    organization_name = serializers.CharField(
        required=False,
        allow_blank=True,
        allow_null=True,
        max_length=63,
        validators=[
            UniqueValidator(
                queryset=Associate.objects.all(),
            )
        ],
    )
    organization_type_of = serializers.IntegerField(
        required=False,
        validators=[]
    )
    organization_type_of_label = serializers.ReadOnlyField(source="get_organization_type_of_label")
    type_of = serializers.IntegerField(
        required=False,
        allow_null=True,
        validators=[]
    )
    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=Associate.objects.all()),
        ],
        required=True,
        allow_blank=False,
    )

    # All comments are created by our `create` function and not by
    # `django-rest-framework`.
    # comments = AssociateCommentSerializer(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)

    # 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)
    insurance_requirements = serializers.PrimaryKeyRelatedField(many=True, queryset=InsuranceRequirement.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()
    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)

    is_active = serializers.BooleanField(
        write_only=True,
        required=True,
    )

    join_date = serializers.DateField(
        required=True,
    )

    # 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()
    pretty_skill_sets = serializers.SerializerMethodField()
    pretty_tags = serializers.SerializerMethodField()
    pretty_insurance_requirements = serializers.SerializerMethodField()
    pretty_vehicle_types = serializers.SerializerMethodField()
    latest_completed_and_paid_order = serializers.SerializerMethodField()
    balance_owing_amount = serializers.SerializerMethodField()
    created_by = serializers.SerializerMethodField()
    last_modified_by = serializers.SerializerMethodField()
    score = serializers.FloatField(read_only=True)
    avatar_url = serializers.SerializerMethodField()
    associate_id = serializers.PrimaryKeyRelatedField(many=False, queryset=Associate.objects.all(), source="id")

    # SharedUser
    id = serializers.PrimaryKeyRelatedField(many=False, queryset=SharedUser.objects.all(), source="owner.id")
    first_name = serializers.CharField(read_only=True, allow_blank=False, source="owner.first_name")
    last_name = serializers.CharField(read_only=True, allow_blank=False, source="owner.last_name")
    group_id = serializers.SerializerMethodField()
    date_joined = serializers.DateTimeField(read_only=True, source="owner.date_joined")
    franchise = serializers.PrimaryKeyRelatedField(many=False, queryset=SharedFranchise.objects.all(), source="owner.franchise")

    class Meta:
        model = Associate
        fields = (
            # SharedUser
            'id',
            'first_name',
            'last_name',
            'group_id',
            'date_joined',
            'franchise',

            # Thing
            'associate_id',
            'created',
            'created_by',
            'last_modified',
            'last_modified_by',
            'description',

            # Person
            'organization_name',
            'organization_type_of',
            'organization_type_of_label',
            'given_name',
            'middle_name',
            'last_name',
            'birthdate',
            'join_date',
            'gender',
            'description',
            'tax_id',

            # Misc (Read/Write)
            'type_of',
            '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',
            # 'assigned_skill_sets',
            # 'organizations', #TODO: FIX
            'full_name',
            'address',
            'address_url',
            'full_address',
            'e164_telephone',
            'pretty_skill_sets',
            'pretty_tags',
            'pretty_insurance_requirements',
            'pretty_vehicle_types',
            'latest_completed_and_paid_order',
            'balance_owing_amount',
            'score',
            'avatar_url',

            # # 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 setup_eager_loading(cls, queryset):
        """ Perform necessary eager loading of data. """
        queryset = queryset.prefetch_related(
            'owner',
            'created_by',
            'last_modified_by',
            'skill_sets',
            'tags',
            'vehicle_types'
            'comments',
            'insurance_requirements'
        )
        return queryset

    def get_group_id(self, obj):
        try:
            return obj.owner.groups.first().id
        except Exception as e:
            print("AssociateProfileSerializer | get_group_id |", e)
            return None

    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 get_pretty_skill_sets(self, obj):
        try:
            s = SkillSetListCreateSerializer(obj.skill_sets.all(), many=True)
            return s.data
        except Exception as e:
            return None

    def get_pretty_tags(self, obj):
        try:
            s = TagListCreateSerializer(obj.tags.all(), many=True)
            return s.data
        except Exception as e:
            return None

    def get_pretty_insurance_requirements(self, obj):
        try:
            s = InsuranceRequirementListCreateSerializer(obj.insurance_requirements.all(), many=True)
            return s.data
        except Exception as e:
            return None

    def get_pretty_vehicle_types(self, obj):
        try:
            s = VehicleTypeListCreateSerializer(obj.vehicle_types.all(), many=True)
            return s.data
        except Exception as e:
            return None

    def get_latest_completed_and_paid_order(self, obj):
        try:
            task_item = obj.latest_completed_and_paid_order
            return {
                'id': task_item.id,
                'paid_at': str(task_item.invoice_service_fee_payment_date)
            }
        except Exception as e:
            return {
                'id': None,
                'paid_at': None
            }

    def get_balance_owing_amount(self, obj):
        try:
            return str(obj.balance_owing_amount).replace("C", "")
        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_avatar_url(self, obj):
        try:
            return obj.avatar_image.image_file.url
        except Exception as e:
            return None
예제 #13
0
class AssociateRetrieveUpdateDestroySerializer(serializers.ModelSerializer):
    organization_name = serializers.CharField(
        required=False,
        allow_blank=True,
        allow_null=True,
        max_length=63,
        validators=[UniqueValidator(queryset=Associate.objects.all(), )],
    )
    organization_type_of = serializers.IntegerField(required=False,
                                                    validators=[])
    organization_type_of_label = serializers.ReadOnlyField(
        source="get_organization_type_of_label")
    type_of = serializers.IntegerField(required=False,
                                       allow_null=True,
                                       validators=[])
    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=Associate.objects.all()),
        ],
        required=True,
        allow_blank=False,
    )

    # All comments are created by our `create` function and not by
    # `django-rest-framework`.
    # comments = AssociateCommentSerializer(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)

    # 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)
    insurance_requirements = serializers.PrimaryKeyRelatedField(
        many=True,
        queryset=InsuranceRequirement.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()
    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)

    is_active = serializers.BooleanField(
        write_only=True,
        required=True,
    )

    join_date = serializers.DateField(required=True, )

    # 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()
    pretty_skill_sets = serializers.SerializerMethodField()
    pretty_tags = serializers.SerializerMethodField()
    pretty_insurance_requirements = serializers.SerializerMethodField()
    pretty_vehicle_types = serializers.SerializerMethodField()
    latest_completed_and_paid_order = serializers.SerializerMethodField()
    balance_owing_amount = serializers.SerializerMethodField()
    created_by = serializers.SerializerMethodField()
    last_modified_by = serializers.SerializerMethodField()
    score = serializers.FloatField(read_only=True)
    avatar_url = serializers.SerializerMethodField()
    state = serializers.IntegerField(read_only=True, source="owner.is_active")

    # Attach with our foreign keys.
    service_fee = serializers.PrimaryKeyRelatedField(
        many=False,
        required=True,
        allow_null=False,
        queryset=WorkOrderServiceFee.objects.all())
    service_fee_label = serializers.CharField(
        read_only=True,
        source="service_fee.title",
        allow_null=True,
        allow_blank=True,
    )
    away_log = AwayLogRetrieveUpdateDestroySerializer(
        many=False,
        allow_null=True,
    )

    class Meta:
        model = Associate
        fields = (
            # Thing
            'id',
            'created',
            'created_by',
            'last_modified',
            'last_modified_by',
            'description',

            # Person
            'organization_name',
            'organization_type_of',
            'organization_type_of_label',
            'given_name',
            'middle_name',
            'last_name',
            'birthdate',
            'join_date',
            'gender',
            'description',
            'tax_id',

            # Misc (Read/Write)
            'type_of',
            '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
            'service_fee',
            'service_fee_label',

            # Misc (Read Only)
            # 'comments',
            # 'assigned_skill_sets',
            # 'organizations', #TODO: FIX
            'full_name',
            'address',
            'address_url',
            'full_address',
            'e164_telephone',
            'pretty_skill_sets',
            'pretty_tags',
            'pretty_insurance_requirements',
            'pretty_vehicle_types',
            'latest_completed_and_paid_order',
            'balance_owing_amount',
            'score',
            'avatar_url',
            'state',
            'away_log',

            # # 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 setup_eager_loading(cls, queryset):
        """ Perform necessary eager loading of data. """
        queryset = queryset.prefetch_related('owner', 'created_by',
                                             'last_modified_by', 'skill_sets',
                                             'tags', 'vehicle_types'
                                             'comments',
                                             'insurance_requirements')
        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 get_pretty_skill_sets(self, obj):
        try:
            s = SkillSetListCreateSerializer(obj.skill_sets.all(), many=True)
            return s.data
        except Exception as e:
            return None

    def get_pretty_tags(self, obj):
        try:
            s = TagListCreateSerializer(obj.tags.all(), many=True)
            return s.data
        except Exception as e:
            return None

    def get_pretty_insurance_requirements(self, obj):
        try:
            s = InsuranceRequirementListCreateSerializer(
                obj.insurance_requirements.all(), many=True)
            return s.data
        except Exception as e:
            return None

    def get_pretty_vehicle_types(self, obj):
        try:
            s = VehicleTypeListCreateSerializer(obj.vehicle_types.all(),
                                                many=True)
            return s.data
        except Exception as e:
            return None

    def get_latest_completed_and_paid_order(self, obj):
        try:
            task_item = obj.latest_completed_and_paid_order
            return {
                'id': task_item.id,
                'paid_at': str(task_item.invoice_service_fee_payment_date)
            }
        except Exception as e:
            return {'id': None, 'paid_at': None}

    def get_balance_owing_amount(self, obj):
        try:
            return str(obj.balance_owing_amount).replace("C", "")
        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_avatar_url(self, obj):
        try:
            return obj.avatar_image.image_file.url
        except Exception as e:
            return None

    @transaction.atomic
    def update(self, instance, validated_data):
        """
        Override this function to include extra functionality.
        """
        # For debugging purposes only.
        # logger.info(validated_data)

        # Get our inputs.
        email = validated_data.get('email', instance.email)
        skill_sets = validated_data.get('skill_sets', None)
        vehicle_types = validated_data.get('vehicle_types', None)
        insurance_requirements = validated_data.get('insurance_requirements',
                                                    None)

        # Update telephone numbers.
        fax_number = validated_data.get('fax_number', instance.fax_number)
        if fax_number is not None:
            validated_data['fax_number'] = phonenumbers.parse(fax_number, "CA")
        telephone = validated_data.get('telephone', instance.telephone)
        if telephone is not None:
            validated_data['telephone'] = phonenumbers.parse(telephone, "CA")
        other_telephone = validated_data.get('other_telephone',
                                             instance.other_telephone)
        if other_telephone is not None:
            validated_data['other_telephone'] = phonenumbers.parse(
                other_telephone, "CA")

        #---------------------------
        # Update `SharedUser` object.
        #---------------------------
        instance.owner, created = SharedUser.objects.update_or_create(
            email=email,
            defaults={
                'email': email,
                'first_name': validated_data.get('given_name',
                                                 instance.given_name),
                'last_name': validated_data.get('last_name',
                                                instance.last_name),
                'is_active': validated_data.get('is_active', False)
            })
        logger.info("Updated shared user.")

        # Update the password.
        password = validated_data.get('password', None)
        if password:
            instance.owner.set_password(password)

            # Save the model.
            instance.owner.save()
            logger.info("Password was updated.")

        #---------------------------
        # Update `Associate` object.
        #---------------------------
        instance.email = email

        # Profile
        instance.given_name = validated_data.get('given_name',
                                                 instance.given_name)
        instance.last_name = validated_data.get('last_name',
                                                instance.last_name)
        instance.middle_name = validated_data.get('middle_name',
                                                  instance.middle_name)
        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)
        instance.description = validated_data.get('description',
                                                  instance.description)
        instance.tax_id = validated_data.get('tax_id', instance.tax_id)

        # Misc
        instance.organization_name = validated_data.get(
            "organization_name", None)
        instance.organization_type_of = validated_data.get(
            "organization_type_of", UNKNOWN_ORGANIZATION_TYPE_OF_ID)
        instance.type_of = validated_data.get("type_of",
                                              UNASSIGNED_ASSOCIATE_TYPE_OF_ID)
        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.hourly_salary_desired = validated_data.get(
            'hourly_salary_desired', instance.hourly_salary_desired)
        instance.limit_special = validated_data.get('limit_special',
                                                    instance.limit_special)
        instance.dues_date = validated_data.get('dues_date',
                                                instance.dues_date)
        instance.commercial_insurance_expiry_date = validated_data.get(
            'commercial_insurance_expiry_date',
            instance.commercial_insurance_expiry_date)
        instance.auto_insurance_expiry_date = validated_data.get(
            'auto_insurance_expiry_date', instance.auto_insurance_expiry_date)
        instance.wsib_insurance_date = validated_data.get(
            'wsib_insurance_date', instance.wsib_insurance_date)
        instance.wsib_number = validated_data.get('wsib_number',
                                                  instance.wsib_number)
        instance.police_check = validated_data.get('police_check',
                                                   instance.police_check)
        instance.drivers_license_class = validated_data.get(
            'drivers_license_class', instance.drivers_license_class)
        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.last_modified_from = self.context['last_modified_from']
        instance.last_modified_from_is_public = self.context[
            'last_modified_from_is_public']
        instance.last_modified_by = self.context['last_modified_by']
        instance.service_fee = validated_data.get('service_fee',
                                                  instance.service_fee)

        # 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.fax_number = validated_data.get('fax_number',
                                                 instance.fax_number)
        # 'hours_available', #TODO: IMPLEMENT.
        instance.telephone = validated_data.get('telephone',
                                                instance.telephone)
        instance.telephone_extension = validated_data.get(
            'telephone_extension', instance.telephone_extension)
        instance.telephone_type_of = validated_data.get(
            'telephone_type_of', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)
        instance.other_telephone = validated_data.get('other_telephone',
                                                      instance.other_telephone)
        instance.other_telephone_extension = validated_data.get(
            'other_telephone_extension', instance.other_telephone_extension)
        instance.other_telephone_type_of = validated_data.get(
            'other_telephone_type_of', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)

        # 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: IMPLEMENT.

        # Emergency contact.
        instance.emergency_contact_name = validated_data.get(
            'emergency_contact_name', instance.emergency_contact_name)
        instance.emergency_contact_relationship = validated_data.get(
            'emergency_contact_relationship',
            instance.emergency_contact_relationship)
        instance.emergency_contact_telephone = validated_data.get(
            'emergency_contact_telephone',
            instance.emergency_contact_telephone)
        instance.emergency_contact_alternative_telephone = validated_data.get(
            'emergency_contact_alternative_telephone',
            instance.emergency_contact_alternative_telephone)

        # Save our instance.
        instance.save()
        logger.info("Updated the associate.")

        #-----------------------------
        # Set our `SkillSet` objects.
        #-----------------------------
        if skill_sets is not None:
            if len(skill_sets) > 0:
                instance.skill_sets.set(skill_sets)
                logger.info("Set associate skill sets.")

        #-------------------------------
        # Set our `VehicleType` objects.
        #-------------------------------
        if vehicle_types is not None:
            if len(vehicle_types) > 0:
                instance.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:
                instance.tags.set(tags)
                logger.info("Set associate 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'])
            AssociateComment.objects.create(
                about=instance,
                comment=comment,
            )
            logger.info("Set associate comments.")

        #----------------------------------------
        # Set our `InsuranceRequirement` objects.
        #----------------------------------------
        if insurance_requirements is not None:
            if len(insurance_requirements) > 0:
                instance.insurance_requirements.set(insurance_requirements)
                logger.info("Set associate insurance requirements.")

        #---------------------------
        # Update validation data.
        #---------------------------
        # validated_data['comments'] = AssociateComment.objects.filter(associate=instance)
        validated_data['last_modified_by'] = self.context['last_modified_by']
        # validated_data['extra_comment'] = None
        # validated_data['assigned_skill_sets'] = instance.skill_sets.all()

        # This is for debugging purposes only.
        # raise serializers.ValidationError("This is a temporary stop point set by the programmer.")

        # Return our validated data.
        return validated_data
class AssociateAccountUpdateSerializer(serializers.ModelSerializer):
    skill_sets = serializers.PrimaryKeyRelatedField(many=True, queryset=SkillSet.objects.all(), allow_null=True)
    insurance_requirements = serializers.PrimaryKeyRelatedField(many=True, queryset=InsuranceRequirement.objects.all(), allow_null=True)
    emergency_contact_telephone = PhoneNumberField(allow_null=True, required=False)
    emergency_contact_alternative_telephone = PhoneNumberField(allow_null=True, required=False)
    service_fee = serializers.PrimaryKeyRelatedField(many=False, required=True, allow_null=False, queryset=WorkOrderServiceFee.objects.all())

    class Meta:
        model = Associate
        fields = (
            'tax_id',
            '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
            'skill_sets',            # many-to-many
            'insurance_requirements', # many-to-many
            'service_fee',

            # Emergency Contact
            'emergency_contact_name',
            'emergency_contact_relationship',
            'emergency_contact_telephone',
            'emergency_contact_alternative_telephone'
        )
        extra_kwargs = {
            "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 update(self, instance, validated_data):
        """
        Override this function to include extra functionality.
        """
        # For debugging purposes only.
        # logger.info(validated_data)

        # Get our inputs.
        skill_sets = validated_data.get('skill_sets', None)
        vehicle_types = validated_data.get('vehicle_types', None)
        insurance_requirements = validated_data.get('insurance_requirements', None)

        #---------------------------
        # Update `Associate` object.
        #---------------------------
        instance.tax_id = validated_data.get('tax_id', instance.tax_id)
        instance.hourly_salary_desired=validated_data.get('hourly_salary_desired', instance.hourly_salary_desired)
        instance.limit_special=validated_data.get('limit_special', instance.limit_special)
        instance.dues_date=validated_data.get('dues_date', instance.dues_date)
        instance.commercial_insurance_expiry_date=validated_data.get('commercial_insurance_expiry_date', instance.commercial_insurance_expiry_date)
        instance.auto_insurance_expiry_date = validated_data.get('auto_insurance_expiry_date', instance.auto_insurance_expiry_date)
        instance.wsib_insurance_date = validated_data.get('wsib_insurance_date', instance.wsib_insurance_date)
        instance.wsib_number = validated_data.get('wsib_number', instance.wsib_number)
        instance.police_check=validated_data.get('police_check', instance.police_check)
        instance.drivers_license_class=validated_data.get('drivers_license_class', instance.drivers_license_class)
        instance.last_modified_from = self.context['last_modified_from']
        instance.last_modified_from_is_public = self.context['last_modified_from_is_public']
        instance.last_modified_by = self.context['last_modified_by']
        instance.emergency_contact_name=validated_data.get('emergency_contact_name', instance.emergency_contact_name)
        instance.emergency_contact_relationship=validated_data.get('emergency_contact_relationship', instance.emergency_contact_relationship)
        instance.emergency_contact_telephone=validated_data.get('emergency_contact_telephone', instance.emergency_contact_telephone)
        instance.emergency_contact_alternative_telephone=validated_data.get('emergency_contact_alternative_telephone', instance.emergency_contact_alternative_telephone)
        instance.service_fee = validated_data.get('service_fee', instance.service_fee) 
        instance.save()
        logger.info("Updated the associate.")

        #-----------------------------
        # Set our `SkillSet` objects.
        #-----------------------------
        if skill_sets is not None:
            if len(skill_sets) > 0:
                instance.skill_sets.set(skill_sets)
                logger.info("Set associate skill sets.")

        #-------------------------------
        # Set our `VehicleType` objects.
        #-------------------------------
        if vehicle_types is not None:
            if len(vehicle_types) > 0:
                instance.vehicle_types.set(vehicle_types)
                logger.info("Set associate vehicle types.")

        #----------------------------------------
        # Set our `InsuranceRequirement` objects.
        #----------------------------------------
        if insurance_requirements is not None:
            if len(insurance_requirements) > 0:
                instance.insurance_requirements.set(insurance_requirements)
                logger.info("Set associate insurance requirements.")

        return instance
class OngoingWorkOrderRetrieveUpdateDestroySerializer(
        serializers.ModelSerializer):
    associate_full_name = serializers.SerializerMethodField()
    associate_telephone = PhoneNumberField(read_only=True,
                                           source="associate.telephone")
    associate_telephone_type_of = serializers.IntegerField(
        read_only=True, source="associate.telephone_type_of")
    associate_pretty_telephone_type_of = serializers.CharField(
        read_only=True, source="associate.get_pretty_telephone_type_of")
    associate_other_telephone = PhoneNumberField(
        read_only=True, source="associate.other_telephone")
    associate_other_telephone_type_of = serializers.IntegerField(
        read_only=True, source="associate.other_telephone_type_of")
    associate_pretty_other_telephone_type_of = serializers.CharField(
        read_only=True, source="associate.get_pretty_other_telephone_type_of")
    customer_full_name = serializers.SerializerMethodField()
    customer_telephone = PhoneNumberField(read_only=True,
                                          source="customer.telephone")
    customer_telephone_type_of = serializers.IntegerField(
        read_only=True, source="customer.telephone_type_of")
    customer_pretty_telephone_type_of = serializers.CharField(
        read_only=True, source="customer.get_pretty_telephone_type_of")
    customer_other_telephone = PhoneNumberField(
        read_only=True, source="customer.other_telephone")
    customer_other_telephone_type_of = serializers.IntegerField(
        read_only=True, source="customer.other_telephone_type_of")
    customer_pretty_other_telephone_type_of = serializers.CharField(
        read_only=True, source="customer.get_pretty_other_telephone_type_of")
    pretty_status = serializers.CharField(read_only=True,
                                          source="get_pretty_status")
    pretty_type_of = serializers.CharField(read_only=True,
                                           source="get_pretty_type_of")

    class Meta:
        model = OngoingWorkOrder
        fields = (
            # Read only fields.
            'id',

            # Read Only fields.
            'associate_full_name',
            'associate_telephone',
            'associate_telephone_type_of',
            'associate_pretty_telephone_type_of',
            'associate_other_telephone',
            'associate_other_telephone_type_of',
            'associate_pretty_other_telephone_type_of',
            'customer_full_name',
            'customer_telephone',
            'customer_telephone_type_of',
            'customer_pretty_telephone_type_of',
            'customer_other_telephone',
            'customer_other_telephone_type_of',
            'customer_pretty_other_telephone_type_of',
            'pretty_status',
            'pretty_type_of',

            # Read / write fields.
            'associate',
            'customer',
            'state')

    def setup_eager_loading(cls, queryset):
        """ Perform necessary eager loading of data. """
        queryset = queryset.prefetch_related('associate', 'customer',
                                             'comments', 'last_modified_by',
                                             'created_by', 'work_orders')
        return queryset

    def get_associate_full_name(self, obj):
        try:
            if obj.associate:
                return str(obj.associate)
        except Exception as e:
            pass
        return None

    def get_customer_full_name(self, obj):
        try:
            if obj.customer:
                return str(obj.customer)
        except Exception as e:
            pass
        return None

    # def get_pretty_skill_sets(self, obj):
    #     try:
    #         s = SkillSetListCreateSerializer(obj.skill_sets.all(), many=True)
    #         return s.data
    #     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 validate_invoice_service_fee_payment_date(self, value):
    #     """
    #     Include validation on no-blanks if the state is set to be changed
    #     to ``completed_and_paid`` state of the work order.
    #     """
    #     state = self.context['state']
    #     if state:
    #         if state == WORK_ORDER_STATE.COMPLETED_AND_PAID:
    #             if value is None:
    #                 raise serializers.ValidationError("This field may not be blank when submitting a payment status.")
    #     return value

    # def validate_invoice_date(self, value):
    #     """
    #     Include validation on no-blanks
    #     """
    #     if value is None:
    #         raise serializers.ValidationError("This field may not be blank.")
    #     return value
    #
    # def get_pretty_tags(self, obj):
    #     try:
    #         s = TagListCreateSerializer(obj.tags.all(), many=True)
    #         return s.data
    #     except Exception as e:
    #         return None

    @transaction.atomic
    def update(self, instance, validated_data):
        """
        Override this function to include extra functionality.
        """
        # (a) Object details.
        instance.customer = validated_data.get('customer', instance.customer)
        instance.associate = validated_data.get('associate',
                                                instance.associate)
        instance.state = validated_data.get('state', instance.state)

        # (b) System details.
        instance.last_modified_from = self.context['last_modified_from']
        instance.last_modified_from_is_public = self.context[
            'last_modified_from_is_public']
        instance.last_modified_by = self.context['last_modified_by']

        instance.save()

        # Return our validated data.
        return validated_data
class PartnerContactUpdateSerializer(serializers.ModelSerializer):
    organization_name = serializers.CharField(required=True,
                                              allow_blank=False,
                                              validators=[])
    given_name = serializers.CharField(required=True,
                                       allow_blank=False,
                                       validators=[])
    last_name = 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=Partner.objects.all())],
        required=True,
    )

    # Custom formatting of our telephone fields.
    primary_phone = PhoneNumberField(allow_null=False,
                                     required=True,
                                     source="telephone")
    primary_phone_type_of = serializers.IntegerField(
        required=True, validators=[], source="telephone_type_of")
    secondary_phone = PhoneNumberField(allow_null=True,
                                       required=False,
                                       source="other_telephone")
    secondary_phone_type_of = serializers.IntegerField(
        required=False, validators=[], source="other_telephone_type_of")

    class Meta:
        model = Partner
        fields = (
            'organization_name',
            'given_name',
            'last_name',
            'primary_phone',
            'primary_phone_type_of',
            'secondary_phone',
            'secondary_phone_type_of',
            'email',
            'is_ok_to_email',
            'is_ok_to_text',
        )
        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 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)

        # Update telephone numbers.
        telephone = validated_data.get('telephone', instance.telephone)
        if telephone is not None:
            validated_data['telephone'] = phonenumbers.parse(telephone, "CA")
        other_telephone = validated_data.get('other_telephone',
                                             instance.other_telephone)
        if other_telephone is not None:
            validated_data['other_telephone'] = phonenumbers.parse(
                other_telephone, "CA")

        #---------------------------
        # Update `SharedUser` object.
        #---------------------------
        instance.owner, created = SharedUser.objects.update_or_create(
            email=email,
            defaults={
                'email': email,
                'first_name': validated_data.get('given_name',
                                                 instance.given_name),
                'last_name': validated_data.get('last_name',
                                                instance.last_name),
                'is_active': True
            })
        logger.info("Updated shared user.")

        #---------------------------
        # Update `Partner` object.
        #---------------------------
        instance.email = email
        instance.organization_name = validated_data.get(
            'organization_name', None)
        instance.organization_type_of = validated_data.get(
            'organization_type_of', UNKNOWN_ORGANIZATION_TYPE_OF_ID)
        instance.given_name = validated_data.get('given_name',
                                                 instance.given_name)
        instance.last_name = validated_data.get('last_name',
                                                instance.last_name)
        instance.middle_name = validated_data.get('middle_name',
                                                  instance.middle_name)
        instance.is_ok_to_email = validated_data.get('is_ok_to_email', None)
        instance.is_ok_to_text = validated_data.get('is_ok_to_text', 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']
        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', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)
        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', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)
        instance.save()
        logger.info("Updated the partner.")
        return instance
class TaskItemAvailableAssociateListCreateSerializer(
        serializers.ModelSerializer):
    full_name = serializers.SerializerMethodField()
    telephone = PhoneNumberField()
    e164_telephone = serializers.SerializerMethodField()
    pretty_tags = serializers.SerializerMethodField()

    # Meta Information.
    class Meta:
        model = Associate
        fields = (
            'id',
            'type_of',
            'full_name',
            'past_30_days_activity_sheet_count',
            'telephone',
            'e164_telephone',
            'email',
            'wsib_number',
            'hourly_salary_desired',
            'score',
            'tags',
            'pretty_tags',
        )
        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 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

    def get_full_name(self, obj):
        try:
            return str(obj)
        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 get_pretty_tags(self, obj):
        try:
            s = TagListCreateSerializer(obj.tags.all(), many=True)
            return s.data
        except Exception as e:
            return None

    @transaction.atomic
    def create(self, validated_data):
        """
        Override the `create` function to add extra functinality:
        """
        return validated_data
예제 #18
0
class StaffRetrieveSerializer(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)

    # Attach with our foreign keys.
    how_hear = serializers.PrimaryKeyRelatedField(
        many=False,
        required=False,
        allow_null=True,
        queryset=HowHearAboutUsItem.objects.all())

    # 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)

    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()
    pretty_tags = serializers.SerializerMethodField()
    group_id = serializers.ReadOnlyField(allow_null=True)
    group_description = serializers.ReadOnlyField(allow_null=True)
    state = serializers.IntegerField(read_only=True, source="owner.is_active")
    is_archived = serializers.BooleanField(read_only=True)
    avatar_url = serializers.SerializerMethodField()

    # Meta Information.
    class Meta:
        model = Staff
        fields = (
            # Thing
            'id',
            'created',
            'last_modified',
            # 'owner',
            'description',

            # Person
            'given_name',
            'middle_name',
            'last_name',
            'birthdate',
            'join_date',
            'gender',

            # Misc (Read/Write)
            'tags',
            # # 'is_senior',
            # # 'is_support',
            # # 'job_info_read',
            'how_hear',
            'how_hear_other',

            # Misc (Read Only)
            'is_archived',
            'avatar_url',

            # # Misc (Write Only)
            # 'extra_comment',
            'full_name',
            'address',
            'address_url',
            'full_address',
            'created_by',
            'last_modified_by',
            'e164_telephone',
            'how_hear_pretty',
            'pretty_tags',
            'group_id',
            'group_description',
            'state',

            # 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',

            # Misc
            'police_check',
        )

    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

    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_how_hear_pretty(self, obj):
        try:
            return str(obj.how_hear)
        except Exception as e:
            return None

    def get_pretty_tags(self, obj):
        try:
            s = TagListCreateSerializer(obj.tags.all(), many=True)
            return s.data
        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 get_avatar_url(self, obj):
        try:
            return obj.avatar_image.image_file.url
        except Exception as e:
            return None
예제 #19
0
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
예제 #20
0
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
예제 #21
0
class PartnerRetrieveUpdateDestroySerializer(serializers.ModelSerializer):
    # owner = serializers.PrimaryKeyRelatedField(many=False, read_only=True)

    # We are overriding the `email` field to include unique email validation.
    email = serializers.EmailField(
        validators=[UniqueValidator(queryset=Partner.objects.all())],
        required=True,
        allow_blank=False,
    )

    gender = serializers.CharField(
        required=True,
        allow_blank=False,
        allow_null=False,
    )

    # All comments are created by our `create` function and not by
    # `django-rest-framework`.
    # comments = PartnerCommentSerializer(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()
    other_telephone = PhoneNumberField(allow_null=True, required=False)

    is_active = serializers.BooleanField(
        write_only=True,
        required=True,
    )

    # Attach with our foreign keys.
    how_hear = serializers.PrimaryKeyRelatedField(
        many=False,
        required=True,
        allow_null=False,
        queryset=HowHearAboutUsItem.objects.all())

    class Meta:
        model = Partner
        fields = (
            # Thing
            'id',
            'created',
            'last_modified',
            # 'owner',
            'description',

            # Profile
            'given_name',
            'middle_name',
            'last_name',
            'birthdate',
            'join_date',

            # Misc (Read/Write)
            'is_active',
            'is_ok_to_email',
            'is_ok_to_text',
            # 'is_senior',
            # 'is_support',
            # 'job_info_read',
            'gender',
            'how_hear',

            # Misc (Read Only)
            # 'comments',
            # 'organizations', #TODO: FIX

            # 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
        )

    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 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)

        # Update telephone numbers.
        fax_number = validated_data.get('fax_number', instance.fax_number)
        if fax_number is not None:
            validated_data['fax_number'] = phonenumbers.parse(fax_number, "CA")
        telephone = validated_data.get('telephone', instance.telephone)
        if telephone is not None:
            validated_data['telephone'] = phonenumbers.parse(telephone, "CA")
        other_telephone = validated_data.get('other_telephone',
                                             instance.other_telephone)
        if other_telephone is not None:
            validated_data['other_telephone'] = phonenumbers.parse(
                other_telephone, "CA")

        #---------------------------
        # Update `SharedUser` object.
        #---------------------------
        instance.owner, created = SharedUser.objects.update_or_create(
            email=email,
            defaults={
                'email': email,
                'first_name': validated_data.get('given_name',
                                                 instance.given_name),
                'last_name': validated_data.get('last_name',
                                                instance.last_name),
                'is_active': validated_data.get('is_active', False)
            })
        logger.info("Updated shared user.")

        # Update the password.
        password = validated_data.get('password', None)
        if password:
            instance.owner.set_password(password)

            # Save the model.
            instance.owner.save()

        #---------------------------
        # Update `Partner` object.
        #---------------------------
        instance.email = email

        # Profile
        instance.given_name = validated_data.get('given_name',
                                                 instance.given_name)
        instance.last_name = validated_data.get('last_name',
                                                instance.last_name)
        instance.middle_name = validated_data.get('middle_name',
                                                  instance.middle_name)
        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)
        instance.description = validated_data.get('description',
                                                  instance.description)

        # Misc
        instance.is_ok_to_email = validated_data.get('is_ok_to_email', None)
        instance.is_ok_to_text = validated_data.get('is_ok_to_text', None)
        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.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', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)
        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', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)

        # 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.

        # Save our instance.
        instance.save()
        logger.info("Updated the partner.")

        # #---------------------------
        # # 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
        #     )
        #     partner_comment = PartnerComment.objects.create(
        #         partner=instance,
        #         comment=comment,
        # created_from = self.context['last_modified_from'],
        # created_from_is_public = self.context['last_modified_from_is_public']
        #     )

        #---------------------------
        # Update validation data.
        #---------------------------
        # validated_data['comments'] = PartnerComment.objects.filter(partner=instance)
        validated_data['last_modified_by'] = self.context['last_modified_by']
        # validated_data['extra_comment'] = None

        # Return our validated data.
        return validated_data
class AssociateContactUpdateSerializer(serializers.ModelSerializer):
    # OVERRIDE THE MODEL FIELDS AND ENFORCE THE FOLLOWING CUSTOM VALIDATION RULES.
    organization_name = serializers.CharField(
        required=False,
        validators=[],
        allow_null=True,
        allow_blank=True,
    )
    organization_type_of = serializers.IntegerField(required=False,
                                                    validators=[])
    given_name = serializers.CharField(required=True,
                                       allow_blank=False,
                                       validators=[])
    last_name = 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=Associate.objects.all())],
        required=True,
    )

    # Custom formatting of our telephone fields.
    primary_phone = PhoneNumberField(allow_null=False,
                                     required=True,
                                     source="telephone")
    primary_phone_type_of = serializers.IntegerField(
        required=True, validators=[], source="telephone_type_of")
    secondary_phone = PhoneNumberField(allow_null=True,
                                       required=False,
                                       source="other_telephone")
    secondary_phone_type_of = serializers.IntegerField(
        required=False, validators=[], source="other_telephone_type_of")

    # Meta Information.
    class Meta:
        model = Associate
        fields = (
            'organization_name',
            'organization_type_of',
            'given_name',
            'last_name',
            'primary_phone',
            'primary_phone_type_of',
            'secondary_phone',
            'secondary_phone_type_of',
            'email',
            'is_ok_to_email',
            'is_ok_to_text',
        )
        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_name(self, value):
        """
        Include validation on no-blanks
        """
        associate = self.context['associate']
        if associate.type_of == COMMERCIAL_ASSOCIATE_TYPE_OF_ID or associate.type_of == UNASSIGNED_ASSOCIATE_TYPE_OF_ID:
            if value is None:
                raise serializers.ValidationError(
                    "This field may not be blank.")
        return value

    def validate_organization_type_of(self, value):
        associate = self.context['associate']
        if associate.type_of == COMMERCIAL_ASSOCIATE_TYPE_OF_ID or associate.type_of == UNASSIGNED_ASSOCIATE_TYPE_OF_ID:
            if value is None or value == 1:
                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.
        # logger.info(validated_data)

        # Get our inputs.
        email = validated_data.get('email', instance.email)
        telephone = validated_data.get('telephone', instance.telephone)
        if telephone is not None:
            validated_data['telephone'] = phonenumbers.parse(telephone, "CA")
        other_telephone = validated_data.get('other_telephone',
                                             instance.other_telephone)
        if other_telephone is not None:
            validated_data['other_telephone'] = phonenumbers.parse(
                other_telephone, "CA")

        #---------------------------
        # Update `SharedUser` object.
        #---------------------------
        instance.owner, created = SharedUser.objects.update_or_create(
            email=email,
            defaults={
                'email': email,
                'first_name': validated_data.get('given_name',
                                                 instance.given_name),
                'last_name': validated_data.get('last_name',
                                                instance.last_name),
                'is_active': True
            })
        logger.info("Updated shared user.")

        #---------------------------
        # Update `Associate` object.
        #---------------------------
        instance.email = email

        # Profile
        instance.organization_name = validated_data.get(
            'organization_name', instance.organization_name)
        instance.organization_type_of = validated_data.get(
            'organization_type_of', instance.organization_type_of)
        instance.given_name = validated_data.get('given_name',
                                                 instance.given_name)
        instance.last_name = validated_data.get('last_name',
                                                instance.last_name)
        instance.middle_name = validated_data.get('middle_name',
                                                  instance.middle_name)
        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)
        instance.description = validated_data.get('description',
                                                  instance.description)
        instance.tax_id = validated_data.get('tax_id', instance.tax_id)

        # Misc
        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.last_modified_from = self.context['last_modified_from']
        instance.last_modified_from_is_public = self.context[
            'last_modified_from_is_public']
        instance.last_modified_by = self.context['last_modified_by']
        instance.telephone = validated_data.get('telephone',
                                                instance.telephone)
        instance.telephone_extension = validated_data.get(
            'telephone_extension', instance.telephone_extension)
        instance.telephone_type_of = validated_data.get(
            'telephone_type_of', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)
        instance.other_telephone = validated_data.get('other_telephone',
                                                      instance.other_telephone)
        instance.other_telephone_extension = validated_data.get(
            'other_telephone_extension', instance.other_telephone_extension)
        instance.other_telephone_type_of = validated_data.get(
            'other_telephone_type_of', TELEPHONE_CONTACT_POINT_TYPE_OF_ID)

        # Save our instance.
        instance.save()
        logger.info("Updated the associate.")

        # Return our validated data.
        return instance