Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions MAES.Fiskal2/IPosrednik.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace MAES.Fiskal2;
[JsonPolymorphic(TypeDiscriminatorPropertyName = "$type")]
[JsonDerivedType(typeof(Super), "Super")]
[JsonDerivedType(typeof(EPoslovanje), "EPoslovanje")]
[JsonDerivedType(typeof(Fina), "Fina")]
public interface IPosrednik
{
/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion MAES.Fiskal2/IzlazniERacun.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class IzlazniERacun
/// <summary>
/// Jedinstveni identifikator računa u sustavu posrednika.
/// </summary>
public Guid Guid { get; set; }
public string Id { get; set; } = "";

/// <summary>
/// Redni broj računa.
Expand Down
2 changes: 1 addition & 1 deletion MAES.Fiskal2/MAES.Fiskal2.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

<!-- NuGet Metadata -->
<PackageId>MAES.Fiskal2</PackageId>
<Version>0.1.3</Version>
<Version>0.2.0</Version>
<Authors>Roko Tomović</Authors>
<Company>MAES</Company>
<Description>C# biblioteka za rad s Hrvatskim fiskalnim posrednicima</Description>
Expand Down
192 changes: 134 additions & 58 deletions MAES.Fiskal2/Posrednici/EPoslovanje.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,93 +32,169 @@ public class EPoslovanje : IPosrednik
/// </summary>
public string Password { get; set; } = "";

/// <summary>
/// Dohvaća XML/UBL sadržaj ulaznog računa. Nije implementirano.
/// </summary>
public Task<string> UlazniUBLAsync(string id, CancellationToken token = default) => throw new NotImplementedException();
HttpClient createClient()
{
var client = new HttpClient
{
BaseAddress = new Uri(IsDev ? URI_DEV : URI)
};

client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

return client;
}

async Task<string> apiKey()
{
using var client = createClient();

var request = new HttpRequestMessage(HttpMethod.Post, "/api/v2/account/apikey");
request.Content = new StringContent(JsonSerializer.Serialize(new
{
username = Username,
password = Password,
vatId = OIB,
softwareId = "MAES.Fiskal2"
}), Encoding.UTF8, "application/json");

var response = await client.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();

if (!response.IsSuccessStatusCode)
throw new HttpRequestException(json);

using var doc = JsonDocument.Parse(json);
return doc.RootElement.GetProperty("apiKey").GetString()!;
}

async Task<JsonDocument> sendRequest(HttpMethod method, string url, object? body, CancellationToken token)
{
using var client = createClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", await apiKey());

var req = new HttpRequestMessage(method, url);
if (body != null) req.Content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json");

var res = await client.SendAsync(req, token);
var json = await res.Content.ReadAsStringAsync();

if (!res.IsSuccessStatusCode) throw new HttpRequestException(json);

return JsonDocument.Parse(json);
}

async Task changeStatusAsync(string id, int status, string? note = null, double? partialPaymentAmount = null, CancellationToken token = default)
{
var body = new Dictionary<string, object?>
{
["status"] = status,
["changedOn"] = DateTimeOffset.Now.ToString("O")
};

if (!string.IsNullOrWhiteSpace(note)) body["note"] = note;
if (partialPaymentAmount.HasValue) body["partialPaymentAmount"] = partialPaymentAmount.Value;

await sendRequest(HttpMethod.Post, $"/api/v2/document/changestatus/{id}", body, token);
}

/// <summary>
/// Dohvaća PDF sadržaj ulaznog računa. Nije implementirano.
/// Dohvaća XML/UBL sadržaj ulaznog računa.
/// </summary>
public Task<byte[]> UlazniPdfAsync(string id, CancellationToken token = default) => throw new NotImplementedException();
public async Task<string> UlazniUBLAsync(string id, CancellationToken token = default) =>
(await sendRequest(HttpMethod.Get, $"/api/v2/document/get/{id}", null, token)).RootElement.GetProperty("document").GetString()!;

/// <summary>
/// Dohvaća popis ulaznih e-računa. Nije implementirano.
/// Dohvaća PDF sadržaj ulaznog računa.
/// </summary>
public Task<IEnumerable<UlazniERacun>> UlazniListAsync(DateTime from, DateTime to, CancellationToken token = default) => throw new NotImplementedException();
public async Task<byte[]> UlazniPdfAsync(string id, CancellationToken token = default) =>
Convert.FromBase64String((await sendRequest(HttpMethod.Get, $"/api/v2/document/visualization/{id}", null, token)).RootElement.GetProperty("pdf").GetString()!);

/// <summary>
/// Dohvaća XML/UBL sadržaj izlaznog računa. Nije implementirano.
/// Dohvaća popis ulaznih e-računa.
/// </summary>
public Task<string> IzlazniUBLAsync(string id, CancellationToken token = default) => throw new NotImplementedException();
public async Task<IEnumerable<UlazniERacun>> UlazniListAsync(DateTime from, DateTime to, CancellationToken token = default)
{
var doc = await sendRequest(HttpMethod.Get, $"/api/v2/document/incoming?insertedFrom={from:O}&insertedTo={to:O}&limit=1000&offset=0", null, token);

var list = new List<UlazniERacun>();

foreach (var item in doc.RootElement.EnumerateArray())
{
list.Add(new UlazniERacun
{
Id = item.GetProperty("id").GetInt64().ToString(),
Datum = item.GetProperty("issuedOn").GetDateTime(),
Broj = item.GetProperty("documentId").GetString()!,
Status = UlazniERacunStatus.Zaprimljeno, // TODO: ovo treba popravit
Partner = item.GetProperty("customerPartyName").GetString()!,
PartnerOIB = item.GetProperty("customerPartyVATId").GetString()!
});
}

return list;
}

/// <summary>
/// Dohvaća PDF sadržaj izlaznog računa. Nije implementirano.
/// Dohvaća XML/UBL sadržaj izlaznog računa.
/// </summary>
public Task<byte[]> IzlazniPdfAsync(string id, CancellationToken token = default) => throw new NotImplementedException();
public Task<string> IzlazniUBLAsync(string id, CancellationToken token = default)
=> UlazniUBLAsync(id, token);

/// <summary>
/// Dohvaća popis izlaznih e-računa. Nije implementirano.
/// Dohvaća PDF sadržaj izlaznog računa.
/// </summary>
public Task<IEnumerable<IzlazniERacun>> IzlazniListAsync(DateTime from, DateTime to, CancellationToken token = default) => throw new NotImplementedException();
public Task<byte[]> IzlazniPdfAsync(string id, CancellationToken token = default)
=> UlazniPdfAsync(id, token);

/// <summary>
/// Evidentira UBL dokument u ePoslovanje sustav.
/// Dohvaća popis izlaznih e-računa.
/// </summary>
public async Task EvidentirajUBLAsync(string ubl, CancellationToken token = default)
public async Task<IEnumerable<IzlazniERacun>> IzlazniListAsync(
DateTime from,
DateTime to,
CancellationToken token = default)
{
using var client = new HttpClient();
client.BaseAddress = new Uri(IsDev ? URI_DEV : URI);
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var doc = await sendRequest(HttpMethod.Get, $"/api/v2/document/outgoing?insertedFrom={from:O}&insertedTo={to:O}&limit=1000&offset=0", null, token);

var key = await apiKey(client);
var list = new List<IzlazniERacun>();

client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(key);

using var request = new HttpRequestMessage(HttpMethod.Post, "/api/v2/document/send");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Content = new StringContent(JsonSerializer.Serialize(new Dictionary<string, object>
foreach (var item in doc.RootElement.EnumerateArray())
{
["document"] = ubl,
["softwareId"] = "MAES.Blagajna"
}), Encoding.UTF8, "application/json");

var response = await client.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();

if (!response.IsSuccessStatusCode) throw new HttpRequestException($"ePoslovanje send document error: {json}");

// TODO: Ovo treba popravit, dohvatit id od eposlovanja
list.Add(new IzlazniERacun
{
Id = item.GetProperty("id").GetInt64().ToString(),
Broj = item.GetProperty("documentId").GetString()!,
Datum = item.GetProperty("issuedOn").GetDateTime(),
PartnerNaziv = item.GetProperty("customerPartyName").GetString()!,
PartnerOIB = item.GetProperty("customerPartyVATId").GetString()!,
Status = IzlazniERacunStatus.Poslano // TODO: ovo treba popravit
});
}

return list;
}

/// <summary>
/// Evidentira uplatu za račun. Nije implementirano.
/// Evidentira UBL dokument u ePoslovanje sustav.
/// </summary>
public Task EvidentirajUplatuAsync(string id, DateTime date, double amount, NacinPlacanja paymentMethod, CancellationToken token = default) => throw new NotImplementedException();
public async Task EvidentirajUBLAsync(string ubl, CancellationToken token = default) => await sendRequest(HttpMethod.Post, "/api/v2/document/send", new
{
document = ubl,
softwareId = "MAES.Blagajna"
}, token);

/// <summary>
/// Odbija račun. Nije implementirano.
/// Evidentira uplatu za račun.
/// </summary>
public Task OdbijRacunAsync(string id, RazlogOdbijanja razlog, string opis, CancellationToken token = default) => throw new NotImplementedException();

async Task<string> apiKey(HttpClient client)
public Task EvidentirajUplatuAsync(string id, DateTime date, double amount, NacinPlacanja paymentMethod, CancellationToken token = default)
{
using var request = new HttpRequestMessage(HttpMethod.Post, "api/v2/account/apikey");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
request.Content = new StringContent(JsonSerializer.Serialize(new Dictionary<string, string>
{
["username"] = Username,
["password"] = Password,
["vatId"] = OIB,
["softwareId"] = "MAES.Blagajna"
}), Encoding.UTF8, "application/json");

var response = await client.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();

if (!response.IsSuccessStatusCode) throw new HttpRequestException($"{json}");

using var doc = JsonDocument.Parse(json);
return doc.RootElement.GetProperty("apiKey").GetString()!;
var status = amount > 0 ? 8 : 7; // 8: partialno, 7: potpuno
return changeStatusAsync(id, status, partialPaymentAmount: status == 8 ? amount : null, token: token);
}

/// <summary>
/// Odbija račun.
/// </summary>
public Task OdbijRacunAsync(string id, RazlogOdbijanja razlog, string opis, CancellationToken token = default) =>
changeStatusAsync(id, status: 6, $"{razlog}: {opis}", token: token);
}
113 changes: 113 additions & 0 deletions MAES.Fiskal2/Posrednici/Fina.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
namespace MAES.Fiskal2.Posrednici;

/// <summary>
/// Implementacija informacijskog posrednika FINA za razmjenu e-računa.
/// </summary>
public class Fina : IPosrednik
{
/// <summary>
/// Evidentira i šalje UBL/XML dokument prema FINA e-Račun sustavu.
/// </summary>
/// <param name="ubl">UBL/XML sadržaj dokumenta.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>Asinkrona operacija slanja dokumenta.</returns>
public Task EvidentirajUBLAsync(string ubl, CancellationToken token = default)
{
throw new NotImplementedException();
}

/// <summary>
/// Evidentira uplatu za dokument unutar FINA sustava.
/// </summary>
/// <param name="id">ID dokumenta u sustavu posrednika.</param>
/// <param name="date">Datum i vrijeme evidentiranja uplate.</param>
/// <param name="amount">Iznos uplate.</param>
/// <param name="paymentMethod">Način plaćanja.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>Asinkrona operacija evidentiranja uplate.</returns>
public Task EvidentirajUplatuAsync(string id, DateTime date, double amount, NacinPlacanja paymentMethod, CancellationToken token = default)
{
throw new NotImplementedException();
}

/// <summary>
/// Dohvaća popis izlaznih e-računa za zadani period.
/// </summary>
/// <param name="from">Početni datum pretrage.</param>
/// <param name="to">Završni datum pretrage.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>Popis izlaznih e-računa.</returns>
public Task<IEnumerable<IzlazniERacun>> IzlazniListAsync(DateTime from, DateTime to, CancellationToken token = default)
{
throw new NotImplementedException();
}

/// <summary>
/// Dohvaća PDF vizualizaciju izlaznog dokumenta.
/// </summary>
/// <param name="id">ID dokumenta.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>PDF dokument kao byte array.</returns>
public Task<byte[]> IzlazniPdfAsync(string id, CancellationToken token = default)
{
throw new NotImplementedException();
}

/// <summary>
/// Dohvaća UBL/XML sadržaj izlaznog dokumenta.
/// </summary>
/// <param name="id">ID dokumenta.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>UBL/XML sadržaj dokumenta.</returns>
public Task<string> IzlazniUBLAsync(string id, CancellationToken token = default)
{
throw new NotImplementedException();
}

/// <summary>
/// Odbija dokument uz zadani razlog i opis.
/// </summary>
/// <param name="id">ID dokumenta.</param>
/// <param name="razlog">Razlog odbijanja.</param>
/// <param name="opis">Dodatni opis odbijanja.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>Asinkrona operacija odbijanja dokumenta.</returns>
public Task OdbijRacunAsync(string id, RazlogOdbijanja razlog, string opis, CancellationToken token = default)
{
throw new NotImplementedException();
}

/// <summary>
/// Dohvaća popis ulaznih e-računa za zadani period.
/// </summary>
/// <param name="from">Početni datum pretrage.</param>
/// <param name="to">Završni datum pretrage.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>Popis ulaznih e-računa.</returns>
public Task<IEnumerable<UlazniERacun>> UlazniListAsync(DateTime from, DateTime to, CancellationToken token = default)
{
throw new NotImplementedException();
}

/// <summary>
/// Dohvaća PDF vizualizaciju ulaznog dokumenta.
/// </summary>
/// <param name="id">ID dokumenta.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>PDF dokument kao byte array.</returns>
public Task<byte[]> UlazniPdfAsync(string id, CancellationToken token = default)
{
throw new NotImplementedException();
}

/// <summary>
/// Dohvaća UBL/XML sadržaj ulaznog dokumenta.
/// </summary>
/// <param name="id">ID dokumenta.</param>
/// <param name="token">Cancellation token.</param>
/// <returns>UBL/XML sadržaj dokumenta.</returns>
public Task<string> UlazniUBLAsync(string id, CancellationToken token = default)
{
throw new NotImplementedException();
}
}
4 changes: 2 additions & 2 deletions MAES.Fiskal2/Posrednici/Super.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ public async Task<IEnumerable<UlazniERacun>> UlazniListAsync(DateTime from, Date
$"{x.GetProperty("SupplierAddress").GetString()}, " +
$"{x.GetProperty("SupplierZip").GetString()} " +
$"{x.GetProperty("SupplierCity").GetString()}",
Guid = x.GetProperty("Guid").GetGuid(),
Id = x.GetProperty("Guid").GetGuid().ToString(),
Status = UlazniERacunStatus.Zaprimljeno // treba popravit
});
}
Expand Down Expand Up @@ -198,7 +198,7 @@ public async Task<IEnumerable<IzlazniERacun>> IzlazniListAsync(DateTime from, Da
$"{x.GetProperty("SupplierAddress").GetString()}, " +
$"{x.GetProperty("SupplierZip").GetString()} " +
$"{x.GetProperty("SupplierCity").GetString()}",
Guid = x.GetProperty("Guid").GetGuid(),
Id = x.GetProperty("Guid").GetGuid().ToString(),
Status = IzlazniERacunStatus.Poslano // treba popravit
});
}
Expand Down
2 changes: 1 addition & 1 deletion MAES.Fiskal2/UlazniERacun.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public class UlazniERacun
/// <summary>
/// Jedinstveni identifikator računa u sustavu posrednika.
/// </summary>
public Guid Guid { get; set; }
public string Id { get; set; } = "";

/// <summary>
/// Redni broj računa.
Expand Down
Loading
Loading