diff --git a/Controllers/PdfController.cs b/Controllers/PdfController.cs new file mode 100644 index 0000000..bfb24fa --- /dev/null +++ b/Controllers/PdfController.cs @@ -0,0 +1,253 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using FirmTracker_Server.nHibernate; +using FirmTracker_Server.nHibernate.Expenses; +using FirmTracker_Server.nHibernate.Transactions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using QuestPDF.Fluent; +using QuestPDF.Helpers; +using QuestPDF.Infrastructure; + +namespace FirmTracker_Server.Controllers +{ + [Route("api/[controller]")] + [ApiController] + [Authorize] + public class PdfController : ControllerBase + { + private readonly IExpenseRepository _expenseRepository; + private readonly ITransactionRepository _transactionRepository; + + public PdfController(IExpenseRepository expenseRepository, ITransactionRepository transactionRepository) + { + _expenseRepository = expenseRepository; + _transactionRepository = transactionRepository; + } + + [HttpGet("download")] + [Authorize(Roles = Roles.Admin)] + public IActionResult DownloadReport( + [FromQuery] string reportType, // "expenses" or "transactions" + [FromQuery] DateTime? startDate, + [FromQuery] DateTime? endDate) + { + try + { + // Validate date inputs and set default values + DateTime start = startDate ?? DateTime.MinValue; + DateTime end = endDate ?? DateTime.MaxValue; + + // Validate report type + if (string.IsNullOrEmpty(reportType) || + (reportType.ToLower() != "expenses" && reportType.ToLower() != "transactions")) + { + return BadRequest("Invalid report type. Please specify 'expenses' or 'transactions'."); + } + + if (reportType.ToLower() == "expenses") + { + return GenerateExpenseReport(start, end); + } + else + { + return GenerateTransactionReport(start, end); + } + } + catch (Exception ex) + { + return StatusCode(500, $"Internal server error: {ex.Message}"); + } + } + + private IActionResult GenerateExpenseReport(DateTime start, DateTime end) + { + var expenses = _expenseRepository.GetAllExpenses() + .Where(e => e.Date >= start && e.Date <= end) + .ToList(); + + if (!expenses.Any()) + { + return BadRequest($"No expenses found between {start:yyyy-MM-dd} and {end:yyyy-MM-dd}."); + } + + var pdfBytes = GenerateExpensePdf(expenses, start, end); + string fileName = $"ExpenseReport_{start:yyyy-MM-dd}_to_{end:yyyy-MM-dd}.pdf"; + return File(pdfBytes, "application/pdf", fileName); + } + + private IActionResult GenerateTransactionReport(DateTime start, DateTime end) + { + var transactions = _transactionRepository.GetTransactionsByDateRange(start, end); + + if (!transactions.Any()) + { + return BadRequest($"No transactions found between {start:yyyy-MM-dd} and {end:yyyy-MM-dd}."); + } + + // Fetch transaction products for all transactions in one query + var transactionIds = transactions.Select(t => t.Id).ToList(); + var transactionProducts = _transactionRepository.GetTransactionProductsForTransactions(transactionIds); + + var pdfBytes = GenerateTransactionPdf(transactions, transactionProducts, start, end); + string fileName = $"TransactionReport_{start:yyyy-MM-dd}_to_{end:yyyy-MM-dd}.pdf"; + return File(pdfBytes, "application/pdf", fileName); + } + + private byte[] GenerateTransactionPdf(List transactions, List transactionProducts, DateTime startDate, DateTime endDate) + { + using (var ms = new MemoryStream()) + { + Document.Create(container => + { + container.Page(page => + { + page.Size(PageSizes.A4); + page.Margin(2, Unit.Centimetre); + page.PageColor(Colors.White); + page.DefaultTextStyle(x => x.FontSize(12)); + + // Main header + page.Header() + .Text("Raport transakcji") + .FontSize(20) + .SemiBold() + .AlignCenter(); + + // Summary section + page.Content().PaddingVertical(1, Unit.Centimetre).Column(column => + { + column.Spacing(10); + + column.Item().Text($"Transakcje od ({startDate:yyyy-MM-dd} do {endDate:yyyy-MM-dd})") + .FontSize(16).Underline(); + + // Add table header + column.Item().Row(row => + { + row.RelativeItem().Text("Data").SemiBold(); + row.RelativeItem().Text("Typ płatności").SemiBold(); + row.RelativeItem().Text("Kwota razem").SemiBold(); + row.RelativeItem().Text("Rabat").SemiBold(); + row.RelativeItem().Text("Opis").SemiBold(); + }); + + // Populate table rows with transaction data + foreach (var transaction in transactions) + { + column.Item().Row(row => + { + row.RelativeItem().Text(transaction.Date.ToString("yyyy-MM-dd")); + row.RelativeItem().Text(transaction.PaymentType); + row.RelativeItem().Text(transaction.TotalPrice.ToString("C")); + row.RelativeItem().Text(transaction.Discount.ToString("C")); + row.RelativeItem().Text(transaction.Description); + }); + + // Fetch and display transaction products for this transaction + var products = transactionProducts + .Where(tp => tp.TransactionId == transaction.Id) + .ToList(); + + if (products.Any()) + { + column.Item().Text("Produkty:").SemiBold(); + foreach (var product in products) + { + column.Item().Row(productRow => + { + productRow.RelativeItem().Text($"Nazwa produktu: {product.ProductName}"); + productRow.RelativeItem().Text($"Ilość: {product.Quantity}"); + }); + } + } + } + }); + + // Footer with generation date + page.Footer() + .AlignCenter() + .Text(text => + { + text.Span("Wygenerowano przez automat FT: "); + text.Span(DateTime.Now.ToString("yyyy-MM-dd")).SemiBold(); + }); + }); + }).GeneratePdf(ms); + + return ms.ToArray(); + } + } + + private byte[] GenerateExpensePdf(List expenses, DateTime startDate, DateTime endDate) + { + using (var ms = new MemoryStream()) + { + decimal totalExpenses = expenses.Sum(e => e.Value); + decimal averageExpense = expenses.Any() ? totalExpenses / expenses.Count : 0; + + Document.Create(container => + { + container.Page(page => + { + page.Size(PageSizes.A4); + page.Margin(2, Unit.Centimetre); + page.PageColor(Colors.White); + page.DefaultTextStyle(x => x.FontSize(12)); + + // Main header + page.Header() + .Text("Raport wydatków") + .FontSize(20) + .SemiBold() + .AlignCenter(); + + // Summary section + page.Content().PaddingVertical(1, Unit.Centimetre).Column(column => + { + column.Spacing(10); + + column.Item().Row(row => + { + row.RelativeItem().Text($"Łączne wydatki: {totalExpenses:C}").FontSize(14).Bold(); + row.RelativeItem().Text($"Średnie wydatki dzienne: {averageExpense:C}").FontSize(14).Bold(); + }); + + column.Item().Text($"Szczegóły wydatków od ({startDate:yyyy-MM-dd} do {endDate:yyyy-MM-dd})") + .FontSize(16).Underline(); + + column.Item().Row(row => + { + row.RelativeItem().Text("Data").SemiBold(); + row.RelativeItem().Text("Kwota").SemiBold(); + row.RelativeItem().Text("Opis").SemiBold(); + }); + + foreach (var expense in expenses) + { + column.Item().Row(row => + { + row.RelativeItem().Text(expense.Date.ToString("yyyy-MM-dd")); + row.RelativeItem().Text(expense.Value.ToString("C")); + row.RelativeItem().Text(expense.Description); + }); + } + }); + + page.Footer() + .AlignCenter() + .Text(text => + { + text.Span("Wygenerowano przez automat FT: "); + text.Span(DateTime.Now.ToString("yyyy-MM-dd")).SemiBold(); + }); + }); + }).GeneratePdf(ms); + + return ms.ToArray(); + } + } + } +} diff --git a/Controllers/TransactionController.cs b/Controllers/TransactionController.cs index 276a50a..fbe2110 100644 --- a/Controllers/TransactionController.cs +++ b/Controllers/TransactionController.cs @@ -55,14 +55,21 @@ namespace FirmTracker_Server.Controllers { try { - + foreach (var product in transaction.TransactionProducts) { + // Validate if the product quantity is positive + if (product.Quantity <= 0) + { + return BadRequest($"Ilość na produktu {product.ProductName} musi być dodatnia."); + } var productByName = _productCRUD.GetProductByName(product.ProductName); if (productByName == null) { throw new InvalidOperationException($"Produkt o nazwie {product.ProductName} nie istnieje."); } + + product.ProductID = productByName.Id; product.TransactionId = transaction.Id; @@ -132,6 +139,11 @@ namespace FirmTracker_Server.Controllers { foreach (var product in transaction.TransactionProducts) { + // Validate if the product quantity is positive + if (product.Quantity <= 0) + { + return BadRequest($"Sprzedawana ilość produktu {product.ProductName} musi być ilością dodatnią."); + } var productByName = _productCRUD.GetProductByName(product.ProductName); if (productByName == null) { @@ -207,5 +219,27 @@ namespace FirmTracker_Server.Controllers return Ok(transactions); } + // DELETE: api/Transaction/5/product/10 + [HttpDelete("{transactionId}/product/{productId}")] + [ProducesResponseType(StatusCodes.Status204NoContent)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [Authorize(Roles = Roles.Admin + "," + Roles.User)] + public IActionResult DeleteTransactionProduct(int transactionId, int productId) + { + try + { + _transactionCRUD.DeleteTransactionProduct(transactionId, productId); + return NoContent(); // Successfully removed the product + } + catch (InvalidOperationException ioe) + { + return BadRequest(ioe.Message); // If the transaction or product isn't found + } + catch (Exception ex) + { + return NotFound(ex.Message); // Other general errors + } + } } } diff --git a/Controllers/WorkDayController.cs b/Controllers/WorkDayController.cs new file mode 100644 index 0000000..844d115 --- /dev/null +++ b/Controllers/WorkDayController.cs @@ -0,0 +1,95 @@ +/* + * This file is part of FirmTracker - Server. + * + * FirmTracker - Server is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * FirmTracker - Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FirmTracker - Server. If not, see . + */ + +using FirmTracker_Server.nHibernate; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System; +using System.Security.Claims; + +namespace FirmTracker_Server.Controllers +{ + [Route("api/[controller]")] + [ApiController] + [Authorize] + public class WorkdayController : ControllerBase + { + private readonly WorkdayRepository _workdayCRUD; + + public WorkdayController() + { + _workdayCRUD = new WorkdayRepository(); + } + + // Endpoint to start a workday + [HttpPost("start")] + [Authorize(Roles = Roles.Admin + "," + Roles.User)] + public IActionResult StartWorkday() + { + try + { + var userIdString = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; + int userId = int.Parse(userIdString); + + // Attempt to start a new workday + _workdayCRUD.StartWorkday(userId); + return Ok(new { status = "started", userId }); + } + catch (Exception ex) + { + // If there's an error (like previous workday not stopped), handle it + return BadRequest(new { message = "An error occurred while starting the workday.", error = ex.Message }); + } + } + // Endpoint to stop a workday + [HttpPost("stop")] + [Authorize(Roles = Roles.Admin + "," + Roles.User)] + public IActionResult StopWorkday() + { + try + { + var userIdString = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; + int userId = int.Parse(userIdString); + + var result = _workdayCRUD.StopWorkday(userId); + return Ok(new { status = result ? "stopped" : "already stopped", userId }); + } + catch (Exception ex) + { + return BadRequest(new { message = "An error occurred while stopping the workday.", error = ex.Message }); + } + } + + // Endpoint to get all workdays for a user + [HttpGet("user/{userMail}/workdays")] + [Authorize(Roles = Roles.Admin + "," + Roles.User)] + public IActionResult GetWorkdays(string userMail) + { + try + { + var workdays = _workdayCRUD.GetWorkdaysByUser(userMail); + return Ok(workdays); + } + catch (Exception ex) + { + return BadRequest(new { message = "An error occurred while fetching workdays.", error = ex.Message }); + } + } + + + } +} diff --git a/Entities/User.cs b/Entities/User.cs index 1282962..6e2619c 100644 --- a/Entities/User.cs +++ b/Entities/User.cs @@ -2,7 +2,7 @@ { public class User { - public virtual int Id { get; set; } + public virtual int UserId { get; set; } public virtual string Login { get; set; } public virtual string Email { get; set; } public virtual string Role { get; set; } = "User"; diff --git a/FirmTracker-Server.csproj b/FirmTracker-Server.csproj index 91768c3..05849f9 100644 --- a/FirmTracker-Server.csproj +++ b/FirmTracker-Server.csproj @@ -29,6 +29,7 @@ + diff --git a/Models/EmployeeDto.cs b/Models/EmployeeDto.cs new file mode 100644 index 0000000..7a30e5b --- /dev/null +++ b/Models/EmployeeDto.cs @@ -0,0 +1,11 @@ +using FirmTracker_Server.Controllers; + +namespace FirmTracker_Server.Models +{ + public class EmployeeDto + { + public virtual int Id { get; set; } + public virtual string email { get; set; } + + } +} diff --git a/Models/Workday.cs b/Models/Workday.cs new file mode 100644 index 0000000..31313b8 --- /dev/null +++ b/Models/Workday.cs @@ -0,0 +1,15 @@ +using FirmTracker_Server.Entities; +using System; + +namespace YourNamespace.Models +{ + public class Workday + { + public virtual int Id { get; set; } + public virtual DateTime? StartTime { get; set; } + public virtual DateTime? EndTime { get; set; } + public TimeSpan WorkedHours { get; set; } + // Many-to-One relationship to the User entity + public virtual User User { get; set; } + } +} diff --git a/Program.cs b/Program.cs index 2c10c9c..4a73767 100644 --- a/Program.cs +++ b/Program.cs @@ -37,6 +37,8 @@ using FirmTracker_Server.Middleware; using FirmTracker_Server.Services; using System.Reflection; using FirmTracker_Server.Mappings; +using NuGet.Packaging; + namespace FirmTracker_Server @@ -69,7 +71,8 @@ namespace FirmTracker_Server TestClass test = new TestClass(); test.AddTestProduct(); - + QuestPDF.Settings.License = QuestPDF.Infrastructure.LicenseType.Community; + builder.Services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", @@ -173,6 +176,9 @@ namespace FirmTracker_Server services.AddScoped(); services.AddScoped(); services.AddScoped, PasswordHasher>(); + services.AddScoped(); + services.AddScoped(); + // services.AddScoped(); services.AddMvc(); } diff --git a/Services/UserService.cs b/Services/UserService.cs index 4b5cc53..a846b49 100644 --- a/Services/UserService.cs +++ b/Services/UserService.cs @@ -69,7 +69,7 @@ namespace FirmTracker_Server.Services { session.Save(user); transaction.Commit(); - return user.Id; + return user.UserId; } catch { @@ -128,7 +128,7 @@ namespace FirmTracker_Server.Services // Generate JWT token var claims = new List() { - new(ClaimTypes.NameIdentifier, user.Id.ToString()), + new(ClaimTypes.NameIdentifier, user.UserId.ToString()), new(ClaimTypes.Role, user.Role) }; diff --git a/TestClass.cs b/TestClass.cs index 02cd16a..9797a9d 100644 --- a/TestClass.cs +++ b/TestClass.cs @@ -157,7 +157,7 @@ namespace FirmTracker_Server }; var expense2 = new Expense { - Date = DateTime.Now, + Date = DateTime.Parse("2024-09-10 16:11:17.6232408"), Value = 990.99m, Description = "naprawa pieca - 25.05.2024" }; @@ -239,9 +239,9 @@ namespace FirmTracker_Server expenseCrud.AddExpense(expense3); List testTransactionProducts = new List { - new TransactionProduct { ProductID =17, Quantity = 10 }, + new TransactionProduct { ProductID =17, Quantity = 3 }, new TransactionProduct { ProductID = 14, Quantity = 1 }, - new TransactionProduct { ProductID = 1, Quantity = 0 }, + new TransactionProduct { ProductID = 1, Quantity = 1 }, }; foreach (var transactionProduct in testTransactionProducts) { diff --git a/nHIbernate/PdfData.cs b/nHIbernate/PdfData.cs new file mode 100644 index 0000000..5792f4b --- /dev/null +++ b/nHIbernate/PdfData.cs @@ -0,0 +1,220 @@ +using System.Collections.Generic; +using System.Linq; +using FirmTracker_Server.nHibernate.Expenses; +using FirmTracker_Server.nHibernate.Transactions; +using NHibernate; + +namespace FirmTracker_Server.nHibernate +{ + public interface IExpenseRepository + { + List GetAllExpenses(); + Expense GetExpense(int expenseId); + void AddExpense(Expense expense); + void UpdateExpense(Expense expense); + void DeleteExpense(int expenseId); + } + public interface ITransactionRepository + { + List GetAllTransactions(); + Transaction GetTransaction(int transactionId); + List GetTransactionsByDateRange(DateTime startDate, DateTime endDate); + List GetTransactionProducts(int transactionId); + void AddTransaction(Transaction transaction); + void UpdateTransaction(Transaction transaction); + void DeleteTransaction(int transactionId); + List GetTransactionProductsForTransactions(List transactionIds); + } + public class TransactionRepository : ITransactionRepository + { + // Retrieve all transactions + public List GetAllTransactions() + { + using (var session = SessionFactory.OpenSession()) + { + return session.Query().ToList(); + } + } + public List GetTransactionProductsForTransactions(List transactionIds) + { + using (var session = SessionFactory.OpenSession()) + { + return session.Query() + .Where(tp => transactionIds.Contains(tp.TransactionId)) + .ToList(); + } + } + + public Transaction GetTransaction(int transactionId) + { + using (var session = SessionFactory.OpenSession()) + { + return session.Get(transactionId); + } + } + + + public List GetTransactionsByDateRange(DateTime startDate, DateTime endDate) + { + using (var session = SessionFactory.OpenSession()) + { + return session.Query() + .Where(t => t.Date >= startDate && t.Date <= endDate) + .ToList(); + } + } + + + public List GetTransactionProducts(int transactionId) + { + using (var session = SessionFactory.OpenSession()) + { + return session.Query() + .Where(tp => tp.TransactionId == transactionId) + .ToList(); + } + } + + + public void AddTransaction(Transaction transaction) + { + using (var session = SessionFactory.OpenSession()) + using (var transactionScope = session.BeginTransaction()) + { + try + { + session.Save(transaction); + transactionScope.Commit(); + } + catch + { + transactionScope.Rollback(); + throw; + } + } + } + + // Update an existing transaction + public void UpdateTransaction(Transaction transaction) + { + using (var session = SessionFactory.OpenSession()) + using (var transactionScope = session.BeginTransaction()) + { + try + { + session.Update(transaction); + transactionScope.Commit(); + } + catch + { + transactionScope.Rollback(); + throw; + } + } + } + + + public void DeleteTransaction(int transactionId) + { + using (var session = SessionFactory.OpenSession()) + using (var transactionScope = session.BeginTransaction()) + { + try + { + var transaction = session.Get(transactionId); + if (transaction != null) + { + session.Delete(transaction); + } + transactionScope.Commit(); + } + catch + { + transactionScope.Rollback(); + throw; + } + } + } + } + public class ExpenseRepository : IExpenseRepository + { + // Retrieve all expenses + public List GetAllExpenses() + { + using (var session = SessionFactory.OpenSession()) + { + return session.Query().ToList(); + } + } + + // Retrieve a specific expense by ID + public Expense GetExpense(int expenseId) + { + using (var session = SessionFactory.OpenSession()) + { + return session.Get(expenseId); + } + } + + // Add a new expense + public void AddExpense(Expense expense) + { + using (var session = SessionFactory.OpenSession()) + using (var transaction = session.BeginTransaction()) + { + try + { + session.Save(expense); + transaction.Commit(); + } + catch + { + transaction.Rollback(); + throw; + } + } + } + + // Update an existing expense + public void UpdateExpense(Expense expense) + { + using (var session = SessionFactory.OpenSession()) + using (var transaction = session.BeginTransaction()) + { + try + { + session.Update(expense); + transaction.Commit(); + } + catch + { + transaction.Rollback(); + throw; + } + } + } + + // Delete an expense by ID + public void DeleteExpense(int expenseId) + { + using (var session = SessionFactory.OpenSession()) + using (var transaction = session.BeginTransaction()) + { + try + { + var expense = session.Get(expenseId); + if (expense != null) + { + session.Delete(expense); + } + transaction.Commit(); + } + catch + { + transaction.Rollback(); + throw; + } + } + } + } +} diff --git a/nHIbernate/SessionFactory.cs b/nHIbernate/SessionFactory.cs index 6218b36..1a3deff 100644 --- a/nHIbernate/SessionFactory.cs +++ b/nHIbernate/SessionFactory.cs @@ -55,7 +55,8 @@ namespace FirmTracker_Server.nHibernate .AddFromAssemblyOf() .AddFromAssemblyOf() .AddFromAssemblyOf() - .AddFromAssemblyOf(); + .AddFromAssemblyOf() + .AddFromAssemblyOf(); }) .ExposeConfiguration(cfg => new SchemaExport(cfg).Create(true, true)) //SchemaUpdate . Execute dla only update diff --git a/nHIbernate/UserMapping.cs b/nHIbernate/UserMapping.cs index 44c7e7b..8f1487e 100644 --- a/nHIbernate/UserMapping.cs +++ b/nHIbernate/UserMapping.cs @@ -7,7 +7,7 @@ public class UserMapping : ClassMap { Table("Users"); // The name of your table in the database - Id(x => x.Id); // Mapping the Id property + Id(x => x.UserId); // Mapping the Id property Map(x => x.Email); // Mapping other properties Map(x => x.PassHash); Map(x => x.Role); diff --git a/nHIbernate/Workday.cs b/nHIbernate/Workday.cs new file mode 100644 index 0000000..ac85b39 --- /dev/null +++ b/nHIbernate/Workday.cs @@ -0,0 +1,24 @@ +using FirmTracker_Server.Entities; + +namespace FirmTracker_Server.nHibernate +{ + public class Workday + { + public virtual int Id { get; set; } + public virtual DateTime StartTime { get; set; } + public virtual DateTime? EndTime { get; set; } // Nullable EndTime, if not finished + public virtual TimeSpan WorkedHours + { + get + { + // Calculate the worked hours, using 5 PM as the fallback for the EndTime + return (EndTime ?? DateTime.Today.AddHours(24)) - StartTime; + } + set + { + + } + } + public virtual User User { get; set; } + } +} diff --git a/nHIbernate/WorkdayMapping.cs b/nHIbernate/WorkdayMapping.cs new file mode 100644 index 0000000..5cdbfde --- /dev/null +++ b/nHIbernate/WorkdayMapping.cs @@ -0,0 +1,15 @@ +using FluentNHibernate.Mapping; +namespace FirmTracker_Server.nHibernate +{ + public class WorkdayMapping : ClassMap + { + public WorkdayMapping() + { + Table("Workdays"); // Make sure the table name matches the one in the database + Id(x => x.Id).GeneratedBy.Identity(); + Map(x => x.StartTime); + Map(x => x.EndTime); + References(x => x.User).Column("UserId"); // Assuming Workday is related to a User + } + } +} diff --git a/nHIbernate/WorkdayRepository.cs b/nHIbernate/WorkdayRepository.cs new file mode 100644 index 0000000..eff5486 --- /dev/null +++ b/nHIbernate/WorkdayRepository.cs @@ -0,0 +1,104 @@ +using FirmTracker_Server.Entities; +using FirmTracker_Server.nHibernate; + +public class WorkdayRepository +{ + public void StartWorkday(int userId) + { + using (var session = SessionFactory.OpenSession()) + using (var transaction = session.BeginTransaction()) + { + try + { + // Check if there is an existing workday that hasn't been stopped yet + var ongoingWorkday = session.Query() + .Where(w => w.User.UserId == userId && w.EndTime == null) + .OrderByDescending(w => w.StartTime) + .FirstOrDefault(); + + if (ongoingWorkday != null) + { + // If there is an ongoing workday, throw an exception or return a specific message + throw new Exception("Previous workday wasn't stopped yet."); + } + + // Fetch the user entity + var user = session.Get(userId); + if (user == null) throw new Exception("User not found"); + + // Create a new workday if there is no ongoing one + var workday = new Workday + { + StartTime = DateTime.Now, + User = user + }; + + session.Save(workday); + transaction.Commit(); + } + catch (Exception ex) + { + transaction.Rollback(); + throw new Exception("An error occurred while starting the workday", ex); + } + } + } + + public bool StopWorkday(int userId) + { + using (var session = SessionFactory.OpenSession()) + using (var transaction = session.BeginTransaction()) + { + try + { + var workday = session.Query() + .Where(w => w.User.UserId == userId && w.EndTime == null) + .OrderByDescending(w => w.StartTime) + .FirstOrDefault(); + + if (workday == null) + { + return false; // No ongoing workday found + } + + workday.EndTime = DateTime.Now; + + session.Update(workday); + transaction.Commit(); + + return true; + } + catch (Exception ex) + { + transaction.Rollback(); + throw new Exception("An error occurred while stopping the workday", ex); + } + } + } + + public List GetWorkdaysByUser(string email) + { + using (var session = SessionFactory.OpenSession()) + { + try + { + var workdays = session.Query() + .Where(w => w.User.Email == email) + .Select(w => new Workday + { + Id = w.Id, + StartTime = w.StartTime, + EndTime = w.EndTime ?? DateTime.Today.AddHours(17), + WorkedHours = (w.EndTime ?? DateTime.Today.AddHours(17)) - w.StartTime, + }) + .ToList(); + + return workdays; + } + catch (Exception ex) + { + throw new Exception("An error occurred while fetching workdays", ex); + } + } + } +} diff --git a/nHibernate/Transactions/TransactionCRUD.cs b/nHibernate/Transactions/TransactionCRUD.cs index c16d574..9be2044 100644 --- a/nHibernate/Transactions/TransactionCRUD.cs +++ b/nHibernate/Transactions/TransactionCRUD.cs @@ -94,6 +94,10 @@ namespace FirmTracker_Server.nHibernate.Transactions { var product = session.Get(tp.ProductID); + if(tp.Quantity < 0) + { + + } if (product.Type != 0) { product.Availability += tp.Quantity; @@ -253,6 +257,63 @@ namespace FirmTracker_Server.nHibernate.Transactions } } } + public void DeleteTransactionProduct(int transactionId, int productId) + { + using (var session = SessionFactory.OpenSession()) + using (var t = session.BeginTransaction()) + { + try + { + // Get the transaction to update + var transaction = session.Get(transactionId); + if (transaction == null) + { + throw new InvalidOperationException($"Transaction with ID {transactionId} not found."); + } + + // Find the transaction product to remove + var transactionProduct = transaction.TransactionProducts.FirstOrDefault(tp => tp.ProductID == productId); + if (transactionProduct == null) + { + throw new InvalidOperationException($"Product with ID {productId} not found in the transaction."); + } + + // Get the product to update availability + var product = session.Get(productId); + if (product == null) + { + throw new InvalidOperationException($"Product with ID {productId} not found."); + } + + // Revert the product availability + if (product.Type != 0) + { + product.Availability += transactionProduct.Quantity; + session.Update(product); + } + + // Remove the product from the transaction + transaction.TotalPrice -= (transactionProduct.Quantity * product.Price * (1 - (transaction.Discount / 100))); + transaction.TotalPrice = Math.Round(transaction.TotalPrice, 2, MidpointRounding.AwayFromZero); + + // Remove the product from the Transaction's Product list + transaction.TransactionProducts.Remove(transactionProduct); + + // Now delete the transaction product + session.Delete(transactionProduct); + + // Update the transaction total price + session.Update(transaction); + + t.Commit(); + } + catch (Exception ex) + { + t.Rollback(); + throw new InvalidOperationException($"Error while deleting product from transaction: {ex.Message}"); + } + } + } public IList GetAllTransactions()