feat: add comprehensive project documentation in README and project summary
This commit is contained in:
52
README.md
Normal file
52
README.md
Normal 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)
|
||||||
213
project_summary.md
Normal file
213
project_summary.md
Normal 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).
|
||||||
Reference in New Issue
Block a user