0

Parent - Child Record Update Example. Code Help to Complete Needed.

I hope this will be enlightening for others, in addition, to help to me. Thank you in advance.  I appreciate the help I’ve received. Nonetheless, I am working in circles. Perhaps if I lay out the use case, experts can fill the gaps.

 
We do social service. Our workforce is ‘volunteer’ with quite a bit of turn-over through time. For many reasons, we need to track attendance and work hours.

Work Flow -- PROCESS 1

A volunteer comes to our work location and ‘signs in’.  IF the volunteer doesn’t exist, they create themselves at the kiosk. The volunteer information is captured in a simple form.

 VOLUNTEER  [table / fields]
  ID
  Name
  Email
  Work Status (values:  Not Working, Working, Work Complete)

The Volunteer assigned the initial ‘Work Status’ of “Not Working.

PROCESS 2

 
The Ninox display form is in the KANBAN VIEW.  The volunteer finds him/herself in the list of ‘active’ volunteers in the NOT WORKING list (Kanban view tied to Volunteer.Work Status.).
Volunteer drags card from “Not Working” to “Working”

 
After Update Trigger:  This action triggers the creation of a TIMESHEET record for that volunteer.

 
TIMESHEET [table / fields]

>ID
>Work Date
>Clock in Time

Clock Out Time
Hours Worked

>Timesheet Status (‘Clocked In’, ‘Clocked Out’)

 
Record create = ID, Work Date, Clock In Time, Sets Timesheet status = Clocked In.

A volunteer can have only 1 timesheet in a single day.

 
IF they can’t find themselves on the list, they go to PROCESS 1.

IF they have already clocked in, the alert says “Already Clocked In”

 
Code Block 1-create volunteer timesheet [note:  my code creates the record and fields, but the ‘check for today’ logic doesn’t work]

—-

let v := this;
let a := last(select Timesheet where Volunteer = this.Id).'Work Date';
let t := (create Timesheet);

if a = date(today()) then
alert("Already checked in!")

else

if text('Work Status') = "Working" then
t.(Volunteer := v);
t.('Work Date' := today());
t.('Clock In' := datetime(now()));
t.('Timesheet Status' := 1);
alert("Timesheet created. Welcome!")

else

null

       end

end

—-

PROCESS 3

From NINOX KANBAN view. Volunteer drags card from ‘Working’ to ‘Work Complete.’

After Update Trigger:  This action triggers the update of the timesheet.

Record update = today’s record:  Clock Out Time, Hours Worked is calculated, Timesheet Status = Clocked Out.

Code Block 2-Check Volunteer out. [doesn’t work. updates ALL timesheet records for the volunteer]

let a := last(select Timesheet where Volunteer = this).'Work Date';

    if a = today() then

    Timesheet.('Clock Out' := datetime(now())) and
    Timesheet.'Timesheet Status' = 2

end

— 

 

Code Block 3 -- email volunteer record of timesheet and hours worked.

If an email for a volunteer is populated, an email is automatically sent with a record of the timesheet (volunteer name, date, start time, end time, hours worked.)

NO CODE BLOCK [I have not written this code yet] 

PROCESS 4.

Code Block 4 -- Reset Volunteer Work Status. Schedule based. Not sure how to write this one either.

Every day the Volunteer work status is reset to “Not Working”

17 replies

null
    • Fred
    • 3 yrs ago
    • Reported - view

    In Code Block 1:

     

    let v := this;
    let a := last(select Timesheet where Volunteer = v order by 'Work Date').'Work Date'; <--Since Volunteer is a reference field you only need to set it to your this variable then you can sort by Work Date so you make sure you get the most recent. Or another way is to use your Status field. A volunteer should only have one Timesheet with a status of 1 at any given time, so you can just search for Status =1.
    let t := (create Timesheet); <-- see your other thread for the fix for this.

    This is my sample code in a my volunteer db that I put in a button.

     

    let curRec := this;
    let xStatus := ((select Timesheet where Volunteer = curRec and Status = 1) order by WorkDay).WorkDay;
    if xStatus = date(today()) then
    alert("You have already checked in")
    else
    let t := (create Timesheet);
    t.(Volunteer := curRec);
    t.(ClockIn := datetime(now()));
    t.(Status := 1);
    alert("Timesheet Created! Welcome!")
    end

    • Fred
    • 3 yrs ago
    • Reported - view

    Code block 2:

     

    You should be using the variable a in your field references instead of Timesheet. I think that is the issue as you are telling it to use the whole table Timesheet and not the single record in "a". Which means we need to modify you let a statement so it is not tied to only Work Date.

    let a := last(select Timesheet where Volunteer = this order by 'Work Date'); <-- removed the 'Work Date' field specific so you can use the record reference later. also add the order by to makes sure you get the latest version

        if a.'Work Date' = today() then <--this is where we add the Work Date check

     

        a.('Clock Out' := datetime(now())); <--Now we can also reference other fields from a and not be stuck with only Work Date, also added the ";" to separate the the two fields that need to be updated

     

        a.('Timesheet Status' := 2) <--added the ":"in front of the equal so it will set the field to 2.

     

    end

    • Chapel_of_Praise
    • 3 yrs ago
    • Reported - view

    Fred, Thank you. RE Code Block 2:  I think I set it up as you suggested, but I get a "Field not found: a at line 2, column 4." Can you see what the issue is?

    let a := last((select Timesheet where volunteer = this order by 'Work Date');
    if a.'Work Date' = today() then
    a.('Clock Out' := datetime(now()));
    a.('Timesheet Status' := 2);
    'Work Status' := 3
    else
    if 'Work Status' = 3 then
    alert("Already clocked out. Thank you!");
    end
    end

    • Fred
    • 3 yrs ago
    • Reported - view

    let a := last((select Timesheet where volunteer = this order by 'Work Date'); <-- too many open parenths, just get rid of one of them

    • Fred
    • 3 yrs ago
    • Reported - view

    If you really want the alerts then they can NOT be in Triggers. They have to be in buttons cause they have to be run client side. Triggers run on the server side.

    • Chapel_of_Praise
    • 3 yrs ago
    • Reported - view

    Fred, I really appreciate the help. I just can't get it to work. The CREATE code for Timesheet works. The only difference from your code is the reference to 'status' in the xStatus variable. I got an error "can't find status' so I prefixed it with curRec (this). Code as follows:

    ---

    let curRec := this;
    let xStatus := ((select Timesheet where VolunteerID = curRec and curRec.Status = 1) order by WorkDay).WorkDay;
    if xStatus = date(today()) then
    alert("You have already checked in!")
    else
    let t:=(create Timesheet);
    t.(VolunteerID := curRec);
    t.(WorkDay := today());
    t.('Clock-In' := datetime(now()));
    t.(TStatus := 1);
    Status := 2;
    alert("Timesheet Created! Welcome!")
    end

    ---

    The UPDATE doesn't work. If I prefix the fields with the variable as shown below, it updates the Status but not the Clock-out date-time. It leaves them blank. If I change the variable to a table reference of Timesheet, it UPDATES all the Clock-out records for the given Volunteer.

    ---

    let curRec := this;
    let a := last((select Timesheet where VolunteerID = curRec) order by WorkDay);
    if a.WorkDay = today() then
    a.('Clock-Out' := datetime(now();
    a.(TStatus := 2);
    curRec.(Status := 3)))
    else
    if Status = 3 then
    alert("work day complete")
    end
    end

    ----

    • John_Halls
    • 3 yrs ago
    • Reported - view

    Hi

     

    I have put a version together.

     

    Volunteer

     

    Timesheet

     

    Script

     

    I have added an OldStatus exactly as Status to track where the card has come from, and another Reference filed that just holds the current timesheet. The Trigger after update script will only allow the volunteer cards to go between allowed statuses.

     

    Regards

     

    John

    • John_Halls
    • 3 yrs ago
    • Reported - view

    In addition to this

     

    The Volunteer Trigger on create needs

    Status := "Not Working";
    OldStatus := "Not Working"

     

    The second relationship CurrentTimesheetID is the other way round to TimesheetID, one timesheet can have many volunteers (even though it only ever has one).

    Also I would move the destruction of this relationship CurrentTimesheetID :=0 to OldStatus = 3 and Status = 1 this way you can display the log in and log out times on your Kanban Board if you wish.

     

    Regards

     

    John

    • John_Halls
    • 3 yrs ago
    • Reported - view

    Working

    • Fred
    • 3 yrs ago
    • Reported - view

    Here is my code:

     

    let curRec := this;
    let t := last((select Timesheet where Volunteer = curRec) order by WorkDay);
    if t.WorkDay = today() then
    t.(ClockOut := datetime(now()));
    t.(Status := 2);
    WorkStatus := 3;
    alert("Thanks for helping today. See you soon.")
    end

     

    The only thing I can see is that you may have parenthesis in wrong places:

     

    a.('Clock-Out' := datetime(now()));<--add two at the end so it closes this line. with the line closed properly this may fix the Status line...
    a.(TStatus := 2);
    Status := 3; <-- now this should work like in my example

     

    Also testing for WorkDay first may not work in all circumstances. What if they have checked out today, then currently the if statement will check them out again. An easy solution would be to test for Status first then check for WorkDay? Or you can do what John has developed.

    • Chapel_of_Praise
    • 3 yrs ago
    • Reported - view

    John,

    Thank you very much for your answer. I recreated as you've laid it out, but ran into a problem. The On Update trigger is setup as you did, but I get a 'Field Not Found error.. see below. Thoughts?

    • Chapel_of_Praise
    • 3 yrs ago
    • Reported - view

    Fred, The first time through the process everything works. If I try to check out multiple times, the code gives me the correct message "work date complete."

    However, if I try and check in again for the same volunteer on the same day, based on the code, it should not allow it.

    let xStatus := ((select Timesheet where VolunteerID = curRec and curRec.Status = 1) order by WorkDay).WorkDay;
    if xStatus = today() then
    alert("You have already checked in!")

    However, it is creating a new record each time I transact. I currently have create code in a button Check In and then a Check Out button. I am using buttons to test. The second time through the Check Out code fails.

    • John_Halls
    • 3 yrs ago
    • Reported - view

    Hi

    You need an extra right hand bracket in the line above, so it becomes

    t.(In := datetime(now()));

    But you don't need the datetime function. just use

    t.(In := now());

     

    Regards John

    • Chapel_of_Praise
    • 3 yrs ago
    • Reported - view

    John:  Great! This is working. However, I can put the same person through the process for the same day. For one day, a volunteer should only have one timesheet. Did you envision a way to do that?

    Also, on the Kanban view:  Is there any way to eliminate the column called "(empty)"? Technically this should not be possible. Same might be said for the "+Add value" column. thoughts?

    • John_Halls
    • 3 yrs ago
    • Reported - view

    Re the Empty column, in the Status field change Required to Yes.

    More coming re the rest...

    • Fred
    • 3 yrs ago
    • Reported - view

    You wrote:

     

    let xStatus := ((select Timesheet where VolunteerID = curRec and curRec.Status = 1) order by WorkDay).WorkDay;
    if xStatus = today() then
    alert("You have already checked in!")

     

    1) in the first line you need to do add last, because this is a Many to 1 relationship you can theoretically get many records for your search so you have to specify you only want one.

     

    2) for proper syntax, you can't use curRec at the beginning of a filter in a select. You use curRec.Status = 1. In the Timesheet table there is no reference to curRec and I don't know if Ninox knows which relationship to use. Since you want to reference the Status field from the Volunteer table you would type:

     

    VolunteerID.Status = 1 <--I'm guessing that VolunteerID is the name of the reference field in Timesheets

     

    3) Since the Timesheet is a child of the Volunteer, whatever the VolunteerID.Status is currently equal to is updated in all of the records of the volunteer in the Timesheet table. If you show the Volunteer.Status field in your Timesheet table you will see it change whenever you update the Status of the Volunteer. So I don't think using the Volunteer Status field is a good way to check if they are set to not working.

     

    You can do a Display field only if, to hide the checkin button if their Status = 2. You can do a similar thing for checkout to hide if Status = 1. That way people can't accidentially do things they shouldn't and less error correction needed in your code.

    • John_Halls
    • 3 yrs ago
    • Reported - view

    I would use a Reset button but first you need to update the Trigger after update so that this allows the Reset button to work

     

    if OldStatus = 1 and Status = 2 then
    OldStatus := Status;
    let v := this;
    let t := (create Timesheet);
    t.(VolunteerID := v);
    t.(In := now());
    CurrentTimesheetID := t
    end;
    if OldStatus = 1 and Status = 3 then
    Status := 1
    end;
    if OldStatus = 2 and Status = 1 and CurrentTimesheetID != null then
    Status := 2
    end;
    if OldStatus = 2 and Status = 3 then
    OldStatus := Status;
    CurrentTimesheetID.(Out := now())
    end;
    if OldStatus = 3 and Status = 1 and CurrentTimesheetID != null then
    Status := 3
    end;
    if OldStatus = 3 and Status = 2 then
    Status := 3
    end

     

    Add this to your reset button

     

    let t := (select Volunteer);
    t.(CurrentTimesheetID := 0);
    t.(Status := 1);
    t.(OldStatus := 1)

     

    The trigger doesn't allow the Volunteer to move from 2 to 1 or from 3 to 1 if there is a CuurentTimesheetID. The reset button removes this and so all the records can be set to 1.

     

    I'm stumped on not having + Add Value in the Kanban view

     

    Regards John

Content aside

  • 3 yrs agoLast active
  • 17Replies
  • 1079Views