WorkingProgramService
Documentație pentru serviciul WorkingProgramService
1. Descriere Generală
IWorkingProgramService
este o interfață server-side care definește funcționalitățile pentru gestionarea programului de lucru în aplicațiile FOD. Serviciul permite verificarea orelor de lucru, obținerea orelor de deschidere/închidere și navigarea între zilele lucrătoare.
Implementări disponibile: - EightToFiveWorkingProgramService - Program standard 8:00-17:00, luni-vineri - ConfigurationWorkingProgramService - Program configurabil prin settings - FodWorkingProgramService - Program bazat pe date din baza de date
Caracteristici principale: - Verificare ore de lucru - Determinare program zilnic - Navigare între zile lucrătoare - Suport pentru parametri personalizați - Configurare flexibilă
2. Configurare și Înregistrare
// Program.cs sau Startup.cs (server-side)
// Înregistrare implementare simplă
builder.Services.AddScoped<IWorkingProgramService, EightToFiveWorkingProgramService>();
// Sau înregistrare implementare configurabilă
builder.Services.Configure<FodConfiguration>(configuration);
builder.Services.AddScoped<IWorkingProgramService, ConfigurationWorkingProgramService>();
// Înregistrare multiplă pentru EstimatedDateCalculator
builder.Services.AddScoped<IWorkingProgramService, EightToFiveWorkingProgramService>();
builder.Services.AddScoped<IWorkingProgramService, ConfigurationWorkingProgramService>();
// Configurare în appsettings.json
{
"FodConfiguration": {
"Services": {
"UseEightToFiveWorkingProgramService": true,
"ConfigurationWorkingProgramService": {
"WorkingDays": [
{
"Day": "Monday",
"OpenHours": 8,
"OpenMinutes": 0,
"CloseHours": 17,
"CloseMinutes": 0
},
{
"Day": "Tuesday",
"OpenHours": 8,
"OpenMinutes": 0,
"CloseHours": 17,
"CloseMinutes": 0
},
// ... alte zile
]
}
}
}
}
3. Interfață
public interface IWorkingProgramService
{
bool IsInWorkingHours(DateTime dateTime, IDictionary<string, object>? @params = null);
DateTime? GetCurrentWorkingDayOpening(DateTime dateTime, IDictionary<string, object>? @params = null);
DateTime GetCurrentWorkingDayClosing(DateTime currentDate, IDictionary<string, object>? @params = null);
DateTime GetNextWorkingDayOpening(DateTime currentDate, IDictionary<string, object>? @params = null);
}
4. Metode Disponibile
IsInWorkingHours
Verifică dacă o dată/oră se află în programul de lucru.
Parametri:
- dateTime
(DateTime) - Data și ora de verificat
- @params
(IDictionary
Returnează:
- bool
- True dacă este în ore de lucru, false altfel
GetCurrentWorkingDayOpening
Obține ora de deschidere pentru ziua curentă.
Parametri:
- dateTime
(DateTime) - Data pentru care se caută ora de deschidere
- @params
(IDictionary
Returnează:
- DateTime?
- Ora de deschidere sau null dacă nu este zi lucrătoare
GetCurrentWorkingDayClosing
Obține ora de închidere pentru ziua curentă.
Parametri:
- currentDate
(DateTime) - Data pentru care se caută ora de închidere
- @params
(IDictionary
Returnează:
- DateTime
- Ora de închidere
GetNextWorkingDayOpening
Obține ora de deschidere pentru următoarea zi lucrătoare.
Parametri:
- currentDate
(DateTime) - Data de la care se caută
- @params
(IDictionary
Returnează:
- DateTime
- Ora de deschidere a următoarei zile lucrătoare
5. Implementări Standard
EightToFiveWorkingProgramService
public class BusinessHoursService
{
private readonly IWorkingProgramService _workingProgram;
public BusinessHoursService(IWorkingProgramService workingProgram)
{
_workingProgram = workingProgram;
}
public WorkingHoursInfo GetTodaySchedule()
{
var now = DateTime.Now;
var opening = _workingProgram.GetCurrentWorkingDayOpening(now);
if (!opening.HasValue)
{
return new WorkingHoursInfo
{
IsWorkingDay = false,
Message = "Astăzi este zi nelucrătoare"
};
}
var closing = _workingProgram.GetCurrentWorkingDayClosing(now);
var isOpen = _workingProgram.IsInWorkingHours(now);
return new WorkingHoursInfo
{
IsWorkingDay = true,
OpeningTime = opening.Value,
ClosingTime = closing,
IsCurrentlyOpen = isOpen,
Message = isOpen
? $"Deschis până la {closing:HH:mm}"
: $"Închis. Program: {opening:HH:mm} - {closing:HH:mm}"
};
}
}
ConfigurationWorkingProgramService
public class FlexibleScheduleService
{
private readonly IWorkingProgramService _workingProgram;
private readonly IConfiguration _configuration;
public async Task<ScheduleResponse> GetDepartmentSchedule(
string departmentId)
{
// Parametri pentru departament specific
var parameters = new Dictionary<string, object>
{
["DepartmentId"] = departmentId
};
var today = DateTime.Today;
var schedule = new List<DaySchedule>();
// Obține programul pentru următoarele 7 zile
for (int i = 0; i < 7; i++)
{
var date = today.AddDays(i);
var opening = _workingProgram.GetCurrentWorkingDayOpening(date, parameters);
if (opening.HasValue)
{
var closing = _workingProgram.GetCurrentWorkingDayClosing(date, parameters);
schedule.Add(new DaySchedule
{
Date = date,
DayName = date.ToString("dddd"),
OpeningTime = opening.Value,
ClosingTime = closing,
IsWorkingDay = true
});
}
else
{
schedule.Add(new DaySchedule
{
Date = date,
DayName = date.ToString("dddd"),
IsWorkingDay = false
});
}
}
return new ScheduleResponse
{
DepartmentId = departmentId,
Schedule = schedule
};
}
}
6. Verificare disponibilitate serviciu
public class ServiceAvailabilityChecker
{
private readonly IWorkingProgramService _workingProgram;
private readonly ILogger<ServiceAvailabilityChecker> _logger;
public ServiceAvailabilityResult CheckAvailability(
DateTime requestedTime,
TimeSpan serviceDuration)
{
// Verifică dacă este în ore de lucru
if (!_workingProgram.IsInWorkingHours(requestedTime))
{
var nextOpening = _workingProgram.GetNextWorkingDayOpening(requestedTime);
return new ServiceAvailabilityResult
{
IsAvailable = false,
Reason = "În afara orelor de program",
NextAvailableTime = nextOpening,
Message = $"Serviciul poate fi solicitat începând cu {nextOpening:dddd, dd MMMM, HH:mm}"
};
}
// Verifică dacă serviciul poate fi completat în aceeași zi
var closing = _workingProgram.GetCurrentWorkingDayClosing(requestedTime);
var endTime = requestedTime.Add(serviceDuration);
if (endTime > closing)
{
return new ServiceAvailabilityResult
{
IsAvailable = false,
Reason = "Timp insuficient în ziua curentă",
NextAvailableTime = _workingProgram.GetNextWorkingDayOpening(requestedTime),
Message = $"Serviciul necesită {serviceDuration.TotalHours:F1} ore și nu poate fi finalizat astăzi"
};
}
return new ServiceAvailabilityResult
{
IsAvailable = true,
EstimatedCompletionTime = endTime,
Message = $"Serviciul poate fi finalizat până la {endTime:HH:mm}"
};
}
}
7. Calcul timp rămas de lucru
public class WorkingTimeCalculator
{
private readonly IWorkingProgramService _workingProgram;
public TimeSpan GetRemainingWorkingTime(DateTime fromTime)
{
if (!_workingProgram.IsInWorkingHours(fromTime))
{
return TimeSpan.Zero;
}
var closing = _workingProgram.GetCurrentWorkingDayClosing(fromTime);
var remaining = closing - fromTime;
return remaining > TimeSpan.Zero ? remaining : TimeSpan.Zero;
}
public int GetWorkingDaysBetween(DateTime startDate, DateTime endDate)
{
var workingDays = 0;
var current = startDate.Date;
while (current <= endDate.Date)
{
var opening = _workingProgram.GetCurrentWorkingDayOpening(current);
if (opening.HasValue)
{
workingDays++;
}
current = current.AddDays(1);
}
return workingDays;
}
}
8. Implementare personalizată cu sărbători
public class HolidayAwareWorkingProgramService : IWorkingProgramService
{
private readonly IWorkingProgramService _baseService;
private readonly IHolidayService _holidayService;
public HolidayAwareWorkingProgramService(
IWorkingProgramService baseService,
IHolidayService holidayService)
{
_baseService = baseService;
_holidayService = holidayService;
}
public bool IsInWorkingHours(DateTime dateTime, IDictionary<string, object>? @params = null)
{
// Verifică dacă este sărbătoare
if (_holidayService.IsHoliday(dateTime.Date))
{
return false;
}
return _baseService.IsInWorkingHours(dateTime, @params);
}
public DateTime? GetCurrentWorkingDayOpening(DateTime dateTime, IDictionary<string, object>? @params = null)
{
if (_holidayService.IsHoliday(dateTime.Date))
{
return null;
}
return _baseService.GetCurrentWorkingDayOpening(dateTime, @params);
}
public DateTime GetCurrentWorkingDayClosing(DateTime currentDate, IDictionary<string, object>? @params = null)
{
return _baseService.GetCurrentWorkingDayClosing(currentDate, @params);
}
public DateTime GetNextWorkingDayOpening(DateTime currentDate, IDictionary<string, object>? @params = null)
{
var nextDate = currentDate.AddDays(1);
// Sari peste sărbători
while (_holidayService.IsHoliday(nextDate.Date))
{
nextDate = nextDate.AddDays(1);
}
var opening = _baseService.GetCurrentWorkingDayOpening(nextDate, @params);
if (opening.HasValue)
{
return opening.Value;
}
// Dacă nu e zi lucrătoare, continuă căutarea
return GetNextWorkingDayOpening(nextDate, @params);
}
}
9. Notificări bazate pe program
public class WorkingHoursNotificationService
{
private readonly IWorkingProgramService _workingProgram;
private readonly INotificationService _notifications;
public async Task SendWorkingHoursReminder()
{
var now = DateTime.Now;
var closing = _workingProgram.GetCurrentWorkingDayClosing(now);
var timeUntilClosing = closing - now;
if (timeUntilClosing.TotalMinutes <= 30 && timeUntilClosing.TotalMinutes > 0)
{
await _notifications.SendToAll(new Notification
{
Title = "Program de lucru",
Message = $"Birourile se închid în {timeUntilClosing.TotalMinutes:F0} minute",
Type = NotificationType.Info
});
}
}
public async Task NotifyNextOpeningTime(string userId)
{
var now = DateTime.Now;
if (!_workingProgram.IsInWorkingHours(now))
{
var nextOpening = _workingProgram.GetNextWorkingDayOpening(now);
await _notifications.SendToUser(userId, new Notification
{
Title = "În afara programului",
Message = $"Reveniți {nextOpening:dddd, dd MMMM} la ora {nextOpening:HH:mm}",
Type = NotificationType.Warning
});
}
}
}
10. Dashboard ore de lucru
public class WorkingHoursDashboard
{
private readonly IWorkingProgramService _workingProgram;
public DashboardData GetDashboardData()
{
var now = DateTime.Now;
var data = new DashboardData
{
CurrentTime = now,
IsWorkingHours = _workingProgram.IsInWorkingHours(now)
};
if (data.IsWorkingHours)
{
var closing = _workingProgram.GetCurrentWorkingDayClosing(now);
data.TimeUntilClosing = closing - now;
data.ClosingTime = closing;
data.Status = "Deschis";
data.StatusColor = "green";
}
else
{
var nextOpening = _workingProgram.GetNextWorkingDayOpening(now);
data.TimeUntilOpening = nextOpening - now;
data.NextOpeningTime = nextOpening;
data.Status = "Închis";
data.StatusColor = "red";
}
// Programul săptămânii
data.WeekSchedule = GetWeekSchedule();
return data;
}
private List<DaySchedule> GetWeekSchedule()
{
var schedule = new List<DaySchedule>();
var startOfWeek = DateTime.Today.AddDays(-(int)DateTime.Today.DayOfWeek + 1);
for (int i = 0; i < 7; i++)
{
var date = startOfWeek.AddDays(i);
var opening = _workingProgram.GetCurrentWorkingDayOpening(date);
schedule.Add(new DaySchedule
{
Date = date,
DayName = date.ToString("dddd"),
IsWorkingDay = opening.HasValue,
OpeningTime = opening,
ClosingTime = opening.HasValue
? _workingProgram.GetCurrentWorkingDayClosing(date)
: null
});
}
return schedule;
}
}
11. Testing
[TestClass]
public class WorkingProgramServiceTests
{
private EightToFiveWorkingProgramService _service;
[TestInitialize]
public void Setup()
{
_service = new EightToFiveWorkingProgramService();
}
[TestMethod]
public void IsInWorkingHours_DuringBusinessHours_ReturnsTrue()
{
// Arrange
var tuesday10AM = new DateTime(2024, 6, 25, 10, 0, 0);
// Act
var result = _service.IsInWorkingHours(tuesday10AM);
// Assert
Assert.IsTrue(result);
}
[TestMethod]
public void IsInWorkingHours_Weekend_ReturnsFalse()
{
// Arrange
var saturday = new DateTime(2024, 6, 29, 10, 0, 0);
// Act
var result = _service.IsInWorkingHours(saturday);
// Assert
Assert.IsFalse(result);
}
[TestMethod]
public void GetNextWorkingDayOpening_Friday_ReturnsMonday()
{
// Arrange
var friday = new DateTime(2024, 6, 28, 16, 0, 0);
// Act
var result = _service.GetNextWorkingDayOpening(friday);
// Assert
Assert.AreEqual(DayOfWeek.Monday, result.DayOfWeek);
Assert.AreEqual(8, result.Hour);
}
}
12. Model de configurare
public class ConfigurationWorkingProgramServiceOptions
{
public List<WorkingDay> WorkingDays { get; set; } = new();
}
public class WorkingDay
{
public DayOfWeek Day { get; set; }
public int OpenHours { get; set; }
public int OpenMinutes { get; set; }
public int CloseHours { get; set; }
public int CloseMinutes { get; set; }
}
13. Best Practices
- Timezone awareness: Folosiți UTC pentru aplicații internaționale
- Caching: Cache-uiți rezultatele pentru date fixe (sărbători, program standard)
- Parametri consistenți: Folosiți aceiași parametri pentru calcule legate
- Validare: Validați că ora de închidere > ora de deschidere
- Flexibilitate: Permiteți override prin parametri pentru cazuri speciale
- Logging: Logați deciziile importante pentru debugging
14. Concluzie
IWorkingProgramService
oferă o abstractizare flexibilă pentru gestionarea programului de lucru în aplicațiile FOD. Cu implementări multiple disponibile și suport pentru parametri personalizați, serviciul poate fi adaptat la diverse scenarii de business, de la program standard 8-17 până la programe complexe cu excepții și sărbători.