Przejdź do głównej treści
Programowanie · Odoo 19, 18 i 17

Utrzymanie tego samego modułu w Odoo 19, 18 i 17: co się zmienia, co psuje się po cichu i jak testować naprawdę

Jedna gałąź na wersję, jeden prawdziwy build na wersję i inwentarz różnic. Co gryzie między 17, 18 i 19, zweryfikowane w kodzie źródłowym Odoo, i jak testujemy to na 105 opublikowanych modułach.

Ekran Apps w Odoo z zainstalowanymi modułami w prawdziwym backendzie

Utrzymywanie modułu Odoo w wersjach 19, 18 i 17 jednocześnie to nie skopiowanie folderu do trzech gałęzi i zmiana numeru w manifeście. To jedna gałąź git na wersję, jeden build z prawdziwą instalacją na wersję i inwentarz różnic między wersjami, przeglądany przy każdym wydaniu. Utrzymujemy 105 opublikowanych modułów w trzech wersjach; oto lista tego, co się psuje, razem z naszymi własnymi błędami.

Jedna karta, trzy gałęzie (19.0, 18.0 i 17.0 o tej samej nazwie technicznej) jest opisane w artykule publikacja modułu w App Store Odoo. Tutaj piszemy o tym, co dzieje się wewnątrz każdej gałęzi.

Co zmienia się między 17, 18 i 19, a nie ma tego w przewodnikach po nowościach

Nowości Odoo 19 opisują funkcje; ta tabela opisuje to, co zawodzi przy instalacji lub, co gorsza, nie zawodzi. Sprawdzona na kodzie źródłowym trzech wersji, a nie na changelogu.

CoOdoo 17Odoo 18Odoo 19Jak zawodzi
Ograniczenia SQL_sql_constraints_sql_constraintsmodels.ConstraintPo cichu: 19 ostrzega i działa dalej; ograniczenie nie istnieje
Cron bez limitunumbercall=-1 wymaganePole nie istniejePole nie istnieje17: bez niego uruchamia się raz i wyłącza. 18/19: z nim moduł się nie instaluje
Grupy użytkownikagroups_idgroups_idgroup_idsNieprawidłowe pole przy instalacji lub uruchomieniu
Użytkownicy grupyusersusersuser_idsTak samo; wywrócił nam cron powiadomień w 17 i 18
Kategoria grupycategory_idcategory_idprivilege_id → res.groups.privilegeInvalid field 'category_id' in 'res.groups'
Widok listy<tree><list><list>Nieprawidłowy typ widoku; xpath //tree nie pasują

Najgroźniejszy: _sql_constraints

W Odoo 19 _sql_constraints jest ignorowane: loader modeli zapisuje w logu „no longer supported” i idzie dalej. Moduł się instaluje, testy przechodzą, a ograniczenie unikalności nie istnieje w bazie danych; konektor, który polega na unique(external_id), żeby nie dublować zamówień, traci tę gwarancję, a nic o tym nie informuje. Nowa forma, models.Constraint('unique(...)', 'mensaje'), istnieje tylko w 19. Odoo dostarcza konwerter, odoo/cli/upgrade_code.py --script 18.1-00-sql-constraint, z pułapką, której nauczyliśmy się przy migracji własnych modułów: zwraca kod wyjścia 1, gdy zmienił pliki, czyli 1 oznacza sukces. Jedyne, co dowodzi, że ograniczenie działa, to test próbujący wstawić duplikat.

Ten, który nas zaskoczył: numbercall

W Odoo 17 ir.cron.numbercall ma domyślnie wartość 1: cron zadeklarowany bez tego pola wykonuje się raz i sam się dezaktywuje, bez błędu i bez linii w logu. Odoo 18 usunęło to pole i od tego momentu jego deklaracja uniemożliwia instalację, więc gałąź 17 potrzebuje <field name="numbercall">-1</field> w każdym cronie, a pozostałe dwie nie mogą go mieć. We wrześniu 2026 przeprowadziliśmy audyt własnych portów do 17 i znaleźliśmy 14 cronów bez tego pola w czterech konektorach, w dwóch z działającym demo: nikt nie testuje synchronizacji okresowej, czekając na drugie wykonanie. Poprawka to dwie linie na cron; kosztem było to, że nie mieliśmy tego w inwentarzu.

Co Odoo 17 waliduje, a 18 i 19 przepuszczają

Ruch nie idzie tylko w górę. Odoo 17 wymaga, aby pole użyte w modyfikatorze widoku (invisible, readonly) znajdowało się w tym samym widoku; 18 i 19 tego nie sprawdzają. Moduł napisany w 19 i przeportowany wstecz zawodzi w 17 z błędem Field 'state' used in modifier 'invisible' (state == 'draft') must be present in view but is missing. Dlatego port nie jest uznany za dobry, dopóki nie zainstaluje się w najstarszej wersji. Więcej przypadków w moduł Odoo 19 się kompiluje, ale nie instaluje i błędy migracji do Odoo 19.

Strategia gałęzi: jedna na wersję i żadnych warunków

Gałąź główna to ta z najnowszą wersją, dziś 19.0. Każda zmiana jest wprowadzana i testowana tam, a potem przenoszona wstecz przez cherry-pick do 18.0 i 17.0, z dostosowaniem tego, co wymusza tabela. Czego nie robimy nigdy, to jeden kod z if version >= 18 w środku: powstaje moduł z trzema API naraz, o którym nikt nie wie, którą z trzech gałęzi właśnie testuje. Dla nowych konektorów generujemy gałęzie 18 i 17 ze źródła 19 skryptem, który koduje powyższą tabelę; nie zastępuje testu, zastępuje kopiuj-wklej.

Dwa wyjątki, oba pytają o możliwość, a nie o numer wersji:

  • Wykrywanie pola lub modelu w czasie wykonania. Jeśli moduł zapisuje do pola, które nie istnieje we wszystkich wersjach, pytamy rejestr: 'mobile' in partner._fields lub 'hr.expense.sheet' in self.env. Patrzy na to, co jest, a nie na to, co powinno być.
  • Testy, które są pomijane, gdy możliwości brak, z skipTest uzależnionym od obecności pola. Nie wolno natomiast przechodzić z wynikiem 0 z 0: jeden z naszych modułów miał zero testów w trzech gałęziach; po napisaniu 25 wyszły trzy prawdziwe błędy, jeden to cron powiadomień używający user_ids w 17 i 18.

Test, który się liczy: prawdziwa instalacja w trzech wersjach

Ani linter, ani import, ani zielony zestaw testów na Twoim komputerze nie dowodzą, że moduł się instaluje: XML z numbercall jest poprawnym XML-em, a xpath na //tree jest poprawnym xpathem. Błąd pojawia się dopiero, gdy Odoo ładuje moduł na bazie danych tej wersji. Nasza zasada: zmiana nie istnieje, dopóki nie została zainstalowana, na czysto, w prawdziwym Odoo każdej wersji, a jeśli moduł jest już u kogoś wdrożony, także jako aktualizacja z poprzedniej wersji.

Aktualizacja ma znaczenie z konkretnego powodu: grupy base mają noupdate="1", więc moduł, który dodaje swoją grupę menedżera do base.group_system przez implied_ids, uzyskuje to w nowej instalacji, a przy aktualizacji Odoo po cichu to ignoruje. Widzieliśmy to na produkcji w Odoo.sh: grupa została bez członków i nawet administrator nie mógł odczytać pola z danymi uwierzytelniającymi, które od niej zależało. Potrzebna jest migracja w migrations/ oprócz XML oraz test, który zawodzi, jeśli grupa zostaje pusta.

Używamy dwóch rodzajów stanowisk. Stanowisko testowe, jedno Odoo na wersję (lokalnie lub w kontenerach), które instaluje każdy moduł i uruchamia jego zestaw testów w trzech wersjach: tam przeszło 37 aplikacji naszej pierwszej kampanii, tam konektor Mirakl przechodzi 163 testy w każdej wersji, a konektor Amazon 786 w 19 i 685 w 18 i 17. Oraz Odoo.sh, jeden build na gałąź, do prawdziwej instalacji. Jedna runda bramki to około dziesięć minut i każda jest tego warta: dwa moduły, które kosztowały nas w tym roku najwięcej, miały lokalnie wszystko na zielono i nie instalowały się (zielone testy, a konektor zawodzi na produkcji).

Nasz własny błąd: pierwszą kampanię 37 aplikacji uznaliśmy za zakończoną, gdy instalacja i testy były na zielono. Cztery dni później drugi przebieg po 47 aplikacjach w Odoo 17 i 18 Community, z użyciem każdego widoku na danych, dał 61 z 94 czystych przebiegów: 28 błędów instalacji i 4 błędy użycia, około jedenastu prawdziwych błędów. Xpath na //list w 17, kotwice na polach, które w 17 nie istnieją. Kod przeportowany wstecz bez instalacji w wersji docelowej.

Rzeczywisty koszt i kiedy porzucić 17

Trzy gałęzie nie potrajają programowania, bo logikę pisze się raz. Potrajają testowanie i publikację: trzy instalacje na zmianę, trzy zestawy testów, trzy karty do sprawdzenia i inwentarz do przeczytania od nowa przy każdej nowej wersji, jak 20 (wrzesień 2026), która dodaje wiersze do tabeli. Odoo wydaje jedną wersję główną rocznie i wspiera trzy ostatnie; w dniu publikacji 20 wersja 17 wypada z tego okna i przestaje być utrzymywana. Do tego czasu gałąź 17 dostaje każdą poprawkę: kto wciąż jest na 17, planuje migrację i zmigruje z Twoim modułem lub bez niego.

Koszt realnie obniża inwentarz jako narzędzie: skaner tylko do odczytu, który wskazuje, która linia nadepnie na którą różnicę, zależnie od wersji w manifeście. Nasz oznaczył kiedyś 67 modułów za zapis category_id w grupie i wszystkie 67 były w porządku: ta linia znajdowała się w <record> z res.groups.privilege, czyli dokładnie w poprawnym rozwiązaniu dla 19. Reguła patrzyła na linię, a nie na rekord, który ją otacza. Liczba bez otwarcia kodu nie jest daną, a reguła, która wskazuje poprawne rozwiązanie, uczy ignorować narzędzie.

Lista kontrolna przed każdym push

  1. Ograniczenia: models.Constraint w 19, _sql_constraints w 18 i 17 oraz test próbujący wstawić duplikat.
  2. Crony: numbercall=-1 we wszystkich w gałęzi 17; żadnego w 18 i 19.
  3. Grupy: group_ids, user_ids i privilege_id (model res.groups.privilege) w 19; groups_id, users i category_id w 18 i 17.
  4. Widoki: <list> w 19 i 18, <tree> w 17; w 17 każde pole użyte w modyfikatorze jest w widoku.
  5. Czysta instalacja w trzech wersjach oraz aktualizacja z poprzedniej wersji, jeśli moduł jest już wdrożony.
  6. Pełny zestaw testów na zielono w trzech wersjach i niepusty: 0 z 0 to nie zielono.
  7. Żadnego __pycache__ w commicie. git add -A po testach wrzucił nam 1 078 plików .pyc do 35 z 37 repozytoriów.

Tak działamy, gdy partner Odoo zleca nam utrzymanie swojego modułu w kilku wersjach albo gdy firma doradcza potrzebuje, aby rozwiązanie działające u niej w 17 trafiło do 19 bez utraty 17. Droga na skróty to to, co odkrywa pierwszy kupujący kartę.

Najczęstsze pytania

Czy mogę utrzymywać jeden kod z warunkami zależnymi od wersji?

App Store publikuje z gałęzi 17.0, 18.0 i 19.0, więc i tak kończysz z trzema gałęziami, tyle że z trzema API wymieszanymi w każdej. Jedna gałąź na wersję to więcej powtórzonego kodu i znacznie mniej błędów. Jedyne akceptowalne warunki pytają o możliwość, nigdy o numer wersji.

Dlaczego nie wystarczy, że kod się kompiluje i testy przechodzą lokalnie?

Ponieważ błędy wersji pojawiają się przy ładowaniu modułu na bazie danych tej wersji, a nie przy imporcie kodu; a niektóre, jak _sql_constraints w 19, w ogóle nie zawodzą: trzeba udowodnić, że ograniczenie istnieje.

Przydatne linki w FlexigoTech

Publikacja modułu w App Store OdooJedna karta, trzy gałęzie: repozytorium, manifest i ponowne skanowanieModuł Odoo 19 się kompiluje, ale nie instalujePułapki API w 19, których analiza statyczna nie widziZielone testy, a konektor zawodzi na produkcjiDlaczego test, który się liczy, to prawdziwa instalacjaMigracja do Odoo 19Usługa migracji z wliczonymi modułami własnymi

Masz moduł w jednej wersji, a potrzebujesz go w trzech?

Instalujemy go w 19, 18 i 17, mówimy Ci, co psuje się w każdej, i zostawiamy z osobną gałęzią na wersję i zielonym buildem. Napisz na comercial@flexigobe.com lub zadzwoń pod numer +34 616 809 504.

Porozmawiaj z inżynierem