Merge pull request #25 from fynmanoj/collections-api
AL-54-Collections api
diff --git a/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/AttendanceEnum.java b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/AttendanceEnum.java
new file mode 100644
index 0000000..8b68b65
--- /dev/null
+++ b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/AttendanceEnum.java
@@ -0,0 +1,23 @@
+/*
+ * 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.cn.deposit.api.v1.collection.domain.data;
+
+public enum AttendanceEnum {
+ PRESENT,
+ ABSENT;
+}
diff --git a/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/CollectionsRequest.java b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/CollectionsRequest.java
new file mode 100644
index 0000000..a91eefc
--- /dev/null
+++ b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/CollectionsRequest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.cn.deposit.api.v1.collection.domain.data;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+public class CollectionsRequest {
+ private String txnDate;
+ @NotNull
+ private BigDecimal amount;
+ private String currency;
+ private String remarks;
+ @NotNull
+ private String accountId;
+ private String subtxnId;
+ private BigDecimal fee;
+ private List<IndividualPayments> individualPayments;
+ private String reference;
+
+ public CollectionsRequest() {
+ }
+
+ public CollectionsRequest(String txnDate, BigDecimal amount, String currency, String remarks,
+ String accountId, String subtxnId, BigDecimal fee,
+ List<IndividualPayments> individualPayments, String reference) {
+ this.txnDate = txnDate;
+ this.amount = amount;
+ this.currency = currency;
+ this.remarks = remarks;
+ this.accountId = accountId;
+ this.subtxnId = subtxnId;
+ this.fee = fee;
+ this.individualPayments = individualPayments;
+ this.reference = reference;
+ }
+
+ public String getTxnDate() {
+ return txnDate;
+ }
+
+ public void setTxnDate(String txnDate) {
+ this.txnDate = txnDate;
+ }
+
+ public BigDecimal getAmount() {
+ return amount;
+ }
+
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+
+ public String getCurrency() {
+ return currency;
+ }
+
+ public void setCurrency(String currency) {
+ this.currency = currency;
+ }
+
+ public String getRemarks() {
+ return remarks;
+ }
+
+ public void setRemarks(String remarks) {
+ this.remarks = remarks;
+ }
+
+ public String getAccountId() {
+ return accountId;
+ }
+
+ public void setAccountId(String accountId) {
+ this.accountId = accountId;
+ }
+
+ public String getSubtxnId() {
+ return subtxnId;
+ }
+
+ public void setSubtxnId(String subtxnId) {
+ this.subtxnId = subtxnId;
+ }
+
+ public BigDecimal getFee() {
+ return fee;
+ }
+
+ public void setFee(BigDecimal fee) {
+ this.fee = fee;
+ }
+
+ public List<IndividualPayments> getIndividualPayments() {
+ return individualPayments;
+ }
+
+ public void setIndividualPayments(List<IndividualPayments> individualPayments) {
+ this.individualPayments = individualPayments;
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ public void setReference(String reference) {
+ this.reference = reference;
+ }
+}
diff --git a/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/CollectionsResponse.java b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/CollectionsResponse.java
new file mode 100644
index 0000000..4f64b57
--- /dev/null
+++ b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/CollectionsResponse.java
@@ -0,0 +1,75 @@
+/*
+ * 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.cn.deposit.api.v1.collection.domain.data;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+import java.time.LocalDateTime;
+import java.util.List;
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class CollectionsResponse {
+ private String reference;
+ private String token;
+ @JsonFormat(pattern = "yyyy-MMMM-dd hh:mm:ss")
+ private LocalDateTime tokenExpiresBy;
+ private List<IndividualPayments> individualPayments;
+
+ public CollectionsResponse() {
+ }
+
+ public CollectionsResponse(String reference, String token, LocalDateTime tokenExpiresBy, List<IndividualPayments> individualPayments) {
+ this.reference = reference;
+ this.token = token;
+ this.tokenExpiresBy = tokenExpiresBy;
+ this.individualPayments = individualPayments;
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ public void setReference(String reference) {
+ this.reference = reference;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public LocalDateTime getTokenExpiresBy() {
+ return tokenExpiresBy;
+ }
+
+ public void setTokenExpiresBy(LocalDateTime tokenExpiresBy) {
+ this.tokenExpiresBy = tokenExpiresBy;
+ }
+
+ public List<IndividualPayments> getIndividualPayments() {
+ return individualPayments;
+ }
+
+ public void setIndividualPayments(List<IndividualPayments> individualPayments) {
+ this.individualPayments = individualPayments;
+ }
+}
diff --git a/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/IndividualPayments.java b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/IndividualPayments.java
new file mode 100644
index 0000000..d0e6401
--- /dev/null
+++ b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/IndividualPayments.java
@@ -0,0 +1,108 @@
+/*
+ * 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.cn.deposit.api.v1.collection.domain.data;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fasterxml.jackson.annotation.JsonInclude;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class IndividualPayments{
+ @NotNull
+ private String accountNumber;
+ private BigDecimal amount;
+ private AttendanceEnum attendance;
+ private String reference;
+ private String token;
+ @JsonFormat(pattern = "yyyy-MMMM-dd hh:mm:ss")
+ private LocalDateTime tokenExpiresBy;
+
+ public IndividualPayments() {
+ }
+
+ public IndividualPayments(String accountNumber, BigDecimal amount, AttendanceEnum attendance, String reference) {
+ this.accountNumber = accountNumber;
+ this.amount = amount;
+ this.attendance = attendance;
+ this.reference = reference;
+ }
+
+ public IndividualPayments(String accountNumber, AttendanceEnum attendance, String reference,
+ BigDecimal amount, String token, LocalDateTime tokenExpiresBy){
+ this.accountNumber = accountNumber;
+ this.attendance = attendance;
+ this.reference =reference;
+ this.token = token;
+ this.tokenExpiresBy = tokenExpiresBy;
+ this.amount = amount;
+ }
+
+ public String getAccountNumber() {
+ return accountNumber;
+ }
+
+ public void setAccountNumber(String accountNumber) {
+ this.accountNumber = accountNumber;
+ }
+
+ public BigDecimal getAmount() {
+ return amount;
+ }
+
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+
+ public String getAttendance() {
+ return attendance==null? null: attendance.name();
+ }
+
+ public void setAttendance(String attendance) {
+ this.attendance = AttendanceEnum.valueOf(attendance);
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ public void setReference(String reference) {
+ this.reference = reference;
+ }
+
+ public void setAttendance(AttendanceEnum attendance) {
+ this.attendance = attendance;
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public LocalDateTime getTokenExpiresBy() {
+ return tokenExpiresBy;
+ }
+
+ public void setTokenExpiresBy(LocalDateTime tokenExpiresBy) {
+ this.tokenExpiresBy = tokenExpiresBy;
+ }
+}
\ No newline at end of file
diff --git a/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/TokenEntities.java b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/TokenEntities.java
new file mode 100644
index 0000000..9bc0576
--- /dev/null
+++ b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/TokenEntities.java
@@ -0,0 +1,23 @@
+/*
+ * 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.cn.deposit.api.v1.collection.domain.data;
+
+public enum TokenEntities {
+ COLLECTION,
+ INDV_COLLECTION;
+}
diff --git a/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/TokenStatus.java b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/TokenStatus.java
new file mode 100644
index 0000000..ca3773e
--- /dev/null
+++ b/api/src/main/java/org/apache/fineract/cn/deposit/api/v1/collection/domain/data/TokenStatus.java
@@ -0,0 +1,24 @@
+/*
+ * 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.cn.deposit.api.v1.collection.domain.data;
+
+public enum TokenStatus {
+ ACTIVE,
+ USED,
+ EXPIRED;
+}
diff --git a/service/build.gradle b/service/build.gradle
index dc4325f..64d471b 100644
--- a/service/build.gradle
+++ b/service/build.gradle
@@ -62,7 +62,8 @@
[group: 'org.hibernate', name: 'hibernate-validator', version: versions.validator],
[group: 'org.threeten', name: 'threeten-extra', version: '1.2'],
[group: 'io.netty', name: 'netty-all', version: '4.1.39.Final'],
- [group: 'io.netty', name: 'netty-transport-native-epoll', version: '4.1.39.Final']
+ [group: 'io.netty', name: 'netty-transport-native-epoll', version: '4.1.39.Final'],
+ [group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.8.8']
)
}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/CreateCollectionsCommand.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/CreateCollectionsCommand.java
new file mode 100644
index 0000000..c602629
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/CreateCollectionsCommand.java
@@ -0,0 +1,32 @@
+/*
+ * 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.cn.deposit.service.internal.command;
+
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.CollectionsRequest;
+
+public class CreateCollectionsCommand {
+ private final CollectionsRequest collectionRequest;
+
+ public CreateCollectionsCommand(CollectionsRequest collectionRequest) {
+ this.collectionRequest = collectionRequest;
+ }
+
+ public CollectionsRequest getCollectionRequest() {
+ return collectionRequest;
+ }
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/UpdateCollectionsCommand.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/UpdateCollectionsCommand.java
new file mode 100644
index 0000000..3f1f85a
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/UpdateCollectionsCommand.java
@@ -0,0 +1,38 @@
+/*
+ * 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.cn.deposit.service.internal.command;
+
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.CollectionsRequest;
+
+public class UpdateCollectionsCommand {
+ private final String collectionsReference;
+ private final CollectionsRequest collectionRequest;
+
+ public UpdateCollectionsCommand(String collectionsReference, CollectionsRequest collectionRequest) {
+ this.collectionsReference = collectionsReference;
+ this.collectionRequest = collectionRequest;
+ }
+
+ public CollectionsRequest getCollectionRequest() {
+ return collectionRequest;
+ }
+
+ public String getCollectionsReference() {
+ return collectionsReference;
+ }
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/handler/CollectionsCommandHandler.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/handler/CollectionsCommandHandler.java
new file mode 100644
index 0000000..1b77e0d
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/command/handler/CollectionsCommandHandler.java
@@ -0,0 +1,121 @@
+/*
+ * 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.cn.deposit.service.internal.command.handler;
+
+import org.apache.fineract.cn.command.annotation.Aggregate;
+import org.apache.fineract.cn.command.annotation.CommandHandler;
+import org.apache.fineract.cn.command.annotation.CommandLogLevel;
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.CollectionsResponse;
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.IndividualPayments;
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.TokenEntities;
+import org.apache.fineract.cn.deposit.service.ServiceConstants;
+import org.apache.fineract.cn.deposit.service.internal.command.CreateCollectionsCommand;
+import org.apache.fineract.cn.deposit.service.internal.command.UpdateCollectionsCommand;
+import org.apache.fineract.cn.deposit.service.internal.mapper.CollectionsMapper;
+import org.apache.fineract.cn.deposit.service.internal.repository.*;
+import org.apache.fineract.cn.deposit.service.internal.service.SelfExpiringTokenService;
+import org.apache.fineract.cn.lang.ServiceException;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+import java.time.Clock;
+import java.time.LocalDateTime;
+import java.util.Optional;
+
+@Aggregate
+public class CollectionsCommandHandler {
+ private final Logger logger;
+ private final ProductInstanceRepository productInstanceRepository;
+ private final SubTransactionTypeRepository subTransactionTypeRepository;
+ private final CollectionsRepository collectionsRepository;
+ private final SelfExpiringTokenService selfExpiringTokenService;
+
+ @Autowired
+ public CollectionsCommandHandler(@Qualifier(ServiceConstants.LOGGER_NAME) Logger logger,
+ ProductInstanceRepository productInstanceRepository,
+ SubTransactionTypeRepository subTransactionTypeRepository,
+ CollectionsRepository collectionsRepository,
+ SelfExpiringTokenService selfExpiringTokenService) {
+ this.logger = logger;
+ this.productInstanceRepository = productInstanceRepository;
+ this.subTransactionTypeRepository = subTransactionTypeRepository;
+ this.collectionsRepository = collectionsRepository;
+ this.selfExpiringTokenService = selfExpiringTokenService;
+ }
+
+ @Transactional
+ @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+ public CollectionsResponse saveCollection(@NotNull CreateCollectionsCommand command) {
+
+ CollectionsEntity collectionsEntity = CollectionsMapper.map(command.getCollectionRequest(),
+ productInstanceRepository, subTransactionTypeRepository);
+
+ //create self expiring tokens
+ LocalDateTime currentTime = getNow();
+ collectionsEntity.setToken(this.selfExpiringTokenService.generateAndSaveToken(TokenEntities.COLLECTION.name(), collectionsEntity.getCollectionReference(), currentTime));
+
+ collectionsEntity.getIndvCollections().forEach(
+ x-> {
+ SelfExpiringTokenEntity token = selfExpiringTokenService.generateAndSaveToken(TokenEntities.INDV_COLLECTION.name(), x.getIndividualCollectionReference(), currentTime);
+ x.setToken(token);
+ }
+ );
+
+ //save collections
+ collectionsRepository.save(collectionsEntity);
+
+ return CollectionsMapper.map(collectionsEntity);
+ }
+
+ @Transactional
+ @CommandHandler(logStart = CommandLogLevel.INFO, logFinish = CommandLogLevel.INFO)
+ public CollectionsResponse updateCollection(@NotNull UpdateCollectionsCommand command) {
+
+ CollectionsEntity collectionsEntity = this.collectionsRepository.findByCollectionReference(command.getCollectionsReference())
+ .orElseThrow(() -> ServiceException.notFound("Collection {0} not found", command.getCollectionsReference()));
+
+ collectionsEntity.setAmount(BigDecimal.ZERO);
+ collectionsEntity.getIndvCollections().forEach(x->{
+ Optional<IndividualPayments> individualPaymentsOptional = command.getCollectionRequest().getIndividualPayments().stream().filter(a -> x.getAccount().getAccountIdentifier().equals(a.getAccountNumber())).findFirst();
+ if(individualPaymentsOptional.isPresent()){
+ IndividualPayments individualPayment = individualPaymentsOptional.get();
+ x.setAttendance(individualPayment.getAttendance());
+ if(individualPayment.getAmount() != null) {
+ x.setAmount(individualPayment.getAmount());
+ }
+ collectionsEntity.setAmount(collectionsEntity.getAmount().add(x.getAmount()));
+ }
+ }
+ );
+
+ //save collections
+ collectionsRepository.save(collectionsEntity);
+
+ return CollectionsMapper.map(collectionsEntity);
+ }
+
+ private static LocalDateTime getNow() {
+ return LocalDateTime.now(Clock.systemUTC());
+ }
+
+
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/mapper/CollectionsMapper.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/mapper/CollectionsMapper.java
new file mode 100644
index 0000000..adba9d6
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/mapper/CollectionsMapper.java
@@ -0,0 +1,95 @@
+/*
+ * 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.cn.deposit.service.internal.mapper;
+
+import org.apache.fineract.cn.api.util.UserContextHolder;
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.AttendanceEnum;
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.CollectionsRequest;
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.CollectionsResponse;
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.IndividualPayments;
+import org.apache.fineract.cn.deposit.service.internal.repository.*;
+import org.apache.fineract.cn.lang.ServiceException;
+
+import java.math.BigDecimal;
+import java.time.Clock;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.UUID;
+import java.util.stream.Collectors;
+
+public class CollectionsMapper {
+ private CollectionsMapper() {
+ super();
+ }
+
+ public static CollectionsEntity map(CollectionsRequest collectionsRequest,
+ ProductInstanceRepository accountRepository,
+ SubTransactionTypeRepository subTxnRepository){
+ CollectionsEntity entity = new CollectionsEntity();
+
+ if(collectionsRequest.getTxnDate() != null) {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MMMM-dd");
+ entity.setTransactionDate(LocalDate.parse(collectionsRequest.getTxnDate(), formatter).atStartOfDay());
+ }else {
+ entity.setTransactionDate(getNow());
+ }
+ entity.setAmount(BigDecimal.ZERO);
+ entity.setTransportFeeAmount(collectionsRequest.getFee());
+ entity.setCurrency(collectionsRequest.getCurrency());
+ entity.setRemarks(collectionsRequest.getRemarks());
+ entity.setAccount(accountRepository.findByAccountIdentifier(collectionsRequest.getAccountId()).orElseThrow(() -> ServiceException.notFound("Account {0} not found.", collectionsRequest.getAccountId())));
+ entity.setSubTxnType(subTxnRepository.findByIdentifier(collectionsRequest.getSubtxnId()).orElseThrow(() -> ServiceException.notFound("Sub Txn Type {0} not found.", collectionsRequest.getSubtxnId())));
+ entity.setStatus("INIT");
+ entity.setCollectionReference(UUID.randomUUID().toString());
+ entity.setCreatedBy(getLoginUser());
+ entity.setCreatedOn(getNow());
+ entity.setLastModifiedBy(getLoginUser());
+ entity.setLastModifiedOn(getNow());
+ entity.setIndvCollections(collectionsRequest.getIndividualPayments().stream().map(x-> {
+ IndividualCollectionsEntity indv = new IndividualCollectionsEntity();
+ indv.setCollection(entity);
+ indv.setAccount(accountRepository.findByAccountIdentifier(x.getAccountNumber()).orElseThrow(() -> ServiceException.notFound("Account {0} not found.", x.getAccountNumber())));
+ indv.setAccountExternalId(x.getAccountNumber());
+ indv.setAmount(x.getAmount());
+ indv.setIndividualCollectionReference(UUID.randomUUID().toString());
+ entity.setAmount(entity.getAmount().add(x.getAmount()));
+ return indv;
+ }).collect(Collectors.toSet()));
+ return entity;
+ }
+
+ public static CollectionsResponse map(CollectionsEntity collectionsEntity){
+ return new CollectionsResponse(collectionsEntity.getCollectionReference(),
+ collectionsEntity.getToken().getToken(),
+ collectionsEntity.getToken().getTokenExpiresBy(),
+ collectionsEntity.getIndvCollections().stream().map( x ->{
+ return new IndividualPayments(x.getAccount().getAccountIdentifier(),
+ x.getAttendance() == null ? null: AttendanceEnum.valueOf(x.getAttendance()),
+ x.getIndividualCollectionReference(), x.getAmount(),
+ x.getToken().getToken(), x.getToken().getTokenExpiresBy());
+ }).collect(Collectors.toList()));
+ }
+
+ private static LocalDateTime getNow() {
+ return LocalDateTime.now(Clock.systemUTC());
+ }
+ private static String getLoginUser() {
+ return UserContextHolder.checkedGetUser();
+ }
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/CollectionsEntity.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/CollectionsEntity.java
new file mode 100644
index 0000000..33d01b8
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/CollectionsEntity.java
@@ -0,0 +1,214 @@
+/*
+ * 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.cn.deposit.service.internal.repository;
+
+import org.apache.fineract.cn.deposit.api.v1.instance.domain.ProductInstance;
+import org.apache.fineract.cn.postgresql.util.LocalDateTimeConverter;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Set;
+
+@Entity
+@Table(name = "shed_collections")
+public class CollectionsEntity {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id", nullable = false)
+ private Long id;
+
+ @Column(name = "transaction_date")
+ @Convert(converter = LocalDateTimeConverter.class)
+ private LocalDateTime transactionDate;
+
+ @Column(name = "amount", nullable = false)
+ private BigDecimal amount;
+
+ @Column(name = "transport_fee_amount", nullable = false)
+ private BigDecimal transportFeeAmount;
+
+ @Column(name = "currency", length = 10)
+ private String currency;
+
+ @Column(name = "remarks", length = 1024)
+ private String remarks;
+
+ @ManyToOne(fetch = FetchType.EAGER, optional = false)
+ @JoinColumn(name = "account_identifier", nullable = false, unique = true, referencedColumnName = "account_identifier")
+ private ProductInstanceEntity account;
+
+ @ManyToOne(fetch = FetchType.EAGER, optional = false)
+ @JoinColumn(name = "sub_txn_type_id", nullable = false, unique = true, referencedColumnName = "identifier")
+ private SubTransactionTypeEntity subTxnType;
+
+ @Column(name = "status", length = 20)
+ private String status;
+
+ @Column(name = "c_reference", length = 36)
+ private String collectionReference;
+
+ @Column(name = "created_by", nullable = false, length = 32)
+ private String createdBy;
+
+ @Column(name = "created_on", nullable = false)
+ @Convert(converter = LocalDateTimeConverter.class)
+ private LocalDateTime createdOn;
+
+ @Column(name = "last_modified_by", length = 32)
+ private String lastModifiedBy;
+
+ @Column(name = "last_modified_on")
+ @Convert(converter = LocalDateTimeConverter.class)
+ private LocalDateTime lastModifiedOn;
+
+ @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
+ @JoinColumn(name = "collections_id") // we need to duplicate the physical information
+ private Set<IndividualCollectionsEntity> indvCollections;
+
+ @ManyToOne(fetch = FetchType.EAGER, optional = false)
+ @JoinColumn(name = "token", nullable = false, unique = true)
+ private SelfExpiringTokenEntity token;
+
+ public CollectionsEntity() {
+ }
+
+ public LocalDateTime getTransactionDate() {
+ return transactionDate;
+ }
+
+ public void setTransactionDate(LocalDateTime transactionDate) {
+ this.transactionDate = transactionDate;
+ }
+
+ public BigDecimal getAmount() {
+ return amount;
+ }
+
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+
+ public BigDecimal getTransportFeeAmount() {
+ return transportFeeAmount;
+ }
+
+ public void setTransportFeeAmount(BigDecimal transportFeeAmount) {
+ this.transportFeeAmount = transportFeeAmount;
+ }
+
+ public String getCurrency() {
+ return currency;
+ }
+
+ public void setCurrency(String currency) {
+ this.currency = currency;
+ }
+
+ public String getRemarks() {
+ return remarks;
+ }
+
+ public void setRemarks(String remarks) {
+ this.remarks = remarks;
+ }
+
+ public ProductInstanceEntity getAccount() {
+ return account;
+ }
+
+ public void setAccount(ProductInstanceEntity account) {
+ this.account = account;
+ }
+
+ public SubTransactionTypeEntity getSubTxnType() {
+ return subTxnType;
+ }
+
+ public void setSubTxnType(SubTransactionTypeEntity subTxnType) {
+ this.subTxnType = subTxnType;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getCollectionReference() {
+ return collectionReference;
+ }
+
+ public void setCollectionReference(String collectionReference) {
+ this.collectionReference = collectionReference;
+ }
+
+ public String getCreatedBy() {
+ return createdBy;
+ }
+
+ public void setCreatedBy(String createdBy) {
+ this.createdBy = createdBy;
+ }
+
+ public LocalDateTime getCreatedOn() {
+ return createdOn;
+ }
+
+ public void setCreatedOn(LocalDateTime createdOn) {
+ this.createdOn = createdOn;
+ }
+
+ public String getLastModifiedBy() {
+ return lastModifiedBy;
+ }
+
+ public void setLastModifiedBy(String lastModifiedBy) {
+ this.lastModifiedBy = lastModifiedBy;
+ }
+
+ public LocalDateTime getLastModifiedOn() {
+ return lastModifiedOn;
+ }
+
+ public void setLastModifiedOn(LocalDateTime lastModifiedOn) {
+ this.lastModifiedOn = lastModifiedOn;
+ }
+
+ public Set<IndividualCollectionsEntity> getIndvCollections() {
+ return indvCollections;
+ }
+
+ public void addToIndvCollections(IndividualCollectionsEntity indvCollection) {
+ this.indvCollections.add(indvCollection);
+ }
+
+ public void setIndvCollections(Set<IndividualCollectionsEntity> indvCollections) {
+ this.indvCollections = indvCollections;
+ }
+
+ public SelfExpiringTokenEntity getToken() {
+ return token;
+ }
+
+ public void setToken(SelfExpiringTokenEntity token) {
+ this.token = token;
+ }
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/CollectionsRepository.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/CollectionsRepository.java
new file mode 100644
index 0000000..40948ab
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/CollectionsRepository.java
@@ -0,0 +1,28 @@
+/*
+ * 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.cn.deposit.service.internal.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface CollectionsRepository extends JpaRepository<CollectionsEntity, Long> {
+
+ Optional<CollectionsEntity> findByCollectionReference(String collectionReference);
+
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/IndividualCollectionsEntity.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/IndividualCollectionsEntity.java
new file mode 100644
index 0000000..48a83f0
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/IndividualCollectionsEntity.java
@@ -0,0 +1,115 @@
+/*
+ * 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.cn.deposit.service.internal.repository;
+
+import org.apache.fineract.cn.deposit.api.v1.instance.domain.ProductInstance;
+
+import javax.persistence.*;
+import java.math.BigDecimal;
+
+@Entity
+@Table(name = "shed_collections_inidividual")
+public class IndividualCollectionsEntity {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id", nullable = false)
+ private Long id;
+
+ @ManyToOne(fetch = FetchType.EAGER, optional = false, cascade = CascadeType.ALL)
+ @JoinColumn(name = "collections_id", nullable = false, unique = true)
+ private CollectionsEntity collection;
+
+ @ManyToOne(fetch = FetchType.EAGER, optional = false)
+ @JoinColumn(name = "account_identifier", nullable = false, unique = true, referencedColumnName = "account_identifier")
+ private ProductInstanceEntity account;
+
+ @Column(name = "account_external_id", length = 64)
+ private String accountExternalId;
+
+ @Column(name = "amount", nullable = false)
+ private BigDecimal amount;
+
+ @Column(name = "i_reference", length = 36)
+ private String individualCollectionReference;
+
+ @Column(name = "attendance", length = 10)
+ private String attendance;
+
+ @ManyToOne(fetch = FetchType.EAGER, optional = false)
+ @JoinColumn(name = "token", nullable = false, unique = true)
+ private SelfExpiringTokenEntity token;
+
+ public IndividualCollectionsEntity() {
+ }
+
+ public CollectionsEntity getCollection() {
+ return collection;
+ }
+
+ public void setCollection(CollectionsEntity collection) {
+ this.collection = collection;
+ }
+
+ public ProductInstanceEntity getAccount() {
+ return account;
+ }
+
+ public void setAccount(ProductInstanceEntity account) {
+ this.account = account;
+ }
+
+ public String getAccountExternalId() {
+ return accountExternalId;
+ }
+
+ public void setAccountExternalId(String accountExternalId) {
+ this.accountExternalId = accountExternalId;
+ }
+
+ public BigDecimal getAmount() {
+ return amount;
+ }
+
+ public void setAmount(BigDecimal amount) {
+ this.amount = amount;
+ }
+
+ public String getIndividualCollectionReference() {
+ return individualCollectionReference;
+ }
+
+ public void setIndividualCollectionReference(String individualCollectionReference) {
+ this.individualCollectionReference = individualCollectionReference;
+ }
+
+ public String getAttendance() {
+ return attendance;
+ }
+
+ public void setAttendance(String attendance) {
+ this.attendance = attendance;
+ }
+
+ public SelfExpiringTokenEntity getToken() {
+ return token;
+ }
+
+ public void setToken(SelfExpiringTokenEntity token) {
+ this.token = token;
+ }
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/IndividualCollectionsRepository.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/IndividualCollectionsRepository.java
new file mode 100644
index 0000000..03797d8
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/IndividualCollectionsRepository.java
@@ -0,0 +1,26 @@
+/*
+ * 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.cn.deposit.service.internal.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface IndividualCollectionsRepository extends JpaRepository<IndividualCollectionsEntity, Long> {
+ Optional<IndividualCollectionsEntity> findByIndividualCollectionReference(String individualCollectionReference);
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/SelfExpiringTokenEntity.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/SelfExpiringTokenEntity.java
new file mode 100644
index 0000000..3d9d992
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/SelfExpiringTokenEntity.java
@@ -0,0 +1,103 @@
+/*
+ * 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.cn.deposit.service.internal.repository;
+
+import org.apache.fineract.cn.postgresql.util.LocalDateTimeConverter;
+
+import javax.persistence.*;
+import java.time.LocalDateTime;
+
+@Entity
+@Table(name = "shed_self_expiring_tokens")
+public class SelfExpiringTokenEntity {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id", nullable = false)
+ private Long id;
+
+ @Column(name = "token", length = 10)
+ private String token;
+
+ @Column(name = "created_time")
+ @Convert(converter = LocalDateTimeConverter.class)
+ private LocalDateTime createdTime;
+
+ @Column(name = "token_expires_by")
+ @Convert(converter = LocalDateTimeConverter.class)
+ private LocalDateTime tokenExpiresBy;
+
+ @Column(name = "status", length = 10)
+ private String status;
+
+ @Column(name = "entity_type", length = 36)
+ private String entityType;
+
+ @Column(name = "entity_reference", length = 36)
+ private String entityReference;
+
+ public SelfExpiringTokenEntity() {
+ }
+
+ public String getToken() {
+ return token;
+ }
+
+ public void setToken(String token) {
+ this.token = token;
+ }
+
+ public LocalDateTime getCreatedTime() {
+ return createdTime;
+ }
+
+ public void setCreatedTime(LocalDateTime createdTime) {
+ this.createdTime = createdTime;
+ }
+
+ public LocalDateTime getTokenExpiresBy() {
+ return tokenExpiresBy;
+ }
+
+ public void setTokenExpiresBy(LocalDateTime tokenExpiresBy) {
+ this.tokenExpiresBy = tokenExpiresBy;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getEntityType() {
+ return entityType;
+ }
+
+ public void setEntityType(String entityType) {
+ this.entityType = entityType;
+ }
+
+ public String getEntityReference() {
+ return entityReference;
+ }
+
+ public void setEntityReference(String entityReference) {
+ this.entityReference = entityReference;
+ }
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/SelfExpiringTokenRepository.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/SelfExpiringTokenRepository.java
new file mode 100644
index 0000000..a49f4b9
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/repository/SelfExpiringTokenRepository.java
@@ -0,0 +1,26 @@
+/*
+ * 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.cn.deposit.service.internal.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface SelfExpiringTokenRepository extends JpaRepository<SelfExpiringTokenEntity, Long> {
+ Optional<SelfExpiringTokenEntity> findByTokenAndStatus(String token, String status);
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/service/CollectionsService.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/service/CollectionsService.java
new file mode 100644
index 0000000..dd91df9
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/service/CollectionsService.java
@@ -0,0 +1,46 @@
+/*
+ * 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.cn.deposit.service.internal.service;
+
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.CollectionsResponse;
+import org.apache.fineract.cn.deposit.service.ServiceConstants;
+import org.apache.fineract.cn.deposit.service.internal.mapper.CollectionsMapper;
+import org.apache.fineract.cn.deposit.service.internal.repository.CollectionsRepository;
+import org.apache.fineract.cn.lang.ServiceException;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Service;
+
+@Service
+public class CollectionsService {
+ private final Logger logger;
+ private final CollectionsRepository collectionsRepository;
+
+ @Autowired
+ public CollectionsService(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+ CollectionsRepository collectionsRepository) {
+ this.collectionsRepository = collectionsRepository;
+ this.logger = logger;
+ }
+
+ public CollectionsResponse fetchCollection(String collectionReference){
+ return CollectionsMapper.map(this.collectionsRepository.findByCollectionReference(collectionReference)
+ .orElseThrow(() -> ServiceException.notFound("collection {0} not found ", collectionReference)));
+ }
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/service/SelfExpiringTokenService.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/service/SelfExpiringTokenService.java
new file mode 100644
index 0000000..dae344e
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/internal/service/SelfExpiringTokenService.java
@@ -0,0 +1,82 @@
+/*
+ * 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.cn.deposit.service.internal.service;
+
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.TokenStatus;
+import org.apache.fineract.cn.deposit.service.ServiceConstants;
+import org.apache.fineract.cn.deposit.service.internal.repository.SelfExpiringTokenEntity;
+import org.apache.fineract.cn.deposit.service.internal.repository.SelfExpiringTokenRepository;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+import java.security.SecureRandom;
+import java.time.LocalDateTime;
+import java.util.Random;
+
+@Service
+public class SelfExpiringTokenService {
+ private final SelfExpiringTokenRepository selfExpiringTokenRepository;
+ private final Logger logger;
+
+ @Value("${config.otpTokenLength}")
+ private Integer otpTokenLength;
+
+ @Value("${config.tokenExpiryInSeconds}")
+ private Integer tokenExpiryInSeconds;
+
+ @Autowired
+ public SelfExpiringTokenService(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
+ SelfExpiringTokenRepository selfExpiringTokenRepository) {
+ this.selfExpiringTokenRepository = selfExpiringTokenRepository;
+ this.logger = logger;
+ }
+
+ public SelfExpiringTokenEntity generateAndSaveToken(String entityType, String entityReference, LocalDateTime currentTime){
+ SelfExpiringTokenEntity entity = new SelfExpiringTokenEntity();
+ entity.setToken(generateUniqueToken());
+
+ LocalDateTime tokenExpiresBy = currentTime.plusSeconds(tokenExpiryInSeconds);
+ entity.setCreatedTime(currentTime);
+ entity.setTokenExpiresBy(tokenExpiresBy);
+ entity.setStatus(TokenStatus.ACTIVE.name());
+ entity.setEntityType(entityType);
+ entity.setEntityReference(entityReference);
+ this.selfExpiringTokenRepository.save(entity);
+ return entity;
+ }
+
+ private String generateUniqueToken(){
+ String token = generateRandomToken(otpTokenLength);
+ while(this.selfExpiringTokenRepository.findByTokenAndStatus(token, TokenStatus.ACTIVE.name()).isPresent()){
+ token = generateRandomToken(otpTokenLength);
+ }
+ return token;
+ }
+ private String generateRandomToken(Integer length){
+ final char[] buf = new char[length];
+ final String alphanum = "0123456789" ;
+ final char[] symbols = alphanum.toCharArray();
+ final Random random= new SecureRandom();
+ for (int idx = 0; idx < buf.length; ++idx)
+ buf[idx] = symbols[random.nextInt(symbols.length)];
+ return new String(buf);
+ }
+}
diff --git a/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/CollectionsRestController.java b/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/CollectionsRestController.java
new file mode 100644
index 0000000..3c0b30b
--- /dev/null
+++ b/service/src/main/java/org/apache/fineract/cn/deposit/service/rest/CollectionsRestController.java
@@ -0,0 +1,104 @@
+/*
+ * 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.cn.deposit.service.rest;
+
+import org.apache.fineract.cn.anubis.annotation.AcceptedTokenType;
+import org.apache.fineract.cn.anubis.annotation.Permittable;
+import org.apache.fineract.cn.command.domain.CommandCallback;
+import org.apache.fineract.cn.command.gateway.CommandGateway;
+import org.apache.fineract.cn.deposit.api.v1.PermittableGroupIds;
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.CollectionsRequest;
+import org.apache.fineract.cn.deposit.api.v1.collection.domain.data.CollectionsResponse;
+import org.apache.fineract.cn.deposit.service.ServiceConstants;
+import org.apache.fineract.cn.deposit.service.internal.command.CreateCollectionsCommand;
+import org.apache.fineract.cn.deposit.service.internal.command.UpdateCollectionsCommand;
+import org.apache.fineract.cn.deposit.service.internal.service.CollectionsService;
+import org.slf4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/collection")
+public class CollectionsRestController {
+ private final Logger logger;
+ private final CommandGateway commandGateway;
+ private final CollectionsService collectionsService;
+
+ @Autowired
+ public CollectionsRestController(@Qualifier(ServiceConstants.LOGGER_NAME) Logger logger,
+ CommandGateway commandGateway,
+ CollectionsService collectionsService) {
+ this.logger = logger;
+ this.commandGateway = commandGateway;
+ this.collectionsService = collectionsService;
+ }
+ @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.INSTANCE_MANAGEMENT)
+ @RequestMapping(
+ value = "",
+ method = RequestMethod.POST,
+ consumes = MediaType.APPLICATION_JSON_VALUE,
+ produces = MediaType.APPLICATION_JSON_VALUE
+ )
+ public
+ @ResponseBody
+ ResponseEntity<CollectionsResponse> saveCollection(@RequestBody CollectionsRequest requestData)
+ throws Throwable {
+ CommandCallback<CollectionsResponse> result = commandGateway.process(new CreateCollectionsCommand(requestData),
+ CollectionsResponse.class);
+
+ return ResponseEntity.ok(result.get());
+ }
+
+
+ @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.INSTANCE_MANAGEMENT)
+ @RequestMapping(
+ value = "/{collectionsReference}",
+ method = RequestMethod.PUT,
+ consumes = MediaType.APPLICATION_JSON_VALUE,
+ produces = MediaType.APPLICATION_JSON_VALUE
+ )
+ public
+ @ResponseBody
+ ResponseEntity<CollectionsResponse> updateCollection(@PathVariable("collectionsReference") String collectionsReference, @RequestBody CollectionsRequest requestData)
+ throws Throwable {
+ CommandCallback<CollectionsResponse> result = commandGateway.process(new UpdateCollectionsCommand(collectionsReference, requestData),
+ CollectionsResponse.class);
+ return ResponseEntity.ok(result.get());
+ }
+
+
+ @Permittable(value = AcceptedTokenType.TENANT, groupId = PermittableGroupIds.INSTANCE_MANAGEMENT)
+ @RequestMapping(
+ value = "/{collectionsReference}",
+ method = RequestMethod.GET,
+ consumes = MediaType.APPLICATION_JSON_VALUE,
+ produces = MediaType.APPLICATION_JSON_VALUE
+ )
+ public
+ @ResponseBody
+ ResponseEntity<CollectionsResponse> getCollection(@PathVariable("collectionsReference") String collectionsReference)
+ throws Throwable {
+ CollectionsResponse result = collectionsService.fetchCollection(collectionsReference);
+ return ResponseEntity.ok(result);
+ }
+
+
+}
diff --git a/service/src/main/resources/application.yml b/service/src/main/resources/application.yml
index 16e3bee..7a6ef48 100644
--- a/service/src/main/resources/application.yml
+++ b/service/src/main/resources/application.yml
@@ -23,6 +23,9 @@
enabled: false
config:
enabled: false
+ jackson:
+ serialization:
+ write_dates_as_timestamps: false
eureka:
client:
@@ -31,6 +34,7 @@
instance:
homePageUrl: "http://${eureka.hostname}:2027/deposit/v1/"
+
server:
port: 2027
contextPath: /deposit/v1
@@ -72,3 +76,7 @@
config:
fixedAccountId: true
+ otpTokenLength: 6
+ tokenExpiryInSeconds: 172800
+
+
diff --git a/service/src/main/resources/db/migrations/postgresql/V11__collections.sql b/service/src/main/resources/db/migrations/postgresql/V10__collections.sql
similarity index 89%
rename from service/src/main/resources/db/migrations/postgresql/V11__collections.sql
rename to service/src/main/resources/db/migrations/postgresql/V10__collections.sql
index 43f951d..87b7275 100644
--- a/service/src/main/resources/db/migrations/postgresql/V11__collections.sql
+++ b/service/src/main/resources/db/migrations/postgresql/V10__collections.sql
@@ -17,7 +17,6 @@
-- under the License.
--
-
CREATE TABLE shed_collections (
id bigserial NOT null,
transaction_date TIMESTAMP NULL,
@@ -25,17 +24,18 @@
transport_fee_amount numeric(15,5) NULL,
currency VARCHAR(10) NULL,
remarks VARCHAR(1024) NULL,
- account_identifier BIGINT NULL,
- sub_txn_type_id BIGINT NULL,
+ account_identifier varchar(34) NULL,
+ sub_txn_type_id varchar(32) NULL,
status VARCHAR(20) NOT NULL,
c_reference VARCHAR(36) NOT NULL,
+ token BIGINT null,
created_by VARCHAR(32) NOT NULL,
created_on TIMESTAMP NOT NULL,
last_modified_by VARCHAR(32) NULL,
last_modified_on TIMESTAMP NULL,
CONSTRAINT pk_shed_collections PRIMARY KEY (id),
CONSTRAINT uk_shed_collections_ref UNIQUE (c_reference),
- CONSTRAINT fk_collection_sub_txn_type_id FOREIGN KEY (sub_txn_type_id) REFERENCES shed_sub_tx_type (id),
+ CONSTRAINT fk_collection_sub_txn_type_id FOREIGN KEY (sub_txn_type_id) REFERENCES shed_sub_tx_type (identifier),
CONSTRAINT fk_collection_account_identifier FOREIGN KEY (account_identifier) REFERENCES shed_product_instances (account_identifier)
);
@@ -43,10 +43,12 @@
CREATE TABLE shed_collections_inidividual (
id bigserial NOT NULL,
collections_id BIGINT NOT NULL,
- account_identifier BIGINT NULL,
+ account_identifier varchar(34) NULL,
account_external_id VARCHAR(64) NULL,
amount numeric(15,5) NOT NULL,
i_reference VARCHAR(36) NOT NULL,
+ attendance VARCHAR(20) null,
+ token BIGINT null,
CONSTRAINT pk_shed_collections_inidividual PRIMARY KEY (id),
CONSTRAINT uk_shed_collections_inidividual_ref UNIQUE (i_reference),
CONSTRAINT fk_indidual_collections FOREIGN KEY (collections_id) REFERENCES shed_collections (id),
@@ -58,10 +60,11 @@
CREATE TABLE shed_self_expiring_tokens (
id bigserial NOT NULL,
token VARCHAR(10) NOT NULL,
+ created_time TIMESTAMP NOT null,
token_expires_by TIMESTAMP NOT NULL,
status VARCHAR(10) NOT NULL,
entity_type VARCHAR(36) NOT NULL,
entity_reference VARCHAR(36) NOT NULL,
CONSTRAINT pk_shed_self_expiring_tokens PRIMARY KEY (id),
CONSTRAINT uk_shed_self_expiring_tokens_token UNIQUE (token, status)
-);
+);
\ No newline at end of file
diff --git a/service/src/main/resources/db/migrations/postgresql/V8__transaction.sql b/service/src/main/resources/db/migrations/postgresql/V8__transaction.sql
index 8fba6c2..e21e267 100644
--- a/service/src/main/resources/db/migrations/postgresql/V8__transaction.sql
+++ b/service/src/main/resources/db/migrations/postgresql/V8__transaction.sql
@@ -24,6 +24,7 @@
routing_code VARCHAR(40) NULL,
external_id VARCHAR(40) NULL,
a_name VARCHAR(256) NULL,
+ a_type VARCHAR(32) NULL,
description VARCHAR(1024) NULL,
transaction_type VARCHAR(32) NOT NULL,
sub_txn_type VARCHAR(32) NULL,
@@ -35,6 +36,7 @@
nostro_account_identifier VARCHAR(32) NULL,
transaction_date TIMESTAMP NULL,
expiration_date TIMESTAMP NULL,
+ parent_txn_id BIGINT NULL,
created_by VARCHAR(32) NULL,
created_on TIMESTAMP NULL,
last_modified_by VARCHAR(32) NULL,
@@ -42,5 +44,6 @@
CONSTRAINT pk_shed_transactions PRIMARY KEY (id),
CONSTRAINT uk_shed_transactions_id UNIQUE (identifier),
CONSTRAINT shed_txn_sub_txn_type_fk FOREIGN KEY (sub_txn_type) REFERENCES shed_sub_tx_type (identifier),
- CONSTRAINT shed_txn_prod_instance_fk FOREIGN KEY (account_identifier) REFERENCES shed_product_instances (account_identifier)
+ CONSTRAINT shed_txn_prod_instance_fk FOREIGN KEY (account_identifier) REFERENCES shed_product_instances (account_identifier),
+ CONSTRAINT shed_txn_to_txn_fk FOREIGN KEY (parent_txn_id) REFERENCES shed_transactions (id)
);
diff --git a/service/src/main/resources/db/migrations/postgresql/V10__ibb_conf_to_sub_txn.sql b/service/src/main/resources/db/migrations/postgresql/V9__ibb_conf_to_sub_txn.sql
similarity index 90%
rename from service/src/main/resources/db/migrations/postgresql/V10__ibb_conf_to_sub_txn.sql
rename to service/src/main/resources/db/migrations/postgresql/V9__ibb_conf_to_sub_txn.sql
index 1abb9af..6b3ce8a 100644
--- a/service/src/main/resources/db/migrations/postgresql/V10__ibb_conf_to_sub_txn.sql
+++ b/service/src/main/resources/db/migrations/postgresql/V9__ibb_conf_to_sub_txn.sql
@@ -18,7 +18,7 @@
--
ALTER TABLE shed_sub_tx_type
-ADD COLUMN ibb_conf_plus_days INT 0;
+ADD COLUMN ibb_conf_plus_days INT default 0;
ALTER TABLE shed_sub_tx_type
-ADD COLUMN ibb_conf_minus_days INT 0;
+ADD COLUMN ibb_conf_minus_days INT default 0;
diff --git a/service/src/main/resources/db/migrations/postgresql/V9__transaction_update.sql b/service/src/main/resources/db/migrations/postgresql/V9__transaction_update.sql
deleted file mode 100644
index 07bfe2e..0000000
--- a/service/src/main/resources/db/migrations/postgresql/V9__transaction_update.sql
+++ /dev/null
@@ -1,30 +0,0 @@
-<<<<<<< HEAD
---
--- 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.
---
-
-ALTER TABLE shed_transactions
-ADD COLUMN a_type varchar(32) NULL;
-
-ALTER TABLE shed_transactions
-ADD COLUMN parent_txn_id bigint NULL;
-
-ALTER TABLE shed_transactions
-ADD CONSTRAINT shed_txn_to_txn_fk
-FOREIGN KEY (parent_txn_id)
-REFERENCES shed_transactions (id);