feat: add comprehensive project documentation in README and project summary
All checks were successful
Deploy Application / deploy (push) Successful in 18s
Test runner / check-runner (push) Successful in 3s

This commit is contained in:
2026-06-25 17:20:38 +02:00
parent 389bc63592
commit 5e711a9b34
3 changed files with 265 additions and 0 deletions

52
README.md Normal file
View File

@@ -0,0 +1,52 @@
# Kalkulator Godzin Odoo (Duck Odoo Calc)
Aplikacja webowa służąca do pobierania, analizowania oraz interaktywnego kalkulowania godzin pracy na podstawie rejestru obecności z systemu **Odoo** (`hr.attendance`).
## Główne Funkcjonalności
* **Logowanie danymi Odoo**: Bezpieczne logowanie i generowanie sesji JWT.
* **Automatyczne pobieranie danych**: Pobieranie historii wejść/wyjść dla wybranego miesiąca i roku.
* **Wizualizacja danych**: Stackowany wykres kolumnowy (Chart.js) prezentujący przepracowany czas, dni urlopowe oraz chorobowe.
* **Interaktywny kalendarz**:
* Szybkie dodawanie i usuwanie wpisów obecności.
* Wygodne suwaki czasu (`TimeStepper`) do edycji godzin wejścia/wyjścia (z dokładnością do minuty).
* Możliwość oznaczania dni jako urlop (8h) lub chorobowe (6.4h - 80%).
* **Automatyczne przeliczanie**:
* Uwzględnianie świąt państwowych w Polsce oraz weekendów.
* Automatyczne odliczanie 15-minutowej przerwy obiadowej (0.25h) przy każdym dniu pracy.
* Bieżące wyliczanie salda nadgodzin (bilansu dziennego i ogólnego).
---
## Wymagania i Konfiguracja
Przed uruchomieniem aplikacji należy utworzyć plik `.env` w katalogu `backend/` i skonfigurować następujące zmienne:
```env
SECRET_KEY=twoj_klucz_jwt_np_z_openssl_rand_hex_32
ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=60
ODDO_URL=https://odoo.twoja-firma.pl
DB_NAME=nazwa_bazy_danych_odoo
ORIGINS=http://localhost:6080
```
---
## Uruchomienie Projektu
Aplikacja jest w pełni konteneryzowana za pomocą Docker Compose. Aby ją zbudować i uruchomić locally, wykonaj polecenie:
```bash
docker compose up --build
```
Po uruchomieniu:
* Aplikacja frontendowa (Vue) będzie dostępna pod adresem: **`http://localhost:6080/odoo/`**
* API backendowe (FastAPI) będzie dostępne pod adresem: **`http://localhost:8000/`** (proxy z frontendu przekierowuje zapytania `/odoo/api/` na backend).
---
## Szczegółowa Dokumentacja
Pełna specyfikacja techniczna, w tym struktura plików, algorytmy obliczeniowe, konfiguracja Nginx oraz opis testów jednostkowych znajduje się w pliku:
* [project_summary.md](project_summary.md)

View File

213
project_summary.md Normal file
View File

@@ -0,0 +1,213 @@
# Duck Odoo Calc (Odoo Hours) - Project Summary
This document provides a comprehensive overview of the **Duck Odoo Calc** (also referred to as `odoo-hours`) project. It maps the architecture, directory structure, data flows, business logic rules, configuration setup, and tests to act as a token-saving context file for future developer prompts.
---
## 1. Project Overview & Tech Stack
The application is a work hours tracker and calculator that integrates with Odoo's attendance registry (`hr.attendance`). It fetches check-in/check-out events for a given month and year, allows users to view their accumulated hours, public holidays, sick leaves, remaining hours, and overtime balance, and enables manual hour adjustments in real time on the frontend interface.
### Technology Stack
* **Backend**:
* Python 3.12 (slim)
* FastAPI & Uvicorn (REST API)
* `requests` (JSON-RPC client for Odoo)
* `PyJWT` (JSON Web Tokens authentication)
* `holidays` (Polish public holidays detection)
* `numpy` (specifically `busday_count` for working days calculations)
* **Frontend**:
* Vue 3 (Vite, Options/Composition API)
* Pinia (State Management)
* Axios (HTTP client)
* Chart.js & Vue-Chartjs (Data visualization)
* Material Design Icons (`@mdi/js`, `@jamescoyle/vue-icon`)
* **Infrastructure**:
* Docker Compose
* Nginx (serving Vue client & proxying requests to the backend)
---
## 2. Repository Structure
```text
duck-odoo-calc/
├── docker-compose.yml # Multi-container runtime configuration
├── project_summary.md # Project summary (this file)
├── README.md # Main project documentation (currently empty)
├── backend/
│ ├── Dockerfile # Python runtime builder
│ ├── requirements.txt # Python dependencies (FastAPI, JWT, holidays, numpy)
│ ├── main.py # FastAPI entry point, authentication & formatting logic
│ └── odoo_api/
│ ├── __init__.py
│ └── client.py # Odoo JSON-RPC API client wrapper
└── frontend/
├── Dockerfile # Multi-stage build (Node 20 build -> Nginx server)
├── nginx.conf # Nginx proxy & routing rules
├── index.html # HTML entry point (loads Inter and Roboto fonts)
├── package.json # NPM packages & scripts
├── vite.config.js # Vite bundler configuration
├── eslint.config.js # Linter rules
├── .prettierrc.json # Code formatter configuration
└── src/
├── App.vue # Base Vue shell (renders RouterView)
├── main.js # App mounting, imports assets/main.css, Pinia, Router
├── api/
│ ├── api.js # Base Axios client with request/response interceptors
│ ├── authApi.js # Authentication endpoint caller
│ ├── dataApi.js # Attendance data loader
│ └── mock_response.js # Local development mock helper (May, June, July data)
├── assets/
│ └── main.css # Main styling variables and layouts
├── components/
│ ├── BarChart.vue # Chart.js integration displaying worked hours per day
│ ├── CardTop.vue # KPI indicator card
│ ├── DayRow.vue # Interactive row for a single day's attendance
│ ├── HoursSummary.vue # Progress bar and hours breakdown card
│ ├── MonthMenu.vue # Dropdown menu to switch months
│ ├── TimeLabel.vue # Formatted duration display (HH:MM)
│ └── TimeStepper.vue # Hoverable time-adjuster component (increment/decrement)
├── router/
│ └── index.js # Vue Router with navigation guards (auth requirements)
├── stores/
│ ├── attendanceStore.js # Pinia store for month-level attendance data
│ └── authStore.js # Pinia store for JWT token management
└── utils/
├── utils.js # Time math, break deductions, and date helper utilities
└── utils.spec.js # Vitest suite verifying hour calculation logic
```
---
## 3. Configuration & Environment Variables
### Docker Compose Configuration ([docker-compose.yml](file:///home/bartool/local_projects/duck-odoo-calc/docker-compose.yml))
Coordinates two containers in a single bridge network:
1. **backend** (`odoo-hours-backend`):
* Builds from `./backend/Dockerfile` using `python:3.12-slim`.
* Runs on port `8000` internally.
2. **frontend** (`odoo-hours-frontend`):
* Builds from `./frontend/Dockerfile` utilizing a multi-stage approach (builds assets with Node 20, then runs Alpine Nginx).
* Maps host port `6080` to container port `80`.
### Backend Environment Variables (`backend/.env` - Ignored)
The backend expects a `.env` file containing:
* `SECRET_KEY`: Private key used for encoding/decoding JWTs.
* `ALGORITHM`: JWT signing algorithm (e.g., `HS256`).
* `ACCESS_TOKEN_EXPIRE_MINUTES`: Expiry duration for authentication tokens.
* `ODDO_URL`: Target Odoo RPC endpoint (Note: spelled `ODDO_URL` in codebase).
* `DB_NAME`: The company's database name in Odoo.
* `ORIGINS`: Comma-separated list of CORS-allowed domains.
### Nginx Routing Rules ([frontend/nginx.conf](file:///home/bartool/local_projects/duck-odoo-calc/frontend/nginx.conf))
* Listens on port `80` inside the container.
* Routes `/odoo/api/` requests to `http://backend:8000/` (removes the prefix).
* Routes `/odoo/` to serve the Vue application (`dist/index.html` mapping under the subfolder `/odoo/`).
* Implements caching for static assets under `/odoo/.*` (1-year expiration) and prevents caching for `/odoo/index.html`.
---
## 4. Backend Logic & API Endpoints
### Odoo API Client ([backend/odoo_api/client.py](file:///home/bartool/local_projects/duck-odoo-calc/backend/odoo_api/client.py))
Handles sessions and authentications using Odoo JSON-RPC endpoints:
* `login(username, password)`: Calls `/web/session/authenticate` with DB details and logs the user in.
* `get_hr_attendance_data(month, year)`: Requests check-in, check-out, worked hours, and attendance reasons for the target month from the `hr.attendance` model via `/web/dataset/call_kw/hr.attendance/web_search_read`.
### API Endpoints ([backend/main.py](file:///home/bartool/local_projects/duck-odoo-calc/backend/main.py))
1. **`POST /token`**
* Receives `username` and `password` via form submission.
* Logs the user into Odoo, retrieves the user metadata, and encodes `sub` (email), `uid`, and `full_name` into a signed JWT.
* Returns `{ "access_token": "...", "token_type": "bearer" }`.
2. **`GET /data/{year}/{month}`**
* Requires a valid JWT Bearer Token.
* Retrieves all attendance records for the given month/year via `OdooAPIClient`.
* Retrieves Polish public holidays using the `holidays.Poland` package.
* Calculates the total days in the month and uses NumPy (`np.busday_count`) to compute standard working days (omitting weekends and public holidays).
* Returns:
```json
{
"year": 2026,
"month": 6,
"days_in_month": 30,
"employee": "John Doe",
"working_days": 21,
"public_holidays": [{"date": "2026-06-04", "name": "Boże Ciało"}],
"days": [...]
}
```
---
## 5. Frontend Stores & State Management
### Authentication Store ([frontend/src/stores/authStore.js](file:///home/bartool/local_projects/duck-odoo-calc/frontend/src/stores/authStore.js))
* **State**: Holds the `token` (initialized from `localStorage.getItem('token')`).
* **Actions**:
* `loginUser(email, password)`: Dispatches request to API, updates state, and persists the token in local storage.
* `logout()`: Deletes token from local storage and state.
* `isAuthenticated()`: Helper returning boolean check on token presence.
### Attendance Store ([frontend/src/stores/attendanceStore.js](file:///home/bartool/local_projects/duck-odoo-calc/frontend/src/stores/attendanceStore.js))
* **State**: Stores active `employee`, `year`, `month`, `daysInMonth`, `workingDays`, `workingHours` (workingDays × 8), and `days` array.
* **Calculated Properties (Getters)**:
* `sumOfHours`: Total hours at the end of the month (combines worked, holiday, and sick hours).
* `workedHours`: `sumOfHours` minus holiday and sick hours.
* `overtimeHours`: Overtime balance from the last day of the month.
* `holidayHours`: `holidayCount` × 8.0.
* `sickHours`: `sickCount` × 6.4 (80% of 8 hours standard).
* `toGoHours`: `leaveDayCount` × 8.0.
* `leaveDayCount`: Calculated days remaining to work in the month (excluding weekends, public holidays, sick/holiday leave, and days with recorded work hours).
* **Actions**:
* `loadFromResponse(response)`: Maps the backend response into individual calendar days (1 to days_in_month). It groups daily attendance entries, parses their check-in/out times, links public holidays, runs the initial hour math calculations, and updates the state.
* `updateDay(day)`: Recalculates hours and cumulative balance from the changed day onwards.
---
## 6. Core Business Logic & Calculations ([frontend/src/utils/utils.js](file:///home/bartool/local_projects/duck-odoo-calc/frontend/src/utils/utils.js))
The core logic handles standard office timings, break policies, and cumulative balances:
1. **Lunch Break Deduction**:
* If a day contains recorded work hours, **0.25 hours (15 minutes)** is automatically subtracted from the day's total worked hours:
$$\text{workedHours} = \text{totalWorkedTime} - 0.25$$
2. **Working Days Definition**:
* A valid working day is Monday through Friday, excluding public holidays:
$$\text{isValidWorkDay} = \text{dayOfWeek} \notin \{\text{"So"}, \text{"Nd"}\} \land \neg \text{isPublicHoliday}$$
3. **Overtime & Balances**:
* For standard working days (not marked as sick or holiday leave):
$$\text{overtime} = \text{workedHours} - 8.0$$
* For weekends, holidays, or leaves:
$$\text{overtime} = \text{workedHours}$$
4. **Sick and Holiday Leaves**:
* Holiday leaves grant 8.0 hours towards the month's total.
* Sick leaves grant 6.4 hours (80% value of an 8-hour day).
5. **Cumulative Rollforward**:
* `calculateMonth(days)` and `calculateMonthFromDay(startDay, days)` loop sequentially from a given day to the end of the month. They roll up `accumulatedHours` and compute the running overtime `balanceHours`:
$$\text{balanceHours}_d = \text{accumulatedHours}_d - (\text{workingDaysElapsed}_d \times 8)$$
---
## 7. Key Frontend UI Components
* **[DashboardView.vue](file:///home/bartool/local_projects/duck-odoo-calc/frontend/src/views/DashboardView.vue)**: Consolidates metrics, the bar chart, and the interactive scrollable table displaying each calendar day.
* **[DayRow.vue](file:///home/jamescoyle/vue-icon)**: A component representing a single row in the calendar. It supports:
* Entering/exiting time editing via the `TimeStepper` component.
* Adding attendance entries (defaults to `07:00``15:15`).
* Marking days as Holiday or Sick Leave (represented by checkboxes).
* Navigating between multiple entry intervals if the user clocked in/out multiple times in one day.
* **[TimeStepper.vue](file:///home/bartool/local_projects/duck-odoo-calc/frontend/src/components/TimeStepper.vue)**: Renders HH:MM numbers. Hovering over a block displays vertical adjustment arrows. Pressing (or holding) these buttons triggers incremental changes in hours or minutes (bounded between `06:00` and `20:00`).
* **[BarChart.vue](file:///home/bartool/local_projects/duck-odoo-calc/frontend/src/components/BarChart.vue)**: Utilizes Chart.js to render a stacked bar chart representing hours (blue), holiday leaves (yellow), and sick leaves (red) for every day of the month. Weekend tick labels are colored dynamically (Saturday = Orange, Sunday = Red, Public Holiday = Green).
---
## 8. Test Suite ([frontend/src/utils/utils.spec.js](file:///home/bartool/local_projects/duck-odoo-calc/frontend/src/utils/utils.spec.js))
The project includes unit tests written in Vitest to validate `calculateDay` logic under various parameters:
* **Standard day**: Verifies that 8h 30m of clocked time (e.g. `08:00` to `16:30`) correctly results in `workedHours = 8.25` (due to `-0.25` break deduction) and `overtime = 0.25`.
* **Multiple check-ins**: Confirms that splitting times (e.g. `08:00`-`12:00` and `13:00`-`16:30` - total 7.5 hours) properly calculates `workedHours = 7.25` and `overtime = -0.75`.
* **Active clock-in**: Tests system time mocking (via `vi.setSystemTime`). When a user is currently clocked in (empty exitTime), the utility dynamically uses the current time to compute the elapsed hours and logs it.
* **Leaves**: Verifies holiday leave (+8h hours) and sick leave (+6.4h hours) math.
* **Weekends**: Confirms that working on weekends calculates all worked time as pure overtime (no standard 8-hour requirement subtraction).