[ADD] migration: offer the theme uninstall where the theme is blamed
The question was asked once, at step one, and a flag kept it from ever
returning. But a theme only becomes incompatible at a given bump, hours
later -- asking at the start could not cover it. Entry [6] appears when
TWO conditions hold: a theme is installed, and the recent output names
it. One condition alone would offer to wreck the site's design over an
error that has nothing to do with it.
Also, the 17 to 18 rename OpenUpgrade declares and never applies:
forum.post / tag_ids : column1 is now 'forum_post_id' ('forum_id')
website_forum/18.0.1.2/ holds only an analysis, no script, so the load
dies on a foreign key to a column that does not exist and leaves the
database half migrated.
--- FR ---
La question était posée une fois, à l'étape 1, et un drapeau
l'empêchait de revenir. Or un thème ne devient incompatible qu'à un
palier donné, des heures plus tard : la poser au départ ne pouvait pas
suffire. L'entrée [6] paraît quand DEUX conditions tiennent — un thème
installé, et la sortie récente qui le nomme. Une seule offrirait de
casser le design du site pour une panne étrangère.
Et le renommage 17 → 18 qu'OpenUpgrade déclare sans jamais l'appliquer :
forum.post / tag_ids : column1 is now 'forum_post_id' ('forum_id')
website_forum/18.0.1.2/ ne porte qu'une analyse, aucun script : le
chargement meurt sur une clé étrangère vers une colonne absente et
laisse la base à moitié migrée.
Assisted-by: Claude Opus 5
2026-08-22 01:52:53 -04:00
|
|
|
-- © 2021-2026 TechnoLibre (http://www.technolibre.ca)
|
|
|
|
|
-- License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
|
--
|
|
|
|
|
-- Correctifs à appliquer AVANT qu'OpenUpgrade ne migre vers Odoo 18.
|
|
|
|
|
--
|
|
|
|
|
-- En SQL et non en Python : ce fichier tourne sur une base encore en
|
|
|
|
|
-- 17, que le code de la 18 ne saurait pas charger.
|
|
|
|
|
|
|
|
|
|
-- forum_tag_rel : la colonne qui pointe vers forum.post s'appelait
|
|
|
|
|
-- `forum_id` — un nom trompeur, elle ne désignait pas un forum. La 18 la
|
|
|
|
|
-- nomme `forum_post_id`.
|
|
|
|
|
--
|
|
|
|
|
-- OpenUpgrade le DÉCLARE dans son analyse :
|
|
|
|
|
-- website_forum / forum.post / tag_ids (many2many)
|
|
|
|
|
-- : column1 is now 'forum_post_id' ('forum_id') [forum_tag_rel]
|
|
|
|
|
-- mais `website_forum/18.0.1.2/` ne contient aucun script : rien ne
|
|
|
|
|
-- l'applique. Le chargement casse alors sur
|
|
|
|
|
-- column "forum_post_id" referenced in foreign key constraint does not exist
|
|
|
|
|
-- et la base reste à moitié migrée.
|
|
|
|
|
--
|
|
|
|
|
-- Les deux conditions rendent l'ordre rejouable : rien à faire si la
|
|
|
|
|
-- table n'existe pas (website_forum non installé) ni si le renommage a
|
|
|
|
|
-- déjà eu lieu.
|
|
|
|
|
DO $$
|
|
|
|
|
BEGIN
|
|
|
|
|
IF EXISTS (
|
|
|
|
|
SELECT 1 FROM information_schema.columns
|
|
|
|
|
WHERE table_name = 'forum_tag_rel' AND column_name = 'forum_id'
|
|
|
|
|
) AND NOT EXISTS (
|
|
|
|
|
SELECT 1 FROM information_schema.columns
|
|
|
|
|
WHERE table_name = 'forum_tag_rel' AND column_name = 'forum_post_id'
|
|
|
|
|
) THEN
|
|
|
|
|
ALTER TABLE forum_tag_rel RENAME COLUMN forum_id TO forum_post_id;
|
|
|
|
|
RAISE NOTICE 'forum_tag_rel.forum_id renommee en forum_post_id';
|
|
|
|
|
END IF;
|
|
|
|
|
END $$;
|
[FIX] migration 17→18 : retirer la vue morte account_root
Odoo 17 créait une vue SQL pour account.root ; en 18 le modèle porte
_auto = False et _table_query = '0' : son nom n'entre plus dans aucune
requête, et le contrôle des tables manquantes ignore ces modèles.
Elle n'est pas seulement morte, elle est FAUSSE : bâtie sur la colonne code que
l'ORM 18 n'écrit plus, elle rend trop peu de racines. Elle est aussi l'unique
épingle des deux colonnes héritées où database_cleanup échoue, d'où ce retrait
avant le chargement en 18 ; aucun DROP COLUMN, OpenUpgrade les lit encore.
Sur base jetable, l'ordre est rejouable et laisse intactes une vue d'une
autre forme, une vue dont dépend un objet, une table homonyme.
--- EN ---
Odoo 17 created a SQL view for account.root; in 18 the model carries
_auto = False and _table_query = '0': its name no longer enters any query,
and the missing-table check ignores such models.
It is not merely dead, it is WRONG: built on the code column the 18 ORM no
longer writes, it returns too few roots. It is also the sole pin holding the
two legacy columns database_cleanup fails on, hence its removal before the
load into 18; no DROP COLUMN, OpenUpgrade still reads them afterwards.
On a throwaway database the statement replays cleanly and leaves alone a
view of another shape, a view something depends on, a table of that name.
Assisted-by: Claude Opus 5
(cherry picked from commit b50e2308cbdd1efdfa914497062919eab1bbd329)
2026-08-28 01:20:25 -04:00
|
|
|
|
|
|
|
|
-- account_root : une VUE SQL qu'Odoo 17 créait pour le modèle
|
|
|
|
|
-- `account.root` (son init() bâtissait la vue sur account_account.code),
|
|
|
|
|
-- et que la 18 a orphelinée sans la retirer. En 18 le modèle porte
|
|
|
|
|
-- `_auto = False` et `_table_query = '0'` : son nom n'entre plus dans
|
|
|
|
|
-- aucune requête, et registry.py exclut ces modèles du contrôle des
|
|
|
|
|
-- tables manquantes — Odoo ne la recréera donc jamais.
|
|
|
|
|
--
|
|
|
|
|
-- Elle n'est pas seulement morte, elle est FAUSSE : bâtie sur la colonne
|
|
|
|
|
-- `code` que l'ORM 18 n'écrit plus, elle rendait 14 racines là où la
|
|
|
|
|
-- donnée vivante (code_store) en portait 31. Un objet du schéma public
|
|
|
|
|
-- qui répond faux, sans avertir, à qui l'interroge à la main ou par un
|
|
|
|
|
-- outil décisionnel.
|
|
|
|
|
--
|
|
|
|
|
-- Elle est AUSSI l'unique épingle de deux colonnes héritées :
|
|
|
|
|
-- database_cleanup a purgé 110 colonnes orphelines au palier 18 et n'a
|
|
|
|
|
-- échoué que sur account_account.code et .company_id, dont elle dépend.
|
|
|
|
|
-- On retire donc l'obstacle ICI, et l'outil déjà en place finit sa passe
|
|
|
|
|
-- APRÈS le chargement en 18.
|
|
|
|
|
--
|
|
|
|
|
-- On ne supprime SURTOUT PAS les colonnes ici : le post-migration
|
|
|
|
|
-- d'OpenUpgrade les lit encore pour remplir code_store et company_ids,
|
|
|
|
|
-- et il tourne après ce fichier.
|
|
|
|
|
DO $$
|
|
|
|
|
DECLARE
|
|
|
|
|
oid_vue oid;
|
|
|
|
|
oid_table oid;
|
|
|
|
|
nb_dependants int;
|
|
|
|
|
BEGIN
|
|
|
|
|
SELECT c.oid INTO oid_vue
|
|
|
|
|
FROM pg_class c
|
|
|
|
|
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
|
|
|
WHERE n.nspname = 'public'
|
|
|
|
|
AND c.relname = 'account_root'
|
|
|
|
|
AND c.relkind = 'v';
|
|
|
|
|
IF oid_vue IS NULL THEN
|
|
|
|
|
-- Ni vue, ni base sans le module account : rien à faire. C'est
|
|
|
|
|
-- aussi ce qui rend l'ordre rejouable après un premier passage.
|
|
|
|
|
RETURN;
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
oid_table := to_regclass('public.account_account');
|
|
|
|
|
IF oid_table IS NULL THEN
|
|
|
|
|
RAISE NOTICE 'account_root sans account_account : laissee en place';
|
|
|
|
|
RETURN;
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
-- La vue morte est CELLE QUI LIT account_account.code. Le contrôle
|
|
|
|
|
-- est structurel et non textuel : si une version future d'Odoo
|
|
|
|
|
-- recree un account_root d'une autre forme, on n'y touche pas.
|
|
|
|
|
IF NOT EXISTS (
|
|
|
|
|
SELECT 1
|
|
|
|
|
FROM pg_depend d
|
|
|
|
|
JOIN pg_rewrite r ON r.oid = d.objid
|
|
|
|
|
JOIN pg_attribute a ON a.attrelid = d.refobjid
|
|
|
|
|
AND a.attnum = d.refobjsubid
|
|
|
|
|
WHERE r.ev_class = oid_vue
|
|
|
|
|
AND d.refobjid = oid_table
|
|
|
|
|
AND a.attname = 'code'
|
|
|
|
|
) THEN
|
|
|
|
|
RAISE NOTICE 'account_root ne lit pas account_account.code : laissee en place';
|
|
|
|
|
RETURN;
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
-- Rien ne doit en dependre. Le jour ou quelque chose en dependrait,
|
|
|
|
|
-- APPRENDRE plutot que detruire : on le dit et on passe. Jamais de
|
|
|
|
|
-- CASCADE, et jamais d echec — ce fichier tourne sous ON_ERROR_STOP.
|
|
|
|
|
SELECT count(DISTINCT ev.oid) INTO nb_dependants
|
|
|
|
|
FROM pg_depend d
|
|
|
|
|
JOIN pg_rewrite r ON r.oid = d.objid
|
|
|
|
|
JOIN pg_class ev ON ev.oid = r.ev_class
|
|
|
|
|
WHERE d.refobjid = oid_vue
|
|
|
|
|
AND ev.oid <> oid_vue;
|
|
|
|
|
IF nb_dependants > 0 THEN
|
|
|
|
|
RAISE NOTICE 'account_root : % objet(s) en dependent, laissee en place',
|
|
|
|
|
nb_dependants;
|
|
|
|
|
RETURN;
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
DROP VIEW public.account_root;
|
|
|
|
|
RAISE NOTICE 'vue morte account_root supprimee (Odoo 18 ne la recree pas)';
|
|
|
|
|
END $$;
|