Add project files.
This commit is contained in:
parent
7fd695f5c4
commit
de36f710df
25
OdooAPI.sln
Normal file
25
OdooAPI.sln
Normal file
@ -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}") = "OdooAPI", "OdooAPI\OdooAPI.csproj", "{AC50ACEB-8209-4330-B8BA-DB84922F8226}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{AC50ACEB-8209-4330-B8BA-DB84922F8226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AC50ACEB-8209-4330-B8BA-DB84922F8226}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AC50ACEB-8209-4330-B8BA-DB84922F8226}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{AC50ACEB-8209-4330-B8BA-DB84922F8226}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B1E1DA3F-BBFA-44B2-9C16-F05361CE4C27}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
7
OdooAPI/DataAccess/Odoo/Dtos/OdooProductDto.cs
Normal file
7
OdooAPI/DataAccess/Odoo/Dtos/OdooProductDto.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace OdooAPI.DataAccess.Odoo.Dtos;
|
||||
|
||||
public class OdooProductDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string DefaultCode { get; set; } = "";
|
||||
}
|
||||
10
OdooAPI/DataAccess/Odoo/Dtos/OdooSupplierDto.cs
Normal file
10
OdooAPI/DataAccess/Odoo/Dtos/OdooSupplierDto.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace OdooAPI.DataAccess.Odoo.Dtos;
|
||||
|
||||
public class OdooSupplierDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int PartnerId { get; set; }
|
||||
|
||||
public string Name { get; set; } = "";
|
||||
}
|
||||
63
OdooAPI/DataAccess/Odoo/OdooLogin.cs
Normal file
63
OdooAPI/DataAccess/Odoo/OdooLogin.cs
Normal file
@ -0,0 +1,63 @@
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace OdooAPI.DataAccess.Odoo;
|
||||
|
||||
public class OdooLogin
|
||||
{
|
||||
private const string Url = "https://odooapi1.odoo.com/jsonrpc";
|
||||
|
||||
private const string Database = "odooapi1";
|
||||
private const string Username = "luca@corsie.nl";
|
||||
private const string Password = "XS82ns23!";
|
||||
|
||||
private readonly HttpClient _http;
|
||||
|
||||
public OdooLogin(HttpClient http)
|
||||
{
|
||||
_http = http;
|
||||
}
|
||||
|
||||
public async Task<int?> LoginAsync()
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
jsonrpc = "2.0",
|
||||
method = "call",
|
||||
id = 1,
|
||||
@params = new
|
||||
{
|
||||
service = "common",
|
||||
method = "login",
|
||||
args = new object[]
|
||||
{
|
||||
Database,
|
||||
Username,
|
||||
Password
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var content = new StringContent(
|
||||
JsonSerializer.Serialize(payload),
|
||||
Encoding.UTF8,
|
||||
"application/json");
|
||||
|
||||
var response = await _http.PostAsync(Url, content);
|
||||
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Console.WriteLine("LOGIN RESPONSE:");
|
||||
Console.WriteLine(json);
|
||||
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
|
||||
if (!doc.RootElement.TryGetProperty("result", out var result))
|
||||
return null;
|
||||
|
||||
return result.ValueKind == JsonValueKind.Number
|
||||
? result.GetInt32()
|
||||
: null;
|
||||
}
|
||||
}
|
||||
249
OdooAPI/DataAccess/Odoo/Repositories/OdooProductRepository.cs
Normal file
249
OdooAPI/DataAccess/Odoo/Repositories/OdooProductRepository.cs
Normal file
@ -0,0 +1,249 @@
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace OdooAPI.DataAccess.Odoo.Repositories;
|
||||
|
||||
public class OdooProductRepository
|
||||
{
|
||||
private readonly HttpClient _http;
|
||||
|
||||
private const string Url = "https://odooapi1.odoo.com/jsonrpc";
|
||||
private const string Database = "odooapi1";
|
||||
private const string Username = "luca@corsie.nl";
|
||||
private const string Password = "XS82ns23!";
|
||||
|
||||
private int _uid;
|
||||
|
||||
// ================================
|
||||
// PROPERTY (fix voor jouw error)
|
||||
// ================================
|
||||
public int Uid => _uid;
|
||||
|
||||
public OdooProductRepository(HttpClient http)
|
||||
{
|
||||
_http = http;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// LOGIN
|
||||
// =========================================
|
||||
public async Task LoginAsync()
|
||||
{
|
||||
var result = await CallAsync(
|
||||
"common",
|
||||
"login",
|
||||
new object[] { Database, Username, Password });
|
||||
|
||||
_uid = result.GetInt32();
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// PRODUCTEN OPHALEN (fix voor jouw error)
|
||||
// =========================================
|
||||
public async Task<List<string>> GetAllProductsAsync()
|
||||
{
|
||||
var result = await CallAsync(
|
||||
"object",
|
||||
"execute_kw",
|
||||
new object[]
|
||||
{
|
||||
Database, _uid, Password,
|
||||
"product.product",
|
||||
"search_read",
|
||||
new object[] { new object[] { } }
|
||||
},
|
||||
new { fields = new[] { "default_code" } }
|
||||
);
|
||||
|
||||
var list = new List<string>();
|
||||
|
||||
foreach (var p in result.EnumerateArray())
|
||||
{
|
||||
if (p.TryGetProperty("default_code", out var code) &&
|
||||
code.ValueKind == JsonValueKind.String)
|
||||
{
|
||||
list.Add(code.GetString()!);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// UPSERT SUPPLIER (CREATE of UPDATE)
|
||||
// =========================================
|
||||
public async Task UpsertSupplierAsync(
|
||||
string defaultCode,
|
||||
string supplierName,
|
||||
string leverancierProductCode,
|
||||
decimal price,
|
||||
int delay)
|
||||
{
|
||||
var partnerId = await GetPartnerIdAsync(supplierName);
|
||||
if (partnerId == null)
|
||||
throw new Exception($"Supplier '{supplierName}' niet gevonden.");
|
||||
|
||||
// product template ophalen (VERPLICHT in Odoo)
|
||||
var tmplId = await GetProductTemplateIdAsync(defaultCode);
|
||||
if (tmplId == null)
|
||||
throw new Exception($"Product template niet gevonden voor {defaultCode}");
|
||||
|
||||
var existingId =
|
||||
await GetExistingSupplierInfoId(partnerId.Value, leverancierProductCode);
|
||||
|
||||
var values = new
|
||||
{
|
||||
partner_id = partnerId,
|
||||
product_tmpl_id = tmplId, // ⭐ BELANGRIJKSTE FIX
|
||||
product_code = leverancierProductCode,
|
||||
price = price,
|
||||
delay = delay,
|
||||
product_uom_id = 1
|
||||
};
|
||||
|
||||
if (existingId != null)
|
||||
{
|
||||
Console.WriteLine(" ␦ UPDATE in Odoo");
|
||||
|
||||
await CallAsync(
|
||||
"object",
|
||||
"execute_kw",
|
||||
new object[]
|
||||
{
|
||||
Database,_uid,Password,
|
||||
"product.supplierinfo",
|
||||
"write",
|
||||
new object[] { new [] { existingId }, values }
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(" ␦ CREATE in Odoo");
|
||||
|
||||
await CallAsync(
|
||||
"object",
|
||||
"execute_kw",
|
||||
new object[]
|
||||
{
|
||||
Database,_uid,Password,
|
||||
"product.supplierinfo",
|
||||
"create",
|
||||
new object[] { values }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// TEMPLATE ID (nieuw – verplicht)
|
||||
// =========================================
|
||||
private async Task<int?> GetProductTemplateIdAsync(string defaultCode)
|
||||
{
|
||||
var result = await CallAsync(
|
||||
"object",
|
||||
"execute_kw",
|
||||
new object[]
|
||||
{
|
||||
Database,_uid,Password,
|
||||
"product.product",
|
||||
"search_read",
|
||||
new object[]
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
new object[] { "default_code", "=", defaultCode }
|
||||
}
|
||||
}
|
||||
},
|
||||
new { fields = new[] { "product_tmpl_id" }, limit = 1 }
|
||||
);
|
||||
|
||||
foreach (var r in result.EnumerateArray())
|
||||
{
|
||||
return r.GetProperty("product_tmpl_id")[0].GetInt32();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async Task<int?> GetExistingSupplierInfoId(int partnerId, string code)
|
||||
{
|
||||
var result = await CallAsync(
|
||||
"object",
|
||||
"execute_kw",
|
||||
new object[]
|
||||
{
|
||||
Database,_uid,Password,
|
||||
"product.supplierinfo",
|
||||
"search",
|
||||
new object[]
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
new object[] { "partner_id", "=", partnerId },
|
||||
new object[] { "product_code", "=", code }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var ids = result.EnumerateArray().ToList();
|
||||
|
||||
return ids.Count == 0 ? null : ids[0].GetInt32();
|
||||
}
|
||||
|
||||
private async Task<int?> GetPartnerIdAsync(string name)
|
||||
{
|
||||
var result = await CallAsync(
|
||||
"object",
|
||||
"execute_kw",
|
||||
new object[]
|
||||
{
|
||||
Database,_uid,Password,
|
||||
"res.partner",
|
||||
"search",
|
||||
new object[]
|
||||
{
|
||||
new object[]
|
||||
{
|
||||
new object[] { "name", "=", name }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var ids = result.EnumerateArray().ToList();
|
||||
|
||||
return ids.Count == 0 ? null : ids[0].GetInt32();
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// CORE JSON RPC
|
||||
// =========================================
|
||||
private async Task<JsonElement> CallAsync(
|
||||
string service,
|
||||
string method,
|
||||
object[] args,
|
||||
object? kwargs = null)
|
||||
{
|
||||
var payload = new
|
||||
{
|
||||
jsonrpc = "2.0",
|
||||
method = "call",
|
||||
id = 1,
|
||||
@params = new { service, method, args, kwargs }
|
||||
};
|
||||
|
||||
var json = JsonSerializer.Serialize(payload);
|
||||
|
||||
var response = await _http.PostAsync(
|
||||
Url,
|
||||
new StringContent(json, Encoding.UTF8, "application/json"));
|
||||
|
||||
var content = await response.Content.ReadAsStringAsync();
|
||||
|
||||
using var doc = JsonDocument.Parse(content);
|
||||
|
||||
if (doc.RootElement.TryGetProperty("error", out var error))
|
||||
throw new Exception(error.ToString());
|
||||
|
||||
return doc.RootElement.GetProperty("result").Clone();
|
||||
}
|
||||
}
|
||||
15
OdooAPI/DataAccess/Repositories/ArtikelRepository.cs
Normal file
15
OdooAPI/DataAccess/Repositories/ArtikelRepository.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OdooAPI.Database;
|
||||
|
||||
public class ArtikelRepository
|
||||
{
|
||||
public async Task<List<string>> GetSupplierNamesAsync(int artikelId)
|
||||
{
|
||||
using var db = new AppDbContext();
|
||||
|
||||
return await db.LevArtikelen
|
||||
.Where(x => x.ArtikelId == artikelId)
|
||||
.Select(x => x.Leverancier.Naam)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
32
OdooAPI/DataAccess/Repositories/LevArtikelRepository.cs
Normal file
32
OdooAPI/DataAccess/Repositories/LevArtikelRepository.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OdooAPI.Database;
|
||||
using OdooAPI.Database.Tabellen;
|
||||
|
||||
namespace OdooAPI.DataAccess.Repositories;
|
||||
|
||||
public class LevArtikelRepository
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
|
||||
public LevArtikelRepository(AppDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public Dictionary<(int, int), LevArtikel> GetAllLookup()
|
||||
{
|
||||
return _db.LevArtikelen
|
||||
.AsNoTracking()
|
||||
.ToDictionary(x => (x.LeverancierId, x.ArtikelId), x => x);
|
||||
}
|
||||
|
||||
public void AddRange(List<LevArtikel> items)
|
||||
{
|
||||
_db.LevArtikelen.AddRange(items);
|
||||
}
|
||||
|
||||
public void UpdateRange(List<LevArtikel> items)
|
||||
{
|
||||
_db.LevArtikelen.UpdateRange(items);
|
||||
}
|
||||
}
|
||||
27
OdooAPI/DataAccess/Repositories/LeverancierRepository.cs
Normal file
27
OdooAPI/DataAccess/Repositories/LeverancierRepository.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OdooAPI.Database;
|
||||
using OdooAPI.Database.Tabellen;
|
||||
|
||||
namespace OdooAPI.DataAccess.Repositories;
|
||||
|
||||
public class LeverancierRepository
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
|
||||
public LeverancierRepository(AppDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public Leverancier? GetByNaam(string naam)
|
||||
{
|
||||
return _db.Leveranciers.FirstOrDefault(x => x.Naam == naam);
|
||||
}
|
||||
|
||||
public Leverancier Add(string naam)
|
||||
{
|
||||
var lev = new Leverancier { Naam = naam };
|
||||
_db.Leveranciers.Add(lev);
|
||||
return lev;
|
||||
}
|
||||
}
|
||||
10
OdooAPI/DataAccess/Xml/XmlItemDto.cs
Normal file
10
OdooAPI/DataAccess/Xml/XmlItemDto.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace OdooAPI.DataAccess.Xml;
|
||||
|
||||
public class XmlItemDto
|
||||
{
|
||||
public string VendorId { get; set; } = "";
|
||||
public string Naam { get; set; } = "";
|
||||
public decimal Prijs { get; set; }
|
||||
public int Stock { get; set; }
|
||||
public string? Ean { get; set; }
|
||||
}
|
||||
32
OdooAPI/DataAccess/Xml/XmlItemMapper.cs
Normal file
32
OdooAPI/DataAccess/Xml/XmlItemMapper.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using OdooAPI.Database.Tabellen;
|
||||
|
||||
namespace OdooAPI.DataAccess.Xml;
|
||||
|
||||
public class XmlItemMapper
|
||||
{
|
||||
public Artikel MapToArtikel(XmlItemDto dto)
|
||||
{
|
||||
return new Artikel
|
||||
{
|
||||
ArtikelNummer = dto.VendorId,
|
||||
Naam = dto.Naam,
|
||||
Ean = dto.Ean,
|
||||
InOdoo = false
|
||||
};
|
||||
}
|
||||
|
||||
public LevArtikel MapToLevArtikel(XmlItemDto dto, int leverancierId, int artikelId)
|
||||
{
|
||||
return new LevArtikel
|
||||
{
|
||||
LeverancierId = leverancierId,
|
||||
ArtikelId = artikelId,
|
||||
ArtikelNummerLev = dto.VendorId,
|
||||
Inkoopprijs = dto.Prijs,
|
||||
Levertijd = 1,
|
||||
Omschrijving = "",
|
||||
Leverbaar = dto.Stock > 0,
|
||||
Korting = null
|
||||
};
|
||||
}
|
||||
}
|
||||
38
OdooAPI/DataAccess/Xml/XmlSupplierReader.cs
Normal file
38
OdooAPI/DataAccess/Xml/XmlSupplierReader.cs
Normal file
@ -0,0 +1,38 @@
|
||||
using System.Xml.Linq;
|
||||
using OdooAPI.Services.Helpers;
|
||||
|
||||
namespace OdooAPI.DataAccess.Xml;
|
||||
|
||||
public class XmlSupplierReader
|
||||
{
|
||||
public List<XmlItemDto> ReadFolder(string supplierFolder)
|
||||
{
|
||||
var result = new List<XmlItemDto>();
|
||||
|
||||
var files = Directory.GetFiles(supplierFolder, "*.xml");
|
||||
|
||||
foreach (var file in files)
|
||||
{
|
||||
result.AddRange(ReadFile(file));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private IEnumerable<XmlItemDto> ReadFile(string filePath)
|
||||
{
|
||||
var doc = XDocument.Load(filePath);
|
||||
|
||||
foreach (var item in doc.Descendants("item"))
|
||||
{
|
||||
yield return new XmlItemDto
|
||||
{
|
||||
VendorId = item.Element("vendor_id")?.Value ?? "",
|
||||
Naam = item.Element("long_desc")?.Value ?? "",
|
||||
Prijs = DecimalHelper.Parse(item.Element("price")?.Value),
|
||||
Stock = int.TryParse(item.Element("stock")?.Value, out var s) ? s : 0,
|
||||
Ean = item.Element("EAN_code")?.Value
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
41
OdooAPI/Database/AppDbContext.cs
Normal file
41
OdooAPI/Database/AppDbContext.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OdooAPI.Database.Tabellen;
|
||||
|
||||
namespace OdooAPI.Database;
|
||||
|
||||
public class AppDbContext : DbContext
|
||||
{
|
||||
public DbSet<Artikel> Artikelen => Set<Artikel>();
|
||||
public DbSet<Leverancier> Leveranciers => Set<Leverancier>();
|
||||
public DbSet<LevArtikel> LevArtikelen => Set<LevArtikel>();
|
||||
public DbSet<Marge> Marges => Set<Marge>();
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
{
|
||||
var path = Path.Combine(AppContext.BaseDirectory, "database.db");
|
||||
options.UseSqlite($"Data Source={path}");
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Artikel>()
|
||||
.HasIndex(x => x.ArtikelNummer)
|
||||
.IsUnique();
|
||||
|
||||
modelBuilder.Entity<LevArtikel>()
|
||||
.HasIndex(x => new { x.LeverancierId, x.ArtikelId })
|
||||
.IsUnique();
|
||||
|
||||
modelBuilder.Entity<LevArtikel>()
|
||||
.HasOne(x => x.Artikel)
|
||||
.WithMany(x => x.LevArtikelen)
|
||||
.HasForeignKey(x => x.ArtikelId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
|
||||
modelBuilder.Entity<LevArtikel>()
|
||||
.HasOne(x => x.Leverancier)
|
||||
.WithMany(x => x.LevArtikelen)
|
||||
.HasForeignKey(x => x.LeverancierId)
|
||||
.OnDelete(DeleteBehavior.Restrict);
|
||||
}
|
||||
}
|
||||
21
OdooAPI/Database/ArtikelRepository.cs
Normal file
21
OdooAPI/Database/ArtikelRepository.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace OdooAPI.Database;
|
||||
|
||||
public class ArtikelRepository
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
|
||||
public ArtikelRepository(AppDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetAllArtikelNummersAsync()
|
||||
{
|
||||
return await _db.Artikelen
|
||||
.Where(a => a.ArtikelNummer != null)
|
||||
.Select(a => a.ArtikelNummer!)
|
||||
.ToListAsync();
|
||||
}
|
||||
}
|
||||
10
OdooAPI/Database/DbInitializer.cs
Normal file
10
OdooAPI/Database/DbInitializer.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace OdooAPI.Database;
|
||||
|
||||
public static class DbInitializer
|
||||
{
|
||||
public static void Initialize()
|
||||
{
|
||||
using var db = new AppDbContext();
|
||||
db.Database.EnsureCreated();
|
||||
}
|
||||
}
|
||||
16
OdooAPI/Database/Tabellen/Artkel.cs
Normal file
16
OdooAPI/Database/Tabellen/Artkel.cs
Normal file
@ -0,0 +1,16 @@
|
||||
namespace OdooAPI.Database.Tabellen;
|
||||
|
||||
public class Artikel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Naam { get; set; } = "";
|
||||
|
||||
public string ArtikelNummer { get; set; } = "";
|
||||
|
||||
public string? Ean { get; set; }
|
||||
|
||||
public bool InOdoo { get; set; }
|
||||
|
||||
public List<LevArtikel> LevArtikelen { get; set; } = new();
|
||||
}
|
||||
24
OdooAPI/Database/Tabellen/LevArtikel.cs
Normal file
24
OdooAPI/Database/Tabellen/LevArtikel.cs
Normal file
@ -0,0 +1,24 @@
|
||||
namespace OdooAPI.Database.Tabellen;
|
||||
|
||||
public class LevArtikel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int LeverancierId { get; set; }
|
||||
public Leverancier Leverancier { get; set; } = null!;
|
||||
|
||||
public int ArtikelId { get; set; }
|
||||
public Artikel Artikel { get; set; } = null!;
|
||||
|
||||
public string ArtikelNummerLev { get; set; } = "";
|
||||
|
||||
public decimal Inkoopprijs { get; set; }
|
||||
|
||||
public int Levertijd { get; set; } = 1;
|
||||
|
||||
public string Omschrijving { get; set; } = "";
|
||||
|
||||
public bool Leverbaar { get; set; }
|
||||
|
||||
public decimal? Korting { get; set; }
|
||||
}
|
||||
10
OdooAPI/Database/Tabellen/Leverancier.cs
Normal file
10
OdooAPI/Database/Tabellen/Leverancier.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace OdooAPI.Database.Tabellen;
|
||||
|
||||
public class Leverancier
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public string Naam { get; set; } = "";
|
||||
|
||||
public List<LevArtikel> LevArtikelen { get; set; } = new();
|
||||
}
|
||||
10
OdooAPI/Database/Tabellen/Marge.cs
Normal file
10
OdooAPI/Database/Tabellen/Marge.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace OdooAPI.Database.Tabellen;
|
||||
|
||||
public class Marge
|
||||
{
|
||||
public int Id { get; set; }
|
||||
|
||||
public int ArtikelId { get; set; }
|
||||
|
||||
public decimal Percentage { get; set; }
|
||||
}
|
||||
12
OdooAPI/Import/OdooSycnProcessor.cs
Normal file
12
OdooAPI/Import/OdooSycnProcessor.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OdooAPI.Import
|
||||
{
|
||||
internal class OdooSycnProcessor
|
||||
{
|
||||
}
|
||||
}
|
||||
119
OdooAPI/Import/SupplierImportProcessor.cs
Normal file
119
OdooAPI/Import/SupplierImportProcessor.cs
Normal file
@ -0,0 +1,119 @@
|
||||
using OdooAPI.Database;
|
||||
using OdooAPI.Database.Tabellen;
|
||||
using OdooAPI.DataAccess.Xml;
|
||||
|
||||
namespace OdooAPI.Import;
|
||||
|
||||
public class SupplierImportProcessor
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
|
||||
public SupplierImportProcessor(AppDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public void Process(string supplierFolder)
|
||||
{
|
||||
var supplierName = Path.GetFileName(supplierFolder);
|
||||
|
||||
Console.WriteLine($"Start import: {supplierName}");
|
||||
|
||||
var leverancier = _db.Leveranciers
|
||||
.FirstOrDefault(x => x.Naam == supplierName);
|
||||
|
||||
if (leverancier == null)
|
||||
{
|
||||
leverancier = new Leverancier { Naam = supplierName };
|
||||
_db.Leveranciers.Add(leverancier);
|
||||
_db.SaveChanges();
|
||||
Console.WriteLine("Nieuwe leverancier aangemaakt");
|
||||
}
|
||||
|
||||
var reader = new XmlSupplierReader();
|
||||
var items = reader.ReadFolder(supplierFolder);
|
||||
|
||||
Console.WriteLine($"XML items gevonden: {items.Count}");
|
||||
|
||||
var bestaandeArtikelen = _db.Artikelen
|
||||
.ToDictionary(x => x.ArtikelNummer);
|
||||
|
||||
int nieuweArtikelen = 0;
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (!bestaandeArtikelen.ContainsKey(item.VendorId))
|
||||
{
|
||||
var artikel = new Artikel
|
||||
{
|
||||
Naam = item.Naam,
|
||||
ArtikelNummer = item.VendorId,
|
||||
Ean = item.Ean,
|
||||
InOdoo = false
|
||||
};
|
||||
|
||||
_db.Artikelen.Add(artikel);
|
||||
bestaandeArtikelen[item.VendorId] = artikel;
|
||||
|
||||
nieuweArtikelen++;
|
||||
}
|
||||
}
|
||||
|
||||
_db.SaveChanges();
|
||||
|
||||
Console.WriteLine($"Nieuwe artikelen: {nieuweArtikelen}");
|
||||
|
||||
var bestaandeLevArtikelen = _db.LevArtikelen
|
||||
.Where(x => x.LeverancierId == leverancier.Id)
|
||||
.ToDictionary(x => x.ArtikelId);
|
||||
|
||||
var gezienArtikelIds = new HashSet<int>();
|
||||
|
||||
int nieuweLev = 0;
|
||||
int updates = 0;
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
var artikel = bestaandeArtikelen[item.VendorId];
|
||||
|
||||
if (!bestaandeLevArtikelen.TryGetValue(artikel.Id, out var levArtikel))
|
||||
{
|
||||
levArtikel = new LevArtikel
|
||||
{
|
||||
LeverancierId = leverancier.Id,
|
||||
ArtikelId = artikel.Id,
|
||||
ArtikelNummerLev = item.VendorId,
|
||||
Inkoopprijs = item.Prijs,
|
||||
Levertijd = 1,
|
||||
Leverbaar = true,
|
||||
Omschrijving = "",
|
||||
Korting = null
|
||||
};
|
||||
|
||||
_db.LevArtikelen.Add(levArtikel);
|
||||
nieuweLev++;
|
||||
}
|
||||
else
|
||||
{
|
||||
levArtikel.Inkoopprijs = item.Prijs;
|
||||
levArtikel.Leverbaar = true;
|
||||
updates++;
|
||||
}
|
||||
|
||||
gezienArtikelIds.Add(artikel.Id);
|
||||
}
|
||||
|
||||
foreach (var lev in bestaandeLevArtikelen.Values)
|
||||
{
|
||||
if (!gezienArtikelIds.Contains(lev.ArtikelId))
|
||||
lev.Leverbaar = false;
|
||||
}
|
||||
|
||||
_db.SaveChanges();
|
||||
|
||||
Console.WriteLine($"Nieuwe leverancier-artikelen: {nieuweLev}");
|
||||
Console.WriteLine($"Updates: {updates}");
|
||||
Console.WriteLine($"Klaar met: {supplierName}");
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
19
OdooAPI/Import/XMLImportRunner.cs
Normal file
19
OdooAPI/Import/XMLImportRunner.cs
Normal file
@ -0,0 +1,19 @@
|
||||
using OdooAPI.Database;
|
||||
|
||||
namespace OdooAPI.Import;
|
||||
|
||||
public class XmlImportRunner
|
||||
{
|
||||
public void Run()
|
||||
{
|
||||
using var db = new AppDbContext();
|
||||
|
||||
|
||||
db.Database.EnsureCreated();
|
||||
|
||||
|
||||
|
||||
var importer = new XmlImporter(db);
|
||||
importer.Run();
|
||||
}
|
||||
}
|
||||
41
OdooAPI/Import/XmlImporter.cs
Normal file
41
OdooAPI/Import/XmlImporter.cs
Normal file
@ -0,0 +1,41 @@
|
||||
using OdooAPI.Database;
|
||||
using OdooAPI.Services;
|
||||
using System.IO;
|
||||
|
||||
namespace OdooAPI.Import;
|
||||
|
||||
public class XmlImporter
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
|
||||
public XmlImporter(AppDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
var baseDir = Path.Combine(AppContext.BaseDirectory, "Leveranciers");
|
||||
|
||||
Console.WriteLine($"Zoeken naar leveranciers in: {baseDir}");
|
||||
|
||||
if (!Directory.Exists(baseDir))
|
||||
{
|
||||
Console.WriteLine("Leveranciers map niet gevonden");
|
||||
return;
|
||||
}
|
||||
|
||||
var scanner = new LeverancierScanner();
|
||||
var supplierFolders = scanner.GetSupplierFolders(baseDir);
|
||||
|
||||
foreach (var folder in supplierFolders)
|
||||
{
|
||||
var name = Path.GetFileName(folder);
|
||||
|
||||
Console.WriteLine($"Leverancier gevonden: {name}");
|
||||
|
||||
var processor = new SupplierImportProcessor(_db);
|
||||
processor.Process(folder);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
OdooAPI/OdooAPI.csproj
Normal file
20
OdooAPI/OdooAPI.csproj
Normal file
@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.23" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.23" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.23">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
44
OdooAPI/Program.cs
Normal file
44
OdooAPI/Program.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using OdooAPI.Import;
|
||||
using OdooAPI.Services;
|
||||
|
||||
internal class Program
|
||||
{
|
||||
static async Task Main(string[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
// =====================================
|
||||
// 1️⃣ XML IMPORT
|
||||
// =====================================
|
||||
Console.WriteLine("=================================");
|
||||
Console.WriteLine("XML IMPORT START");
|
||||
Console.WriteLine("=================================");
|
||||
|
||||
var xml = new XmlImportRunner();
|
||||
xml.Run();
|
||||
|
||||
Console.WriteLine("XML IMPORT KLAAR\n");
|
||||
|
||||
|
||||
// =====================================
|
||||
// 2️⃣ ODOO SYNC
|
||||
// =====================================
|
||||
Console.WriteLine("=================================");
|
||||
Console.WriteLine("ODOO SYNC START");
|
||||
Console.WriteLine("=================================");
|
||||
|
||||
var app = new OdooSyncApp();
|
||||
await app.RunAsync();
|
||||
|
||||
Console.WriteLine("\nKlaar.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("\nFOUT:");
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
|
||||
Console.WriteLine("\nDruk op een toets om te sluiten...");
|
||||
Console.ReadKey();
|
||||
}
|
||||
}
|
||||
13
OdooAPI/Services/ArtikelMatcher.cs
Normal file
13
OdooAPI/Services/ArtikelMatcher.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace OdooAPI.Services;
|
||||
|
||||
public class ArtikelMatcher
|
||||
{
|
||||
public bool IsMatch(string artikelNummerA, string artikelNummerB)
|
||||
{
|
||||
return string.Equals(
|
||||
artikelNummerA?.Trim(),
|
||||
artikelNummerB?.Trim(),
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
);
|
||||
}
|
||||
}
|
||||
9
OdooAPI/Services/AvailabilityRules.cs
Normal file
9
OdooAPI/Services/AvailabilityRules.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace OdooAPI.Services;
|
||||
|
||||
public static class AvailabilityRules
|
||||
{
|
||||
public static bool IsAvailable(int stock)
|
||||
{
|
||||
return stock > 0;
|
||||
}
|
||||
}
|
||||
12
OdooAPI/Services/EanValidator.cs
Normal file
12
OdooAPI/Services/EanValidator.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace OdooAPI.Services;
|
||||
|
||||
public static class EanValidator
|
||||
{
|
||||
public static bool IsValid(string? ean)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ean))
|
||||
return false;
|
||||
|
||||
return ean.All(char.IsDigit);
|
||||
}
|
||||
}
|
||||
18
OdooAPI/Services/Helpers/DecimalHelper.cs
Normal file
18
OdooAPI/Services/Helpers/DecimalHelper.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace OdooAPI.Services.Helpers;
|
||||
|
||||
public static class DecimalHelper
|
||||
{
|
||||
public static decimal Parse(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return 0m;
|
||||
|
||||
value = value.Replace(",", ".");
|
||||
|
||||
decimal.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
9
OdooAPI/Services/Helpers/Keybuilder.cs
Normal file
9
OdooAPI/Services/Helpers/Keybuilder.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace OdooAPI.Services.Helpers;
|
||||
|
||||
public static class Keybuilder
|
||||
{
|
||||
public static string Build(string leverancier, string artikelNummer)
|
||||
{
|
||||
return $"{leverancier}_{artikelNummer}".ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
12
OdooAPI/Services/Helpers/Normalizer.cs
Normal file
12
OdooAPI/Services/Helpers/Normalizer.cs
Normal file
@ -0,0 +1,12 @@
|
||||
namespace OdooAPI.Services.Helpers;
|
||||
|
||||
public static class Normalizer
|
||||
{
|
||||
public static string Clean(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
return string.Empty;
|
||||
|
||||
return value.Trim();
|
||||
}
|
||||
}
|
||||
14
OdooAPI/Services/LeverancierScanner.cs
Normal file
14
OdooAPI/Services/LeverancierScanner.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System.IO;
|
||||
|
||||
namespace OdooAPI.Services;
|
||||
|
||||
public class LeverancierScanner
|
||||
{
|
||||
public IEnumerable<string> GetSupplierFolders(string baseDir)
|
||||
{
|
||||
if (!Directory.Exists(baseDir))
|
||||
return Enumerable.Empty<string>();
|
||||
|
||||
return Directory.GetDirectories(baseDir);
|
||||
}
|
||||
}
|
||||
12
OdooAPI/Services/MatchResult.cs
Normal file
12
OdooAPI/Services/MatchResult.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace OdooAPI.Services
|
||||
{
|
||||
internal class MatchResult
|
||||
{
|
||||
}
|
||||
}
|
||||
57
OdooAPI/Services/OdooSyncApp.cs
Normal file
57
OdooAPI/Services/OdooSyncApp.cs
Normal file
@ -0,0 +1,57 @@
|
||||
using OdooAPI.DataAccess.Odoo.Repositories;
|
||||
using OdooAPI.Database;
|
||||
|
||||
namespace OdooAPI.Services;
|
||||
|
||||
public class OdooSyncApp
|
||||
{
|
||||
public async Task RunAsync()
|
||||
{
|
||||
Console.WriteLine("Inloggen in Odoo...");
|
||||
|
||||
var http = new HttpClient();
|
||||
var repo = new OdooProductRepository(http);
|
||||
|
||||
await repo.LoginAsync();
|
||||
|
||||
Console.WriteLine($"Ingelogd bij Odoo (UID = {repo.Uid})");
|
||||
|
||||
// =========================================
|
||||
// PRODUCTEN OPHALEN
|
||||
// =========================================
|
||||
|
||||
Console.WriteLine("\nProducten ophalen...");
|
||||
|
||||
var odooProducts = await repo.GetAllProductsAsync(); // List<string>
|
||||
|
||||
Console.WriteLine($"Odoo producten: {odooProducts.Count}");
|
||||
|
||||
var db = new AppDbContext();
|
||||
|
||||
var localCodes = db.LevArtikelen
|
||||
.Select(x => x.ArtikelNummerLev)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
Console.WriteLine($"Database producten: {localCodes.Count}");
|
||||
|
||||
// ✅ BELANGRIJK: strings vergelijken (GEEN DefaultCode meer!)
|
||||
var matches = odooProducts
|
||||
.Where(code => localCodes.Contains(code))
|
||||
.ToList();
|
||||
|
||||
Console.WriteLine($"Matches: {matches.Count}");
|
||||
|
||||
// =========================================
|
||||
// SUPPLIER MATCHING
|
||||
// =========================================
|
||||
|
||||
Console.WriteLine("\nSUPPLIER MATCHING");
|
||||
|
||||
var supplierMatch = new SupplierMatchService(db, repo);
|
||||
|
||||
await supplierMatch.RunAsync(matches);
|
||||
|
||||
Console.WriteLine("Klaar.");
|
||||
}
|
||||
}
|
||||
27
OdooAPI/Services/OdooXmlMatchService.cs
Normal file
27
OdooAPI/Services/OdooXmlMatchService.cs
Normal file
@ -0,0 +1,27 @@
|
||||
using OdooAPI.DataAccess.Odoo.Dtos;
|
||||
|
||||
namespace OdooAPI.Services;
|
||||
|
||||
public class OdooXmlMatchService
|
||||
{
|
||||
public List<string> GetMatchedCodes(
|
||||
List<OdooProductDto> odooProducts,
|
||||
List<string> localCodes)
|
||||
{
|
||||
var localSet = new HashSet<string>(localCodes);
|
||||
|
||||
var matches = odooProducts
|
||||
.Where(p =>
|
||||
!string.IsNullOrEmpty(p.DefaultCode) &&
|
||||
localSet.Contains(p.DefaultCode))
|
||||
.Select(p => p.DefaultCode)
|
||||
.ToList();
|
||||
|
||||
Console.WriteLine($"Aantal matches: {matches.Count}");
|
||||
|
||||
foreach (var m in matches)
|
||||
Console.WriteLine($"MATCH: {m}");
|
||||
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
9
OdooAPI/Services/PriceComparer.cs
Normal file
9
OdooAPI/Services/PriceComparer.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace OdooAPI.Services;
|
||||
|
||||
public static class PriceComparer
|
||||
{
|
||||
public static bool HasChanged(decimal oldPrice, decimal newPrice)
|
||||
{
|
||||
return oldPrice != newPrice;
|
||||
}
|
||||
}
|
||||
50
OdooAPI/Services/SupplierMatchService.cs
Normal file
50
OdooAPI/Services/SupplierMatchService.cs
Normal file
@ -0,0 +1,50 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using OdooAPI.Database;
|
||||
using OdooAPI.DataAccess.Odoo.Repositories;
|
||||
|
||||
namespace OdooAPI.Services;
|
||||
|
||||
public class SupplierMatchService
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
private readonly OdooProductRepository _repo;
|
||||
|
||||
public SupplierMatchService(AppDbContext db, OdooProductRepository repo)
|
||||
{
|
||||
_db = db;
|
||||
_repo = repo;
|
||||
}
|
||||
|
||||
public async Task RunAsync(List<string> matchedCodes)
|
||||
{
|
||||
Console.WriteLine("SUPPLIER MATCHING");
|
||||
|
||||
foreach (var code in matchedCodes)
|
||||
{
|
||||
Console.WriteLine($"\nProduct: {code}");
|
||||
|
||||
var localSuppliers = await _db.LevArtikelen
|
||||
.Where(x => x.ArtikelNummerLev == code)
|
||||
.Select(x => new
|
||||
{
|
||||
Naam = x.Leverancier.Naam,
|
||||
Code = x.ArtikelNummerLev,
|
||||
Prijs = x.Inkoopprijs,
|
||||
Delay = x.Leverbaar ? 1 : 0
|
||||
})
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var s in localSuppliers)
|
||||
{
|
||||
Console.WriteLine($"UPSERT {s.Naam}");
|
||||
|
||||
await _repo.UpsertSupplierAsync(
|
||||
code,
|
||||
s.Naam,
|
||||
s.Code,
|
||||
s.Prijs,
|
||||
s.Delay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user