Review the code logic solution
Your challenge was to update the QuarterlySalesReport
method to achieve the following requirements:
Create the ConstructProductId method.
Create the DeconstructProductId method.
Update
QuarterlySalesReport
method to identify the most profitable product.
This review describes the Copilot Chat features and prompts used to complete the challenge.
Note
The responses generated by GitHub Copilot Chat are based on chat history and the context provided by the user. Other factors, such as AI model updates, may also influence responses. Your results should be similar to the results shown below with some variations expected.
The following sections show one possible solution for the challenge from the previous unit.
Review the "Create the ConstructProductId method" solution
The challenge instructions have you download the sample app for this challenge.
Open the Program.cs file, locate the
GenerateSalesData
method, and then select the following code lines:int indexOfDept = Array.IndexOf(ProdDepartments.departmentNames, salesData[i].departmentName); string deptAbb = ProdDepartments.departmentAbbreviations[indexOfDept]; string firstDigit = (indexOfDept + 1).ToString(); string nextTwoDigits = random.Next(1, 100).ToString("D2"); string sizeCode = new string[] { "XS", "S", "M", "L", "XL" }[random.Next(0, 5)]; string colorCode = new string[] { "BK", "BL", "GR", "RD", "YL", "OR", "WT", "GY" }[random.Next(0, 8)]; string manufacturingSite = ManufacturingSites.manufacturingSites[random.Next(0, ManufacturingSites.manufacturingSites.Length)]; salesData[i].productID = $"{deptAbb}-{firstDigit}{nextTwoDigits}-{sizeCode}-{colorCode}-{manufacturingSite}";
Open the Chat view and then enter the following prompt:
#selection Create a public static method named ConstructProductId that uses the current index element of the salesData array (salesData[i]) to construct and return a productID for that item.
In the code editor, create a blank code line below the GenerateSalesData method.
In the Chat view, hover the mouse pointer over the code block containing the new method, and then select Insert at Cursor.
If the method is enclosed in a class, strip away the class definition and any other unnecessary code.
Your code should be similar to the following snippet:
public static string ConstructProductId(SalesData salesDataItem) { Random random = new(); int indexOfDept = Array.IndexOf(ProdDepartments.departmentNames, salesDataItem.departmentName); string deptAbb = ProdDepartments.departmentAbbreviations[indexOfDept]; string firstDigit = (indexOfDept + 1).ToString(); string nextTwoDigits = random.Next(1, 100).ToString("D2"); string sizeCode = new string[] { "XS", "S", "M", "L", "XL" }[random.Next(0, 5)]; string colorCode = new string[] { "BK", "BL", "GR", "RD", "YL", "OR", "WT", "GY" }[random.Next(0, 8)]; string manufacturingSite = ManufacturingSites.manufacturingSites[random.Next(0, ManufacturingSites.manufacturingSites.Length)]; return $"{deptAbb}-{firstDigit}{nextTwoDigits}-{sizeCode}-{colorCode}-{manufacturingSite}"; }
In the code editor, comment out the following code block:
int indexOfDept = Array.IndexOf(ProdDepartments.departmentNames, salesData[i].departmentName); string deptAbb = ProdDepartments.departmentAbbreviations[indexOfDept]; string firstDigit = (indexOfDept + 1).ToString(); string nextTwoDigits = random.Next(1, 100).ToString("D2"); string sizeCode = new string[] { "XS", "S", "M", "L", "XL" }[random.Next(0, 5)]; string colorCode = new string[] { "BK", "BL", "GR", "RD", "YL", "OR", "WT", "GY" }[random.Next(0, 8)]; string manufacturingSite = ManufacturingSites.manufacturingSites[random.Next(0, ManufacturingSites.manufacturingSites.Length)]; salesData[i].productID = $"{deptAbb}-{firstDigit}{nextTwoDigits}-{sizeCode}-{colorCode}-{manufacturingSite}";
Create a blank code line below the commented code block.
GitHub Copilot suggests a code line completion that uses the new method to assign the productID value.
To accept the suggested code line completion, select Accept.
Your updated
GenerateSalesData
method should be similar to the following snippet:public SalesData[] GenerateSalesData() { SalesData[] salesData = new SalesData[1000]; Random random = new Random(); for (int i = 0; i < 1000; i++) { salesData[i].dateSold = new DateOnly(2023, random.Next(1, 13), random.Next(1, 29)); salesData[i].departmentName = ProdDepartments.departmentNames[random.Next(0, ProdDepartments.departmentNames.Length)]; // int indexOfDept = Array.IndexOf(ProdDepartments.departmentNames, salesData[i].departmentName); // string deptAbb = ProdDepartments.departmentAbbreviations[indexOfDept]; // string firstDigit = (indexOfDept + 1).ToString(); // string nextTwoDigits = random.Next(1, 100).ToString("D2"); // string sizeCode = new string[] { "XS", "S", "M", "L", "XL" }[random.Next(0, 5)]; // string colorCode = new string[] { "BK", "BL", "GR", "RD", "YL", "OR", "WT", "GY" }[random.Next(0, 8)]; // string manufacturingSite = ManufacturingSites.manufacturingSites[random.Next(0, ManufacturingSites.manufacturingSites.Length)]; // // salesData[i].productID = $"{deptAbb}-{firstDigit}{nextTwoDigits}-{sizeCode}-{colorCode}-{manufacturingSite}"; salesData[i].productID = ConstructProductId(salesData[i]); salesData[i].quantitySold = random.Next(1, 101); salesData[i].unitPrice = random.Next(25, 300) + random.NextDouble(); salesData[i].baseCost = salesData[i].unitPrice * (1 - (random.Next(5, 21) / 100.0)); salesData[i].volumeDiscount = (int)(salesData[i].quantitySold * 0.1); } return salesData; }
Create the DeconstructProductId method
The DeconstructProductId
method extracts the department abbreviation, product serial number, size code, color code, and manufacturing site from the productID. The method returns a 2-dimensional array containing the description and value for each of these components.
Your goal is a QuarterlySalesReport method that uses the extracted product serial number from DeconstructProductId to identify the most profitable product for each quarter (ignoring size, color, and manufacturing location).
Create a blank code line below the ConstructProductId method.
Open inline chat and then enter the following prompt:
Create a public static method named DeconstructProductId that takes a productID string as input and returns a 2-dimensional array containing the description and value for: department abbreviation, product serial number, size code, color code, and manufacturing site.
Review the suggested code update and then select Accept.
The suggested code update should be similar to the following snippet:
public static string[,] DeconstructProductId(string productID) { string[] parts = productID.Split('-'); string[,] result = new string[5, 2]; result[0, 0] = "Department Abbreviation"; result[0, 1] = parts[0]; result[1, 0] = "Serial Number"; result[1, 1] = parts[1]; result[2, 0] = "Size Code"; result[2, 1] = parts[2]; result[3, 0] = "Color Code"; result[3, 1] = parts[3]; result[4, 0] = "Manufacturing Site"; result[4, 1] = parts[4]; return result; }
Update QuarterlySalesReport
method to identify the most profitable product
Select the
QuarterlySalesReport
method in the code editor.Open an inline chat and then enter the following prompt:
Update the QuarterlySalesReport method to include a dictionary named quarterlyTopProfitForProductNumbers that uses the calendar quarter as the key and another dictionary as the value. The inner dictionary should use string for the key and it should have five values: integer, double, double, double, double. Inside the foreach loop that iterates through the SalesData array, use DeconstructProductId()[1, 1] to get the product serial number and GetQuarter to get the quarter. For each yearly quarter, build the inner dictionary as follows: If the product serial number isn't already a key in the inner dictionary for the quarter, add it the appropriate inner dictionary as a new key and set the associated values as follows: units sold, total sales amount, unit cost, total profit, and profit percentage for the current sales order. If the product serial number is already a key in the inner dictionary for the quarter, update the appropriate inner dictionary values as follows: sum the units sold, sum the total sales amount, calculate the average unit cost (total sales divided by units sold), sum the total profit, and calculate the average profit percentage.
Review the suggested code update and then select Accept.
Review the updates in the code editor and use GitHub Copilot to fix any errors or issues that are identified.
Locate the code line that assigns the value to the product serial number variable.
For example:
string productSerialNumber = DeconstructProductId(data.productID)[1, 1];
Manually update the code line to include the abbreviation for the department and placeholders values for the other productId components as follows:
string[,] deconstructedProductId = DeconstructProductId(data.productID); string productSerialNumber = deconstructedProductId[0, 1] + "-" + deconstructedProductId[1, 1] + "-ss-cc-mmm";
Select the
QuarterlySalesReport
method in the code editor.Open an inline chat and then enter the following prompt:
Update the QuarterlySalesReport method to identify the three product serial numbers in quarterlyTopProfitForProductNumbers with highest profit during each quarter. Update the quarterly report to display the following data for the three most profitable product serial numbers in each quarter: the product serial number, the sum of units sold, the sum of total sales, the calculated average unit cost, the sum of total profit, and the calculated average profit percentage.
Review the suggested code update and then select Accept.
Review the updates in the code editor and use GitHub Copilot to fix any errors or issues that are identified.
At this point, the code generated by GitHub Copilot could be displaying the top three product serial numbers in descending order. The code could also be formatting the information as a table.
Save the changes to the Program.cs file, and then run the application to verify that the updates work as expected.
If the output isn't ordered correctly and displayed in a table format, use the following prompts to finish updating the QuarterlySalesReport method.
Select the
QuarterlySalesReport
method in the code editor.Open an inline chat and then enter the following prompt:
Update the QuarterlySalesReport method to list three most profitable product serial numbers in descending order of profit (highest to lowest).
Save and run the application to verify that the updates work as expected before applying updates with the next prompt.
Update the QuarterlySalesReport method to display all of information for the top three most profitable products in a table. The left column should be 22 characters wide. The remaining columns should be 20 characters wide. Each column should have a right-aligned header. The sales data should be right-aligned and padded to fit the column with.
Save and run the application to verify that the updates work as expected before applying updates with the next prompt.
Use extended ASCII characters to add border lines to the top three most profitable products table. Border lines should enclose the table perimeter. Border lines should separate the interior columns of the table.
Your updated report should appear similar to the following output:
Quarterly Sales Report ---------------------- Q1: Sales: $2,010,770.47, Profit: $250,625.54, Profit Percentage: 18.00% By Department: ┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Department │ Sales │ Profit │ Profit Percentage │ ├───────────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ Accessories │ $252,135.52 │ $30,488.55 │ 12.00 │ │ Children's Clothing │ $228,808.70 │ $30,664.28 │ 8.00 │ │ Footwear │ $312,223.23 │ $37,551.63 │ 8.00 │ │ Men's Clothing │ $233,815.71 │ $29,824.88 │ 18.00 │ │ Outerwear │ $313,551.48 │ $39,147.46 │ 13.00 │ │ Sportswear │ $243,726.05 │ $26,286.92 │ 18.00 │ │ Undergarments │ $217,172.86 │ $28,275.53 │ 9.00 │ │ Women's Clothing │ $209,336.93 │ $28,386.29 │ 14.00 │ └───────────────────────┴───────────────────┴───────────────────┴───────────────────┘ Top 3 Sales Orders: ┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Product ID │ Quantity Sold │ Unit Price │ Total Sales │ Profit │ Profit Percentage │ ├───────────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ UNDR-838-S-YL-CA1 │ 97 │ $292.46 │ $28,368.69 │ $5,390.05 │ 19.00 │ │ FOOT-556-XL-WT-US1 │ 91 │ $250.74 │ $22,817.69 │ $4,563.54 │ 20.00 │ │ UNDR-855-XL-GY-CA1 │ 76 │ $265.29 │ $20,162.19 │ $4,032.44 │ 20.00 │ └───────────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘ Top 3 Most Profitable Product Serial Numbers: ┌───────────────────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Product Serial Number │ Units Sold │ Total Sales │ Avg Unit Cost │ Total Profit │ Avg Profit % │ ├───────────────────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ UNDR-838-ss-cc-mmm │ 97 │ $28,368.69 │ $292.46 │ $5,390.05 │ 19.00 │ │ MENS-152-ss-cc-mmm │ 120 │ $30,943.28 │ $257.86 │ $4,797.87 │ 15.51 │ │ ACCS-445-ss-cc-mmm │ 152 │ $41,133.81 │ $270.62 │ $4,590.81 │ 11.16 │ └───────────────────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘ Q2: Sales: $2,068,016.14, Profit: $236,217.45, Profit Percentage: 15.00% By Department: ┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Department │ Sales │ Profit │ Profit Percentage │ ├───────────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ Accessories │ $268,849.76 │ $26,080.13 │ 15.00 │ │ Children's Clothing │ $305,464.95 │ $37,580.35 │ 15.00 │ │ Footwear │ $216,937.30 │ $25,441.11 │ 17.00 │ │ Men's Clothing │ $278,199.36 │ $30,657.58 │ 5.00 │ │ Outerwear │ $302,285.12 │ $35,261.71 │ 12.00 │ │ Sportswear │ $224,366.31 │ $26,223.01 │ 15.00 │ │ Undergarments │ $299,010.01 │ $32,781.72 │ 7.00 │ │ Women's Clothing │ $172,903.33 │ $22,191.85 │ 13.00 │ └───────────────────────┴───────────────────┴───────────────────┴───────────────────┘ Top 3 Sales Orders: ┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Product ID │ Quantity Sold │ Unit Price │ Total Sales │ Profit │ Profit Percentage │ ├───────────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ UNDR-817-M-BL-JP1 │ 80 │ $261.50 │ $20,919.64 │ $3,974.73 │ 19.00 │ │ SPRT-793-S-GR-UK3 │ 97 │ $215.94 │ $20,946.30 │ $3,560.87 │ 17.00 │ │ SPRT-768-L-WT-CA1 │ 94 │ $289.49 │ $27,211.60 │ $3,265.39 │ 12.00 │ └───────────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘ Top 3 Most Profitable Product Serial Numbers: ┌───────────────────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Product Serial Number │ Units Sold │ Total Sales │ Avg Unit Cost │ Total Profit │ Avg Profit % │ ├───────────────────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ CHLD-376-ss-cc-mmm │ 195 │ $46,889.55 │ $240.46 │ $6,774.29 │ 14.45 │ │ SPRT-768-ss-cc-mmm │ 254 │ $49,206.83 │ $193.73 │ $6,379.36 │ 12.96 │ │ WOMN-227-ss-cc-mmm │ 131 │ $33,440.90 │ $255.27 │ $5,366.41 │ 16.05 │ └───────────────────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘ Q3: Sales: $1,935,109.61, Profit: $236,097.44, Profit Percentage: 9.00% By Department: ┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Department │ Sales │ Profit │ Profit Percentage │ ├───────────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ Accessories │ $209,769.49 │ $23,696.91 │ 14.00 │ │ Children's Clothing │ $284,063.17 │ $36,659.61 │ 9.00 │ │ Footwear │ $317,501.38 │ $46,104.88 │ 11.00 │ │ Men's Clothing │ $247,665.36 │ $26,148.64 │ 7.00 │ │ Outerwear │ $192,230.64 │ $20,248.42 │ 9.00 │ │ Sportswear │ $157,918.85 │ $19,439.11 │ 16.00 │ │ Undergarments │ $235,916.29 │ $28,426.17 │ 13.00 │ │ Women's Clothing │ $290,044.42 │ $35,373.69 │ 8.00 │ └───────────────────────┴───────────────────┴───────────────────┴───────────────────┘ Top 3 Sales Orders: ┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Product ID │ Quantity Sold │ Unit Price │ Total Sales │ Profit │ Profit Percentage │ ├───────────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ CHLD-335-L-RD-JP2 │ 99 │ $270.39 │ $26,769.08 │ $5,353.82 │ 20.00 │ │ FOOT-521-M-GY-JP1 │ 92 │ $243.07 │ $22,362.70 │ $4,472.54 │ 20.00 │ │ FOOT-580-M-WT-CA1 │ 71 │ $247.36 │ $17,562.49 │ $3,336.87 │ 19.00 │ └───────────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘ Top 3 Most Profitable Product Serial Numbers: ┌───────────────────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Product Serial Number │ Units Sold │ Total Sales │ Avg Unit Cost │ Total Profit │ Avg Profit % │ ├───────────────────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ CHLD-335-ss-cc-mmm │ 99 │ $26,769.08 │ $270.39 │ $5,353.82 │ 20.00 │ │ FOOT-521-ss-cc-mmm │ 92 │ $22,362.70 │ $243.07 │ $4,472.54 │ 20.00 │ │ FOOT-580-ss-cc-mmm │ 112 │ $23,402.50 │ $208.95 │ $4,446.48 │ 19.00 │ └───────────────────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘ Q4: Sales: $1,921,871.92, Profit: $243,382.82, Profit Percentage: 19.00% By Department: ┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Department │ Sales │ Profit │ Profit Percentage │ ├───────────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ Accessories │ $256,825.90 │ $33,466.30 │ 19.00 │ │ Children's Clothing │ $304,779.08 │ $34,547.16 │ 7.00 │ │ Footwear │ $254,356.72 │ $33,535.07 │ 20.00 │ │ Men's Clothing │ $238,480.94 │ $34,185.50 │ 19.00 │ │ Outerwear │ $265,190.55 │ $34,568.15 │ 9.00 │ │ Sportswear │ $202,590.30 │ $23,044.17 │ 15.00 │ │ Undergarments │ $176,759.63 │ $22,135.85 │ 13.00 │ │ Women's Clothing │ $222,888.81 │ $27,900.62 │ 7.00 │ └───────────────────────┴───────────────────┴───────────────────┴───────────────────┘ Top 3 Sales Orders: ┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Product ID │ Quantity Sold │ Unit Price │ Total Sales │ Profit │ Profit Percentage │ ├───────────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ MENS-109-XS-RD-US2 │ 96 │ $262.21 │ $25,171.89 │ $5,034.38 │ 20.00 │ │ MENS-128-L-YL-UK2 │ 89 │ $291.70 │ $25,961.39 │ $4,153.82 │ 16.00 │ │ MENS-176-XS-WT-US3 │ 87 │ $282.90 │ $24,611.94 │ $3,937.91 │ 16.00 │ └───────────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘ Top 3 Most Profitable Product Serial Numbers: ┌───────────────────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┐ │ Product Serial Number │ Units Sold │ Total Sales │ Avg Unit Cost │ Total Profit │ Avg Profit % │ ├───────────────────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┤ │ MENS-176-ss-cc-mmm │ 128 │ $32,260.04 │ $252.03 │ $5,314.57 │ 16.47 │ │ ACCS-489-ss-cc-mmm │ 129 │ $31,755.76 │ $246.17 │ $5,063.68 │ 15.95 │ │ MENS-109-ss-cc-mmm │ 96 │ $25,171.89 │ $262.21 │ $5,034.38 │ 20.00 │ └───────────────────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘
Updated Source Code
You can review the code updates for this challenge below.
namespace ReportGenerator
{
class QuarterlyIncomeReport
{
static void Main(string[] args)
{
// create a new instance of the class
QuarterlyIncomeReport report = new QuarterlyIncomeReport();
// call the GenerateSalesData method
SalesData[] salesData = report.GenerateSalesData();
// call the QuarterlySalesReport method
report.QuarterlySalesReport(salesData);
}
/* public struct SalesData includes the following fields: date sold, department name, product ID, quantity sold, unit price */
public struct SalesData
{
public DateOnly dateSold;
public string departmentName;
public string productID;
public int quantitySold;
public double unitPrice;
public double baseCost;
public int volumeDiscount;
}
public struct ProdDepartments
{
public static string[] departmentNames = { "Men's Clothing", "Women's Clothing", "Children's Clothing", "Accessories", "Footwear", "Outerwear", "Sportswear", "Undergarments" };
public static string[] departmentAbbreviations = { "MENS", "WOMN", "CHLD", "ACCS", "FOOT", "OUTR", "SPRT", "UNDR" };
}
public struct ManufacturingSites
{
public static string[] manufacturingSites = { "US1", "US2", "US3", "UK1", "UK2", "UK3", "JP1", "JP2", "JP3", "CA1" };
}
/* the GenerateSalesData method returns 1000 SalesData records. It assigns random values to each field of the data structure */
public SalesData[] GenerateSalesData()
{
SalesData[] salesData = new SalesData[100000];
Random random = new Random();
for (int i = 0; i < 100000; i++)
{
salesData[i].dateSold = new DateOnly(2023, random.Next(1, 13), random.Next(1, 29));
salesData[i].departmentName = ProdDepartments.departmentNames[random.Next(0, ProdDepartments.departmentNames.Length)];
// int indexOfDept = Array.IndexOf(ProdDepartments.departmentNames, salesData[i].departmentName);
// string deptAbb = ProdDepartments.departmentAbbreviations[indexOfDept];
// string firstDigit = (indexOfDept + 1).ToString();
// string nextTwoDigits = random.Next(1, 100).ToString("D2");
// string sizeCode = new string[] { "XS", "S", "M", "L", "XL" }[random.Next(0, 5)];
// string colorCode = new string[] { "BK", "BL", "GR", "RD", "YL", "OR", "WT", "GY" }[random.Next(0, 8)];
// string manufacturingSite = ManufacturingSites.manufacturingSites[random.Next(0, ManufacturingSites.manufacturingSites.Length)];
// salesData[i].productID = $"{deptAbb}-{firstDigit}{nextTwoDigits}-{sizeCode}-{colorCode}-{manufacturingSite}";
salesData[i].productID = ConstructProductId(salesData[i], random);
salesData[i].quantitySold = random.Next(1, 101);
salesData[i].unitPrice = random.Next(25, 300) + random.NextDouble();
salesData[i].baseCost = salesData[i].unitPrice * (1 - (random.Next(5, 21) / 100.0));
salesData[i].volumeDiscount = (int)(salesData[i].quantitySold * 0.1);
}
return salesData;
}
public static string ConstructProductId(SalesData salesDataItem, Random random)
{
int indexOfDept = Array.IndexOf(ProdDepartments.departmentNames, salesDataItem.departmentName);
string deptAbb = ProdDepartments.departmentAbbreviations[indexOfDept];
string firstDigit = (indexOfDept + 1).ToString();
string nextTwoDigits = random.Next(1, 100).ToString("D2");
string sizeCode = new string[] { "XS", "S", "M", "L", "XL" }[random.Next(0, 5)];
string colorCode = new string[] { "BK", "BL", "GR", "RD", "YL", "OR", "WT", "GY" }[random.Next(0, 8)];
string manufacturingSite = ManufacturingSites.manufacturingSites[random.Next(0, ManufacturingSites.manufacturingSites.Length)];
return $"{deptAbb}-{firstDigit}{nextTwoDigits}-{sizeCode}-{colorCode}-{manufacturingSite}";
}
public static string[,] DeconstructProductId(string productID)
{
string[] parts = productID.Split('-');
string[,] deconstructedProduct = new string[5, 2];
deconstructedProduct[0, 0] = "Department Abbreviation";
deconstructedProduct[0, 1] = parts[0];
deconstructedProduct[1, 0] = "Product Serial Number";
deconstructedProduct[1, 1] = parts[1];
deconstructedProduct[2, 0] = "Size Code";
deconstructedProduct[2, 1] = parts[2];
deconstructedProduct[3, 0] = "Color Code";
deconstructedProduct[3, 1] = parts[3];
deconstructedProduct[4, 0] = "Manufacturing Site";
deconstructedProduct[4, 1] = parts[4];
return deconstructedProduct;
}
public void QuarterlySalesReport(SalesData[] salesData)
{
// create a dictionary to store the quarterly sales data
Dictionary<string, double> quarterlySales = new Dictionary<string, double>();
Dictionary<string, double> quarterlyProfit = new Dictionary<string, double>();
Dictionary<string, double> quarterlyProfitPercentage = new Dictionary<string, double>();
// create a dictionary to store the quarterly sales data by department
Dictionary<string, Dictionary<string, double>> quarterlySalesByDepartment = new Dictionary<string, Dictionary<string, double>>();
Dictionary<string, Dictionary<string, double>> quarterlyProfitByDepartment = new Dictionary<string, Dictionary<string, double>>();
Dictionary<string, Dictionary<string, double>> quarterlyProfitPercentageByDepartment = new Dictionary<string, Dictionary<string, double>>();
// create a dictionary to store the top 3 sales orders by quarter
Dictionary<string, List<SalesData>> top3SalesOrdersByQuarter = new Dictionary<string, List<SalesData>>();
// create a dictionary to store the quarterly top profit for product numbers
Dictionary<string, Dictionary<string, (int, double, double, double, double)>> quarterlyTopProfitForProductNumbers = new Dictionary<string, Dictionary<string, (int, double, double, double, double)>>();
// iterate through the sales data
foreach (SalesData data in salesData)
{
// calculate the total sales for each quarter
string quarter = GetQuarter(data.dateSold.Month);
double totalSales = data.quantitySold * data.unitPrice;
double totalCost = data.quantitySold * data.baseCost;
double profit = totalSales - totalCost;
double profitPercentage = (profit / totalSales) * 100;
// calculate the total sales, profit, and profit percentage by department
if (!quarterlySalesByDepartment.ContainsKey(quarter))
{
quarterlySalesByDepartment.Add(quarter, new Dictionary<string, double>());
quarterlyProfitByDepartment.Add(quarter, new Dictionary<string, double>());
quarterlyProfitPercentageByDepartment.Add(quarter, new Dictionary<string, double>());
}
if (quarterlySalesByDepartment[quarter].ContainsKey(data.departmentName))
{
quarterlySalesByDepartment[quarter][data.departmentName] += totalSales;
quarterlyProfitByDepartment[quarter][data.departmentName] += profit;
}
else
{
quarterlySalesByDepartment[quarter].Add(data.departmentName, totalSales);
quarterlyProfitByDepartment[quarter].Add(data.departmentName, profit);
}
if (!quarterlyProfitPercentageByDepartment[quarter].ContainsKey(data.departmentName))
{
quarterlyProfitPercentageByDepartment[quarter].Add(data.departmentName, profitPercentage);
}
// calculate the total sales and profit for each quarter
if (quarterlySales.ContainsKey(quarter))
{
quarterlySales[quarter] += totalSales;
quarterlyProfit[quarter] += profit;
}
else
{
quarterlySales.Add(quarter, totalSales);
quarterlyProfit.Add(quarter, profit);
}
if (!quarterlyProfitPercentage.ContainsKey(quarter))
{
quarterlyProfitPercentage.Add(quarter, profitPercentage);
}
// add the sales data to the top 3 sales orders by quarter
if (!top3SalesOrdersByQuarter.ContainsKey(quarter))
{
top3SalesOrdersByQuarter.Add(quarter, new List<SalesData>());
}
top3SalesOrdersByQuarter[quarter].Add(data);
// update the quarterly top profit for product numbers
//string productSerialNumber = DeconstructProductId(data.productID)[1, 1];
string[,] deconstructedProductId = DeconstructProductId(data.productID);
string productSerialNumber = deconstructedProductId[0, 1] + "-" + deconstructedProductId[1, 1] + "-ss-cc-mmm";
if (!quarterlyTopProfitForProductNumbers.ContainsKey(quarter))
{
quarterlyTopProfitForProductNumbers.Add(quarter, new Dictionary<string, (int, double, double, double, double)>());
}
if (quarterlyTopProfitForProductNumbers[quarter].ContainsKey(productSerialNumber))
{
var (unitsSold, totalSalesAmount, unitCost, totalProfit, innerProfitPercentage) = quarterlyTopProfitForProductNumbers[quarter][productSerialNumber];
unitsSold += data.quantitySold;
totalSalesAmount += totalSales;
unitCost = totalSalesAmount / unitsSold;
totalProfit += profit;
innerProfitPercentage = (totalProfit / totalSalesAmount) * 100;
quarterlyTopProfitForProductNumbers[quarter][productSerialNumber] = (unitsSold, totalSalesAmount, unitCost, totalProfit, innerProfitPercentage);
}
else
{
quarterlyTopProfitForProductNumbers[quarter].Add(productSerialNumber, (data.quantitySold, totalSales, data.baseCost, profit, profitPercentage));
}
}
// sort the top 3 sales orders by profit in descending order
foreach (var quarter in top3SalesOrdersByQuarter.Keys)
{
top3SalesOrdersByQuarter[quarter] = top3SalesOrdersByQuarter[quarter]
.OrderByDescending(order => (order.quantitySold * order.unitPrice) - (order.quantitySold * order.baseCost))
.Take(3)
.ToList();
}
// display the quarterly sales report
Console.WriteLine("Quarterly Sales Report");
Console.WriteLine("----------------------");
// sort the quarterly sales by key (quarter)
var sortedQuarterlySales = quarterlySales.OrderBy(q => q.Key);
foreach (KeyValuePair<string, double> quarter in sortedQuarterlySales)
{
// format the sales amount as currency using regional settings
string formattedSalesAmount = quarter.Value.ToString("C");
string formattedProfitAmount = quarterlyProfit[quarter.Key].ToString("C");
string formattedProfitPercentage = quarterlyProfitPercentage[quarter.Key].ToString("F2");
Console.WriteLine("{0}: Sales: {1}, Profit: {2}, Profit Percentage: {3}%", quarter.Key, formattedSalesAmount, formattedProfitAmount, formattedProfitPercentage);
// display the quarterly sales, profit, and profit percentage by department
Console.WriteLine("By Department:");
var sortedQuarterlySalesByDepartment = quarterlySalesByDepartment[quarter.Key].OrderBy(d => d.Key);
// Print table headers
Console.WriteLine("┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┐");
Console.WriteLine("│ Department │ Sales │ Profit │ Profit Percentage │");
Console.WriteLine("├───────────────────────┼───────────────────┼───────────────────┼───────────────────┤");
foreach (KeyValuePair<string, double> department in sortedQuarterlySalesByDepartment)
{
string formattedDepartmentSalesAmount = department.Value.ToString("C");
string formattedDepartmentProfitAmount = quarterlyProfitByDepartment[quarter.Key][department.Key].ToString("C");
string formattedDepartmentProfitPercentage = quarterlyProfitPercentageByDepartment[quarter.Key][department.Key].ToString("F2");
Console.WriteLine("│ {0,-22}│ {1,17} │ {2,17} │ {3,17} │", department.Key, formattedDepartmentSalesAmount, formattedDepartmentProfitAmount, formattedDepartmentProfitPercentage);
}
Console.WriteLine("└───────────────────────┴───────────────────┴───────────────────┴───────────────────┘");
Console.WriteLine();
// display the top 3 sales orders for the quarter
Console.WriteLine("Top 3 Sales Orders:");
var top3SalesOrders = top3SalesOrdersByQuarter[quarter.Key];
// Print table headers
Console.WriteLine("┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┐");
Console.WriteLine("│ Product ID │ Quantity Sold │ Unit Price │ Total Sales │ Profit │ Profit Percentage │");
Console.WriteLine("├───────────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┤");
foreach (SalesData salesOrder in top3SalesOrders)
{
double orderTotalSales = salesOrder.quantitySold * salesOrder.unitPrice;
double orderProfit = orderTotalSales - (salesOrder.quantitySold * salesOrder.baseCost);
double orderProfitPercentage = (orderProfit / orderTotalSales) * 100;
Console.WriteLine("│ {0,-22}│ {1,17} │ {2,17} │ {3,17} │ {4,17} │ {5,17} │", salesOrder.productID, salesOrder.quantitySold, salesOrder.unitPrice.ToString("C"), orderTotalSales.ToString("C"), orderProfit.ToString("C"), orderProfitPercentage.ToString("F2"));
}
Console.WriteLine("└───────────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘");
Console.WriteLine();
// display the quarterly top profit for product numbers
Console.WriteLine("Quarterly Top Profit for Product Numbers:");
var quarterlyTopProfit = quarterlyTopProfitForProductNumbers[quarter.Key];
// Sort the quarterly top profit by profit in descending order
var sortedQuarterlyTopProfit = quarterlyTopProfit.OrderByDescending(p => p.Value.Item4).Take(3);
// Print table headers
Console.WriteLine("┌───────────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┬───────────────────┐");
Console.WriteLine("│ Product Serial No │ Units Sold │ Total Sales │ Unit Cost │ Total Profit │ Profit Percentage │");
Console.WriteLine("├───────────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┼───────────────────┤");
foreach (KeyValuePair<string, (int, double, double, double, double)> product in sortedQuarterlyTopProfit)
{
var (unitsSold, totalSalesAmount, unitCost, totalProfit, profitPercentage) = product.Value;
Console.WriteLine("│ {0,-22}│ {1,17} │ {2,17} │ {3,17} │ {4,17} │ {5,17} │", product.Key, unitsSold, totalSalesAmount.ToString("C"), unitCost.ToString("C"), totalProfit.ToString("C"), profitPercentage.ToString("F2"));
}
Console.WriteLine("└───────────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┴───────────────────┘");
Console.WriteLine();
}
}
public string GetQuarter(int month)
{
if (month >= 1 && month <= 3)
{
return "Q1";
}
else if (month >= 4 && month <= 6)
{
return "Q2";
}
else if (month >= 7 && month <= 9)
{
return "Q3";
}
else
{
return "Q4";
}
}
}
}