def __call__(self, data=None, order=None): """ Check that: - vat_status is specified - if vat_status == eu: - vat_verified is specified - if vat_verified == True: - vat_number is specified """ data_combiner = DataCombiner(order, data) vat_status = data_combiner.get_value('vat_status') if not vat_status: raise ValidationError({ 'vat_status': [self.message], }) if vat_status == VATStatus.eu: vat_verified = data_combiner.get_value('vat_verified') if vat_verified is None: raise ValidationError({ 'vat_verified': [self.message], }) vat_number = data_combiner.get_value('vat_number') if vat_verified and not vat_number: raise ValidationError({ 'vat_number': [self.message], })
def _notify_consent_service(self, validated_data): """ Trigger the update_contact_consent task with the current version of `validated_data`. The actual enqueuing of the task happens in the on_commit hook so it won't actually notify the consent service unless the database transaction was successful. """ if 'accepts_dit_email_marketing' not in validated_data: # If no consent value in request body return # Remove the accepts_dit_email_marketing from validated_data accepts_dit_email_marketing = validated_data.pop( 'accepts_dit_email_marketing') # If consent value in POST, notify combiner = DataCombiner(self.instance, validated_data) request = self.context.get('request', None) transaction.on_commit( lambda: update_contact_consent.apply_async( args=( combiner.get_value('email'), accepts_dit_email_marketing, ), kwargs={ 'modified_at': now().isoformat(), 'zipkin_headers': get_zipkin_headers(request), }, ), )
def __call__(self, data, serializer): """Performs validation.""" data_combiner = DataCombiner(serializer.instance, data) service = data_combiner.get_value('service') service_answers = data_combiner.get_value('service_answers') expected_questions = { str(question.pk): question for question in service.interaction_questions.all() } if service else {} self._validate_type_and_truthiness(expected_questions, service_answers) if service_answers is not None: self._validate_questions(expected_questions, service_answers)
def __call__(self, data, serializer): """ Performs validation. TODO: this method has do be simplified once `company` field is removed. """ instance = serializer.instance company_has_changed = not instance or ( 'company' in data and data['company'] != instance.company) companies_have_changed = not instance or ('companies' in data and set( data['companies']) != set(instance.companies.all())) contacts_have_changed = not instance or ('contacts' in data and set( data['contacts']) != set(instance.contacts.all())) if not (company_has_changed or companies_have_changed or contacts_have_changed): return combiner = DataCombiner(instance, data) company = combiner.get_value('company') if company_has_changed and company: companies = (company, ) else: companies = combiner.get_value_to_many('companies') contacts = combiner.get_value_to_many('contacts') if any(contact.company not in companies for contact in contacts): raise serializers.ValidationError( 'The interaction contacts must belong to the specified company.', code='inconsistent_contacts_and_company', )
def validate(instance=None, update_data=None, fields=None, next_stage=False): """Validates an investment project for the current stage. :param instance: Model instance (for update operations only) :param update_data: New data to update or create the instance with :param fields: Fields to restrict validation to :param next_stage: Perform validation for the next stage (rather than the current stage) :return: dict containing errors for incomplete fields """ combiner = DataCombiner(instance, update_data, model=InvestmentProject) desired_stage = combiner.get_value('stage') or Stage.prospect.value desired_stage_order = _get_desired_stage_order(desired_stage, next_stage) errors = {} for field, req_stage in (InvestmentProjectStageValidationConfig. get_required_fields_after_stage().items()): if _should_skip_rule(field, fields, desired_stage_order, req_stage.order): continue if field_incomplete(combiner, field): errors[field] = REQUIRED_MESSAGE for field, rule in (InvestmentProjectStageValidationConfig. get_conditional_rules_after_stage().items()): if _should_skip_rule(field, fields, desired_stage_order, rule.stage.order): continue if _check_rule(combiner, rule) and field_incomplete(combiner, field): errors[field] = REQUIRED_MESSAGE return errors
def __call__(self, data=None, order=None): """Validate that all the fields required are set.""" data_combiner = DataCombiner(order, data) meta = order._meta errors = defaultdict(list) # direct required fields for field_name in self.REQUIRED_FIELDS: field = meta.get_field(field_name) if isinstance(field, models.ManyToManyField): value = data_combiner.get_value_to_many(field_name) else: value = data_combiner.get_value(field_name) if not value: errors[field_name] = [self.message] # extra validators extra_errors = self._run_extra_validators(data, order) for field, field_errors in extra_errors.items(): errors[field] += field_errors if errors: raise ValidationError(errors)
def _reset_vat_fields_if_necessary(self, data): """If vat_status is set and != 'eu', vat_number and vat_verified are reset.""" data_combiner = DataCombiner(self.instance, data) vat_status = data_combiner.get_value('vat_status') if vat_status and vat_status != VATStatus.eu: data['vat_number'] = '' data['vat_verified'] = None return data
def _validate_theme(self, data): """Make sure that a theme is not unset once it has been set for an interaction.""" combiner = DataCombiner(self.instance, data) if self.instance and self.instance.theme and not combiner.get_value('theme'): error = { 'theme': [ self.error_messages['cannot_unset_theme'], ], } raise serializers.ValidationError(error, code='cannot_unset_theme')
def __call__(self, data): """Validate that contact works at company.""" data_combiner = DataCombiner(self.instance, data) company = data_combiner.get_value(self.company_field) contact = data_combiner.get_value(self.contact_field) if contact.company != company: raise ValidationError({ self.contact_field: self.message, })
def __call__(self, data, serializer): """Validate that contact works at company.""" instance = getattr(serializer, 'instance', None) data_combiner = DataCombiner(instance, data) company = data_combiner.get_value(self.company_field) contact = data_combiner.get_value(self.contact_field) if contact.company != company: raise ValidationError({ self.contact_field: self.message, })
def validate(self, data): """ Performs cross-field validation after individual fields have been validated. Ensures that either a person or company name has been provided, as well as an email address or phone number. """ errors = {} data_combiner = DataCombiner(self.instance, data) company_name = data_combiner.get_value('company_name') trading_name = data_combiner.get_value('trading_name') company = data_combiner.get_value('company') first_name = data_combiner.get_value('first_name') last_name = data_combiner.get_value('last_name') telephone_number = data_combiner.get_value('telephone_number') email = data_combiner.get_value('email') has_company_name = any((company_name, company, trading_name)) has_contact_name = first_name and last_name if not (has_company_name or has_contact_name): errors['company_name'] = NAME_REQUIRED_MESSAGE errors['first_name'] = NAME_REQUIRED_MESSAGE errors['last_name'] = NAME_REQUIRED_MESSAGE if not (email or telephone_number): errors['telephone_number'] = CONTACT_REQUIRED_MESSAGE errors['email'] = CONTACT_REQUIRED_MESSAGE if errors: raise serializers.ValidationError(errors) return data
def __call__(self, data): """ Performs validation. """ if not self.instance: return existing_interaction_complete = self.instance.status == Interaction.STATUSES.complete combiner = DataCombiner(self.instance, data) new_status = combiner.get_value('status') update_changes_status = new_status != self.instance.status if existing_interaction_complete and update_changes_status: raise ValidationError( 'The status of a complete interaction cannot change.', code='complete_interaction_status_cannot_change', )
def __call__(self, data): """Validate the address fields.""" data_combiner = DataCombiner(self.instance, data) data_combined = { field_name: data_combiner.get_value(field_name) for field_name in self.fields_mapping.keys() } if not self._should_validate(data_combined): return errors = self._validate_fields(data_combined) if errors: raise ValidationError(errors)
def __call__(self, data, serializer): """ Performs validation. """ instance = serializer.instance if not instance: return existing_interaction_complete = instance.status == Interaction.Status.COMPLETE combiner = DataCombiner(instance, data) new_status = combiner.get_value('status') update_changes_status = new_status != instance.status if existing_interaction_complete and update_changes_status: raise serializers.ValidationError( 'The status of a complete interaction cannot change.', code='complete_interaction_status_cannot_change', )
def _update_status(self, data): """Updates the project status when the stage changes to or from Won.""" old_stage = self.instance.stage if self.instance else None new_stage = data.get('stage') if not new_stage or new_stage == old_stage: return combiner = DataCombiner(instance=self.instance, update_data=data) new_status = combiner.get_value('status') if str(new_stage.id) == InvestmentProjectStage.won.value.id: data['status'] = InvestmentProject.STATUSES.won elif (old_stage and str(old_stage.id) == InvestmentProjectStage.won.value.id and new_status == InvestmentProject.STATUSES.won): data['status'] = InvestmentProject.STATUSES.ongoing
def _get_updated_status(instance, data): """Updates the project status when the stage changes to or from Won.""" old_stage = instance.stage if instance else None new_stage = data.get('stage') if not new_stage or new_stage == old_stage: return None combiner = DataCombiner(instance=instance, update_data=data) new_status = combiner.get_value('status') if str(new_stage.id) == InvestmentProjectStage.won.value.id: return InvestmentProject.Status.WON elif ( old_stage and str(old_stage.id) == InvestmentProjectStage.won.value.id and new_status == InvestmentProject.Status.WON ): return InvestmentProject.Status.ONGOING return None
def validate(self, attrs): """ Validates the data if necessary. This is needed because some addresses only need to be validated if they are passed in. """ validated_data = super().validate(attrs) data_combiner = DataCombiner(self.parent.instance, validated_data) if self.should_validate(data_combiner): errors = {} for field_name in self.REQUIRED_FIELDS: field = self.fields[field_name] value = data_combiner.get_value(field.source) if not value: errors[field_name] = self.error_messages['required'] if errors: raise ValidationError(errors) return validated_data
def __call__(self, data, serializer): """Performs validation.""" instance = serializer.instance company_has_changed = not instance or ( 'company' in data and data['company'] != instance.company) contacts_have_changed = not instance or ('contacts' in data and set( data['contacts']) != set(instance.contacts.all())) if not (company_has_changed or contacts_have_changed): return combiner = DataCombiner(instance, data) company = combiner.get_value('company') contacts = combiner.get_value_to_many('contacts') if any(contact.company != company for contact in contacts): raise serializers.ValidationError( 'The interaction contacts must belong to the specified company.', code='inconsistent_contacts_and_company', )
def test_get_value_data(self): """Tests getting a simple value from update data.""" instance = Mock(field1=1, field2=2) data = {'field2': 456} data_combiner = DataCombiner(instance, data) assert data_combiner.get_value('field2') == 456