NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky
Approvals for Laravel

Žiadosti pre viacerých schvaľovateľov

Žiadosť pre subjekt otvoríte cez Approvals::request(): schvaľovateľov vymenujete vo from(), zvolíte pravidlo a zavoláte open():

use RoundlyConsulting\Approvals\Facades\Approvals;

// Unanimous (the default): every approver must approve; one rejection rejects the request.
Approvals::request($release)->from([$lead, $qa, $pm])->open();

// Quorum: 2 of 3 approvals resolve it; it rejects once 2 approvals are no longer reachable.
Approvals::request($release)->from([$lead, $qa, $pm])->quorum(2)->open();

// Any: the first approval resolves it.
Approvals::request($release)->from([$lead, $qa, $pm])->any()->open();

// Weighted: resolves once the summed approver weight reaches 3.
Approvals::request($release)->from([$lead, $qa, $pm])->weighted(3)->open();

Ak má subjekt trait RequiresApproval, requestApproval() je skratkou pre to isté volanie:

use RoundlyConsulting\Approvals\Enums\ApprovalRule;

// RequiresApproval shorthand — the same request through the same manager
$release->requestApproval([$lead, $qa, $pm], ApprovalRule::Quorum, quorum: 2);

Schvaľovatelia rozhodujú o subjekte ako zvyčajne. Každé rozhodnutie sa započíta do otvorenej žiadosti a žiadosť sa sama vyhodnotí v momente, keď je pravidlo splnené:

Approvals::request($release)->from([$lead, $qa, $pm])->open();   // Unanimous by default

Approvals::for($release)->as($lead)->approve();   // decisions flow into the open request automatically
Approvals::for($release)->as($qa)->approve();
Approvals::status($release);          // ApprovalStatus::Pending — one approval still missing

Approvals::for($release)->as($pm)->approve();
Approvals::status($release);          // ApprovalStatus::Approved
$release->isApproved();               // true — RequiresApproval shorthand

Rozhodujú len menovaní schvaľovatelia

Schvaľovatelia, ktorých odovzdáte — do from(), requestApproval(), StageDefinition alebo open() šablóny workflow — sa uložia na žiadosť, pri viacstupňovej žiadosti ku každému stupňu. Rozhodovať môžu len oni alebo zástupca, ktorý koná za niektorého z nich. Ktokoľvek iný dostane z approve(), reject(), toggle() aj ask() výnimku UnauthorizedApprovalException a nič sa nezapíše:

use RoundlyConsulting\Approvals\Facades\Approvals;

$request = Approvals::request($release)->from([$lead, $qa, $pm])->quorum(2)->open();

Approvals::for($release)->as($intern)->approve();   // throws UnauthorizedApprovalException — not a named approver
$request->namedApprovers();                         // list<NamedApprover>

Žiadosť otvorená bez menovaných schvaľovateľov — from([]) alebo ApprovalRequest, ktorú vytvoríte sami len s required_approvers — si ponechá otvorený režim: započíta sa rozhodnutie ktoréhokoľvek schvaľovateľa, kým ich nerozhodne required_approvers. Potrebuje však pravidlo, ktoré sa dá splniť aj bez mien — any(), quorum($n) alebo requiredApprovers: $n v ApprovalRequestData či StageDefinition. Pri predvolenom pravidle Unanimous by potrebovala nula schválení, čo sa nikdy nevyhodnotí, preto jej otvorenie vyhodí výnimku:

Approvals::request($release)->open();               // InvalidApprovalRequestException
Approvals::request($release)->any()->open();        // any one approval resolves it
Approvals::request($release)->quorum(2)->open();    // two approvals resolve it

Každý schvaľovateľ sa uloží raz, žiadosť nemôže vyžadovať viac schválení, než vymenúva schvaľovateľov, a prah kvóra či váhy, ktorý by schvaľovatelia nikdy nedosiahli, sa odmietne už pri otvorení — vždy výnimkou InvalidApprovalRequestException.

Ako sa pravidlá vyhodnocujú

Unanimous a Any počítajú hlavy; Quorum a Weighted sčítavajú váhy rozhodnutí, čo je pri predvolenej váhe 1 tiež obyčajný počet hláv. required je počet schválení, ktoré žiadosť potrebuje — jedno na menovaného schvaľovateľa, ak nie je určené inak; prahom je kvórum, alebo required, ak kvórum nezadáte:

PravidloSchválené, keďZamietnuté, keď
Unanimouspočet schválení ≥ requiredpríde akékoľvek zamietnutie
Quorumschválená váha ≥ prahprah je nedosiahnuteľný: schválená váha + zostávajúca váha < prah
Anyprvé schválenievšetci schvaľovatelia rozhodli a nikto neschválil
Weightedschválená váha ≥ prahako Quorum — bez menovaných schvaľovateľov až keď rozhodnú všetci

Ku ktorej žiadosti sa rozhodnutie pripojí

Rozhodnutie sa pripojí k poslednej otvorenej žiadosti schvaľovaného modelu. Ak ho chcete pripojiť ku konkrétnej žiadosti, pripnite ho cez within($request); žiadosť musí patriť schvaľovanému modelu, inak vznikne InvalidApprovalRequestException a nič sa nezapíše. Model, ktorý nikdy žiadosť nemal, naďalej prijíma samostatné rozhodnutia.

Uzavreté žiadosti

Keď je žiadosť uzavretá — schválená, zamietnutá, zrušená alebo expirovaná — jej kolo sa skončilo a nič ďalšie sa do nej nezapíše. Neskoré approve(), reject(), toggle() či ask() na jej subjekte (alebo pripnuté k nej cez within()) a cancel(), ktorý by stiahol niektoré z jej rozhodnutí, vyhodia ClosedApprovalRequestException — aj pri zástupcovi. Vlastnosť $request výnimky je uzavretá žiadosť. Zopakovanie rozhodnutia, ktoré tam aktér už má, zostane neškodné a vráti ho:

$release->requestApproval([$lead]);
$lead->approve($release);           // resolves the request as approved

$lead->approve($release);           // no-op: returns the same approval
$lead->reject($release);            // throws ClosedApprovalRequestException
$lead->cancelApproval($release);    // throws ClosedApprovalRequestException

$release->requestApproval([$lead]); // a new round: decisions count towards it again

Keď sa žiadosť vyhodnotí — schválením, zamietnutím či vypršaním — alebo ju uzavrie vaša aplikácia, vyžiadania, ktoré v nej ešte čakajú, sa zrušia a každé spustí ApprovalCancelled a ApprovalStatusChanged. pendingApprovals(), hasPending() ani @pendingApproval tak už nehlásia vyžiadania, na ktoré nemá kto odpovedať.

Uzavretie kola z vašej aplikácie

Keď vaša aplikácia sama zruší subjekt alebo mu nechá uplynúť čas — stiahnutú žiadosť o nákup, rezerváciu po termíne — uzavrite jeho otvorené kolo, aby ho neskoré rozhodnutie už nemohlo vyhodnotiť. close() prijme len for() a voliteľne within():

use RoundlyConsulting\Approvals\Enums\ApprovalStatus;
use RoundlyConsulting\Approvals\Facades\Approvals;

Approvals::for($purchase)->close();                                         // as cancelled: every open round of $purchase
Approvals::for($purchase)->close(ApprovalStatus::Expired);                  // as expired
Approvals::for($purchase)->within($round)->close(ApprovalStatus::Expired);  // only $round

// int — how many rounds this call closed; 0 when none was open

Kolo sa uzavrie rovnako, ako ho uzavrie engine pri vyhodnotení. Podmienený zápis ho posunie, len kým ešte čaká, a nastaví resolved_at, takže rozhodnutie, ktoré ho vyhodnotilo skôr, si svoj výsledok ponechá; nevybavené vyžiadania sa zrušia (pre každé ApprovalCancelled a ApprovalStatusChanged); potom sa pre kolo spustia ApprovalRequestResolved a ApprovalStatusChanged. Rozhodnutia počas zápisu zamknú riadok žiadosti, takže rozhodnutie, ktoré súperí s uzavretím, sa buď zapíše pred ním, alebo ho odmietne ClosedApprovalRequestException. Už prijaté rozhodnutia zostanú, ako sú.

  • Výsledky — len Cancelled (predvolene) a Expired. Approved a Rejected vznikajú len z rozhodnutí schvaľovateľov, preto ony aj Pending vyhodia InvalidStatusTransitionException, aj keď nie je otvorené žiadne kolo.
  • Už uzavreté — kolo, ktoré je už uzavreté, nech ho uzavrelo čokoľvek, zostane nedotknuté a nič sa nespustí; uzavreté kolo z within() tu nie je chybou. Subjekt bez otvoreného kola vráti 0.
  • Po termíne — kolo po svojom expires_at sa už číta ako expirované, preto sa uzavrie ako expired bez ohľadu na zadaný výsledok.
  • Viacstupňové kolá — zrušia sa vyžiadania vo všetkých stupňoch a currentStage() vráti null; riadky stupňov si ponechajú svoj stav, rovnako ako keď kolo vyprší.
  • Bez aktéra, autorizácie a dôvodu — uzavretie je krok životného cyklu, o ktorom rozhoduje vaša aplikácia, rovnako ako o otvorení kola, takže ho autorizujte vo vlastnom kóde. as(), weight(), expirácia či dôvod cez because() vyhodia InvalidApprovalRequestException a kolo zostane otvorené; prečo ste ho uzavreli, si uložte na vlastný model. because(null) znamená bez dôvodu a kolo uzavrie ako zvyčajne.
  • Fake — Approvals::fake() ho zaznamená ako ApprovalOperation::Close (pozri Testovanie). Krok enginu pod ním, ApprovalRequest::close(), je interný: používajte fasádu alebo CloseApprovalRequestAction.

Priebeh

Approvals::progress() — alebo approvalProgress() na modeli s RequiresApproval — vráti nemennú snímku poslednej žiadosti:

$progress = Approvals::progress($release);   // null when the subject has no request

$progress->status;         // ApprovalStatus
$progress->approved;       // approvals in — summed weight under Quorum and Weighted
$progress->required;       // approvals the request needs
$progress->threshold;      // quorum / weight threshold, or null
$progress->ratio();        // float 0.0–1.0
$progress->percentage();   // int 0–100
ČlenVýznam
statusApprovalStatus žiadosti — Expired, keď jej uplynie platnosť, rovnako ako ho hlási Approvals::status().
approved / rejectedSchválenia / zamietnutia — pri Quorum a Weighted súčet váh, inak počet; pri viacstupňovej žiadosti za otvorený stupeň.
requiredPočet schválení, ktoré žiadosť potrebuje — pri viacstupňovej žiadosti za otvorený stupeň.
thresholdPrah kvóra či váhy, alebo null.
currentStagePoradie otvoreného stupňa, alebo null — pri uzavretej žiadosti či žiadosti po termíne vždy null.
totalStagesPočet stupňov — 0 pri jednostupňovej žiadosti.
clearedStagesStupne, ktoré prešli schválením.
ratio()0,0–1,0 — prejdené/všetky stupne pri viacstupňovej žiadosti, inak schválené/prah.
percentage()ratio() zaokrúhlené na celé percento.

Model žiadosti

$request = Approvals::request($release)->from([$lead, $qa, $pm])->quorum(2)->open();

$request->rule;                 // ApprovalRule::Quorum
$request->quorum;               // 2
$request->required_approvers;   // 3
$request->namedApprovers();     // list<NamedApprover> — empty when opened without names
$request->status;               // ApprovalStatus::Pending
$request->decisions;            // decisions attached to this request
$request->resolved_at;          // ?CarbonImmutable — set when it resolves
$request->isOverdue();          // bool — still pending, but past expires_at

$request->resolve();            // re-evaluate and persist; a final request is returned unchanged

Vyhodnotenie spustí ApprovalRequestResolved a zastrešujúcu ApprovalStatusChanged, a to práve raz — žiadosť sa uzavrie podmieneným updatom, takže ju súbežné rozhodnutia vyhodnotia len raz. Akcie rozhodnutí volajú resolve() za vás — sami ho volajte, len ak rozhodnutia pripájate mimo nich.

Prejavte lásku k open source

Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.

Ďalšie spôsoby podpory vrátane kryptomien

Odoslaním daru súhlasíte s našimi podmienkami prijímania darov.

Chcete to zabudovať do svojho produktu?

Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.