Files
ODOO4projects.com/blog/posts/odoo-19-to-20-invoice-migration-with-n8n-and-mcp.html
webmaster 9d14652a57 hide [hidden] site-wide: one [hidden] { display:none !important } in style.css
A filtered-out directory card stayed visible. The card is targeted by an
id-based ready-state selector (`.js #directory[data-dir-state="ready"]
#dirGrid > .dev-card`, two ids), which outranked the old per-element patch
`.dev-card[hidden] { display:none }` — so `card.hidden = true` set the
attribute and every card stayed on screen while the count line correctly
read "Showing 1 of 4".

Fix: one rule, `[hidden] { display:none !important }`, early in /style.css.
A class- or id-based `display` can no longer beat the attribute, so this
cannot recur with the next class that sets `display`. The per-element
`[hidden]` patches are removed so a single mechanism governs:
 - odoo-community-developers: cards + filter row + pager + modal
   (load-bearing — each sets `display`); focus-sliders/noscript (redundant)
 - index/de/es `.hero-notice[hidden]`, odoo-black-friday `.ads-consent[hidden]`
   (redundant — those elements set no `display`)
Re-fingerprint local assets (style.css changed): /style.css?v 3332fbba93 ->
061517baef across 51 pages.

Measured in a real browser (local harness, stubbed webhook; no record created,
no probe write) — computed display + geometry, never the `hidden` property:
 - filtered to one card: the other three report display:none and height 0;
   the visible set (1) equals the count line ("Showing 1 of 4") — 1 == 1
 - HEAD (pre-fix) reproduces the defect: 3 cards hidden=true yet display:flex,
   height 599.38 each; 4 visible vs the count's 1
 - pager: display:none height 0 with one page; display:flex height 66.83 with
   12 cards (Page 1 of 2); page 2 hides page-1 cards (display:none)
 - modals closed: display:none height 0; load state: cards 0, pager/empty
   display:none, filter row visibility:hidden by design
 - 375 px: same result, no horizontal overflow; avatar ask-row top identical
   across a row (2053.97 / links 2086.91)
2026-11-03 22:34:39 +00:00

181 lines
9.4 KiB
HTML

<!doctype html>
<html lang="en-US">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>This is how i updated my Invoices from Odoo 19 to Odoo 20 with n8n and much MCP | ODOO4projects</title>
<meta
name="description"
content="Copying invoices from one Odoo instance to another by hand is the kind of task that looks small until you are on invoice"
/>
<link
rel="canonical"
href="https://odoo4projects.com/blog/posts/odoo-19-to-20-invoice-migration-with-n8n-and-mcp.html"
/>
<meta
property="og:title"
content="This is how i updated my Invoices from Odoo 19 to Odoo 20 with n8n and much MCP"
/>
<meta
property="og:description"
content="Copying invoices from one Odoo instance to another by hand is the kind of task that looks small until you are on invoice"
/>
<meta property="og:type" content="article" />
<meta property="og:image" content="https://image.office.odoo4projects.com/img/much.png" />
<meta name="twitter:card" content="summary_large_image" />
<link rel="icon" href="../../images/favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Inter+Tight:wght@500;600;700;800&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
/>
<link rel="stylesheet" href="../../style.css?v=061517baef" />
<style>
:root {
--primary: #714b67;
--dark: #212529;
--border: #e5e3e0;
--radius: 0.6rem;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: Inter, sans-serif;
font-size: 1rem;
line-height: 1.7;
background: #fff;
color: #212529;
}
.container { width: 80%; max-width: 800px; margin: 0 auto; padding: 0 20px; }
@media (max-width: 768px) { .container { width: 100%; } }
.blog-header { padding: 2rem 0; border-bottom: 1px solid var(--border); }
.blog-header a { color: var(--primary); text-decoration: none; font-size: 0.85rem; }
.blog-header a:hover { text-decoration: underline; }
.post-title {
font-family: "Inter Tight", sans-serif;
font-size: clamp(1.5rem, 3vw, 2.2rem);
font-weight: 700; margin: 1.5rem 0 0.5rem; color: var(--dark);
}
.post-meta { color: rgba(33,37,41,0.5); font-size: 0.85rem; margin-bottom: 2rem; }
.post-image { width: 100%; border-radius: 0.75rem; margin-bottom: 2rem; box-shadow: 0 2px 8px rgba(13,27,46,0.08); }
.post-content h2 {
font-family: "Inter Tight", sans-serif;
font-size: 1.3rem; margin-top: 2rem; color: var(--dark);
}
.post-content p { margin: 1rem 0; color: rgba(33,37,41,0.7); line-height: 1.7; }
.post-content ul, .post-content ol { padding-left: 1.5rem; }
.post-content li { margin: 0.5rem 0; color: rgba(33,37,41,0.7); }
.post-content a { color: var(--primary); }
.post-footer { padding: 2rem 0; border-top: 1px solid var(--border); margin-top: 2rem; }
.post-footer a { color: var(--primary); text-decoration: none; }
.post-footer a:hover { text-decoration: underline; }
</style>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "This is how i updated my Invoices from Odoo 19 to Odoo 20 with n8n and much MCP",
"datePublished": "2026-10-23",
"url": "https://odoo4projects.com/blog/posts/odoo-19-to-20-invoice-migration-with-n8n-and-mcp.html",
"image": "https://image.office.odoo4projects.com/img/much.png",
"author": {
"@type": "Organization",
"name": "ODOO4projects",
"url": "https://odoo4projects.com/"
},
"publisher": {
"@type": "Organization",
"name": "ODOO4projects",
"url": "https://odoo4projects.com/",
"logo": {
"@type": "ImageObject",
"url": "https://odoo4projects.com/images/logo.png"
}
}
}
</script>
<!-- Privacy-friendly analytics by Plausible -->
<script async src="https://plausible.office.odoo4projects.com/js/pa-NhA7Rf-79VopXFQXhSS4e.js"></script>
<script>
window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};
plausible.init()
</script>
</head>
<body>
<main>
<div class="container">
<div class="blog-header">
<a href="../../index.html#blog">&larr; Back to Blog</a>
</div>
<article>
<h1 class="post-title">This is how i updated my Invoices from Odoo 19 to Odoo 20 with n8n and much MCP</h1>
<div class="post-meta">October 23, 2026 &middot; Tips</div>
<img src="https://image.office.odoo4projects.com/img/much.png"
alt="This is how i updated my Invoices from Odoo 19 to Odoo 20 with n8n and much MCP"
class="post-image" loading="lazy" />
<div class="post-content">
<p>Copying invoices from one Odoo instance to another by hand is the kind of task that looks small until you are on invoice forty. I moved mine <a href="../../odoo-community-upgrade.html">from Odoo 19 to Odoo 20</a> with an <a href="../../odoo-n8n-masterclass/">n8n workflow</a> and the ODOO MCP from much ( available at the APP store for free) pointed at the new system, and the setup is simpler than it sounds: the old invoice goes in as context, the tool writes it into the new Odoo, and the agent handles the mapping in between.</p>
<p>What decides whether this works or turns into a mess is not the automation itself. It is the order of operations and the rules you give the agent.</p>
<h2>Create contacts and products first</h2>
<p>Before a single invoice moves, the contacts and the products need to exist in Odoo 20. I did that the classical Export / Import way</p>
<h2>Three rules for every invoice</h2>
<ol>
<li><strong>Invoice date and number must stay identical.</strong> The imported invoice is not a new document. If the number or the date shifts, your books stop lining up with the old system.</li>
<li><strong>Search for the existing contact and map it.</strong> The agent looks the customer up in Odoo 20 and links it to the invoice with the new ID.</li>
<li><strong>Search for the existing product and map it.</strong> The same logic applies to the product lines.</li>
</ol>
<p>And one hard constraint on top: <strong>do not create a contact or a product</strong>. If something is missing, the import should fail loudly instead of quietly filling your database with near-duplicates.</p>
<h2>The prompt that does the work</h2>
<p>The instructions I hand the agent are short and blunt:</p>
<ul>
<li>Import the invoice from Odoo 19 into Odoo 20.</li>
<li>The old invoice is in this prompt, so use the much MCP tool to write it to the new Odoo.</li>
<li>Invoice date and number must be the same.</li>
<li>The contact already exists, so search for it in the new Odoo and map accordingly.</li>
<li>The product already exists, so search for it in the new Odoo and map accordingly.</li>
<li>Do not create a contact or a product.</li>
</ul>
<p>Nothing about formatting, nothing about rewriting descriptions. The fewer decisions the agent has to make, the more predictable the result.</p>
<h2>Check the result, then go again</h2>
<ul>
<li>Does the invoice number match the source exactly?</li>
<li>Is the invoice date unchanged?</li>
<li>Is it attached to the right contact?</li>
<li>Are the product lines pointing at products that already exist in Odoo 20?</li>
</ul>
<p>Run one invoice, verify it, then let the workflow loose on the rest.</p>
<h2>Why this approach holds up</h2>
<p>Moving invoices between two Odoo systems is really a matching problem. The old system holds the documents, the new one holds the records those documents need to point at. n8n provides the workflow, the MCP tool gives the agent hands inside Odoo 20, and the rules stop it from guessing. Contacts and products first, dates and numbers untouched, lookups instead of creations: that combination is what made the migration boring, which is exactly what you want from an accounting import.</p>
<p>If you are planning the same move, start with the contacts and the products in Odoo 20. Get those right, and the rest of the migration is mostly bookkeeping.</p>
</div>
</article>
<div class="post-footer">
<a href="../../index.html#blog">&larr; Back to all posts</a>
</div>
</div>
</main>
<script defer src="/static/agent-odoo4projects/agent.js?v=c3bb0cee2b"></script>
</body>
</html>