diff --git a/OdooTool.sln b/OdooTool.sln
new file mode 100644
index 0000000..29f0e1a
--- /dev/null
+++ b/OdooTool.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.14.36623.8 d17.14
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OdooTool", "OdooTool\OdooTool.csproj", "{2548C905-823E-4986-B8AC-C0D2F42990FB}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2548C905-823E-4986-B8AC-C0D2F42990FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2548C905-823E-4986-B8AC-C0D2F42990FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2548C905-823E-4986-B8AC-C0D2F42990FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2548C905-823E-4986-B8AC-C0D2F42990FB}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {61AD41D9-DB20-4415-8F92-804E7A3A60C4}
+ EndGlobalSection
+EndGlobal
diff --git a/OdooTool/OdooTool.csproj b/OdooTool/OdooTool.csproj
new file mode 100644
index 0000000..cd3b4d2
--- /dev/null
+++ b/OdooTool/OdooTool.csproj
@@ -0,0 +1,17 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
diff --git a/OdooTool/Program.cs b/OdooTool/Program.cs
new file mode 100644
index 0000000..3056af7
--- /dev/null
+++ b/OdooTool/Program.cs
@@ -0,0 +1,339 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+
+class Program
+{
+ static async Task Main()
+ {
+ string url = "https://testsync1.odoo.com/jsonrpc";
+ string db = "testsync1";
+ string username = "*";
+ string password = "*";
+
+ using var http = new HttpClient();
+
+ Console.WriteLine("Verbinden met Odoo...");
+ int uid = await LoginOdoo(http, url, db, username, password);
+ if (uid == -1) return;
+
+ Console.WriteLine("Producten ophalen uit Odoo...");
+ var odooProducts = await GetOdooProducts(http, url, db, uid, password);
+ Console.WriteLine($"{odooProducts.Count} Odoo producten gevonden");
+
+ using var dbContext = new ToolContext();
+ var dbArtikels = dbContext.Artikels.Include(a => a.LevArtikels).ToList();
+ Console.WriteLine($"{dbArtikels.Count} artikelen in DB");
+
+ int matchCount = 0;
+ int supplierCreateCount = 0;
+ int supplierUpdateCount = 0;
+
+ foreach (var odoo in odooProducts)
+ {
+ if (string.IsNullOrWhiteSpace(odoo.Name) ||
+ string.IsNullOrWhiteSpace(odoo.DefaultCode))
+ continue;
+
+ var matches = dbArtikels
+ .Where(a =>
+ a.ArtikelNaam != null &&
+ a.FactoryNR != null &&
+ a.ArtikelNaam.Trim().Equals(odoo.Name.Trim(), StringComparison.OrdinalIgnoreCase) &&
+ a.FactoryNR.Trim().Equals(odoo.DefaultCode.Trim(), StringComparison.OrdinalIgnoreCase) &&
+ (string.IsNullOrEmpty(a.EAN) ||
+ string.IsNullOrEmpty(odoo.Barcode) ||
+ a.EAN == odoo.Barcode)
+ )
+ .ToList();
+
+ foreach (var artikel in matches)
+ {
+ matchCount++;
+
+ if (artikel.InOdoo == 0)
+ artikel.InOdoo = 1;
+
+ foreach (var lev in artikel.LevArtikels)
+ {
+ int? partnerId = await GetOdooPartnerId(http, url, db, uid, password, lev.LevID);
+ if (partnerId == null)
+ {
+ Console.WriteLine($"Leverancier ID {lev.LevID} bestaat niet in Odoo");
+ continue;
+ }
+
+ int? supplierId = await GetOdooSupplierInfoId(
+ http, url, db, uid, password, odoo.Id, partnerId.Value);
+
+ if (supplierId.HasValue)
+ {
+ bool updated = await UpdateOdooSupplierInfo(
+ http, url, db, uid, password,
+ supplierId.Value,
+ lev.Inkoopprijs ?? 0,
+ lev.ArtikelNRLev ?? ""
+ );
+ if (updated) supplierUpdateCount++;
+ }
+ else
+ {
+ bool created = await CreateOdooSupplierInfo(
+ http, url, db, uid, password,
+ odoo.TemplateId, partnerId.Value,
+ lev.ArtikelNRLev ?? "", lev.Inkoopprijs ?? 0);
+ if (created) supplierCreateCount++;
+ }
+ }
+ }
+ }
+
+ dbContext.SaveChanges();
+
+ Console.WriteLine("------------ RESULTAAT ------------");
+ Console.WriteLine($"Matches gevonden: {matchCount}");
+ Console.WriteLine($"SupplierInfo aangemaakt: {supplierCreateCount}");
+ Console.WriteLine($"SupplierInfo geüpdatet: {supplierUpdateCount}");
+ Console.WriteLine("----------------------------------");
+ }
+
+ static async Task LoginOdoo(HttpClient http, string url, string db, string username, string password)
+ {
+ var payload = new
+ {
+ jsonrpc = "2.0",
+ method = "call",
+ @params = new
+ {
+ service = "common",
+ method = "authenticate",
+ args = new object[] { db, username, password, new { } }
+ },
+ id = 1
+ };
+
+ var response = await http.PostAsync(
+ url,
+ new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"));
+
+ var text = await response.Content.ReadAsStringAsync();
+ using var doc = JsonDocument.Parse(text);
+
+ if (doc.RootElement.TryGetProperty("result", out var result) &&
+ result.ValueKind == JsonValueKind.Number)
+ {
+ Console.WriteLine($"Ingelogd (UID {result.GetInt32()})");
+ return result.GetInt32();
+ }
+
+ Console.WriteLine("Login mislukt");
+ return -1;
+ }
+
+ class OdooProduct
+ {
+ public int Id { get; set; }
+ public string? Name { get; set; }
+ public string? DefaultCode { get; set; }
+ public string? Barcode { get; set; }
+ public decimal ListPrice { get; set; }
+ public int TemplateId { get; set; }
+ }
+
+ static async Task> GetOdooProducts(HttpClient http, string url, string db, int uid, string password)
+ {
+ var payload = new
+ {
+ jsonrpc = "2.0",
+ method = "call",
+ @params = new
+ {
+ service = "object",
+ method = "execute_kw",
+ args = new object[]
+ {
+ db, uid, password,
+ "product.product", "search_read",
+ new object[] { },
+ new { fields = new[] { "id", "name", "default_code", "barcode", "list_price", "product_tmpl_id" } }
+ }
+ },
+ id = 2
+ };
+
+ var response = await http.PostAsync(
+ url,
+ new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"));
+
+ var text = await response.Content.ReadAsStringAsync();
+ using var doc = JsonDocument.Parse(text);
+
+ if (!doc.RootElement.TryGetProperty("result", out var result) ||
+ result.ValueKind != JsonValueKind.Array)
+ return new List();
+
+ var list = new List();
+
+ foreach (var item in result.EnumerateArray())
+ {
+ if (!item.TryGetProperty("id", out var idElem) || idElem.ValueKind != JsonValueKind.Number)
+ continue;
+
+ int templateId = 0;
+ if (item.TryGetProperty("product_tmpl_id", out var tmpl))
+ {
+ var maybe = GetIntFromJsonElement(tmpl);
+ if (maybe.HasValue) templateId = maybe.Value;
+ }
+
+ list.Add(new OdooProduct
+ {
+ Id = idElem.GetInt32(),
+ Name = item.TryGetProperty("name", out var n) && n.ValueKind == JsonValueKind.String ? n.GetString() : null,
+ DefaultCode = item.TryGetProperty("default_code", out var d) && d.ValueKind == JsonValueKind.String ? d.GetString() : null,
+ Barcode = item.TryGetProperty("barcode", out var b) && b.ValueKind == JsonValueKind.String ? b.GetString() : null,
+ ListPrice = item.TryGetProperty("list_price", out var p) && p.ValueKind == JsonValueKind.Number ? p.GetDecimal() : 0,
+ TemplateId = templateId
+ });
+ }
+
+ return list;
+ }
+
+ static int? GetIntFromJsonElement(JsonElement e)
+ {
+ if (e.ValueKind == JsonValueKind.Number)
+ return e.GetInt32();
+
+ if (e.ValueKind == JsonValueKind.Array)
+ {
+ var arr = e.EnumerateArray();
+ if (arr.MoveNext() && arr.Current.ValueKind == JsonValueKind.Number)
+ return arr.Current.GetInt32();
+ }
+
+ if (e.ValueKind == JsonValueKind.Object &&
+ e.TryGetProperty("id", out var idProp) &&
+ idProp.ValueKind == JsonValueKind.Number)
+ return idProp.GetInt32();
+
+ return null;
+ }
+
+ static async Task GetOdooPartnerId(HttpClient http, string url, string db, int uid, string password, int levId)
+ {
+ return levId;
+ }
+
+ static async Task GetOdooSupplierInfoId(HttpClient http, string url, string db, int uid, string password, int productId, int partnerId)
+ {
+ return null;
+ }
+
+ static async Task CreateOdooSupplierInfo(HttpClient http, string url, string db, int uid, string password,
+ int templateId, int partnerId, string productCode, decimal price)
+ {
+ var payload = new
+ {
+ jsonrpc = "2.0",
+ method = "call",
+ @params = new
+ {
+ service = "object",
+ method = "execute_kw",
+ args = new object[]
+ {
+ db, uid, password,
+ "product.supplierinfo", "create",
+ new
+ {
+ product_tmpl_id = templateId,
+ name = partnerId,
+ product_code = productCode,
+ price = price,
+ delay = 0
+ }
+ }
+ },
+ id = 3
+ };
+
+ var response = await http.PostAsync(
+ url,
+ new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"));
+
+ return response.IsSuccessStatusCode;
+ }
+
+ static async Task UpdateOdooSupplierInfo(HttpClient http, string url, string db, int uid, string password,
+ int supplierId, decimal price, string productCode)
+ {
+ var payload = new
+ {
+ jsonrpc = "2.0",
+ method = "call",
+ @params = new
+ {
+ service = "object",
+ method = "execute_kw",
+ args = new object[]
+ {
+ db, uid, password,
+ "product.supplierinfo", "write",
+ new object[] { new int[] { supplierId }, new { price = price, product_code = productCode } }
+ }
+ },
+ id = 4
+ };
+
+ var response = await http.PostAsync(
+ url,
+ new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json"));
+
+ return response.IsSuccessStatusCode;
+ }
+
+ public class ToolContext : DbContext
+ {
+ public DbSet Artikels { get; set; } = null!;
+ public DbSet LevArtikels { get; set; } = null!;
+ public DbSet Leveranciers { get; set; } = null!;
+
+ protected override void OnConfiguring(DbContextOptionsBuilder options)
+ => options.UseSqlite("Data Source=Tool.db");
+ }
+
+ public class Artikel
+ {
+ public int ID { get; set; }
+ public string? FactoryNR { get; set; }
+ public string? ArtikelNaam { get; set; }
+ public string? EAN { get; set; }
+ public int InOdoo { get; set; }
+ public List LevArtikels { get; set; } = new();
+ }
+
+ public class LevArtikel
+ {
+ public int ID { get; set; }
+ public int ArtikelID { get; set; }
+ public int LevID { get; set; }
+ public string? ArtikelNRLev { get; set; }
+ public decimal? Inkoopprijs { get; set; }
+ public decimal? Korting { get; set; }
+ public string? LeverTijd { get; set; }
+ public string? Omschrijving { get; set; }
+ public bool Leverbaar { get; set; }
+ }
+
+ public class Leverancier
+ {
+ public int ID { get; set; }
+ public string? Naam { get; set; }
+ }
+}