Employee self-service
GARP HR ships a separate app for everyone who is not HR staff. Your people use it on their phones to punch in and out, apply for leave, check a leave balance, claim an expense and read a salary slip. Managers get the same app with one extra view, Team Requests, and approve their team's paperwork from it.
The app lives at /hrms on your workspace address — for PT Maju Jaya, majujaya.garp.aiconec.com/hrms. It is an installable mobile web app. Everything it writes is an ordinary document that HR sees in the desk: there is no separate self-service store, and every screen in the app is built from the same forms the desk uses.
Do not confuse /hrms with /hr/roster, the shift roster planner in Attendance and shifts. Both are served from the HR app, and the address decides which one you get.
What you probably came here to do
- Give a new member of staff their login for the app
- Punch in or out from a phone, and force people to be at the office to do it
- Apply for leave, or find out why a leave request is being refused
- Claim an expense and attach the receipt photo
- Ask for cash up front before a trip
- Fix a day someone was marked absent by mistake, or ask for a shift
- Approve your team's leave, claims, advances and shift requests
- Use your own approval flow instead of the built-in buttons
- Set the app up for the first time
- Work out why a manager cannot approve anything
- Understand why the Rejected figure on an expense screen looks wrong
- Check what the app does not do for PPh 21, BPJS, THR and lembur
- Look up what a particular screen shows
Give an employee access to the app
- Create a User for the person — see Invite your team. On a brand-new Employee record you can instead tick Create User Automatically, which builds the User from Preferred Email, Company Email or Personal Email and adds the Employee role. If all three are blank it refuses with "Company or Personal Email is mandatory when 'Create User Automatically' is enabled". The checkbox only shows on an unsaved record that has no User ID yet.
- Open their Employee record, set User ID to that user, and save. Saving adds the Employee role to the user if it is missing.
- Confirm Status on the Employee is Active.
- Send them
/hrms. They log in with the email and password they would use for the desk, or with a social login if you have one set up.
Two messages block step 2:
User {0} does not exist— that email has no User record yet.User {0} is already assigned to Employee {1}— another Active Employee already claims that login. An inactive duplicate is allowed, which is how a rehire works.
When someone cannot get in. If the link is missing or the employee is inactive, the app stops at a Login Failed dialog reading "No active employee found associated with the email ID {0}. Try logging in with your employee email ID or contact your HR manager for access." and logs the person out. The check is strict: the app looks for the Employee whose User ID matches the person signing in and whose Status is Active. Anyone on Suspended, Left or Inactive is refused.
Changing Status on an Employee away from Active also disables the linked User account, and setting it back to Active re-enables it. That is a workspace-wide lockout, not just the self-service app. Do not use Status as a leave-of-absence flag.
What staff see on the login screen
The screen reads "Login to HRMS" and shows an email and password form, unless Disable User Pass Login is ticked in System Settings — then the form is replaced by whatever social logins you have.
- Every Social Login Key with Enable Social Login ticked and a client ID, base URL and client secret set appears as a "Login with ..." button that returns to
/hrms. - With neither password nor social login available, the screen reads "No login methods are available. Please contact your administrator."
- An expired password raises a Reset Password dialog that links out to the desk.
- Two-factor logins raise an OTP Verification dialog inside the app. Where OTP setup is incomplete, it falls back to opening the desk login page in a new tab.
The app is not rebranded: the login screen says "Login to HRMS", the browser tab reads HRMS and it installs as HRMS. To install it, open /hrms in the phone's browser and accept the Install HRMS prompt. On iPhone, tap the share icon and then Add to Home Screen — the app prompts for this with a blue banner, since iOS offers no install button.
Check in and out
- On Home, tap Check In. The button alternates with Check Out based on your single most recent log by time, and the line above reads "Last check-in was at 8:47 am" — or "8:47 am yesterday", or "8:47 am on 3 Mar" for older logs.
- A sheet shows the current time, frozen at the moment you opened it. Confirm it.
- View List opens Employee Checkin History, filterable by Log Type and Shift. Tapping a row shows ID, Log Type, Date, Time, Latitude, Longitude and an embedded map.
The button only appears when Allow Employee Checkin from Mobile App is ticked in HR Settings, Shift and Attendance tab, Attendance Settings section. It is on by default. With it off, the panel shows today's date instead and there is no way to punch from the phone.
To make people be at the office. Tick Allow Geolocation Tracking in the same section. The sheet then asks the browser for a location, shows the coordinates and an embedded Google map, and the punch carries them.
Three refusals follow from that:
- No coordinates — the browser denied the request, or the device has no fix — gives "Latitude and longitude values are required for checking in."
- A punch that lands inside a shift for which you have an active Shift Assignment with a Shift Location set gives "You must be within
{0}meters of your shift location to check in." when the great-circle distance exceeds the location's Checkin Radius. A radius of zero or less disables the check. - Two punches of the same Log Type at the same second give "This employee already has a log with the same timestamp." with a link to the existing log.
There is a fourth, easy to hit. When the punch falls inside a shift whose Determine Check-in and Check-out is set to "Strictly based on Log Type in Employee Checkin", a log with no type is refused with "Log Type is required for check-ins falling in the shift: {0}." The app always sends IN or OUT, so this bites integrations rather than the phone.
The radius check reads the first Shift Location among your active assignments for that shift. It does nothing at all when the punch falls outside every shift — an off-shift punch is stored with Off-shift ticked, no shift, and no distance check.
Apply for leave
- Tap Request Leave, or the Leaves tab and Request a Leave.
- Pick the Leave Type. Only types with an allocation covering the chosen date, plus every type with Is Leave Without Pay ticked, are offered.
- Fill From Date. To Date auto-fills to match; change it if the leave runs longer.
- Tick Half Day if it applies. Half Day Date appears, becomes mandatory, and is constrained to the From–To range; on a single-day application it fills itself.
- Total Leave Days and Leave Balance Before Application compute as you type and are read-only.
- Type a Reason, confirm the Leave Approver, attach a doctor's note, and save.
The employee only saves a draft, which is why an application sits at Open until someone acts on it.
Which approver is offered. Leave Approver defaults to the approver on your Employee record, falling back to the first row of your Department's leave approver table. The dropdown offers every approver on your department and its ancestors. It is mandatory when Leave Approver Mandatory In Leave Application is ticked in HR Settings, which it is by default.
What the form hides. On a new application it hides Employee, Employee Name, Department, Company, Follow via Email, Posting Date and Status, and it always hides Series, Salary Slip, Letter Head and the Other Details section. Posting Date is set to today behind the scenes. Employee is stamped from whoever is signed in at save time, so the applicant is always you.
Why a leave request is refused. Saving runs every check the server would run in the desk. The messages you will actually see on a phone:
| Message | Cause |
|---|---|
| "To Date cannot be before From Date" | Shown inline under From Date before you even save |
| "To date cannot be before from date" | The same condition, from the server |
"Insufficient leave balance for Leave Type {0}" | Not enough allocation, and the type is not leave without pay |
| "The day(s) on which you are applying for leave are holidays. You need not apply for leave." | Every day requested is a holiday |
| "Application period cannot be outside leave allocation period" | Dates fall outside the allocation |
| "Application period cannot be across two allocation records" | The range straddles two allocations |
| "Backdated Leave Application is restricted. Please set the ..." | Restrict Backdated Leave Application is on with no role named |
"Only users with the {0} role can create backdated leave applications" | Same setting, role named, you lack it |
"Salary already processed for period between {0} and {1}, Leave application period cannot be between this date range." | Payroll has closed those days |
"Attendance for employee {0} is already marked for the following dates: ..." | Submitted Attendance already exists with status Present or Work From Home |
Claim an expense
- Tap Claim an Expense. The form opens with three tabs.
- Expenses holds the Expense Approver, a Currency section, the expense rows and the taxes table. Tap the plus icon to open New Expense Item: Expense Date (today), Expense Claim Type, Description, Amount, Sanctioned Amount, Cost Center and Project. The Add Expense button stays disabled until Expense Claim Type and Amount are filled. Typing an Amount copies it into Sanctioned Amount; picking a type copies that type's description into Description if you left it blank.
- Advances lists your outstanding Employee Advances and shows the amount the app has allocated against them.
- Totals holds the read-only totals, exchange gain/loss, Posting Date, Bank / Cash Account, Payable Account, Project and Cost Center.
- Attach the receipt photo and save.
Where the currency and accounts come from. Currency is fetched from the Salary Currency on your Employee record, and the Exchange Rate from that currency to the company currency. Cost Center and Payable Account default from the Company's Cost Center and Default Expense Claim Payable Account. Changing the claim's cost centre rewrites it onto every expense row.
How advances are used up. Advances allocate themselves. The app takes sanctioned plus taxes and walks your unclaimed advances oldest first, filling each row up to its unclaimed amount until the figure is covered, ticking the rows it used. Only ticked rows are sent when you save. Grand Total is sanctioned + taxes − advances, and a tax row's amount is rate ÷ 100 × total sanctioned amount, added on top.
Two refusals come from the server:
- "Sanctioned Amount cannot be greater than Claim Amount in Row
{0}." - "Total advance amount cannot be greater than total sanctioned amount"
Attaching the receipt. The attachment picker accepts anything, but the upload does not: anything outside JPG, PNG, PDF, TXT and Microsoft documents comes back as a message reading "File upload failed for {0}. You can only upload JPG, PNG, PDF, TXT or Microsoft documents." Uploaded files are private. On a new document, files you pick are held in the browser and uploaded only after the document is created.
Status, Delivery Trip and Vehicle Log sit after Cost Center in the form layout and therefore fall outside all three tabs — the app never shows them. If you need to see a claim's Paid or Unpaid status, look at the list, which shows it as a badge, or open the claim in the desk.
Request an advance
Tap Request an Advance. The form asks for Posting Date (today), Purpose, Advance Amount, Currency, Advance Account, Mode of Payment and Repay Unclaimed Amount from Salary.
Currency is mandatory and nothing fills it in for you. Only Paid Amount carries the currency in its label here, and it is read-only and hidden until the advance is actually paid — the "(undefined)" labels you may have seen come from an expense claim's Totals tab, where all five totals are currency-suffixed.
Submitting also needs an advance account. With none on the document, the server takes the Company's Default Employee Advance Account when the claim currency matches the company currency. Otherwise you get one of three refusals:
- "Advance Account is mandatory. Please set the Default Employee Advance Account in the Company
{0}and submit this document." - "Advance Account
{0}currency should be same as Salary Currency of Employee{1}. ..." — the account currency does not match. - "Employee advance account
{0}should be of type Receivable." — the account is the wrong type.
There is no approver field on Employee Advance, so nothing is shared and no notification is sent. Someone with Expense Approver must find and submit it.
Request attendance or a shift
Attendance Request is how a day gets marked when the punches are missing or wrong. It asks for From Date, To Date, Half Day with Half Day Date, Reason (Work From Home or On Duty, mandatory), Explanation, Shift and Include Holidays. The server refuses overlapping requests, refuses a half-day date outside the range with "Half day date should be in between from date and to date", and picks your shift automatically when you have exactly one active assignment. With two it throws "There are multiple shifts assigned to the employee for the same period. Please mention the shift".
Shift Request asks for Shift Type, From Date, To Date and Approver. The approver list comes from your Shift Request Approver field, then your Department's shift request approver table, then the approvers on ancestor departments; the first entry is pre-selected. With none of those set, the field is empty and mandatory, and saving fails with "Approver field is mandatory".
Neither form lets an ordinary employee submit. Attendance Request has no approver field at all, so no share is created and no notification is sent — HR has to go looking.
Approve your team's requests
- On Home, switch the panel to Team Requests, or open a list and use its Team Leaves, Team Claims, Team Requests or Team Advances tab.
- Tap a request. The sheet shows its summary fields and attachments, and an external-link icon opens the full form.
- For a Leave Application, tap Approve or Reject. Submit replaces them once the status is Approved or Rejected.
Approve and Reject in the request sheet only ever appear for Leave Applications. For Expense Claims, Shift Requests and Attendance Requests the app asks the server for an "approval" right that does not exist, so the buttons never appear. Approve those by tapping the external-link icon in the sheet header, setting Approval Status (Expense Claim) or Status (Shift Request) on the full form, saving, and then tapping Submit. Attendance Requests have no status to set — they go straight to Submit.
Also note that the Team tabs in the list views are not approval queues. Unlike the Home panel, they filter on nothing but "employee is not me", so they show every non-cancelled document of that type you are permitted to read, draft or submitted.
Stopping people approving their own paperwork. Two HR Settings checkboxes do this. Both are off by default, both sit on their own tab, and both are skipped entirely when a Workflow exists on that kind of document:
- Prevent self approval for leaves even if user has permissions (Leaves tab). GARP refuses to save it: setting your own leave to Approved fails immediately with "Self-approval for leaves is not allowed". This is the one setting the app also reads, so it hides your own Approve and Reject buttons rather than letting you press them.
- Prevent self approval for expense claims even if user has permissions (Expenses tab). This one is only checked at the moment you press Submit, and is never sent to the app — so you can set Approval Status to Approved and save, and only discover "Self-approval for Expense Claims is not allowed" when you submit.
Attendance Requests have no approver field, so nothing is shared. Other people's draft attendance requests appear only to someone who can already read them: HR User, HR Manager, System Manager, or a user permission that grants it.
If Approve and Reject produce an "Approval failed!" message for a manager, the cause is almost always the access restriction described in Who is allowed to do what.
Approving through a Workflow
Define a Workflow on Leave Application, Expense Claim, Shift Request or Attendance Request and the app changes behaviour without any further configuration:
- The status badge on lists and forms switches to the workflow state, unless Don't Override Status is ticked on the workflow.
- The Approve/Reject/Submit/Cancel buttons are replaced by the transitions available to your roles. In the request sheet, two or fewer transitions appear as buttons and three or more collapse into an Actions button; on the full form it is always an Actions button. Transitions whose action name contains "approve" turn green, "reject" or "cancel" turn red.
- Transitions with Allow Self Approval unticked are dropped when the document is your own.
- The Team Requests panel stops filtering on approver and status, and instead shows drafts whose workflow state matches a transition your roles are allowed to make.
- Fields become read-only when the current state's Allow Edit role is not one of yours.
Workflows are built in the desk at /app/workflow. Nothing about them is configurable from the app.
Set up the app for the first time
- A User per employee, with User ID filled on an Active Employee record, and Create User Permission decided deliberately for approvers.
- Leave Approver and Expense Approver on each Employee, or the Department approver tables. Add Shift Request Approver too if you want shift requests to reach anyone.
- The Expense Approver role on whoever handles Employee Advance, since HR User and HR Manager cannot see it.
- A Holiday List assigned through Holiday List Assignment, so the Leaves tab and the calendar have something to show.
- In HR Settings, Shift and Attendance tab: Allow Employee Checkin from Mobile App, plus Allow Geolocation Tracking and a Shift Location with a Checkin Radius on the shift assignment to enforce distance.
- A Payroll Period, if staff should see salary slips.
- Leave allocations, or the Leave Balance panel reads "You have no leaves allocated".
- Salary Currency set to IDR on every Employee record.
push_relay_server_urlin the site configuration and Push Notification Settings enabled, if you want push as well as the in-app bell.
Which modules are available depends on the plan — see Plans and limits.
Worked examples
Sari Dewi punches from the wrong side of Sudirman
PT Maju Jaya has a Shift Location for the Jakarta office with a Checkin Radius of 100 metres, named on Sari Dewi's active Shift Assignment for the Office Day shift. She punches in from a café 260 metres away.
The document works out the great-circle distance between the shift location and her coordinates, compares it against 100, and refuses: "You must be within 100 meters of your shift location to check in." Nothing is saved.
The same punch made at 06:15, before the shift's check-in window opens, matches no shift at all. The log saves with Off-shift ticked and no Shift, the location lookup finds no assignment for a blank shift, and the radius is never checked — she is let in from anywhere. Tighten Begin check-in before shift start time (in minutes) on the Shift Type if that matters to you.
Budi Santoso's expense summary does not add up
Budi has three claims this quarter, all in IDR:
- Claim A, submitted and Approved: claimed IDR 1,500,000, sanctioned IDR 1,200,000 after his manager trimmed a client lunch.
- Claim B, still a draft: claimed IDR 400,000.
- Claim C, Rejected: claimed IDR 250,000.
The Expense Claim Summary on his phone reads:
Total Claimed Amount IDR 2,150,000 = 400,000 + 1,500,000 + 250,000
Pending IDR 400,000 = draft claims, at claimed value
Approved IDR 1,200,000 = approved claims, at sanctioned value
Rejected IDR 550,000 = 250,000 + (1,500,000 - 1,200,000)
An Indonesian practitioner reading "Rejected" expects IDR 250,000 — the value of the one rejected claim. The product produces IDR 550,000, because the tile is built to make the three figures sum back to Total Claimed Amount, and so it absorbs the IDR 300,000 trimmed off Claim A. If you need the plain rejected figure, run the claim list in the desk and total Total Claimed Amount filtered on Approval Status Rejected.
Rina Halim settles a site visit against an advance
Rina holds a submitted Employee Advance with Paid Amount IDR 2,000,000 and nothing claimed. She claims two receipts: transport IDR 750,000 and materials IDR 500,000, both sanctioned in full, no taxes.
The app computes:
Total Claimed Amount IDR 1,250,000
Total Sanctioned Amount IDR 1,250,000
Amount to allocate 1,250,000 + 0 taxes = IDR 1,250,000
Advance row unclaimed 2,000,000 >= 1,250,000
-> Allocated Amount 1,250,000, row ticked
Total Advance Amount IDR 1,250,000
Grand Total 1,250,000 + 0 - 1,250,000 = IDR 0
Her approver sets Approval Status to Approved and submits. Because the grand total is zero and the sanctioned amount is positive, the claim's Status goes straight to Paid — no payable line, no reimbursement, nothing for finance to pay. The advance's Claimed Amount becomes 1,250,000, leaving IDR 750,000 of company money still with Rina, which she settles with a return or through Repay Unclaimed Amount from Salary. See Expense claims.
Indonesian practice
There is no Indonesian regional module in the HR app. It ships regional modules for India and the United Arab Emirates only, and setting a Company's Country to Indonesia silently skips the regional setup step — no regional custom fields and no tax slabs.
Salary components are still created: the standard Basic, Arrear, Leave Encashment and Income Tax, plus the Indonesian components this build installs automatically — Gaji Pokok, Masa Kerja, Kehadiran, Potongan, BPJS TK and Upah Lembur — along with the Lembur GARP Overtime Type and the Overtime Request Approver field on Employee and Department. Everything beyond that is configuration you do, not compliance you get.
- PPh 21 and BPJS. The Income Tax Breakup tab on a payslip shows generic income-tax fields fed by whatever Income Tax Slab and salary components your consultant built. Nothing in the product computes PPh 21 or BPJS Kesehatan, and there are no PTKP/TER tax slabs. The one statutory-looking deduction it does ship is BPJS TK, installed automatically with the flat formula
(2 * (GP + TMK)) / 100— a placeholder, not a JHT/JP/JKK/JKM split. What an employee sees under Earnings and Deductions is those shipped components plus whatever you created yourself. See Tax and benefits. - Overtime. Lembur is raised on an Overtime Request, and the Employee role can create one — but there is no Overtime screen anywhere in
/hrms. Staff must use the desk, or you route the request through another channel and have a supervisor enter it. See Overtime. - THR. Nothing in the app. Pay it as an Additional Salary or a component, and it will appear on the payslip like any other earning.
- PKWT and PKWTT. Contract type lives in Employment Type and Contract End Date on the Employee record. Employment Type shows on the Profile screen under Company Information; the contract end date does not.
- Statutory leave. Cuti tahunan, cuti melahirkan and cuti besar are Leave Types you create. The app offers whatever has an allocation, under whatever name you gave it, so name them in Bahasa if that is what staff expect to see.
- Currency and number format. Set Salary Currency to IDR on every Employee record — that is what an expense claim's Currency fetches from, and a blank one leaves currency labels reading "(undefined)". Number formatting is done by the phone's browser, but the app never loads the System Settings record it means to read the format from, so the grouping follows the phone's own language setting. Set the device language to Indonesian if you want
Rp 12.500.000rather thanRp 12,500,000. - India-only fields. The four India fields on the Profile's Salary Information group cannot be removed from the app; they are fixed into the screen. They will always show as blank-label dashes on an Indonesian workspace.
What each screen shows
Everything from here down is reference. You do not need it to use the app — read it when a screen shows something you did not expect.
Five tabs run along the bottom of the app, plus a header with the page title, a notification bell and an avatar. The bell carries a red dot whenever you have unread notifications; the avatar opens Profile.
Home
A greeting reading "Hey, {first name}", the check-in panel, six Quick Links — Request Attendance, Request a Shift, Request Leave, Claim an Expense, Request an Advance, View Salary Slips — and a My Requests / Team Requests panel.
The panel merges Leave Applications, Expense Claims, Shift Requests and Attendance Requests into one list, sorts by creation date newest first, and keeps the newest ten. Each of the four sources is fetched with its own limit of ten, so a busy month of expense claims can push your leave applications out of the panel entirely; open the individual tab to see the rest.
- My Requests filters on your employee and excludes only cancelled documents.
- Team Requests filters on drafts belonging to someone else, and narrows further to: Leave Applications with status Open naming you as Leave Approver; Expense Claims whose Status is Draft naming you as Expense Approver — that is the read-only Status field, not Approval Status, so it matches every unsubmitted claim; and Shift Requests with status Draft naming you as Approver.
- Attendance Requests get no approver narrowing at all. Every draft attendance request you are permitted to read appears there.
Attendance
A month calendar colouring each day, buttons to request attendance or a shift, the last five of each request type, and upcoming shifts.
The calendar reads submitted Attendance records first and falls back to the employee's Holiday List for days with no attendance. Colours: Present and Work From Home green, Half Day yellow, Absent red, On Leave blue, Holiday grey. The four counters underneath are Present, Half Day, Absent and On Leave — Work From Home is folded into the Present count, and holidays are not counted at all. Draft attendance is invisible here; only submitted attendance records are read.
Upcoming Shifts lists submitted Shift Assignments with Status Active, showing only those with no End Date or an end date after today, five at most. An assignment that is running now counts as upcoming.
Leaves
Titled Leaves & Holidays. A Leave Balance dial per leave type that has an allocation covering today, Request a Leave, your ten most recent applications, and Upcoming Holidays.
Each dial shows balance/allocated and fills to balance ÷ allocated × 100. Balance is the remaining leave counting everything taken across the whole allocation period, not just up to today. Leave types with no allocation never appear, which is why the panel reads "You have no leaves allocated" on a fresh workspace.
Upcoming Holidays lists the next five holidays from the employee's Holiday List with Weekly Off unticked, so Sundays do not clutter it. View All opens the full list including past dates, greyed.
Expenses
Titled Expense Claims. An Expense Claim Summary, Claim an Expense, your ten most recent claims, and Employee Advance Balance.
The summary counts every claim of yours that is not cancelled, in the company's default currency:
| Tile | What it sums |
|---|---|
| Total Claimed Amount | Pending + the claimed amount of approved claims + the claimed amount of rejected claims |
| Pending | Total Claimed Amount of claims whose Approval Status is Draft |
| Approved | Total Sanctioned Amount of claims whose Approval Status is Approved |
| Rejected | Claimed amount of rejected claims plus the amount trimmed off approved claims |
That last row surprises people. See Budi Santoso's expense summary does not add up.
Employee Advance Balance lists submitted advances with a paid amount whose status is Paid or Unpaid, newest first, each showing paid − (claimed + returned). Fully claimed or returned advances drop off the list.
Salary
Titled Salary Slips. A Year To Date figure, a Payroll Period selector, and that period's submitted slips.
The selector lists every Payroll Period for the employee's company, newest first, labelled as MMM YYYY - MMM YYYY, and auto-selects the first. The list shows only your own slips, only submitted ones, and only those whose Start Date falls inside the selected period. The Year To Date figure is taken from the newest slip in the selected period — pick last year's period and the YTD number is last year's, not today's.
Opening a slip gives five tabs. The app builds them by cutting the Salary Slip form at fixed points:
| Tab | Contains |
|---|---|
| Details | Employee Info and Payroll Info, then Working Days, Unmarked days, Leave Without Pay, Absent Days and Payment Days |
| Earnings & Deductions | The two component tables inside the form, each row showing Salary Component and amount, plus the gross and total deduction figures |
| Net Pay Info | Net Pay, Rounded Total, Year To Date, Month To Date, Total in words |
| Income Tax Breakup | CTC, Income from Other Sources, Total Earnings, Non Taxable Earnings, Standard Tax Exemption Amount, Tax Exemption Declaration, Deductions before tax calculation, Annual Taxable Amount, Income Tax Deducted Till Date, Current Month Income Tax, Future Income Tax, Total Income Tax |
| Bank Details | Journal Entry, Bank Name, Bank Account No |
Download PDF renders the Salary Slip's default print format, or the Standard format if none is set, and fails with "Failed to download PDF" if the render breaks. Timesheet fields are stripped unless the slip actually has timesheets, and company-currency duplicates of every amount are hidden when the slip's currency already equals the company currency.
Profile
Four read-only groups opened from the avatar, each a fixed list of Employee fields:
| Group | Fields |
|---|---|
| Employee Details | Full Name, Employee Number, Gender, Date of Birth, Date of Joining, Blood Group |
| Company Information | Company, Department, Designation, Branch, Grade, Reports to, Employment Type |
| Contact Information | Mobile, Personal Email, Company Email, and a fourth row that is always blank — the screen asks for a field spelled preferred_email while the Employee record spells it prefered_email, so nothing is ever found |
| Salary Information | Cost to Company (CTC), Payroll Cost Center, PAN Number, Provident Fund Account, Salary Mode, Bank Name, Bank A/C No., IFSC Code, MICR Code, IBAN |
Any field with no value shows as a dash. PAN Number, Provident Fund Account, IFSC Code and MICR Code are custom fields created only by the India regional setup, so on an Indonesian workspace those four rows come up with a blank label and a dash. Below the groups sit Settings (only when push notifications are available) and Log Out.
Notifications
The bell opens a list of your notifications, ten at a time with Load more, each linking to the referenced Leave Application or Expense Claim. Mark all as read clears the badge.
PWA Notification grants read to the System Manager and Employee roles and write to neither, so tapping a single notification cannot clear its own unread flag. The blue dot and the red badge clear only through Mark all as read, which runs a server action that writes the flag directly.
Push notifications on top of the in-app bell need two things: a push_relay_server_url in the site configuration, and Enable Push Notification Relay ticked in Push Notification Settings. Without both, the Settings entry under Profile does not appear at all and the toggle is unreachable. The in-app bell works regardless.
The records behind the app
Everything the app shows is one of these records, which HR sees in the desk under the same names.
- Employee (
/app/employee) — the record the app hangs off. It looks up the Active employee whose User ID is the person signed in; nothing else identifies you to the app. - Employee Checkin (
/app/employee-checkin) — one punch, IN or OUT, with a timestamp and optional latitude and longitude. - Leave Application, Expense Claim, Employee Advance, Attendance Request and Shift Request — the five things an employee can create here. All five have to be submitted before they take effect.
- Shift Assignment, Salary Slip, Holiday List, Attendance — read-only here.
- PWA Notification (
/app/pwa-notification) — the bell icon. One goes to the approver when a Leave Application, Expense Claim or Shift Request is raised, and one back to the employee when its status turns Approved or Rejected. Attendance Requests and advances produce none. - Workflow — if you define one on Leave Application, Expense Claim, Shift Request or Attendance Request, the app drops its own Approve and Reject buttons and shows your workflow transitions instead.
- HR Settings (
/app/hr-settings) — mobile check-in, geolocation, self-approval blocks, and whether an approver is mandatory.
The app reads exactly three HR Settings values for itself: Allow Employee Checkin from Mobile App, Allow Geolocation Tracking and Prevent self approval for leaves even if user has permissions. Two more, Leave Approver Mandatory In Leave Application and Expense Approver Mandatory In Expense Claim, reach the forms through the approver lookup. Everything else in HR Settings still applies, but only when the server checks the document — the app cannot warn you in advance.
What happens when you submit
Submitting happens either from the app — the Submit button, then a "Permanently submit {0}?" confirmation — or from the desk. The effects are the same either way.
Leave Application. Refuses with "Only Leave Applications with status 'Approved' and 'Rejected' can be submitted" while the status is Open or Cancelled. On an approved application it marks or overwrites Attendance for each leave day, writes a negative Leave Ledger Entry consuming the balance, and emails the applicant when Send Leave Notification is ticked. Cancelling reverses the ledger entry and cancels the attendance it created.
Expense Claim. Refuses with "Approval Status must be 'Approved' or 'Rejected'" while Draft, and with "Payable Account is mandatory to submit an Expense Claim" when there is no payable account and Is Paid is unticked. On submit it posts GL entries, updates the linked task and project, writes back Claimed Amount on every allocated Employee Advance, and sets Status to Paid or Unpaid depending on whether the grand total is already covered.
Employee Advance. Posts nothing. The status moves to Unpaid, and money only moves when someone raises a Payment Entry or Journal Entry against it in the desk.
Attendance Request. Creates one Attendance record per day in the range, with Include Holidays deciding whether holidays are covered. Cancelling cancels those Attendance records.
Shift Request. Refuses with "Only Shift Request with status 'Approved' and 'Rejected' can be submitted". On Approved it creates and submits a Shift Assignment for the same employee, shift type and dates, ignoring permissions, and reports "Shift Assignment: {0} created for Employee: {1}". That assignment is what then appears under Upcoming Shifts. Cancelling the request cancels the assignment.
Employee Checkin does not need submitting. A punch takes effect the moment it saves, and becomes Attendance only when auto attendance runs.
Who is allowed to do what
The app has no access rules of its own. Every button it shows is decided by three answers from the server: what your roles may do with that kind of record, which fields your roles may edit, and whether a User Permission blocks that particular document.
Role permissions on the five records
| Record | Employee role | Approver role | HR User | HR Manager |
|---|---|---|---|---|
| Leave Application | read, write, create | Leave Approver: read, write, submit, cancel, delete | full | full |
| Expense Claim | read, write, create | Expense Approver: full | full | full |
| Employee Advance | read, write, create | Expense Approver: full | none | none |
| Attendance Request | read, write, create, delete | none | full | full |
| Shift Request | read, write, create | none | read, write, create, submit | full |
| Employee Checkin | read, write, create, delete | none | full | full |
"Full" means create, write, submit, cancel, amend and delete. The gaps matter:
- The Employee role has no submit on anything. An employee saves a draft; someone else submits it.
- Employee Advance is invisible to HR User and HR Manager unless they also hold Expense Approver. Add that role to your finance staff or they cannot see advances at all.
- Shift Request has no approver role. HR User or HR Manager must act on it.
- The Employee role can delete its own Employee Checkin records at the desk. The app never offers that, but the permission is there.
Two fields are locked to senior roles, so an employee can see them but never change them: Status on Leave Application, editable only by Leave Approver, HR User and HR Manager; and Approval Status on Expense Claim, editable only by Expense Approver, HR User and HR Manager. Time on Employee Checkin is locked the same way, which is why an employee can create a punch but never edit its timestamp.
Naming someone in Leave Approver or Expense Approver on an Employee record and saving it adds the matching Leave Approver or Expense Approver role to that user automatically. Shift Request Approver and Overtime Request Approver grant no role — those two approvers rely entirely on the document share.
Document shares and User Permissions
Saving a Leave Application, Expense Claim or Shift Request that names an approver shares the document with that user with submit rights, and shows the alert "Shared document with the user {0} with 'Submit' permission". Changing the approver removes the previous share. The share carries read and submit — not write.
Separately, saving an Employee record that has a User ID with Create User Permission ticked (the default; its own description reads "This will restrict user access to other employee records") creates User Permissions on Employee and Company for that user. That is what stops staff reading each other's payslips.
Those two mechanisms fight each other for managers. A User Permission on Employee blocks access to any request belonging to someone else — regardless of role — and the share only grants read and submit. So a manager who is themselves an Employee with a User ID can see a team member's request in the list (list queries add shared documents) but cannot write to it: Approve and Reject fail with an "Approval failed!" message. Fix it in one of three ways: untick Create User Permission on the approvers' own Employee records, add a User Permission on each direct report's Employee record to the manager's user, or delete the manager's Employee User Permission by hand at /app/user-permission.
The HR app adds no further hidden visibility rules on top of these. What a person can see is decided by role permissions, User Permissions and document shares — nothing else.
Jobs that run on their own
None of these are self-service jobs, but all of them change what an employee sees when they next open the app.
| Job | How often | Effect in the app |
|---|---|---|
Advancing Last Sync of Checkin on shifts (shift_type.update_last_sync_of_checkin) | Hourly (long queue) | Moves the marker forward on shifts that have Automatically update Last Sync of Checkin ticked, which is what makes the next job eligible to run |
Auto attendance for all shifts (shift_type.process_auto_attendance_for_all_shifts) | Hourly (long queue) | Turns punches into Attendance, which is what colours the Attendance calendar |
Auto shift creation (shift_schedule_assignment.process_auto_shift_creation) | Hourly (long queue) | Creates Shift Assignments that then appear under Upcoming Shifts |
Marking expired shift assignments inactive (shift_assignment.mark_expired_shift_assignments_as_inactive) | Daily | Flips ended assignments to Inactive, removing them from Upcoming Shifts |
Expiring leave allocations (leave_ledger_entry.process_expired_allocation) | Daily (long queue) | Expires allocations, dropping the leave balance dials |
Allocating earned leave (hr.utils.allocate_earned_leaves) | Daily (long queue) | Adds earned leave, raising the dials |
Generating leave encashment (hr.utils.generate_leave_encashment) | Daily (long queue) | Creates Leave Encashment where the leave type allows it |
Birthday and work anniversary reminders (employee_reminders.send_birthday_reminders and send_work_anniversary_reminders) | Daily | Emails only. Neither creates a PWA Notification, so nothing appears under the bell |
The app also refreshes itself as things change: check-ins, team requests, the employee's own profile and salary slips all reload without a pull-to-refresh when the underlying document changes.
Details, limits and edge cases
- Only submitted salary slips appear, and only inside a Payroll Period for the company. With none defined, the Salary tab reads "No salary slips found".
- The app does not evaluate the rules that hide a field until another field is filled. Fields the desk hides until a condition is met — an expense claim's Bank / Cash Account, an expense claim's exchange rate — are shown unconditionally. Read-only fields with no value are hidden instead.
- The Employee role has only read on Shift Assignment: staff see their shifts and raise a Shift Request, HR creates the assignment.
- A submitted or cancelled document is entirely read-only in the app, and Delete disappears once a document is submitted.
- Opening someone else's Leave Application makes every field read-only unless you are the named Leave Approver. The same is true of Attendance Request with no exception, since it has no approver.
- Time fields have no editor in the app. Any field of type Time shows as a label with nothing under it.
- There is no search. Lists offer a fixed filter sheet per record type — status, type, employee, department and dates — and infinite scroll at 50 rows a page.