Osy#betaRun it on Osyrin: osyrin.com

Reference / Workflow

Imported history — StartedAt, and raising an event at the time it happened

workflow W { StartedAt = this.Item.OpenedAt; … } · W.RaiseReply(item, at: repliedAt)

For an item whose history began before its run did — a ticket moved from another helpdesk, a seeded month of work — `StartedAt = <DateTime over this.Item>;` starts the run at that instant, and `Raise<Event>(item, at: when)` raises each later step at the time it happened. Every SLA clock then counts from when things really happened.

preview1 example compiled by CIworkflowimportsla

Summary#

A run starts when its item is created, and an event happens when it is raised — which is right for live work and wrong for history. Import five hundred tickets and every SLA clock starts at the moment of the import, so the queue reads 0:59, 0:59, 3:59, 3:59 and a ticket opened a week ago is as fresh as one opened a minute ago. Two surfaces put the history's own times back:

  • StartedAt = this.Item.OpenedAt; — a workflow setting: the run is taken to have started then, and every clock it arms at the start counts from it.
  • W.RaiseX(item, …, at: when) — the event happened when: the run moves as of that instant, and every clock the move arms counts from it.

Signature#

workflow SupportTicket {
  Tracks    = Ticket.Stage;
  Autostart = true;
  StartedAt = this.Item.Conversation.StartedAt;   // a live ticket: now; an imported one: when it was opened
  …
}

SupportTicket.RaiseReply(t, at: firstReplyAt);
SupportTicket.RaiseHold(t, "Waiting on the carrier", at: heldAt);

Description#

When does a run with StartedAt start?#

At the instant the setting names, evaluated once as the run starts — never later than now (a run cannot start in the future, so a value after now is simply now). A live item whose field holds "now" starts exactly as it always did, which is why a helpdesk can write StartedAt = this.Item.Conversation.StartedAt; for every ticket rather than only for imported ones. A clock whose promise had already run out by the time of the import is missed straight away — the breach is recorded, as it would have been had the run been there all along.

What may at: be?#

A DateTime no earlier than the moment the run entered the state it is in, and no later than now. History is raised in the order it happened: an at: before the run's current state is refused, naming both instants, and so is one in the future. Both refusals are ValidationException. An event that itself declares a parameter called at keeps it — the parameter wins, as a C# named argument would.

Examples#

using Osysharp.Workflow;

enum Stage { New, Open, Done }

entity Ticket {
  [Required, MaxLength(200)] string Title;
  DateTime OpenedAt;
  Stage Stage;
  security { allow read, create, update when IsAuthenticated; }
}

workflow Desk {
  Tracks    = Ticket.Stage;
  Autostart = true;
  Initial   = New;
  StartedAt = this.Item.OpenedAt;

  event Reply();
  event Close();

  state New {
    Expire = TimeSpan.FromHours(4);
    on Expire { }
    subscribe Reply();
    on Reply { goto Open; }
  }
  state Open {
    on Close { goto Done; }
  }
  terminal success Done { }
}

/// One ticket from the old system: opened, and answered, when it really was.
public void ImportTicket(string title, DateTime opened, DateTime? answered) {
  var t = new Ticket { Title = title, OpenedAt = opened };
  UnitOfWork.Commit();
  if (answered != null) { Desk.RaiseReply(t, at: answered.Value); }
}

Testing it#

Read the clock the way a list does — Workflow.WorkByItem<Ticket>()'s BreachesAt — and assert it counts from the history, not from the test: a ticket opened three hours ago on a four-hour promise has about an hour left.

See also#

Related

Raising a workflow event

Raise a typed event on the run bound to an entity, from anywhere — an ordinary server function, a webhook handler, a…

ServiceHours (SLA-accrual windows)

A schedule the SLA clock accrues within — the platform WALKS its weekly windows (and holiday exceptions) to advance…

Assigned / Finished (milestones)

A milestone puts an SLA on a slot's progress — Assigned (someone must PICK IT UP within Within) and Finished (it must…

Tracks and Initial (the field a workflow drives)

Names the enum field a workflow owns and the state a run starts in. No application code may write that field, and when…