Summary#
use Osysharp.IncidentReports@0; keeps incident reports. The app says who reviews them:
policy ReviewsIncidents => IsSafetyLead; // reads every report, reviews, assigns actions, tells people, the register
policy ReportsIncidents => IsStaff; // who may file one — anybody signed in, unless you narrow itA report is then read by the person who filed it and by ReviewsIncidents — nobody else, in lists, counts and searches.
Signature#
virtual policy ReviewsIncidents => false; virtual policy ReportsIncidents => IsAuthenticated;
virtual policy ManagesIncidentSetup => ReviewsIncidents;
app.Incidents = new IncidentsSetup { AuthorityWarningHours = 24, CloseWithOpenActions = false, ReportersReadOutcome = true, Organisation = "" };
public interface IIncidentContacts { List<Principal> ContactsFor(Principal person); }
IncidentReport ReportIncident(IncidentCategory category, DateTime occurredAt, string location, string description,
string immediateAction = "", IncidentSeverity? severity = null);
IncidentPerson AddIncidentPerson(IncidentReport report, IncidentRole role, Principal? person = null, string name = "",
string injury = "", IncidentTreatment treatment = IncidentTreatment.None, string treatmentNote = "");
void GiveStatement(IncidentPerson p, string statement);
void StartIncidentReview(IncidentReport report, string notes = ""); void ClassifyIncident(IncidentReport report, IncidentSeverity severity);
void RecordIncidentFindings(IncidentReport report, string rootCause = "", string outcome = "", string notes = "");
void MarkIncidentReportable(IncidentReport report, bool reportable, string authority = "", DateTime? dueAt = null);
void RecordAuthorityReport(IncidentReport report, string reference, DateTime? at = null);
void CloseIncident(IncidentReport report, string outcome = ""); void ReopenIncident(IncidentReport report, string why);
CorrectiveAction AssignCorrectiveAction(IncidentReport report, string what, Principal owner, DateOnly dueOn);
void CompleteCorrectiveAction(CorrectiveAction action, string note);
List<IncidentNotice> InformContacts(IncidentPerson about, string message = ""); void AcknowledgeIncidentNotice(IncidentNotice n, string response = "");
List<IncidentRegisterRow> IncidentRegister(DateOnly from, DateOnly to); string IncidentRegisterCsv(DateOnly from, DateOnly to);Description#
What does a report hold?#
A running number (INC-00001), who filed it (stamped from the caller, never chosen), when it happened and was reported,
a category and — when known — a severity, where, what happened and what was done there and then. Categories and
severities are the app's rows (IncidentCategory, IncidentSeverity with a rank). A category that AsksAboutInjury
asks who was hurt on the form. The person who filed it corrects the facts while it is Open; once a reviewer takes it
up it is the record. Nothing is deleted.
Who was involved?#
IncidentPerson rows: hurt, involved or a witness, with an account (staff, a member, or a child or visitor with an
account they never sign in to — managed accounts) or just a name. Each carries the injury, the
care given, days away and their statement. A witness reads their own line, never the report, and writes their own
statement (GiveStatement). Photos and files go on the report (KeepIncidentFile, AttachIncidentFile).
How is it reviewed?#
StartIncidentReview takes it up; ClassifyIncident sets its severity; RecordIncidentFindings keeps the cause
(reviewers only) and the outcome (which its reporter reads once it closes, unless the app says otherwise).
AssignCorrectiveAction sets what will stop it happening again, with an owner and a due date — the owner reads their
action and the report's number and category it carries, never the report, and marks it done. Each action's clock raises
CorrectiveActionOverdue once when it runs late. CloseIncident refuses while an action is open or the report waits
for its authority, naming what is left; ReopenIncident takes it back, the reason kept. Status, notes and review
fields move only inside these functions (deny … except via).
Who is told?#
The app answers IIncidentContacts — next of kin, a carer — found, never wired (App.Implementations<T>()); with
Osysharp.Guardianship, Osysharp.IncidentReports.Guardianship answers with a child's guardians by itself.
ContactsToInform lists who has not been told; InformAboutIncident / InformContacts send an IncidentNotice that
keeps exactly what they were told (the facts of that person's part and the reviewer's own words), raising
IncidentNoticeSent for the app to mail. They read their notice — never the report — and acknowledge it.
When must it go to an authority?#
A severity may carry ReportWithinHours and an Authority ("HSE (RIDDOR)", 240): classifying a report at it makes it
reportable, due that long after it was reported. A reviewer can set or clear one by hand (MarkIncidentReportable).
Each report's clock raises IncidentAuthorityReportDue AuthorityWarningHours before the deadline and
IncidentAuthorityReportOverdue when it passes, once each; RecordAuthorityReport keeps the authority's reference and
silences both.
The register#
IncidentRegister(from, to) is every report of the period, one line each — number, when, category, severity, where,
who was hurt and how they were cared for, days away, status, the authority line, open actions — and
IncidentRegisterCsv the same as CSV, for an inspector, an insurer or a board.
Which pages does it serve?#
/incidents — report one; your reports, the actions you own, statements asked of you, what you were told.
/staff/incidents — the desk: what waits for review, what is under review, deadlines, late actions; one report open in
a dialog with the whole review. /staff/incidents/register and /staff/incidents/setup. Each stands in the app's
working shell with theme tokens only; take one over by declaring a component of the same name, or place
MyIncidents, ReportIncidentForm, IncidentsDesk, IncidentDetail, IncidentRegisterView or IncidentSetup on a
page of your own.
Whose data is kept?#
What happened and a statement are Personal.Content, names Personal.Identity, an injury and the care given
Personal.Special. With Osysharp.Privacy, Osysharp.Privacy.IncidentReports arrives by itself (Bridges — a package that arrives when an app uses the two it completes):
special-category data kept on a legal obligation, counted, copied and — when its period ends — erased from the lines
naming the person, while the report stays.
Examples#
A workshop that tells a trainee's guardian and chases late actions#
app Workshop {
use Osysharp.Identity@0;
use Osysharp.Csv@0;
use Osysharp.IncidentReports@0;
use Osysharp.Storage;
use Osysharp.Ui;
use Osysharp.Workflow;
model "model/**/*.osy";
}
using Osysharp.Identity;
using Osysharp.IncidentReports;
[Role] enum ShopRole { Authenticator, Trainee, Instructor, SafetyLead }
policy IsStaff => RoleGrant.Any(g => g.Member == user && (g.Role == ShopRole.Instructor || g.Role == ShopRole.SafetyLead));
policy ReportsIncidents => IsStaff;
policy ReviewsIncidents => RoleGrant.Any(g => g.Member == user && g.Role == ShopRole.SafetyLead);
app.Incidents = new IncidentsSetup { AuthorityWarningHours = 48, Organisation = "Northgate Training Workshop" };
/// Who signed a trainee up — the one told when the trainee is named in an incident.
entity Sponsor {
[Required("Which trainee?")] Principal Trainee;
[Required("Who sponsors them?")] Principal Contact;
security { allow read when IsStaff; }
}
public class Sponsors : IIncidentContacts {
public List<Principal> ContactsFor(Principal person) => Sponsor.Where(s => s.Trainee == person).Select(s => s.Contact).Take(5).ToList();
}
/// A reminder the safety lead sees when an action runs late.
entity LateAction {
[MaxLength(1000)] string What = "";
security { allow read when ReviewsIncidents; }
}
[On]
void NoteLate(CorrectiveActionOverdue e) { new LateAction { What = e.Action.What }; }See also#
- The Guardianship kit — who acts for whom: children, carers, consents and emergency contacts — a child's guardians, told by the bridge
- The UserAccounts kit — sign-in done properly, once — an account for somebody named who never signs in
- [Attachment] — a file readable wherever its row is — photos, readable wherever the report is
- deny … except via — the review moving only through the kit's functions
- The Privacy kit — erasure that keeps only what the company must or may — a person's incident record in their privacy requests
kits/incident-reports/tests/— every behaviour above as a test, the screens under--pixels