39294-vm/02_API_REFERENCE.md
2026-03-24 15:28:07 +00:00

481 lines
9.6 KiB
Markdown

# API Reference — Swish Auto Care GMS
## Backend: Node.js + Express.js
> Base URL (local): `http://localhost:5000/api`
> All protected routes require: `Authorization: Bearer <JWT_TOKEN>` header.
---
## Auth Routes — `/api/auth`
### POST `/api/auth/login`
**Public.** Validates credentials, returns JWT.
Request:
```json
{ "username": "admin", "password": "swish@2024" }
```
Response (200):
```json
{ "token": "eyJhbG...", "user": { "id": 1, "username": "admin", "full_name": "Swish Admin" } }
```
Response (401): `{ "error": "Invalid credentials" }`
### GET `/api/auth/me`
**Protected.** Returns logged-in user info from JWT.
Response:
```json
{ "id": 1, "username": "admin", "full_name": "Swish Admin" }
```
---
## Dashboard Routes — `/api/dashboard`
### GET `/api/dashboard/today`
**Protected.** Returns all today's summary widgets.
Response:
```json
{
"active_jobs": 4,
"completed_jobs": 7,
"today_revenue": 28500,
"cash_balance": 14200,
"staff_present": 3,
"date": "24/03/2026"
}
```
---
## Job Card Routes — `/api/job-cards`
### GET `/api/job-cards`
**Protected.** List all job cards with optional filters.
Query params: `?status=active|done&date=YYYY-MM-DD&reg_no=GJ06AB1234&page=1&limit=20`
Response:
```json
{
"data": [
{
"id": 1,
"job_no": "SAC-20260324-001",
"job_type": "full",
"reg_no": "GJ06AB1234",
"car_name": "Swift",
"owner_name": "Rajesh Shah",
"mobile_no": "9876543210",
"service_type": "Full Detailing",
"assigned_staff_name": "Rajan Mehta",
"estimated_amount": 5000,
"final_amount": null,
"payment_mode": null,
"status": "active",
"job_date": "2026-03-24T10:30:00",
"closed_at": null
}
],
"total": 1,
"page": 1
}
```
### POST `/api/job-cards`
**Protected.** Create a new job card.
Request (Full Job):
```json
{
"job_type": "full",
"reg_no": "GJ06AB1234",
"car_name": "Swift",
"owner_name": "Rajesh Shah",
"mobile_no": "9876543210",
"service_type": "Full Detailing",
"assigned_staff_id": 1,
"estimated_amount": 5000,
"notes": "Check AC vent"
}
```
Request (Quick Wash):
```json
{
"job_type": "wash",
"reg_no": "GJ06CD5678",
"car_name": "Creta",
"mobile_no": "",
"service_type": "Basic Wash",
"assigned_staff_id": 2,
"estimated_amount": 300
}
```
Response (201):
```json
{ "id": 1, "job_no": "SAC-20260324-001", "message": "Job card created" }
```
### GET `/api/job-cards/:id`
**Protected.** Get single job card with full details.
### PUT `/api/job-cards/:id`
**Protected.** Update job card fields (only while status = 'active').
Request: Partial fields allowed.
```json
{ "notes": "Updated notes", "estimated_amount": 5500 }
```
### POST `/api/job-cards/:id/close`
**Protected.** Mark job as Done. Sets final_amount, payment_mode, closed_at.
Request:
```json
{
"final_amount": 4800,
"payment_mode": "upi"
}
```
Response: `{ "message": "Job marked as done", "closed_at": "2026-03-24T15:45:00" }`
**Error if already done:** `{ "error": "Job already closed" }`
---
## Staff Routes — `/api/staff`
### GET `/api/staff`
**Protected.** List all staff.
Query: `?is_active=1|0`
Response:
```json
{
"data": [
{
"id": 1,
"staff_code": "SAC-STF-001",
"full_name": "Rajan Mehta",
"role": "Senior Detailer",
"mobile_no": "9898989898",
"date_of_joining": "2024-01-15",
"monthly_salary": 18000,
"is_active": 1
}
]
}
```
### POST `/api/staff`
**Protected.** Add new staff member. Auto-generates staff_code.
Request:
```json
{
"full_name": "Deepak Verma",
"role": "Washer",
"mobile_no": "9712345678",
"date_of_joining": "2026-03-01",
"monthly_salary": 13000
}
```
### PUT `/api/staff/:id`
**Protected.** Update staff details.
### PATCH `/api/staff/:id/toggle`
**Protected.** Toggle is_active status.
---
## Attendance Routes — `/api/attendance`
### GET `/api/attendance`
**Protected.** Get attendance for a date (default: today).
Query: `?date=YYYY-MM-DD`
Response:
```json
{
"date": "2026-03-24",
"records": [
{ "staff_id": 1, "staff_name": "Rajan Mehta", "status": "present" },
{ "staff_id": 2, "staff_name": "Suresh Kumar", "status": "absent" },
{ "staff_id": 3, "staff_name": "Vikram Patel", "status": null }
]
}
```
`null` status means not yet marked for today.
### POST `/api/attendance/bulk`
**Protected.** Save attendance for multiple staff in one call.
Request:
```json
{
"date": "2026-03-24",
"records": [
{ "staff_id": 1, "status": "present" },
{ "staff_id": 2, "status": "absent" },
{ "staff_id": 3, "status": "half" }
]
}
```
Response: `{ "message": "Attendance saved", "count": 3 }`
**Backend uses:** `INSERT INTO attendance ... ON DUPLICATE KEY UPDATE status = VALUES(status)`
### GET `/api/attendance/summary/:staffId`
**Protected.** Monthly attendance summary for one staff.
Query: `?month=3&year=2026`
Response:
```json
{
"staff_id": 1,
"month": "March 2026",
"present": 18,
"absent": 3,
"half": 2,
"records": [...]
}
```
---
## Salary Routes — `/api/salary`
### GET `/api/salary`
**Protected.** List all payments, filterable.
Query: `?staff_id=1&month=March 2026`
### POST `/api/salary`
**Protected.** Log a payment to a staff member.
Request:
```json
{
"staff_id": 1,
"payment_date": "2026-03-24",
"payment_month": "March 2026",
"days_present": 22,
"calculated_salary": 15230.77,
"paid_amount": 15000,
"payment_type": "salary",
"payment_mode": "cash",
"notes": "March salary"
}
```
### GET `/api/salary/calculate/:staffId`
**Protected.** Auto-calculate salary based on attendance.
Query: `?month=3&year=2026`
Response:
```json
{
"staff_id": 1,
"staff_name": "Rajan Mehta",
"monthly_salary": 18000,
"days_present": 22,
"calculated_salary": 15230.77
}
```
Formula: `(18000 / 26) * 22 = 15230.77`
---
## Parts Routes — `/api/parts`
### GET `/api/parts`
**Protected.** List all parts with stock info.
Query: `?category=Chemicals&low_stock=true`
Response:
```json
{
"data": [
{
"id": 1,
"part_code": "SAC-PRT-001",
"name": "Ceramic Coat 9H",
"category": "Chemicals",
"unit": "bottle",
"purchase_price": 1200,
"selling_price": 2500,
"stock_qty": 15,
"low_stock_alert": 5,
"is_low": false
}
]
}
```
### POST `/api/parts`
**Protected.** Add new part. Auto-generates part_code.
### PUT `/api/parts/:id`
**Protected.** Update part details (not stock — stock changes via sales).
### PATCH `/api/parts/:id/stock`
**Protected.** Manual stock adjustment (restock).
Request: `{ "adjustment": 10, "reason": "New stock received" }`
---
## Parts Sales Routes — `/api/parts-sales`
### GET `/api/parts-sales`
**Protected.** List all sales.
Query: `?date=YYYY-MM-DD&job_card_id=5`
### POST `/api/parts-sales`
**Protected.** Record a parts sale. Auto-deducts stock.
Request:
```json
{
"part_id": 1,
"job_card_id": null,
"quantity": 2,
"unit_price": 2500,
"total_amount": 5000,
"customer_name": "Ankit Patel",
"payment_mode": "upi"
}
```
**Backend must:**
1. Verify stock_qty >= quantity. If not: `{ "error": "Insufficient stock" }`
2. Insert into parts_sales
3. `UPDATE parts SET stock_qty = stock_qty - ? WHERE id = ?`
4. Return sale record
---
## Cash Ledger Routes — `/api/cash-ledger`
### GET `/api/cash-ledger`
**Protected.** List entries with optional date filter.
Query: `?from=YYYY-MM-DD&to=YYYY-MM-DD&type=in|out`
### POST `/api/cash-ledger`
**Protected.** Add cash in or out entry.
Request:
```json
{
"entry_type": "out",
"amount": 500,
"category": "Utilities",
"description": "Electricity bill payment"
}
```
---
## Accounts Routes — `/api/accounts`
### GET `/api/accounts/summary`
**Protected.** Consolidated financial summary.
Query: `?from=YYYY-MM-DD&to=YYYY-MM-DD` (default: today)
Response:
```json
{
"period": { "from": "2026-03-24", "to": "2026-03-24" },
"income": {
"job_revenue": 28500,
"parts_revenue": 7500,
"cash_in_other": 2000,
"total": 38000
},
"expense": {
"staff_payments": 15000,
"cash_out_other": 800,
"total": 15800
},
"net_balance": 22200
}
```
### GET `/api/accounts/transactions`
**Protected.** All transactions in a date range (for drill-down).
Returns unified list of all financial movements with source type tag.
---
## Reports Routes — `/api/reports`
### GET `/api/reports/daily`
**Protected.** Get daily report data (JSON, for on-screen preview).
Query: `?date=YYYY-MM-DD` (default: today)
### GET `/api/reports/daily/download`
**Protected.** Download daily report as .xlsx file.
Query: `?date=YYYY-MM-DD`
Response: Binary .xlsx stream with headers:
```
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Disposition: attachment; filename="SwishGMS_DailyReport_24-03-2026.xlsx"
```
---
## Export Routes — `/api/exports`
### GET `/api/exports/job-cards`
**Protected.** Export all job cards as .xlsx.
Query: `?from=YYYY-MM-DD&to=YYYY-MM-DD`
### GET `/api/exports/staff-attendance`
**Protected.** Export staff + attendance as .xlsx.
Query: `?month=3&year=2026`
### GET `/api/exports/parts-inventory`
**Protected.** Export parts inventory as .xlsx.
### GET `/api/exports/accounts`
**Protected.** Export accounts/transactions as .xlsx.
Query: `?from=YYYY-MM-DD&to=YYYY-MM-DD`
---
## Error Response Format (Consistent Across All Routes)
```json
{ "error": "Human-readable error message", "code": "OPTIONAL_ERROR_CODE" }
```
HTTP status codes used:
- `200` OK
- `201` Created
- `400` Bad Request (validation errors)
- `401` Unauthorized (missing/invalid JWT)
- `403` Forbidden
- `404` Not Found
- `409` Conflict (e.g. duplicate attendance)
- `500` Internal Server Error