# API Reference — Swish Auto Care GMS ## Backend: Node.js + Express.js > Base URL (local): `http://localhost:5000/api` > All protected routes require: `Authorization: Bearer ` 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