diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f4b8d9b980165d3b60c01525c5938a387c245c9..760807404ab5536e9affdb955539ee33ec787c6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## [1.0.75-RELEASE] +### Added +- MS-114/Export sale session data to CSV and upload to FTP server +- +### Changed +- Changes in existing functionality. + +### Deprecated +- Soon-to-be removed features. + +### Removed +- Features that have been removed. + +### Fixed +- Any bug fixes. + +### Security +- Any security improvements. + ## [1.0.74-RELEASE] ### Added - MYD-739/Fix List with sale sessions stats diff --git a/src/main/java/com/marketingconfort/mydressin/Application.java b/src/main/java/com/marketingconfort/mydressin/Application.java index 2c6a023f09c6391ecf7dbf6a680540d5ee0fb0c0..f0c5a4ca698b99b28bb76aa7cfd4bbe596b75e9d 100644 --- a/src/main/java/com/marketingconfort/mydressin/Application.java +++ b/src/main/java/com/marketingconfort/mydressin/Application.java @@ -1,15 +1,18 @@ package com.marketingconfort.mydressin; +import com.marketingconfort.mydressin.config.FTPConfig; import com.marketingconfort.starter.core.config.props.CustomRestProperties; import com.marketingconfort.starter.core.services.MCRestTemplateService; import com.marketingconfort.starter.core.services.implementations.*; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.Import; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication(scanBasePackages = "com.marketingconfort.mydressin") @ComponentScan(basePackages = { @@ -25,6 +28,7 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories; "com.marketingconfort.mydressin.common.cart.models" }) @Import({MCRestTemplateService.class, CustomRestProperties.class}) +@EnableScheduling public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); diff --git a/src/main/java/com/marketingconfort/mydressin/config/FTPConfig.java b/src/main/java/com/marketingconfort/mydressin/config/FTPConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..96cadec05614754f7fc35a0033df2609f464b171 --- /dev/null +++ b/src/main/java/com/marketingconfort/mydressin/config/FTPConfig.java @@ -0,0 +1,29 @@ +package com.marketingconfort.mydressin.config; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Component; + + +@Primary +@Component +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@ConfigurationProperties(prefix = "ftp") +public class FTPConfig { + + + private String host; + + private String username; + + private String password; + +} diff --git a/src/main/java/com/marketingconfort/mydressin/repositories/SaleSessionRepository.java b/src/main/java/com/marketingconfort/mydressin/repositories/SaleSessionRepository.java index 15e4d76c6867796a1bcf13e8197ec72c67cbed79..268e9c0962a375672894462eacd9098e6643c40b 100644 --- a/src/main/java/com/marketingconfort/mydressin/repositories/SaleSessionRepository.java +++ b/src/main/java/com/marketingconfort/mydressin/repositories/SaleSessionRepository.java @@ -120,4 +120,7 @@ public interface SaleSessionRepository extends JpaRepository<SaleSession, Long> @Param("name") String name , @Param("startDate") Date startDate , @Param("endDate") Date endDate); + + @Query("SELECT s FROM SaleSession s WHERE CAST(s.startedDate AS date ) = :yesterdayDate ") + List<SaleSession> getAllYesterdaySaleSessions(@Param("yesterdayDate") Date yesterdayDate); } diff --git a/src/main/java/com/marketingconfort/mydressin/services/SaleSessionService.java b/src/main/java/com/marketingconfort/mydressin/services/SaleSessionService.java index a0314fc418b52ec3a07796568b9b39deef2fde87..9959efd5d6f363a7382f494b636941e7b40b46d0 100644 --- a/src/main/java/com/marketingconfort/mydressin/services/SaleSessionService.java +++ b/src/main/java/com/marketingconfort/mydressin/services/SaleSessionService.java @@ -39,4 +39,6 @@ public interface SaleSessionService { Page<SaleSessionDTO> getDeletedSaleSessionsPage(String name , String type, Date startDate, Date endDate, int page, int size, String sortBy, String order); List<String[]> getDeletedTypeCount(String name , String type, Date startDate, Date endDate); + + void exportYesterdaySaleSessions(); } diff --git a/src/main/java/com/marketingconfort/mydressin/services/impl/SaleSessionServiceImp.java b/src/main/java/com/marketingconfort/mydressin/services/impl/SaleSessionServiceImp.java index c5e5fa66564ed586cfc52bbd21023af1c614ffe4..21041c2277379522bd38ebcae6571122147b0732 100644 --- a/src/main/java/com/marketingconfort/mydressin/services/impl/SaleSessionServiceImp.java +++ b/src/main/java/com/marketingconfort/mydressin/services/impl/SaleSessionServiceImp.java @@ -6,7 +6,7 @@ import com.marketingconfort.mydressin.common.cart.enumurations.ItemCartStatus; import com.marketingconfort.mydressin.common.cart.enumurations.ItemSource; import com.marketingconfort.mydressin.common.cart.enumurations.OrderStatus; import com.marketingconfort.mydressin.common.cart.models.*; - +import com.marketingconfort.mydressin.config.FTPConfig; import com.marketingconfort.mydressin.dtos.SaleSessionDTO; import com.marketingconfort.mydressin.dtos.SessionOrderDTO; import com.marketingconfort.mydressin.dtos.ItemRequestDTO; @@ -18,18 +18,29 @@ import com.marketingconfort.mydressin.mappers.SaleSessionMapper; import com.marketingconfort.mydressin.repositories.SaleSessionRepository; import com.marketingconfort.mydressin.repositories.SessionOrderRepository; import com.marketingconfort.mydressin.services.*; +import com.opencsv.CSVWriter; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.net.ftp.FTPClient; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.beans.factory.annotation.Value; +import java.io.*; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.ZoneId; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; @@ -38,6 +49,8 @@ import java.util.stream.Collectors; @Service @Slf4j public class SaleSessionServiceImp implements SaleSessionService { + private static final Logger logger = LoggerFactory.getLogger(SaleSessionServiceImp.class); + private final SaleSessionRepository saleSessionRepository; private final SaleSessionMapper saleSessionMapper; private final ItemCartService itemCartService; @@ -45,8 +58,11 @@ public class SaleSessionServiceImp implements SaleSessionService { private final ProductStockService productStockService; private final SessionOrderRepository orderRepository; private final SessionOrderMapper sessionOrderMapper; + private final SessionOrderService sessionOrderService; private final UnregisteredCartService unregisteredCartService; private final SessionOrderRepository sessionOrderRepository; + private FTPConfig ftpConfig; + @Override @@ -309,4 +325,124 @@ public class SaleSessionServiceImp implements SaleSessionService { return stringList; } + @Scheduled(cron = "0 0 7 * * ?") + @Override + public void exportYesterdaySaleSessions() { + LocalDate yesterday = LocalDate.now().minusDays(1); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + String formattedDate = yesterday.format(formatter); + + logger.info("Starting export of yesterday's sale sessions for date: {}", formattedDate); + + List<SaleSession> saleSessions = saleSessionRepository.getAllYesterdaySaleSessions( + Date.from(yesterday.atStartOfDay(ZoneId.systemDefault()).toInstant()) + ); + logger.info("Sale sessions count: {}", saleSessions.size()); + + String fileName = "sale_sessions_" + formattedDate + ".csv"; + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + CSVWriter writer = new CSVWriter(new OutputStreamWriter(byteArrayOutputStream))) { + + writer.writeNext(new String[]{ + "idUtilisateur", "idCommande", "idSessionVente", "nameSession", "startDate", "expirationDate", "description", + "lastModification", "typeLive", "liveId", "statutPanier", "statutCommande", "prenom", "nom", "email", "telephone", + "nomUtilisateur", "nomProduit", "UGS", "quantite", "prixProduit", "montant", "dateCreationProduitCommande", "dateExpiration" + }); + + logger.info("CSV header written successfully for date: {}", formattedDate); + + for (SaleSession saleSession : saleSessions) { + logger.debug("Processing SaleSession with ID: {}", saleSession.getId()); + List<SessionOrderDTO> sessionOrderDTOS = sessionOrderService.getAllOrdersBySessionId(saleSession.getId()); + + for (SessionOrderDTO order : sessionOrderDTOS) { + List<String> row = new ArrayList<>(); + + String clientUid = order.getClient() != null ? String.valueOf(order.getClient().getUid()) : "N/A"; + String firstName = order.getClient() != null ? order.getClient().getFirstName() : "N/A"; + String lastName = order.getClient() != null ? order.getClient().getLastName() : "N/A"; + String email = order.getClient() != null ? order.getClient().getEmail() : "N/A"; + String phoneNumber = order.getClient() != null ? order.getClient().getPhoneNumber() : "N/A"; + String pseudo = order.getClient() != null ? order.getClient().getPseudo() : "N/A"; + + row.add(clientUid); + row.add(String.valueOf(order.getId())); + row.add(String.valueOf(saleSession.getId())); + row.add(saleSession.getName()); + row.add(saleSession.getStartedDate() != null ? saleSession.getStartedDate().toString() : ""); + row.add(saleSession.getExpirationDate() != null ? saleSession.getExpirationDate().toString() : ""); + row.add(saleSession.getDescription() != null ? saleSession.getDescription() : ""); + row.add(saleSession.getLastModification() != null ? saleSession.getLastModification().toString() : ""); + row.add(saleSession.getType() != null ? saleSession.getType().name() : ""); + row.add(String.valueOf(saleSession.getLive())); + row.add(order.getStatus().name()); + row.add(order.getStatus().name()); + row.add(firstName); + row.add(lastName); + row.add(email); + row.add(phoneNumber); + row.add(pseudo); + row.add(order.getItemCartDTO() != null ? order.getItemCartDTO().getProduct().getName() : ""); + row.add(order.getItemCartDTO() != null ? order.getItemCartDTO().getUgs() : ""); + row.add(String.valueOf(order.getItemCartDTO() != null ? order.getItemCartDTO().getQuantity() : 0)); + row.add(order.getCreatedDate() != null ? order.getCreatedDate().toString() : ""); + row.add(String.valueOf(order.getPrice())); + row.add(String.valueOf(order.getPrice())); + row.add(order.getItemCartDTO() != null && order.getItemCartDTO().getExpirationDate() != null ? order.getItemCartDTO().getExpirationDate().toString() : ""); + + writer.writeNext(row.toArray(new String[0])); + } + } + writer.flush(); + logger.info("Export completed successfully for date: {}", formattedDate); + + // Now upload the data to the FTP server + uploadFileToFTPServer(fileName, byteArrayOutputStream.toByteArray()); + } catch (IOException e) { + logger.error("Error occurred during export for date: {}", formattedDate, e); + } + } + + private void uploadFileToFTPServer(String fileName, byte[] fileData) { + FTPClient ftpClient = new FTPClient(); + try { + ftpClient.connect(ftpConfig.getHost()); + ftpClient.login(ftpConfig.getUsername(), ftpConfig.getPassword()); + + logger.info("Connected to FTP server."); + + ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE); + ftpClient.enterLocalPassiveMode(); + + logger.info("FTP Response Code: {}", ftpClient.getReplyCode()); + logger.info("FTP Reply String: {}", ftpClient.getReplyString()); + + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(fileData)) { + boolean done = ftpClient.storeFile("/sale_sessions/" + fileName, byteArrayInputStream); + if (done) { + logger.info("File uploaded successfully to FTP server."); + } else { + int replyCode = ftpClient.getReplyCode(); + logger.error("Failed to upload file, FTP server reply: {}", replyCode); + } + } + + ftpClient.logout(); + logger.info("Logged out from FTP server."); + + } catch (IOException e) { + logger.error("Error occurred while uploading file to FTP server: ", e); + } finally { + try { + if (ftpClient.isConnected()) { + ftpClient.disconnect(); + } + } catch (IOException e) { + logger.error("Error occurred while disconnecting from FTP server: ", e); + } + } + } + + } +