public void validateForUpdate(final String json) {

    if (StringUtils.isBlank(json)) {
      throw new InvalidJsonException();
    }

    final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
    this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.supportedParameters);
    final JsonElement element = this.fromApiJsonHelper.parse(json);

    final List<ApiParameterError> dataValidationErrors = new ArrayList<>();

    final DataValidatorBuilder baseDataValidator =
        new DataValidatorBuilder(dataValidationErrors).resource("calendar");

    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.TITLE.getValue(), element)) {
      final String title =
          this.fromApiJsonHelper.extractStringNamed(
              CALENDAR_SUPPORTED_PARAMETERS.TITLE.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.TITLE.getValue())
          .value(title)
          .notBlank()
          .notExceedingLengthOf(50);
    }

    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.DESCRIPTION.getValue(), element)) {
      final String description =
          this.fromApiJsonHelper.extractStringNamed(
              CALENDAR_SUPPORTED_PARAMETERS.DESCRIPTION.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.DESCRIPTION.getValue())
          .value(description)
          .ignoreIfNull()
          .notExceedingLengthOf(100);
    }

    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.LOCATION.getValue(), element)) {
      final String location =
          this.fromApiJsonHelper.extractStringNamed(
              CALENDAR_SUPPORTED_PARAMETERS.LOCATION.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.LOCATION.getValue())
          .value(location)
          .ignoreIfNull()
          .notExceedingLengthOf(50);
    }

    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue(), element)) {
      final String startDateStr =
          this.fromApiJsonHelper.extractStringNamed(
              CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue())
          .value(startDateStr)
          .notNull();

      final LocalDate startDate =
          this.fromApiJsonHelper.extractLocalDateNamed(
              CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.START_DATE.getValue())
          .value(startDate)
          .notNull();
    }
    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.END_DATE.getValue(), element)) {
      final LocalDate endDate =
          this.fromApiJsonHelper.extractLocalDateNamed(
              CALENDAR_SUPPORTED_PARAMETERS.END_DATE.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.END_DATE.getValue())
          .value(endDate)
          .notNull();
    }

    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.DURATION.getValue(), element)) {
      final Integer duration =
          this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
              CALENDAR_SUPPORTED_PARAMETERS.DURATION.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.DURATION.getValue())
          .value(duration)
          .ignoreIfNull();
    }
    // TODO: AA do not allow to change calendar type.
    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.TYPE_ID.getValue(), element)) {
      final Integer typeId =
          this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
              CALENDAR_SUPPORTED_PARAMETERS.TYPE_ID.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.TYPE_ID.getValue())
          .value(typeId)
          .notNull()
          .inMinMaxRange(CalendarEntityType.getMinValue(), CalendarEntityType.getMaxValue());
    }
    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.REPEATING.getValue(), element)) {
      // FIXME - Throws NullPointerException when boolean value is null
      final boolean repeating =
          this.fromApiJsonHelper.extractBooleanNamed(
              CALENDAR_SUPPORTED_PARAMETERS.REPEATING.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.REPEATING.getValue())
          .value(repeating)
          .notNull();

      if (repeating) {
        final Integer frequency =
            this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
                CALENDAR_SUPPORTED_PARAMETERS.FREQUENCY.getValue(), element);
        baseDataValidator
            .reset()
            .parameter(CALENDAR_SUPPORTED_PARAMETERS.FREQUENCY.getValue())
            .value(frequency)
            .notBlank()
            .inMinMaxRange(
                CalendarFrequencyType.getMinValue(), CalendarFrequencyType.getMaxValue());

        if (this.fromApiJsonHelper.parameterExists(
            CALENDAR_SUPPORTED_PARAMETERS.INTERVAL.getValue(), element)) {
          final Integer interval =
              this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
                  CALENDAR_SUPPORTED_PARAMETERS.INTERVAL.getValue(), element);
          baseDataValidator
              .reset()
              .parameter(CALENDAR_SUPPORTED_PARAMETERS.INTERVAL.getValue())
              .value(interval)
              .notNull()
              .integerGreaterThanZero();
        }

        if (this.fromApiJsonHelper.parameterExists(
            CALENDAR_SUPPORTED_PARAMETERS.REPEATS_ON_DAY.getValue(), element)) {
          final Integer repeatsOnDay =
              this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
                  CALENDAR_SUPPORTED_PARAMETERS.REPEATS_ON_DAY.getValue(), element);
          baseDataValidator
              .reset()
              .parameter(CALENDAR_SUPPORTED_PARAMETERS.REPEATS_ON_DAY.getValue())
              .value(repeatsOnDay)
              .notBlank()
              .inMinMaxRange(
                  CalendarWeekDaysType.getMinValue(), CalendarWeekDaysType.getMaxValue());
        }
      }
    }

    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.REMIND_BY_ID.getValue(), element)) {
      final Integer remindById =
          this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
              CALENDAR_SUPPORTED_PARAMETERS.REMIND_BY_ID.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.REMIND_BY_ID.getValue())
          .value(remindById)
          .ignoreIfNull()
          .inMinMaxRange(CalendarRemindBy.getMinValue(), CalendarRemindBy.getMaxValue());
    }

    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.FIRST_REMINDER.getValue(), element)) {
      final Integer firstReminder =
          this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
              CALENDAR_SUPPORTED_PARAMETERS.FIRST_REMINDER.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.FIRST_REMINDER.getValue())
          .value(firstReminder)
          .ignoreIfNull();
    }

    if (this.fromApiJsonHelper.parameterExists(
        CALENDAR_SUPPORTED_PARAMETERS.SECOND_REMINDER.getValue(), element)) {
      final Integer secondReminder =
          this.fromApiJsonHelper.extractIntegerSansLocaleNamed(
              CALENDAR_SUPPORTED_PARAMETERS.SECOND_REMINDER.getValue(), element);
      baseDataValidator
          .reset()
          .parameter(CALENDAR_SUPPORTED_PARAMETERS.SECOND_REMINDER.getValue())
          .value(secondReminder)
          .ignoreIfNull();
    }

    if (!dataValidationErrors.isEmpty()) {
      throw new PlatformApiDataValidationException(
          "validation.msg.validation.errors.exist",
          "Validation errors exist.",
          dataValidationErrors);
    }
  }
  @Override
  public JLGCollectionSheetData generateGroupCollectionSheet(
      final Long groupId, final JsonQuery query) {

    this.collectionSheetGenerateCommandFromApiJsonDeserializer.validateForGenerateCollectionSheet(
        query.json());

    final Long calendarId = query.longValueOfParameterNamed(calendarIdParamName);
    final LocalDate transactionDate =
        query.localDateValueOfParameterNamed(transactionDateParamName);
    final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
    final String transactionDateStr = df.format(transactionDate.toDate());

    final Calendar calendar =
        this.calendarRepositoryWrapper.findOneWithNotFoundDetection(calendarId);
    // check if transaction against calendar effective from date

    if (!calendar.isValidRecurringDate(transactionDate)) {
      throw new NotValidRecurringDateException(
          "collectionsheet",
          "The date '" + transactionDate + "' is not a valid meeting date.",
          transactionDate);
    }

    final AppUser currentUser = this.context.authenticatedUser();
    final String hierarchy = currentUser.getOffice().getHierarchy();
    final String officeHierarchy = hierarchy + "%";

    final GroupGeneralData group = this.groupReadPlatformService.retrieveOne(groupId);

    final JLGCollectionSheetFaltDataMapper mapper = new JLGCollectionSheetFaltDataMapper();

    // entityType should be center if it's within a center
    final CalendarEntityType entityType =
        (group.isChildGroup()) ? CalendarEntityType.CENTERS : CalendarEntityType.GROUPS;

    final SqlParameterSource namedParameters =
        new MapSqlParameterSource()
            .addValue("dueDate", transactionDateStr)
            .addValue("groupId", group.getId())
            .addValue("officeHierarchy", officeHierarchy)
            .addValue("entityTypeId", entityType.getValue());

    final Collection<JLGCollectionSheetFlatData> collectionSheetFlatDatas =
        this.namedParameterjdbcTemplate.query(
            mapper.collectionSheetSchema(false), namedParameters, mapper);

    // loan data for collection sheet
    JLGCollectionSheetData collectionSheetData =
        buildJLGCollectionSheet(transactionDate, collectionSheetFlatDatas);

    // mandatory savings data for collection sheet
    Collection<JLGGroupData> groupsWithSavingsData =
        this.namedParameterjdbcTemplate.query(
            mandatorySavingsExtractor.collectionSheetSchema(false),
            namedParameters,
            mandatorySavingsExtractor);

    // merge savings data into loan data
    mergeSavingsGroupDataIntoCollectionsheetData(groupsWithSavingsData, collectionSheetData);

    collectionSheetData =
        JLGCollectionSheetData.withSavingsProducts(
            collectionSheetData, retrieveSavingsProducts(groupsWithSavingsData));

    return collectionSheetData;
  }