Files
ODOO4projects.com/blog/posts/odoo-19-to-20-invoice-migration-with-n8n-and-mcp.html
T
oliver 62d9d76bde Add supplied post images to two posts and to the blog overview cards
- blog/posts/odoo-19-to-20-invoice-migration-with-n8n-and-mcp.html: in-post image
  much.png + og:image
- blog/posts/track-the-oca-modules-you-actually-use.html: in-post image
  oca_module_status.png + og:image
- blog/index.json: optional "image" key on those two entries only (text splice)
- script.js: renderBlog() draws .blog-thumb only when the entry carries an image
- style.css: .blog-thumb rule
- sitemap.xml: lastmod 2026-10-24 for the invoice-migration post

Built by profile webmaster (run 20261024_094206_5c4f28, verified PROBLEMS: 0 /
node --check OK); committed by olchi after that run died on an OpenRouter 402.
2026-10-24 09:46:13 +00:00

149 lines
8.0 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" />
<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>
</head>
<body>
<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 from Odoo 19 to Odoo 20 with an n8n workflow 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>
<script defer src="/static/agent-odoo4projects/agent.js"></script>
</body>
</html>