---
title: "17. Development Guide"
space: "CAMS Biometric Integration"
url: "https://docs.navari.co.ke/cams-biometric/development-guide"
updated: "2026-05-27"
---

This section is for developers who want to extend, customise, or contribute to the app.

### 17.0 Repository Structure

```
navari_cams_biometric/
├── .github/
│   └── workflows/          # CI/CD pipeline (GitHub Actions)
├── navari_cams_biometric/
│   └── cams_biometric/
│       ├── controllers/
│       │   ├── cams_call.py          # Core: attendance(), handle_attendance_log(),
│       │   │                         # handle_punch_logs(), update_last_sync_time()
│       │   └── test_cams_call.py     # Unit + integration tests
│       ├── doctype/
│       │   └── cams_biometric_settings/
│       │       ├── cams_biometric_settings.json  # DocType schema
│       │       ├── cams_biometric_settings.py    # Server-side logic
│       │       └── cams_biometric_settings.js    # Client-side logic
│       └── fixtures/        # Exported DocType fields (e.g. punch_type new field)
├── .cspell.json             # Spell checker config for code review
├── .gitignore
├── .pre-commit-config.yaml  # Pre-commit hooks (linting, formatting)
├── .releaserc               # Semantic release config
├── README.md
├── license.txt              # AGPL-3.0
└── pyproject.toml           # Python project metadata

```



### 17.1 Core Controller: cams_call.py

The heart of the app is `navari_cams_biometric/cams_biometric/controllers/cams_call.py`.



**Key functions:**

`attendance()`: The main entry point. Decorated with `@frappe.whitelist(allow_guest=True)`. Parses the incoming JSON, validates the AuthToken, determines the payload type (RealTime vs PunchLog batch), and dispatches to the appropriate handler.



`handle_attendance_log(stgid, log)`: Handles a single real-time punch log entry. Resolves the employee, creates an Employee Checkin, calls `update_last_sync_time()`.



`handle_punch_logs(stgid, logs)`: Handles a batch of historical punch log entries. Iterates over the list and calls `handle_attendance_log()` for each.



`update_last_sync_time(employee, log_time)`: Finds the employee's active Shift Assignment and updates its `last_sync_datetime` field to the punch time.



### 17.2 Adding Custom Fields

If you need to capture additional data from the CAMS payload (e.g. `Temperature`, `FaceMask`, `InputType`), you can add custom fields to the `Employee Checkin` doctype:



1. Go to **Setup > Customize Form > Employee Checkin**
2. Add the desired fields (e.g. `custom_temperature`, `custom_face_mask`, `custom_input_type`)
3. In `cams_call.py`, extend `handle_attendance_log()` to populate these fields from the payload

Always prefix custom fields with `custom_` to follow Frappe conventions and avoid conflicts with future FrappeHR updates.



### 17.3 Pre-commit Hooks

The project uses pre-commit for code quality:

```bash
pip install pre-commit
pre-commit install
```



Hooks include linting and code formatting. Run manually with:

```bash
pre-commit run --all-files

```



### 17.4 CI/CD Pipeline

The `.github/workflows/` directory contains GitHub Actions workflows that run on push and pull request events. The pipeline:

- Installs MariaDB (pinned version, with a fix committed for the MariaDB client installation)
- Sets up a Frappe bench with ERPNext and HRMS
- Installs `navari_cams_biometric`
- Runs the test suite



Semantic release is configured via `.releaserc` and commits using the Conventional Commits format (`feat:`, `fix:`, `docs:`, etc.) automatically trigger version bumps and GitHub release creation.



### 17.5 Writing Tests

Tests live in `test_cams_call.py` alongside the controller. Use `FrappeTestCase` (not Python's `unittest.TestCase`) to ensure Frappe's database session and setup/teardown lifecycle is respected.



**Test philosophy (per maintainer review feedback):**

- Avoid excessive mocking: test real database interactions where possible
- Mock only outer dependencies (e.g. `frappe.request`, `frappe.form_dict`) not internal Frappe ORM calls
- Use Frappe test helpers like `create_test_employee()` to set up fixtures
- Verify actual database state after operations (use `frappe.db.exists()`)



**Example test pattern:**

```python
from frappe.tests.utils import FrappeTestCase

class TestCamsCall(FrappeTestCase):
    def test_handle_attendance_log_creates_checkin(self):
        employee = create_test_employee()
        log = {
            "UserId": employee.attendance_device_id,
            "LogTime": "2024-09-17 08:00:00 GMT +0300",
            "Type": "CheckIn",
            "InputType": "Fingerprint"
        }
        handle_attendance_log("STG001", log)
        self.assertTrue(
            frappe.db.exists("Employee Checkin", {"employee": employee.name})
        )

```

### 17.6 Release History & Changelog

#### v1.0.0 — December 19, 2025 (Latest)

##### **Features added:**

- Punch type field (`MealIn`/`MealOut`): captured separately to avoid corrupting normal attendance records
- Logging capability: all key events and errors now produce log entries
- Device ID to Employee mapping hardening: improved resilience when biometric IDs have no matching employee
- Enhanced employee validation and error handling throughout the attendance pipeline
- Improved employee filtering logic in the biometric attendance system
- Fixture export for the new punch type field
- Semantic auto-release pipeline (GitHub Actions + semantic-release)

##### **Bug fixes:**

- MariaDB client installation issue in CI/CD fixed
- Pre-commit setup corrected for linters and formatting
- Deprecated `actions/cache@v2` updated to `v4` in GitHub Actions workflow



### 17.7 Roadmap

The following items are planned or under active development:

#### In Progress

**CAMS Dashboards (Issue #13 / PR #14)** A built-in attendance dashboard within FrappeHR showing:

- Real-time check-in/check-out status
- Daily attendance summary by department
- Punch log visualisation by device/location
- Late arrival and early departure metrics

#### Planned

- Multi-device support: multiple CAMS devices with separate AuthTokens manageable from a single CAMS Biometric Settings page or via a child table
- Webhook retry visibility: surface CAMS Gateway delivery failures in the FrappeHR UI
- Employee self-service: allow employees to view their own biometric check-in history
- More granular punch type mapping: configurable per organisation whether BreakOut/BreakIn count as attendance events