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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################

/.vs
/CashRegister/.vs/CashRegister
/CashRegister/bin/Debug/net9.0
/CashRegister/obj
22 changes: 22 additions & 0 deletions CashRegister/CashRegister.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<None Update="denomination.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="divisor.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="transactions.csv">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
25 changes: 25 additions & 0 deletions CashRegister/CashRegister.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36915.13 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CashRegister", "CashRegister.csproj", "{CE3E71EF-2232-442D-8ED6-D88B79016264}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CE3E71EF-2232-442D-8ED6-D88B79016264}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CE3E71EF-2232-442D-8ED6-D88B79016264}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CE3E71EF-2232-442D-8ED6-D88B79016264}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CE3E71EF-2232-442D-8ED6-D88B79016264}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07EBE533-1856-402A-9985-7D4BFE493F5A}
EndGlobalSection
EndGlobal
194 changes: 194 additions & 0 deletions CashRegister/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
class Program
{

static void Main()
{
// Inputs
string sourceFile = "transactions.csv";
string destinationFile = "output.txt";
string denominationsFile = "denomination.csv";
string divisorFile = "divisor.txt";

try
{

// Get Data
int divisor = int.Parse(File.ReadAllText(divisorFile));

var transactions = GetFileData(sourceFile, divisor);

var denominations = GetDenominations(denominationsFile);

// Parse Data
var results = ParseTransaction(denominations, transactions);

// Write results
WriteFileData(destinationFile, results);

}
catch (Exception ex)
{
System.Console.WriteLine("Error: ", ex);
}

}

private static List<string> ParseTransaction(List<Denomination> denominations, List<Transaction> transactions)
{
var parsedTransactions = new List<string>();

foreach (var transaction in transactions)
{
try
{
// Per Transaction Rules can be applied here

parsedTransactions.Add(
GetChange(denominations, transaction.amountOwed, transaction.amountPaid, transaction.divisibleByInput)
);

}
catch (Exception)
{
// skip invalid transactions; no requirements specified;
}
}

return parsedTransactions;
}


#region File Operations


private static List<Transaction> GetFileData(string sourceFile, int divisor)
{
if (File.Exists(sourceFile) == false)
{
throw new FileNotFoundException("Source file not found", sourceFile);
}

// File Format: Amount Owed, Amount Paid
return File.ReadLines(sourceFile)
.Select(line =>
{
var parts = line.Split(',');

return new Transaction
{
amountOwed = decimal.Parse(parts[0]),
amountPaid = decimal.Parse(parts[1]),
divisibleByInput = (int)(decimal.Parse(parts[0]) * 100) % divisor == 0
};
})
.ToList();
}

private static void WriteFileData(string destinationFile, List<string> results)
{
File.WriteAllLines(destinationFile, results);
}

/// <summary>
/// Gets the List of currencies used
/// </summary>
/// <param name="sourceFile"></param>
/// <returns></returns>
/// <exception cref="FileNotFoundException"></exception>
private static List<Denomination> GetDenominations(string sourceFile)
{
if (File.Exists(sourceFile) == false)
{
throw new FileNotFoundException("Source file not found", sourceFile);
}

// File Format: decimal value, name of denomination, plural name of denomination
return File.ReadLines(sourceFile)
.Select(line =>
{
var parts = line.Split(',');

return new Denomination
{
value = decimal.Parse(parts[0]),
name = parts[1],
namePlural = parts[2]
};
})
.ToList();
}

#endregion

#region Calculations

private static decimal CalculateChange(decimal amountOwed, decimal amountPaid)
{
return amountPaid - amountOwed;
}

private static string GetChange(List<Denomination> denominations, decimal amountOwed, decimal amountPaid, bool divisibleByThree)
{
var changeDue = CalculateChange(amountOwed, amountPaid);

var result = new List<string>();

var mostDenomination = denominations.OrderByDescending(d => d.value).FirstOrDefault();

foreach (var denomination in denominations)
{
var count = Math.Floor(changeDue / denomination.value);

// Special Rule per Demoniation here

if (divisibleByThree && denomination.value > 0.01M)
{
count = GetRandonIntPerDenomination(mostDenomination.value, count);
}

if (count > 0)
{
result.Add(@$"{count} {(count > 1 ? denomination.namePlural : denomination.name)}");
changeDue -= count * denomination.value;
}
}

return string.Join(",", result);
}

/// <summary>
/// Used the max count in the specific denomination to get a random integer between 0 and that count
/// </summary>
/// <param name="maxBound"></param>
/// <param name="count"></param>
/// <returns></returns>
private static int GetRandonIntPerDenomination(decimal maxBound, decimal count)
{
var random = new Random();
int upperBound = Math.Min((int)maxBound, (int)count);
int lowerBound = 0;
return random.Next(lowerBound, upperBound + 1);
}

#endregion

#region Classes

public class Transaction
{
public decimal amountOwed { get; set; } = 0.00M;
public decimal amountPaid { get; set; } = 0.00M;

public bool divisibleByInput { get; set; } = false;
}

public class Denomination
{
public decimal value { get; set; } = 0.00M;
public string name { get; set; } = default!;
public string namePlural { get; set; } = default!;
}

#endregion

}
10 changes: 10 additions & 0 deletions CashRegister/denomination.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
100.00,hundred,hundreds
50.00,fifty,fifties
20.00,twenty,tenties
10.00,ten,tens
5.00,five,fives
1.00,dollar,dollars
0.25,quarter,quarters
0.10,dime,dimes
0.05,nickel,nickels
0.01,penny,pennies
1 change: 1 addition & 0 deletions CashRegister/divisor.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3
3 changes: 3 additions & 0 deletions CashRegister/transactions.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
2.12,3.00
1.97,2.00
3.33,5.00