Minule jsme dokončili pomocnou metodu EnumerateWeeks, která vrací týdny, které bude potřeba kontrolovat, a pro každý tento týden vrací relevantní intervaly mezi koncem směny a začátkem směny následující. Týden má dále ve vlastnosti KontrolovatPouzeOdpocinekDodatecny příznak, který určuje zda se má pro daný týden kontrolovat:
- false - Podmínku pro minimální nepřetržitý odpočinek (24 hod) a podmínku pro nepřetržitý odpočinek (35 hod).
- true – Podmínku pro dodatečný nepřetržitý odpočinek (70 hod).
Navíc pokud je KontrolovatPouzeOdpocinekDodatecny rovno true, kontrola se bude provádět pouze tehdy, pokud kontrola předchozího týdne porušila podmínku pro nepřetržitý odpočinek (35 hod).
V prvním díle jsme si také již připravili prázdnou metodu
private static IEnumerable<PracovniCasyValidationError> KontrolaNepretrzityOdpocinekVTydnu(int IDOsoby, int rok, int mesic)
{
//TODO: Kontrola nepřetržitého odpočinku v týdnu
}
kterou již nyní můžeme dokončit.
Její specifikace bude:
- Načtou se hodnoty parametrů nepretrzityOdpocinek , nepretrzityOdpocinekMin a nepretrzityOdpocinekDodatecny.
- Metoda bude procházet jednotlivé týdny vrácené pomoci EnumerateWeeks a bude si udržovat hodnotu, zda se má aktuálně kontrolovat dodatečný nepřetržitý odpočinek či nikoliv (kontrolovatOdpocinekDodatecny).
- Pro každý týden se zjistí zda:
- A) Délka nejdelšího intervalu týdne je pod hodnotou nepretrzityOdpocinekMin, pod hodnotou nepretrzityOdpocinek (ale nad hodnotou nepretrzityOdpocinekMin) nebo nad hodnotou nepretrzityOdpocinek (pro kontrolovatOdpocinekDodatecny = false)
nebo
- B) Protože výklad zákoníku umožňuje tuto celkovou dobu odpočinku ve 2 týdnech rozdělit (tj. nemusí se jednat pouze o jeden interval mezi koncem a začátkem směny v délce alespoň 70 hod), budeme v tomto případě kontrolovat zda:
Součet délky všech intervalů dlouhých alespoň nepretrzityOdpocinekMin je pod hodnotou nepretrzityOdpocinekDodatecny nebo nad hodnotou nepretrzityOdpocinekDodatecny (pro kontrolovatOdpocinekDodatecny = true)
- Pokud bude při kontrole A nesplněna podmínka pro minimální nepřetržitý odpočinek (24 hod), je to nalezený nesoulad, který bude vrácen (jako PracovniCasyValidationError). Také tím algoritmus končí. (další nesoulady je principiálně zbytečné hledat, protože budou stejně ovlivněné opravou aktuálně nalezeného souladu).
- Pokud bude při kontrole B nesplněna podmínka pro dodatečný nepřetržitý odpočinek (70 hod), opět je to nalezený nesoulad, který bude vrácen (jako PracovniCasyValidationError) a algoritmus bude ukončen.
- U prvního týdne se kontroluje A, u dalších týdnů po předchozí kontrole A se kontroluje A v případě, že byl nejdelší interval předchozího týdne nad hodnotou nepretrzityOdpocinek nebo B v případě, že byl nejdelší interval předchozího týdne pod hodnotou nepretrzityOdpocinek. Po předchozí kontrole B se u dalšího týdne kontroluje A.
- Pokud má kontrolovaný týden příznak KontrolovatPouzeOdpocinekDodatecny roven true, kontroluje se pouze pokud se má aktuálně kontrolovat dle B. Jinak se nastaví, že se má aktuálně kontrolovat A a pokračuje se hned dalším týdnem.
A odpovídající implementace:
private enum NepretrzityOdpocinekResult
{
/// <summary>
/// Nejdelší interval mezi koncem a začátkem následující směny za týden je nad hodnotou nepretrzityOdpocinek
/// </summary>
None,
/// <summary>
/// Nejdelší interval mezi koncem a začátkem následující směny za týden je pod hodnotou nepretrzityOdpocinekMin
/// </summary>
NesplneniNepretrzityOdpocinekMin,
/// <summary>
/// Součet intervalů mezi koncem a začátkem následující směny za týden (které jsou větší nebo rovny nepretrzityOdpocinekMin) je pod hodnotou nepretrzityOdpocinekDodatecny
/// </summary>
NesplneniNepretrzityOdpocinekDodatecny,
/// <summary>
/// Nejdelší interval mezi koncem a začátkem následující směny za týden je pod hodnotou nepretrzityOdpocinek (ale nad hodnotou nepretrzityOdpocinekMin)
/// </summary>
NesplneniNepretrzityOdpocinek
}
private static IEnumerable<PracovniCasyValidationError> KontrolaNepretrzityOdpocinekVTydnu(int IDOsoby, int rok, int mesic)
{
int nepretrzityOdpocinek = Nastaveni.Get().GetInt("NepretrzityOdpocinek", IDOsoby, new DateTime(rok, mesic, 1)); //35
int nepretrzityOdpocinekMin = Nastaveni.Get().GetInt("NepretrzityOdpocinekMin", IDOsoby, new DateTime(rok, mesic, 1)); //24
int nepretrzityOdpocinekDodatecny = Nastaveni.Get().GetInt("NepretrzityOdpocinekDodatecny", IDOsoby, new DateTime(rok, mesic, 1)); //70
//Příznak určující zda jsou všechny intervaly mezi koncem a začátkem následující směny v předešlém týdnu pod hodnotou nepretrzityOdpocinek
bool kontrolovatOdpocinekDodatecny = false;
foreach (var tyden in EnumerateWeeks(IDOsoby, rok, mesic))
{
if (tyden.KontrolovatPouzeOdpocinekDodatecny && !kontrolovatOdpocinekDodatecny) //Kontrolovat pouze v případě předešlého nesplnění nepretrzityOdpocinek
{
kontrolovatOdpocinekDodatecny = false;
continue;
}
var result = NepretrzityOdpocinekResult.None;
if (!kontrolovatOdpocinekDodatecny) //Předešlý týden nebyl žádný interval pod hodnotou nepretrzityOdpocinek
{
foreach (var interval in tyden.Intervaly)
{
if (interval.Delka >= TimeSpan.FromHours(nepretrzityOdpocinek))
{
result = NepretrzityOdpocinekResult.None;
break;
}
if (interval.Delka < TimeSpan.FromHours(nepretrzityOdpocinekMin))
{
if (result == NepretrzityOdpocinekResult.None)
{
result = NepretrzityOdpocinekResult.NesplneniNepretrzityOdpocinekMin;
}
}
else
{
result = NepretrzityOdpocinekResult.NesplneniNepretrzityOdpocinek;
}
}
}
else
{
TimeSpan delka = TimeSpan.Zero;
foreach (var interval in tyden.Intervaly)
{
if (interval.Delka >= TimeSpan.FromHours(nepretrzityOdpocinekMin))
{
delka = delka.Add(interval.Delka);
if (delka >= TimeSpan.FromHours(nepretrzityOdpocinekDodatecny)) //Součet intervalů (které jsou větší nebo rovno nepretrzityOdpocinekMin) není pod hodnotou nepretrzityOdpocinekDodatecny
{
result = NepretrzityOdpocinekResult.None;
break;
}
}
result = NepretrzityOdpocinekResult.NesplneniNepretrzityOdpocinekDodatecny;
}
}
if (result == NepretrzityOdpocinekResult.NesplneniNepretrzityOdpocinekMin)
{
//Chyba, konec procházení kalendáře daného zaměstnance
yield return new PracovniCasyValidationError(PracovniCasyValidationType.NepretrzityOdpocinekVTydnu, tyden.DatumOd, tyden.DatumDo, () =>
string.Format(Properties.Resources.NesplneniNepretrzityOdpocinekMin, tyden.DatumOd, tyden.DatumDo, nepretrzityOdpocinekMin));
yield break;
}
if (result == NepretrzityOdpocinekResult.NesplneniNepretrzityOdpocinekDodatecny)
{
//Chyba, konec procházení kalendáře daného zaměstnance
yield return new PracovniCasyValidationError(PracovniCasyValidationType.NepretrzityOdpocinekVTydnu, tyden.DatumOd, tyden.DatumDo, () =>
string.Format(Properties.Resources.NesplneniNepretrzityOdpocinekDodatecny, tyden.DatumOd, tyden.DatumDo, nepretrzityOdpocinekDodatecny, nepretrzityOdpocinek));
yield break;
}
kontrolovatOdpocinekDodatecny = result == NepretrzityOdpocinekResult.NesplneniNepretrzityOdpocinek;
}
}
Texty popisů nesouladů, které jsou uložené v resource jsou:
NesplneniNepretrzityOdpocinekMin:
V týdnu od {0:d} do {1:d} nesplňuje žádný interval mezi koncem a začátkem následující směny dobu pro minimální nepřetržitý odpočinek ({2} hodin).
NesplneniNepretrzityOdpocinekDodatecny:
V týdnu od {0:d} do {1:d} nesplňuje žádný interval mezi koncem a začátkem následující směny dobu pro nepřetržitý odpočinek ({2} hodin) (žádný interval předchozího týdnu nesplňuje dobu {3} hodin).
Tím je implementace algoritmu dokončena.
Při grafickém zobrazení pracovních směn včetně nesouladů nalezených tímto algoritmem může výsledek vypadat nějak takto (6 případů pro měsíc únor 2013):
Popisy zobrazených nesouladů (v aplikaci jsou zobrazované na tooltip):
1. |
V týdnu od 11. 2. 2013 do 17. 2. 2013 nesplňuje žádný interval mezi koncem a začátkem následující směny dobu pro minimální nepřetržitý odpočinek (24 hodin). |
2. |
V týdnu od 17. 2. 2013 do 23. 2. 2013 nesplňuje žádný interval mezi koncem a začátkem následující směny dobu pro nepřetržitý odpočinek (70 hodin) (žádný interval předchozího týdnu nesplňuje dobu 35 hodin). |
3. |
V týdnu od 25. 1. 2013 do 31. 2. 2013 nesplňuje žádný interval mezi koncem a začátkem následující směny dobu pro minimální nepřetržitý odpočinek (24 hodin). |
4. |
V týdnu od 3. 3. 2013 do 9. 3. 2013 nesplňuje žádný interval mezi koncem a začátkem následující směny dobu pro nepřetržitý odpočinek (70 hodin) (žádný interval předchozího týdnu nesplňuje dobu 35 hodin). |
5. |
V týdnu od 25. 2. 2013 do 3. 3. 2013 nesplňuje žádný interval mezi koncem a začátkem následující směny dobu pro minimální nepřetržitý odpočinek (24 hodin). |
6. |
V týdnu od 29. 1. 2013 do 4. 2. 2013 nesplňuje žádný interval mezi koncem a začátkem následující směny dobu pro minimální nepřetržitý odpočinek (24 hodin). |