How to get total annual sales - c#

In my ASP.NET Core 6 Web API, I have used the code below to calculate Total Annual Sales (from Transactions):
private async Task<decimal> GetAllTotalMonthlyTransactions()
{
string transactionMonth = DateTime.Now.ToString("MM");
decimal totalMonthlyTransactions = 0;
var sales = await _dbContext.Sales.ToListAsync();
foreach (var item in sales)
{
var salesDate = item.CreatedAt.ToString();
var salesMonth = salesDate.Substring(3, 2);
if (transactionMonth == salesMonth)
{
totalMonthlyTransactions += item.Amount;
}
}
return totalMonthlyTransactions;
}
How to I re-write the code above to get TotalAnnualSales (Transactions)?

private async Task<decimal> GetAllTotalMonthlyTransactions()
{
int currentYear = DateTime.Now.Year;
return (await dbContext.Sales.Where(t=>t.CreatedAt.Year==currentYear).ToListAsync()).Sum(item=>item.Amount);
}

private async Task<decimal> GetAllTotalMonthlyTransactions()
{
decimal totalAnnualTransactions = 0;
int _year = DateTime.Now.Year;
var sales = await dbContext.Sales.Where(y=>y.CreatedAt.Year==_year).ToListAsync();
// The first method
foreach (var item in sales)
{
totalAnnualTransactions += item.Amount;
}
// The second method
//double totalAnnualTransactions= sales.Sum(item => item.Amount);
return totalAnnualTransactions;
}

Instead of converting the datetimes to strings, try accessing them as ints. You can do this with .Month or .Year respectively

You can try to calculate from and to date range based on a specified month, then query/filter sales data with these from and to date range, like below.
var date1 = DateTime.Now;
var days = DateTime.DaysInMonth(date1.Year, date1.Month);
var from = new DateTime(date1.Year, date1.Month, 1, 0, 0, 0);
var to = new DateTime(date1.Year, date1.Month, days, 23, 59, 59);
var totalMonthlyTransactions = sales.AsEnumerable().Where(s => s.CreatedAt >= from && s.CreatedAt <= to).Select(s => s.Amount).Sum();

I hope following code will work for you...
private async Task<IEnumerable<object>> GetMonthlyTransactions(int year)
{
var starting = new DateTime(year, 1, 1).Date;
var sales = await context.Sales.Where(f => f.SalesDate >= starting).Select(s => new
{
Date = s.SalesDate.ToString("MM-yyyy"),
Amount = s.NetAmount
}).ToListAsync();
var finaldata = sales.GroupBy(s => s.Date, (d, a) => new { Date = d, Am = a }).Select(l => new
{
Date = l.Date,
Amount = l.Am.Sum(h => h.Amount)
});
return finaldata;
}

Related

how to calculate sum of times in a column using foreach loop ASP.NET MVC

enter image description here
suppose A is my Table and inside this table i have one column like times
inside column name times i have n number of times
Example :-1st:-02:30
2nd:-03:25
3rd:-00:45
i want output like TotalTime=06:40
i got out put using jquery but i want how to do inside a controller using foreach loop please help me
my controller code:-
[HttpPost]
public ActionResult getTimeSheetByBasit(DateTime? CurrentDate, string ActivityTime)
//obj.UserDocumentList = ThidDb.UserDocument.Where(x => x.CreatedBy == UserId).ToList();
{
VM_TimeSheet ObjVM_TimeSheet = new VM_TimeSheet();
int LoggedUser = User.KEY();
string LoggedUserName = User.UserName();
string UserEmail = User.EmailID();
DateTime TimeIn, TimeOut;
string TimeInn, TimeOuut, TotalTime;
//code add here fot adding text box time with total houres enter by user select time Sheet
using (SecurenetDB SecurenetDB = new SecurenetDB())
{
ObjVM_TimeSheet.TimesheetList = SecurenetDB.AD_TimeSheet.Where(x => DbFunctions.TruncateTime(x.ActivityDate.Value) == DbFunctions.TruncateTime(CurrentDate) && x.UserKEY == LoggedUser).ToList();
TimeIn = SecurenetDB.AD_CardPunching.Where(x => DbFunctions.TruncateTime(x.EventDate) == DbFunctions.TruncateTime(CurrentDate) && x.UserName == LoggedUserName).Select(x => x.Time_In).FirstOrDefault();
TimeOut = SecurenetDB.AD_CardPunching.Where(x => DbFunctions.TruncateTime(x.EventDate) == DbFunctions.TruncateTime(CurrentDate) && x.UserName == LoggedUserName).Select(x => x.Time_Out).FirstOrDefault();
TimeInn = TimeIn.ToString("hh:mm tt");
TimeOuut = TimeOut.ToString("hh:mm tt");
TotalTime = SecurenetDB.AD_CardPunching.Where(x => DbFunctions.TruncateTime(x.EventDate) == DbFunctions.TruncateTime(CurrentDate) && x.UserName == LoggedUserName).Select(x => x.TotalHours).FirstOrDefault();
// ObjVM_TimeSheet.TimesheetList=SecurenetDB.AD_TimeSheet.Where(x=>x.Hours== TextTime && x.UserKEY == LoggedUser).ToList();
var sum = "00:00";
foreach(var iteam in ActivityTime)
{
sum = sum + iteam;
}
}
return Json(new
{
TimeSheetData = this.RenderPartialViewToString("TimeSheetData", ObjVM_TimeSheet.TimesheetList),
TimeIn = TimeInn,
TimeOut = TimeOuut,
TotalTime = TotalTime
}, JsonRequestBehavior.AllowGet);
}
enter image description here
Use TimeSpan
string[] times = new string[] {"02:30", "03:25", "00:45"};
TimeSpan totalTime = new TimeSpan(0);
foreach (string time in times)
{
TimeSpan ts = TimeSpan.Parse(time);
totalTime += ts;
}
Console.WriteLine(totalTime.ToString(#"hh\:mm"));
Output
06:40

Chart does not load in first run

I create a chart based on data of a GridView, so if a user select a row in my grid view I execute the chart create as:
private void dgvUserActivity_CellClick(object sender, DataGridViewCellEventArgs e)
{
var dg = (DataGridView)sender;
if (e.RowIndex == -1) return;
var selectedrowindex = dg.SelectedCells[0].RowIndex;
var selectedRow = dg.Rows[selectedrowindex];
var selectedUserName = selectedRow.Cells["UserName"].Value.ToString();
UserActivityAuditModel = UserActivityModel.UserActivityAuditList.Where(x => x.UserName == selectedUserName).ToList();
ClearChartPoints();
userChart.Titles.Clear();
LoadChart(UserActivityAuditModel);
}
Then I load chart as:
private void LoadChart(IList<UserActivityAuditViewModel> model)
{
//Filter info
var selectedTime = new List<TimeSpan>();
if (rdoLogOn.Checked)
{
selectedTime = model.Select(x => x.AverageLogOn).ToList();
}
else if (rdoLogOff.Checked)
{
selectedTime = model.Select(x => x.AverageLogOff).ToList();
}
else
{
selectedTime = model.Select(x => x.AverageTotalHours).ToList();
}
Axis XA = userChart.ChartAreas[0].AxisX;
Axis YA = userChart.ChartAreas[0].AxisY;
Series S1 = userChart.Series[0];
S1.ChartType = SeriesChartType.Line;
//Add or change Title
var title = new Title();
title.Font = new Font("Arial", 14, FontStyle.Bold);
title.Text = Helpers.FirstCharToUpper(model.Select(x => x.UserName).FirstOrDefault());
userChart.Titles.Add(title);
var dates = model.Select(x => x.ActivityDate).ToList();
var currentRegister = 0;
foreach (DateTime d in dates)
{
var yValue = selectedTime[currentRegister].ToString();
S1.Points.AddXY(d, yValue);
currentRegister++;
}
var dt = DateTime.Now;
S1.LegendText = "Year " + dt.Year;
// move to the bottom center:
userChart.Legends[0].Docking = Docking.Bottom;
userChart.Legends[0].Alignment = StringAlignment.Center;
if (!rdoTotalHours.Checked)
{
S1.YValueType = ChartValueType.Time;
XA.LabelStyle.Format = "mm:ss";
}
else
{
}
S1.XValueType = ChartValueType.Date;
XA.MajorGrid.Enabled = false;
XA.LabelStyle.Format = "MMM";
XA.IntervalType = DateTimeIntervalType.Months;
XA.Interval = 1;
YA.IsInterlaced = true;
//YA.MajorGrid.Enabled = false;
YA.InterlacedColor = Color.FromArgb(31, Color.LightSeaGreen);
}
For some reason in my first clic of DataGridView it executes the chart create,I debug it and the problem is in this foreach clause :
foreach (DateTime d in dates)
{
var yValue = selectedTime[currentRegister].ToString();
S1.Points.AddXY(d, yValue);
currentRegister++;
}
The value is there, after S1.Points.AddXY(d, yValue); is executed, I debug Points Y value is always 0 but the yValue I set has the correct number!. This is really weird.
Pictures:
As you can see value is there, but when I press F10 to continue debugging:
It added as 0 instead my value
Note: As I say before, this is only first time load, if I clic again in any row, it load correctly, someone have an idea of what is happening there? Regards
----EDIT----
I get the yValue from my global model:
public IList<UserActivityAuditViewModel> UserActivityAuditModel { get; set; } = new List<UserActivityAuditViewModel>();
Then I assign it depending of some radio buttons on the beginning of chart method:
var selectedTime = new List<TimeSpan>();
if (rdoLogOn.Checked)
{
selectedTime = model.Select(x => x.AverageLogOn).ToList();
}
else if (rdoLogOff.Checked)
{
selectedTime = model.Select(x => x.AverageLogOff).ToList();
}
else
{
selectedTime = model.Select(x => x.AverageTotalHours).ToList();
}
and I use that TimeSpan list into yValue
I create a simple test, instead load my Y value with TimeSpan I create an int list as:
var testList = new List<int>();
testList.Add(1);
testList.Add(2);
testList.Add(3);
testList.Add(4);
testList.Add(5);
testList.Add(6);
testList.Add(7);
testList.Add(8);
testList.Add(9);
testList.Add(10);
testList.Add(11);
testList.Add(12);
testList.Add(13);
Then I use in foreach as
foreach (DateTime d in dates)
{
var yValue = testList[currentRegister].ToString();
S1.Points.AddXY(d, yValue);
currentRegister++;
}
And now chart load in first load, but I can not understand why is not working with TimeSpan in first load, can someone have an idea of what is happening and a solution for this?
I found the issue
Charts does not support TimeSpan, so for some reason at the first render of chart it conflicts with Chart data. So the simplest solution I found is to convert TimeSpan to DateTime, at the end of the day we use YValueType as Time so it will take the time of the DateTime and display instead Date:
foreach (DateTime d in dates)
{
var datetime = new DateTime(0).AddSeconds(selectedTime[currentRegister].TotalSeconds);
S1.Points.AddXY(d, datetime);
currentRegister++;
}

Find max time from string list

I want to find max time from a list of time formats, Its not exactly TimeSpan so parsing it won't help.
Please suggest a solution.
var duration = new List<string>() { "116:48:28", "110:36:28", "16:30:28"};
var maxts = duration.Max(x => TimeSpan.Parse(x));
You could try this. it will work in case, that you don't have something such as "1:70:10"...
duration.Select(d=>d.Replace(":", string.Empty)).Select(int.Parse).OrderBy(s=>s)
Or, to get tha value of maximal timestamp:
duration.Select(d => new {Text =d, Val = int.Parse(d.Replace(":", string.Empty))})
.OrderByDescending(x=>x.Val)
.First()
.Text;
You can use LINQ:
var sortedDuraion = duration
.OrderByDescending((s) => Convert.ToInt32(s.Split(':')[0]))
.ThenByDescending((s) => Convert.ToInt32(s.Split(':')[1]))
.ThenByDescending((s) => Convert.ToInt32(s.Split(':')[2]));
var max = sortedDuration.ElementAt(0);
Also you can parse this string to int (delete ":") and order as int values:
var sortedDuration = duration.OrderByDescending((s) => Convert.ToInt32(s.Replace(":", String.Empty)));
var max = sortedDuration.ElementAt(0);
you can try like this
var duration = new List<string>() { "116:48:28", "110:36:28", "16:30:28" };
List<TimeSpan> lst = new List<TimeSpan>();
foreach (var item in duration)
{
string[] data=item.Split(new char[]{':'});
lst.Add(new TimeSpan(int.Parse(data[0]),int.Parse(data[1]),int.Parse(data[2])));
}
var max = lst.Max();
You could use a regex:
internal static TimeSpan ParseSpecialTimespan(string toParse)
{
string pattern = #"^(\d+):([0-5]?\d{1}):([0-5]?\d{1})$";
var match = Regex.Match(toParse, pattern);
if (!match.Success) throw new ArgumentException(#"The provided string is not valid...");
int hours = int.Parse(match.Groups[1].ToString());
int minutes = int.Parse(match.Groups[2].ToString());
int seconds = int.Parse(match.Groups[3].ToString());
TimeSpan t = new TimeSpan(hours, minutes, seconds);
return t;
}
Here's how it's used:
var duration = new List<string>() { "116:48:28", "110:36:28", "16:30:28" };
string largestInput = "";
TimeSpan largestTimespan = new TimeSpan(0);
foreach (string d in duration)
{
TimeSpan parse = ParseSpecialTimespan(d);
if (parse > largestTimespan)
{
largestTimespan = parse;
largestInput = d;
}
}
System.Diagnostics.Debug.Print(#"Largest timespan is ""{0}"" from input ""{1}"".", largestTimespan.ToString(), largestInput);

How to remove a range from a list within a loop?

Greetings Please keep in mind there is no database and these are fake functions to make the component work for testing, i have a List which makes a 24 hours based on 15 minutes scale and produces from this method:
public List<ScaleLine> GetHoursAndScales(ScaleLine.Scales scale = ScaleLine.Scales.Fiftheen)
{
int _scale = Convert.ToInt32(scale);
int _count = _scale * 24;
int _scaleCount = 60 / _scale;
List<ScaleLine> _list = new List<ScaleLine>();
var start = DateTime.Today;
var clockQuery = from offset in Enumerable.Range(1, _count)
select TimeSpan.FromMinutes(_scaleCount * offset);
foreach (var time in clockQuery)
{
_list.Add(new ScaleLine() { Id = _list.Count, Hours = (start + time).ToString("HH:mm"), Scale = _scale });
}
return _list;
}
And i have another list which is called Reserved hours which is produces on this method:
public List<Reservedhours> AllReservedHours()
{
return new List<Reservedhours>
{
new Reservedhours() { Id = 1, Date = DateTime.Now, StartPoint = "08:00", EndPoint = "10:00" },
new Reservedhours() { Id = 2, Date = DateTime.Now, StartPoint = "14:00", EndPoint = "16:00" },
new Reservedhours() { Id = 3, Date = DateTime.Now, StartPoint = "20:00", EndPoint = "22:00" },
new Reservedhours() { Id = 4, Date = DateTime.Now.AddDays(1), StartPoint = "07:00", EndPoint = "11:00" },
new Reservedhours() { Id = 5, Date = DateTime.Now.AddDays(1), StartPoint = "13:00", EndPoint = "15:00" },
new Reservedhours() { Id = 6, Date = DateTime.Now.AddDays(1), StartPoint = "15:00", EndPoint = "18:00" },
new Reservedhours() { Id = 7, Date = DateTime.Now.AddDays(1), StartPoint = "18:00", EndPoint = "22:00" },
};
}
Now i have another list that produces the available hours based on reserved hours and first list that produces 24 hours:
public List<ScaleLine> GetAvailableHours(DateTime date, ScaleLine.Scales scale = ScaleLine.Scales.Fiftheen)
{
List<Reservedhours> _timeLine = AllReservedHours().Where(x => x.Date.Date == date.Date)
.Select( a => new Reservedhours {StartPoint = a.StartPoint, EndPoint = a.EndPoint } )
.ToList();
List<ScaleLine> _available = GetHoursAndScales();
//scale convert:
int _scale = Convert.ToInt32(scale);
foreach (var _item in _timeLine)
{
int index = _available.Where(x => x.Hours == _item.StartPoint)
.SingleOrDefault().Id;
//Convert to datetime
DateTime _opening = DateTime.ParseExact(_item.StartPoint, "HH:mm", System.Globalization.CultureInfo.InvariantCulture);
DateTime _closing = DateTime.ParseExact(_item.EndPoint, "HH:mm", System.Globalization.CultureInfo.InvariantCulture);
//Getting duration time
TimeSpan duration = _closing.Subtract(_opening);
double _duration = duration.TotalMinutes;
//getting coverage
int timeScale = 60 / _scale;
int coverage = Convert.ToInt32(_duration) / timeScale;
//remove unavailable timespots
_available.RemoveRange(index, coverage);
}
return _available;
}
But problem is when the Foreach loop starts it removes the first range correctly based on its index, lets say if _available list has 96 members it removes 8 of them, so second time it should have 88 members and find the index within those 88 members but it doesn't and gets the index wrong (it takes the index as if the list still had 96 members) and so goes for every other action within the loop. how can i fix this issue? is there a way i can get the available list without doing a foreach loop?
The problem is your determination of the index. Instead of asking the list for the index of the desired object, you ask for the property value of an object and using this as index:
int index = _available.Where(x => x.Hours == _item.StartPoint)
.SingleOrDefault()
.Id;
Either you ask really for the index by calling IndexOf():
var matchingItem = _available.Where(x => x.Hours == _item.StartPoint)
.First();
var index = _available.IndexOf(matchingItem);
Or you replace your .RemoveRange() by something else, that really removes your desired elements.

I'm can't access the key and value from a function that returns an IQueryable in winform that i took from a repository class

i'm new in programming and i'm working on a Windows form application. I made a repostory class for all my dataBase work. I made a function in the repository that returns an IQueryable- it returns a key value pair.(key=dayOfWeek and value=revenue). Then when i called this function in the Form, because i wanted the information to be printed on labels, i cannot access the key seperately and the value seperately. It only gives me the option of the the whole key value pair.
this is my code in the repository class:
public class ReportsRepository
{
FruitStoreDataContext db;
public IQueryable RevenuePerDayOfWeek(DateTime startDate, DateTime endDate)
{
db = new FruitStoreDataContext();
var sumPerday = from s in db.OrderDetails
where s.Order.OrderDate >=startDate && s.Order.OrderDate <=endDate
select new
{
day = s.Order.OrderDate.DayOfWeek,
revenue = s.Price * s.Quantity
};
var totalSumPerday = from f in sumPerday
group f.revenue by f.day into g
select new
{
Day= g.Key,
Sum = g.Sum()
};
return totalSumPerday;
}
private void Report1Form_Load(object sender, EventArgs e)
{
ReportsRepository report = new ReportsRepository();
var totalSumPerday = report.RevenuePerDayOfWeek(dateToStart, dateToEnd);
int[]numOfDays = new int[7];
for (DateTime day = dateToStart; day <= dateToEnd; day = day.AddDays(1))
{
dayOfWeek = Convert.ToInt32(day.DayOfWeek);
numOfDays[dayOfWeek]++;
}
Label label;
List<Label> labels = new List<Label>();
int t = 0;
foreach(var totalSum in totalSumPerday)
{
if (numOfDays[dayOfWeek] == 0)
numOfDays[dayOfWeek] = 1;
int y = (38 * t) + 60;
label = new Label();
label.Location = new Point(34, y);
label.Visible = true;
label.Size = new Size(450, 35);
label.BackColor = Color.Gray;
label.ForeColor = Color.White;
label.Font = new Font("Lucida Console", 16);
dayOfWeek = Convert.ToInt16(totalSum.Day.Key);
//on the line below the word 'Day' and 'Sum' are underlined red...it doesn't give me the option to do that. I can only access the whole thing together(key,value)
label.Text = totalSum.Day.ToString() + " : " + (totalSum.Sum / numOfDays[dayOfWeek]).ToString();
labels.Add(label);
panel1.Controls.Add(label);
t++;
}
First, You can do one query to achieve your result. Second, you don't need to return IQueryable as you want in-memory data only (with not further querying). So what you can do is:
public Dictionary<int, decimal> RevenuePerDayOfWeek(DateTime startDate, DateTime endDate)
{
db = new FruitStoreDataContext();
var sumPerday = (from s in db.OrderDetails
where s.Order.OrderDate >= startDate && s.Order.OrderDate <= endDate
group s by s.Order.OrderDate.DayOfWeek into grp
select new
{
Day = grp.Key,
Sum = grp.Sum(a => a.Price * a.Quantity)
}).ToDictionary(x => x.Day, x => x.Sum);
return sumPerday;
}
Usage in Report1Form_Load:
var totalSumPerday = report.RevenuePerDayOfWeek(dateToStart, dateToEnd);
foreach (var totalSum in totalSumPerday)
{
DayOfWeek dayOfWeek = totalSum.Key;
decimal sum = totalSum.Value;
// continue your code
}

Categories

Resources