Algoritmul de Distribuire a Suprasolicitarii

Aceasta pagina descrie in detaliu algoritmul executat de actiunea Acceptare diferente pentru un task ramas in starea EROARE (Q_DISPONIBIL < Q_NECESAR), implementat in WlpServiceImpl.distribuieActivitateOverload(). Acelasi algoritm are si o varianta punctuala, per resursa, descrisa la finalul paginii.

Context

Un task in eroare nu are nicio planificare generata — Pasul 8 al algoritmului de evaluare este sarit pentru taskurile cu eroare blocanta. La Acceptare diferente, sistemul trebuie sa genereze planificarea completa a taskului, tratand distinct:

  • orele care se incadreaza in capacitatea normal disponibila a fiecarei persoane;

  • orele care depasesc aceasta capacitate (suprasolicitare/overload), pe care organizatia le accepta explicit.

Faza 1 — alocarea orelor acoperite normal

Se aplica exact algoritmul standard de alocare (aplicaAlocarePlanificariPeTask, aceeasi logica ca la Pasul 8 al evaluarii, sarita initial din cauza erorii): pentru fiecare persoana din WLP_CA_DETALIU, in ordinea descrescatoare a capacitatii ei disponibile in perioada, se aloca ore pe zilele calendarului ei (WLP_CALENDAR) pana la limita capacitatii nete a zilei respective, fara a depasi niciodata necesarul cumulat al taskului. Rezultatul se scrie in WLP_CA_PLANIFICARI cu STARE = 0 (alocare normala), iar cantitatile agregate (Q_ALOCAT) se resincronizeaza pe WLP_CA_DETALIU si WLP_CA.

Daca la finalul Fazei 1 tot necesarul este acoperit, algoritmul se opreste aici (Faza 2 nu se mai executa).

Faza 2 — distribuirea suprasolicitarii

Se calculeaza diferenta ramasa neacoperita:

ore_neacoperite = Q_NECESAR (task) - Q_DISPONIBIL (task)

Se identifica toate zilele lucratoare din perioada taskului, pentru toate persoanele din detalierea lui (WLP_CA_DETALIU join WLP_CALENDAR unde IS_WORKING_DAY = 1), si se calculeaza numarul lor total: total_zile_lucratoare.

Se calculeaza indicele de suprasolicitare per zi:

indice_brut = ore_neacoperite / total_zile_lucratoare

Regulile de rotunjire si distributie sunt urmatoarele:

Conditie

Comportament

indice_brut >= 2

Se rotunjeste la cel mai apropiat numar intreg (partea fractionara >= 0.5 rotunjeste in sus, altfel in jos): increment_pe_zi = ROUND(indice_brut). Se aplica tuturor zilelor lucratoare gasite, cate increment_pe_zi ore fiecare, cu exceptia ultimei zile procesate, careia i se atribuie exact restul necesar pentru ca suma totala sa fie egala cu ore_neacoperite (corectie de rotunjire).

indice_brut < 2 si ore_neacoperite <= 2

Se aloca intreaga cantitate (ore_neacoperite) intr-o singura zi.

indice_brut < 2 si ore_neacoperite > 2

Se forteaza un minim de 2 ore/zi, aplicat pe un numar de zile determinat prin CEIL(ore_neacoperite / 2), oprindu-se imediat ce suma orelor alocate atinge ore_neacoperite (ultima zi este corectata similar, la restul exact).

Fiecare ora de suprasolicitare astfel calculata se insereaza ca un rand nou si distinct in WLP_CA_PLANIFICARI, cu STARE = 3, separat de eventualele randuri normale (STARE = 0) generate in Faza 1 pentru aceeasi zi si aceeasi persoana.

Important

Suma tuturor randurilor cu STARE = 3 generate in Faza 2 este exact egala cu ore_neacoperite — corectia aplicata pe ultima zi garanteaza acest lucru, indiferent de rotunjirile intermediare.

Exemplu real, verificat

Urmatorul exemplu a fost rulat si verificat direct in sistem (task MAI_01G, ID_WLP_CA = 2400, persoana cu marca 66, meserie TRAGERE):

Marime

Valoare

Q_NECESAR (task)

80 ore

Q_DISPONIBIL (task), inainte de acceptare

0 ore (persoana nu avea deloc capacitate libera in perioada)

total_zile_lucratoare

15 zile

ore_neacoperite

80 - 0 = 80 ore

indice_brut

80 / 15 = 5.333…

increment_pe_zi (rotunjit)

5 ore/zi (0.333 < 0.5, rotunjire in jos)

Rezultatul Fazei 1: niciun rand normal alocat (persoana nu avea nicio ora libera).

Rezultatul Fazei 2, verificat in WLP_CA_PLANIFICARI:

Zile

Alocare

14 zile

5 ore/zi fiecare = 70 ore

a 15-a zi (ultima)

corectata la 10 ore (restul exact, nu 5), astfel incat totalul sa fie 80 ore

Total verificat: 14 × 5 + 10 = 80 ore, exact cat ore_neacoperite, distribuit in 15 randuri distincte cu STARE = 3.

Dupa aplicarea celor doua faze, WLP_CA.STARE a taskului a trecut din EROARE in VALIDATA.

Varianta punctuala — distribuirea overload-ului pe o singura resursa

Actiunea Planificare resursa cu overload (WlpCaDetaliuServiceImpl.planificaResursa cu tipPlanificare = 2) executa acelasi algoritm in doua faze, dar restrans la un singur rand WLP_CA_DETALIU:

  • Faza 1 (alocaDisponibilPeResursa) aloca disponibilul persoanei selectate, limitat la necesarul ramas al taskului (Q_NECESAR - Q_ALOCAT), pentru ca apelurile repetate pe persoane diferite sa nu supra-aloce;

  • Faza 2 (distribuieOverloadPeResursa) distribuie cantitatea de ore de overload introdusa de utilizator in dialog (nu neaparat intreaga diferenta — serverul o plafoneaza la Q_NECESAR - Q_DISPONIBIL al persoanei), doar peste zilele lucratoare ale persoanei selectate, tot ca randuri WLP_CA_PLANIFICARI cu STARE = 3. Intern, ambele fluxuri folosesc aceeasi metoda distribuieOverloadPeTask, cu un filtru optional pe ID_WLP_CA_DETALIU (null = toate resursele, fluxul de la Acceptare diferente).

Dupa distribuire, taskul este reevaluat soft (reevaluareTaskFaraStergereAlocari): se sterg doar mesajele din WLP_CA_LOG, se recalculeaza disponibilul pe toate resursele taskului peste alocarile existente (orele STARE = 3 conteaza ca acoperire), iar taskul se valideaza automat cand nu mai raman erori.

Regula de distributie partajata

Regula de calcul a orelor pe zi (indice brut, pragul de 2 ore/zi, rotunjirea si corectia pe ultima zi) este extrasa in metoda comuna WlpServiceImpl.calculeazaDistributieOrePeZile(totalOre, totalZile) si este refolosita de:

  • distribuirea suprasolicitarii pe task sau pe resursa (aceasta pagina);

  • actiunea Ore suplimentare, optiunea Total ore distribuite automat pe zilele lucratoare — cu diferenta ca acolo orele mareasc capacitatea zilelor (WLP_CALENDAR.Q_OVERTIME + Q_CAPACITATE), nu genereaza randuri de planificare STARE = 3.