Skip to main content

Claims & Expense Reimbursement (CLM)

Module code CLM · Release R2 (mileage from FFM in R3) · Packaging Plan tier (Professional+) · Requirements FR-CLM-*, BR-CLM-*, RULE-CLM-* in the PRD and BRD · Related Payroll, Field Force, claim-to-reimburse

1. Overview and scope​

CLM lets employees claim business expenses and get paid, under a policy that finance controls. One pipeline handles every reimbursement: bills, per-diem, and mileage generated from field travel.

In scope: expense categories; an Expense Policy (limits per category, per grade, per city tier, per day, per month; receipt thresholds; per-diem and mileage rates); multi-bill claims with attachments; GST fields; duplicate-bill detection; line-level verification with verified amounts; finance approval; payout via payroll (non-taxable reimbursement component) or a separate bank file; advances against claims and their settlement; a claims ledger; finance dashboard.

Out of scope: corporate card feeds, OCR (later add-on), travel booking.

2. Learned from the reference product​

Source: Spryple analysis, §9, §16 and §21.6.

  • [Observed] A claim is one or more bills (reimbursement type, bill number, bill date, amount, file) plus a description; the bill image is required with a 2 MB limit; IDs look like CL_GEN_<company>_<n> (A/M/E: Claims; §21.6).
  • [Observed] Two-stage control: the manager verifies (and can reject individual bills and enter a verified amount), then finance approves and marks Paid (A: Claims).
  • [Observed] Finance dashboard shows counts by status, a settlement ratio, a previous vs current scorecard and paid amount by type (A: Claims).
  • [Observed] CRM travel records follow a separate path: Pending → Verified → Approved → Claimed, with an editable "KM Revised" (M/E: CRM; §21.7).
  • [Observed] Status names differ by role (Submitted/In Progress vs Pending/Verified) (§9).
  • [Inferred] There are no policy limits, no duplicate detection and no payroll payout; claims are "marked Paid" manually (§9; §16 gap CLM → PAY).

3. Our design decisions​

IDDecision [Proposed]Reason
D-CLM-01One status model for every role: Draft, Submitted, Verified, Approved, Scheduled, Paid, Rejected, SentBack.Removes role-specific vocabularies.
D-CLM-02An effective-dated Expense Policy assigned by applicability (grade, location, department). Limits are evaluated per line at submit and shown live.Finance control before money moves.
D-CLM-03Mileage claims are generated from FFM daily travel into the same pipeline; the travel approval path is merged.One reimbursement path.
D-CLM-04Payout modes: PAYROLL (approved lines become REIMBURSEMENT pay inputs) or BANK_FILE (weekly payout batch).Most SMEs pay with salary; field-heavy ones pay weekly.
D-CLM-05Duplicate detection by a hash of vendor GSTIN or name, bill number, date and amount across the tenant, plus perceptual hash of the image.A common fraud pattern.
D-CLM-06Advances and claims share a ledger per employee; approved claims settle open advances first.Correct balances.

4. Domain model​

clm_category.kind: BILL, PER_DIEM, MILEAGE. clm_claim.source: MANUAL, FFM_TRAVEL. clm_line.policy_result: OK, OVER_LIMIT_CAPPED, OVER_LIMIT_FLAGGED, RECEIPT_MISSING, DUPLICATE_SUSPECT.

5. State machines​

6. Key business logic​

6.1 Claim limits​

checkLine(line, v, employee): # v = expense policy version for employee on expense_date
rule = v.categories[line.category] for employee's grade and city tier of the expense location
if line.category.kind == MILEAGE: line.amount = line.quantity_km * rule.rate_per_km[vehicle]
if line.category.kind == PER_DIEM: line.amount = days * rule.per_diem[city_tier]
if rule.receipt_required_above is set and line.amount > rule.receipt_required_above
and no attachment: result RECEIPT_MISSING (blocks submit)
caps = [rule.per_bill, rule.per_day - spent(employee, category, expense_date),
rule.per_month - spent(employee, category, month), rule.per_claim]
allowed = min(non-null caps)
if line.amount > allowed:
if v.over_limit_action == CAP: line.claimable = allowed ; result OVER_LIMIT_CAPPED
if v.over_limit_action == FLAG: line.claimable = line.amount ; result OVER_LIMIT_FLAGGED
if v.over_limit_action == BLOCK: reject the line
if expense_date < today - v.submit_within_days: block "too old"
if duplicateHash(line) exists in another non-rejected claim: result DUPLICATE_SUSPECT
# spent() counts Submitted, Verified, Approved, Scheduled and Paid lines (not Rejected)

6.2 Payout​

on claim Approved:
net = approved_amount - open advance balance (settle advance first, post ledger entries)
if payout_mode == PAYROLL: create PAY input REIMBURSEMENT for the next open period ; status Scheduled
if payout_mode == BANK_FILE: add to the open payout batch ; status Scheduled
on pay.run.locked containing the input, or batch bank file confirmed: status Paid ; emit clm.claim.paid

7. Configuration​

SettingTypeDefaultDescription
categoriesarray of objectTravel, Food, Lodging, Fuel, Mileage, Phone, MiscCategory rules by grade and city tier
categories[].per_billnumbernullCap per bill
categories[].per_daynumbernullCap per day
categories[].per_monthnumbernullCap per month
categories[].receipt_required_abovenumber0Amount above which a bill image is needed
mileage.rate_per_kmobjecttwo-wheeler 4, four-wheeler 10Example rates in rupees, set by the tenant
per_diemobjectby city tierDaily allowance
over_limit_actionenumFLAGCAP, FLAG, BLOCK
submit_within_daysinteger60Age limit for expenses
payout_modeenumPAYROLLPAYROLL or BANK_FILE
attachment.max_bytesinteger52428805 MB (reference uses 2 MB)
duplicate_checkbooleantrueDuplicate detection
verification_requiredbooleantrueManager verification step

8. Screens by persona​

PersonaWebMobile
EmployeeNew claim (lines, live policy result), drafts, status tabs, advance request, ledgerCapture bill photo, offline draft, submit, status
Line managerVerification queue with per-line verify, edit amount and reject, historyVerify and reject lines
Payroll manager / financeApproval queue, payout batches, advances, finance dashboard (settlement ratio, scorecard, paid by type)Approvals
Finance adminCategories, expense policy editor, city tiers—

9. Events​

EventConsumers
clm.claim.submittedNTF (workflow)
clm.claim.verifiedNTF
clm.claim.approvedPAY (input when payroll mode), NTF
clm.claim.rejected, clm.claim.sent_backNTF
clm.claim.paidNTF, RPT
clm.advance.approvedPAY or payout batch
clm.duplicate.suspectedNTF (finance)

Consumed: ffm.travel.verified (generate a mileage claim), pay.run.locked and pay.run.reversed (payout status), ext.fnf.initiated (settle open claims and advances in F&F).

10. API surface​

ResourceMain operations
/v1/clm/categories, /v1/clm/policiesCRUD, versions
/v1/clm/claimsPOST, GET, PATCH draft, POST /:id/submit, POST /:id/withdraw
/v1/clm/claims/:id/linesCRUD in Draft; POST /:lineId/verify
/v1/clm/claims/checkPOST dry policy check
/v1/clm/advancesPOST, GET
/v1/clm/payout-batchesPOST, POST /:id/bank-file, POST /:id/confirm
/v1/clm/ledger-entriesGET

Detailed in API endpoints.

11. Permissions​

PermissionScopesDefault roles
clm.claim.createSELFemployee
clm.claim.verifyTEAMline_manager, sales_manager
clm.claim.approveORG, LOCATIONpayroll_manager
clm.payout.manageORGpayroll_manager
clm.policy.manageORGfinance_admin
clm.advance.approveORGpayroll_manager
clm.report.exportTEAM, ORGline_manager, payroll_manager

12. Reports​

ReportContentsReference
Claims registerApplied on, employee, claim no, bill count, requested, verified, approved, status, actioned by[Observed] A: Claims
Finance dashboardStatus counts, settlement ratio, scorecard, paid by type[Observed] A: Claims
Policy exceptionsOver-limit, missing receipts, duplicates[Proposed]
Spend by category, department and monthTrend[Proposed]
GST input registerLines with vendor GSTIN and GST amount[Proposed]
Advance ageingOpen advances by age[Proposed]

13. Release and priority​

CapabilityReleaseMoSCoW
Categories, multi-bill claims, verify, approve, payout via payrollR2Must
Expense policy with limits and receipt thresholdsR2Must
Duplicate detectionR2Should
Bank-file payout batchesR2Should
Advances and ledgerR2Should
Mileage claims from FFM travelR3Must (with FFM)
GST captureR2Should
OCR of billsR4Could

14. Open questions​

  1. Is mileage reimbursement taxable above a threshold for the tenant's employees? Treatment must be configurable per component; verify.
  2. Do customers need a department-head step above an amount by default? The template includes it as a condition.
  3. Should reimbursements paid through payroll appear on the payslip or in a separate statement? The default is a separate payslip section.