Wstęp
Jeśli pracujesz z Odoo, natknąłeś się na pola, które same wyliczają wartość. Przechowywane pole obliczane idzie krok dalej: wynik obliczeń jest zapisany w bazie danych jako normalna kolumna, a nie tylko wyświetlany w locie.
Ta różnica ma konkretne konsekwencje. Pole liczone „na żądanie” nie nadaje się dobrze do wyszukiwania, filtrowania czy grupowania w zapytaniach SQL. Pole przechowywane można traktować jak zwykłe pole — używać w filtrach, eksportach i raportach bez dodatkowych kosztów wydajności przy odczycie.
W tym poradniku wyjaśnię, jak przechowywane pola obliczane wpisują się w model danych Odoo, pokażę jak je dodać przez Studio i przez moduł Pythona, przedstawię praktyczne zastosowania biznesowe oraz wskażę, czego unikać podczas implementacji.
Czym jest przechowywane pole obliczane w Odoo
W ORM Odoo każdy atrybut modelu reprezentuje fragment danych. Standardowe pola trzymają wartości wpisane przez użytkownika. Pole obliczane natomiast otrzymuje wartość z funkcji — to logika w kodzie decyduje o wyniku, a nie użytkownik.
Przechowywane pole obliczane to po prostu pole obliczane z ustawieniem store=True. Gdy zmienią się jego zależności, Odoo uruchamia funkcję obliczającą i zapisuje wynik do kolumny w tabeli, dzięki czemu wartość jest dostępna jak każde inne pole.
W praktyce w kodzie wygląda to według ustalonego wzorca:
Przykładowy wzorzec w Pythonie pokazuje definicję pola, dekorator zależności i samą metodę compute, która iteruje po rekordach i przypisuje wyliczoną wartość.
To właśnie parametr store=True odróżnia pole przechowywane od pola wyliczanego wyłącznie w locie. Bez niego wartość jest przeliczana przy każdym odczycie, ale nigdy nie jest utrwalana w bazie danych.
Dla użytkownika w interfejsie Odoo pole przechowywane wygląda identycznie jak każde inne: formularze, listy i raporty pokazują tę kolumnę, można jej używać w filtrach i grupowaniach. Nie ma widocznego wskaźnika, że wartość jest wynikiem obliczeń.
Przechowywane czy nieprzechowywane?
Ten wybór ma znaczenie przy projektowaniu rozwiązań w Odoo:
- Nieprzechowywane pole obliczane: wartość liczy się przy odczycie. Nie nadaje się do wyszukiwania, filtrowania ani grupowania. Oszczędza miejsce, ale nie uczestniczy bezpośrednio w zapytaniach SQL.
- Przechowywane pole obliczane: wynik zapisuje się w bazie, więc pole jest wyszukiwalne, filtrujące i eksportowalne. Zajmuje miejsce w tabeli tak jak zwykła kolumna.
Nie ma uniwersalnej odpowiedzi, które rozwiązanie jest „lepsze”. Wybór zależy od przeznaczenia pola — jeśli potrzebujesz filtrowania i agregacji, użyj store=True; jeśli tylko prezentacji na formularzu, wystarczy pole nieprzechowywane.
Jak działa to pole
Gdy zdefiniujesz przechowywane pole, ORM automatycznie ustawi mechanizmy wyzwalania przeliczeń na podstawie pól wskazanych w dekoratorze @api.depends().
Kiedy jedna z zależnych wartości się zmienia, Odoo oznacza rekord do przeliczenia, wywołuje metodę compute i zapisuje wynik do kolumny w bazie.
Cykl przeliczeń
Oto typowy przebieg operacji krok po kroku:
- Użytkownik albo proces automatyczny zmienia pole, które jest wymienione w @api.depends().
- Odoo wykrywa zmianę i identyfikuje rekordy zależne od tej wartości.
- Dla tych rekordów wywoływana jest metoda compute.
- Wynik obliczeń zostaje zapisany do kolumny w tabeli bazy danych.
- Pole jest teraz dostępne do wyszukiwania, filtrowania i eksportu z zaktualizowaną wartością.
W większości przypadków przeliczenie odbywa się od razu w tej samej transakcji. Przy dużych operacjach masowych Odoo może odroczyć część rekalkulacji i wykonać je w tle.
Zależności przez relacje
Dekorator @api.depends() obsługuje też kropkowaną notację do śledzenia pól na powiązanych modelach.
Przykład z użyciem partner_id.country_id.name pokazuje, że Odoo będzie monitorować zmianę zarówno referencji, jak i pola nazwy kraju.
W efekcie zmiana nazwy kraju na powiązanym rekordzie spowoduje automatyczne przeliczenie wszystkich zależnych rekordów — to jedna z mocnych stron architektury Odoo.
Wpływ na bazę danych
Ponieważ pole jest przechowywane, w tabeli PostgreSQL pojawia się realna kolumna. Dzięki temu zapytania SQL korzystają bezpośrednio z tej kolumny — wyszukiwania i filtry są szybkie, podobnie jak dla zwykłych pól.
Przykłady zastosowań w firmie
Przechowywane pola mają praktyczne zastosowanie w wielu procesach biznesowych. Poniżej pięć typowych scenariuszy z życia firm.
1. Sprzedaż: procent marży na liniach zamówień
Handlowcy chcą szybko ocenić marżę na pozycji zamówienia. Pole przechowywane może obliczać procent marży z ceny sprzedaży i kosztu zakupu i przechowywać go na linii. Dzięki temu menedżer sprzedaży może filtrować po marży, znaleźć nierentowne pozycje i grupować wyniki w raportach bez dodatkowych obliczeń.
2. CRM: dni bez aktywności dla leada
Na rekordzie leada można trzymać przechowywane pole, które liczy liczbę dni od ostatniej zaplanowanej aktywności. W połączeniu z zaplanowanym cronem, który codziennie wymusza przeliczenie, zespół handlowy może łatwo przefiltrować leady przekraczające próg bez ręcznego monitorowania.
3. Magazyn: netto dostępna ilość
Dla produktów z złożonymi regułami magazynowymi warto przechowywać wynik, np. ilość dostępna = stan magazynowy − ilość zarezerwowana. Skoro wartość jest zapisana, menedżerowie mogą sortować i filtrować produkty po dostępności bez uruchamiania kosztownych obliczeń zapasów dla każdego wiersza w widoku.
4. Księgowość: liczba przeterminowanych faktur na klienta
Na karcie kontrahenta pole przechowywane może zliczać aktualnie przeterminowane faktury. Dzięki temu dział księgowości może jednym kliknięciem posortować klientów według liczby zaległości — możliwe tylko dlatego, że wynik jest zapisany w bazie.
5. Produkcja: łączny szacowany czas pracy
W strukturze BOM pole przechowywane może sumować czasy operacji przypisanych do pozycji. Planer produkcji może filtrować i sortować BOM-y po całkowitym czasie pracy, co ułatwia planowanie zdolności produkcyjnych i harmonogramowanie. Aktualizacje są automatyczne przy zmianie operacji.
Tworzenie i modyfikowanie pola
Masz dwie ścieżki tworzenia przechowywanego pola: szybkie pola przez Odoo Studio lub pełna kontrola w module Pythona.
Tworzenie przez Odoo Studio
Studio pozwala dodać pole obliczane bez kodu. Przy tworzeniu pola typu Integer, Float czy Monetary można włączyć opcję formuły i wpisać wyrażenie przypominające Pythona. Studio zajmuje się śledzeniem zależności automatycznie.
Pola tworzone w Studio sprawdzą się, gdy logika to prosta arytmetyka między polami tego samego rekordu. To szybkie i nie wymaga środowiska developerskiego. Gdy jednak logika obejmuje pola z pokrewnych modeli, warunki czy agregacje po rekordach potomnych, Studio nie wystarczy i trzeba napisać moduł.
To kluczowa decyzja przy planowaniu dostosowań Odoo: Studio daje szybkość przy prostych przypadkach, ale Python daje elastyczność dla złożonych wymagań.
Tworzenie w module Pythona
Dla niestandardowych wymagań definiujemy pole w module Pythona. Poniżej przykładowa implementacja pola procentu marży na linii zamówienia sprzedaży.
Przykład kodu Pythona pokazuje dziedziczenie modelu sale.order.line, dodanie pola x_margin_pct z compute i store=True oraz metodę, która liczy marżę procentową, zabezpieczając przypadki bez ceny.
Po zainstalowaniu modułu Odoo tworzy kolumnę x_margin_pct w bazie, uruchamia przeliczenie dla istniejących rekordów i zaczyna śledzić zmiany pól price_unit i purchase_price.
Prefiks x_ to konwencja dla pól niestandardowych w Odoo, zapobiegająca konfliktom z polami systemowymi — standardowa praktyka w rozwoju Odoo.
Umożliwienie edycji przechowywanego pola
Domyślnie pola obliczane są tylko do odczytu. Aby użytkownik mógł nadpisać wartość ręcznie, dodaj metodę inverse. Odwrotna metoda uruchamia się przy zapisie i może zaktualizować źródłowe pola. To wzorzec przydatny, gdy wynik obliczeń jest dobrą wartością domyślną, ale czasem wymaga korekty manualnej.
Pola ze Studio a API XML-RPC
Zespół automatyzujący strukturę modeli przez XML-RPC może tworzyć standardowe pola przez ir.model.fields. Jednak dla przechowywanych pól z logiką Pythona sama metoda compute musi być w kodzie serwera. API sprawdzi się przy provisioning’u prostych pól, ale logika obliczeniowa zawsze wymaga modułu zainstalowanego na serwerze.
Dobre praktyki
Poniżej zbiór praktyk, które polecają doświadczeni konsultanci Odoo przy pracy z polami przechowywanymi.
Precyzyjnie deklaruj wszystkie zależności
W dekoratorze @api.depends() wymień każde pole, z którego korzysta metoda compute. Pominięcie któregoś sprawi, że rekordy nie będą się przeliczać po zmianie tej wartości. Przejrzyj implementację linia po linii i wypisz wszystkie używane pola.
Optymalizuj szybkość metod compute
Metoda compute uruchamia się dla wszystkich rekordów dotkniętych zmianą — w intensywnym systemie mogą to być tysiące wpisów. Unikaj dodatkowych zapytań do bazy w pętli; jeżeli musisz odwołać się do powiązanych danych, korzystaj z załadowanych pól zamiast dodatkowych search() tam, gdzie to możliwe.
Używaj store=True tylko wtedy, gdy jest potrzebne
Pola przechowywane zajmują miejsce i generują zapisy przy każdym przeliczeniu. Jeśli pole ma służyć tylko do wyświetlenia na formularzu, pole nieprzechowywane jest lżejsze. Podejmuj tę decyzję świadomie, zamiast od razu oznaczać wszystko jako store=True.
Obsługuj przypadki brzegowe w metodzie compute
Sprawdź puste wartości, brak powiązań i ryzyko dzielenia przez zero — to częste źródła cichych błędów. Dodaj zabezpieczenia i wartości domyślne, gdy obliczenie nie może zostać wykonane normalnie.
Zaplanowanie początkowego przeliczenia przy dużych tabelach
Instalacja modułu dodającego nowe przechowywane pole powoduje przeliczenie całej istniejącej tabeli. Przy setkach tysięcy wierszy migracja może potrwać długo — testuj na środowisku stagingowym i planuj ewentualne okna serwisowe lub przeliczanie w tle przy wdrożeniu do produkcji.
Unikaj cyklicznych zależności
Jeśli pole A zależy od pola B, a B od A, moduł zgłosi błąd przy ładowaniu. Projektuj zależności tak, aby miały kierunek i nie tworzyły pętli.
Częste pułapki
Zapomnienie store=True
To najczęstszy błąd. Pole wygląda poprawnie w widoku formularza podczas testów, ale nie działa w filtrach czy raportach, bo nie jest zapisane w bazie. Zastanów się z góry, czy pole ma być przeszukiwalne — jeśli tak, dodaj store=True od początku.
Pominięte zależności w @api.depends
Jeśli metoda odczytuje partner_id.country_id, a w dekoratorze podasz tylko partner_id, zmiana kraju nie wywoła przeliczenia. Wyśledź każdą drogę dostępu do pola i wymień wszystkie składowe w dekoratorze.
Ciche błędy w metodzie compute
Gdy compute rzuca wyjątek dla rekordu, Odoo pomija przeliczenie tego rekordu i zostawia poprzednią wartość. Błąd może pojawić się w logach serwera, ale użytkownik tego nie zobaczy — przez to dane mogą stać się nieaktualne. Testuj compute na rekordach z brakującymi i nietypowymi danymi.
Spadek wydajności przy dużych zbiorach danych
Metoda, która działała w dewelopmencie, może stać się wąskim gardłem w produkcji, gdy tabela urośnie do dziesiątek tysięcy rekordów. Zwróć uwagę na liczbę zapytań wykonywanych na rekord — jedno dodatkowe zapytanie razy 10 000 rekordów to 10 000 zapytań przy jednej operacji zapisu.
Używanie sudo() w compute
Wywoływanie sudo() w metodzie compute, by ominąć uprawnienia, jest ryzykowne. Jeśli obliczana wartość ujawnia dane, do których aktualny użytkownik nie ma dostępu, compute może złamać model uprawnień Odoo. Korzystaj z sudo() tylko po przemyśleniu skutków bezpieczeństwa.
Oczekiwanie natychmiastowego przeliczenia we wszystkich kontekstach
W interaktywnych operacjach przeliczenie zwykle jest synchroniczne, ale przy imporcie masowym, zadaniach w tle lub specyficznych operacjach ORM Odoo może odroczyć przeliczenia. Nie zakładaj, że zapisana wartość zawsze będzie natychmiast aktualna — sprawdź zachowanie w kontekście użycia pola.
Podsumowanie
Podsumowując: przechowywane pola obliczane to potężne narzędzie w Odoo. Automatyzują obliczenia, utrzymują spójność danych i pozwalają na efektywne wyszukiwanie oraz eksport bez dodatkowej pracy użytkowników.
Główne przesłania na koniec:
- Używaj store=True, gdy pole ma być wyszukiwalne, filtrowalne lub eksportowalne.
- Zawsze deklaruj wszystkie zależności w @api.depends(), także ścieżki między modelami.
- Optymalizuj metody compute pod kątem wydajności i obsługuj przypadki brzegowe.
- Dla prostych formuł użyj Odoo Studio; dla logiki złożonej — kodu Pythona.
- Przy wdrożeniu na dużych tabelach zaplanuj początkowe przeliczenie i testy na stagingu.
Niezależnie od tego, czy tworzysz moduł, rozszerzasz model, czy dopiero poznajesz typy pól Odoo, warto dobrze zrozumieć przechowywane pola obliczane — łączą warstwę ORM, bazę danych i logikę biznesową.
Potrzebujesz wsparcia przy wdrożeniu Odoo?
Dasolo pomaga firmom wdrażać, dopasowywać i optymalizować Odoo w różnych obszarach działalności. Jeśli chcesz dodać pola obliczane, zbudować raporty oparte na wyliczonych danych lub rozwinąć własne rozwiązania, nasz zespół ma doświadczenie, by pomóc.
Skontaktuj się z nami — chętnie omówimy Twój przypadek i zaproponujemy odpowiednie rozwiązanie dla Twojego biznesu.