Production companion Android app and enterprise workforce system for Artify HCMS with Oman 360° HR records, multi-site geofenced mobile attendance, leave management, project timesheets, OMR 3-decimal
翻译准备中
商业TypeScript · PLpgSQLGitHub @usmanclaude92-creator在线运营者未确认
试用过了吗? 点一下就好,无需登录。
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Production companion Android app and enterprise workforce system for Artify HCMS with Oman 360° HR records, multi-site geofenced mobile attendance, leave management, project timesheets, OMR 3-decimal payroll, and employee ledger balances.
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
Employee, attendance, payroll, WPS and Oman HR compliance management. Currency is OMR at
three decimal places throughout.
• Frontend — React 19 + Vite + Tailwind, TypeScript
• Backend — Express, TypeScript, mounted both as a long-running server and as a Vercel
serverless function from the same app factory (server/app.ts)
• Store — PostgreSQL. See Data storage — the current shape is a known
constraint, not a finished design.
• Files — Supabase object storage for employee documents and payment receipts查看完整 README ↗Employee, attendance, payroll, WPS and Oman HR compliance management. Currency is OMR at three decimal places throughout.
server/app.ts)npm install
cp .env.example .env # then fill in the values below
npm run dev # http://localhost:3000
With no DATABASE_URL set, the app runs against a local JSON file at
data/payroll_database.json and seeds a demonstration dataset (five employees, one
finalized payroll, sample loans and compliance documents) plus development sign-in
accounts. This never happens when NODE_ENV=production.
npm run lint # tsc --noEmit
npm test # payroll arithmetic, expiry rules, IBAN generation
npm run build # vite build + esbuild server bundle
| Variable | Required | Purpose |
|---|---|---|
DATABASE_URL | Production | PostgreSQL connection string. Use the transaction pooler on a serverless host, not the direct connection. POSTGRES_URL and POSTGRES_URL_NON_POOLING are also accepted. |
JWT_SECRET | Always | Signing secret for session tokens. The server refuses to start without it. |
ADMIN_INITIAL_PASSWORD | First production start | Password for the first administrator. Required when the database has no user accounts and NODE_ENV=production; the server refuses to start otherwise rather than create a known default. |
ADMIN_INITIAL_USERNAME | Optional | Defaults to admin. |
ADMIN_INITIAL_EMAIL | Optional | Defaults to [email protected]. |
SUPABASE_URL | Production | Object storage endpoint. |
SUPABASE_SERVICE_ROLE_KEY | Production | Object storage credentials. |
SUPABASE_DOCUMENTS_BUCKET | Optional | Defaults to employee-documents. |
SUPABASE_RECEIPTS_BUCKET | Optional | Defaults to salary-payment-receipts. |
GEMINI_API_KEY | Optional | Enables the compliance assistant. Without it, a deterministic rule-based responder is used instead. |
WORKFORCE_FUNCTIONS_URL / WORKFORCE_INTEGRATION_SECRET / SUPABASE_ANON_KEY | Optional, but all three are required together | Links to the Artify Workforce app by Civil ID (see "Workforce app integration" below): populates "Shift Start"/"Shift End" on the Workforce Deployment dashboard, and lets Administrators sync eligible employees over so they can register there. Without all three set, cards show "Not Tracked" and the sync endpoint returns 400 -- there is no fallback URL/key/secret baked in. |
DB_CONNECT_TIMEOUT_MS | Optional | Database connect timeout, default 10000. |
ALLOW_DEMO_SEED | Optional | Set to false to suppress the demonstration dataset outside production. |
Bootstrap the first administrator, then change the password through the UI:
ADMIN_INITIAL_PASSWORD='<a strong password>' npm start
All application data currently lives in a single JSONB row (app_state), not in the
per-entity tables the schema creates. Every mutation rewrites that row under an optimistic
version check.
Consequences to be aware of before building on this:
server/app.ts refreshes it
from the durable store on every /api request — unconditionally before a mutation, and
within a 1.5s freshness window for reads — because without that, a record written through
one instance stayed invisible on every other one for the life of that instance, and two
browser refreshes could show different data.Write failures throw. A request that returns success means the data was committed. If a configured database is unreachable the server refuses to start rather than fall back to local storage, because that fallback previously caused demonstration data to be served as though it were production data.
Migrating each repository in server/db.ts onto real tables — starting with the
append-only financial records — is the highest-value structural work outstanding.
| Area | Notes |
|---|---|
| Employee Master | Core record, bank details, Excel import/export with server-side re-validation |
| HR Compliance | Civil ID, visa, driving licence and government documents with expiry tracking and renewal history |
| Document Repository | Uploads to object storage, per-employee and organisation-wide views |
| Attendance | Multi-project day/hour allocation, four-stage workflow, Excel import |
| Timesheets | Per-entry labour capture for project costing. Does not feed payroll. |
| Payroll | Monthly calculation, per-line overrides, finalisation lock, reasoned revision |
| Leave | Request/approve/reject/cancel workflow; approved paid leave feeds payable days in payroll |
| Salary Payments | Partial payments against a finalized entitlement, receipts, soft reversal |
| Payment Planning | Intended "should pay" figures. Never creates a payment. |
| WPS Recovery | Tracks recovery of the excess between declared WPS salary and net pay |
| Loans | Advances with payroll-driven and direct recovery |
| CIF | Bank file upload and reconciliation against payroll totals |
| Gratuity / End-of-Service | Oman Labour Law RD 53/2023 Art. 61 gratuity computation, non-Omani employees |
| Reports | Salary & payroll, payments, WPS, loans, project costing, employee ledger |
| Audit Trail | Action log across modules, retained to 5000 entries |
Worker Gross = hoursWorked × rate
Staff Gross = (monthlySalary ÷ 30) × min(daysWorked, 30)
Net = Gross + Additions − Deductions
Recoverable = max(wpsSalary − Net, 0)
Finalisation locks the month, posts loan recoveries and opens WPS recovery records. Correcting a finalized month requires an explicit revision, which reverses those postings and refuses outright where a WPS recovery has already received money.
Finalisation requires the month's attendance to be Approved. It can be overridden with a recorded reason, which is written to the audit trail.
These are absent by design at present and should not be assumed to work:
Leave management, overtime pay, pro-rata for mid-month joiners/leavers, and end-of-service gratuity are all implemented (see Modules above) — this list previously claimed they were missing, which was stale.
An account can be restricted to a set of companies (companyScope, managed in Users).
companyScopeOf() / canSeeCompany() (server/auth.ts) enforce this on list, single-record
and export endpoints across employees, attendance, payroll, payments, loans, WPS, leave,
gratuity, reports and document storage. This used to be a total gap ("every authenticated
user can see every company's data"); it no longer is, but treat any new endpoint that
reads employee-linked data as scoped-by-default and add the same check rather than assuming
it inherits isolation automatically.
Artify Workforce (a separate Android app + Supabase project, repo staff) tracks selfie-
verified clock-in/out. The two systems are linked by Civil ID, since that is the only
identifier that reliably exists in both -- not HCMS Employee ID, and not Workforce's own
internal UUID.
civil_id_lookup table is an eligibility whitelist: a worker can only
register in the app with a Civil ID that appears there. POST /api/workforce/sync-eligibility
(Administrator only) populates it from this app's active employees, keyed by
db.civilIds.getCurrent(employeeId). An employee with no current Civil ID on file is
skipped -- add one first.GET /api/workforce/shift-status (used by the Workforce Deployment dashboard) looks up
today's clock-in/out for those same Civil IDs and re-keys the result onto HCMS Employee ID
for the frontend.shift-status,
sync-eligibility), authenticated with a shared secret (WORKFORCE_INTEGRATION_SECRET)
that Workforce verifies against a hash in its own integration_secrets table -- the
plaintext secret is never stored there.projects table) or clock-ins there will be geofence-checked
against the wrong location.Employee.assignedProjectCode -> Project.projectCode), not free text. An employee
with nothing selected there is synced without a project (older records that only have
the legacy free-text assignedProject personal-details field are still read as a
fallback until re-saved through Employment Details).WORKER by default -- there's no HCM field for
Workforce-specific supervisor authority today, so promoting someone to SUPERVISOR is a
manual edit in Workforce's own employees table.| Role | Capability |
|---|---|
| Administrator | Everything, including user administration |
| Payroll Manager | Everything except user administration; may finalise and revise payroll |
| Payroll User | Day-to-day data entry; cannot finalise payroll or delete documents |
| Viewer | Read and export only |
Granular permissions are defined in src/permissions.ts and enforced server-side. Hiding a
control in the UI is never the access check.
Vercel, built with vite build; api/index.ts serves all /api/* routes and
vercel.json rewrites everything else to the SPA.
Before the first production deploy, confirm:
JWT_SECRET is set to a value not used anywhere elseADMIN_INITIAL_PASSWORD is set, and changed through the UI after first sign-inDATABASE_URL uses the transaction poolernpm run lint and npm test both pass