diff --git a/docs/administrator/import-pracownikow.md b/docs/administrator/import-pracownikow.md index 1dbb62251..ee1be6718 100644 --- a/docs/administrator/import-pracownikow.md +++ b/docs/administrator/import-pracownikow.md @@ -51,6 +51,25 @@ Aby uruchomić procedure importu danych, wystarczy dodać plik do systemu przy p rozpocznie się automatycznie. +## Nadpisywanie dat zatrudnienia wartościami z pliku + +Domyślnie import **nigdy nie nadpisuje** istniejących dat zatrudnienia — +wypełnia tylko puste („data od"/„data do" bez wartości w bazie), a +różnice pokazuje wyłącznie w podglądzie. Jeśli daty w bazie są błędne +(np. poprzedni import pliku bez dat ostemplował wszystkich datą importu), +zaznacz w szufladzie „Opcje zaawansowane" opcję **Nadpisuj daty +zatrudnienia (od/do) wartościami z pliku**: + +- nadpisywane są tylko daty osób obecnych w pliku, tam gdzie plik niesie + datę różną od bazy; +- puste komórki pliku niczego nie kasują; +- przy zaznaczaniu opcji system prosi o potwierdzenie ryzyka (bez liczby + wierszy — ta jeszcze nie jest znana), a dopiero przed końcowym zapisem + pokazuje liczbę wierszy, których daty zostaną nadpisane; +- nadpisanie, które spowodowałoby nałożenie się dwóch okresów + zatrudnienia w tej samej jednostce, jest odrzucane per-wiersz z + czytelnym błędem. + ## Odpinanie nieaktualnych miejsc pracy Po zaimportowaniu listy pracowników system prezentuje raport z dokonanych zmian. diff --git a/docs/superpowers/plans/2026-07-26-import-nadpisywanie-dat-zatrudnienia.md b/docs/superpowers/plans/2026-07-26-import-nadpisywanie-dat-zatrudnienia.md new file mode 100644 index 000000000..ee70daf0b --- /dev/null +++ b/docs/superpowers/plans/2026-07-26-import-nadpisywanie-dat-zatrudnienia.md @@ -0,0 +1,1086 @@ +# Import: nadpisywanie dat zatrudnienia z pliku — plan implementacji + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Opcja per-import „Nadpisuj daty zatrudnienia (od/do) wartościami +z pliku" — schowana w szufladzie formularza, z confirm-em przy zaznaczaniu +i ostrzeżeniem (callout + confirm) w końcowym formularzu zapisu osób. + +**Architecture:** Flaga `BooleanField` na `ImportPracownikow`; bramka +`zmiany_potrzebne` i `_integruj_daty_aj` stają się flag-aware; pythonowy +pre-check nakładania okresów (lustro `ExclusionConstraint`) przed save; +licznik realnych nadpisań zasila ostrzeżenie finalizacji. + +**Tech Stack:** Django 4.x, pytest + model_bakery, crispy-forms +(Foundation), szablony Django. + +**Spec:** `docs/superpowers/specs/2026-07-26-import-nadpisywanie-dat-zatrudnienia-design.md` + +## Global Constraints + +- ZAWSZE `uv run` przed każdym poleceniem Pythona (`uv run pytest`, `uv run python src/manage.py ...`). +- Testy: pytest, funkcje bez klas, `@pytest.mark.django_db`, `model_bakery.baker.make`. NIGDY unittest.TestCase. +- Pytest z `-n auto` przy pełnych przebiegach; pojedyncze testy mogą iść bez. +- Wyjście testów ZAWSZE do pliku: `... 2>&1 | tee /tmp/wynik.log` — potem grep; nie uruchamiaj tego samego przebiegu dwa razy. +- Max 88 znaków linii (ruff). Po edycjach: `ruff format ` i `ruff check ` (tylko zmienione pliki, nigdy `--all-files`). +- Komentarze szablonów Django `{# ... #}` są JEDNOLINIOWE — każda linia z własnym `{#` i `#}`. +- Publiczny frontend = Foundation Icons (``), NIE emoji. +- NIE modyfikować istniejących (wydanych) migracji; nowa migracja = `0028`. +- Newsfragment: `src/bpp/newsfragments/.feature.rst`, po polsku. +- Baseline bazy NIE odświeżać na gałęzi — raz, przy scalaniu (`make baseline-update`). +- Praca na gałęzi `feat/import-nadpisywanie-dat` od `dev`; commity małe, po każdym tasku. +- Nazwy z specu (spójność międzytaskowa): pole modelu `nadpisuj_daty_zatrudnienia`; metoda wiersza `nadpisze_daty()`; metoda parenta `liczba_nadpisan_dat()`; metoda pre-checku `_sprawdz_nakladanie_okresow(aj)`. + +--- + +### Task 0: Gałąź robocza + +**Files:** brak zmian w plikach. + +- [ ] **Step 1: Utwórz gałąź od dev** + +```bash +cd /Users/mpasternak/Programowanie/bpp +git checkout dev && git pull --ff-only && git checkout -b feat/import-nadpisywanie-dat +``` + +--- + +### Task 1: Pole modelu + migracja 0028 + +**Files:** +- Modify: `src/import_pracownikow/models.py` (klasa `ImportPracownikow`, po polu `przepnij_wszystkie_prace`, ok. linii 171) +- Create: `src/import_pracownikow/migrations/0028_nadpisuj_daty_zatrudnienia.py` (generowana) +- Test: `src/import_pracownikow/tests/test_nadpisywanie_dat.py` (nowy plik) + +**Interfaces:** +- Produces: `ImportPracownikow.nadpisuj_daty_zatrudnienia: bool` (default `False`) — czytane w Taskach 2-6. + +- [ ] **Step 1: Napisz failujący test** + +Utwórz `src/import_pracownikow/tests/test_nadpisywanie_dat.py`: + +```python +"""Opcja „Nadpisuj daty zatrudnienia (od/do) wartościami z pliku" — +flaga modelu, bramka zmian, nadpisywanie w integracji, pre-check +nakładania okresów, licznik ostrzeżenia finalizacji (spec +2026-07-26-import-nadpisywanie-dat-zatrudnienia-design.md).""" + +import pytest +from model_bakery import baker + +from import_pracownikow.models import ImportPracownikow + + +@pytest.mark.django_db +def test_flaga_nadpisywania_domyslnie_wylaczona(): + parent = baker.make(ImportPracownikow) + assert parent.nadpisuj_daty_zatrudnienia is False +``` + +- [ ] **Step 2: Uruchom test — ma paść** + +```bash +uv run pytest src/import_pracownikow/tests/test_nadpisywanie_dat.py -x 2>&1 | tee /tmp/t1.log | tail -5 +``` + +Oczekiwane: FAIL/ERROR (`AttributeError` / pole nie istnieje). + +- [ ] **Step 3: Dodaj pole do modelu** + +W `src/import_pracownikow/models.py`, w klasie `ImportPracownikow`, +bezpośrednio PO polu `przepnij_wszystkie_prace` (przed +`zakres_integracji`): + +```python + nadpisuj_daty_zatrudnienia = models.BooleanField( + "Nadpisuj daty zatrudnienia (od/do) wartościami z pliku", + default=False, + # HTML w help_text (crispy renderuje przez |safe) — wzorzec jak w + # przepnij_wszystkie_prace. Ten sam string dosłownie w migracji 0028. + help_text="Gdy zaznaczone, daty rozpoczęcia i zakończenia pracy " + "ISTNIEJĄCYCH okresów zatrudnienia zostaną " + "NADPISANE wartościami z pliku — tam, gdzie plik " + "niesie datę różną od bazy.
" + "Użyj do KOREKTY dat (np. po wcześniejszym imporcie pliku bez " + "dat, który ostemplował wszystkich datą importu).
" + "Puste komórki pliku niczego nie kasują. Dotyczy wyłącznie osób " + "obecnych w pliku.", + ) +``` + +- [ ] **Step 4: Wygeneruj migrację** + +```bash +DJANGO_BPP_SKIP_DOTENV=1 uv run python src/manage.py makemigrations import_pracownikow -n nadpisuj_daty_zatrudnienia 2>&1 | tail -3 +``` + +Oczekiwane: powstaje `src/import_pracownikow/migrations/0028_nadpisuj_daty_zatrudnienia.py` +(AddField). Obejrzyj plik — ma zawierać wyłącznie to jedno AddField. + +- [ ] **Step 5: Uruchom test — ma przejść** + +```bash +uv run pytest src/import_pracownikow/tests/test_nadpisywanie_dat.py -x 2>&1 | tee /tmp/t1b.log | tail -5 +``` + +Oczekiwane: PASS. + +- [ ] **Step 6: Commit** + +```bash +git add src/import_pracownikow/models.py src/import_pracownikow/migrations/0028_nadpisuj_daty_zatrudnienia.py src/import_pracownikow/tests/test_nadpisywanie_dat.py +git commit -m "feat(import_pracownikow): pole nadpisuj_daty_zatrudnienia (mig 0028)" +``` + +--- + +### Task 2: Formularz (szuflada) + confirm przy zaznaczaniu + +**Files:** +- Modify: `src/import_pracownikow/forms.py` (`NowyImportForm`, linie 16-70) +- Modify: `src/import_pracownikow/templates/import_pracownikow/importpracownikow_form.html` (blok ` {% endblock %} diff --git a/src/import_pracownikow/templates/import_pracownikow/partials/_ostrzezenie_nadpisanie_dat.html b/src/import_pracownikow/templates/import_pracownikow/partials/_ostrzezenie_nadpisanie_dat.html new file mode 100644 index 000000000..a03fc9566 --- /dev/null +++ b/src/import_pracownikow/templates/import_pracownikow/partials/_ostrzezenie_nadpisanie_dat.html @@ -0,0 +1,13 @@ +{# Ostrzeżenie finalizacji: flaga „nadpisuj daty zatrudnienia" — zapis #} +{# osób NADPISZE istniejące daty wartościami z pliku (spec nadpisywania #} +{# dat §3.5). Liczba = realne nadpisania (obie strony niepuste i różne). #} +
+

+ + Włączono nadpisywanie dat zatrudnienia. + Wiersze z datami różnymi od bazy: + {{ liczba_nadpisan_dat }} — ich daty rozpoczęcia + / zakończenia pracy zostaną NADPISANE + wartościami z pliku. Ręcznie ustawione daty zostaną utracone. +

+
diff --git a/src/import_pracownikow/templates/import_pracownikow/przeglad.html b/src/import_pracownikow/templates/import_pracownikow/przeglad.html index a476ecdab..760dac6be 100644 --- a/src/import_pracownikow/templates/import_pracownikow/przeglad.html +++ b/src/import_pracownikow/templates/import_pracownikow/przeglad.html @@ -194,6 +194,9 @@

Najpierw słowniki

{# Ostrzeżenie #4 (PR #531): zapis zmienia podstawowe #} {# miejsce pracy — szczegółowy warning tuż przed przyciskiem. #} {% include "import_pracownikow/partials/_ostrzezenie_podstawowe_miejsce.html" %} + {% if nadpisywanie_dat_wlaczone %} + {% include "import_pracownikow/partials/_ostrzezenie_nadpisanie_dat.html" %} + {% endif %} {# Ostrzeżenie finalizacji: wiersze bez decyzji zostaną #} {# pominięte. {% with %} liczy COUNT raz (nie 2× w if+confirm). #} {% with liczba_pominietych=parent_object.liczba_wierszy_do_pominiecia %} @@ -202,7 +205,7 @@

Najpierw słowniki

{% endif %}
+ {% if liczba_pominietych or liczba_nadpisan_dat %}onsubmit="return confirm('{% if liczba_pominietych %}Wiersze bez dopasowania zostaną pominięte: {{ liczba_pominietych }}. {% endif %}{% if liczba_nadpisan_dat %}Daty zatrudnienia zostaną NADPISANE wartościami z pliku dla wierszy: {{ liczba_nadpisan_dat }}. {% endif %}Kontynuować zapis?');"{% endif %}> {% csrf_token %}