481 lines
9.6 KiB
Markdown
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®_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
|