App Studio Count Up Timer

Hi everyone,

I’m wondering if anyone with C# experience can help me figure out the best way to implement a count‑up timer for a custom field in Epicor.

What I’m trying to achieve:

  • I have a TabStrip with 7 sections.
  • Each section has a checkbox called “Section Complete”.
  • When a new record is created, it always starts in the Sales section.
  • I need a timer that:
    • Starts counting as soon as the record is created
    • Stops and resets when the Sales section is marked complete
    • Immediately starts again for the Commercial section
    • Repeats this process for each section
    • Finally stops completely when the Resolution section is marked complete

Basically, I want to track how long each section was “in progress” before being completed.

Here’s the UI setup for reference:

Is something like this even possible using C# in a customization, or maybe with App Studio timers? I haven’t started writing anything yet because I’m not sure of the best direction, so any guidance or examples would be hugely appreciated.

Thanks in advance!

I’m not a C# / Code guy… but I think you could pull this off via App Studio events if you wanted to.

That being said, the provided App Studio “timer” event action is, I think, just a delay timer (counts down), not a running clock (counts up).

My first thought on an approach would be just to grab Constant.CurrentTime at specific events and then calculate the difference between captures:

Constant.CurrentTime is number of seconds since midnight. So, if you subtract one time from another, you should be left with how many seconds in between those two events. I would store those delta values where ever you’re recording these… assuming you have UD Fields set up for that so you can store and review them later?

~*~

EDIT!!! - THIS APPROACH DOES NOT WORK! The event flow does… but using Constant.CurrentTime is a no-go!!!

See later posts for better approach.

~*~
For example:

First Event, I would set the trigger to after onClick_ToolNew (or whatever is triggering you creating a new record). The only thing you need in this event would be a row update to capture the current time stamp. For example, TransView.StartSalesTime = "{Constant.CurrentTime}"

A potential hiccup I see is that checkboxes can be inadvertently checked/unchecked, etc. So, you would need a condition in those onblur events to only record the next time stamp when the checkbox value = true. So…

Second Event is triggered by your first Section Complete checkbox. You’d need a condition for checkbox value = true, if true, set TransView.EndSalesTime = "{Constant.CurrentTime}"… then you could use a calculation widget to find the difference (TransView.EndSalesTime - TransView.StartSalesTime). Store the value from that calculation widget into your longterm storage column.

If someone unchecks the box [checkbox value = false], no problem, your condition will stop a new time stamp from being recorded. If they re-check the box, it will overwrite the end time with an updated time stamp, calculate a new difference in seconds, and update your final stored value.

For the next event, you could just use the EndSalesTime as the base line for your next phase… again, subtract it from the onblur of your next section completion checkbox (Commercial, etc.). Store that delta value for later.

wash, rinse, repeat for as many sections as you need.

At the end, you would just include another calculation widget that sums all your individual stored section values and that gives you an overall time spent.

So, if you wanted to try using events, I think you would just need one event when you create a new record (to grab the initial CurrentTime) and then an event onblur of each of your completion checkboxes. Then its just math.

1 Like

I agree with this approach :+1: — it’s a smart, pragmatic way to do it without dropping straight into C#.

Using Constant.CurrentTime with event-based captures is exactly how I’d think about it in App Studio as well. You’re effectively turning key user actions into checkpoints, and once you have consistent timestamps, the rest is just math. From a tracking and reporting perspective, that’s far cleaner than trying to infer time later.

That said, if App Studio isn’t a strong skillset for the team (or if this starts getting complex), this logic could translate very naturally into a BPM as well. A pre-processing BPM on BO.Method: SalesOrder.GetNewOrderHed (or even specific update methods) would be a good starting point to:

  • Capture the initial timestamp centrally
  • Enforce consistency regardless of UI customization
  • Reduce reliance on client-side behavior

You could still use App Studio for the UX and checkpoints, but let BPM handle the persistence and validation if needed.

2 Likes

Do as little as possible in application studio and as much as possible in functions & BPM’s

  1. Application studio is horribly buggy and a nightmare to work in
  2. The client can never be trusted.

If you count on the UI to track how long it takes, a savvy person could use browser tools to edit the time. And a non-savvy person could have AI help them do it.

4 Likes

Hi @dcamlin,

Thank you sir!

I will give this ago and let you know how it goes.

Thank you,
James

2 Likes

Hi @dcamlin,

So far so good!

I think using your method, I can get this to work.

The only caveat I can see here is that the sections can be in-progress for several days. So using “{Constant.CurrentTime}” wouldn’t work.

Is there syntax that account for this?

Thanks,
James

1 Like

Add the day to your calculation as well. {Constant.Today}

2 Likes

Hi @hmwillett

Can you elaborate on this please?

Also - it seems like {Constant.CurrentTime} is setting the same seconds after midnight on both events, and it doesn’t matter how long I wait before checking section complete box.

First Event

Second Event

When i preview the app it shows the following in the DevTools

image

I wait a few minutes and check the section complete checkbox, which then shows

Which always equates to 0.

Note I have also used a row-update expression to do the math as I couldn’t get the calculation widget to work.

Thanks,
James

Well, you learn something new everyday.

From what I can tell, the “Constant” dataview is just a snapshot in time when the app starts. It populates the Constant dataview and the values there don’t change.

So… Constant.CurrentTime won’t work for this.

Try this approach… use Date.now()

Date.now() is milliseconds since 1/1/1970 (Unix Epoch), so going this route, you don’t really need to care what day it is. You could come back days later and click your next trigger and the end time will just be another numeric value.

EndTime - StartTime will result in a bunch of milliseconds… divide those by 1,000 to get seconds. Or do additional maths to get minutes/hours/days… whatevs.

So, first row update to get your start time:

Here’s my second event:

First row update… same as the initial, just grab a new Date.now() value and assign it to your EndTime Binding.

OnSuccess… I do another row update to get me a result in seconds:

Preview:

5 Likes

Hi @dcamlin

This works!

Sending some virtual chocolates to you.

Appreciate the assistance.

Thanks,
James

1 Like

Hi @dcamlin,

Sorry for coming back to this after marking it as solved, but…

It would seem my event after GetNewUD05 is preventing me from saving any records.

Event after GetNewUD05

Error when saving the record

I know its because of the event after GetNew trigger, as it works fine if i disable the event, but if i change it to after onClick_toolSave it will reset the time every time someone saves it?

I also tried changing the trigger to event after onClick_toolNew but it didn’t fire at all.

Here are all the events that fire when i click add new.

Blockquote
onClick: OnClick_toolNew
GetNew
SysSetUIFormEventType
SysSetUIFormEventType_Evaluate
SysSetUIFormEventType_GetNew
PerformUpdate
Dataset_hasChanges
SysClearUIFormEventType
SysInvokeClearUIFormEventType
BeforeGetNew
GetNew_Data_Clear
SysData_Clear
Data_Clear
SysInitTemplateViews
initKeyFields
Execute_InitKeyFields
AfterInitKeyFields
GetNewUD05
SysAfterGetNew
SysInitTemplateViews
initKeyFields
Execute_InitKeyFields
AfterInitKeyFields
SysRefresh_KeyFields
Update_KeyFields_ViewName
Navigate_MainPage
AfterGetNew
BO_UD05_CaptureStartTimeOnNew
GetNewUD05_Refresh (disabled)
Changing_sysPages
BeforeRowchanging_sysPages
RowChanged_sysPages

Thanks,

Managed to resolve this using the event AfterUpdate trigger and adding a condition that checks if the field is equal to “0”, if it is do the row-update, otherwise don’t.

So on first save it will add the time, then the condition will prevent the timer resetting each time someone saves the record after this.

Hope this helps.

Thanks,