Attempt to fetch or exercise a contract not visible to the committer

Mod note: As of SDK 1.5.0 Scenarios have been superseded by the more powerful Daml Script. We now recommend using that for all purposes. For more information, and to learn how to use Script please check out @Andreas’ post on our blog.

There is a condition
Associates punchIn and puncOut attendance in Attendance template.
So how the workflow is,

  1. System user create.
  2. System user creates associates.
  3. Attendance Template created
  4. when Associate punchIn, current time added in PunchInTime Variable and its Name added.

I coded this situation
But I have certain issues here
When the attendance table created I have to give the associate name along with the system user, otherwise, it gives me an error
"Error: Attempt to fetch or exercise a contract not visible to the committer"
because attendance is created by the system user and the controller is the associate user who punches in and punches out. Actually, the associate user added at PunchIn time.
How do I solve this problem? Can you suggest a better way of doing this?
here is my code

template SystemUser
    with
        name : Text
        user : Party
    where
        signatory user 
 template AssociateUser
    with 
        name : Text
        associate : Party  
        systemUser : Party
    where 
        signatory  systemUser 
        observer associate
        choice CreateAssociate : ContractId AssociateUser
            controller systemUser 
               do create this  
 template Attendance 
    with
        systemUser : Party
        associate : Optional Party
        punchInTime : Optional Time
        punchOutTime :Optional Time
    where 
        signatory systemUser  
        observer associate
        nonconsuming choice PunchIn 
            : ContractId Attendance
            with
                currentTime : Time
                newAssociate : Party
            controller associate 
               do 
                create this with associate = Some newAssociate , punchInTime = Some currentTime 
        nonconsuming choice PunchOut 
            : ContractId Attendance
            with
                currentTime : Time
                newAssociate : Party
            controller associate 
               do 
                create this with associate = Some newAssociate  , punchOutTime = Some currentTime

Here is my Scenarios

setup = do 
   systemUser <- getParty "Alice"
   associatedummy <- getParty "Dummy" 
   associate  <- getParty "Bob"
   associate1  <- getParty "Bob1"
   uid <- submit systemUser do
        create SystemUser 
            with 
                name = "Alice" 
                user = systemUser
   asid <- submit systemUser $ create AssociateUser 
    with 
       name = "Bob" 
       associate 
       systemUser  
   return()  
   atid <- submit systemUser do
        create Attendance
            with
                systemUser
                associate  = Some associate  // here   
                punchInTime = None
                punchOutTime = None

Hi @Muhammad_Moiz,

There are a few different things to keep apart here:

  1. So-called role templates that are setup as part of some user initialization and then provide non-consuming choices for various actions. In your example, that’s SystemUser and AssociateUser. SystemUser only needs one choice to create a new user. AssociateUser provides a choice to punch in. Note that you need one AssociateUser template per associate.
  2. A running attendance, i.e., the state where the user has punched in but not yet punched out. I would generally recommend to represent different states in different templates so here we can create a special RunningAttendance template for this.
  3. The recorded attendance once the user has punched out. For this we create a new choice on RunningAttendance to punch out and create the final Attendance template. Attendance itself does not provide any choices, it just stores the data for a recorded attendance.

To simplify things a bit, I’ve switched to using DAML’s getTime function but you could stick to passing in the time if you want. There is one issue here where a user can punch in again before having punched out. One option for solving that would be to put a contract key on the RunningAttendance template to enforce uniqueness.

module Main where

import DA.Time

template SystemUser
    with
        name : Text
        user : Party
    where
        signatory user
        nonconsuming choice CreateAssociate : ContractId AssociateUser
          with
            userName : Text
            associate : Party
          controller user
            do create AssociateUser with
                 name = userName
                 associate = associate
                 systemUser = user

template AssociateUser
    with
        name : Text
        associate : Party
        systemUser : Party
    where
        signatory  systemUser
        observer associate
        nonconsuming choice PunchIn : ContractId RunningAttendance
          controller associate
          do punchInTime <- getTime
             create RunningAttendance with
               systemUser
               associate
               punchInTime

template RunningAttendance
  with
    systemUser : Party
    associate : Party
    punchInTime : Time
  where
    signatory systemUser, associate
    choice PunchOut : ContractId Attendance
      controller associate
      do punchOutTime <- getTime
         create Attendance with ..

template Attendance
    with
        systemUser : Party
        associate : Party
        punchInTime : Time
        punchOutTime : Time
    where
        signatory systemUser, associate

setup = do
   systemUser <- getParty "Alice"
   associatedummy <- getParty "Dummy"
   associate  <- getParty "Bob"
   associate1  <- getParty "Bob1"
   uid <- submit systemUser do
        create SystemUser
            with
                name = "Alice"
                user = systemUser
   asid <- submit systemUser $ create AssociateUser
    with
       name = "Bob"
       associate
       systemUser
   running <- submit associate $ exercise asid
     PunchIn
   pass (hours 5)
   submit associate $ exercise running PunchOut
   return ()
3 Likes

There is a condition that a associate punch In on one day and Punch Out on another day Does this code cover this condition. What I understand is that associate cannot punch Out with out punch In

1 Like

No that code doesn’t cover the condition. You could add an assertion in the PunchOut choice to make sure that the date is different than when punching in.

1 Like

We have both conditions
associate can punch out on same day or another day.

1 Like

Oh I see, that’s possible. There is no restriction on the date you punch out on. DAML’s getTime function will ensure that you cannot go backwards. If you pass in the time, you probably want to add an assertion to ensure that the punch out time is after the punch in time.

1 Like

I have one more condition, sorry I forget to say
let say associate user punch In. Now when associate user punch out, there I check a condition,
If he punch In on same day he simply update it punch out time by current time else a new instance created where there is N/A in punch In time and current Time in punch Out time.

1 Like

Here is the update situation, which I believe is easy to understand

  • In case of punch in, a new record is created every time by the same person
  • In case of punch out, we get the last record for that person for current day. If the last record is for punch in, we associate the punch out with the punch in.
  • If the last record is for punch out, we create a new record for current day.
  • If there is no record for that person for current day, we create a new punch out record

Example:
Sep 27 (punch in missing) 11:30 out
(day changes)
Sep 28 (punch in missing) 5:30 out
Sep 28 6:30 in 7:40 out
Sep 28 (punch in missing) 8:55 out
(day ends)

1 Like

That mostly makes sense, @Muhammad_Moiz. However, I’m not entirely sure where the problem is. What’s your question?

Did you try and implement this, or are you asking for help on where to start?

1 Like

I have coded this situation and Moritz Kiefer update my code, after update we can create attendance record and punch In and punch out.
example
A user punch In first and than he punch out. But what I want is …(above message).
I am working on it and if get any problem than I asked it. Thanks

1 Like
1 Like

What’s the meaning of punching out without punching in? I would expect that you always have to punch in before punching out, otherwise what’s the start time?

Your question presumes that the Running Attendance template wipes clean every day. It doesn’t necessarily (though can be made to do so).

There would never be need to associate a check-out with a prior day’s check-in. “getTime” will record timestamp showing check-in on day x and check-out on day x+1.

If a person punchOut without punchIn than there is simply N/A in the punchIn field.

You could add an assertion that prevents a check-out without a check-in. Or more realistically, allows such a check-out only with the sign-off of system users/admin.

Here is one implementation that matches your example. There are a few things that you left open, e.g., what happens if you punch in a second time before punching out. I’ve ignored them here but feel free to fill in whatever implementation you need. I do find the requirements a bit confusing which ime often points at the fact that rather than making the implementation match the requirements exactly, you might be better off going back to the drawing board and trying to remove all the edge cases from the specification (things like clocking out without clocking in).

module Main where

import DA.Date
import DA.Time

template SystemUser
    with
        name : Text
        user : Party
    where
        signatory user
        nonconsuming choice CreateAssociate : ContractId AssociateUser
          with
            userName : Text
            associate : Party
          controller user
            do create AssociateUser with
                 name = userName
                 associate = associate
                 systemUser = user

template AssociateUser
    with
        name : Text
        associate : Party
        systemUser : Party
    where
        signatory  systemUser
        observer associate
        nonconsuming choice PunchIn : ContractId RunningAttendance
          controller associate
          do punchInTime <- getTime
             create RunningAttendance with
               systemUser
               associate
               punchInTime
        nonconsuming choice PunchOut : ContractId Attendance
          controller associate
          do punchOutTime <- getTime
             optRunning <- lookupByKey @RunningAttendance (systemUser, associate, toDateUTC punchOutTime)
             case optRunning of
               None ->
                 create Attendance with
                   punchInTime = None
                   ..
               Some runningCid -> do
                 running <- fetch runningCid
                 archive runningCid
                 create Attendance with
                   punchInTime = Some running.punchInTime
                   ..

template RunningAttendance
  with
    systemUser : Party
    associate : Party
    punchInTime : Time
  where
    signatory systemUser, associate
    key (systemUser, associate, toDateUTC punchInTime) : (Party, Party, Date)
    maintainer key._2

template Attendance
    with
        systemUser : Party
        associate : Party
        punchInTime : Optional Time
        punchOutTime : Time
    where
      signatory systemUser, associate

setup = do
   systemUser <- getParty "Alice"
   associatedummy <- getParty "Dummy"
   associate  <- getParty "Bob"
   associate1  <- getParty "Bob1"
   uid <- submit systemUser do
        create SystemUser
            with
                name = "Alice"
                user = systemUser
   asid <- submit systemUser $ create AssociateUser
    with
       name = "Bob"
       associate
       systemUser
   passToDate (date 2020 Sep 27)
   pass (hours 11 + minutes 30)
   submit associate $ exercise asid PunchOut
   passToDate (date 2020 Sep 28)
   pass (hours 5 + minutes 30)
   submit associate $ exercise asid PunchOut
   pass (hours 1)
   submit associate $ exercise asid PunchIn
   pass (minutes 70)
   submit associate $ exercise asid PunchOut
   pass (minutes 75)
   submit associate $ exercise asid PunchOut
1 Like