Lalabase
Tickets, Doku, Zeiterfassung und Kundengespräche an einem Ort, durchsuchbar per KI. Für Softwarefirmen mit Kundenprojekten. Open Source: auf deinem eigenen Server oder in unserer Cloud in Deutschland.
翻譯準備中
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