[IMP] migration: say why Odoo changing its own template is our problem

The declarations view showed a 12.0 template becoming a 13.0 extension and
stopped there. Read alone it says « Odoo changed its code », and the obvious
reaction is to ask why that is anyone's problem — which is exactly what it
prompted.

It is not the problem. On a database without a copy the module upgrade
rewrites the view and nothing breaks. It breaks because a COPY exists and
froze the old shape. That sentence was missing, and so was the other half of
the answer: what the copy actually holds. A copy identical to the view it
shadows costs nothing to neutralize; one carrying five lines of theme hooks
costs those five lines. Both are now stated where the question arises.

Checked on the database mid-migration: the copy that raised the question
reports +5/-3, and the two other cases — identical to its twin, and a page
with no twin at all — each say so.

--- FR ---

La vue des déclarations montrait un gabarit 12.0 devenant une extension 13.0
et s'arrêtait là. Lue seule, elle dit « Odoo a changé son code », et la
réaction naturelle est de demander en quoi cela nous regarde — ce qu'elle a
justement provoqué.

Ce n'est pas le problème. Sur une base sans copie, la mise à jour du module
réécrit la vue et rien ne casse. Ça casse parce qu'une COPIE existe et a figé
l'ancienne forme. Cette phrase manquait, et l'autre moitié de la réponse
aussi : ce que la copie contient réellement. Une copie identique à la vue
qu'elle masque ne coûte rien à neutraliser ; une qui porte cinq lignes
d'ancres de thème coûte ces cinq lignes. Les deux sont dites là où la question
se pose.

Vérifié sur la base en cours de migration : la copie qui a soulevé la question
rapporte +5/-3, et les deux autres cas — identique à sa jumelle, et page sans
jumelle — le disent chacun.

Assisted-by: Claude Opus 5
This commit is contained in:
Mathieu Benoit 2026-08-12 01:28:50 -04:00
parent 98be22cb77
commit fcfbcd25ce

View file

@ -273,12 +273,56 @@ def render_shape(finding):
)
lines += [f" -> {shape}", ""]
lines += [
" A copy frozen in one shape cannot be applied in the other: Odoo",
" stops on « cannot be located in parent view ».",
" Odoo changing the shape of its own template is NOT the problem: on a",
" database without a copy, the module upgrade rewrites the view and",
" nothing breaks. It breaks here because a COPY exists and froze the",
" old shape — the copy follows the module and becomes an extension,",
" while still holding a standalone template. Odoo then applies that",
" template as an inheritance spec and stops on « cannot be located in",
" parent view ».",
"",
]
# Sans ceci, cette vue se lit comme « du code Odoo qui change », et l'on
# se demande pourquoi c'est notre affaire. Ce qui la rend nôtre est ce que
# la copie contient — la seule chose que neutraliser ferait perdre.
lines += _what_the_copy_holds(finding)
return "\n".join(lines)
def _what_the_copy_holds(finding):
"""Ce que la copie porte en propre, dit en une ou deux lignes.
Relie les deux moitiés de la question : le mécanisme explique POURQUOI ça
casse, ceci dit CE QU'IL EN COÛTE. Une copie identique à sa jumelle ne
coûte rien, et le savoir change la décision.
"""
if finding["module_id"] is None:
return [
" This copy has no module view of that name: it is a page made in",
" the website editor, and nothing else holds its content.",
]
left = finding["module_arch"].splitlines()
right = finding["copy_arch"].splitlines()
diff = [
line
for line in difflib.unified_diff(left, right, lineterm="", n=0)
if line[:1] in "+-" and not line.startswith(("+++", "---"))
]
if not diff:
return [
" This copy is IDENTICAL to the module view it shadows: it holds no",
" customization at all, so neutralizing it loses nothing.",
]
n_plus = sum(1 for x in diff if x.startswith("+"))
n_minus = sum(1 for x in diff if x.startswith("-"))
return [
f" This copy differs from the module view by +{n_plus}/-{n_minus}"
" line(s):",
" that is the customization, and all that neutralizing gives up.",
" Run without --shape to read it.",
]
def render_all(lst_finding, shape=False):
"""The whole report, one block per finding."""
if not lst_finding: