From ffa8f61b92a37466a4e4018dc50f5bac0a113c6e Mon Sep 17 00:00:00 2001 From: Luca Date: Wed, 14 Jan 2026 16:15:44 +0100 Subject: [PATCH] Add project files. --- OdooTool.sln | 25 +++ OdooTool/OdooTool.csproj | 17 ++ OdooTool/Program.cs | 339 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 381 insertions(+) create mode 100644 OdooTool.sln create mode 100644 OdooTool/OdooTool.csproj create mode 100644 OdooTool/Program.cs 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; } + } +}