Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PAY-5978 Duplicate Payment Report #1492

Closed
wants to merge 27 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
960f412
initial PR
Apr 4, 2023
41e9f26
Surpressions removed for initial build
Apr 5, 2023
d17f77d
controller and recent master pull for cve-resolution
Apr 21, 2023
cf2daf2
sonarcube properties for eval report surpression
May 4, 2023
7812c05
sonarcube properties for eval report surpression
May 4, 2023
c4cb0c7
sonarcube properties for eval report surpression test
May 9, 2023
e42f9f2
added test for duplicate payment
Jun 7, 2023
f61edcd
Updated from master.
davejones74 Sep 4, 2023
71eac8d
Updated to support local development.
davejones74 Sep 5, 2023
3e5115a
PAY-5978: Database changes for running native query.
davejones74 Sep 5, 2023
938d195
PAY-5978: Updated to add report config.
davejones74 Sep 5, 2023
314143a
Updated to connect to the DB.
davejones74 Sep 6, 2023
8bd8f79
Bumping chart version/ fixing aliases
hmcts-jenkins-a-to-c[bot] Sep 7, 2023
acfc2f5
Merge branch 'master' into PAY-5978-DuplicatePayment
davejones74 Apr 30, 2024
65ecf3f
Bumping chart version/ fixing aliases
hmcts-jenkins-a-to-c[bot] Apr 30, 2024
6a68f45
Merge branch 'master' into PAY-5978-DuplicatePayment
davejones74 May 15, 2024
c402b7b
Bumping chart version/ fixing aliases
hmcts-jenkins-a-to-c[bot] May 15, 2024
99da15a
Add missing import.
davejones74 May 15, 2024
feb2109
Minor correction to PbaSmcPaymentReportConfig.
davejones74 May 15, 2024
4bd72d7
Adding reference to secret email-to.
davejones74 May 15, 2024
6052add
Test class for Payment Report
Jun 7, 2024
095a9dc
Bumping chart version/ fixing aliases
hmcts-jenkins-a-to-c[bot] Jun 7, 2024
4530777
PAY-5978: Updated tests.
davejones74 Jun 10, 2024
f9471be
PAY-5978: Fix coverage.
davejones74 Jun 10, 2024
534ec35
Bumping chart version/ fixing aliases
hmcts-jenkins-a-to-c[bot] Jul 2, 2024
469541a
Merge branch 'master' into PAY-5978-DuplicatePayment
davejones74 Jul 2, 2024
d70d59a
Merge branch 'master' of github.com:hmcts/ccpay-payment-app into PAY-…
davejones74 Sep 2, 2024
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
Binary file added .DS_Store
Binary file not shown.
2 changes: 1 addition & 1 deletion api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ serenity {
reports = ["single-page-html"]
}

def getCheckedOutGitCommitHash() {
static def getCheckedOutGitCommitHash() {
'git rev-parse --verify --short HEAD'.execute().text.trim()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,16 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import uk.gov.hmcts.payment.api.reports.PaymentReportType;
import uk.gov.hmcts.payment.api.reports.config.BarPaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.CardPaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.PaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.PbaCmcPaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.PbaCivilPaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.PbaDivorcePaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.PbaFinremPaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.PbaProbatePaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.PbaFplPaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.PbaPrlPaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.PbaIacPaymentReportConfig;
import uk.gov.hmcts.payment.api.reports.config.PbaSmcPaymentReportConfig;

import uk.gov.hmcts.payment.api.reports.config.*;

import java.util.Map;

@Configuration
public class PaymentReportConfiguration {

@Bean
public Map<PaymentReportType, PaymentReportConfig> configMap(CardPaymentReportConfig cardPaymentReportConfig,
public Map<PaymentReportType, PaymentReportConfig> configMap(DuplicatePaymentReportConfig duplicatePaymentReportConfig,
CardPaymentReportConfig cardPaymentReportConfig,
BarPaymentReportConfig barPaymentReportConfig,
PbaCmcPaymentReportConfig pbaCmcPaymentReportConfig,
PbaProbatePaymentReportConfig pbaProbatePaymentReportConfig,
Expand All @@ -36,6 +25,7 @@ public Map<PaymentReportType, PaymentReportConfig> configMap(CardPaymentReportCo
PbaIacPaymentReportConfig pbaIacPaymentReportConfig,
PbaSmcPaymentReportConfig pbaSmcPaymentReportConfig) {
return ImmutableMap.<PaymentReportType, PaymentReportConfig>builder()
.put(PaymentReportType.DUPLICATE_PAYMENT, duplicatePaymentReportConfig)
.put(PaymentReportType.CARD, cardPaymentReportConfig)
.put(PaymentReportType.DIGITAL_BAR, barPaymentReportConfig)
.put(PaymentReportType.PBA_CMC, pbaCmcPaymentReportConfig)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,23 @@ public void generateAndEmailReport(@RequestParam(name = "payment_method", requir

paymentsReportFacade.generateCsvAndSendEmail(fromDate, toDate, paymentMethodTypeName, service);
}

@Operation(summary = "API to generate report for payment failure ", description = "Get list of payments failures by providing date range. MM/dd/yyyy is the supported date/time format.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Report Generated, email only sent if data exists"),
@ApiResponse(responseCode = "500", description = "Internal Server Error")
})
@PostMapping(value = "/jobs/duplicate-payment-process")
public void duplicatePaymentEmailReport(@RequestParam(name = "start_date", required = false) Optional<String> startDateString,
@RequestParam(name = "end_date", required = false) Optional<String> endDateString) {

LOG.info("Inside /jobs/duplicate-payment-process");
validator.validateToFromDates(startDateString, endDateString);

Date fromDate = startDateString.map(s -> clock.atStartOfDay(s, FORMATTER)).orElseGet(clock::getYesterdayDate);
Date toDate = endDateString.map(s -> clock.atEndOfDay(s, FORMATTER)).orElseGet(clock::getTodayDate);

paymentsReportFacade.generateDuplicatePaymentCsvAndSendEmail(fromDate, toDate);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

public enum PaymentReportType {

DUPLICATE_PAYMENT,
CARD,
DIGITAL_BAR,
PBA_CMC,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,30 @@ public void generateCsvAndSendEmail(Date startDate, Date endDate, PaymentMethodT
PaymentReportConfig reportConfig = configMap.get(PaymentReportType.from(paymentMethodType, serviceType));
if (reportConfig.isEnabled()) {
LOG.info("payments report flag is enabled for type :{} and service :{}. creating csv", paymentMethodType, serviceType);
reportService.generateCsvAndSendEmail(startDate, endDate, paymentMethodType, serviceType, reportConfig);
reportService.generateCsvAndSendEmail(
startDate,
endDate,
paymentMethodType,
serviceType,
reportConfig);
} else {
LOG.info("payments report flag is disabled for type :{} and service :{}. So, system will not send CSV email", paymentMethodType, serviceType);
}
}

public void generateDuplicatePaymentCsvAndSendEmail(Date startDate, Date endDate) {
LOG.info("Inside generateDuplicatePaymentCsvAndSendEmail with startDate: {} and endDate: {}",
startDate, endDate);
PaymentReportConfig reportConfig = configMap.get(PaymentReportType.DUPLICATE_PAYMENT);
if (reportConfig.isEnabled()) {
LOG.info("duplicate payments report flag is enabled. creating csv");
reportService.generateDuplicatePaymentsCsvAndSendEmail(
startDate,
endDate,
reportConfig);
} else {
LOG.info("duplicate payments report flag is disabled. So, system will not send CSV email");
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,23 @@
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import uk.gov.hmcts.payment.api.dto.DuplicatePaymentDto;
import uk.gov.hmcts.payment.api.contract.PaymentDto;
import uk.gov.hmcts.payment.api.dto.PaymentSearchCriteria;
import uk.gov.hmcts.payment.api.dto.mapper.PaymentDtoMapper;
import uk.gov.hmcts.payment.api.email.Email;
import uk.gov.hmcts.payment.api.email.EmailService;
import uk.gov.hmcts.payment.api.model.Payment2Repository;
import uk.gov.hmcts.payment.api.model.PaymentFeeLink;
import uk.gov.hmcts.payment.api.reports.config.PaymentReportConfig;
import uk.gov.hmcts.payment.api.service.DelegatingPaymentService;
import uk.gov.hmcts.payment.api.util.PaymentMethodType;

import javax.persistence.Tuple;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
Expand All @@ -42,15 +47,20 @@ public class PaymentsReportService {

private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss");

private final Payment2Repository paymentRepository;
private final DelegatingPaymentService<PaymentFeeLink, String> delegatingPaymentService;


private final PaymentDtoMapper paymentDtoMapper;
private final EmailService emailService;
private final FeesService feesService;

@Autowired
public PaymentsReportService(@Qualifier("loggingPaymentService") DelegatingPaymentService<PaymentFeeLink, String> delegatingPaymentService, PaymentDtoMapper paymentDtoMapper,
public PaymentsReportService(@Qualifier("loggingPaymentService") DelegatingPaymentService<PaymentFeeLink, String> delegatingPaymentService,
Payment2Repository paymentRepository, PaymentDtoMapper paymentDtoMapper,
EmailService emailService, FeesService feesService) {
this.delegatingPaymentService = delegatingPaymentService;
this.paymentRepository = paymentRepository;
this.paymentDtoMapper = paymentDtoMapper;
this.emailService = emailService;
this.feesService = feesService;
Expand All @@ -67,6 +77,21 @@ public void generateCsvAndSendEmail(Date startDate, Date endDate, PaymentMethodT
LOG.info("End of payments csv report for method type :{} and service name :{}", paymentMethodType, serviceName);
}

public void generateDuplicatePaymentsCsvAndSendEmail(Date startDate, Date endDate, PaymentReportConfig reportConfig) {

LOG.info("Start of duplicate payments csv report");

List<DuplicatePaymentDto> duplicatePaymentsCsvData = findDuplicatePaymentsBy(startDate, endDate);

if (duplicatePaymentsCsvData.isEmpty()) {
LOG.info("No duplicate payments were found, skipping email");
} else {
generateDuplicatePaymentReportCsvAndSendEmail(duplicatePaymentsCsvData, reportConfig);
}

LOG.info("End of duplicate payments csv report");
}

private List<PaymentDto> findPaymentsBy(Date startDate, Date endDate, PaymentMethodType paymentMethodType, String serviceName) {
String serviceType = serviceName;
String paymentMethodTypeString = Optional.ofNullable(paymentMethodType).map(PaymentMethodType::getType).orElse(null);
Expand All @@ -85,6 +110,28 @@ private List<PaymentDto> findPaymentsBy(Date startDate, Date endDate, PaymentMet
.collect(Collectors.toList());
}

private List<DuplicatePaymentDto> findDuplicatePaymentsBy(Date startDate, Date endDate) {
LOG.info("Inside findDuplicatePaymentsBy");

// Use of Tuple requires the data in the nativeQuery is returned the same order as presented in the DTO.
List<Tuple> duplicatePaymentsTuples = paymentRepository.findDuplicatePaymentsByDate(startDate, endDate);

List<DuplicatePaymentDto> duplicatePaymentsDto = duplicatePaymentsTuples.stream()
.map(tup -> new DuplicatePaymentDto(
tup.get(0, Date.class),
tup.get(1, String.class),
tup.get(2, String.class),
tup.get(3, BigDecimal.class),
tup.get(4, String.class),
tup.get(5, String.class),
tup.get(6, Integer.class),
tup.get(7, BigInteger.class)
))
.collect(Collectors.toList());

return duplicatePaymentsDto;
}

private void generateCsvAndSendEmail(List<PaymentDto> payments, PaymentReportConfig reportConfig) {
LOG.info("CollectionUtils.isNotEmpty(payments): {}", CollectionUtils.isNotEmpty(payments));
String paymentsCsvFileName = reportConfig.getCsvFileNamePrefix() + LocalDateTime.now().format(formatter) + PAYMENTS_CSV_FILE_EXTENSION;
Expand All @@ -98,6 +145,19 @@ private void generateCsvAndSendEmail(List<PaymentDto> payments, PaymentReportCon
sendEmail(email, paymentsByteArray, paymentsCsvFileName);
}

private void generateDuplicatePaymentReportCsvAndSendEmail(List<DuplicatePaymentDto> duplicatePayments, PaymentReportConfig reportConfig) {
LOG.info("CollectionUtils.isNotEmpty(duplicatePayments): {}", CollectionUtils.isNotEmpty(duplicatePayments));
String paymentsCsvFileName = reportConfig.getCsvFileNamePrefix() + LocalDateTime.now().format(formatter) + PAYMENTS_CSV_FILE_EXTENSION;
byte[] paymentsByteArray = createDuplicatePaymentsCsvByteArray(duplicatePayments, paymentsCsvFileName, reportConfig);
Email email = Email.emailWith()
.from(reportConfig.getFrom())
.to(reportConfig.getTo())
.subject(reportConfig.getSubject())
.message(reportConfig.getMessage())
.build();
sendEmail(email, paymentsByteArray, paymentsCsvFileName);
}

private void sendEmail(Email email, byte[] paymentsCsvByteArray, String csvFileName) {
email.setAttachments(newArrayList(csv(paymentsCsvByteArray, csvFileName)));
emailService.sendEmail(email);
Expand All @@ -120,5 +180,22 @@ private byte[] createPaymentsCsvByteArray(List<PaymentDto> payments, String paym
}
return paymentsCsvByteArray;
}

private byte[] createDuplicatePaymentsCsvByteArray(List<DuplicatePaymentDto> duplicatePayments, String paymentsCsvFileName, PaymentReportConfig reportConfig) {
byte[] paymentsCsvByteArray = null;
try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
bos.write(reportConfig.getCsvHeader().getBytes(utf8));
bos.write(BYTE_ARRAY_OUTPUT_STREAM_NEWLINE.getBytes(utf8));
for (DuplicatePaymentDto duplicatePayment : duplicatePayments) {
bos.write(reportConfig.getCsvRecord(duplicatePayment).getBytes(utf8));
bos.write(BYTE_ARRAY_OUTPUT_STREAM_NEWLINE.getBytes(utf8));
}
LOG.info("PaymentsReportService - Total {} duplicate payment records written in csv file {}", duplicatePayments.size(), paymentsCsvFileName);
paymentsCsvByteArray = bos.toByteArray();
} catch (IOException ex) {
LOG.error("PaymentsReportService - Error while creating duplicate payments csv file {}. Error message is {}", paymentsCsvFileName, ex.getMessage());
}
return paymentsCsvByteArray;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class BarPaymentReportConfig implements PaymentReportConfig {
public class BarPaymentReportConfig implements PaymentReportConfig<PaymentDto> {

private static final String PAYMENTS_HEADER = "Service,Payment Group reference,Payment reference," +
"CCD reference,Case reference,Organisation name,Customer internal reference,PBA Number,Payment created date," +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class CardPaymentReportConfig implements PaymentReportConfig {
public class CardPaymentReportConfig implements PaymentReportConfig<PaymentDto> {

private static final String CARD_PAYMENTS_HEADER = "Service,Payment Group reference,Payment reference," +
"CCD reference,Case reference,Payment created date,Payment status updated date,Payment status," +
Expand All @@ -33,10 +33,7 @@ public class CardPaymentReportConfig implements PaymentReportConfig {
private boolean enabled;

@Override
public PaymentReportType getType() {
return PaymentReportType.CARD;
}

public PaymentReportType getType() { return PaymentReportType.CARD; }
@Override
public String getCsvHeader() {
return CARD_PAYMENTS_HEADER;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package uk.gov.hmcts.payment.api.reports.config;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import uk.gov.hmcts.payment.api.contract.PaymentDto;
import uk.gov.hmcts.payment.api.dto.DuplicatePaymentDto;
import uk.gov.hmcts.payment.api.reports.PaymentReportType;

@Component
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class DuplicatePaymentReportConfig implements PaymentReportConfig<DuplicatePaymentDto> {

private static final String DUPLICATE_PAYMENTS_HEADER = "Date,Time,CCD Case Number,Service Type,Amount,"
+ "Payment Channel,Payment Method,Payment Link ID,Count";

private static final String DUPLICATE_PAYMENTS_CSV_FILE_PREFIX = "hmcts_potential_duplicate_payments_";

@Value("${duplicate.payments.email.from:dummy}")
private String from;
@Value("${duplicate.payments.email.to:dummy}")
private String[] to;
@Value("${duplicate.payments.email.subject:dummy}")
private String subject;
@Value("${duplicate.payments.email.message:dummy}")
private String message;
@Value("${duplicate.payments.report.scheduler.enabled:false}")
private boolean enabled;
@Override
public PaymentReportType getType() { return PaymentReportType.DUPLICATE_PAYMENT; }
@Override
public String getCsvHeader() {
return DUPLICATE_PAYMENTS_HEADER;
}
@Override
public String getCsvRecord(DuplicatePaymentDto duplicatePaymentDto) {
return duplicatePaymentDto.toDuplicatePaymentCsv();
}
@Override
public String getCsvFileNamePrefix() {
return DUPLICATE_PAYMENTS_CSV_FILE_PREFIX;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package uk.gov.hmcts.payment.api.reports.config;

import uk.gov.hmcts.payment.api.contract.PaymentDto;
import uk.gov.hmcts.payment.api.dto.DuplicatePaymentDto;
import uk.gov.hmcts.payment.api.reports.PaymentReportType;

public interface PaymentReportConfig {
public interface PaymentReportConfig<T> {

PaymentReportType getType();

Expand All @@ -21,6 +22,6 @@ public interface PaymentReportConfig {

String getCsvHeader();

String getCsvRecord(PaymentDto paymentDto);
String getCsvRecord(T dto);

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class PbaCivilPaymentReportConfig implements PaymentReportConfig {
public class PbaCivilPaymentReportConfig implements PaymentReportConfig<PaymentDto> {

private static final String CREDIT_ACCOUNT_PAYMENTS_HEADER = "Service,Payment Group reference,Payment reference," +
"CCD reference,Case reference,Organisation name,Customer internal reference,PBA Number,Payment created date," +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class PbaCmcPaymentReportConfig implements PaymentReportConfig {
public class PbaCmcPaymentReportConfig implements PaymentReportConfig<PaymentDto> {

private static final String CREDIT_ACCOUNT_PAYMENTS_HEADER = "Service,Payment Group reference,Payment reference," +
"CCD reference,Case reference,Organisation name,Customer internal reference,PBA Number,Payment created date," +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class PbaDivorcePaymentReportConfig implements PaymentReportConfig {
public class PbaDivorcePaymentReportConfig implements PaymentReportConfig<PaymentDto> {

private static final String CREDIT_ACCOUNT_PAYMENTS_HEADER = "Service,Payment Group reference,Payment reference," +
"CCD reference,Case reference,Organisation name,Customer internal reference,PBA Number,Payment created date," +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class PbaFinremPaymentReportConfig implements PaymentReportConfig {
public class PbaFinremPaymentReportConfig implements PaymentReportConfig<PaymentDto> {

private static final String CREDIT_ACCOUNT_PAYMENTS_HEADER = "Service,Payment Group reference,Payment reference," +
"CCD reference,Case reference,Organisation name,Customer internal reference,PBA Number,Payment created date," +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class PbaFplPaymentReportConfig implements PaymentReportConfig {
public class PbaFplPaymentReportConfig implements PaymentReportConfig<PaymentDto> {

private static final String CREDIT_ACCOUNT_PAYMENTS_HEADER = "Service,Payment Group reference,Payment reference," +
"CCD reference,Case reference,Organisation name,Customer internal reference,PBA Number,Payment created date," +
Expand Down
Loading
Loading