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,
- System user create.
- System user creates associates.
- Attendance Template created
- 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:
- 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.
- 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.
- 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
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