Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_QUOTE;
import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_REQUEST;
import static org.apache.fineract.interoperation.util.InteropUtil.ENTITY_NAME_TRANSFER;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand Down Expand Up @@ -312,9 +313,10 @@ public String createQuote(@Parameter(hidden = true) String quotesJson, @Context
@Path("transactions/{transactionCode}/transfers/{transferCode}")
@Operation(summary = "Query Interoperation Transfer", description = "")
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = InteropTransferResponseData.class)))
@ApiResponse(responseCode = "404", description = "Transfer not found")
public String getTransfer(@PathParam("transactionCode") @Parameter(description = "transactionCode") String transactionCode,
@PathParam("transferCode") @Parameter(description = "transferCode") String transferCode, @Context UriInfo uriInfo) {
context.authenticatedUser().validateHasReadPermission(ENTITY_NAME_QUOTE);
context.authenticatedUser().validateHasReadPermission(ENTITY_NAME_TRANSFER);

InteropTransferResponseData result = interopService.getTransfer(transactionCode, transferCode);
ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* 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.interoperation.domain;

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface InteropTransferRepository extends JpaRepository<InteropTransfer, Long> {

Optional<InteropTransfer> findByTransactionCodeAndTransferCode(String transactionCode, String transferCode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* 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.interoperation.exception;

import org.apache.fineract.infrastructure.core.exception.AbstractPlatformResourceNotFoundException;

public class InteropTransferNotFoundException extends AbstractPlatformResourceNotFoundException {

public InteropTransferNotFoundException(String transactionCode, String transferCode) {
super("error.msg.interop.transfer.not.found",
"No transfer found with transactionCode " + transactionCode + " and transferCode " + transferCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,15 @@
import org.apache.fineract.interoperation.domain.InteropIdentifier;
import org.apache.fineract.interoperation.domain.InteropIdentifierRepository;
import org.apache.fineract.interoperation.domain.InteropIdentifierType;
import org.apache.fineract.interoperation.domain.InteropTransfer;
import org.apache.fineract.interoperation.domain.InteropTransferRepository;
import org.apache.fineract.interoperation.exception.InteropAccountNotFoundException;
import org.apache.fineract.interoperation.exception.InteropAccountTransactionNotAllowedException;
import org.apache.fineract.interoperation.exception.InteropKycDataNotFoundException;
import org.apache.fineract.interoperation.exception.InteropTransferAlreadyCommittedException;
import org.apache.fineract.interoperation.exception.InteropTransferAlreadyOnHoldException;
import org.apache.fineract.interoperation.exception.InteropTransferMissingException;
import org.apache.fineract.interoperation.exception.InteropTransferNotFoundException;
import org.apache.fineract.interoperation.serialization.InteropDataValidator;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepository;
Expand Down Expand Up @@ -129,6 +132,7 @@ public class InteropServiceImpl implements InteropService {
private final NoteRepository noteRepository;
private final PaymentTypeRepository paymentTypeRepository;
private final InteropIdentifierRepository identifierRepository;
private final InteropTransferRepository transferRepository;
private final LoanRepositoryWrapper loanRepositoryWrapper;

private final SavingsHelper savingsHelper;
Expand Down Expand Up @@ -363,8 +367,12 @@ public InteropQuoteResponseData createQuote(@NonNull JsonCommand command) {
}

@Override
@Transactional(readOnly = true)
public InteropTransferResponseData getTransfer(@NonNull String transactionCode, @NonNull String transferCode) {
return null;
InteropTransfer transfer = transferRepository.findByTransactionCodeAndTransferCode(transactionCode, transferCode)
.orElseThrow(() -> new InteropTransferNotFoundException(transactionCode, transferCode));
return InteropTransferResponseData.build(transfer.getTransactionCode(), transfer.getState(), null, transfer.getTransferCode(),
transfer.getCompletedTimestamp());
}

@Override
Expand Down Expand Up @@ -406,8 +414,11 @@ public InteropTransferResponseData prepareTransfer(@NonNull JsonCommand command)
savingsAccountRepository.save(savingsAccount);
}

return InteropTransferResponseData.build(command.commandId(), request.getTransactionCode(), InteropActionState.ACCEPTED,
request.getExpiration(), request.getExtensionList(), transferCode, DateUtils.getLocalDateTimeOfTenant());
LocalDateTime completedTimestamp = DateUtils.getLocalDateTimeOfTenant();
InteropTransferResponseData response = InteropTransferResponseData.build(command.commandId(), request.getTransactionCode(),
InteropActionState.ACCEPTED, request.getExpiration(), request.getExtensionList(), transferCode, completedTimestamp);
recordTransfer(request.getTransactionCode(), transferCode, completedTimestamp);
return response;
}

@Override
Expand Down Expand Up @@ -470,8 +481,11 @@ public InteropTransferResponseData commitTransfer(@NonNull JsonCommand command)
noteRepository.save(Note.savingsTransactionNote(savingsAccount, transaction, note));
}

return InteropTransferResponseData.build(command.commandId(), request.getTransactionCode(), InteropActionState.ACCEPTED,
request.getExpiration(), request.getExtensionList(), request.getTransferCode(), transactionDateTime);
InteropTransferResponseData response = InteropTransferResponseData.build(command.commandId(), request.getTransactionCode(),
InteropActionState.ACCEPTED, request.getExpiration(), request.getExtensionList(), request.getTransferCode(),
transactionDateTime);
recordTransfer(request.getTransactionCode(), request.getTransferCode(), transactionDateTime);
return response;
}

@Override
Expand Down Expand Up @@ -501,8 +515,18 @@ public InteropTransferResponseData commitTransfer(@NonNull JsonCommand command)
throw new InteropTransferMissingException(savingsAccount.getExternalId().getValue(), request.getTransferCode());
}

return InteropTransferResponseData.build(command.commandId(), request.getTransactionCode(), InteropActionState.ACCEPTED,
request.getExpiration(), request.getExtensionList(), request.getTransferCode(), transactionDateTime);
InteropTransferResponseData response = InteropTransferResponseData.build(command.commandId(), request.getTransactionCode(),
InteropActionState.ACCEPTED, request.getExpiration(), request.getExtensionList(), request.getTransferCode(),
transactionDateTime);
recordTransfer(request.getTransactionCode(), request.getTransferCode(), transactionDateTime);
return response;
}

private void recordTransfer(String transactionCode, String transferCode, LocalDateTime completedTimestamp) {
InteropTransfer transfer = transferRepository.findByTransactionCodeAndTransferCode(transactionCode, transferCode)
.orElseGet(() -> new InteropTransfer(transactionCode, transferCode, InteropActionState.ACCEPTED, completedTimestamp));
transfer.update(InteropActionState.ACCEPTED, completedTimestamp);
transferRepository.save(transfer);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.apache.fineract.infrastructure.core.service.database.DatabaseSpecificSQLGenerator;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.interoperation.domain.InteropIdentifierRepository;
import org.apache.fineract.interoperation.domain.InteropTransferRepository;
import org.apache.fineract.interoperation.serialization.InteropDataValidator;
import org.apache.fineract.interoperation.service.InteropService;
import org.apache.fineract.interoperation.service.InteropServiceImpl;
Expand Down Expand Up @@ -51,14 +52,14 @@ public InteropService interopService(PlatformSecurityContext securityContext, In
SavingsAccountRepository savingsAccountRepository, SavingsAccountTransactionRepository savingsAccountTransactionRepository,
ApplicationCurrencyRepository applicationCurrencyRepository, NoteRepository noteRepository,
PaymentTypeRepository paymentTypeRepository, InteropIdentifierRepository identifierRepository,
LoanRepositoryWrapper loanRepositoryWrapper, SavingsHelper savingsHelper,
InteropTransferRepository transferRepository, LoanRepositoryWrapper loanRepositoryWrapper, SavingsHelper savingsHelper,
SavingsAccountTransactionSummaryWrapper savingsAccountTransactionSummaryWrapper,
SavingsAccountDomainService savingsAccountService, ConfigurationDomainService configurationDomainService,
JdbcTemplate jdbcTemplate, PortfolioCommandSourceWritePlatformService commandsSourceWritePlatformService,
DefaultToApiJsonSerializer<LoanAccountData> toApiJsonSerializer, DatabaseSpecificSQLGenerator sqlGenerator) {
return new InteropServiceImpl(securityContext, interopDataValidator, savingsAccountRepository, savingsAccountTransactionRepository,
applicationCurrencyRepository, noteRepository, paymentTypeRepository, identifierRepository, loanRepositoryWrapper,
savingsHelper, savingsAccountTransactionSummaryWrapper, savingsAccountService, configurationDomainService, jdbcTemplate,
commandsSourceWritePlatformService, toApiJsonSerializer, sqlGenerator);
applicationCurrencyRepository, noteRepository, paymentTypeRepository, identifierRepository, transferRepository,
loanRepositoryWrapper, savingsHelper, savingsAccountTransactionSummaryWrapper, savingsAccountService,
configurationDomainService, jdbcTemplate, commandsSourceWritePlatformService, toApiJsonSerializer, sqlGenerator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -267,4 +267,5 @@
<include file="parts/0245_standardize_email_address_column_length.xml" relativeToChangelogFile="true" />
<include file="parts/0246_drop_request_audit_table.xml" relativeToChangelogFile="true" />
<include file="parts/0247_add_payment_detail_search_indexes.xml" relativeToChangelogFile="true" />
<include file="parts/0248_create_interop_transfer.xml" relativeToChangelogFile="true" />
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

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.

-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
<changeSet author="fineract" id="1">
<createTable tableName="interop_transfer">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true"/>
</column>
<column name="transaction_code" type="VARCHAR(128)">
<constraints nullable="false"/>
</column>
<column name="transfer_code" type="VARCHAR(128)">
<constraints nullable="false"/>
</column>
<column name="state" type="VARCHAR(16)">
<constraints nullable="false"/>
</column>
<column name="completed_timestamp" type="timestamp(6)"/>
</createTable>
<addUniqueConstraint columnNames="transaction_code, transfer_code" constraintName="uk_interop_transfer_codes"
tableName="interop_transfer"/>
</changeSet>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* 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.interoperation.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;

@Entity
@Getter
@NoArgsConstructor
@Table(name = "interop_transfer", uniqueConstraints = {
@UniqueConstraint(name = "uk_interop_transfer_codes", columnNames = { "transaction_code", "transfer_code" }) })
public class InteropTransfer extends AbstractPersistableCustom<Long> {

@Column(name = "transaction_code", nullable = false, length = 128)
private String transactionCode;

@Column(name = "transfer_code", nullable = false, length = 128)
private String transferCode;

@Column(name = "state", nullable = false, length = 16)
@Enumerated(EnumType.STRING)
private InteropActionState state;

@Column(name = "completed_timestamp")
private LocalDateTime completedTimestamp;

public InteropTransfer(@NotNull String transactionCode, @NotNull String transferCode, @NotNull InteropActionState state,
LocalDateTime completedTimestamp) {
this.transactionCode = transactionCode;
this.transferCode = transferCode;
update(state, completedTimestamp);
}

public void update(@NotNull InteropActionState state, LocalDateTime completedTimestamp) {
this.state = state;
this.completedTimestamp = completedTimestamp;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -360,12 +360,23 @@ private String buildQuoteJson(String quoteCode, InteropTransactionRole role) {
// org.apache.fineract.client.models.PostLoansLoanIdRequest)
@Deprecated(forRemoval = true)
public String getTransfer(String transferCode) {
return getJsonAttribute(getTransfer(transactionCode, transferCode), InteropUtil.PARAM_TRANSFER_CODE);
}

/**
* @return response json
*/
// TODO: Rewrite to use fineract-client instead!
// Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
// org.apache.fineract.client.models.PostLoansLoanIdRequest)
@Deprecated(forRemoval = true)
public String getTransfer(String transactionCode, String transferCode) {
String url = buildUrl(TRANSACTIONS_URL + '/' + transactionCode + '/' + TRANSFERS_URL_PARAM + '/' + transferCode);
LOG.debug("Calling Interoperable GET Transfer: {}", url);

String response = Utils.performServerGet(requestSpec, responseSpec, url, null);
LOG.debug("Response Interoperable GET Transfer: {}", response);
return getJsonAttribute(response, InteropUtil.PARAM_TRANSFER_CODE);
return response;
}

/**
Expand All @@ -390,6 +401,17 @@ public String createTransfer(String transferCode, InteropTransactionRole role) {
return postTransfer(transferCode, InteropTransferActionType.CREATE, role);
}

/**
* @return response json
*/
// TODO: Rewrite to use fineract-client instead!
// Example: org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper.disburseLoan(java.lang.Long,
// org.apache.fineract.client.models.PostLoansLoanIdRequest)
@Deprecated(forRemoval = true)
public String releaseTransfer(String transferCode) {
return postTransfer(transferCode, InteropTransferActionType.RELEASE, InteropTransactionRole.PAYER);
}

/**
* @return response json
*/
Expand Down
Loading
Loading