FINERACT-1932: Fineract modularization - fineract-accounting

This commit is contained in:
Jose Alberto Hernandez
2024-02-01 23:51:50 +01:00
committed by Adam Saghy
parent 129748e30d
commit f3710cda92
158 changed files with 266 additions and 5 deletions
@@ -1,66 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.accrual.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.springframework.stereotype.Component;
@Path("/v1/runaccruals")
@Component
@Tag(name = "Periodic Accrual Accounting", description = "Periodic Accrual is to accrue the loan income till the specific date or till batch job scheduled time.\n")
@RequiredArgsConstructor
public class AccrualAccountingApiResource {
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final DefaultToApiJsonSerializer<String> apiJsonSerializerService;
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Executes Periodic Accrual Accounting", method = "POST", description = "Mandatory Fields\n" + "\n" + "tillDate\n")
@RequestBody(required = true, content = @Content(schema = @Schema(implementation = AccrualAccountingApiResourceSwagger.PostRunaccrualsRequest.class)))
@ApiResponses({ @ApiResponse(responseCode = "200", description = "OK") })
public String executePeriodicAccrualAccounting(@Parameter(hidden = true) final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().excuteAccrualAccounting().withJson(jsonRequestBody).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
}
@@ -1,47 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.accrual.api;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* Created by sanyam on 24/7/17.
*/
final class AccrualAccountingApiResourceSwagger {
private AccrualAccountingApiResourceSwagger() {
// don't allow to instantiate; use only for live API documentation
}
@Schema(description = "runaccrualsRequest")
public static final class PostRunaccrualsRequest {
private PostRunaccrualsRequest() {
// don't allow to instantiate; use only for live API documentation
}
@Schema(example = "en")
public String locale;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
@Schema(example = "04 June 2014", description = "which specifies periodic accruals should happen till the given Date", required = true)
public String tillDate;
}
}
@@ -1,33 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.accrual.api;
public final class AccrualAccountingConstants {
private AccrualAccountingConstants() {
}
public static final String accrueTillParamName = "tillDate";
public static final String localeParamName = "locale";
public static final String dateFormatParamName = "dateFormat";
public static final String PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME = "periodicaccrual";
public static final String PERIODIC_ACCRUAL_ACCOUNTING_EXECUTION_ERROR_CODE = "execution.failed";
}
@@ -1,42 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.accrual.handler;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.accrual.service.AccrualAccountingWritePlatformService;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "PERIODICACCRUALACCOUNTING", action = "EXECUTE")
@RequiredArgsConstructor
public class ExecutePeriodicAccrualCommandHandler implements NewCommandSourceHandler {
private final AccrualAccountingWritePlatformService writePlatformService;
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.executeLoansPeriodicAccrual(command);
}
}
@@ -1,83 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.accrual.serialization;
import static org.apache.fineract.accounting.accrual.api.AccrualAccountingConstants.PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME;
import static org.apache.fineract.accounting.accrual.api.AccrualAccountingConstants.accrueTillParamName;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.accrual.api.AccrualAccountingConstants;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.loanaccount.guarantor.command.GuarantorCommand;
import org.springframework.stereotype.Component;
/**
* Implementation of {@link FromApiJsonDeserializer} for {@link GuarantorCommand}'s.
*/
@Component
@RequiredArgsConstructor
public final class AccrualAccountingDataValidator {
private static final Set<String> LOAN_PERIODIC_REQUEST_DATA_PARAMETERS = new HashSet<>(
Arrays.asList(accrueTillParamName, AccrualAccountingConstants.localeParamName, AccrualAccountingConstants.dateFormatParamName));
private final FromJsonHelper fromApiJsonHelper;
public void validateLoanPeriodicAccrualData(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, LOAN_PERIODIC_REQUEST_DATA_PARAMETERS);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(PERIODIC_ACCRUAL_ACCOUNTING_RESOURCE_NAME);
final LocalDate date = this.fromApiJsonHelper.extractLocalDateNamed(accrueTillParamName, element);
baseDataValidator.reset().parameter(accrueTillParamName).value(date).notNull().validateDateBefore(DateUtils.getBusinessLocalDate());
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
public void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -1,28 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.accrual.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface AccrualAccountingWritePlatformService {
CommandProcessingResult executeLoansPeriodicAccrual(JsonCommand command);
}
@@ -1,61 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.api;
import java.util.HashSet;
import java.util.Set;
/***
* Enum of all parameters passed in while creating/updating a journal Entry
***/
public enum JournalEntryJsonInputParams {
OFFICE_ID("officeId"), TRANSACTION_DATE("transactionDate"), COMMENTS("comments"), CREDITS("credits"), DEBITS("debits"), LOCALE(
"locale"), DATE_FORMAT("dateFormat"), REFERENCE_NUMBER("referenceNumber"), USE_ACCOUNTING_RULE(
"useAccountingRule"), ACCOUNTING_RULE("accountingRule"), AMOUNT("amount"), CURRENCY_CODE(
"currencyCode"), PAYMENT_TYPE_ID("paymentTypeId"), ACCOUNT_NUMBER("accountNumber"), CHECK_NUMBER(
"checkNumber"), ROUTING_CODE("routingCode"), RECEIPT_NUMBER("receiptNumber"), BANK_NUMBER("bankNumber");
private final String value;
JournalEntryJsonInputParams(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<>();
static {
for (final JournalEntryJsonInputParams type : JournalEntryJsonInputParams.values()) {
values.add(type.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().replaceAll("_", " ");
}
public String getValue() {
return this.value;
}
}
@@ -1,121 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.command;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.journalentry.api.JournalEntryJsonInputParams;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
/**
* Immutable command for adding an accounting closure
*/
@RequiredArgsConstructor
@Getter
public class JournalEntryCommand {
private final Long officeId;
private final String currencyCode;
private final LocalDate transactionDate;
private final String comments;
private final String referenceNumber;
private final Long accountingRuleId;
private final BigDecimal amount;
private final Long paymentTypeId;
private final String accountNumber;
private final String checkNumber;
private final String receiptNumber;
private final String bankNumber;
private final String routingCode;
private final SingleDebitOrCreditEntryCommand[] credits;
private final SingleDebitOrCreditEntryCommand[] debits;
public void validateForCreate() {
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLJournalEntry");
baseDataValidator.reset().parameter("transactionDate").value(this.transactionDate).notBlank();
baseDataValidator.reset().parameter("officeId").value(this.officeId).notNull().integerGreaterThanZero();
baseDataValidator.reset().parameter(JournalEntryJsonInputParams.CURRENCY_CODE.getValue()).value(this.currencyCode).notBlank();
baseDataValidator.reset().parameter("comments").value(this.comments).ignoreIfNull().notExceedingLengthOf(500);
baseDataValidator.reset().parameter("referenceNumber").value(this.referenceNumber).ignoreIfNull().notExceedingLengthOf(100);
baseDataValidator.reset().parameter("accountingRule").value(this.accountingRuleId).ignoreIfNull().longGreaterThanZero();
baseDataValidator.reset().parameter("paymentTypeId").value(this.paymentTypeId).ignoreIfNull().longGreaterThanZero();
// validation for credit array elements
if (this.credits != null) {
if (this.credits.length == 0) {
validateSingleDebitOrCredit(baseDataValidator, "credits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
} else {
int i = 0;
for (final SingleDebitOrCreditEntryCommand credit : this.credits) {
validateSingleDebitOrCredit(baseDataValidator, "credits", i, credit);
i++;
}
}
}
// validation for debit array elements
if (this.debits != null) {
if (this.debits.length == 0) {
validateSingleDebitOrCredit(baseDataValidator, "debits", 0, new SingleDebitOrCreditEntryCommand(null, null, null, null));
} else {
int i = 0;
for (final SingleDebitOrCreditEntryCommand debit : this.debits) {
validateSingleDebitOrCredit(baseDataValidator, "debits", i, debit);
i++;
}
}
}
baseDataValidator.reset().parameter("amount").value(this.amount).ignoreIfNull().zeroOrPositiveAmount();
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
}
/**
* @param baseDataValidator
* @param i
* @param credit
*/
private void validateSingleDebitOrCredit(final DataValidatorBuilder baseDataValidator, final String paramSuffix, final int arrayPos,
final SingleDebitOrCreditEntryCommand credit) {
baseDataValidator.reset().parameter(paramSuffix + "[" + arrayPos + "].glAccountId").value(credit.getGlAccountId()).notNull()
.integerGreaterThanZero();
baseDataValidator.reset().parameter(paramSuffix + "[" + arrayPos + "].amount").value(credit.getAmount()).notNull()
.zeroOrPositiveAmount();
}
}
@@ -1,50 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.command;
import java.math.BigDecimal;
import java.util.Set;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
/**
* Immutable command for adding a "Credit" entry to the Journal
*/
@Getter
@RequiredArgsConstructor
public class SingleDebitOrCreditEntryCommand {
private final Long glAccountId;
private final BigDecimal amount;
private final String comments;
private final Set<String> parametersPassedInRequest;
public boolean isGlAccountIdChanged() {
return this.parametersPassedInRequest.contains("glAccountId");
}
public boolean isGlAmountChanged() {
return this.parametersPassedInRequest.contains("amount");
}
public boolean isCommentsChanged() {
return this.parametersPassedInRequest.contains("comments");
}
}
@@ -1,69 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.data;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.fineract.accounting.journalentry.api.JournalEntryJsonInputParams;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class JournalEntryDataValidator {
private final FromJsonHelper fromApiJsonHelper;
private static final Set<String> RUNNING_BALANCE_UPDATE_REQUEST_DATA_PARAMETERS = new HashSet<>(
Collections.singletonList(JournalEntryJsonInputParams.OFFICE_ID.getValue()));
@Autowired
public JournalEntryDataValidator(final FromJsonHelper fromApiJsonHelper) {
this.fromApiJsonHelper = fromApiJsonHelper;
}
public void validateForUpdateRunningBalance(final JsonCommand command) {
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, command.json(), RUNNING_BALANCE_UPDATE_REQUEST_DATA_PARAMETERS);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("GLJournalEntry");
if (this.fromApiJsonHelper.parameterExists(JournalEntryJsonInputParams.OFFICE_ID.getValue(), command.parsedJson())) {
final String officeId = this.fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.OFFICE_ID.getValue(),
command.parsedJson());
baseDataValidator.reset().parameter(JournalEntryJsonInputParams.OFFICE_ID.getValue()).value(officeId).ignoreIfNull()
.longGreaterThanZero();
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
dataValidationErrors);
}
}
}
@@ -1,61 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.data;
/**
* Represents the successful result of an REST API call.
*/
public class JournalEntryIdentifier {
private String entityId;
// TODO - Rename variable to commandId or taskId or something that shows
// this is the id of a command in a table/queue for processing.
@SuppressWarnings("unused")
private Long makerCheckerId;
public static JournalEntryIdentifier makerChecker(final Long makerCheckerId) {
return new JournalEntryIdentifier(null, makerCheckerId);
}
public static JournalEntryIdentifier makerChecker(final String resourceId, final Long makerCheckerId) {
return new JournalEntryIdentifier(resourceId, makerCheckerId);
}
public JournalEntryIdentifier() {
//
}
public JournalEntryIdentifier(final String entityId) {
this.entityId = entityId;
}
private JournalEntryIdentifier(final String entityId, final Long makerCheckerId) {
this.entityId = entityId;
this.makerCheckerId = makerCheckerId;
}
public String getEntityId() {
return this.entityId;
}
public void setEntityId(final String entityId) {
this.entityId = entityId;
}
}
@@ -1,48 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.data;
import java.time.LocalDate;
import java.util.List;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
@RequiredArgsConstructor
@Getter
public final class OfficeOpeningBalancesData {
private final Long officeId;
private final String officeName;
private final LocalDate transactionDate;
private final GLAccountData contraAccount;
private final List<JournalEntryData> assetAccountOpeningBalances;
private final List<JournalEntryData> liabityAccountOpeningBalances;
private final List<JournalEntryData> incomeAccountOpeningBalances;
private final List<JournalEntryData> equityAccountOpeningBalances;
private final List<JournalEntryData> expenseAccountOpeningBalances;
public static OfficeOpeningBalancesData createNew(final Long officeId, final String officeName, final LocalDate transactionDate,
final GLAccountData contraAccount, final List<JournalEntryData> assetAccountOpeningBalances,
final List<JournalEntryData> liabityAccountOpeningBalances, final List<JournalEntryData> incomeAccountOpeningBalances,
final List<JournalEntryData> equityAccountOpeningBalances, final List<JournalEntryData> expenseAccountOpeningBalances) {
return new OfficeOpeningBalancesData(officeId, officeName, transactionDate, contraAccount, assetAccountOpeningBalances,
liabityAccountOpeningBalances, incomeAccountOpeningBalances, equityAccountOpeningBalances, expenseAccountOpeningBalances);
}
}
@@ -1,126 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.serialization;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.journalentry.api.JournalEntryJsonInputParams;
import org.apache.fineract.accounting.journalentry.command.JournalEntryCommand;
import org.apache.fineract.accounting.journalentry.command.SingleDebitOrCreditEntryCommand;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.serialization.AbstractFromApiJsonDeserializer;
import org.apache.fineract.infrastructure.core.serialization.FromApiJsonDeserializer;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.stereotype.Component;
/**
* Implementation of {@link FromApiJsonDeserializer} for {@link JournalEntryCommand}'s.
*/
@Component
@RequiredArgsConstructor
public final class JournalEntryCommandFromApiJsonDeserializer extends AbstractFromApiJsonDeserializer<JournalEntryCommand> {
private final FromJsonHelper fromApiJsonHelper;
@Override
public JournalEntryCommand commandFromApiJson(final String json) {
if (StringUtils.isBlank(json)) {
throw new InvalidJsonException();
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
final Set<String> supportedParameters = JournalEntryJsonInputParams.getAllValues();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Long officeId = this.fromApiJsonHelper.extractLongNamed(JournalEntryJsonInputParams.OFFICE_ID.getValue(), element);
final String currencyCode = this.fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.CURRENCY_CODE.getValue(),
element);
final String comments = this.fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.COMMENTS.getValue(), element);
final LocalDate transactionDate = this.fromApiJsonHelper
.extractLocalDateNamed(JournalEntryJsonInputParams.TRANSACTION_DATE.getValue(), element);
final String referenceNumber = this.fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.REFERENCE_NUMBER.getValue(),
element);
final Long accountingRuleId = this.fromApiJsonHelper.extractLongNamed(JournalEntryJsonInputParams.ACCOUNTING_RULE.getValue(),
element);
final JsonObject topLevelJsonElement = element.getAsJsonObject();
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(topLevelJsonElement);
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed(JournalEntryJsonInputParams.AMOUNT.getValue(), element,
locale);
final Long paymentTypeId = this.fromApiJsonHelper.extractLongNamed(JournalEntryJsonInputParams.PAYMENT_TYPE_ID.getValue(), element);
final String accountNumber = this.fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.ACCOUNT_NUMBER.getValue(),
element);
final String checkNumber = this.fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.CHECK_NUMBER.getValue(), element);
final String receiptNumber = this.fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.RECEIPT_NUMBER.getValue(),
element);
final String bankNumber = this.fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.BANK_NUMBER.getValue(), element);
final String routingCode = this.fromApiJsonHelper.extractStringNamed(JournalEntryJsonInputParams.ROUTING_CODE.getValue(), element);
SingleDebitOrCreditEntryCommand[] credits = null;
SingleDebitOrCreditEntryCommand[] debits = null;
if (element.isJsonObject()) {
if (topLevelJsonElement.has(JournalEntryJsonInputParams.CREDITS.getValue())
&& topLevelJsonElement.get(JournalEntryJsonInputParams.CREDITS.getValue()).isJsonArray()) {
credits = populateCreditsOrDebitsArray(topLevelJsonElement, locale, JournalEntryJsonInputParams.CREDITS.getValue());
}
if (topLevelJsonElement.has(JournalEntryJsonInputParams.DEBITS.getValue())
&& topLevelJsonElement.get(JournalEntryJsonInputParams.DEBITS.getValue()).isJsonArray()) {
debits = populateCreditsOrDebitsArray(topLevelJsonElement, locale, JournalEntryJsonInputParams.DEBITS.getValue());
}
}
return new JournalEntryCommand(officeId, currencyCode, transactionDate, comments, referenceNumber, accountingRuleId, amount,
paymentTypeId, accountNumber, checkNumber, receiptNumber, bankNumber, routingCode, credits, debits);
}
/**
* @param topLevelJsonElement
* @param locale
* @param paramName
*/
private SingleDebitOrCreditEntryCommand[] populateCreditsOrDebitsArray(final JsonObject topLevelJsonElement, final Locale locale,
final String paramName) {
final JsonArray array = topLevelJsonElement.get(paramName).getAsJsonArray();
SingleDebitOrCreditEntryCommand[] debitOrCredits = new SingleDebitOrCreditEntryCommand[array.size()];
for (int i = 0; i < array.size(); i++) {
final JsonObject creditElement = array.get(i).getAsJsonObject();
final Set<String> parametersPassedInForCreditsCommand = new HashSet<>();
final Long glAccountId = this.fromApiJsonHelper.extractLongNamed("glAccountId", creditElement);
final String comments = this.fromApiJsonHelper.extractStringNamed("comments", creditElement);
final BigDecimal amount = this.fromApiJsonHelper.extractBigDecimalNamed("amount", creditElement, locale);
debitOrCredits[i] = new SingleDebitOrCreditEntryCommand(glAccountId, amount, comments, parametersPassedInForCreditsCommand);
}
return debitOrCredits;
}
}
@@ -1,33 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.service;
import java.math.BigDecimal;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JournalAmountHolder {
private Integer accountType;
private BigDecimal amount;
}
@@ -1,39 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.service;
import java.time.LocalDate;
import org.apache.fineract.accounting.journalentry.data.JournalEntryAssociationParametersData;
import org.apache.fineract.accounting.journalentry.data.JournalEntryData;
import org.apache.fineract.accounting.journalentry.data.OfficeOpeningBalancesData;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
public interface JournalEntryReadPlatformService {
JournalEntryData retrieveGLJournalEntryById(long glJournalEntryId, JournalEntryAssociationParametersData associationParametersData);
Page<JournalEntryData> retrieveAll(SearchParameters searchParameters, Long glAccountId, Boolean onlyManualEntries, LocalDate fromDate,
LocalDate toDate, LocalDate submittedOnDateFrom, LocalDate submittedOnDateTo, String transactionId, Integer entityType,
JournalEntryAssociationParametersData associationParametersData);
OfficeOpeningBalancesData retrieveOfficeOpeningBalances(Long officeId, String currencyCode);
Page<JournalEntryData> retrieveJournalEntriesByEntityId(String transactionId, Long entityId, Integer entityType);
}
@@ -1,30 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.journalentry.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface JournalEntryRunningBalanceUpdateService {
void updateRunningBalance();
CommandProcessingResult updateOfficeRunningBalance(JsonCommand command);
}
@@ -1,169 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.provisioning.constant.ProvisioningEntriesApiConstants;
import org.apache.fineract.accounting.provisioning.data.LoanProductProvisioningEntryData;
import org.apache.fineract.accounting.provisioning.data.ProvisioningEntryData;
import org.apache.fineract.accounting.provisioning.service.ProvisioningEntriesReadPlatformService;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.stereotype.Component;
@Path("/v1/provisioningentries")
@Component
@Tag(name = "Provisioning Entries", description = "This defines the Provisioning Entries for all active loan products\n" + "\n"
+ "Field Descriptions\n" + "date\n" + "Date on which day provisioning entries should be created\n" + "createjournalentries\n"
+ "Boolean variable whether to add journal entries for generated provisioning entries\n")
@RequiredArgsConstructor
public class ProvisioningEntriesApiResource {
private static final Set<String> PROVISIONING_ENTRY_PARAMETERS = new HashSet<>(
Arrays.asList(ProvisioningEntriesApiConstants.PROVISIONINGENTRY_PARAM, ProvisioningEntriesApiConstants.ENTRIES_PARAM));
private static final Set<String> ALL_PROVISIONING_ENTRIES = new HashSet<>(
Arrays.asList(ProvisioningEntriesApiConstants.PROVISIONINGENTRY_PARAM));
private final PlatformSecurityContext platformSecurityContext;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final DefaultToApiJsonSerializer<ProvisioningEntryData> toApiJsonSerializer;
private final DefaultToApiJsonSerializer<Object> entriesApiJsonSerializer;
private final ProvisioningEntriesReadPlatformService provisioningEntriesReadPlatformService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create new Provisioning Entries", description = "Creates a new Provisioning Entries\n" + "\n"
+ "Mandatory Fields\n" + "date\n" + "dateFormat\n" + "locale\n" + "Optional Fields\n" + "createjournalentries")
@RequestBody(content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PostProvisioningEntriesRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PostProvisioningEntriesResponse.class))) })
public String createProvisioningEntries(@Parameter(hidden = true) final String apiRequestBodyAsJson) {
CommandWrapper commandWrapper = null;
this.platformSecurityContext.authenticatedUser();
commandWrapper = new CommandWrapperBuilder().createProvisioningEntries().withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}
@POST
@Path("{entryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Recreates Provisioning Entry", description = "Recreates Provisioning Entry | createjournalentry.")
@RequestBody(content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PutProvisioningEntriesRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ProvisioningEntriesApiResourceSwagger.PutProvisioningEntriesResponse.class))) })
public String modifyProvisioningEntry(@PathParam("entryId") @Parameter(description = "entryId") final Long entryId,
@QueryParam("command") @Parameter(description = "command=createjournalentry\ncommand=recreateprovisioningentry") final String commandParam,
@Parameter(hidden = true) final String apiRequestBodyAsJson) {
CommandWrapper commandWrapper = null;
this.platformSecurityContext.authenticatedUser();
if ("createjournalentry".equals(commandParam)) {
commandWrapper = new CommandWrapperBuilder().createProvisioningJournalEntries(entryId).withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService
.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
} else if ("recreateprovisioningentry".equals(commandParam)) {
commandWrapper = new CommandWrapperBuilder().reCreateProvisioningEntries(entryId).withJson(apiRequestBodyAsJson).build();
final CommandProcessingResult commandProcessingResult = this.commandsSourceWritePlatformService
.logCommandSource(commandWrapper);
return this.toApiJsonSerializer.serialize(commandProcessingResult);
}
throw new UnrecognizedQueryParamException("command", commandParam);
}
@GET
@Path("{entryId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieves a Provisioning Entry", description = "Returns the details of a generated Provisioning Entry.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = ProvisioningEntryData.class))) })
public String retrieveProvisioningEntry(@PathParam("entryId") @Parameter(description = "entryId") final Long entryId,
@Context final UriInfo uriInfo) {
platformSecurityContext.authenticatedUser();
ProvisioningEntryData data = this.provisioningEntriesReadPlatformService.retrieveProvisioningEntryData(entryId);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, data, PROVISIONING_ENTRY_PARAMETERS);
}
@GET
@Path("entries")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanProductProvisioningEntryData.class))) })
public String retrieveProviioningEntries(@QueryParam("entryId") final Long entryId, @QueryParam("offset") final Integer offset,
@QueryParam("limit") final Integer limit, @QueryParam("officeId") final Long officeId,
@QueryParam("productId") final Long productId, @QueryParam("categoryId") final Long categoryId,
@Context final UriInfo uriInfo) {
this.platformSecurityContext.authenticatedUser();
SearchParameters params = SearchParameters.forProvisioningEntries(entryId, officeId, productId, categoryId, offset, limit);
Page<LoanProductProvisioningEntryData> entries = this.provisioningEntriesReadPlatformService.retrieveProvisioningEntries(params);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.entriesApiJsonSerializer.serialize(settings, entries, PROVISIONING_ENTRY_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "List all Provisioning Entries")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = ProvisioningEntryData.class)))) })
public String retrieveAllProvisioningEntries(@QueryParam("offset") @Parameter(description = "offset") final Integer offset,
@QueryParam("limit") @Parameter(description = "limit") final Integer limit, @Context final UriInfo uriInfo) {
platformSecurityContext.authenticatedUser();
Page<ProvisioningEntryData> data = this.provisioningEntriesReadPlatformService.retrieveAllProvisioningEntries(offset, limit);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.entriesApiJsonSerializer.serialize(settings, data, ALL_PROVISIONING_ENTRIES);
}
}
@@ -1,88 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.api;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* Created by sanyam on 25/7/17.
*/
final class ProvisioningEntriesApiResourceSwagger {
private ProvisioningEntriesApiResourceSwagger() {
// only for Swagger Documentation
}
@Schema(description = "PostProvisioningEntriesRequest")
public static final class PostProvisioningEntriesRequest {
private PostProvisioningEntriesRequest() {
}
@Schema(example = "16 October 2015")
public String date;
@Schema(example = "en")
public String locale;
@Schema(example = "dd MMMM yyyy")
public String dateFormat;
@Schema(example = "true")
public String createjournalentries;
public String provisioningentry;
public String entries;
}
@Schema(description = "PostProvisioningEntriesResponse")
public static final class PostProvisioningEntriesResponse {
private PostProvisioningEntriesResponse() {
}
@Schema(example = "1")
public Long resourceId;
}
@Schema(description = "PutProvisioningEntriesRequest")
public static final class PutProvisioningEntriesRequest {
private PutProvisioningEntriesRequest() {
}
@Schema(example = "recreateprovisioningentry")
public String command;
}
@Schema(description = "PutProvisioningEntriesResponse")
public static final class PutProvisioningEntriesResponse {
private PutProvisioningEntriesResponse() {
}
@Schema(example = "1")
public Long resourceId;
}
}
@@ -1,35 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.constant;
public interface ProvisioningEntriesApiConstants {
String JSON_DATE_PARAM = "date";
String JSON_DATEFORMAT_PARAM = "dateFormat";
String JSON_LOCALE_PARAM = "locale";
String JSON_CREATEJOURNALENTRIES_PARAM = "createjournalentries";
String PROVISIONINGENTRY_PARAM = "provisioningentry";
String ENTRIES_PARAM = "entries";
}
@@ -1,51 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.data;
import java.math.BigDecimal;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class LoanProductProvisioningEntryData {
private Long historyId;
private Long officeId;
private String officeName;
private String currencyCode;
private Long productId;
private String productName;
private Long categoryId;
private String categoryName;
private Long overdueInDays;
private BigDecimal percentage;
private BigDecimal balance;
private BigDecimal amountreserved;
private Long liablityAccount;
private String liabilityAccountCode;
private String liabilityAccountName;
private Long expenseAccount;
private String expenseAccountCode;
private String expenseAccountName;
private Long criteriaId;
}
@@ -1,52 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.data;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Collection;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
@Data
@NoArgsConstructor
@Accessors(chain = true)
@SuppressWarnings("unused")
public class ProvisioningEntryData {
private Long id;
private Boolean journalEntry;
private Long createdById;
private String createdUser;
LocalDate createdDate;
Long modifiedById;
private String modifiedUser;
private BigDecimal reservedAmount;
private Collection<LoanProductProvisioningEntryData> provisioningEntries;
}
@@ -1,28 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class NoProvisioningCriteriaDefinitionFound extends AbstractPlatformResourceNotFoundException {
public NoProvisioningCriteriaDefinitionFound() {
super("error.msg.no.provisioning.criteria.definitions.found", "No Provisioning Criteria Definitions are found");
}
}
@@ -1,30 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.exception;
import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class ProvisioningEntryAlreadyCreatedException extends AbstractPlatformResourceNotFoundException {
public ProvisioningEntryAlreadyCreatedException(Long id, LocalDate date) {
super("error.msg.provisioningentry.already.exists.for.this.date",
"ProvisioningEntry with identifier " + id + " exists for given date" + date, id);
}
}
@@ -1,29 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class ProvisioningEntryNotfoundException extends AbstractPlatformResourceNotFoundException {
public ProvisioningEntryNotfoundException(Long id) {
super("error.msg.provisioningentry.id.invalid", "ProvisioningEntry with identifier " + id + " does not exist", id);
}
}
@@ -1,31 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.exception;
import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
public class ProvisioningJournalEntriesCannotbeCreatedException extends AbstractPlatformResourceNotFoundException {
public ProvisioningJournalEntriesCannotbeCreatedException(LocalDate existingEntriesDate, LocalDate requestedDate) {
super("error.msg.provisioning.journalentries.cannot.be.created", "Provisioning Journal Entries already created on later date "
+ existingEntriesDate + " than requested date " + requestedDate);
}
}
@@ -1,77 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.serialization;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.provisioning.constant.ProvisioningEntriesApiConstants;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.provisioning.exception.ProvisioningCriteriaCannotBeCreatedException;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class ProvisioningEntriesDefinitionJsonDeserializer implements ProvisioningEntriesApiConstants {
private static final Set<String> supportedParameters = new HashSet<>(
Arrays.asList(JSON_DATE_PARAM, JSON_DATEFORMAT_PARAM, JSON_LOCALE_PARAM, JSON_CREATEJOURNALENTRIES_PARAM));
private final FromJsonHelper fromApiJsonHelper;
public void validateForCreate(final String json) {
if (StringUtils.isBlank(json)) {
throw new ProvisioningCriteriaCannotBeCreatedException("error.msg.provisioningentry.cannot.be.created",
"locale, dateformat, date, createjournalentries params are missing in the request");
}
final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, supportedParameters);
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("provisioningcriteria");
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Locale locale = this.fromApiJsonHelper.extractLocaleParameter(element.getAsJsonObject());
baseDataValidator.reset().parameter(JSON_DATEFORMAT_PARAM).value(locale).notNull();
final String dateformat = this.fromApiJsonHelper.extractDateFormatParameter(element.getAsJsonObject());
baseDataValidator.reset().parameter(JSON_DATEFORMAT_PARAM).value(dateformat).notBlank();
LocalDate localDate = this.fromApiJsonHelper.extractLocalDateNamed(JSON_DATE_PARAM, element);
baseDataValidator.reset().parameter(JSON_DATE_PARAM).value(localDate).notBlank();
baseDataValidator.reset().parameter(JSON_DATE_PARAM).value(localDate).validateDateBeforeOrEqual(DateUtils.getBusinessLocalDate());
if (this.fromApiJsonHelper.parameterExists(JSON_CREATEJOURNALENTRIES_PARAM, element)) {
Boolean bool = this.fromApiJsonHelper.extractBooleanNamed(JSON_CREATEJOURNALENTRIES_PARAM, element);
baseDataValidator.reset().parameter(JSON_CREATEJOURNALENTRIES_PARAM).value(bool).validateForBooleanValue();
}
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
}
@@ -1,43 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.service;
import java.time.LocalDate;
import java.util.Collection;
import org.apache.fineract.accounting.provisioning.data.LoanProductProvisioningEntryData;
import org.apache.fineract.accounting.provisioning.data.ProvisioningEntryData;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
public interface ProvisioningEntriesReadPlatformService {
Collection<LoanProductProvisioningEntryData> retrieveLoanProductsProvisioningData(LocalDate date);
ProvisioningEntryData retrieveProvisioningEntryData(Long entryId);
Page<ProvisioningEntryData> retrieveAllProvisioningEntries(Integer offset, Integer limit);
ProvisioningEntryData retrieveProvisioningEntryData(String date);
ProvisioningEntryData retrieveProvisioningEntryDataByCriteriaId(Long criteriaId);
ProvisioningEntryData retrieveExistingProvisioningIdDateWithJournals();
Page<LoanProductProvisioningEntryData> retrieveProvisioningEntries(SearchParameters searchParams);
}
@@ -1,357 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.provisioning.service;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.accounting.provisioning.data.LoanProductProvisioningEntryData;
import org.apache.fineract.accounting.provisioning.data.ProvisioningEntryData;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.Page;
import org.apache.fineract.infrastructure.core.service.PaginationHelper;
import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
@RequiredArgsConstructor
@Slf4j
public class ProvisioningEntriesReadPlatformServiceImpl implements ProvisioningEntriesReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final PaginationHelper loanProductProvisioningEntryDataPaginationHelper;
private final PaginationHelper provisioningEntryDataPaginationHelper;
private final DatabaseSpecificSQLGenerator sqlGenerator;
@Override
public Collection<LoanProductProvisioningEntryData> retrieveLoanProductsProvisioningData(LocalDate date) {
String formattedDate = DateUtils.DEFAULT_DATE_FORMATTER.format(date);
LoanProductProvisioningEntryMapper mapper = new LoanProductProvisioningEntryMapper(sqlGenerator);
final String sql = mapper.schema();
return this.jdbcTemplate.query(sql, mapper, formattedDate, formattedDate, formattedDate);
}
private static final class LoanProductProvisioningEntryMapper implements RowMapper<LoanProductProvisioningEntryData> {
private final StringBuilder sqlQuery;
private LoanProductProvisioningEntryMapper(DatabaseSpecificSQLGenerator sqlGenerator) {
sqlQuery = new StringBuilder().append(
"select (CASE WHEN loan.loan_type_enum=1 THEN mclient.office_id ELSE mgroup.office_id END) as office_id, loan.loan_type_enum, pcd.criteria_id as criteriaid, loan.product_id,loan.currency_code,")
.append("GREATEST(" + sqlGenerator.dateDiff("?", "sch.duedate")
+ ", 0) as numberofdaysoverdue,sch.duedate, pcd.category_id, pcd.provision_percentage,")
.append("loan.total_outstanding_derived as outstandingbalance, pcd.liability_account, pcd.expense_account from m_loan_repayment_schedule sch")
.append(" LEFT JOIN m_loan loan on sch.loan_id = loan.id")
.append(" JOIN m_loanproduct_provisioning_mapping lpm on lpm.product_id = loan.product_id")
.append(" JOIN m_provisioning_criteria_definition pcd on pcd.criteria_id = lpm.criteria_id and ")
.append("(pcd.min_age <= GREATEST(" + sqlGenerator.dateDiff("?", "sch.duedate") + ",0) and GREATEST("
+ sqlGenerator.dateDiff("?", "sch.duedate") + ",0) <= pcd.max_age) and pcd.criteria_id is not null ")
.append("LEFT JOIN m_client mclient ON mclient.id = loan.client_id ")
.append("LEFT JOIN m_group mgroup ON mgroup.id = loan.group_id ")
.append("where loan.loan_status_id=300 and sch.duedate = ")
.append("(select MIN(sch1.duedate) from m_loan_repayment_schedule sch1 where sch1.loan_id=loan.id and sch1.completed_derived=false)");
}
@Override
@SuppressWarnings("unused")
public LoanProductProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long officeId = rs.getLong("office_id");
Long productId = rs.getLong("product_id");
String currentcyCode = rs.getString("currency_code");
Long overdueDays = rs.getLong("numberofdaysoverdue");
Long categoryId = rs.getLong("category_id");
BigDecimal percentage = rs.getBigDecimal("provision_percentage");
BigDecimal outstandingBalance = rs.getBigDecimal("outstandingbalance");
Long liabilityAccountCode = rs.getLong("liability_account");
Long expenseAccountCode = rs.getLong("expense_account");
Long criteriaId = rs.getLong("criteriaid");
Long historyId = null;
return new LoanProductProvisioningEntryData().setHistoryId(historyId).setOfficeId(officeId).setCurrencyCode(currentcyCode)
.setProductId(productId).setCategoryId(categoryId).setOverdueInDays(overdueDays).setPercentage(percentage)
.setBalance(outstandingBalance).setLiablityAccount(liabilityAccountCode).setExpenseAccount(expenseAccountCode)
.setCriteriaId(criteriaId);
}
public String schema() {
return sqlQuery.toString();
}
}
@Override
public ProvisioningEntryData retrieveProvisioningEntryData(Long entryId) {
ProvisioningEntryDataMapperWithSumReserved mapper1 = new ProvisioningEntryDataMapperWithSumReserved();
// Programmatic query, disable sonar
final String sql = "select" + mapper1.getSchema() + " where entry.id = ? group by entry.id, created.username, modified.username";
return this.jdbcTemplate.queryForObject(sql, mapper1, entryId);// NOSONAR
}
private static final class ProvisioningEntryDataMapper implements RowMapper<ProvisioningEntryData> {
private final StringBuilder sqlQuery = new StringBuilder()
.append(" entry.id, entry.journal_entry_created, entry.createdby_id, entry.created_date, created.username as createduser,")
.append("entry.lastmodifiedby_id, modified.username as modifieduser, entry.lastmodified_date ")
.append("from m_provisioning_history entry ").append("left JOIN m_appuser created ON created.id = entry.createdby_id ")
.append("left JOIN m_appuser modified ON modified.id = entry.lastmodifiedby_id ");
@Override
@SuppressWarnings("unused")
public ProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long id = rs.getLong("id");
Boolean journalEntry = rs.getBoolean("journal_entry_created");
Long createdById = rs.getLong("createdby_id");
String createdUser = rs.getString("createduser");
Date createdDate = rs.getDate("created_date");
Long modifiedById = rs.getLong("lastmodifiedby_id");
String modifieUser = rs.getString("modifieduser");
BigDecimal totalReservedAmount = null;
LocalDate createdLocalDate = createdDate != null ? createdDate.toLocalDate() : null;
return new ProvisioningEntryData().setId(id).setJournalEntry(journalEntry).setCreatedById(createdById)
.setCreatedUser(createdUser).setCreatedDate(createdLocalDate).setModifiedById(modifiedById).setModifiedUser(modifieUser)
.setReservedAmount(totalReservedAmount);
}
public String getSchema() {
return sqlQuery.toString();
}
}
private static final class LoanProductProvisioningEntryRowMapper implements RowMapper<LoanProductProvisioningEntryData> {
private final StringBuilder sqlQuery = new StringBuilder().append(
" entry.id, entry.history_id as historyId, office_id, entry.criteria_id as criteriaid, office.name as officename, product.name as productname, entry.product_id, ")
.append("category_id, category.category_name, liability.id as liabilityid, liability.gl_code as liabilitycode, liability.name as liabilityname, ")
.append("expense.id as expenseid, expense.gl_code as expensecode, expense.name as expensename, entry.currency_code, entry.overdue_in_days, entry.reseve_amount from m_loanproduct_provisioning_entry entry ")
.append("left join m_office office ON office.id = entry.office_id ")
.append("left join m_product_loan product ON product.id = entry.product_id ")
.append("left join m_provision_category category ON category.id = entry.category_id ")
.append("left join acc_gl_account liability ON liability.id = entry.liability_account ")
.append("left join acc_gl_account expense ON expense.id = entry.expense_account ");
@Override
@SuppressWarnings("unused")
public LoanProductProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long historyId = rs.getLong("historyId");
Long officeId = rs.getLong("office_id");
String officeName = rs.getString("officename");
Long productId = rs.getLong("product_id");
String productName = rs.getString("productname");
String currentcyCode = rs.getString("currency_code");
Long overdueDays = rs.getLong("overdue_in_days");
Long categoryId = rs.getLong("category_id");
String categoryName = rs.getString("category_name");
BigDecimal amountreserved = rs.getBigDecimal("reseve_amount");
Long liabilityAccountCode = rs.getLong("liabilityid");
String liabilityAccountglCode = rs.getString("liabilitycode");
String expenseAccountglCode = rs.getString("expensecode");
Long expenseAccountCode = rs.getLong("expenseid");
Long criteriaId = rs.getLong("criteriaid");
String liabilityAccountName = rs.getString("liabilityname");
String expenseAccountName = rs.getString("expensename");
return new LoanProductProvisioningEntryData().setHistoryId(historyId).setOfficeId(officeId).setOfficeName(officeName)
.setCurrencyCode(currentcyCode).setProductId(productId).setProductName(productName).setCategoryId(categoryId)
.setCategoryName(categoryName).setOverdueInDays(overdueDays).setAmountreserved(amountreserved)
.setLiablityAccount(liabilityAccountCode).setLiabilityAccountCode(liabilityAccountglCode)
.setLiabilityAccountName(liabilityAccountName).setExpenseAccount(expenseAccountCode)
.setExpenseAccountCode(expenseAccountglCode).setExpenseAccountName(expenseAccountName).setCriteriaId(criteriaId);
}
public String getSchema() {
return sqlQuery.toString();
}
}
private static final class ProvisioningEntryDataMapperWithSumReserved implements RowMapper<ProvisioningEntryData> {
private final StringBuilder sqlQuery = new StringBuilder()
.append(" entry.id, journal_entry_created, createdby_id, created_date, created.username as createduser,")
.append("lastmodifiedby_id, modified.username as modifieduser, lastmodified_date, SUM(reserved.reseve_amount) as totalreserved ")
.append("from m_provisioning_history entry ")
.append("JOIN m_loanproduct_provisioning_entry reserved on entry.id = reserved.history_id ")
.append("left JOIN m_appuser created ON created.id = entry.createdby_id ")
.append("left JOIN m_appuser modified ON modified.id = entry.lastmodifiedby_id ");
@Override
@SuppressWarnings("unused")
public ProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long id = rs.getLong("id");
Boolean journalEntry = rs.getBoolean("journal_entry_created");
Long createdById = rs.getLong("createdby_id");
String createdUser = rs.getString("createduser");
Date createdDate = rs.getDate("created_date");
Long modifiedById = rs.getLong("lastmodifiedby_id");
String modifieUser = rs.getString("modifieduser");
BigDecimal totalReservedAmount = rs.getBigDecimal("totalreserved");
LocalDate createdLocalDate = createdDate != null ? createdDate.toLocalDate() : null;
return new ProvisioningEntryData().setId(id).setJournalEntry(journalEntry).setCreatedById(createdById)
.setCreatedUser(createdUser).setCreatedDate(createdLocalDate).setModifiedById(modifiedById).setModifiedUser(modifieUser)
.setReservedAmount(totalReservedAmount);
}
public String getSchema() {
return sqlQuery.toString();
}
}
@Override
public Page<ProvisioningEntryData> retrieveAllProvisioningEntries(Integer offset, Integer limit) {
ProvisioningEntryDataMapper mapper = new ProvisioningEntryDataMapper();
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ");
sqlBuilder.append(mapper.getSchema());
sqlBuilder.append(" order by entry.created_date");
if (limit != null) {
sqlBuilder.append(" limit ").append(limit);
}
if (offset != null) {
sqlBuilder.append(" offset ").append(offset);
}
Object[] whereClauseItemsitems = new Object[] {};
return this.provisioningEntryDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), whereClauseItemsitems,
mapper);
}
@Override
public ProvisioningEntryData retrieveProvisioningEntryData(String date) {
ProvisioningEntryDataMapper mapper1 = new ProvisioningEntryDataMapper();
date = date + "%";
final String sql1 = "select " + mapper1.getSchema() + " where entry.created_date like ? ";
ProvisioningEntryData data = null;
try {
data = this.jdbcTemplate.queryForObject(sql1, mapper1, date); // NOSONAR
} catch (EmptyResultDataAccessException e) {
log.error("Problem occurred in retrieveProvisioningEntryData function", e);
}
return data;
}
@Override
public ProvisioningEntryData retrieveProvisioningEntryDataByCriteriaId(Long criteriaId) {
ProvisioningEntryData data = null;
LoanProductProvisioningEntryRowMapper mapper = new LoanProductProvisioningEntryRowMapper();
final String sql = "select " + mapper.getSchema() + " where entry.criteria_id = ?";
Collection<LoanProductProvisioningEntryData> entries = this.jdbcTemplate.query(sql, mapper, criteriaId); // NOSONAR
if (entries != null && entries.size() > 0) {
Long entryId = ((LoanProductProvisioningEntryData) entries.toArray()[0]).getHistoryId();
ProvisioningEntryDataMapper mapper1 = new ProvisioningEntryDataMapper();
final String sql1 = "select " + mapper1.getSchema() + " where entry.id = ?";
data = this.jdbcTemplate.queryForObject(sql1, mapper1, entryId); // NOSONAR
data.setProvisioningEntries(entries);
}
return data;
}
@Override
public ProvisioningEntryData retrieveExistingProvisioningIdDateWithJournals() {
ProvisioningEntryData data = null;
ProvisioningEntryIdDateRowMapper mapper = new ProvisioningEntryIdDateRowMapper();
try {
data = this.jdbcTemplate.queryForObject(mapper.schema(), mapper, new Object[] {});
} catch (EmptyResultDataAccessException e) {
data = null;
}
return data;
}
private static final class ProvisioningEntryIdDateRowMapper implements RowMapper<ProvisioningEntryData> {
StringBuilder buff = new StringBuilder().append("select history1.id, history1.created_date from m_provisioning_history history1 ")
.append("where history1.created_date = (select max(history2.created_date) from m_provisioning_history history2 ")
.append("where history2.journal_entry_created='1')");
@Override
public ProvisioningEntryData mapRow(ResultSet rs, int rowNum) throws SQLException {
Long id = rs.getLong("id");
Date createdDate = rs.getDate("created_date");
Long createdBy = null;
String createdName = null;
Long modifiedBy = null;
String modifiedName = null;
BigDecimal totalReservedAmount = null;
LocalDate createdLocalDate = createdDate != null ? createdDate.toLocalDate() : null;
return new ProvisioningEntryData().setId(id).setJournalEntry(Boolean.TRUE).setCreatedById(createdBy).setCreatedUser(createdName)
.setCreatedDate(createdLocalDate).setModifiedById(modifiedBy).setModifiedUser(modifiedName)
.setReservedAmount(totalReservedAmount);
}
public String schema() {
return buff.toString();
}
}
@Override
public Page<LoanProductProvisioningEntryData> retrieveProvisioningEntries(SearchParameters searchParams) {
LoanProductProvisioningEntryRowMapper mapper = new LoanProductProvisioningEntryRowMapper();
final StringBuilder sqlBuilder = new StringBuilder(200);
sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ");
sqlBuilder.append(mapper.getSchema());
String whereClose = " where ";
List<Object> items = new ArrayList<>();
if (searchParams.isProvisioningEntryIdPassed()) {
sqlBuilder.append(whereClose + " entry.history_id = ?");
items.add(searchParams.getProvisioningEntryId());
whereClose = " and ";
}
if (searchParams.isOfficeIdPassed()) {
sqlBuilder.append(whereClose + " entry.office_id = ?");
items.add(searchParams.getOfficeId());
whereClose = " and ";
}
if (searchParams.isProductIdPassed()) {
sqlBuilder.append(whereClose + " entry.product_id = ?");
items.add(searchParams.getProductId());
whereClose = " and ";
}
if (searchParams.isCategoryIdPassed()) {
sqlBuilder.append(whereClose + " entry.category_id = ?");
items.add(searchParams.getCategoryId());
}
sqlBuilder.append(" order by entry.id");
if (searchParams.isLimited()) {
sqlBuilder.append(" limit ").append(searchParams.getLimit());
if (searchParams.isOffset()) {
sqlBuilder.append(" offset ").append(searchParams.getOffset());
}
}
Object[] whereClauseItemsitems = items.toArray();
return this.loanProductProvisioningEntryDataPaginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(),
whereClauseItemsitems, mapper);
}
}
@@ -1,293 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.api;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.UriInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingConstants;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.glaccount.service.GLAccountReadPlatformService;
import org.apache.fineract.accounting.rule.data.AccountingRuleData;
import org.apache.fineract.accounting.rule.data.AccountingTagRuleData;
import org.apache.fineract.accounting.rule.service.AccountingRuleReadPlatformService;
import org.apache.fineract.commands.domain.CommandWrapper;
import org.apache.fineract.commands.service.CommandWrapperBuilder;
import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.codes.service.CodeValueReadPlatformService;
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.office.data.OfficeData;
import org.apache.fineract.organisation.office.service.OfficeReadPlatformService;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.stereotype.Component;
@Path("/v1/accountingrules")
@Component
@Tag(name = "Accounting Rules", description = "It is typical scenario in MFI's that non accountants pass journal entries on a regular basis. For Ex: A branch office might deposit their entire cash at hand to their Bank account at the end of a working day. The branch office users might not understand enough of accounting to figure out which account needs to get credited and which account needs to be debited to represent this transaction.\n"
+ "\n"
+ "Enter accounting rules, an abstraction on top of manual Journal entires for enabling simpler data entry. An accounting rule can define any of the following abstractions\n"
+ "\n" + "A Simple journal entry where both the credit and debit account have been preselected\n"
+ "A Simple journal entry where either credit or debit accounts have been limited to a pre-selected list of accounts (Ex: Debit account should be one of \"Bank of America\" of \"JP Morgan\" and credit account should be \"Cash\")\n"
+ "A Compound journal entry where multiple debits and / or multiple credits may be made amongst a set of preselected list of accounts (Ex: Credit account should be either \"Bank Of America\" or \"Cash\" and debit account can be \"Employee Salary\" and/or \"Miscellenous Expenses\")\n"
+ "An accounting rule can also be optionally associated with a branch, so that only a particular Branch's users have access to the rule")
@RequiredArgsConstructor
public class AccountingRuleApiResource {
private static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(
Arrays.asList("id", "officeId", "officeName", "accountToDebitId", "accountToCreditId", "name", "description", "systemDefined",
"allowedCreditTagOptions", "allowedDebitTagOptions", "debitTags", "creditTags", "creditAccounts", "debitAccounts",
"allowMultipleCreditEntries", "allowMultipleDebitEntries", "tag"));
private static final String RESOURCE_NAME_FOR_PERMISSION = "ACCOUNTINGRULE";
private final AccountingRuleReadPlatformService accountingRuleReadPlatformService;
private final GLAccountReadPlatformService accountReadPlatformService;
private final OfficeReadPlatformService officeReadPlatformService;
private final DefaultToApiJsonSerializer<AccountingRuleData> apiJsonSerializerService;
private final ApiRequestParameterHelper apiRequestParameterHelper;
private final PlatformSecurityContext context;
private final PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService;
private final CodeValueReadPlatformService codeValueReadPlatformService;
@GET
@Path("template")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Accounting Rule Details Template", description = "This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of:\n"
+ "\n" + "Field Defaults\n" + "Allowed Value Lists\n" + "Example Request:\n" + "\n" + "accountingrules/template")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.GetAccountRulesTemplateResponse.class))) })
public String retrieveTemplate(@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
AccountingRuleData accountingRuleData = null;
accountingRuleData = handleTemplate(accountingRuleData);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, accountingRuleData, RESPONSE_DATA_PARAMETERS);
}
@GET
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve Accounting Rules", description = "Returns the list of defined accounting rules.\n" + "\n"
+ "Example Requests:\n" + "\n" + "accountingrules")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.GetAccountRulesResponse.class)))) })
public String retrieveAllAccountingRules(@Context final UriInfo uriInfo) {
final AppUser currentUser = this.context.authenticatedUser();
currentUser.validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
final String hierarchy = currentUser.getOffice().getHierarchy();
final String hierarchySearchString = hierarchy + "%";
final Set<String> associationParameters = ApiParameterHelper.extractAssociationsForResponseIfProvided(uriInfo.getQueryParameters());
boolean isAssociationParametersExists = false;
if (!associationParameters.isEmpty()) {
if (associationParameters.contains("all")) {
isAssociationParametersExists = true; // If true, retrieve
// additional fields for
// journal entry form.
}
}
final List<AccountingRuleData> accountingRuleDatas = this.accountingRuleReadPlatformService
.retrieveAllAccountingRules(hierarchySearchString, isAssociationParametersExists);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.apiJsonSerializerService.serialize(settings, accountingRuleDatas, RESPONSE_DATA_PARAMETERS);
}
@GET
@Path("{accountingRuleId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Retrieve a Accounting rule", description = "Returns the details of a defined Accounting rule.\n" + "\n"
+ "Example Requests:\n" + "\n" + "accountingrules/1")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleData.class))) })
public String retreiveAccountingRule(
@PathParam("accountingRuleId") @Parameter(description = "accountingRuleId") final Long accountingRuleId,
@Context final UriInfo uriInfo) {
this.context.authenticatedUser().validateHasReadPermission(RESOURCE_NAME_FOR_PERMISSION);
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
AccountingRuleData accountingRuleData = this.accountingRuleReadPlatformService.retrieveAccountingRuleById(accountingRuleId);
if (settings.isTemplate()) {
accountingRuleData = handleTemplate(accountingRuleData);
}
return this.apiJsonSerializerService.serialize(settings, accountingRuleData, RESPONSE_DATA_PARAMETERS);
}
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Create/Define a Accounting rule", description = "Define a new Accounting rule.\n" + "\n" + "Mandatory Fields\n"
+ "name, officeId,\n" + "accountToDebit OR debitTags,\n" + "accountToCredit OR creditTags.\n" + "\n" + "Optional Fields\n"
+ "description")
@RequestBody(content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.PostAccountingRulesRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.PostAccountingRulesResponse.class))) })
public String createAccountingRule(@Parameter(hidden = true) final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().createAccountingRule().withJson(jsonRequestBody).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
@PUT
@Path("{accountingRuleId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Update a Accounting Rule", description = "Updates the details of a Accounting rule.")
@RequestBody(content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.PutAccountingRulesRequest.class)))
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.PutAccountingRulesResponse.class))) })
public String updateAccountingRule(
@PathParam("accountingRuleId") @Parameter(description = "accountingRuleId") final Long accountingRuleId,
@Parameter(hidden = true) final String jsonRequestBody) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().updateAccountingRule(accountingRuleId).withJson(jsonRequestBody)
.build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
@DELETE
@Path("{accountingRuleId}")
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
@Operation(summary = "Delete a Accounting Rule", description = "Deletes a Accounting rule.")
@ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = AccountingRuleApiResourceSwagger.DeleteAccountingRulesResponse.class))) })
public String deleteAccountingRule(
@PathParam("accountingRuleId") @Parameter(description = "accountingRuleId") final Long accountingRuleId) {
final CommandWrapper commandRequest = new CommandWrapperBuilder().deleteAccountingRule(accountingRuleId).build();
final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
return this.apiJsonSerializerService.serialize(result);
}
private AccountingRuleData handleTemplate(AccountingRuleData accountingRuleData) {
final List<GLAccountData> allowedAccounts = this.accountReadPlatformService.retrieveAllEnabledDetailGLAccounts();
final List<OfficeData> allowedOffices = (List<OfficeData>) this.officeReadPlatformService.retrieveAllOfficesForDropdown();
final Collection<CodeValueData> allowedTagOptions = this.codeValueReadPlatformService
.retrieveCodeValuesByCode(AccountingConstants.ASSESTS_TAG_OPTION_CODE_NAME);
allowedTagOptions
.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.LIABILITIES_TAG_OPTION_CODE_NAME));
allowedTagOptions
.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.EQUITY_TAG_OPTION_CODE_NAME));
allowedTagOptions
.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.INCOME_TAG_OPTION_CODE_NAME));
allowedTagOptions
.addAll(this.codeValueReadPlatformService.retrieveCodeValuesByCode(AccountingConstants.EXPENSES_TAG_OPTION_CODE_NAME));
if (accountingRuleData == null) {
final Collection<CodeValueData> allowedCreditTagOptions = allowedTagOptions;
final Collection<CodeValueData> allowedDebitTagOptions = allowedTagOptions;
accountingRuleData = new AccountingRuleData().setAllowedOffices(allowedOffices).setAllowedAccounts(allowedAccounts)
.setAllowedCreditTagOptions(allowedCreditTagOptions).setAllowedDebitTagOptions(allowedDebitTagOptions);
} else {
final Collection<CodeValueData> allowedCreditTagOptions;
final Collection<CodeValueData> allowedDebitTagOptions;
if (accountingRuleData.getCreditTags() != null) {
allowedCreditTagOptions = retrieveSelectedTags(allowedTagOptions, accountingRuleData.getCreditTags());
} else {
allowedCreditTagOptions = allowedTagOptions;
}
if (accountingRuleData.getDebitTags() != null) {
allowedDebitTagOptions = retrieveSelectedTags(allowedTagOptions, accountingRuleData.getDebitTags());
} else {
allowedDebitTagOptions = allowedTagOptions;
}
accountingRuleData = new AccountingRuleData().setId(accountingRuleData.getId()).setOfficeId(accountingRuleData.getOfficeId())
.setOfficeName(accountingRuleData.getOfficeName()).setName(accountingRuleData.getName())
.setDescription(accountingRuleData.getDescription()).setSystemDefined(accountingRuleData.isSystemDefined())
.setAllowMultipleCreditEntries(accountingRuleData.isAllowMultipleCreditEntries())
.setAllowMultipleDebitEntries(accountingRuleData.isAllowMultipleDebitEntries()).setAllowedAccounts(allowedAccounts)
.setAllowedOffices(allowedOffices).setAllowedCreditTagOptions(allowedCreditTagOptions)
.setAllowedDebitTagOptions(allowedDebitTagOptions);
}
return accountingRuleData;
}
private Collection<CodeValueData> retrieveSelectedTags(final Collection<CodeValueData> allowedTagOptions,
final List<AccountingTagRuleData> existedTags) {
final Collection<CodeValueData> tempOptions = new ArrayList<>(allowedTagOptions);
final Map<Long, CodeValueData> selectedTags = new HashMap<>();
for (final AccountingTagRuleData accountingTagRuleData : existedTags) {
for (final CodeValueData codeValueData : tempOptions) {
if (codeValueData.getId().equals(accountingTagRuleData.getTag().getId())) {
selectedTags.put(codeValueData.getId(), codeValueData);
}
}
}
tempOptions.removeAll(selectedTags.values());
return tempOptions;
}
}
@@ -1,142 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.api;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.ArrayList;
import java.util.List;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.rule.data.AccountingTagRuleData;
import org.apache.fineract.organisation.office.data.OfficeData;
/**
* Created by sanyam on 26/7/17.
*/
final class AccountingRuleApiResourceSwagger {
private AccountingRuleApiResourceSwagger() {
// For Swagger Documentation
}
@Schema(description = "GetAccountRulesResponse")
public static final class GetAccountRulesResponse {
private GetAccountRulesResponse() {
}
public Long id;
public Long officeId;
public String officeName;
public String name;
public String description;
public boolean systemDefined;
public boolean allowMultipleDebitEntries;
public boolean allowMultipleCreditEntries;
public List<AccountingTagRuleData> creditTags;
public List<AccountingTagRuleData> debitTags;
}
@Schema(description = "GetAccountRulesTemplateResponse")
public static final class GetAccountRulesTemplateResponse {
private GetAccountRulesTemplateResponse() {
}
@Schema(example = "false")
public boolean systemDefined;
public List<OfficeData> allowedOffices = new ArrayList<OfficeData>();
public List<GLAccountData> allowedAccounts = new ArrayList<GLAccountData>();
}
@Schema(description = "PostAccountingRulesRequest")
public static final class PostAccountingRulesRequest {
private PostAccountingRulesRequest() {
}
@Schema(example = "test")
public String name;
@Schema(example = "1")
public Long officeId;
@Schema(example = "21")
public Long accountToDebit;
@Schema(example = "9")
public Long accountToCredit;
@Schema(example = "Employee salary")
public String description;
}
@Schema(description = "PostAccountingRulesResponse")
public static final class PostAccountingRulesResponse {
private PostAccountingRulesResponse() {
}
@Schema(example = "1")
public Long officeId;
@Schema(example = "1")
public Long resourceId;
}
@Schema(description = "PutAccountingRulesRequest")
public static final class PutAccountingRulesRequest {
private PutAccountingRulesRequest() {
}
@Schema(example = "Employee Salary posting rule")
public String name;
}
@Schema(description = "PutAccountingRulesResponse")
public static final class PutAccountingRulesResponse {
private PutAccountingRulesResponse() {
}
public static class PutAccountingRulesResponsechangesSwagger {
PutAccountingRulesResponsechangesSwagger() {}
@Schema(example = "Employee Salary posting rule")
public String name;
}
@Schema(example = "1")
public Long resourceId;
public PutAccountingRulesResponsechangesSwagger changes;
}
@Schema(description = "DeleteAccountingRulesResponse")
public static final class DeleteAccountingRulesResponse {
private DeleteAccountingRulesResponse() {
}
@Schema(example = "1")
public Long resourceId;
}
}
@@ -1,60 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.api;
import java.util.HashSet;
import java.util.Set;
/***
* Enum of all parameters passed in while creating/updating a loan product
***/
public enum AccountingRuleJsonInputParams {
ID("id"), OFFICE_ID("officeId"), ACCOUNT_TO_DEBIT("accountToDebit"), ACCOUNT_TO_CREDIT("accountToCredit"), NAME("name"), DESCRIPTION(
"description"), SYSTEM_DEFINED("systemDefined"), DEBIT_ACCOUNT_TAGS("debitTags"), CREDIT_ACCOUNT_TAGS(
"creditTags"), ALLOW_MULTIPLE_CREDIT_ENTRIES(
"allowMultipleCreditEntries"), ALLOW_MULTIPLE_DEBIT_ENTRIES("allowMultipleDebitEntries");
private final String value;
AccountingRuleJsonInputParams(final String value) {
this.value = value;
}
private static final Set<String> values = new HashSet<>();
static {
for (final AccountingRuleJsonInputParams type : AccountingRuleJsonInputParams.values()) {
values.add(type.value);
}
}
public static Set<String> getAllValues() {
return values;
}
@Override
public String toString() {
return name().toString().replace("_", " ");
}
public String getValue() {
return this.value;
}
}
@@ -1,65 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.data;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.accounting.glaccount.data.GLAccountDataForLookup;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.organisation.office.data.OfficeData;
/**
* Immutable object representing a General Ledger Account
*
* Note: no getter/setters required as google-gson will produce json from fields of object.
*/
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class AccountingRuleData {
private Long id;
private Long officeId;
private String officeName;
private String name;
private String description;
private boolean systemDefined;
private boolean allowMultipleDebitEntries;
private boolean allowMultipleCreditEntries;
private List<AccountingTagRuleData> creditTags;
private List<AccountingTagRuleData> debitTags;
// template
@SuppressWarnings("unused")
private List<OfficeData> allowedOffices = new ArrayList<OfficeData>();
@SuppressWarnings("unused")
private List<GLAccountData> allowedAccounts = new ArrayList<GLAccountData>();
@SuppressWarnings("unused")
private Collection<CodeValueData> allowedCreditTagOptions;
@SuppressWarnings("unused")
private Collection<CodeValueData> allowedDebitTagOptions;
private List<GLAccountDataForLookup> creditAccounts;
private List<GLAccountDataForLookup> debitAccounts;
}
@@ -1,35 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.data;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class AccountingTagRuleData {
private Long id;
private CodeValueData tag;
private EnumOptionData transactionType;
}
@@ -1,205 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.domain;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
import org.apache.fineract.accounting.rule.api.AccountingRuleJsonInputParams;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.organisation.office.domain.Office;
@Getter
@Setter
@NoArgsConstructor
@Accessors(chain = true)
@Entity
@Table(name = "acc_accounting_rule", uniqueConstraints = {
@UniqueConstraint(columnNames = { "name" }, name = "accounting_rule_name_unique") })
public class AccountingRule extends AbstractPersistableCustom {
@Column(name = "name", nullable = false, length = 500)
private String name;
@ManyToOne
@JoinColumn(name = "office_id", nullable = true)
private Office office;
@ManyToOne
@JoinColumn(name = "debit_account_id", nullable = true)
private GLAccount accountToDebit;
@ManyToOne
@JoinColumn(name = "credit_account_id", nullable = true)
private GLAccount accountToCredit;
@Column(name = "description", nullable = true, length = 500)
private String description;
@Column(name = "system_defined", nullable = false)
private Boolean systemDefined;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "accountingRule", orphanRemoval = true, fetch = FetchType.EAGER)
private List<AccountingTagRule> accountingTagRules = new ArrayList<>();
@Column(name = "allow_multiple_credits", nullable = false)
private boolean allowMultipleCreditEntries;
@Column(name = "allow_multiple_debits", nullable = false)
private boolean allowMultipleDebitEntries;
public static AccountingRule fromJson(final Office office, final GLAccount accountToDebit, final GLAccount accountToCredit,
final JsonCommand command, final boolean allowMultipleCreditEntries, final boolean allowMultipleDebitEntries) {
final String name = command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.NAME.getValue());
final String description = command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.DESCRIPTION.getValue());
final boolean systemDefined = false;
return new AccountingRule().setOffice(office).setAccountToDebit(accountToDebit).setAccountToCredit(accountToCredit).setName(name)
.setDescription(StringUtils.trim(description)).setSystemDefined(systemDefined)
.setAllowMultipleCreditEntries(allowMultipleCreditEntries).setAllowMultipleDebitEntries(allowMultipleDebitEntries);
}
public Map<String, Object> update(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(10);
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.OFFICE_ID.getValue(),
this.office == null ? 0L : this.office.getId());
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(),
this.accountToDebit == null ? 0L : this.accountToDebit.getId());
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
this.accountToCredit == null ? 0L : this.accountToCredit.getId());
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.NAME.getValue(), this.name);
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.DESCRIPTION.getValue(), this.description);
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.SYSTEM_DEFINED.getValue(), this.systemDefined);
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue(),
this.allowMultipleCreditEntries);
handlePropertyUpdate(command, actualChanges, AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue(),
this.allowMultipleDebitEntries);
return actualChanges;
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
final String propertyToBeUpdated) {
if (command.isChangeInStringParameterNamed(paramName, propertyToBeUpdated)) {
final String newValue = command.stringValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(AccountingRuleJsonInputParams.DESCRIPTION.getValue())) {
this.description = newValue;
} else if (paramName.equals(AccountingRuleJsonInputParams.NAME.getValue())) {
this.name = newValue;
}
}
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
final boolean propertyToBeUpdated) {
if (command.isChangeInBooleanParameterNamed(paramName, propertyToBeUpdated)) {
final Boolean newValue = command.booleanObjectValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(AccountingRuleJsonInputParams.SYSTEM_DEFINED.getValue())) {
this.systemDefined = newValue;
} else if (paramName.equals(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())) {
if (this.accountToCredit == null) {
this.allowMultipleCreditEntries = newValue;
}
} else if (paramName.equals(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())) {
if (this.accountToDebit == null) {
this.allowMultipleDebitEntries = newValue;
}
}
}
}
private void handlePropertyUpdate(final JsonCommand command, final Map<String, Object> actualChanges, final String paramName,
final Long propertyToBeUpdated) {
if (command.isChangeInLongParameterNamed(paramName, propertyToBeUpdated)) {
final Long newValue = command.longValueOfParameterNamed(paramName);
actualChanges.put(paramName, newValue);
// now update actual property
if (paramName.equals(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) {
// do nothing as this is a nested property
} else if (paramName.equals(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) {
// do nothing as this is a nested property
} else if (paramName.equals(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) {
// do nothing as this is a nested property
}
}
}
public void updateAccountingRuleForTags(final List<AccountingTagRule> debitAccountingTagRules) {
for (final AccountingTagRule accountingTagRule : debitAccountingTagRules) {
accountingTagRule.setAccountingRule(this);
this.accountingTagRules.add(accountingTagRule);
}
}
public void updateTags(final JournalEntryType type) {
final Set<AccountingTagRule> existedCreditTags = new HashSet<>();
final Set<AccountingTagRule> existedDebitTags = new HashSet<>();
for (final AccountingTagRule accountingTagRule : this.accountingTagRules) {
if (JournalEntryType.fromInt(accountingTagRule.getAccountType()).isCreditType()) {
existedCreditTags.add(accountingTagRule);
} else if (JournalEntryType.fromInt(accountingTagRule.getAccountType()).isDebitType()) {
existedDebitTags.add(accountingTagRule);
}
}
if (type.isCreditType()) {
this.accountingTagRules.retainAll(existedCreditTags);
} else if (type.isDebitType()) {
this.accountingTagRules.retainAll(existedDebitTags);
}
}
public Set<AccountingTagRule> getAccountingTagRulesByType(final JournalEntryType type) {
final Set<AccountingTagRule> existedTags = new HashSet<>();
for (final AccountingTagRule accountingTagRule : this.accountingTagRules) {
if (accountingTagRule.getAccountType().equals(type.getValue())) {
existedTags.add(accountingTagRule);
}
}
return existedTags;
}
public void removeOldTags(final List<AccountingTagRule> accountsToRemove) {
this.accountingTagRules.removeAll(accountsToRemove);
}
}
@@ -1,30 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.domain;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface AccountingRuleRepository extends JpaRepository<AccountingRule, Long>, JpaSpecificationExecutor<AccountingRule> {
@Query("select accountingRule from AccountingRule accountingRule where accountingRule.office is null or accountingRule.office.id =:officeId")
AccountingRule getAccountingRuleByOfficeId(@Param("officeId") Long officeId);
}
@@ -1,44 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.domain;
import org.apache.fineract.accounting.rule.exception.AccountingRuleNotFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* <p>
* Wrapper for {@link AccountingRuleRepository} .
* </p>
*/
@Service
public class AccountingRuleRepositoryWrapper {
private final AccountingRuleRepository repository;
@Autowired
public AccountingRuleRepositoryWrapper(final AccountingRuleRepository repository) {
this.repository = repository;
}
public AccountingRule findOneWithNotFoundDetection(final Long id) {
return this.repository.findById(id).orElseThrow(() -> new AccountingRuleNotFoundException(id));
}
}
@@ -1,58 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.domain;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
@Getter
@Setter
@NoArgsConstructor
@Accessors(chain = true)
@Entity
@Table(name = "acc_rule_tags", uniqueConstraints = {
@UniqueConstraint(columnNames = { "acc_rule_id", "tag_id", "acc_type_enum" }, name = "UNIQUE_ACCOUNT_RULE_TAGS") })
public class AccountingTagRule extends AbstractPersistableCustom {
@ManyToOne
@JoinColumn(name = "acc_rule_id", nullable = false)
private AccountingRule accountingRule;
@ManyToOne
@JoinColumn(name = "tag_id", nullable = false)
private CodeValue tagId;
@Column(name = "acc_type_enum", nullable = false)
private Integer accountType;
public static AccountingTagRule create(final CodeValue tagId, final Integer accountType) {
return new AccountingTagRule().setTagId(tagId).setAccountType(accountType);
}
}
@@ -1,30 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class AccountingRuleDataException extends AbstractPlatformDomainRuleException {
public AccountingRuleDataException(final String debitOrCreditAccount, final String debitOrCreditTags) {
super("error.msg.accounting.rule." + debitOrCreditAccount + ".or." + debitOrCreditTags + ".required",
debitOrCreditAccount + " or " + debitOrCreditTags + " required", debitOrCreditAccount, debitOrCreditTags);
}
}
@@ -1,36 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when an Accounting rule with a given name already exists
*/
public class AccountingRuleDuplicateException extends AbstractPlatformDomainRuleException {
public AccountingRuleDuplicateException(final String name) {
super("error.msg.accounting.rule.duplicate", "An accounting rule with the name " + name + " already exists" + name);
}
public AccountingRuleDuplicateException() {
super("error.msg.accounting.rule.tag.duplicate", "The accounting rule already have the tags which you defined");
}
}
@@ -1,33 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.exception;
import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when a GL Closure Delte command is invalid
*/
public class AccountingRuleInvalidDeleteException extends AbstractPlatformDomainRuleException {
public AccountingRuleInvalidDeleteException(final Long officeId, final String officeName, final LocalDate latestclosureDate) {
super("error.msg.glclosure.invalid.delete", "The latest closure for office with Id " + officeId + " and name " + officeName
+ " is on " + latestclosureDate.toString() + ", please delete this closure first", latestclosureDate);
}
}
@@ -1,56 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.exception;
import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
* A {@link RuntimeException} thrown when a GL Closure is Invalid
*/
public class AccountingRuleInvalidException extends AbstractPlatformDomainRuleException {
/*** enum of reasons for invalid Accounting Closure **/
public enum GlClosureInvalidReason {
FUTURE_DATE, ACCOUNTING_CLOSED;
public String errorMessage() {
if (name().toString().equalsIgnoreCase("FUTURE_DATE")) {
return "Accounting closures cannot be made for a future date";
} else if (name().toString().equalsIgnoreCase("ACCOUNTING_CLOSED")) {
return "Accounting Closure for this branch has already been defined for a greater date";
}
return name().toString();
}
public String errorCode() {
if (name().toString().equalsIgnoreCase("FUTURE_DATE")) {
return "error.msg.glclosure.invalid.future.date";
} else if (name().toString().equalsIgnoreCase("ACCOUNTING_CLOSED")) {
return "error.msg.glclosure.invalid.accounting.closed";
}
return name().toString();
}
}
public AccountingRuleInvalidException(final GlClosureInvalidReason reason, final LocalDate date) {
super(reason.errorCode(), reason.errorMessage(), date);
}
}
@@ -1,36 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException;
/**
* A {@link RuntimeException} thrown when Accounting rule resources are not found.
*/
public class AccountingRuleNotFoundException extends AbstractPlatformResourceNotFoundException {
public AccountingRuleNotFoundException(final Long id) {
super("error.msg.accounting.rule.id.invalid", "Accounting Rule with identifier " + id + " does not exist", id);
}
public AccountingRuleNotFoundException(final Long id, EmptyResultDataAccessException e) {
super("error.msg.accounting.rule.id.invalid", "Accounting Rule with identifier " + id + " does not exist", id, e);
}
}
@@ -1,42 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.handler;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.rule.service.AccountingRuleWritePlatformService;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "ACCOUNTINGRULE", action = "CREATE")
@RequiredArgsConstructor
public class CreateAccountingRuleCommandHandler implements NewCommandSourceHandler {
private final AccountingRuleWritePlatformService writePlatformService;
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.createAccountingRule(command);
}
}
@@ -1,42 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.handler;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.rule.service.AccountingRuleWritePlatformService;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "ACCOUNTINGRULE", action = "DELETE")
@RequiredArgsConstructor
public class DeleteAccountingRuleCommandHandler implements NewCommandSourceHandler {
private final AccountingRuleWritePlatformService writePlatformService;
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.deleteAccountingRule(command.entityId());
}
}
@@ -1,42 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.handler;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.rule.service.AccountingRuleWritePlatformService;
import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@CommandType(entity = "ACCOUNTINGRULE", action = "UPDATE")
@RequiredArgsConstructor
public class UpdateAccountingRuleCommandHandler implements NewCommandSourceHandler {
private final AccountingRuleWritePlatformService writePlatformService;
@Transactional
@Override
public CommandProcessingResult processCommand(final JsonCommand command) {
return this.writePlatformService.updateAccountingRule(command.entityId(), command);
}
}
@@ -1,224 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.serialization;
import com.google.gson.JsonElement;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.rule.api.AccountingRuleJsonInputParams;
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
@Component
@RequiredArgsConstructor
public class AccountingRuleCommandFromApiJsonDeserializer {
/**
* The parameters supported for this command.
*/
private final Set<String> supportedParameters = AccountingRuleJsonInputParams.getAllValues();
private final FromJsonHelper fromApiJsonHelper;
public void validateForCreate(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 List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("AccountingRule");
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Long accountToDebitId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(),
element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()).value(accountToDebitId)
.ignoreIfNull().integerGreaterThanZero();
final Long accountToCreditId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()).value(accountToCreditId)
.ignoreIfNull().integerGreaterThanZero();
final Long officeId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.OFFICE_ID.getValue()).value(officeId).notNull()
.integerGreaterThanZero();
final String name = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.NAME.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.NAME.getValue()).value(name).notBlank().notExceedingLengthOf(100);
final String description = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.DESCRIPTION.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DESCRIPTION.getValue()).value(description).ignoreIfNull()
.notExceedingLengthOf(500);
final String[] creditTags = this.fromApiJsonHelper.extractArrayNamed(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue(),
element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue()).value(creditTags).ignoreIfNull()
.arrayNotEmpty();
validateCreditOrDebitTagArray(creditTags, baseDataValidator, AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
final String[] debitTags = this.fromApiJsonHelper.extractArrayNamed(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue(),
element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue()).value(debitTags).ignoreIfNull()
.arrayNotEmpty();
validateCreditOrDebitTagArray(debitTags, baseDataValidator, AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
if (creditTags == null && accountToCreditId == null) {
final String creditTag = AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue();
final String creditAccount = AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue();
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(creditAccount).append(".or.")
.append(creditTag).append(".required");
final StringBuilder defaultUserMessage = new StringBuilder("The parameter ").append(creditAccount).append(" or")
.append(creditTag).append(" required");
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultUserMessage.toString(),
creditAccount + "," + creditTag, new Object[] { creditAccount, creditTag });
dataValidationErrors.add(error);
}
if (debitTags == null && accountToDebitId == null) {
final String debitTag = AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue();
final String debitAccount = AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue();
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(debitAccount).append(".or.")
.append(debitTag).append(".required");
final StringBuilder defaultUserMessage = new StringBuilder("The parameter ").append(debitAccount).append(" or").append(debitTag)
.append(" required");
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultUserMessage.toString(),
debitAccount + "," + debitTag, new Object[] { debitAccount, debitTag });
dataValidationErrors.add(error);
}
final String allowMultipleCredits = this.fromApiJsonHelper
.extractStringNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())
.value(allowMultipleCredits).ignoreIfNull().notBlank();
final Boolean allowMultipleCreditEntries = this.fromApiJsonHelper
.extractBooleanNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())
.value(allowMultipleCreditEntries).ignoreIfNull();
final String allowMultipleDebits = this.fromApiJsonHelper
.extractStringNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())
.value(allowMultipleDebits).ignoreIfNull().notBlank();
final Boolean allowMultipleDebitEntries = this.fromApiJsonHelper
.extractBooleanNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())
.value(allowMultipleDebitEntries).ignoreIfNull();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors);
}
}
public void validateCreditOrDebitTagArray(final String[] creditOrDebitTagArray, final DataValidatorBuilder baseDataValidator,
final String parameter) {
if (creditOrDebitTagArray != null && !ObjectUtils.isEmpty(creditOrDebitTagArray)) {
for (final String tag : creditOrDebitTagArray) {
baseDataValidator.reset().parameter(parameter).value(tag).ignoreIfNull().notBlank().longGreaterThanZero();
}
}
}
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 List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("AccountingRule");
final JsonElement element = this.fromApiJsonHelper.parse(json);
final Long accountToDebitId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(),
element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue()).value(accountToDebitId)
.ignoreIfNull().notBlank().integerGreaterThanZero();
final Long accountToCreditId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue()).value(accountToCreditId)
.ignoreIfNull().notBlank().integerGreaterThanZero();
final Long officeId = this.fromApiJsonHelper.extractLongNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.OFFICE_ID.getValue()).value(officeId).ignoreIfNull()
.integerGreaterThanZero();
final String name = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.NAME.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.NAME.getValue()).value(name).ignoreIfNull().notBlank()
.notExceedingLengthOf(100);
final String description = this.fromApiJsonHelper.extractStringNamed(AccountingRuleJsonInputParams.DESCRIPTION.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DESCRIPTION.getValue()).value(description).ignoreIfNull()
.notExceedingLengthOf(500);
final String[] creditTags = this.fromApiJsonHelper.extractArrayNamed(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue(),
element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue()).value(creditTags).ignoreIfNull()
.arrayNotEmpty();
validateCreditOrDebitTagArray(creditTags, baseDataValidator, AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
final String[] debitTags = this.fromApiJsonHelper.extractArrayNamed(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue(),
element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue()).value(debitTags).ignoreIfNull()
.arrayNotEmpty();
validateCreditOrDebitTagArray(debitTags, baseDataValidator, AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
final String allowMultipleCredits = this.fromApiJsonHelper
.extractStringNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())
.value(allowMultipleCredits).ignoreIfNull().notBlank();
final Boolean allowMultipleCreditEntries = this.fromApiJsonHelper
.extractBooleanNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())
.value(allowMultipleCreditEntries).ignoreIfNull();
final String allowMultipleDebits = this.fromApiJsonHelper
.extractStringNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())
.value(allowMultipleDebits).ignoreIfNull().notBlank();
final Boolean allowMultipleDebitEntries = this.fromApiJsonHelper
.extractBooleanNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue(), element);
baseDataValidator.reset().parameter(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())
.value(allowMultipleDebitEntries).ignoreIfNull();
throwExceptionIfValidationWarningsExist(dataValidationErrors);
}
}
@@ -1,30 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.service;
import java.util.List;
import org.apache.fineract.accounting.rule.data.AccountingRuleData;
public interface AccountingRuleReadPlatformService {
List<AccountingRuleData> retrieveAllAccountingRules(String hierarchySearchString, boolean isAssociationParametersExists);
AccountingRuleData retrieveAccountingRuleById(Long accountingRuleId);
}
@@ -1,204 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.accounting.common.AccountingEnumerations;
import org.apache.fineract.accounting.glaccount.data.GLAccountDataForLookup;
import org.apache.fineract.accounting.glaccount.service.GLAccountReadPlatformService;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
import org.apache.fineract.accounting.rule.data.AccountingRuleData;
import org.apache.fineract.accounting.rule.data.AccountingTagRuleData;
import org.apache.fineract.accounting.rule.exception.AccountingRuleNotFoundException;
import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.domain.JdbcSupport;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
@RequiredArgsConstructor
public class AccountingRuleReadPlatformServiceImpl implements AccountingRuleReadPlatformService {
private final JdbcTemplate jdbcTemplate;
private final GLAccountReadPlatformService glAccountReadPlatformService;
private static final class AccountingRuleDataExtractor implements ResultSetExtractor<Map<Long, AccountingRuleData>> {
private final String schemaSql;
private final JdbcTemplate jdbcTemplate;
private final GLAccountReadPlatformService glAccountReadPlatformService;
private final boolean isAssociationParametersExists;
AccountingRuleDataExtractor(final JdbcTemplate jdbcTemplate, final GLAccountReadPlatformService glAccountReadPlatformService,
final boolean isAssociationParametersExists) {
this.jdbcTemplate = jdbcTemplate;
this.glAccountReadPlatformService = glAccountReadPlatformService;
this.isAssociationParametersExists = isAssociationParametersExists;
final StringBuilder sqlBuilder = new StringBuilder(400);
sqlBuilder.append(" rule.id as id,rule.name as name, rule.office_id as officeId,office.name as officeName,").append(
" rule.description as description, rule.system_defined as systemDefined, rule.allow_multiple_debits as allowMultipleDebitEntries, rule.allow_multiple_credits as allowMultipleCreditEntries, ")
.append("debitAccount.id AS debitAccountId, debitAccount.name as debitAccountName, debitAccount.gl_code as debitAccountGLCode, ")
.append("creditAccount.id AS creditAccountId, creditAccount.name as creditAccountName, creditAccount.gl_code as creditAccountGLCode")
.append(" from m_office AS office, acc_accounting_rule AS rule ")
.append(" LEFT JOIN acc_gl_account AS creditAccount ON rule.credit_account_id = creditAccount.id ")
.append(" LEFT JOIN acc_gl_account AS debitAccount ON rule.debit_account_id = debitAccount.id ")
.append("WHERE office.id=rule.office_id ");
this.schemaSql = sqlBuilder.toString();
}
public String schema() {
return this.schemaSql;
}
@Override
public Map<Long, AccountingRuleData> extractData(final ResultSet rs) throws SQLException, DataAccessException {
final Map<Long, AccountingRuleData> extractedData = new HashMap<>();
while (rs.next()) {
final Long id = rs.getLong("id");
AccountingRuleData accountingRuleData = extractedData.get(id);
if (accountingRuleData == null) {
final Long officeId = JdbcSupport.getLong(rs, "officeId");
final String officeName = rs.getString("officeName");
final String name = rs.getString("name");
final String description = rs.getString("description");
final Long accountToDebitId = JdbcSupport.getLong(rs, "debitAccountId");
final Long accountToCreditId = JdbcSupport.getLong(rs, "creditAccountId");
final boolean systemDefined = rs.getBoolean("systemDefined");
final boolean allowMultipleDebitEntries = rs.getBoolean("allowMultipleDebitEntries");
final boolean allowMultipleCreditEntries = rs.getBoolean("allowMultipleCreditEntries");
final String debitAccountName = rs.getString("debitAccountName");
final String creditAccountName = rs.getString("creditAccountName");
final String debitAccountGLCode = rs.getString("debitAccountGLCode");
final String creditAccountGLCode = rs.getString("creditAccountGLCode");
final List<AccountingTagRuleData> creditTags;
final List<AccountingTagRuleData> debitTags;
final List<GLAccountDataForLookup> creditAccounts;
final List<GLAccountDataForLookup> debitAccounts;
if (accountToCreditId == null) {
creditTags = !this.isAssociationParametersExists ? getCreditOrDebitTags(id, JournalEntryType.CREDIT.getValue())
: null;
creditAccounts = this.isAssociationParametersExists
? this.glAccountReadPlatformService.retrieveAccountsByTagId(id, JournalEntryType.CREDIT.getValue())
: null;
} else {
creditTags = null;
final GLAccountDataForLookup creditAccount = new GLAccountDataForLookup().setId(accountToCreditId)
.setName(creditAccountName).setGlCode(creditAccountGLCode);
creditAccounts = new ArrayList<>(Arrays.asList(creditAccount));
}
if (accountToDebitId == null) {
debitTags = !this.isAssociationParametersExists ? getCreditOrDebitTags(id, JournalEntryType.DEBIT.getValue())
: null;
debitAccounts = this.isAssociationParametersExists
? this.glAccountReadPlatformService.retrieveAccountsByTagId(id, JournalEntryType.DEBIT.getValue())
: null;
} else {
debitTags = null;
final GLAccountDataForLookup debitAccount = new GLAccountDataForLookup().setId(accountToDebitId)
.setName(debitAccountName).setGlCode(debitAccountGLCode);
debitAccounts = new ArrayList<>(Arrays.asList(debitAccount));
}
accountingRuleData = new AccountingRuleData().setId(id).setOfficeId(officeId).setOfficeName(officeName).setName(name)
.setDescription(description).setSystemDefined(systemDefined)
.setAllowMultipleDebitEntries(allowMultipleDebitEntries)
.setAllowMultipleCreditEntries(allowMultipleCreditEntries).setCreditTags(creditTags).setDebitTags(debitTags)
.setCreditAccounts(creditAccounts).setDebitAccounts(debitAccounts);
}
extractedData.put(id, accountingRuleData);
}
return extractedData;
}
private List<AccountingTagRuleData> getCreditOrDebitTags(final Long creditOrDebitAccount, final Integer transactionType) {
final AccountingTagRuleDataMapper mapper = new AccountingTagRuleDataMapper();
final String taggedAccountsSchema = "Select " + mapper.taggedAccountSchema() + " where rule.id = ? and tag.acc_type_enum=?";
return this.jdbcTemplate.query(taggedAccountsSchema, mapper, new Object[] { creditOrDebitAccount, transactionType }); // NOSONAR
}
}
@Override
public List<AccountingRuleData> retrieveAllAccountingRules(final String hierarchySearchString,
final boolean isAssociationParametersExists) {
final AccountingRuleDataExtractor resultSetExtractor = new AccountingRuleDataExtractor(this.jdbcTemplate,
this.glAccountReadPlatformService, isAssociationParametersExists);
Object[] arguments = new Object[] {};
String sql = "select " + resultSetExtractor.schema() + " and system_defined=false ";
if (hierarchySearchString != null) {
sql = sql + " and office.hierarchy like ?";
arguments = new Object[] { hierarchySearchString };
}
sql = sql + " order by rule.id asc";
final Map<Long, AccountingRuleData> extractedData = this.jdbcTemplate.query(sql, resultSetExtractor, arguments); // NOSONAR
return new ArrayList<>(extractedData.values());
}
@Override
public AccountingRuleData retrieveAccountingRuleById(final Long accountingRuleId) {
try {
final AccountingRuleDataExtractor resultSetExtractor = new AccountingRuleDataExtractor(this.jdbcTemplate,
this.glAccountReadPlatformService, false);
final String sql = "select " + resultSetExtractor.schema() + " and rule.id = ?";
final Map<Long, AccountingRuleData> extractedData = this.jdbcTemplate.query(sql, resultSetExtractor, // NOSONAR
new Object[] { accountingRuleId });
final AccountingRuleData accountingRuleData = extractedData.get(accountingRuleId);
if (accountingRuleData == null) {
throw new AccountingRuleNotFoundException(accountingRuleId);
}
return accountingRuleData;
} catch (final EmptyResultDataAccessException e) {
throw new AccountingRuleNotFoundException(accountingRuleId, e);
}
}
private static final class AccountingTagRuleDataMapper implements RowMapper<AccountingTagRuleData> {
public String taggedAccountSchema() {
return " tag.id as id,tag.tag_id as tagId, tag.acc_type_enum as transactionType, cv.code_value as tagName from m_code_value cv join acc_rule_tags tag on tag.tag_id=cv.id "
+ "join acc_accounting_rule rule on tag.acc_rule_id=rule.id ";
}
@Override
public AccountingTagRuleData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id");
final Long tagId = rs.getLong("tagId");
final Integer transactionType = JdbcSupport.getInteger(rs, "transactionType");
final String tagName = rs.getString("tagName");
final CodeValueData tag = CodeValueData.instance(tagId, tagName);
final EnumOptionData transactionTypeEnum = AccountingEnumerations.journalEntryType(transactionType);
return new AccountingTagRuleData().setId(id).setTag(tag).setTransactionType(transactionTypeEnum);
}
}
}
@@ -1,32 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface AccountingRuleWritePlatformService {
CommandProcessingResult createAccountingRule(JsonCommand command);
CommandProcessingResult updateAccountingRule(Long accountingRuleId, JsonCommand command);
CommandProcessingResult deleteAccountingRule(Long accountingRuleId);
}
@@ -1,338 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.accounting.closure.api.GLClosureJsonInputParams;
import org.apache.fineract.accounting.glaccount.domain.GLAccount;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.apache.fineract.accounting.journalentry.domain.JournalEntryType;
import org.apache.fineract.accounting.rule.api.AccountingRuleJsonInputParams;
import org.apache.fineract.accounting.rule.domain.AccountingRule;
import org.apache.fineract.accounting.rule.domain.AccountingRuleRepository;
import org.apache.fineract.accounting.rule.domain.AccountingRuleRepositoryWrapper;
import org.apache.fineract.accounting.rule.domain.AccountingTagRule;
import org.apache.fineract.accounting.rule.exception.AccountingRuleDataException;
import org.apache.fineract.accounting.rule.exception.AccountingRuleDuplicateException;
import org.apache.fineract.accounting.rule.serialization.AccountingRuleCommandFromApiJsonDeserializer;
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
import org.apache.fineract.infrastructure.codes.exception.CodeValueNotFoundException;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.transaction.annotation.Transactional;
@RequiredArgsConstructor
@Slf4j
public class AccountingRuleWritePlatformServiceJpaRepositoryImpl implements AccountingRuleWritePlatformService {
private final AccountingRuleRepositoryWrapper accountingRuleRepositoryWrapper;
private final AccountingRuleRepository accountingRuleRepository;
private final GLAccountRepositoryWrapper accountRepositoryWrapper;
private final OfficeRepositoryWrapper officeRepositoryWrapper;
private final AccountingRuleCommandFromApiJsonDeserializer fromApiJsonDeserializer;
private final CodeValueRepository codeValueRepository;
/**
* @param command
* @param dve
*/
private void handleAccountingRuleIntegrityIssues(final JsonCommand command, final Throwable realCause,
final NonTransientDataAccessException dve) {
if (realCause.getMessage().contains("accounting_rule_name_unique")) {
throw new AccountingRuleDuplicateException(command.stringValueOfParameterNamed(AccountingRuleJsonInputParams.NAME.getValue()));
} else if (realCause.getMessage().contains("UNIQUE_ACCOUNT_RULE_TAGS")) {
throw new AccountingRuleDuplicateException();
}
log.error("Error occured.", dve);
throw ErrorHandler.getMappable(dve, "error.msg.accounting.rule.unknown.data.integrity.issue",
"Unknown data integrity issue with resource Accounting Rule: " + realCause.getMessage());
}
@Transactional
@Override
public CommandProcessingResult createAccountingRule(final JsonCommand command) {
try {
this.fromApiJsonDeserializer.validateForCreate(command.json());
// check office is valid
final Long officeId = command.longValueOfParameterNamed(GLClosureJsonInputParams.OFFICE_ID.getValue());
Office office = null;
if (officeId != null) {
office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
}
final AccountingRule accountingRule = assembleAccountingRuleAndTags(office, command);
this.accountingRuleRepository.saveAndFlush(accountingRule);
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withOfficeId(officeId)
.withEntityId(accountingRule.getId()).build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
final Throwable throwable = dve.getMostSpecificCause();
handleAccountingRuleIntegrityIssues(command, throwable, dve);
return CommandProcessingResult.empty();
}
}
private AccountingRule assembleAccountingRuleAndTags(final Office office, final JsonCommand command) {
// get the GL Accounts or tags to Debit and Credit
final String[] debitTags = command.arrayValueOfParameterNamed(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
final String[] creditTags = command.arrayValueOfParameterNamed(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
final Set<String> incomingDebitTags = debitTags == null ? new HashSet<String>() : new HashSet<>(Arrays.asList(debitTags));
final Set<String> incomingCreditTags = creditTags == null ? new HashSet<String>() : new HashSet<>(Arrays.asList(creditTags));
final Long accountToDebitId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue());
final Long accountToCreditId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue());
boolean allowMultipleCreditEntries = false;
boolean allowMultipleDebitEntries = false;
GLAccount debitAccount = null;
GLAccount creditAccount = null;
List<AccountingTagRule> accountingTagRules = new ArrayList<>();
if ((accountToDebitId != null && debitTags != null) || (accountToDebitId == null && debitTags == null)) {
throw new AccountingRuleDataException(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(),
AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
} else if (accountToDebitId != null) {
debitAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToDebitId);
} else if (debitTags != null) {
accountingTagRules = saveDebitOrCreditTags(incomingDebitTags, JournalEntryType.DEBIT, accountingTagRules);
allowMultipleDebitEntries = command
.booleanPrimitiveValueOfParameterNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue());
}
if ((accountToCreditId != null && creditTags != null) || (accountToCreditId == null && creditTags == null)) {
throw new AccountingRuleDataException(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
} else if (accountToCreditId != null) {
creditAccount = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId);
} else if (creditTags != null) {
accountingTagRules = saveDebitOrCreditTags(incomingCreditTags, JournalEntryType.CREDIT, accountingTagRules);
allowMultipleCreditEntries = command
.booleanPrimitiveValueOfParameterNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue());
}
final AccountingRule accountingRule = AccountingRule.fromJson(office, debitAccount, creditAccount, command,
allowMultipleCreditEntries, allowMultipleDebitEntries);
accountingRule.updateAccountingRuleForTags(accountingTagRules);
return accountingRule;
}
@Transactional
@Override
public CommandProcessingResult updateAccountingRule(final Long accountingRuleId, final JsonCommand command) {
try {
this.fromApiJsonDeserializer.validateForUpdate(command.json());
Long officeId = null;
if (command.parameterExists(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) {
officeId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.OFFICE_ID.getValue());
}
Long accountToDebitId = null;
if (command.parameterExists(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) {
accountToDebitId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue());
}
Long accountToCreditId = null;
if (command.parameterExists(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) {
accountToCreditId = command.longValueOfParameterNamed(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue());
}
String[] debitTags = null;
if (command.parameterExists(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue())) {
debitTags = command.arrayValueOfParameterNamed(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
}
String[] creditTags = null;
if (command.parameterExists(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue())) {
creditTags = command.arrayValueOfParameterNamed(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
}
if (accountToDebitId != null && debitTags != null) {
throw new AccountingRuleDataException(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue(),
AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue());
}
if (accountToCreditId != null && creditTags != null) {
throw new AccountingRuleDataException(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue(),
AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue());
}
boolean allowMultipleCreditEntries = false;
if (command.parameterExists(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue())) {
allowMultipleCreditEntries = command
.booleanPrimitiveValueOfParameterNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_CREDIT_ENTRIES.getValue());
}
boolean allowMultipleDebitEntries = false;
if (command.parameterExists(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue())) {
allowMultipleDebitEntries = command
.booleanPrimitiveValueOfParameterNamed(AccountingRuleJsonInputParams.ALLOW_MULTIPLE_DEBIT_ENTRIES.getValue());
}
final AccountingRule accountingRule = this.accountingRuleRepositoryWrapper.findOneWithNotFoundDetection(accountingRuleId);
final Map<String, Object> changesOnly = accountingRule.update(command);
if (accountToDebitId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.ACCOUNT_TO_DEBIT.getValue())) {
final GLAccount accountToDebit = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToDebitId);
accountingRule.setAccountToDebit(accountToDebit);
accountingRule.updateTags(JournalEntryType.CREDIT);
}
if (accountToCreditId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.ACCOUNT_TO_CREDIT.getValue())) {
final GLAccount accountToCredit = this.accountRepositoryWrapper.findOneWithNotFoundDetection(accountToCreditId);
accountingRule.setAccountToCredit(accountToCredit);
accountingRule.updateTags(JournalEntryType.DEBIT);
}
if (creditTags != null && creditTags.length > 0
&& command.parameterExists(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue())) {
final Set<String> creditTagsToAdd = determineCreditTagToAddAndRemoveOldTags(creditTags, JournalEntryType.CREDIT,
accountingRule);
if (!creditTagsToAdd.isEmpty()) {
List<AccountingTagRule> accountingTagRules = new ArrayList<>();
accountingTagRules = saveDebitOrCreditTags(creditTagsToAdd, JournalEntryType.CREDIT, accountingTagRules);
accountingRule.updateAccountingRuleForTags(accountingTagRules);
accountingRule.setAccountToCredit(null);
if (allowMultipleCreditEntries) {
accountingRule.setAllowMultipleCreditEntries(allowMultipleCreditEntries);
}
changesOnly.put(AccountingRuleJsonInputParams.CREDIT_ACCOUNT_TAGS.getValue(), creditTagsToAdd);
}
}
if (debitTags != null && debitTags.length > 0
&& command.parameterExists(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue())) {
final Set<String> debitTagsToAdd = determineCreditTagToAddAndRemoveOldTags(debitTags, JournalEntryType.DEBIT,
accountingRule);
if (!debitTagsToAdd.isEmpty()) {
List<AccountingTagRule> accountingTagRules = new ArrayList<>();
accountingTagRules = saveDebitOrCreditTags(debitTagsToAdd, JournalEntryType.DEBIT, accountingTagRules);
accountingRule.updateAccountingRuleForTags(accountingTagRules);
accountingRule.setAccountToDebit(null);
if (allowMultipleDebitEntries) {
accountingRule.setAllowMultipleDebitEntries(allowMultipleDebitEntries);
}
changesOnly.put(AccountingRuleJsonInputParams.DEBIT_ACCOUNT_TAGS.getValue(), debitTagsToAdd);
}
}
if (officeId != null && changesOnly.containsKey(AccountingRuleJsonInputParams.OFFICE_ID.getValue())) {
final Office userOffice = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
accountingRule.setOffice(userOffice);
}
if (!changesOnly.isEmpty()) {
this.accountingRuleRepository.saveAndFlush(accountingRule);
}
return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(accountingRule.getId())
.with(changesOnly).build();
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
final Throwable throwable = dve.getMostSpecificCause();
handleAccountingRuleIntegrityIssues(command, throwable, dve);
return CommandProcessingResult.empty();
}
}
private Set<String> determineCreditTagToAddAndRemoveOldTags(final String[] creditOrDebitTags, final JournalEntryType type,
final AccountingRule accountingRule) {
final Set<String> incomingTags = new HashSet<>(Arrays.asList(creditOrDebitTags));
final Set<AccountingTagRule> existingTags = accountingRule.getAccountingTagRulesByType(type);
final Set<String> existingTagIds = retrieveExistingTagIds(existingTags);
final Set<String> tagsToAdd = new HashSet<>();
final Set<String> tagsToRemove = existingTagIds;
final Map<Long, AccountingTagRule> accountsToRemove = new HashMap<>();
for (final String tagId : incomingTags) {
if (existingTagIds.contains(tagId)) {
tagsToRemove.remove(tagId);
} else {
tagsToAdd.add(tagId);
}
}
if (!tagsToRemove.isEmpty()) {
for (final String tagId : tagsToRemove) {
for (final AccountingTagRule accountingTagRule : existingTags) {
if (tagId.equals(accountingTagRule.getTagId().getId().toString())) {
accountsToRemove.put(accountingTagRule.getId(), accountingTagRule);
}
}
}
accountingRule.removeOldTags(new ArrayList<>(accountsToRemove.values()));
}
return tagsToAdd;
}
private Set<String> retrieveExistingTagIds(final Set<AccountingTagRule> existingCreditTags) {
final Set<String> existingCreditTagIds = new HashSet<>();
for (final AccountingTagRule accountingTagRule : existingCreditTags) {
existingCreditTagIds.add(accountingTagRule.getTagId().getId().toString());
}
return existingCreditTagIds;
}
@Transactional
@Override
public CommandProcessingResult deleteAccountingRule(final Long accountingRuleId) {
final AccountingRule accountingRule = this.accountingRuleRepositoryWrapper.findOneWithNotFoundDetection(accountingRuleId);
this.accountingRuleRepository.delete(accountingRule);
return new CommandProcessingResultBuilder().withEntityId(accountingRule.getId()).build();
}
private List<AccountingTagRule> saveDebitOrCreditTags(final Set<String> creditOrDebitTagArray, final JournalEntryType transactionType,
final List<AccountingTagRule> accountingTagRules) {
for (final String creditOrDebitTag : creditOrDebitTagArray) {
if (creditOrDebitTag != null && StringUtils.isNotBlank(creditOrDebitTag)) {
final Long creditOrDebitTagIdLongValue = Long.valueOf(creditOrDebitTag);
final CodeValue creditOrDebitAccount = this.codeValueRepository.findById(creditOrDebitTagIdLongValue)
.orElseThrow(() -> new CodeValueNotFoundException(creditOrDebitTagIdLongValue));
final AccountingTagRule accountingTagRule = AccountingTagRule.create(creditOrDebitAccount, transactionType.getValue());
accountingTagRules.add(accountingTagRule);
}
}
return accountingTagRules;
}
}
@@ -1,57 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.accounting.rule.starter;
import org.apache.fineract.accounting.glaccount.domain.GLAccountRepositoryWrapper;
import org.apache.fineract.accounting.glaccount.service.GLAccountReadPlatformService;
import org.apache.fineract.accounting.rule.domain.AccountingRuleRepository;
import org.apache.fineract.accounting.rule.domain.AccountingRuleRepositoryWrapper;
import org.apache.fineract.accounting.rule.serialization.AccountingRuleCommandFromApiJsonDeserializer;
import org.apache.fineract.accounting.rule.service.AccountingRuleReadPlatformService;
import org.apache.fineract.accounting.rule.service.AccountingRuleReadPlatformServiceImpl;
import org.apache.fineract.accounting.rule.service.AccountingRuleWritePlatformService;
import org.apache.fineract.accounting.rule.service.AccountingRuleWritePlatformServiceJpaRepositoryImpl;
import org.apache.fineract.infrastructure.codes.domain.CodeValueRepository;
import org.apache.fineract.organisation.office.domain.OfficeRepositoryWrapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
@Configuration
public class AccountingRuleConfiguration {
@Bean
@ConditionalOnMissingBean(AccountingRuleReadPlatformService.class)
public AccountingRuleReadPlatformService accountingRuleReadPlatformService(JdbcTemplate jdbcTemplate,
GLAccountReadPlatformService glAccountReadPlatformService) {
return new AccountingRuleReadPlatformServiceImpl(jdbcTemplate, glAccountReadPlatformService);
}
@Bean
@ConditionalOnMissingBean(AccountingRuleWritePlatformService.class)
public AccountingRuleWritePlatformService accountingRuleWritePlatformService(
AccountingRuleRepositoryWrapper accountingRuleRepositoryWrapper, AccountingRuleRepository accountingRuleRepository,
GLAccountRepositoryWrapper accountRepositoryWrapper, OfficeRepositoryWrapper officeRepositoryWrapper,
AccountingRuleCommandFromApiJsonDeserializer fromApiJsonDeserializer, CodeValueRepository codeValueRepository) {
return new AccountingRuleWritePlatformServiceJpaRepositoryImpl(accountingRuleRepositoryWrapper, accountingRuleRepository,
accountRepositoryWrapper, officeRepositoryWrapper, fromApiJsonDeserializer, codeValueRepository);
}
}
@@ -1,61 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.organisation.provisioning.data;
import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* Immutable object representing organization's provision category data
*/
@Data
@NoArgsConstructor
@Accessors(chain = true)
public class ProvisioningCategoryData implements Comparable<ProvisioningCategoryData>, Serializable {
private static final long serialVersionUID = 1L;
private Long id;
private String categoryName;
private String categoryDescription;
@Override
public boolean equals(final Object obj) {
if (!(obj instanceof ProvisioningCategoryData)) {
return false;
}
final ProvisioningCategoryData provisionCategoryData = (ProvisioningCategoryData) obj;
return provisionCategoryData.id.equals(this.id);
}
@Override
public int hashCode() {
return this.id.hashCode();
}
@Override
public int compareTo(ProvisioningCategoryData obj) {
if (obj == null) {
return -1;
}
return obj.id.compareTo(this.id);
}
}
@@ -1,30 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.organisation.provisioning.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
public class ProvisioningCriteriaCannotBeCreatedException extends AbstractPlatformDomainRuleException {
public ProvisioningCriteriaCannotBeCreatedException(String globalisationMessageCode, String defaultUserMessage,
final Object... defaultUserMessageArgs) {
super(globalisationMessageCode, defaultUserMessage, defaultUserMessageArgs);
}
}
@@ -1,33 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.organisation.provisioning.exception;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;
import org.springframework.dao.EmptyResultDataAccessException;
public class ProvisioningCriteriaNotFoundException extends AbstractPlatformResourceNotFoundException {
public ProvisioningCriteriaNotFoundException(final Long id) {
super("error.msg.provisioning.criteria.id.invalid", "Provisioning Criteria with identifier " + id + " does not exist", id);
}
public ProvisioningCriteriaNotFoundException(Long id, EmptyResultDataAccessException e) {
super("error.msg.provisioning.criteria.id.invalid", "Provisioning Criteria with identifier " + id + " does not exist", id, e);
}
}
@@ -1,27 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.organisation.provisioning.service;
import java.util.Collection;
import org.apache.fineract.organisation.provisioning.data.ProvisioningCategoryData;
public interface ProvisioningCategoryReadPlatformService {
Collection<ProvisioningCategoryData> retrieveAllProvisionCategories();
}
@@ -1,31 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.organisation.provisioning.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface ProvisioningCategoryWritePlatformService {
CommandProcessingResult createProvisioningCateogry(JsonCommand command);
CommandProcessingResult updateProvisioningCategory(Long categoryId, JsonCommand command);
CommandProcessingResult deleteProvisioningCateogry(JsonCommand command);
}
@@ -1,31 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.organisation.provisioning.service;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
public interface ProvisioningCriteriaWritePlatformService {
CommandProcessingResult createProvisioningCriteria(JsonCommand command);
CommandProcessingResult updateProvisioningCriteria(Long categoryId, JsonCommand command);
CommandProcessingResult deleteProvisioningCriteria(Long entryId);
}
@@ -136,6 +136,9 @@
<class>org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction</class>
<class>org.apache.fineract.portfolio.savings.domain.SavingsAccountTransactionTaxDetails</class>
<class>org.apache.fineract.portfolio.savings.domain.SavingsOfficerAssignmentHistory</class>
<!-- Fineract Accounting module -->
<class>org.apache.fineract.accounting.rule.domain.AccountingRule</class>
<class>org.apache.fineract.accounting.rule.domain.AccountingTagRule</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="eclipselink.weaving" value="static" />