423 lines
14 KiB
C#
423 lines
14 KiB
C#
using Azure;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Options;
|
|
using OnlineAssessment.Common;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Net.Http;
|
|
using System.Net.Http.Json;
|
|
using System.Text.Json;
|
|
using System.Threading.Tasks;
|
|
|
|
public class OdooService
|
|
{
|
|
protected readonly HttpClient _httpClient;
|
|
protected readonly OdooConfig _config;
|
|
protected readonly IMemoryCache _cache;
|
|
|
|
public OdooService(HttpClient httpClient, IOptions<OdooConfig> config, IMemoryCache cache)
|
|
{
|
|
_httpClient = httpClient;
|
|
_config = config.Value;
|
|
_cache = cache;
|
|
}
|
|
|
|
public async Task<string> AuthenticateAsync()
|
|
{
|
|
const string cacheKey = "OdooSession";
|
|
if (_cache.TryGetValue(cacheKey, out string cachedSessionId))
|
|
{
|
|
return cachedSessionId;
|
|
}
|
|
|
|
var payload = new
|
|
{
|
|
jsonrpc = "2.0",
|
|
@params = new
|
|
{
|
|
db = _config.Db,
|
|
login = _config.Username,
|
|
password = _config.Password
|
|
}
|
|
};
|
|
|
|
var response = await _httpClient.PostAsJsonAsync($"{_config.BaseUrl}/web/session/authenticate", payload);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
throw new HttpRequestException($"Authentication failed with status code: {response.StatusCode}");
|
|
}
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<JsonDocument>();
|
|
var sessionId = ExtractSessionId(response);
|
|
|
|
if (string.IsNullOrEmpty(sessionId))
|
|
{
|
|
throw new Exception("Session ID not found in authentication response.");
|
|
}
|
|
|
|
CacheSessionId(sessionId);
|
|
|
|
return sessionId;
|
|
}
|
|
|
|
public async Task<int> CreateAsync(string model, object fields)
|
|
{
|
|
var sessionId = await AuthenticateAsync();
|
|
_httpClient.DefaultRequestHeaders.Add("Cookie", $"session_id={sessionId}");
|
|
|
|
var payload = new
|
|
{
|
|
jsonrpc = "2.0",
|
|
method = "call",
|
|
@params = new
|
|
{
|
|
model = model,
|
|
method = "create",
|
|
args = new[] { fields },
|
|
kwargs = new { }
|
|
}
|
|
};
|
|
|
|
var response = await _httpClient.PostAsJsonAsync($"{_config.BaseUrl}/web/dataset/call_kw/{model}/create", payload);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
throw new HttpRequestException($"Failed to create record with status code: {response.StatusCode}");
|
|
}
|
|
|
|
var responseContent1 = await response.Content.ReadAsStringAsync();
|
|
Console.WriteLine($"Response Content: {responseContent1}");
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<JsonDocument>();
|
|
if (result == null || !result.RootElement.TryGetProperty("result", out var idElement))
|
|
{
|
|
throw new Exception("Failed to retrieve ID from response.");
|
|
}
|
|
|
|
return idElement.GetInt32();
|
|
}
|
|
|
|
|
|
public async Task<bool> DeleteAsync(string model, int recordId)
|
|
{
|
|
var sessionId = await AuthenticateAsync();
|
|
_httpClient.DefaultRequestHeaders.Add("Cookie", $"session_id={sessionId}");
|
|
|
|
var payload = new
|
|
{
|
|
jsonrpc = "2.0",
|
|
method = "call",
|
|
@params = new
|
|
{
|
|
model = model,
|
|
method = "unlink", // Use the 'unlink' method to delete the record
|
|
args = new[] { recordId },
|
|
kwargs = new { }
|
|
}
|
|
};
|
|
|
|
var response = await _httpClient.PostAsJsonAsync($"{_config.BaseUrl}/web/dataset/call_kw", payload);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
throw new HttpRequestException($"Failed to delete record with status code: {response.StatusCode}");
|
|
}
|
|
|
|
var responseContent = await response.Content.ReadAsStringAsync();
|
|
Console.WriteLine($"Response Content: {responseContent}");
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<JsonDocument>();
|
|
if (result == null || !result.RootElement.TryGetProperty("result", out var resultElement))
|
|
{
|
|
throw new Exception("Failed to retrieve result from the response.");
|
|
}
|
|
|
|
// If result is 1, it means the operation (deletion) was successful
|
|
return resultElement.GetBoolean();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Updates an existing record in Odoo.
|
|
/// </summary>
|
|
/// <param name="model">The Odoo model to update.</param>
|
|
/// <param name="recordId">The ID of the record to update.</param>
|
|
/// <param name="updateData">The fields to update.</param>
|
|
/// <returns>True if the update was successful, otherwise false.</returns>
|
|
public async Task<bool> UpdateAsync(string model, int recordId, Dictionary<string, object> updateData)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(model))
|
|
{
|
|
throw new ArgumentException("Model name cannot be null or empty.", nameof(model));
|
|
}
|
|
|
|
if (recordId <= 0)
|
|
{
|
|
throw new ArgumentException("Invalid record ID.", nameof(recordId));
|
|
}
|
|
|
|
if (updateData == null || updateData.Count == 0)
|
|
{
|
|
throw new ArgumentNullException(nameof(updateData), "Update data cannot be null or empty.");
|
|
}
|
|
|
|
var sessionId = await AuthenticateAsync();
|
|
_httpClient.DefaultRequestHeaders.Add("Cookie", $"session_id={sessionId}");
|
|
|
|
var payload = new
|
|
{
|
|
jsonrpc = "2.0",
|
|
method = "call",
|
|
@params = new
|
|
{
|
|
model = model,
|
|
method = "write",
|
|
args = new object[] { new int[] { recordId }, updateData },
|
|
kwargs = new { }
|
|
}
|
|
};
|
|
|
|
var response = await _httpClient.PostAsJsonAsync($"{_config.BaseUrl}/web/dataset/call_kw/{model}/write", payload);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
throw new HttpRequestException($"Failed to update record with status code: {response.StatusCode}");
|
|
}
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<JsonDocument>();
|
|
if (result == null || !result.RootElement.TryGetProperty("result", out var successElement))
|
|
{
|
|
throw new Exception("Failed to retrieve update status from response.");
|
|
}
|
|
|
|
return successElement.GetBoolean();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Performs a search_read operation on the specified Odoo model.
|
|
/// </summary>
|
|
/// <param name="model">The Odoo model to search in.</param>
|
|
/// <param name="filterData">The filter criteria and fields to retrieve.</param>
|
|
/// <returns>A list of results from the search_read operation.</returns>
|
|
/// <exception cref="Exception">Thrown when the request fails or response is invalid.</exception>
|
|
public async Task<List<object>> SearchAsync(string model, object filterData, string[] fields)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(model))
|
|
throw new ArgumentException("Model name cannot be null or empty.", nameof(model));
|
|
|
|
var sessionId = await AuthenticateAsync();
|
|
_httpClient.DefaultRequestHeaders.Add("Cookie", $"session_id={sessionId}");
|
|
|
|
// Extract `domain` from `filterData`
|
|
var domainFilter = filterData.GetType().GetProperty("domain")?.GetValue(filterData) as List<object>;
|
|
|
|
var payload = new
|
|
{
|
|
jsonrpc = "2.0",
|
|
method = "call",
|
|
@params = new
|
|
{
|
|
model,
|
|
method = "search_read",
|
|
args = new object[] { domainFilter },
|
|
kwargs = new { fields }
|
|
}
|
|
};
|
|
|
|
var response = await _httpClient.PostAsJsonAsync($"{_config.BaseUrl}/web/dataset/call_kw", payload);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
throw new HttpRequestException($"Failed to search '{model}' with status code: {response.StatusCode}");
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<JsonDocument>();
|
|
if (result == null || !result.RootElement.TryGetProperty("result", out var records))
|
|
throw new Exception($"Failed to retrieve records from search response for '{model}'.");
|
|
|
|
return records.EnumerateArray().Select(record => JsonSerializer.Deserialize<object>(record.GetRawText())).ToList();
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Performs a search_read operation on the specified Odoo model.
|
|
/// </summary>
|
|
/// <param name="model">The Odoo model to search in.</param>
|
|
/// <param name="filterData">The filter criteria and fields to retrieve.</param>
|
|
/// <returns>A list of results from the search_read operation.</returns>
|
|
/// <exception cref="Exception">Thrown when the request fails or response is invalid.</exception>
|
|
public async Task<List<object>> SearchReadAttendanceAsync(string model, object filterData)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(model))
|
|
{
|
|
throw new ArgumentException("Model name cannot be null or empty.", nameof(model));
|
|
}
|
|
|
|
if (filterData == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(filterData), "Filter data cannot be null.");
|
|
}
|
|
|
|
var sessionId = await AuthenticateAsync();
|
|
_httpClient.DefaultRequestHeaders.Add("Cookie", $"session_id={sessionId}");
|
|
|
|
// Extract `domain` from `filterData`
|
|
var domainFilter = filterData.GetType().GetProperty("domain")?.GetValue(filterData) as List<object>;
|
|
|
|
|
|
var payload = new
|
|
{
|
|
jsonrpc = "2.0",
|
|
method = "call",
|
|
@params = new
|
|
{
|
|
model,
|
|
method = "search_read",
|
|
args = new object[] { domainFilter },
|
|
kwargs = new { fields = new[] { "id", "employee_id", "check_in", "check_out" } }
|
|
}
|
|
};
|
|
|
|
var response = await _httpClient.PostAsJsonAsync($"{_config.BaseUrl}/web/dataset/call_kw", payload);
|
|
|
|
if (!response.IsSuccessStatusCode)
|
|
{
|
|
throw new HttpRequestException($"Failed to perform search_read on model '{model}' with status code: {response.StatusCode}");
|
|
}
|
|
|
|
|
|
var responseContent1 = await response.Content.ReadAsStringAsync();
|
|
Console.WriteLine($"Response Content: {responseContent1}");
|
|
|
|
|
|
var result = await response.Content.ReadFromJsonAsync<JsonDocument>();
|
|
if (result == null || !result.RootElement.TryGetProperty("result", out var records))
|
|
{
|
|
throw new Exception("Failed to retrieve results from search_read response.");
|
|
}
|
|
|
|
return records.EnumerateArray().Select(record => JsonSerializer.Deserialize<object>(record.GetRawText())).ToList();
|
|
}
|
|
|
|
public async Task<bool> ValidateSubscriptionAsync(string instituteId)
|
|
{
|
|
try
|
|
{
|
|
var sessionId = await AuthenticateAsync();
|
|
_httpClient.DefaultRequestHeaders.Add("Cookie", $"session_id={sessionId}");
|
|
|
|
var domain = new[]
|
|
{
|
|
new object[] { "company_registry", "=", instituteId }
|
|
};
|
|
|
|
var companySearchPayload = new
|
|
{
|
|
jsonrpc = "2.0",
|
|
method = "call",
|
|
@params = new
|
|
{
|
|
model = "res.partner",
|
|
method = "search_read",
|
|
args = new object[] { domain },
|
|
kwargs = new { fields = new[] { "id" } }
|
|
}
|
|
};
|
|
|
|
var companyResponse = await _httpClient.PostAsJsonAsync($"{_config.BaseUrl}/web/dataset/call_kw", companySearchPayload);
|
|
|
|
if (!companyResponse.IsSuccessStatusCode)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var companyData = await companyResponse.Content.ReadFromJsonAsync<JsonDocument>();
|
|
if (companyData == null || !companyData.RootElement.TryGetProperty("result", out var resultIdList))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var firstRecord = resultIdList.EnumerateArray().FirstOrDefault();
|
|
|
|
if (!(firstRecord.ValueKind == JsonValueKind.Object) || !firstRecord.TryGetProperty("id", out var companyIdElement))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var companyId = companyIdElement.GetInt32();
|
|
|
|
var domain2 = new[]
|
|
{
|
|
new object[] { "partner_id", "=", companyId }
|
|
};
|
|
|
|
var subscriptionSearchPayload = new
|
|
{
|
|
jsonrpc = "2.0",
|
|
method = "call",
|
|
@params = new
|
|
{
|
|
model = "sale.subscription.report",
|
|
method = "search_read",
|
|
args = new object[] { domain2 },
|
|
kwargs = new { fields = new[] { "subscription_state" } }
|
|
}
|
|
};
|
|
|
|
var subscriptionResponse = await _httpClient.PostAsJsonAsync($"{_config.BaseUrl}/web/dataset/call_kw", subscriptionSearchPayload);
|
|
|
|
if (!subscriptionResponse.IsSuccessStatusCode)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var subscriptionData = await subscriptionResponse.Content.ReadFromJsonAsync<JsonDocument>();
|
|
if (subscriptionData == null || !subscriptionData.RootElement.TryGetProperty("result", out var resultStateList))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
firstRecord = resultStateList.EnumerateArray().FirstOrDefault();
|
|
|
|
if (!(firstRecord.ValueKind == JsonValueKind.Object) || !firstRecord.TryGetProperty("subscription_state", out var subscriptionElement))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var subscriptionState = subscriptionElement.GetString();
|
|
|
|
return subscriptionState == "3_progress";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error validating subscription: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private string ExtractSessionId(HttpResponseMessage response)
|
|
{
|
|
if (response.Headers.TryGetValues("Set-Cookie", out var cookies))
|
|
{
|
|
var sessionCookie = cookies.FirstOrDefault(c => c.Contains("session_id"));
|
|
return sessionCookie?.Split(';').FirstOrDefault()?.Split('=')[1];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private void CacheSessionId(string sessionId)
|
|
{
|
|
var cacheEntryOptions = new MemoryCacheEntryOptions
|
|
{
|
|
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30),
|
|
Size = 1
|
|
};
|
|
|
|
_cache.Set("OdooSession", sessionId, cacheEntryOptions);
|
|
}
|
|
}
|