Mysterious Logical Error in Multi-Cell Combinational CSV File Analytics Java Program

this question will be a bit of a coding challenge perhaps, as I am not sure what exactly has gone wrong in the 400-odd lines of the class in question (though I suspect it will be related to either compareSalesWithCampaigns(), getMaxCampaignBonusForDate(), isProductEligibleForCampaign(), getCampaignBonus(), getCampaignBonusForDate() or isCampaignDateValid() ).

in short, we have a bug in a java program the company was hoping to use to track bonus (free) products that it gives customers (the company is a baby products distributor, and they sell to pharmacies through warehouses). The problem is that warehouses sometimes sell products with extra bonuses which are not in the official campaigns list for that month, and so when they send us back their sales data, the company needs to track this and inform them of cuts.

The issue in the code, which functions as part of a wider program which standardises and analyses the data in question, occurs when a campaign which has minimum quantity requirements, and a certain pharmacy (identified by the “PHARMACY GLN NUMBER” unique tag) satisfies minimum order quantity for the first product, but not the second one. That campaign in question, which is represented by one line in a bonuses.csv file, should not be valid for the first product sale either. in line 10 in the below example/test input file, this is the only situation in which the code fails – it should cut 3 of the 6 bonuses, but it does not.

This is the code so far:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>package mf_app.BonusCalculate_3;
import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;
import tocsv.UniversalConverter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;
public class BonusTracker {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
private static final String[] dateFormats = {
"dd/MM/yyyy", "d/M/yyyy", "dd/M/yyyy", "d/MM/yyyy", // Original formats with slash as separator
"dd.MM.yyyy", "d.M.yyyy", "dd.M.yyyy", "d.MM.yyyy", "dd.MM.yy", // Formats with dot as separator
"MM/dd/yyyy", "M/d/yyyy", "MM/d/yyyy", "M/dd/yyyy", // Additional US-style formats with slash as separator
"MM/dd/yy"
};
private static final NumberFormat US_FORMAT = NumberFormat.getInstance(Locale.US);
private static final NumberFormat EU_FORMAT = NumberFormat.getInstance(Locale.GERMANY);
private static List<Map<String, String>> campaignData; // Added campaignData as class-level variable
public static void main(String[] args) {
String bonusesDirectoryPath = "./data/0_UTILITIES/BONUSES";
String salesFilePath = "./data/5_1_CALCULATIONS_1/CALCULATED_1.csv";
String reportFilePath = "./data/5_2_MF_CHECKED/MF_CHECKED.csv";
try {
String campaignsFilePath = findAndConvertBonusesFile(bonusesDirectoryPath);
if (campaignsFilePath == null) {
System.err.println("No bonuses CSV or Excel file found in the directory: " + bonusesDirectoryPath);
System.exit(1);
}
ParsedCSV salesParsedCSV = parseCSV(salesFilePath);
ParsedCSV campaignsParsedCSV = parseCSV(campaignsFilePath);
campaignData = campaignsParsedCSV.data;
List<Map<String, String>> discrepancies = compareSalesWithCampaigns(salesParsedCSV.data, campaignData);
System.out.println("Total Discrepancies Found: " + discrepancies.size());
generateReport(discrepancies, reportFilePath, salesParsedCSV.headers);
System.out.println("Discrepancies reported: " + discrepancies.size());
System.out.println("Report generated successfully: " + reportFilePath);
} catch (IOException | ParseException e) {
e.printStackTrace();
System.exit(1); // Exit with error status
} catch (Error err) {
// This block catches OutOfMemoryError and other serious errors
System.err.println("A severe error occurred: " + err.getMessage());
if (err instanceof OutOfMemoryError) {
System.err.println("The application ran out of memory. Consider increasing the heap size or optimizing memory usage.");
} else {
System.err.println("Please check the application's environment and configuration.");
}
System.exit(2); // Exit with a specific error status for this type of failure
}
}
private static String findAndConvertBonusesFile(String directoryPath) throws IOException {
File directory = new File(directoryPath);
if (directory.isDirectory()) {
// Check for Excel file first
File[] excelFiles = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".xlsx"));
if (excelFiles != null && excelFiles.length > 0) {
System.out.println("excel not found!");
// Convert the first Excel file found to CSV
String excelFilePath = excelFiles[0].getPath();
String csvFilePath = directoryPath + "/" + excelFiles[0].getName().replace(".xlsx", ".csv");
UniversalConverter.convertFiles(directoryPath, directoryPath);
return csvFilePath;
}
// If no Excel file, check for CSV file
File[] csvFiles = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".csv"));
if (csvFiles != null && csvFiles.length > 0) {
System.out.println("csv not found!");
return csvFiles[0].getPath();
}
}
return null;
}
// static ParsedCSV class:
static class ParsedCSV {
List<String> headers;
List<Map<String, String>> data;
public ParsedCSV(List<String> headers, List<Map<String, String>> data) {
this.headers = headers;
this.data = data;
}
}
private static ParsedCSV parseCSV(String filePath) throws IOException {
List<Map<String, String>> data = new ArrayList<>();
List<String> headersList = new ArrayList<>();
try (CSVReader reader = new CSVReader(new FileReader(filePath))) {
String[] headers = reader.readNext();
if (headers != null) {
for (int i = 0; i < headers.length; i++) {
headers[i] = headers[i].replaceAll("[^\x20-\x7E]", "").trim();
headersList.add(headers[i]);
}
}
String[] line;
while ((line = reader.readNext()) != null) {
Map<String, String> row = new HashMap<>();
for (int i = 0; i < headers.length; i++) {
row.put(headers[i], line[i]);
}
data.add(row);
}
}
return new ParsedCSV(headersList, data);
}
private static List<Map<String, String>> compareSalesWithCampaigns(List<Map<String, String>> salesData, List<Map<String, String>> campaignData) throws ParseException {
List<Map<String, String>> discrepancies = new ArrayList<>();
for (Map<String, String> sale : salesData) {
boolean noSale = false;
boolean saleNoBonus = false;
String productBarcode = sale.get("BARCODE"); // Use the barcode instead of the product name
int saleQuantity = 0;
int saleBonus = 0;
try {
saleQuantity = parseEuropeanInteger(sale.get("QUANTITY"));
} catch (NumberFormatException e) {
noSale = true;
saleQuantity = 0;
}
if(!noSale) {
try {
saleBonus = parseEuropeanInteger(sale.get("BONUS"));
} catch (NumberFormatException e) {
noSale = true;
saleBonus = 0;
}
if (saleBonus == 0) {
saleNoBonus = true;
}
}
//int saleQuantity = parseEuropeanInteger(sale.get("QUANTITY"));
System.out.println("the bonus is: " + sale.get("BONUS"));
//int saleBonus = parseEuropeanInteger(sale.get("BONUS"));
double salePrice = parsePrice(sale.get("PRICE"));
Date saleDate = parseDate(sale.get("DATE"), false);
Map<String, String> bestCampaign = null;
int bestCampaignBonus = 0;
boolean priceMismatch = false;
//System.out.println("Processing sale: " + sale); // Debug statement
for (Map<String, String> campaign : campaignData) {
boolean[] eligibilityAndMismatch = isProductEligibleForCampaign(campaign, productBarcode, saleQuantity, salePrice); // Pass product barcode
boolean isEligible = eligibilityAndMismatch[0];
boolean currentPriceMismatch = eligibilityAndMismatch[1];
if (isCampaignDateValid(campaign, saleDate) && isEligible) {
int campaignBonus = getCampaignBonus(campaign, saleQuantity);
//System.out.println("Campaign: " + campaign + " Bonus: " + campaignBonus); // Debug statement
if (campaignBonus > bestCampaignBonus && campaignBonus <= saleBonus) {
bestCampaignBonus = campaignBonus;
bestCampaign = campaign;
priceMismatch = currentPriceMismatch;
}
}
}
System.out.println("Best Campaign: " + bestCampaign + " With Bonus: " + bestCampaignBonus);
int autoApproved = bestCampaignBonus;
int autoCut = 0;
if (saleBonus > 0 || autoApproved > 0) {
autoCut = saleBonus - autoApproved;
}
//System.out.println("Sale: " + sale.get("PRODUCT NAME") + ", Auto Approved: " + autoApproved + ", Auto Cut: " + autoCut); // Debug statement
Map<String, String> discrepancyRecord = new HashMap<>(sale);
discrepancyRecord.put("AUTO APPROVED", String.valueOf(autoApproved));
discrepancyRecord.put("AUTO CUT", String.valueOf(autoCut));
if (noSale) {
discrepancyRecord.put("NOTE", "NO SALE");
}
else if (saleNoBonus) {
discrepancyRecord.put("NOTE", "NO BONUS");
}
else if (priceMismatch) {
discrepancyRecord.put("PRICE HIGHLIGHT", "!!");
//discrepancyRecord.put("NOTE", "Campaign found but price mismatch");
discrepancyRecord.put("NOTE", ("Price mismatch, but: " + calculateNote(bestCampaign, saleQuantity, autoApproved)));
} else {
discrepancyRecord.put("HIGHLIGHT", autoCut > 0 ? "!" : "");
discrepancyRecord.put("NOTE", calculateNote(bestCampaign, saleQuantity, autoApproved));
}
discrepancies.add(discrepancyRecord);
}
return discrepancies;
}
private static String calculateNote(Map<String, String> bestCampaign, int saleQuantity, int autoApprovedBonus) throws ParseException {
if (bestCampaign == null) {
// Handle the case where no eligible campaign is found
return "Rejected: No matching campaign found";
}
// Get the date of the sale from the bestCampaign
String saleDateString = bestCampaign.get("ValidFrom");
String saleDateStrings = bestCampaign.get("ValidUntil");
if (saleDateString == null || saleDateString.isEmpty()) {
// Handle the case where the sale date is missing
// System.out.println("DEBUG: Sale date is missing for the sale. Best Campaign: " + bestCampaign);
return "Note: Sale date is missing.";
}
// System.out.println("DEBUG: Sale date string: " + saleDateString);
// Parse the sale date
Date saleDate;
try {
saleDate = dateFormat.parse(saleDateString);
saleDate = dateFormat.parse(saleDateStrings);
// System.out.println("DEBUG: Parsed sale date: " + saleDate);
} catch (ParseException e) {
// Handle parsing exception
e.printStackTrace();
return "oups; this is not parseable as a date..."; // Return empty note in case of error
}
// Calculate the maximum campaign bonus for the given date
int maxCampaignBonus = getMaxCampaignBonusForDate(saleDate);
// System.out.println("DEBUG: Maximum campaign bonus for sale date: " + maxCampaignBonus);
// Calculate auto cut and auto approve amounts
int autoCut = Math.max(0, autoApprovedBonus - maxCampaignBonus);
int autoApproved = autoApprovedBonus - autoCut;
/*
System.out.println("DEBUG: Auto Approved: " + autoApproved);
System.out.println("DEBUG: Auto Cut: " + autoCut);*/
// Construct the note with auto cut and auto approve amounts
StringBuilder note = new StringBuilder();
note.append("Auto Approved: ").append(autoApproved).append(". ");
if (autoCut > 0) {
note.append("Auto Cut: ").append(autoCut).append(". ");
}
return note.toString();
}
private static int getMaxCampaignBonusForDate(Date saleDate) throws ParseException {
int maxCampaignBonus = 0;
// Iterate through all campaigns and calculate the maximum campaign bonus for the given date
for (Map<String, String> campaign : campaignData) {
if (isCampaignDateValid(campaign, saleDate)) {
int campaignBonus = getCampaignBonusForDate(campaign);
maxCampaignBonus = Math.max(maxCampaignBonus, campaignBonus);
}
}
return maxCampaignBonus;
}
private static boolean[] isProductEligibleForCampaign(Map<String, String> campaign, String productBarcode, int saleQuantity, double salePrice) {
boolean[] result = new boolean[2]; // [eligible, priceMismatch]
for (int i = 1; i <= 3; i++) {
String campaignProductBarcode = campaign.get("PRODUCT_" + i + "_BARCODE");
if (!campaignProductBarcode.isEmpty() && campaignProductBarcode.equals(productBarcode)) {
int minOrderQuantity = Integer.parseInt(campaign.getOrDefault("PRODUCT_" + i + "_MINIMUM_QUANTITY", "0"));
double campaignPrice = Double.parseDouble(campaign.getOrDefault("PRODUCT_" + i + "_PRICE", "0"));
if (saleQuantity >= minOrderQuantity) {
result[0] = true; // Eligible based on quantity
result[1] = (salePrice != campaignPrice); // Price mismatch check
return result;
}
}
}
result[0] = false; // Not eligible
return result;
}
private static int getCampaignBonus(Map<String, String> campaign, int totalSaleQuantity) {
int maxCampaignBonus = 0;
// Check if the combined campaign for all products meets the minimum order quantity
String campaignQtyStr1 = campaign.getOrDefault("PRODUCT_1_MINIMUM_QUANTITY", "0");
int campaignQty1 = campaignQtyStr1.isEmpty() ? 0 : Integer.parseInt(campaignQtyStr1);
String campaignQtyStr2 = campaign.getOrDefault("PRODUCT_2_MINIMUM_QUANTITY", "0");
int campaignQty2 = campaignQtyStr2.isEmpty() ? 0 : Integer.parseInt(campaignQtyStr2);
String campaignQtyStr3 = campaign.getOrDefault("PRODUCT_3_MINIMUM_QUANTITY", "0");
int campaignQty3 = campaignQtyStr3.isEmpty() ? 0 : Integer.parseInt(campaignQtyStr3);
// Check if all products involved in the campaign meet their criteria
boolean isValidCampaign1 = totalSaleQuantity >= campaignQty1;
boolean isValidCampaign2 = totalSaleQuantity >= campaignQty2;
boolean isValidCampaign3 = totalSaleQuantity >= campaignQty3;
// Set the maximum campaign bonus based on valid combinations
if (isValidCampaign1 && isValidCampaign2 && isValidCampaign3) {
String campaignBonusStr1 = campaign.getOrDefault("PRODUCT_1_MF", "0");
int campaignBonus1 = campaignBonusStr1.isEmpty() ? 0 : Integer.parseInt(campaignBonusStr1);
String campaignBonusStr2 = campaign.getOrDefault("PRODUCT_2_MF", "0");
int campaignBonus2 = campaignBonusStr2.isEmpty() ? 0 : Integer.parseInt(campaignBonusStr2);
String campaignBonusStr3 = campaign.getOrDefault("PRODUCT_3_MF", "0");
int campaignBonus3 = campaignBonusStr3.isEmpty() ? 0 : Integer.parseInt(campaignBonusStr3);
maxCampaignBonus = Math.max(Math.max(campaignBonus1, campaignBonus2), campaignBonus3);
} else if (isValidCampaign1 && isValidCampaign2) {
maxCampaignBonus = Math.max(campaign.getOrDefault("PRODUCT_1_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_1_MF", "0")), campaign.getOrDefault("PRODUCT_2_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_2_MF", "0")));
} else if (isValidCampaign1 && isValidCampaign3) {
maxCampaignBonus = Math.max(campaign.getOrDefault("PRODUCT_1_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_1_MF", "0")), campaign.getOrDefault("PRODUCT_3_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_3_MF", "0")));
} else if (isValidCampaign2 && isValidCampaign3) {
maxCampaignBonus = Math.max(campaign.getOrDefault("PRODUCT_2_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_2_MF", "0")), campaign.getOrDefault("PRODUCT_3_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_3_MF", "0")));
} else if (isValidCampaign1) {
maxCampaignBonus = campaign.getOrDefault("PRODUCT_1_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_1_MF", "0"));
} else if (isValidCampaign2) {
maxCampaignBonus = campaign.getOrDefault("PRODUCT_2_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_2_MF", "0"));
} else if (isValidCampaign3) {
maxCampaignBonus = campaign.getOrDefault("PRODUCT_3_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_3_MF", "0"));
}
return maxCampaignBonus;
}
private static int getCampaignBonusForDate(Map<String, String> campaign) {
// Extract the campaign bonus for the given date from the campaign map
String campaignBonusStr = campaign.get("PRODUCT_1_MF"); // Assuming bonus for product 1 is used to represent campaign bonus
int campaignBonus = Integer.parseInt(campaignBonusStr.isEmpty() ? "0" : campaignBonusStr);
// You can add additional logic here if the campaign bonus depends on specific conditions or criteria
return campaignBonus;
}
private static boolean isCampaignDateValid(Map<String, String> campaign, Date saleDate) throws ParseException {
// If saleDate is null, set it to the default value of 01/01/2000
if (saleDate == null) {
saleDate = new SimpleDateFormat("dd/MM/yyyy").parse("01/01/2000");
}
String validFromStr = campaign.getOrDefault("ValidFrom", "");
String validUntilStr = campaign.getOrDefault("ValidUntil", "");
Date validFrom = validFromStr.isEmpty() ? new Date(Long.MIN_VALUE) : parseDate(validFromStr, true);
Date validUntil = validUntilStr.isEmpty() ? new Date(Long.MAX_VALUE) : parseDate(validUntilStr, true);
return !saleDate.before(validFrom) && !saleDate.after(validUntil);
}
// Refactor parseDate to minimize unnecessary parsing attempts
private static Date parseDate(String dateString, Boolean isQuiet) throws ParseException {
if (dateString == null || dateString.trim().isEmpty()) {
return null; // Or a more appropriate default date
}
ParseException lastException = null;
for (String format : dateFormats) {
try {
SimpleDateFormat dateFormat = new SimpleDateFormat(format);
dateFormat.setLenient(false); // Ensure strict parsing
return dateFormat.parse(dateString);
} catch (ParseException e) {
lastException = e;
}
}
if (lastException != null) {
throw lastException;
}
return null; // This should never happen if dateFormats is comprehensive
}
private static double parsePrice(String priceString) throws ParseException {
if (priceString == null || priceString.trim().isEmpty()) {
return 0.0; // Return zero or another appropriate default value
}
// Try parsing in US format
try {
Number number = US_FORMAT.parse(priceString);
return number.doubleValue();
} catch (ParseException e) {
// Try parsing in EU format
try {
Number number = EU_FORMAT.parse(priceString);
return number.doubleValue();
} catch (ParseException euException) {
throw euException; // Throw the exception from the EU format attempt
}
}
}
public static int parseEuropeanInteger(String value) {
try {
// Remove commas before parsing
return Integer.parseInt(value.replaceAll(",", ""));
} catch (NumberFormatException e) {
throw new NumberFormatException("Unable to parse the integer: " + value);
}
}
private static void generateReport(List<Map<String, String>> discrepancies, String filePath, List<String> additionalHeaders) throws IOException {
try (CSVWriter writer = new CSVWriter(new FileWriter(filePath))) {
// Predefined headers
List<String> predefinedHeaders = Arrays.asList(
"PRODUCT NAME", "BARCODE", "PHARMACY GLN NUMBER", "PHARMACY NAME", "DATE", "ORDER NO",
"TOWN", "PROVINCE", "QUANTITY", "BONUS", "PRICE", "WAREHOUSE",
"AUTO APPROVED", "AUTO CUT", "MANUAL APPROVED", "HIGHLIGHT", "PRICE HIGHLIGHT", "NOTE"
);
// Combine predefined headers with additional headers, avoiding duplicates
Set<String> combinedHeadersSet = new LinkedHashSet<>(predefinedHeaders);
combinedHeadersSet.addAll(additionalHeaders);
List<String> combinedHeaders = new ArrayList<>(combinedHeadersSet);
// Write headers to CSV
writer.writeNext(combinedHeaders.toArray(new String[0]));
// Write data records to CSV
for (Map<String, String> discrepancy : discrepancies) {
List<String> record = new ArrayList<>();
for (String header : combinedHeaders) {
record.add(discrepancy.getOrDefault(header, ""));
}
writer.writeNext(record.toArray(new String[0]));
}
}
}
}
</code>
<code>package mf_app.BonusCalculate_3; import com.opencsv.CSVReader; import com.opencsv.CSVWriter; import tocsv.UniversalConverter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; import java.text.DecimalFormat; import java.text.NumberFormat; import java.text.ParseException; public class BonusTracker { private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy"); private static final String[] dateFormats = { "dd/MM/yyyy", "d/M/yyyy", "dd/M/yyyy", "d/MM/yyyy", // Original formats with slash as separator "dd.MM.yyyy", "d.M.yyyy", "dd.M.yyyy", "d.MM.yyyy", "dd.MM.yy", // Formats with dot as separator "MM/dd/yyyy", "M/d/yyyy", "MM/d/yyyy", "M/dd/yyyy", // Additional US-style formats with slash as separator "MM/dd/yy" }; private static final NumberFormat US_FORMAT = NumberFormat.getInstance(Locale.US); private static final NumberFormat EU_FORMAT = NumberFormat.getInstance(Locale.GERMANY); private static List<Map<String, String>> campaignData; // Added campaignData as class-level variable public static void main(String[] args) { String bonusesDirectoryPath = "./data/0_UTILITIES/BONUSES"; String salesFilePath = "./data/5_1_CALCULATIONS_1/CALCULATED_1.csv"; String reportFilePath = "./data/5_2_MF_CHECKED/MF_CHECKED.csv"; try { String campaignsFilePath = findAndConvertBonusesFile(bonusesDirectoryPath); if (campaignsFilePath == null) { System.err.println("No bonuses CSV or Excel file found in the directory: " + bonusesDirectoryPath); System.exit(1); } ParsedCSV salesParsedCSV = parseCSV(salesFilePath); ParsedCSV campaignsParsedCSV = parseCSV(campaignsFilePath); campaignData = campaignsParsedCSV.data; List<Map<String, String>> discrepancies = compareSalesWithCampaigns(salesParsedCSV.data, campaignData); System.out.println("Total Discrepancies Found: " + discrepancies.size()); generateReport(discrepancies, reportFilePath, salesParsedCSV.headers); System.out.println("Discrepancies reported: " + discrepancies.size()); System.out.println("Report generated successfully: " + reportFilePath); } catch (IOException | ParseException e) { e.printStackTrace(); System.exit(1); // Exit with error status } catch (Error err) { // This block catches OutOfMemoryError and other serious errors System.err.println("A severe error occurred: " + err.getMessage()); if (err instanceof OutOfMemoryError) { System.err.println("The application ran out of memory. Consider increasing the heap size or optimizing memory usage."); } else { System.err.println("Please check the application's environment and configuration."); } System.exit(2); // Exit with a specific error status for this type of failure } } private static String findAndConvertBonusesFile(String directoryPath) throws IOException { File directory = new File(directoryPath); if (directory.isDirectory()) { // Check for Excel file first File[] excelFiles = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".xlsx")); if (excelFiles != null && excelFiles.length > 0) { System.out.println("excel not found!"); // Convert the first Excel file found to CSV String excelFilePath = excelFiles[0].getPath(); String csvFilePath = directoryPath + "/" + excelFiles[0].getName().replace(".xlsx", ".csv"); UniversalConverter.convertFiles(directoryPath, directoryPath); return csvFilePath; } // If no Excel file, check for CSV file File[] csvFiles = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".csv")); if (csvFiles != null && csvFiles.length > 0) { System.out.println("csv not found!"); return csvFiles[0].getPath(); } } return null; } // static ParsedCSV class: static class ParsedCSV { List<String> headers; List<Map<String, String>> data; public ParsedCSV(List<String> headers, List<Map<String, String>> data) { this.headers = headers; this.data = data; } } private static ParsedCSV parseCSV(String filePath) throws IOException { List<Map<String, String>> data = new ArrayList<>(); List<String> headersList = new ArrayList<>(); try (CSVReader reader = new CSVReader(new FileReader(filePath))) { String[] headers = reader.readNext(); if (headers != null) { for (int i = 0; i < headers.length; i++) { headers[i] = headers[i].replaceAll("[^\x20-\x7E]", "").trim(); headersList.add(headers[i]); } } String[] line; while ((line = reader.readNext()) != null) { Map<String, String> row = new HashMap<>(); for (int i = 0; i < headers.length; i++) { row.put(headers[i], line[i]); } data.add(row); } } return new ParsedCSV(headersList, data); } private static List<Map<String, String>> compareSalesWithCampaigns(List<Map<String, String>> salesData, List<Map<String, String>> campaignData) throws ParseException { List<Map<String, String>> discrepancies = new ArrayList<>(); for (Map<String, String> sale : salesData) { boolean noSale = false; boolean saleNoBonus = false; String productBarcode = sale.get("BARCODE"); // Use the barcode instead of the product name int saleQuantity = 0; int saleBonus = 0; try { saleQuantity = parseEuropeanInteger(sale.get("QUANTITY")); } catch (NumberFormatException e) { noSale = true; saleQuantity = 0; } if(!noSale) { try { saleBonus = parseEuropeanInteger(sale.get("BONUS")); } catch (NumberFormatException e) { noSale = true; saleBonus = 0; } if (saleBonus == 0) { saleNoBonus = true; } } //int saleQuantity = parseEuropeanInteger(sale.get("QUANTITY")); System.out.println("the bonus is: " + sale.get("BONUS")); //int saleBonus = parseEuropeanInteger(sale.get("BONUS")); double salePrice = parsePrice(sale.get("PRICE")); Date saleDate = parseDate(sale.get("DATE"), false); Map<String, String> bestCampaign = null; int bestCampaignBonus = 0; boolean priceMismatch = false; //System.out.println("Processing sale: " + sale); // Debug statement for (Map<String, String> campaign : campaignData) { boolean[] eligibilityAndMismatch = isProductEligibleForCampaign(campaign, productBarcode, saleQuantity, salePrice); // Pass product barcode boolean isEligible = eligibilityAndMismatch[0]; boolean currentPriceMismatch = eligibilityAndMismatch[1]; if (isCampaignDateValid(campaign, saleDate) && isEligible) { int campaignBonus = getCampaignBonus(campaign, saleQuantity); //System.out.println("Campaign: " + campaign + " Bonus: " + campaignBonus); // Debug statement if (campaignBonus > bestCampaignBonus && campaignBonus <= saleBonus) { bestCampaignBonus = campaignBonus; bestCampaign = campaign; priceMismatch = currentPriceMismatch; } } } System.out.println("Best Campaign: " + bestCampaign + " With Bonus: " + bestCampaignBonus); int autoApproved = bestCampaignBonus; int autoCut = 0; if (saleBonus > 0 || autoApproved > 0) { autoCut = saleBonus - autoApproved; } //System.out.println("Sale: " + sale.get("PRODUCT NAME") + ", Auto Approved: " + autoApproved + ", Auto Cut: " + autoCut); // Debug statement Map<String, String> discrepancyRecord = new HashMap<>(sale); discrepancyRecord.put("AUTO APPROVED", String.valueOf(autoApproved)); discrepancyRecord.put("AUTO CUT", String.valueOf(autoCut)); if (noSale) { discrepancyRecord.put("NOTE", "NO SALE"); } else if (saleNoBonus) { discrepancyRecord.put("NOTE", "NO BONUS"); } else if (priceMismatch) { discrepancyRecord.put("PRICE HIGHLIGHT", "!!"); //discrepancyRecord.put("NOTE", "Campaign found but price mismatch"); discrepancyRecord.put("NOTE", ("Price mismatch, but: " + calculateNote(bestCampaign, saleQuantity, autoApproved))); } else { discrepancyRecord.put("HIGHLIGHT", autoCut > 0 ? "!" : ""); discrepancyRecord.put("NOTE", calculateNote(bestCampaign, saleQuantity, autoApproved)); } discrepancies.add(discrepancyRecord); } return discrepancies; } private static String calculateNote(Map<String, String> bestCampaign, int saleQuantity, int autoApprovedBonus) throws ParseException { if (bestCampaign == null) { // Handle the case where no eligible campaign is found return "Rejected: No matching campaign found"; } // Get the date of the sale from the bestCampaign String saleDateString = bestCampaign.get("ValidFrom"); String saleDateStrings = bestCampaign.get("ValidUntil"); if (saleDateString == null || saleDateString.isEmpty()) { // Handle the case where the sale date is missing // System.out.println("DEBUG: Sale date is missing for the sale. Best Campaign: " + bestCampaign); return "Note: Sale date is missing."; } // System.out.println("DEBUG: Sale date string: " + saleDateString); // Parse the sale date Date saleDate; try { saleDate = dateFormat.parse(saleDateString); saleDate = dateFormat.parse(saleDateStrings); // System.out.println("DEBUG: Parsed sale date: " + saleDate); } catch (ParseException e) { // Handle parsing exception e.printStackTrace(); return "oups; this is not parseable as a date..."; // Return empty note in case of error } // Calculate the maximum campaign bonus for the given date int maxCampaignBonus = getMaxCampaignBonusForDate(saleDate); // System.out.println("DEBUG: Maximum campaign bonus for sale date: " + maxCampaignBonus); // Calculate auto cut and auto approve amounts int autoCut = Math.max(0, autoApprovedBonus - maxCampaignBonus); int autoApproved = autoApprovedBonus - autoCut; /* System.out.println("DEBUG: Auto Approved: " + autoApproved); System.out.println("DEBUG: Auto Cut: " + autoCut);*/ // Construct the note with auto cut and auto approve amounts StringBuilder note = new StringBuilder(); note.append("Auto Approved: ").append(autoApproved).append(". "); if (autoCut > 0) { note.append("Auto Cut: ").append(autoCut).append(". "); } return note.toString(); } private static int getMaxCampaignBonusForDate(Date saleDate) throws ParseException { int maxCampaignBonus = 0; // Iterate through all campaigns and calculate the maximum campaign bonus for the given date for (Map<String, String> campaign : campaignData) { if (isCampaignDateValid(campaign, saleDate)) { int campaignBonus = getCampaignBonusForDate(campaign); maxCampaignBonus = Math.max(maxCampaignBonus, campaignBonus); } } return maxCampaignBonus; } private static boolean[] isProductEligibleForCampaign(Map<String, String> campaign, String productBarcode, int saleQuantity, double salePrice) { boolean[] result = new boolean[2]; // [eligible, priceMismatch] for (int i = 1; i <= 3; i++) { String campaignProductBarcode = campaign.get("PRODUCT_" + i + "_BARCODE"); if (!campaignProductBarcode.isEmpty() && campaignProductBarcode.equals(productBarcode)) { int minOrderQuantity = Integer.parseInt(campaign.getOrDefault("PRODUCT_" + i + "_MINIMUM_QUANTITY", "0")); double campaignPrice = Double.parseDouble(campaign.getOrDefault("PRODUCT_" + i + "_PRICE", "0")); if (saleQuantity >= minOrderQuantity) { result[0] = true; // Eligible based on quantity result[1] = (salePrice != campaignPrice); // Price mismatch check return result; } } } result[0] = false; // Not eligible return result; } private static int getCampaignBonus(Map<String, String> campaign, int totalSaleQuantity) { int maxCampaignBonus = 0; // Check if the combined campaign for all products meets the minimum order quantity String campaignQtyStr1 = campaign.getOrDefault("PRODUCT_1_MINIMUM_QUANTITY", "0"); int campaignQty1 = campaignQtyStr1.isEmpty() ? 0 : Integer.parseInt(campaignQtyStr1); String campaignQtyStr2 = campaign.getOrDefault("PRODUCT_2_MINIMUM_QUANTITY", "0"); int campaignQty2 = campaignQtyStr2.isEmpty() ? 0 : Integer.parseInt(campaignQtyStr2); String campaignQtyStr3 = campaign.getOrDefault("PRODUCT_3_MINIMUM_QUANTITY", "0"); int campaignQty3 = campaignQtyStr3.isEmpty() ? 0 : Integer.parseInt(campaignQtyStr3); // Check if all products involved in the campaign meet their criteria boolean isValidCampaign1 = totalSaleQuantity >= campaignQty1; boolean isValidCampaign2 = totalSaleQuantity >= campaignQty2; boolean isValidCampaign3 = totalSaleQuantity >= campaignQty3; // Set the maximum campaign bonus based on valid combinations if (isValidCampaign1 && isValidCampaign2 && isValidCampaign3) { String campaignBonusStr1 = campaign.getOrDefault("PRODUCT_1_MF", "0"); int campaignBonus1 = campaignBonusStr1.isEmpty() ? 0 : Integer.parseInt(campaignBonusStr1); String campaignBonusStr2 = campaign.getOrDefault("PRODUCT_2_MF", "0"); int campaignBonus2 = campaignBonusStr2.isEmpty() ? 0 : Integer.parseInt(campaignBonusStr2); String campaignBonusStr3 = campaign.getOrDefault("PRODUCT_3_MF", "0"); int campaignBonus3 = campaignBonusStr3.isEmpty() ? 0 : Integer.parseInt(campaignBonusStr3); maxCampaignBonus = Math.max(Math.max(campaignBonus1, campaignBonus2), campaignBonus3); } else if (isValidCampaign1 && isValidCampaign2) { maxCampaignBonus = Math.max(campaign.getOrDefault("PRODUCT_1_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_1_MF", "0")), campaign.getOrDefault("PRODUCT_2_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_2_MF", "0"))); } else if (isValidCampaign1 && isValidCampaign3) { maxCampaignBonus = Math.max(campaign.getOrDefault("PRODUCT_1_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_1_MF", "0")), campaign.getOrDefault("PRODUCT_3_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_3_MF", "0"))); } else if (isValidCampaign2 && isValidCampaign3) { maxCampaignBonus = Math.max(campaign.getOrDefault("PRODUCT_2_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_2_MF", "0")), campaign.getOrDefault("PRODUCT_3_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_3_MF", "0"))); } else if (isValidCampaign1) { maxCampaignBonus = campaign.getOrDefault("PRODUCT_1_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_1_MF", "0")); } else if (isValidCampaign2) { maxCampaignBonus = campaign.getOrDefault("PRODUCT_2_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_2_MF", "0")); } else if (isValidCampaign3) { maxCampaignBonus = campaign.getOrDefault("PRODUCT_3_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_3_MF", "0")); } return maxCampaignBonus; } private static int getCampaignBonusForDate(Map<String, String> campaign) { // Extract the campaign bonus for the given date from the campaign map String campaignBonusStr = campaign.get("PRODUCT_1_MF"); // Assuming bonus for product 1 is used to represent campaign bonus int campaignBonus = Integer.parseInt(campaignBonusStr.isEmpty() ? "0" : campaignBonusStr); // You can add additional logic here if the campaign bonus depends on specific conditions or criteria return campaignBonus; } private static boolean isCampaignDateValid(Map<String, String> campaign, Date saleDate) throws ParseException { // If saleDate is null, set it to the default value of 01/01/2000 if (saleDate == null) { saleDate = new SimpleDateFormat("dd/MM/yyyy").parse("01/01/2000"); } String validFromStr = campaign.getOrDefault("ValidFrom", ""); String validUntilStr = campaign.getOrDefault("ValidUntil", ""); Date validFrom = validFromStr.isEmpty() ? new Date(Long.MIN_VALUE) : parseDate(validFromStr, true); Date validUntil = validUntilStr.isEmpty() ? new Date(Long.MAX_VALUE) : parseDate(validUntilStr, true); return !saleDate.before(validFrom) && !saleDate.after(validUntil); } // Refactor parseDate to minimize unnecessary parsing attempts private static Date parseDate(String dateString, Boolean isQuiet) throws ParseException { if (dateString == null || dateString.trim().isEmpty()) { return null; // Or a more appropriate default date } ParseException lastException = null; for (String format : dateFormats) { try { SimpleDateFormat dateFormat = new SimpleDateFormat(format); dateFormat.setLenient(false); // Ensure strict parsing return dateFormat.parse(dateString); } catch (ParseException e) { lastException = e; } } if (lastException != null) { throw lastException; } return null; // This should never happen if dateFormats is comprehensive } private static double parsePrice(String priceString) throws ParseException { if (priceString == null || priceString.trim().isEmpty()) { return 0.0; // Return zero or another appropriate default value } // Try parsing in US format try { Number number = US_FORMAT.parse(priceString); return number.doubleValue(); } catch (ParseException e) { // Try parsing in EU format try { Number number = EU_FORMAT.parse(priceString); return number.doubleValue(); } catch (ParseException euException) { throw euException; // Throw the exception from the EU format attempt } } } public static int parseEuropeanInteger(String value) { try { // Remove commas before parsing return Integer.parseInt(value.replaceAll(",", "")); } catch (NumberFormatException e) { throw new NumberFormatException("Unable to parse the integer: " + value); } } private static void generateReport(List<Map<String, String>> discrepancies, String filePath, List<String> additionalHeaders) throws IOException { try (CSVWriter writer = new CSVWriter(new FileWriter(filePath))) { // Predefined headers List<String> predefinedHeaders = Arrays.asList( "PRODUCT NAME", "BARCODE", "PHARMACY GLN NUMBER", "PHARMACY NAME", "DATE", "ORDER NO", "TOWN", "PROVINCE", "QUANTITY", "BONUS", "PRICE", "WAREHOUSE", "AUTO APPROVED", "AUTO CUT", "MANUAL APPROVED", "HIGHLIGHT", "PRICE HIGHLIGHT", "NOTE" ); // Combine predefined headers with additional headers, avoiding duplicates Set<String> combinedHeadersSet = new LinkedHashSet<>(predefinedHeaders); combinedHeadersSet.addAll(additionalHeaders); List<String> combinedHeaders = new ArrayList<>(combinedHeadersSet); // Write headers to CSV writer.writeNext(combinedHeaders.toArray(new String[0])); // Write data records to CSV for (Map<String, String> discrepancy : discrepancies) { List<String> record = new ArrayList<>(); for (String header : combinedHeaders) { record.add(discrepancy.getOrDefault(header, "")); } writer.writeNext(record.toArray(new String[0])); } } } } </code>
package mf_app.BonusCalculate_3;

import com.opencsv.CSVReader;
import com.opencsv.CSVWriter;

import tocsv.UniversalConverter;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.ParseException;

public class BonusTracker {
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
    private static final String[] dateFormats = {
        "dd/MM/yyyy", "d/M/yyyy", "dd/M/yyyy", "d/MM/yyyy", // Original formats with slash as separator
        "dd.MM.yyyy", "d.M.yyyy", "dd.M.yyyy", "d.MM.yyyy", "dd.MM.yy", // Formats with dot as separator
        "MM/dd/yyyy", "M/d/yyyy", "MM/d/yyyy", "M/dd/yyyy", // Additional US-style formats with slash as separator
        "MM/dd/yy"
    };
    private static final NumberFormat US_FORMAT = NumberFormat.getInstance(Locale.US);
    private static final NumberFormat EU_FORMAT = NumberFormat.getInstance(Locale.GERMANY);
    private static List<Map<String, String>> campaignData; // Added campaignData as class-level variable

    public static void main(String[] args) {
        String bonusesDirectoryPath = "./data/0_UTILITIES/BONUSES";
        String salesFilePath = "./data/5_1_CALCULATIONS_1/CALCULATED_1.csv";
        String reportFilePath = "./data/5_2_MF_CHECKED/MF_CHECKED.csv";
    
        try {
            String campaignsFilePath = findAndConvertBonusesFile(bonusesDirectoryPath);
            if (campaignsFilePath == null) {
                System.err.println("No bonuses CSV or Excel file found in the directory: " + bonusesDirectoryPath);
                System.exit(1);
            }
    
            ParsedCSV salesParsedCSV = parseCSV(salesFilePath);
            ParsedCSV campaignsParsedCSV = parseCSV(campaignsFilePath);
            campaignData = campaignsParsedCSV.data;
    
            List<Map<String, String>> discrepancies = compareSalesWithCampaigns(salesParsedCSV.data, campaignData);
            System.out.println("Total Discrepancies Found: " + discrepancies.size());
            generateReport(discrepancies, reportFilePath, salesParsedCSV.headers);
            System.out.println("Discrepancies reported: " + discrepancies.size());
            System.out.println("Report generated successfully: " + reportFilePath);
        } catch (IOException | ParseException e) {
            e.printStackTrace();
            System.exit(1); // Exit with error status
        } catch (Error err) {
            // This block catches OutOfMemoryError and other serious errors
            System.err.println("A severe error occurred: " + err.getMessage());
            if (err instanceof OutOfMemoryError) {
                System.err.println("The application ran out of memory. Consider increasing the heap size or optimizing memory usage.");
            } else {
                System.err.println("Please check the application's environment and configuration.");
            }
            System.exit(2); // Exit with a specific error status for this type of failure
        }
    }    

    private static String findAndConvertBonusesFile(String directoryPath) throws IOException {
        File directory = new File(directoryPath);
        if (directory.isDirectory()) {
            // Check for Excel file first
            File[] excelFiles = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".xlsx"));
            if (excelFiles != null && excelFiles.length > 0) {
                System.out.println("excel not found!");
                // Convert the first Excel file found to CSV
                String excelFilePath = excelFiles[0].getPath();
                String csvFilePath = directoryPath + "/" + excelFiles[0].getName().replace(".xlsx", ".csv");
                UniversalConverter.convertFiles(directoryPath, directoryPath);
                return csvFilePath;
            }
            // If no Excel file, check for CSV file
            File[] csvFiles = directory.listFiles((dir, name) -> name.toLowerCase().endsWith(".csv"));
            if (csvFiles != null && csvFiles.length > 0) {
                System.out.println("csv not found!");
                return csvFiles[0].getPath();
            }
        }
        return null;
    }

    // static ParsedCSV class:
    static class ParsedCSV {
        List<String> headers;
        List<Map<String, String>> data;

        public ParsedCSV(List<String> headers, List<Map<String, String>> data) {
            this.headers = headers;
            this.data = data;
        }
    }

    private static ParsedCSV parseCSV(String filePath) throws IOException {
        List<Map<String, String>> data = new ArrayList<>();
        List<String> headersList = new ArrayList<>();
        try (CSVReader reader = new CSVReader(new FileReader(filePath))) {
            String[] headers = reader.readNext();
            if (headers != null) {
                for (int i = 0; i < headers.length; i++) {
                    headers[i] = headers[i].replaceAll("[^\x20-\x7E]", "").trim();
                    headersList.add(headers[i]);
                }
            }
            String[] line;
            while ((line = reader.readNext()) != null) {
                Map<String, String> row = new HashMap<>();
                for (int i = 0; i < headers.length; i++) {
                    row.put(headers[i], line[i]);
                }
                data.add(row);
            }
        }
        return new ParsedCSV(headersList, data);
    }

    private static List<Map<String, String>> compareSalesWithCampaigns(List<Map<String, String>> salesData, List<Map<String, String>> campaignData) throws ParseException {
        List<Map<String, String>> discrepancies = new ArrayList<>();

        for (Map<String, String> sale : salesData) {
            boolean noSale = false;
            boolean saleNoBonus = false;

            String productBarcode = sale.get("BARCODE"); // Use the barcode instead of the product name

            int saleQuantity = 0;
            int saleBonus = 0;
            try {
                saleQuantity = parseEuropeanInteger(sale.get("QUANTITY"));
            } catch (NumberFormatException e) {
                noSale = true;
                saleQuantity = 0;
            }
            if(!noSale) {
                try {
                    saleBonus = parseEuropeanInteger(sale.get("BONUS"));
                } catch (NumberFormatException e) {
                    noSale = true;
                    saleBonus = 0;
                    
                }
                if (saleBonus == 0) {
                    saleNoBonus = true;
                }
            }
            //int saleQuantity = parseEuropeanInteger(sale.get("QUANTITY"));
            System.out.println("the bonus is: " + sale.get("BONUS"));
            //int saleBonus = parseEuropeanInteger(sale.get("BONUS"));
            double salePrice = parsePrice(sale.get("PRICE"));
            Date saleDate = parseDate(sale.get("DATE"), false);

            Map<String, String> bestCampaign = null;
            int bestCampaignBonus = 0;
            boolean priceMismatch = false;
            
            //System.out.println("Processing sale: " + sale); // Debug statement

            for (Map<String, String> campaign : campaignData) {
                boolean[] eligibilityAndMismatch = isProductEligibleForCampaign(campaign, productBarcode, saleQuantity, salePrice); // Pass product barcode
                boolean isEligible = eligibilityAndMismatch[0];
                boolean currentPriceMismatch = eligibilityAndMismatch[1];

                if (isCampaignDateValid(campaign, saleDate) && isEligible) {
                    int campaignBonus = getCampaignBonus(campaign, saleQuantity);
                    //System.out.println("Campaign: " + campaign + " Bonus: " + campaignBonus); // Debug statement

                    if (campaignBonus > bestCampaignBonus && campaignBonus <= saleBonus) {
                        bestCampaignBonus = campaignBonus;
                        bestCampaign = campaign;
                        priceMismatch = currentPriceMismatch;
                    }
                }
            }
            System.out.println("Best Campaign: " + bestCampaign + " With Bonus: " + bestCampaignBonus);

            int autoApproved = bestCampaignBonus;
            int autoCut = 0;
            if (saleBonus > 0 || autoApproved > 0) {
                autoCut = saleBonus - autoApproved;
            }
            //System.out.println("Sale: " + sale.get("PRODUCT NAME") + ", Auto Approved: " + autoApproved + ", Auto Cut: " + autoCut); // Debug statement

            Map<String, String> discrepancyRecord = new HashMap<>(sale);
            discrepancyRecord.put("AUTO APPROVED", String.valueOf(autoApproved));
            discrepancyRecord.put("AUTO CUT", String.valueOf(autoCut));
            if (noSale) {
                discrepancyRecord.put("NOTE", "NO SALE");
            }
            else if (saleNoBonus) {
                discrepancyRecord.put("NOTE", "NO BONUS");
            }
            else if (priceMismatch) {
                discrepancyRecord.put("PRICE HIGHLIGHT", "!!");
                //discrepancyRecord.put("NOTE", "Campaign found but price mismatch");
                discrepancyRecord.put("NOTE", ("Price mismatch, but: " + calculateNote(bestCampaign, saleQuantity, autoApproved)));
            } else {
                discrepancyRecord.put("HIGHLIGHT", autoCut > 0 ? "!" : "");
                discrepancyRecord.put("NOTE", calculateNote(bestCampaign, saleQuantity, autoApproved));
            }

            discrepancies.add(discrepancyRecord);
        }

        return discrepancies;
    }


    private static String calculateNote(Map<String, String> bestCampaign, int saleQuantity, int autoApprovedBonus) throws ParseException {
        if (bestCampaign == null) {
            // Handle the case where no eligible campaign is found
            return "Rejected: No matching campaign found";
        }

        // Get the date of the sale from the bestCampaign
        String saleDateString = bestCampaign.get("ValidFrom");
        String saleDateStrings = bestCampaign.get("ValidUntil");
        if (saleDateString == null || saleDateString.isEmpty()) {
            // Handle the case where the sale date is missing
          //  System.out.println("DEBUG: Sale date is missing for the sale. Best Campaign: " + bestCampaign);
            return "Note: Sale date is missing.";
        }

    //    System.out.println("DEBUG: Sale date string: " + saleDateString);

        // Parse the sale date
        Date saleDate;
        try {
            saleDate = dateFormat.parse(saleDateString);
            saleDate = dateFormat.parse(saleDateStrings);

        //    System.out.println("DEBUG: Parsed sale date: " + saleDate);
        } catch (ParseException e) {
            // Handle parsing exception
            e.printStackTrace();
            return "oups; this is not parseable as a date..."; // Return empty note in case of error
        }

        // Calculate the maximum campaign bonus for the given date
        int maxCampaignBonus = getMaxCampaignBonusForDate(saleDate);

       // System.out.println("DEBUG: Maximum campaign bonus for sale date: " + maxCampaignBonus);

        // Calculate auto cut and auto approve amounts
        int autoCut = Math.max(0, autoApprovedBonus - maxCampaignBonus);
        int autoApproved = autoApprovedBonus - autoCut;
/*
        System.out.println("DEBUG: Auto Approved: " + autoApproved);
        System.out.println("DEBUG: Auto Cut: " + autoCut);*/

        // Construct the note with auto cut and auto approve amounts
        StringBuilder note = new StringBuilder();
        note.append("Auto Approved: ").append(autoApproved).append(". ");
        if (autoCut > 0) {
            note.append("Auto Cut: ").append(autoCut).append(". ");
        }

        return note.toString();
    }


    private static int getMaxCampaignBonusForDate(Date saleDate) throws ParseException {
        int maxCampaignBonus = 0;
        // Iterate through all campaigns and calculate the maximum campaign bonus for the given date
        for (Map<String, String> campaign : campaignData) {
            if (isCampaignDateValid(campaign, saleDate)) {
                int campaignBonus = getCampaignBonusForDate(campaign);
                maxCampaignBonus = Math.max(maxCampaignBonus, campaignBonus);
            }
        }
        return maxCampaignBonus;
    }

    

    private static boolean[] isProductEligibleForCampaign(Map<String, String> campaign, String productBarcode, int saleQuantity, double salePrice) {
        boolean[] result = new boolean[2]; // [eligible, priceMismatch]
        for (int i = 1; i <= 3; i++) {
            String campaignProductBarcode = campaign.get("PRODUCT_" + i + "_BARCODE");
            if (!campaignProductBarcode.isEmpty() && campaignProductBarcode.equals(productBarcode)) {
                int minOrderQuantity = Integer.parseInt(campaign.getOrDefault("PRODUCT_" + i + "_MINIMUM_QUANTITY", "0"));
                double campaignPrice = Double.parseDouble(campaign.getOrDefault("PRODUCT_" + i + "_PRICE", "0"));

                if (saleQuantity >= minOrderQuantity) {
                    result[0] = true; // Eligible based on quantity
                    result[1] = (salePrice != campaignPrice); // Price mismatch check
                    return result;
                }
            }
        }
        result[0] = false; // Not eligible
        return result;
    }

    private static int getCampaignBonus(Map<String, String> campaign, int totalSaleQuantity) {
        int maxCampaignBonus = 0;

        // Check if the combined campaign for all products meets the minimum order quantity
        String campaignQtyStr1 = campaign.getOrDefault("PRODUCT_1_MINIMUM_QUANTITY", "0");
        int campaignQty1 = campaignQtyStr1.isEmpty() ? 0 : Integer.parseInt(campaignQtyStr1);
        String campaignQtyStr2 = campaign.getOrDefault("PRODUCT_2_MINIMUM_QUANTITY", "0");
        int campaignQty2 = campaignQtyStr2.isEmpty() ? 0 : Integer.parseInt(campaignQtyStr2);
        String campaignQtyStr3 = campaign.getOrDefault("PRODUCT_3_MINIMUM_QUANTITY", "0");
        int campaignQty3 = campaignQtyStr3.isEmpty() ? 0 : Integer.parseInt(campaignQtyStr3);

        // Check if all products involved in the campaign meet their criteria
        boolean isValidCampaign1 = totalSaleQuantity >= campaignQty1;
        boolean isValidCampaign2 = totalSaleQuantity >= campaignQty2;
        boolean isValidCampaign3 = totalSaleQuantity >= campaignQty3;

        // Set the maximum campaign bonus based on valid combinations
        if (isValidCampaign1 && isValidCampaign2 && isValidCampaign3) {
            String campaignBonusStr1 = campaign.getOrDefault("PRODUCT_1_MF", "0");
            int campaignBonus1 = campaignBonusStr1.isEmpty() ? 0 : Integer.parseInt(campaignBonusStr1);
            String campaignBonusStr2 = campaign.getOrDefault("PRODUCT_2_MF", "0");
            int campaignBonus2 = campaignBonusStr2.isEmpty() ? 0 : Integer.parseInt(campaignBonusStr2);
            String campaignBonusStr3 = campaign.getOrDefault("PRODUCT_3_MF", "0");
            int campaignBonus3 = campaignBonusStr3.isEmpty() ? 0 : Integer.parseInt(campaignBonusStr3);

            maxCampaignBonus = Math.max(Math.max(campaignBonus1, campaignBonus2), campaignBonus3);
        } else if (isValidCampaign1 && isValidCampaign2) {
            maxCampaignBonus = Math.max(campaign.getOrDefault("PRODUCT_1_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_1_MF", "0")), campaign.getOrDefault("PRODUCT_2_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_2_MF", "0")));
        } else if (isValidCampaign1 && isValidCampaign3) {
            maxCampaignBonus = Math.max(campaign.getOrDefault("PRODUCT_1_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_1_MF", "0")), campaign.getOrDefault("PRODUCT_3_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_3_MF", "0")));
        } else if (isValidCampaign2 && isValidCampaign3) {
            maxCampaignBonus = Math.max(campaign.getOrDefault("PRODUCT_2_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_2_MF", "0")), campaign.getOrDefault("PRODUCT_3_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_3_MF", "0")));
        } else if (isValidCampaign1) {
            maxCampaignBonus = campaign.getOrDefault("PRODUCT_1_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_1_MF", "0"));
        } else if (isValidCampaign2) {
            maxCampaignBonus = campaign.getOrDefault("PRODUCT_2_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_2_MF", "0"));
        } else if (isValidCampaign3) {
            maxCampaignBonus = campaign.getOrDefault("PRODUCT_3_MF", "0").isEmpty() ? 0 : Integer.parseInt(campaign.getOrDefault("PRODUCT_3_MF", "0"));
        }

        return maxCampaignBonus;
    }

    private static int getCampaignBonusForDate(Map<String, String> campaign) {
        // Extract the campaign bonus for the given date from the campaign map
        String campaignBonusStr = campaign.get("PRODUCT_1_MF"); // Assuming bonus for product 1 is used to represent campaign bonus
        int campaignBonus = Integer.parseInt(campaignBonusStr.isEmpty() ? "0" : campaignBonusStr);

        // You can add additional logic here if the campaign bonus depends on specific conditions or criteria

        return campaignBonus;
    }

    private static boolean isCampaignDateValid(Map<String, String> campaign, Date saleDate) throws ParseException {
        // If saleDate is null, set it to the default value of 01/01/2000
        if (saleDate == null) {
            saleDate = new SimpleDateFormat("dd/MM/yyyy").parse("01/01/2000");
        }
        String validFromStr = campaign.getOrDefault("ValidFrom", "");
        String validUntilStr = campaign.getOrDefault("ValidUntil", "");
        Date validFrom = validFromStr.isEmpty() ? new Date(Long.MIN_VALUE) : parseDate(validFromStr, true);
        Date validUntil = validUntilStr.isEmpty() ? new Date(Long.MAX_VALUE) : parseDate(validUntilStr, true);
        return !saleDate.before(validFrom) && !saleDate.after(validUntil);
    }

    // Refactor parseDate to minimize unnecessary parsing attempts
    private static Date parseDate(String dateString, Boolean isQuiet) throws ParseException {
        if (dateString == null || dateString.trim().isEmpty()) {
            return null; // Or a more appropriate default date
        }
        ParseException lastException = null;
        for (String format : dateFormats) {
            try {
                SimpleDateFormat dateFormat = new SimpleDateFormat(format);
                dateFormat.setLenient(false); // Ensure strict parsing
                return dateFormat.parse(dateString);
            } catch (ParseException e) {
                lastException = e;
            }
        }
        if (lastException != null) {
            throw lastException;
        }
        return null; // This should never happen if dateFormats is comprehensive
    }

    private static double parsePrice(String priceString) throws ParseException {
        if (priceString == null || priceString.trim().isEmpty()) {
            return 0.0; // Return zero or another appropriate default value
        }

        // Try parsing in US format
        try {
            Number number = US_FORMAT.parse(priceString);
            return number.doubleValue();
        } catch (ParseException e) {
            // Try parsing in EU format
            try {
                Number number = EU_FORMAT.parse(priceString);
                return number.doubleValue();
            } catch (ParseException euException) {
                throw euException; // Throw the exception from the EU format attempt
            }
        }
    }

    public static int parseEuropeanInteger(String value) {
        try {
            // Remove commas before parsing
            return Integer.parseInt(value.replaceAll(",", ""));
        } catch (NumberFormatException e) {
            throw new NumberFormatException("Unable to parse the integer: " + value);
        }
    }
    
    private static void generateReport(List<Map<String, String>> discrepancies, String filePath, List<String> additionalHeaders) throws IOException {
        try (CSVWriter writer = new CSVWriter(new FileWriter(filePath))) {
            // Predefined headers
            List<String> predefinedHeaders = Arrays.asList(
                "PRODUCT NAME", "BARCODE", "PHARMACY GLN NUMBER", "PHARMACY NAME", "DATE", "ORDER NO",
                "TOWN", "PROVINCE", "QUANTITY", "BONUS", "PRICE", "WAREHOUSE",
                "AUTO APPROVED", "AUTO CUT", "MANUAL APPROVED", "HIGHLIGHT", "PRICE HIGHLIGHT", "NOTE"
            );
    
            // Combine predefined headers with additional headers, avoiding duplicates
            Set<String> combinedHeadersSet = new LinkedHashSet<>(predefinedHeaders);
            combinedHeadersSet.addAll(additionalHeaders);
            List<String> combinedHeaders = new ArrayList<>(combinedHeadersSet);
    
            // Write headers to CSV
            writer.writeNext(combinedHeaders.toArray(new String[0]));
    
            // Write data records to CSV
            for (Map<String, String> discrepancy : discrepancies) {
                List<String> record = new ArrayList<>();
                for (String header : combinedHeaders) {
                    record.add(discrepancy.getOrDefault(header, ""));
                }
                writer.writeNext(record.toArray(new String[0]));
            }
        }
    }

}

this is a sample bonus campaigns CSV file (“MF” is company shorthand for a specific type of bonus):

sample bonus campaigns CSV file

this is a sample sales data CSV file to be checked:

sample sales data CSV file

this is the output the program is supposed to get:

enter image description here

this is the output it is currently generating (notice line 10!!!):

enter image description here

and these are explanations of the cuts and approvals:

enter image description here

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật