diff --git a/.gitignore b/.gitignore index 37eed66b2..a61e5982f 100644 --- a/.gitignore +++ b/.gitignore @@ -214,3 +214,4 @@ _Pvt_Extensions/ ModelManifest.xml .vscode +.DS_Store \ No newline at end of file diff --git a/azure-pipelines/templates/scu-it/test.template.yml b/azure-pipelines/templates/scu-it/test.template.yml index e5ff57aa3..d8d574b8d 100644 --- a/azure-pipelines/templates/scu-it/test.template.yml +++ b/azure-pipelines/templates/scu-it/test.template.yml @@ -31,7 +31,7 @@ steps: inputs: command: 'test' projects: | - $(WorkingDirectory)/**/*.UnitTest.csproj + $(WorkingDirectory)/**/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest.csproj arguments: '--configuration $(BuildConfiguration) --no-restore --collect "Code coverage"' nobuild: true displayName: 'Run unit tests' diff --git a/queue/fiskaltrust.Middleware.sln b/queue/fiskaltrust.Middleware.sln index 86ba71559..14830f384 100644 --- a/queue/fiskaltrust.Middleware.sln +++ b/queue/fiskaltrust.Middleware.sln @@ -139,13 +139,29 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.Stor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Interface.Tagging", "src\fiskaltrust.Interface.Tagging\fiskaltrust.Interface.Tagging.csproj", "{FD564EEA-CEAD-47E0-BB0A-113C07F57CAA}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fiskaltrust.Interface.Tagging.UnitTests", "test\fiskaltrust.Interface.Tagging.UnitTests\fiskaltrust.Interface.Tagging.UnitTests.csproj", "{9E42B570-AEEC-498E-AC81-0AB70264229B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Interface.Tagging.UnitTests", "test\fiskaltrust.Interface.Tagging.UnitTests\fiskaltrust.Interface.Tagging.UnitTests.csproj", "{9E42B570-AEEC-498E-AC81-0AB70264229B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fiskaltrust.Interface.Tagging.Generator", "src\fiskaltrust.Interface.Tagging.Generator\fiskaltrust.Interface.Tagging.Generator.csproj", "{930A9188-4273-45C5-9506-922CE7B1E712}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Interface.Tagging.Generator", "src\fiskaltrust.Interface.Tagging.Generator\fiskaltrust.Interface.Tagging.Generator.csproj", "{930A9188-4273-45C5-9506-922CE7B1E712}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fiskaltrust.Interface.Tagging.Generator.UnitTests", "test\fiskaltrust.Interface.Tagging.Generator.UnitTests\fiskaltrust.Interface.Tagging.Generator.UnitTests.csproj", "{855013AC-6255-4A3C-80C7-5140F5BD20B9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Interface.Tagging.Generator.UnitTests", "test\fiskaltrust.Interface.Tagging.Generator.UnitTests\fiskaltrust.Interface.Tagging.Generator.UnitTests.csproj", "{855013AC-6255-4A3C-80C7-5140F5BD20B9}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fiskaltrust.Interface.Tagging.Generator.IntegrationTests", "test\fiskaltrust.Interface.Tagging.Generator.IntegrationTests\fiskaltrust.Interface.Tagging.Generator.IntegrationTests.csproj", "{34229319-E3C0-4B57-A2D9-F1812CBF5311}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Interface.Tagging.Generator.IntegrationTests", "test\fiskaltrust.Interface.Tagging.Generator.IntegrationTests\fiskaltrust.Interface.Tagging.Generator.IntegrationTests.csproj", "{34229319-E3C0-4B57-A2D9-F1812CBF5311}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.Localization.QueuePT", "src\fiskaltrust.Middleware.Localization.QueuePT\fiskaltrust.Middleware.Localization.QueuePT.csproj", "{917D658E-C488-4EA5-A645-EC0E7B78CE19}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.Localization.v2", "src\fiskaltrust.Middleware.Localization.v2\fiskaltrust.Middleware.Localization.v2.csproj", "{C2242AC0-0031-4388-BF63-33FE2FBA1DD6}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "v2", "v2", "{095CBF40-606D-4CC5-91E3-D009C271BC16}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.Localization.QueuePT.UnitTest", "test\fiskaltrust.Middleware.Localization.QueuePT.UnitTest\fiskaltrust.Middleware.Localization.QueuePT.UnitTest.csproj", "{0A1860FF-6D00-4F85-9058-0854E95CA4CC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.Storage", "src\fiskaltrust.Middleware.Storage\fiskaltrust.Middleware.Storage.csproj", "{AE95A622-FB4B-4C98-A0A3-725C52A736AF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.Localization.QueueGR", "src\fiskaltrust.Middleware.Localization.QueueGR\fiskaltrust.Middleware.Localization.QueueGR.csproj", "{C823688D-1812-4864-BDDC-8BF5E45FEC18}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "fiskaltrust.Middleware.Localization.QueueGR.UnitTest", "test\fiskaltrust.Middleware.Localization.QueueGR.UnitTest\fiskaltrust.Middleware.Localization.QueueGR.UnitTest.csproj", "{6041CD04-8361-450C-91E7-6275DBF4C17B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "fiskaltrust.Middleware.Localization.QueueES.UnitTest", "test\fiskaltrust.Middleware.Localization.QueueES.UnitTest\fiskaltrust.Middleware.Localization.QueueES.UnitTest.csproj", "{EA9BED33-E964-4770-8CD8-9A90D6273996}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -349,6 +365,10 @@ Global {7F52D143-9179-4FCF-9DC7-B5F1681BD22A}.Debug|Any CPU.Build.0 = Debug|Any CPU {7F52D143-9179-4FCF-9DC7-B5F1681BD22A}.Release|Any CPU.ActiveCfg = Release|Any CPU {7F52D143-9179-4FCF-9DC7-B5F1681BD22A}.Release|Any CPU.Build.0 = Release|Any CPU + {EEDA285C-82F8-4E6D-AAFA-9E42D3C4F0E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EEDA285C-82F8-4E6D-AAFA-9E42D3C4F0E6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EEDA285C-82F8-4E6D-AAFA-9E42D3C4F0E6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EEDA285C-82F8-4E6D-AAFA-9E42D3C4F0E6}.Release|Any CPU.Build.0 = Release|Any CPU {FD564EEA-CEAD-47E0-BB0A-113C07F57CAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FD564EEA-CEAD-47E0-BB0A-113C07F57CAA}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD564EEA-CEAD-47E0-BB0A-113C07F57CAA}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -369,10 +389,34 @@ Global {34229319-E3C0-4B57-A2D9-F1812CBF5311}.Debug|Any CPU.Build.0 = Debug|Any CPU {34229319-E3C0-4B57-A2D9-F1812CBF5311}.Release|Any CPU.ActiveCfg = Release|Any CPU {34229319-E3C0-4B57-A2D9-F1812CBF5311}.Release|Any CPU.Build.0 = Release|Any CPU - {EEDA285C-82F8-4E6D-AAFA-9E42D3C4F0E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EEDA285C-82F8-4E6D-AAFA-9E42D3C4F0E6}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EEDA285C-82F8-4E6D-AAFA-9E42D3C4F0E6}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EEDA285C-82F8-4E6D-AAFA-9E42D3C4F0E6}.Release|Any CPU.Build.0 = Release|Any CPU + {917D658E-C488-4EA5-A645-EC0E7B78CE19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {917D658E-C488-4EA5-A645-EC0E7B78CE19}.Debug|Any CPU.Build.0 = Debug|Any CPU + {917D658E-C488-4EA5-A645-EC0E7B78CE19}.Release|Any CPU.ActiveCfg = Release|Any CPU + {917D658E-C488-4EA5-A645-EC0E7B78CE19}.Release|Any CPU.Build.0 = Release|Any CPU + {C2242AC0-0031-4388-BF63-33FE2FBA1DD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2242AC0-0031-4388-BF63-33FE2FBA1DD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2242AC0-0031-4388-BF63-33FE2FBA1DD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2242AC0-0031-4388-BF63-33FE2FBA1DD6}.Release|Any CPU.Build.0 = Release|Any CPU + {0A1860FF-6D00-4F85-9058-0854E95CA4CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0A1860FF-6D00-4F85-9058-0854E95CA4CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0A1860FF-6D00-4F85-9058-0854E95CA4CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0A1860FF-6D00-4F85-9058-0854E95CA4CC}.Release|Any CPU.Build.0 = Release|Any CPU + {AE95A622-FB4B-4C98-A0A3-725C52A736AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AE95A622-FB4B-4C98-A0A3-725C52A736AF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AE95A622-FB4B-4C98-A0A3-725C52A736AF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AE95A622-FB4B-4C98-A0A3-725C52A736AF}.Release|Any CPU.Build.0 = Release|Any CPU + {C823688D-1812-4864-BDDC-8BF5E45FEC18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C823688D-1812-4864-BDDC-8BF5E45FEC18}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C823688D-1812-4864-BDDC-8BF5E45FEC18}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C823688D-1812-4864-BDDC-8BF5E45FEC18}.Release|Any CPU.Build.0 = Release|Any CPU + {6041CD04-8361-450C-91E7-6275DBF4C17B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6041CD04-8361-450C-91E7-6275DBF4C17B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6041CD04-8361-450C-91E7-6275DBF4C17B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6041CD04-8361-450C-91E7-6275DBF4C17B}.Release|Any CPU.Build.0 = Release|Any CPU + {EA9BED33-E964-4770-8CD8-9A90D6273996}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA9BED33-E964-4770-8CD8-9A90D6273996}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA9BED33-E964-4770-8CD8-9A90D6273996}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA9BED33-E964-4770-8CD8-9A90D6273996}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -428,8 +472,8 @@ Global {BD6E6701-2F8E-43D1-B583-F3DC1CA52D67} = {16BF88B4-3302-49F5-A5FA-5DA96DD03F0E} {87E0049F-9DF1-4F6F-8137-7765A2B16ABA} = {5195B05E-EB55-4544-8FE6-B683747B1F9E} {F39B38CB-B8B7-4537-9130-43131AD51FF2} = {31488E86-D7EB-4964-84F1-B7338BB5A7D1} - {27C42C72-639D-4B55-A931-6896D7095685} = {C345F1F7-C2A5-472A-A55F-987A53DF3CCE} - {65633C3A-65AA-4E05-8E56-7A348872CCC3} = {C345F1F7-C2A5-472A-A55F-987A53DF3CCE} + {27C42C72-639D-4B55-A931-6896D7095685} = {095CBF40-606D-4CC5-91E3-D009C271BC16} + {65633C3A-65AA-4E05-8E56-7A348872CCC3} = {095CBF40-606D-4CC5-91E3-D009C271BC16} {5FAB9D88-4B90-4DF0-B1B7-1F50DF7CFCA4} = {16BF88B4-3302-49F5-A5FA-5DA96DD03F0E} {3C0310D9-EC55-4115-A2D5-0DC8C1246645} = {C345F1F7-C2A5-472A-A55F-987A53DF3CCE} {04B32BB6-2E1D-400F-B7B2-2638483B0825} = {16BF88B4-3302-49F5-A5FA-5DA96DD03F0E} @@ -440,6 +484,14 @@ Global {930A9188-4273-45C5-9506-922CE7B1E712} = {08381205-3409-4468-92EB-ACA65F8D82FB} {855013AC-6255-4A3C-80C7-5140F5BD20B9} = {EAEEF28B-F372-48C1-8F9B-45CEB95159A2} {34229319-E3C0-4B57-A2D9-F1812CBF5311} = {EAEEF28B-F372-48C1-8F9B-45CEB95159A2} + {917D658E-C488-4EA5-A645-EC0E7B78CE19} = {095CBF40-606D-4CC5-91E3-D009C271BC16} + {C2242AC0-0031-4388-BF63-33FE2FBA1DD6} = {095CBF40-606D-4CC5-91E3-D009C271BC16} + {095CBF40-606D-4CC5-91E3-D009C271BC16} = {C345F1F7-C2A5-472A-A55F-987A53DF3CCE} + {0A1860FF-6D00-4F85-9058-0854E95CA4CC} = {16BF88B4-3302-49F5-A5FA-5DA96DD03F0E} + {AE95A622-FB4B-4C98-A0A3-725C52A736AF} = {5195B05E-EB55-4544-8FE6-B683747B1F9E} + {C823688D-1812-4864-BDDC-8BF5E45FEC18} = {095CBF40-606D-4CC5-91E3-D009C271BC16} + {6041CD04-8361-450C-91E7-6275DBF4C17B} = {16BF88B4-3302-49F5-A5FA-5DA96DD03F0E} + {EA9BED33-E964-4770-8CD8-9A90D6273996} = {16BF88B4-3302-49F5-A5FA-5DA96DD03F0E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E8BEA609-BD83-4165-A14A-D010D2CC87AD} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/ESSSCD/IESSSCD.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/ESSSCD/IESSSCD.cs new file mode 100644 index 000000000..cf112d218 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/ESSSCD/IESSSCD.cs @@ -0,0 +1,26 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.storage.V0.MasterData; + +namespace fiskaltrust.Middleware.Localization.QueueES.ESSSCD; + +public interface IESSSCD +{ + Task ProcessReceiptAsync(ProcessRequest request); + + Task GetInfoAsync(); +} + + +public class ProcessRequest +{ + public required ReceiptRequest ReceiptRequest { get; set; } + + public required ReceiptResponse ReceiptResponse { get; set; } + public required ReceiptRequest? PreviousReceiptRequest { get; set; } + public required ReceiptResponse? PreviousReceiptResponse { get; set; } +} + +public class ProcessResponse +{ + public required ReceiptResponse ReceiptResponse { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/ESSSCD/InMemorySCU.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/ESSSCD/InMemorySCU.cs new file mode 100644 index 000000000..8d814771e --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/ESSSCD/InMemorySCU.cs @@ -0,0 +1,157 @@ +using System.Security.Cryptography.X509Certificates; +using System.ServiceModel; +using System.Text; +using System.Text.Json; +using System.Xml; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Contracts.Repositories; +using fiskaltrust.Middleware.Localization.QueueES.Exports; +using fiskaltrust.Middleware.Localization.QueueES.Factories; +using fiskaltrust.Middleware.Localization.QueueES.Interface; +using fiskaltrust.Middleware.Localization.v2.Configuration; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.SCU.ES.Helpers; +using fiskaltrust.Middleware.SCU.ES.Models; +using fiskaltrust.Middleware.SCU.ES.Soap; +using fiskaltrust.Middleware.Storage.ES; +using fiskaltrust.storage.V0.MasterData; + +namespace fiskaltrust.Middleware.Localization.QueueES.ESSSCD; + +public class ESSSCDInfo +{ +} + +public class InMemorySCUConfiguration +{ + public string BaseUrl { get; set; } = "https://prewww10.aeat.es"; + public string QRCodeBaseUrl { get; set; } = "https://prewww2.aeat.es"; + + public X509Certificate2 Certificate { get; set; } = null!; + + public static InMemorySCUConfiguration FromConfiguration(PackageConfiguration packageConfiguration) + => JsonSerializer.Deserialize(JsonSerializer.Serialize(packageConfiguration.Configuration)) ?? new InMemorySCUConfiguration(); +} + +public class InMemorySCU : IESSSCD +{ + private readonly InMemorySCUConfiguration _configuration; + + private readonly VeriFactuMapping _veriFactuMapping; + + public InMemorySCU(ftSignaturCreationUnitES _, MasterDataConfiguration masterData, InMemorySCUConfiguration configuration, IMiddlewareQueueItemRepository queueItemRepository) + { + _configuration = configuration; + _veriFactuMapping = new VeriFactuMapping(masterData, queueItemRepository, configuration.Certificate); + } + + public async Task ProcessReceiptAsync(ProcessRequest request) + { + request.ReceiptResponse.ftReceiptIdentification += $"{request.ReceiptResponse.ftQueueRow}/{request.ReceiptRequest.cbReceiptReference}"; + + ReceiptResponse receiptResponse; + + if (request.ReceiptRequest.IsVoid()) + { + if (request.PreviousReceiptRequest is null || request.PreviousReceiptResponse is null) + { + throw new Exception("There needs to be a previous receipt in the chain to perform a void"); + } + + var journalES = await _veriFactuMapping.CreateRegistroFacturacionAnulacionAsync(request.ReceiptRequest, request.ReceiptResponse, request.PreviousReceiptRequest, request.PreviousReceiptResponse); + + var envelope = new Envelope + { + Body = new RequestBody + { + RegFactuSistemaFacturacion = _veriFactuMapping.CreateRegFactuSistemaFacturacion(journalES) + } + }; + + receiptResponse = CreateResponse( + await new Client(new Uri(_configuration.BaseUrl), _configuration.Certificate).SendAsync(envelope), + request, + journalES.IDFactura.NumSerieFacturaAnulada, + journalES.Huella, + journalES.Signature, + journalES.IDFactura.IDEmisorFacturaAnulada + ); + } + else + { + var journalES = await _veriFactuMapping.CreateRegistroFacturacionAltaAsync(request.ReceiptRequest, request.ReceiptResponse, request.PreviousReceiptRequest, request.PreviousReceiptResponse); + + var envelope = new Envelope + { + Body = new RequestBody + { + RegFactuSistemaFacturacion = _veriFactuMapping.CreateRegFactuSistemaFacturacion(journalES) + } + }; + + receiptResponse = CreateResponse( + await new Client(new Uri(_configuration.BaseUrl), _configuration.Certificate).SendAsync(envelope), + request, + journalES.IDFactura.NumSerieFactura, + journalES.Huella, + journalES.Signature, + journalES.IDFactura.IDEmisorFactura, + SignaturItemFactory.CreateESQRCode(_configuration.QRCodeBaseUrl + "/wlpl/TIKE-CONT/ValidarQR", journalES) + ); + } + + return new ProcessResponse + { + ReceiptResponse = receiptResponse, + }; + } + + private ReceiptResponse CreateResponse( + Result veriFactuResponse, + ProcessRequest request, + string numSerieFactura, + string huella, + XmlElement? signature, + string idEmisorFactura, + SignatureItem? signatureItem = null + ) + { + if (veriFactuResponse.IsErr) + { + throw new Exception(veriFactuResponse.ErrValue!.ToString()); + } + var respuesta = veriFactuResponse.OkValue!; + if (respuesta.EstadoEnvio != EstadoEnvio.Correcto) + { + var line = respuesta.RespuestaLinea!.Where(x => x.IDFactura.NumSerieFactura == numSerieFactura).Single(); + throw new Exception($"{respuesta.EstadoEnvio}({line.CodigoErrorRegistro}): {line.DescripcionErrorRegistro}"); + } + + request.ReceiptResponse.AddSignatureItem(new SignatureItem + { + Caption = "Huella", + Data = huella, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = (long) SignatureTypesES.Huella + }); + request.ReceiptResponse.AddSignatureItem(new SignatureItem + { + Caption = $"IDEmisorFactura{(request.ReceiptRequest.IsVoid() ? "Anulada" : null)}", + Data = idEmisorFactura, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = (long) SignatureTypesES.IDEmisorFactura + }); + + + if (signatureItem is not null) + { + request.ReceiptResponse.AddSignatureItem(signatureItem); + } + + request.ReceiptResponse.AddSignatureItem(SignaturItemFactory.CreateESSignature(Encoding.UTF8.GetBytes(XmlHelpers.Serialize(signature)))); + + return request.ReceiptResponse; + } + + public Task GetInfoAsync() => throw new NotImplementedException(); +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Booleano.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Booleano.cs new file mode 100644 index 000000000..dae71462d --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Booleano.cs @@ -0,0 +1,10 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum Booleano +{ + S, + N, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Cabecera.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Cabecera.cs new file mode 100644 index 000000000..6da2d94c7 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Cabecera.cs @@ -0,0 +1,25 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public partial class Cabecera +{ + [XmlElement(Order = 0)] + public required PersonaFisicaJuridicaES ObligadoEmision { get; set; } + + [XmlElement(Order = 1)] + public PersonaFisicaJuridicaES? Representante { get; set; } + [XmlIgnore] + public bool RepresentanteSpecified => Representante is not null; + + [XmlElement(Order = 2)] + public RemisionVoluntaria? RemisionVoluntaria { get; set; } + [XmlIgnore] + public bool RemisionVoluntariaSpecified => RemisionVoluntaria is not null; + + [XmlElement(Order = 3)] + public RemisionRequerimiento? RemisionRequerimiento { get; set; } + [XmlIgnore] + public bool RemisionRequerimientoSpecified => RemisionRequerimiento is not null; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/CalificacionOperacion.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/CalificacionOperacion.cs new file mode 100644 index 000000000..30fd5125d --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/CalificacionOperacion.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum CalificacionOperacion +{ + S1, + S2, + N1, + N2, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/ClaveTipoFactura.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/ClaveTipoFactura.cs new file mode 100644 index 000000000..f0384ebe1 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/ClaveTipoFactura.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum ClaveTipoFactura +{ + F1, + F2, + R1, + R2, + R3, + R4, + R5, + F3, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/ClaveTipoRectificativa.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/ClaveTipoRectificativa.cs new file mode 100644 index 000000000..77671673a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/ClaveTipoRectificativa.cs @@ -0,0 +1,10 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum ClaveTipoRectificativa +{ + S, + I, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Country.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Country.cs new file mode 100644 index 000000000..f27c19600 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Country.cs @@ -0,0 +1,253 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum Country +{ + AF, + AL, + DE, + AD, + AO, + AI, + AQ, + AG, + SA, + DZ, + AR, + AM, + AW, + AU, + AT, + AZ, + BS, + BH, + BD, + BB, + BE, + BZ, + BJ, + BM, + BY, + BO, + BA, + BW, + BV, + BR, + BN, + BG, + BF, + BI, + BT, + CV, + KY, + KH, + CM, + CA, + CF, + CC, + CO, + KM, + CG, + CD, + CK, + KP, + KR, + CI, + CR, + HR, + CU, + TD, + CZ, + CL, + CN, + CY, + CW, + DK, + DM, + DO, + EC, + EG, + AE, + ER, + SK, + SI, + ES, + US, + EE, + ET, + FO, + PH, + FI, + FJ, + FR, + GA, + GM, + GE, + GS, + GH, + GI, + GD, + GR, + GL, + GU, + GT, + GG, + GN, + GQ, + GW, + GY, + HT, + HM, + HN, + HK, + HU, + IN, + ID, + IR, + IQ, + IE, + IM, + IS, + IL, + IT, + JM, + JP, + JE, + JO, + KZ, + KE, + KG, + KI, + KW, + LA, + LS, + LV, + LB, + LR, + LY, + LI, + LT, + LU, + XG, + MO, + MK, + MG, + MY, + MW, + MV, + ML, + MT, + FK, + MP, + MA, + MH, + MU, + MR, + YT, + UM, + MX, + FM, + MD, + MC, + MN, + ME, + MS, + MZ, + MM, + NA, + NR, + CX, + NP, + NI, + NE, + NG, + NU, + NF, + NO, + NC, + NZ, + IO, + OM, + NL, + BQ, + PK, + PW, + PA, + PG, + PY, + PE, + PN, + PF, + PL, + PT, + PR, + QA, + GB, + RW, + RO, + RU, + SB, + SV, + WS, + AS, + KN, + SM, + SX, + PM, + VC, + SH, + LC, + ST, + SN, + RS, + SC, + SL, + SG, + SY, + SO, + LK, + SZ, + ZA, + SD, + SS, + SE, + CH, + SR, + TH, + TW, + TZ, + TJ, + PS, + TF, + TL, + TG, + TK, + TO, + TT, + TN, + TC, + TM, + TR, + TV, + UA, + UG, + UY, + UZ, + VU, + VA, + VE, + VN, + VG, + VI, + WF, + YE, + DJ, + ZM, + ZW, + QU, + XB, + XU, + XN, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/DatosPresentacion.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/DatosPresentacion.cs new file mode 100644 index 000000000..d9078c2d1 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/DatosPresentacion.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class DatosPresentacion +{ + [XmlElement(Order = 0)] + public required string NIFPresentador { get; set; } + + [XmlElement(Order = 1)] + public required DateTime TimestampPresentacion { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/DesgloseRectificacion.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/DesgloseRectificacion.cs new file mode 100644 index 000000000..dd40d7e97 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/DesgloseRectificacion.cs @@ -0,0 +1,18 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class DesgloseRectificacion +{ + [XmlElement(Order = 0)] + public required string BaseRectificada { get; set; } + + [XmlElement(Order = 1)] + public required string CuotaRectificada { get; set; } + + [XmlElement(Order = 2)] + public string? CuotaRecargoRectificado { get; set; } + [XmlIgnore] + public bool CuotaRecargoRectificadoSpecified => CuotaRecargoRectificado is not null; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Detalle.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Detalle.cs new file mode 100644 index 000000000..d51ccd21e --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Detalle.cs @@ -0,0 +1,49 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class Detalle +{ + [XmlElement(Order = 0)] + public Impuesto? Impuesto { get; set; } + [XmlIgnore] + public bool ImpuestoSpecified => Impuesto is not null; + + [XmlElement(Order = 1)] + public IdOperacionesTrascendenciaTributaria? ClaveRegimen { get; set; } + [XmlIgnore] + public bool ClaveRegimenSpecified => ClaveRegimen is not null; + + [XmlElement("CalificacionOperacion", typeof(CalificacionOperacion), Order = 2)] + [XmlElement("OperacionExenta", typeof(OperacionExenta), Order = 2)] + public required object Item { get; set; } + + [XmlElement(Order = 3)] + public string? TipoImpositivo { get; set; } + [XmlIgnore] + public bool TipoImpositivoSpecified => TipoImpositivo is not null; + + [XmlElement(Order = 4)] + public required string BaseImponibleOimporteNoSujeto { get; set; } + + [XmlElement(Order = 5)] + public string? BaseImponibleACoste { get; set; } + [XmlIgnore] + public bool BaseImponibleACosteSpecified => BaseImponibleACoste is not null; + + [XmlElement(Order = 6)] + public string? CuotaRepercutida { get; set; } + [XmlIgnore] + public bool CuotaRepercutidaSpecified => CuotaRepercutida is not null; + + [XmlElement(Order = 7)] + public string? TipoRecargoEquivalencia { get; set; } + [XmlIgnore] + public bool TipoRecargoEquivalenciaSpecified => TipoRecargoEquivalencia is not null; + + [XmlElement(Order = 8)] + public string? CuotaRecargoEquivalencia { get; set; } + [XmlIgnore] + public bool CuotaRecargoEquivalenciaSpecified => CuotaRecargoEquivalencia is not null; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EncadenamientoFacturaAnterior.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EncadenamientoFacturaAnterior.cs new file mode 100644 index 000000000..6bc59833f --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EncadenamientoFacturaAnterior.cs @@ -0,0 +1,19 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class EncadenamientoFacturaAnterior +{ + [XmlElement(Order = 0)] + public required string IDEmisorFactura { get; set; } + + [XmlElement(Order = 1)] + public required string NumSerieFactura { get; set; } + + [XmlElement(Order = 2)] + public required string FechaExpedicionFactura { get; set; } + + [XmlElement(Order = 3)] + public required string Huella { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EstadoEnvio.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EstadoEnvio.cs new file mode 100644 index 000000000..ab551cd3b --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EstadoEnvio.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaSuministro.xsd")] +public enum EstadoEnvio +{ + Correcto, + ParcialmenteCorrecto, + Incorrecto, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EstadoRegistro.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EstadoRegistro.cs new file mode 100644 index 000000000..031f03479 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EstadoRegistro.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaSuministro.xsd")] +public enum EstadoRegistro +{ + Correcto, + AceptadoConErrores, + Incorrecto, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EstadoRegistroSF.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EstadoRegistroSF.cs new file mode 100644 index 000000000..3eecc3b1b --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/EstadoRegistroSF.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum EstadoRegistroSF +{ + Correcta, + AceptadaConErrores, + Anulada, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/GeneradoPor.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/GeneradoPor.cs new file mode 100644 index 000000000..b8e0549ea --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/GeneradoPor.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum GeneradoPor +{ + E, + D, + T, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IDFactura.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IDFactura.cs new file mode 100644 index 000000000..8fcef0968 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IDFactura.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public partial class IDFactura +{ + [XmlElement(Order = 0)] + public required string IDEmisorFactura { get; set; } + + [XmlElement(Order = 1)] + public required string NumSerieFactura { get; set; } + + [XmlElement(Order = 2)] + public required string FechaExpedicionFactura { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IDFacturaExpedidaBaja.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IDFacturaExpedidaBaja.cs new file mode 100644 index 000000000..4f6375e40 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IDFacturaExpedidaBaja.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class IDFacturaExpedidaBaja +{ + [XmlElement(Order = 0)] + public required string IDEmisorFacturaAnulada { get; set; } + + [XmlElement(Order = 1)] + public required string NumSerieFacturaAnulada { get; set; } + + [XmlElement(Order = 2)] + public required string FechaExpedicionFacturaAnulada { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IDOtro.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IDOtro.cs new file mode 100644 index 000000000..7b0a6df12 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IDOtro.cs @@ -0,0 +1,18 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class IDOtro +{ + [XmlElement(Order = 0)] + public Country? CodigoPais { get; set; } + [XmlIgnore] + public bool CodigoPaisSpecified => CodigoPais is not null; + + [XmlElement(Order = 1)] + public required PersonaFisicaJuridicaIDType IDType { get; set; } + + [XmlElement(Order = 2)] + public required string ID { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IdOperacionesTrascendenciaTributaria.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IdOperacionesTrascendenciaTributaria.cs new file mode 100644 index 000000000..169d3573c --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/IdOperacionesTrascendenciaTributaria.cs @@ -0,0 +1,58 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum IdOperacionesTrascendenciaTributaria +{ + [XmlEnum("01")] + Item01, + + [XmlEnum("02")] + Item02, + + [XmlEnum("03")] + Item03, + + [XmlEnum("04")] + Item04, + + [XmlEnum("05")] + Item05, + + [XmlEnum("06")] + Item06, + + [XmlEnum("07")] + Item07, + + [XmlEnum("08")] + Item08, + + [XmlEnum("09")] + Item09, + + [XmlEnum("10")] + Item10, + + [XmlEnum("11")] + Item11, + + [XmlEnum("14")] + Item14, + + [XmlEnum("15")] + Item15, + + [XmlEnum("17")] + Item17, + + [XmlEnum("18")] + Item18, + + [XmlEnum("19")] + Item19, + + [XmlEnum("20")] + Item20, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Impuesto.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Impuesto.cs new file mode 100644 index 000000000..35651a678 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Impuesto.cs @@ -0,0 +1,19 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum Impuesto +{ + [XmlEnum("01")] + Item01, + + [XmlEnum("02")] + Item02, + + [XmlEnum("03")] + Item03, + + [XmlEnum("05")] + Item05, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Operacion.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Operacion.cs new file mode 100644 index 000000000..2c9e03ac3 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Operacion.cs @@ -0,0 +1,25 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class Operacion +{ + [XmlElement(Order = 0)] + public required TipoOperacion TipoOperacion { get; set; } + + [XmlElement(Order = 1)] + public Booleano? Subsanacion { get; set; } + [XmlIgnore] + public bool SubsanacionSpecified => Subsanacion is not null; + + [XmlElement(Order = 2)] + public RechazoPrevio? RechazoPrevio { get; set; } + [XmlIgnore] + public bool RechazoPrevioSpecified => RechazoPrevio is not null; + + [XmlElement(Order = 3)] + public Booleano? SinRegistroPrevio { get; set; } + [XmlIgnore] + public bool SinRegistroPrevioSpecified => SinRegistroPrevio is not null; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/OperacionExenta.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/OperacionExenta.cs new file mode 100644 index 000000000..42094020d --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/OperacionExenta.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum OperacionExenta +{ + E1, + E2, + E3, + E4, + E5, + E6, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PersonaFisicaJuridica.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PersonaFisicaJuridica.cs new file mode 100644 index 000000000..117e895aa --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PersonaFisicaJuridica.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class PersonaFisicaJuridica +{ + [XmlElement(Order = 0)] + public required string NombreRazon { get; set; } + + [XmlElement("IDOtro", typeof(IDOtro), Order = 1)] + [XmlElement("NIF", typeof(string), Order = 1)] + public required object Item { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PersonaFisicaJuridicaES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PersonaFisicaJuridicaES.cs new file mode 100644 index 000000000..051252e7a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PersonaFisicaJuridicaES.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class PersonaFisicaJuridicaES +{ + [XmlElement(Order = 0)] + public required string NombreRazon { get; set; } + + [XmlElement(Order = 1)] + public required string NIF { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PersonaFisicaJuridicaIDType.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PersonaFisicaJuridicaIDType.cs new file mode 100644 index 000000000..3a2a633ba --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PersonaFisicaJuridicaIDType.cs @@ -0,0 +1,25 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum PersonaFisicaJuridicaIDType +{ + [XmlEnum("02")] + Item02, + + [XmlEnum("03")] + Item03, + + [XmlEnum("04")] + Item04, + + [XmlEnum("05")] + Item05, + + [XmlEnum("06")] + Item06, + + [XmlEnum("07")] + Item07, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PrimerRegistroCadena.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PrimerRegistroCadena.cs new file mode 100644 index 000000000..94f368b4b --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/PrimerRegistroCadena.cs @@ -0,0 +1,9 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum PrimerRegistroCadena +{ + S, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RechazoPrevio.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RechazoPrevio.cs new file mode 100644 index 000000000..72150b9e0 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RechazoPrevio.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum RechazoPrevio +{ + N, + S, + X, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegFactuSistemsFacturacion.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegFactuSistemsFacturacion.cs new file mode 100644 index 000000000..2829bfeb0 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegFactuSistemsFacturacion.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(AnonymousType = true, Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd")] +public class RegFactuSistemaFacturacion +{ + [XmlElement(Order = 0)] + public required Cabecera Cabecera { get; set; } + + [XmlElement("RegistroFactura", Order = 1)] + public required RegistroFactura[] RegistroFactura { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroDuplicado.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroDuplicado.cs new file mode 100644 index 000000000..1c12e6d63 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroDuplicado.cs @@ -0,0 +1,23 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class RegistroDuplicado +{ + [XmlElement(Order = 0)] + public required string IdPeticionRegistroDuplicado { get; set; } + + [XmlElement(Order = 1)] + public required EstadoRegistroSF EstadoRegistroDuplicado { get; set; } + + [XmlElement(DataType = "integer", Order = 2)] + public string? CodigoErrorRegistro { get; set; } + [XmlIgnore] + public bool CodigoErrorRegistroSpecified => CodigoErrorRegistro is not null; + + [XmlElement(Order = 3)] + public string? DescripcionErrorRegistro { get; set; } + [XmlIgnore] + public bool DescripcionErrorRegistroSpecified => DescripcionErrorRegistro is not null; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFactura.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFactura.cs new file mode 100644 index 000000000..24eec5999 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFactura.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd")] +public class RegistroFactura +{ + /// + [XmlElement("RegistroAlta", typeof(RegistroFacturacionAlta), Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd", Order = 0)] + [XmlElement("RegistroAnulacion", typeof(RegistroFacturacionAnulacion), Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd", Order = 0)] + public required object Item { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAlta.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAlta.cs new file mode 100644 index 000000000..df1fb4313 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAlta.cs @@ -0,0 +1,141 @@ +using System.Xml; +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class RegistroFacturacionAlta +{ + [XmlElement(Order = 0)] + public required Version IDVersion { get; set; } + + [XmlElement(Order = 1)] + public required IDFactura IDFactura { get; set; } + + [XmlElement(Order = 2)] + public string? RefExterna { get; set; } + [XmlIgnore] + public bool RefExternaSpecified => RefExterna is not null; + + [XmlElement(Order = 3)] + public required string NombreRazonEmisor { get; set; } + + [XmlElement(Order = 4)] + public Booleano? Subsanacion { get; set; } + [XmlIgnore] + public bool SubsanacionSpecified => Subsanacion is not null; + + [XmlElement(Order = 5)] + public RechazoPrevio? RechazoPrevio { get; set; } + [XmlIgnore] + public bool RechazoPrevioSpecified => RechazoPrevio is not null; + + [XmlElement(Order = 6)] + public required ClaveTipoFactura TipoFactura { get; set; } + + [XmlElement(Order = 7)] + public ClaveTipoRectificativa? TipoRectificativa { get; set; } + [XmlIgnore] + public bool TipoRectificativaSpecified => TipoRectificativa is not null; + + [XmlArray(Order = 8)] + [XmlArrayItem("IDFacturaRectificada", IsNullable = false)] + public IDFactura[]? FacturasRectificadas { get; set; } + + [XmlArray(Order = 9)] + [XmlArrayItem("IDFacturaSustituida", IsNullable = false)] + public IDFactura[]? FacturasSustituidas { get; set; } + + [XmlElement(Order = 10)] + public DesgloseRectificacion? ImporteRectificacion { get; set; } + [XmlIgnore] + public bool ImporteRectificacionSpecified => ImporteRectificacion is not null; + + [XmlElement(Order = 11)] + public string? FechaOperacion { get; set; } + [XmlIgnore] + public bool FechaOperacionSpecified => FechaOperacion is not null; + + [XmlElement(Order = 12)] + public required string DescripcionOperacion { get; set; } + + [XmlElement(Order = 13)] + public Booleano? FacturaSimplificadaArt7273 { get; set; } + [XmlIgnore] + public bool FacturaSimplificadaArt7273Specified => FacturaSimplificadaArt7273 is not null; + + [XmlElement(Order = 14)] + public Booleano? FacturaSinIdentifDestinatarioArt61d { get; set; } + [XmlIgnore] + public bool FacturaSinIdentifDestinatarioArt61dSpecified => FacturaSinIdentifDestinatarioArt61d is not null; + + [XmlElement(Order = 15)] + public Booleano? Macrodato { get; set; } + [XmlIgnore] + public bool MacrodatoSpecified => Macrodato is not null; + + [XmlElement(Order = 16)] + public TercerosODestinatario? EmitidaPorTerceroODestinatario { get; set; } + [XmlIgnore] + public bool EmitidaPorTerceroODestinatarioSpecified => EmitidaPorTerceroODestinatario is not null; + + [XmlElement(Order = 17)] + public PersonaFisicaJuridica? Tercero { get; set; } + [XmlIgnore] + public bool TerceroSpecified => Tercero is not null; + + [XmlArray(Order = 18)] + [XmlArrayItem("IDDestinatario", IsNullable = false)] + public PersonaFisicaJuridica[]? Destinatarios { get; set; } + + [XmlElement(Order = 19)] + public Booleano? Cupon { get; set; } + [XmlIgnore] + public bool CuponSpecified => Cupon is not null; + + [XmlArray(Order = 20)] + [XmlArrayItem("DetalleDesglose", IsNullable = false)] + public required Detalle[] Desglose { get; set; } + + [XmlElement(Order = 21)] + public required string CuotaTotal { get; set; } + + [XmlElement(Order = 22)] + public required string ImporteTotal { get; set; } + + [XmlElement(Order = 23)] + public required RegistroFacturacionAltaEncadenamiento Encadenamiento { get; set; } + + [XmlElement(Order = 24)] + public required SistemaInformatico SistemaInformatico { get; set; } + [XmlIgnore] + public required DateTimeOffset FechaHoraHusoGenRegistro { get; set; } + + [XmlElement("FechaHoraHusoGenRegistro", Order = 25)] + public string FechaHoraHusoGenRegistroString + { + get => FechaHoraHusoGenRegistro.ToString("yyyy-MM-ddTHH:mm:sszzz"); + set => FechaHoraHusoGenRegistro = DateTimeOffset.Parse(value); + } + + [XmlElement(Order = 26)] + public string? NumRegistroAcuerdoFacturacion { get; set; } + [XmlIgnore] + public bool NumRegistroAcuerdoFacturacionSpecified => NumRegistroAcuerdoFacturacion is not null; + + [XmlElement(Order = 27)] + public string? IdAcuerdoSistemaInformatico { get; set; } + [XmlIgnore] + public bool IdAcuerdoSistemaInformaticoSpecified => IdAcuerdoSistemaInformatico is not null; + + [XmlElement(Order = 28)] + public required TipoHuella TipoHuella { get; set; } + + [XmlElement(Order = 29)] + public required string Huella { get; set; } + + [XmlElement(Namespace = "http://www.w3.org/2000/09/xmldsig#", Order = 30)] + public XmlElement? Signature { get; set; } + [XmlIgnore] + public bool SignatureSpecified => Signature is not null; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAltaEncadenamiento.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAltaEncadenamiento.cs new file mode 100644 index 000000000..41b8a4613 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAltaEncadenamiento.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(AnonymousType = true, Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class RegistroFacturacionAltaEncadenamiento +{ + [XmlElement("PrimerRegistro", typeof(PrimerRegistroCadena), Order = 0)] + [XmlElement("RegistroAnterior", typeof(EncadenamientoFacturaAnterior), Order = 0)] + public required object Item { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAnulacion.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAnulacion.cs new file mode 100644 index 000000000..654e77e9c --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAnulacion.cs @@ -0,0 +1,67 @@ +using System.Xml; +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class RegistroFacturacionAnulacion +{ + + [XmlElement(Order = 0)] + public required Version IDVersion { get; set; } + + [XmlElement(Order = 1)] + public required IDFacturaExpedidaBaja IDFactura { get; set; } + + [XmlElement(Order = 2)] + public string? RefExterna { get; set; } + [XmlIgnore] + public bool RefExternaSpecified => RefExterna is not null; + + [XmlElement(Order = 3)] + public Booleano? SinRegistroPrevio { get; set; } + [XmlIgnore] + public bool SinRegistroPrevioSpecified => SinRegistroPrevio is not null; + + [XmlElement(Order = 4)] + public Booleano? RechazoPrevio { get; set; } + [XmlIgnore] + public bool RechazoPrevioSpecified => RechazoPrevio is not null; + + [XmlElement(Order = 5)] + public GeneradoPor? GeneradoPor { get; set; } + [XmlIgnore] + public bool GeneradoPorSpecified => GeneradoPor is not null; + + [XmlElement(Order = 6)] + public PersonaFisicaJuridica? Generador { get; set; } + [XmlIgnore] + public bool GeneradorSpecified => Generador is not null; + + [XmlElement(Order = 7)] + public required RegistroFacturacionAnulacionEncadenamiento Encadenamiento { get; set; } + + [XmlElement(Order = 8)] + public required SistemaInformatico SistemaInformatico { get; set; } + + [XmlIgnore] + public required DateTimeOffset FechaHoraHusoGenRegistro { get; set; } + + [XmlElement("FechaHoraHusoGenRegistro", Order = 9)] + public string FechaHoraHusoGenRegistroString + { + get => FechaHoraHusoGenRegistro.ToString("yyyy-MM-ddTHH:mm:sszzz"); + set => FechaHoraHusoGenRegistro = DateTimeOffset.Parse(value); + } + + [XmlElement(Order = 10)] + public required TipoHuella TipoHuella { get; set; } + + [XmlElement(Order = 11)] + public required string Huella { get; set; } + + [XmlElement(Namespace = "http://www.w3.org/2000/09/xmldsig#", Order = 12)] + public XmlElement? Signature { get; set; } + [XmlIgnore] + public bool SignatureSpecified => Signature is not null; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAnulacionEncadenamiento.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAnulacionEncadenamiento.cs new file mode 100644 index 000000000..5a9f1df5e --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RegistroFacturacionAnulacionEncadenamiento.cs @@ -0,0 +1,11 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(AnonymousType = true, Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class RegistroFacturacionAnulacionEncadenamiento +{ + [XmlElement("PrimerRegistro", typeof(PrimerRegistroCadena), Order = 0)] + [XmlElement("RegistroAnterior", typeof(EncadenamientoFacturaAnterior), Order = 0)] + public required object Item { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RemisionRequerimiento.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RemisionRequerimiento.cs new file mode 100644 index 000000000..55bb76131 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RemisionRequerimiento.cs @@ -0,0 +1,15 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(AnonymousType = true, Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class RemisionRequerimiento +{ + [XmlElement(Order = 0)] + public required string RefRequerimiento { get; set; } + + [XmlElement(Order = 1)] + public Booleano? FinRequerimiento { get; set; } + [XmlIgnore] + public bool FinRequerimientoSpecified => FinRequerimiento is not null; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RemisionVoluntaria.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RemisionVoluntaria.cs new file mode 100644 index 000000000..9624695f9 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RemisionVoluntaria.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(AnonymousType = true, Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class RemisionVoluntaria +{ + [XmlElement(Order = 0)] + public string? FechaFinVeriFactu { get; set; } + [XmlIgnore] + public bool FechaFinVeriFactuSpecified => FechaFinVeriFactu is not null; + + [XmlElement(Order = 1)] + public Booleano? Incidencia { get; set; } + [XmlIgnore] + public bool IncidenciaSpecified => Incidencia is not null; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RespuestaBase.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RespuestaBase.cs new file mode 100644 index 000000000..a03276870 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RespuestaBase.cs @@ -0,0 +1,22 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaSuministro.xsd")] +public class RespuestaBase +{ + [XmlElement(Order = 0)] + public string? CSV { get; set; } + [XmlIgnore] + public bool CSVSpecified => CSV is not null; + [XmlElement(Order = 1)] + public DatosPresentacion? DatosPresentacion { get; set; } + [XmlIgnore] + public bool DatosPresentacionSpecified => DatosPresentacion is not null; + [XmlElement(Order = 2)] + public required Cabecera Cabecera { get; set; } + [XmlElement(Order = 3)] + public required string TiempoEsperaEnvio { get; set; } + [XmlElement(Order = 4)] + public required EstadoEnvio EstadoEnvio { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RespuestaExpedida.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RespuestaExpedida.cs new file mode 100644 index 000000000..efc8aeff0 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RespuestaExpedida.cs @@ -0,0 +1,30 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaSuministro.xsd")] +public class RespuestaExpedida +{ + [XmlElement(Order = 0)] + public required IDFactura IDFactura { get; set; } + [XmlElement(Order = 1)] + public required Operacion Operacion { get; set; } + [XmlElement(Order = 2)] + public string? RefExterna { get; set; } + [XmlIgnore] + public bool RefExternaSpecified => RefExterna is not null; + [XmlElement(Order = 3)] + public required EstadoRegistro EstadoRegistro { get; set; } + [XmlElement(DataType = "integer", Order = 4)] + public string? CodigoErrorRegistro { get; set; } + [XmlIgnore] + public bool CodigoErrorRegistroSpecified => CodigoErrorRegistro is not null; + [XmlElement(Order = 5)] + public string? DescripcionErrorRegistro { get; set; } + [XmlIgnore] + public bool DescripcionErrorRegistroSpecified => DescripcionErrorRegistro is not null; + [XmlElement(Order = 6)] + public RegistroDuplicado? RegistroDuplicado { get; set; } + [XmlIgnore] + public bool RegistroDuplicadoSpecified => RegistroDuplicado is not null; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RespuestaRegFactuSistemaFacturacion.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RespuestaRegFactuSistemaFacturacion.cs new file mode 100644 index 000000000..7b1132a5a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/RespuestaRegFactuSistemaFacturacion.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaSuministro.xsd")] +public class RespuestaRegFactuSistemaFacturacion : RespuestaBase +{ + [XmlElement("RespuestaLinea", Order = 0)] + public RespuestaExpedida[]? RespuestaLinea { get; set; } + [XmlIgnore] + public bool RespuestaLineaSpecified => RespuestaLinea is not null; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/RespuestaSuministro.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/RespuestaSuministro.xsd new file mode 100644 index 000000000..30cc95c00 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/RespuestaSuministro.xsd @@ -0,0 +1,139 @@ + + + + + + + + + + + + CSV asociado al envío generado por AEAT. Solo se genera si no hay rechazo del envio + + + + + Se devuelven datos de la presentacion realizada. Solo se genera si no hay rechazo del envio + + + + + Se devuelve la cabecera que se incluyó en el envío. + + + + + + + Estado del envío en conjunto. + Si los datos de cabecera y todos los registros son correctos,el estado es correcto. + En caso de estructura y cabecera correctos donde todos los registros son incorrectos, el estado es incorrecto + En caso de estructura y cabecera correctos con al menos un registro incorrecto, el estado global es parcialmente correcto. + + + + + + + + Respuesta a un envío de registro de facturacion + + + + + + + + Estado detallado de cada línea del suministro. + + + + + + + + + + Respuesta a un envío + + + + + ID Factura Expedida + + + + + + + + Estado del registro. Correcto o Incorrecto + + + + + + + Código del error de registro, en su caso. + + + + + + + Descripción detallada del error de registro, en su caso. + + + + + + + Solo en el caso de que se rechace el registro por duplicado se devuelve este nodo con la informacion registrada en el sistema para este registro + + + + + + + + + + Correcto + + + + + Parcialmente correcto. Ver detalle de errores + + + + + Incorrecto + + + + + + + + + Correcto + + + + + Aceptado con Errores. Ver detalle del error + + + + + Incorrecto + + + + + + + + \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/SistemaFacturacion.wsdl b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/SistemaFacturacion.wsdl new file mode 100644 index 000000000..44c85b09c --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/SistemaFacturacion.wsdl @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/SuministroInformacion.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/SuministroInformacion.xsd new file mode 100644 index 000000000..ddfdd71e5 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/SuministroInformacion.xsd @@ -0,0 +1,1378 @@ + + + + + + Datos de cabecera + + + + + Obligado a expedir la factura. + + + + + Representante del obligado tributario. A rellenar solo en caso de que los registros de facturación remitdos hayan sido generados por un representante/asesor del obligado tributario. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Datos de identificación de factura expedida para operaciones de consulta + + + + + + Nº Serie+Nº Factura de la Factura del Emisor. + + + + + Fecha de emisión de la factura + + + + + + + Datos de identificación de factura que se anula para operaciones de baja + + + + + NIF del obligado + + + + + Nº Serie+Nº Factura de la Factura que se anula. + + + + + Fecha de emisión de la factura que se anula + + + + + + + Datos correspondientes al registro de facturacion de alta + + + + + + + + + + + Clave del tipo de factura + + + + + Identifica si el tipo de factura rectificativa es por sustitución o por diferencia + + + + + + El ID de las facturas rectificadas, únicamente se rellena en el caso de rectificación de facturas + + + + + + + + + + El ID de las facturas sustituidas, únicamente se rellena en el caso de facturas sustituidas + + + + + + + + + + + + + + + + Tercero que expida la factura y/o genera el registro de alta. + + + + + + Contraparte de la operación. Cliente + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Datos correspondientes al registro de facturacion de anulacion + + + + + + + + + + + + + + + + + + + + + + + + + + + Datos de encadenamiento + + + + + NIF del obligado a expedir la factura a que se refiere el registro de facturación anterior + + + + + + + + + + + + + + + + + + + + + + + + + + + + Datos de identificación de factura + + + + + NIF del obligado + + + + + Nº Serie+Nº Factura de la Factura del Emisor + + + + + Fecha de emisión de la factura + + + + + + + + Datos de identificación de factura sustituida o rectificada. El NIF se cogerá del NIF indicado en el bloque IDFactura + + + + + NIF del obligado + + + + + Nº Serie+Nº Factura de la factura + + + + + Fecha de emisión de la factura sustituida o rectificada + + + + + + + + + + + + + + + + + + + + + + + Desglose de Base y Cuota sustituida en las Facturas Rectificativas sustitutivas + + + + + + + + + + + Datos de una persona física o jurídica Española con un NIF asociado + + + + + + + + + + Datos de una persona física o jurídica Española o Extranjera + + + + + + + + + + + + + Identificador de persona Física o jurídica distinto del NIF + (Código pais, Tipo de Identificador, y hasta 15 caractéres) + No se permite CodigoPais=ES e IDType=01-NIFContraparte + para ese caso, debe utilizarse NIF en lugar de IDOtro. + + + + + + + + + + + Rango de fechas de expedicion + + + + + + + + + + + + + + + + + + IdPeticion asociado a la factura registrada previamente en el sistema. Solo se suministra si la factura enviada es rechazada por estar duplicada + + + + + + + Estado del registro duplicado almacenado en el sistema. Los estados posibles son: Correcta, AceptadaConErrores y Anulada. Solo se suministra si la factura enviada es rechazada por estar duplicada + + + + + + + Código del error de registro duplicado almacenado en el sistema, en su caso. + + + + + + + Descripción detallada del error de registro duplicado almacenado en el sistema, en su caso. + + + + + + + + + + + + + + + Año en formato YYYY + + + + + + + + + Período de la factura + + + + + Enero + + + + + Febrero + + + + + Marzo + + + + + Abril + + + + + Mayo + + + + + Junio + + + + + Julio + + + + + Agosto + + + + + Septiembre + + + + + Octubre + + + + + Noviembre + + + + + Diciembre + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NIF + + + + + + + + + + + + + + + + + + + + + + EXENTA por Art. 20 + + + + + EXENTA por Art. 21 + + + + + EXENTA por Art. 22 + + + + + EXENTA por Art. 24 + + + + + EXENTA por Art. 25 + + + + + EXENTA otros + + + + + + + + + + FACTURA (ART. 6, 7.2 Y 7.3 DEL RD 1619/2012) + + + + + FACTURA SIMPLIFICADA Y FACTURAS SIN IDENTIFICACIÓN DEL DESTINATARIO ART. 6.1.D) RD 1619/2012 + + + + + FACTURA RECTIFICATIVA (Art 80.1 y 80.2 y error fundado en derecho) + + + + + FACTURA RECTIFICATIVA (Art. 80.3) + + + + + FACTURA RECTIFICATIVA (Art. 80.4) + + + + + FACTURA RECTIFICATIVA (Resto) + + + + + FACTURA RECTIFICATIVA EN FACTURAS SIMPLIFICADAS + + + + + FACTURA EMITIDA EN SUSTITUCIÓN DE FACTURAS SIMPLIFICADAS FACTURADAS Y DECLARADAS + + + + + + + + + No ha habido rechazo previo por la AEAT. + + + + + Ha habido rechazo previo por la AEAT. + + + + + Independientemente de si ha habido o no algún rechazo previo por la AEAT, el registro de facturación no existe en la AEAT (registro existente en ese SIF o en algún SIF del obligado tributario y que no se remitió a la AEAT, por ejemplo, al acogerse a Veri*factu desde no Veri*factu). No deberían existir operaciones de alta (N,X), por lo que no se admiten. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SUSTITUTIVA + + + + + INCREMENTAL + + + + + + + + + + Destinatario + + + + + Tercero + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Expedidor (obligado a Expedir la factura anulada). + + + + + Destinatario + + + + + Tercero + + + + + + + + + + NIF-IVA + + + + + Pasaporte + + + + + IDEnPaisResidencia + + + + + Certificado Residencia + + + + + Otro documento Probatorio + + + + + No Censado + + + + + + + + + + SHA-256 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + El registro se ha almacenado sin errores + + + + + El registro que se ha almacenado tiene algunos errores. Ver detalle del error + + + + + El registro almacenado ha sido anulado + + + + + + + + + + + + OPERACIÓN SUJETA Y NO EXENTA - SIN INVERSIÓN DEL SUJETO PASIVO. + + + + + OPERACIÓN SUJETA Y NO EXENTA - CON INVERSIÓN DEL SUJETO PASIVO + + + + + OPERACIÓN NO SUJETA ARTÍCULO 7, 14, OTROS. + + + + + OPERACIÓN NO SUJETA POR REGLAS DE LOCALIZACIÓN + + + + + + + + + + + + + + + + + + + Datos de una persona física o jurídica Española o Extranjera + + + + + + + + + + + + Cabecera de la Cobnsulta + + + + + + + Obligado a la emision de los registros de facturacion + + + + + Destinatario (a veces también denominado contraparte, es decir, el cliente) de la operación + + + + + + Flag opcional que tendrá valor S si quien realiza la cosulta es el representante/asesor del obligado tributario. Permite, a quien realiza la cosulta, obtener los registros de facturación en los que figura como representante. Este flag solo se puede cumplimentar cuando esté informado el obligado tributario en la consulta + + + + + + + + Datos de una persona física o jurídica Española con un NIF asociado + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Impuesto sobre el Valor Añadido (IVA) + + + + + Impuesto sobre la Producción, los Servicios y la Importación (IPSI) de Ceuta y Melilla + + + + + Impuesto General Indirecto Canario (IGIC) + + + + + Otros + + + + + + + + + + + + + + + + + + La operación realizada ha sido un alta + + + + + La operación realizada ha sido una anulación + + + + + \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/SuministroLR.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/SuministroLR.xsd new file mode 100644 index 000000000..bde17b772 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Schema/SuministroLR.xsd @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + Datos correspondientes a los registros de facturacion + + + + + + + + + \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/SistemaInformatico.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/SistemaInformatico.cs new file mode 100644 index 000000000..53eb31aee --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/SistemaInformatico.cs @@ -0,0 +1,27 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public class SistemaInformatico +{ + [XmlElement(Order = 0)] + public required string NombreRazon { get; set; } + [XmlElement("IDOtro", typeof(IDOtro), Order = 1)] + [XmlElement("NIF", typeof(string), Order = 1)] + public required object Item { get; set; } + [XmlElement(Order = 2)] + public required string NombreSistemaInformatico { get; set; } + [XmlElement(Order = 3)] + public required string IdSistemaInformatico { get; set; } + [XmlElement(Order = 4)] + public required string Version { get; set; } + [XmlElement(Order = 5)] + public required string NumeroInstalacion { get; set; } + [XmlElement(Order = 6)] + public required Booleano TipoUsoPosibleSoloVerifactu { get; set; } + [XmlElement(Order = 7)] + public required Booleano TipoUsoPosibleMultiOT { get; set; } + [XmlElement(Order = 8)] + public required Booleano IndicadorMultiplesOT { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/TercerosODestinatario.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/TercerosODestinatario.cs new file mode 100644 index 000000000..6ebda8fe6 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/TercerosODestinatario.cs @@ -0,0 +1,10 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum TercerosODestinatario +{ + D, + T, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/TipoHuella.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/TipoHuella.cs new file mode 100644 index 000000000..e2b0e8da7 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/TipoHuella.cs @@ -0,0 +1,10 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum TipoHuella +{ + [XmlEnum("01")] + Item01, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/TipoOperacion.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/TipoOperacion.cs new file mode 100644 index 000000000..34959fcad --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/TipoOperacion.cs @@ -0,0 +1,12 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum TipoOperacion +{ + Alta, + Anulacion, + [XmlEnum("")] + Null +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Version.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Version.cs new file mode 100644 index 000000000..6fbaaf5fc --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Models/Version.cs @@ -0,0 +1,11 @@ + +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Models; + +[XmlType(Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd")] +public enum Version +{ + [XmlEnum("1.0")] + Item10, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Client.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Client.cs new file mode 100644 index 000000000..3c85f9943 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Client.cs @@ -0,0 +1,82 @@ +using System.Diagnostics; +using System.Runtime.InteropServices.Marshalling; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Xml; +using System.Xml.Linq; +using System.Xml.Serialization; +using fiskaltrust.Middleware.Localization.QueueES.Exports; +using fiskaltrust.Middleware.SCU.ES.Helpers; +using fiskaltrust.Middleware.SCU.ES.Models; +using Org.BouncyCastle.Bcpg; + +namespace fiskaltrust.Middleware.SCU.ES.Soap; + +public record Error +{ + public record Http(string Value) : Error(); + public record Soap(string Value) : Error(); + public record Xml(Exception Ex, string Value) : Error(); + +#pragma warning disable CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). + public override string ToString() => this switch + { + Http http => $"HTTP Error: {http.Value}", + Soap soap => $"SOAP Error: {soap.Value}", + Xml xml => $"XML Error: {xml.Ex.Message}\n{xml.Value}", + }; +#pragma warning restore CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). +} + +public class Client +{ + private HttpClient _httpClient { get; } + + public Client(Uri uri, X509Certificate2 certificate) + { + var requestHandler = new HttpClientHandler(); + requestHandler.ClientCertificates.Add(certificate); + _httpClient = new HttpClient(requestHandler) + { + BaseAddress = uri, + }; + _httpClient.DefaultRequestHeaders.Add("AcceptCharset", "utf-8"); + } + + public async Task> SendAsync(Envelope envelope) + { + var requestString = envelope.XmlSerialize(); + var response = await _httpClient.PostAsync("/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP", new StringContent(requestString, Encoding.UTF8, "application/soap+xml")); + + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + var serializer = new XmlSerializer(typeof(Envelope)); + var contentStream = await response.Content.ReadAsStreamAsync(); + + Envelope content; + try + { + content = (Envelope) serializer.Deserialize(contentStream)!; + } + catch (Exception ex) + { + using var reader = new StreamReader(contentStream); + return new Error.Xml(ex, await reader.ReadToEndAsync()); + } + + if (!response.IsSuccessStatusCode) + { + return new Error.Http(content.XmlSerialize()); + } + + if (content.Body.Content is Fault fault) + { + return new Error.Soap($"{fault.FaultCode}{(fault.Detail.ErrorCode.HasValue ? $"({fault.Detail.ErrorCode.Value})" : "")}: {fault.FaultString}"); + } + + if (content.Body.Content is RespuestaRegFactuSistemaFacturacion repusta) + { + return repusta; + } + throw new UnreachableException(); + } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/Envelope.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/Envelope.cs new file mode 100644 index 000000000..4b26b580d --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/Envelope.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; +using fiskaltrust.Middleware.SCU.ES.Helpers; + +namespace fiskaltrust.Middleware.SCU.ES.Soap; + +[Serializable] +[XmlRoot("Envelope", Namespace = NAMESPACE)] +public class Envelope +{ + public const string NAMESPACE = "http://schemas.xmlsoap.org/soap/envelope/"; + + [XmlElement(Order = 1)] + public Header Header { get; set; } = new Header(); + + [XmlElement(Order = 2)] + public required T Body { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/Header.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/Header.cs new file mode 100644 index 000000000..5e2e484c9 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/Header.cs @@ -0,0 +1,7 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.SCU.ES.Soap; + +[Serializable] +[XmlRoot("Header")] +public class Header { } \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/RequestBody.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/RequestBody.cs new file mode 100644 index 000000000..09c3386bc --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/RequestBody.cs @@ -0,0 +1,14 @@ +using System.Xml.Serialization; +using fiskaltrust.Middleware.SCU.ES.Models; + +namespace fiskaltrust.Middleware.SCU.ES.Soap; + +[Serializable] +[XmlRoot("Body")] +public class RequestBody +{ + public const string NAMESPACE = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd"; + + [XmlElement(Namespace = NAMESPACE)] + public required RegFactuSistemaFacturacion RegFactuSistemaFacturacion { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/ResponseBody.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/ResponseBody.cs new file mode 100644 index 000000000..bfaeb340e --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/Soap/Models/ResponseBody.cs @@ -0,0 +1,40 @@ +using System.Xml.Linq; +using System.Xml.Serialization; +using fiskaltrust.Middleware.SCU.ES.Models; + +namespace fiskaltrust.Middleware.SCU.ES.Soap; + +[Serializable] +[XmlRoot("Fault")] +public class Fault +{ + [XmlElement("faultcode", Namespace = "")] + public required string FaultCode { get; set; } + + [XmlElement("faultstring", Namespace = "")] + public required string FaultString { get; set; } + + [XmlElement("detail", Namespace = "")] + public required DetailType Detail { get; set; } + + [Serializable] + [XmlRoot("detail", Namespace = "")] + public class DetailType + { + [XmlElement("errorcode", IsNullable = true)] + public required int? ErrorCode { get; set; } + + [XmlElement("callstack")] + public required string CallStack { get; set; } + // } + } +} + +[Serializable] +[XmlRoot("Body")] +public class ResponseBody +{ + [XmlElement("Fault", Type = typeof(Fault))] + [XmlElement("RespuestaRegFactuSistemaFacturacion", Type = typeof(RespuestaRegFactuSistemaFacturacion), Namespace = "https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/RespuestaSuministro.xsd")] + public required object Content { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/VeriFactuMapping.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/VeriFactuMapping.cs new file mode 100644 index 000000000..8cff2beb6 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Exports/VeriFactu/VeriFactuMapping.cs @@ -0,0 +1,346 @@ +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Web; +using System.Xml; +using System.Xml.Serialization; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Contracts.Repositories; +using fiskaltrust.Middleware.Localization.QueueES.Helpers; +using fiskaltrust.Middleware.Localization.QueueES.Interface; +using fiskaltrust.Middleware.Localization.v2.Helpers; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.SCU.ES.Helpers; +using fiskaltrust.Middleware.SCU.ES.Models; +using fiskaltrust.storage.V0; +using fiskaltrust.storage.V0.MasterData; +using Microsoft.VisualBasic; +using Version = fiskaltrust.Middleware.SCU.ES.Models.Version; + +namespace fiskaltrust.Middleware.Localization.QueueES.Exports; + +public class VeriFactuMapping +{ + private readonly MasterDataConfiguration _masterData; + private readonly IMiddlewareQueueItemRepository _queueItemRepository; + private readonly X509Certificate2? _certificate; + + public VeriFactuMapping(MasterDataConfiguration masterData, IMiddlewareQueueItemRepository queueItemRepository, X509Certificate2? certificate = null) + { + _masterData = masterData; + _queueItemRepository = queueItemRepository; + _certificate = certificate; + } + + public RegFactuSistemaFacturacion CreateRegFactuSistemaFacturacion(RegistroFacturacionAnulacion registroFacturacionAnulacion) => CreateRegFactuSistemaFacturacion(new RegistroFactura { Item = registroFacturacionAnulacion }); + public RegFactuSistemaFacturacion CreateRegFactuSistemaFacturacion(RegistroFacturacionAlta registroFacturacionAlta) => CreateRegFactuSistemaFacturacion(new RegistroFactura { Item = registroFacturacionAlta }); + public RegFactuSistemaFacturacion CreateRegFactuSistemaFacturacion(RegistroFactura registroFactura) => CreateRegFactuSistemaFacturacion([registroFactura]); + public RegFactuSistemaFacturacion CreateRegFactuSistemaFacturacion(IEnumerable registroFactura) + { + var cabecera = new Cabecera + { + ObligadoEmision = new PersonaFisicaJuridicaES + { + // "Name and company name of the person responsible for issuing the invoices." + // Not sure how this needs to be formated. Maybe we'll need some extra fields in the master data? + // Should this be the AccountName, or OutletName or sth from the Agencies? + NombreRazon = _masterData.Account.AccountName, + NIF = _masterData.Account.VatId + }, + + }; + + return new RegFactuSistemaFacturacion + { + Cabecera = cabecera, + RegistroFactura = registroFactura.ToArray() + }; + } + + public async Task CreateRegFactuSistemaFacturacionAsync(IAsyncEnumerable queueItems) + { + var registroFactura = new List(); + ReceiptRequest? previousReceiptRequest = null; + ReceiptResponse? previousReceiptResponse = null; + + await foreach (var queueItem in queueItems) + { + var receiptRequest = JsonSerializer.Deserialize(queueItem.request)!; + var receiptResponse = JsonSerializer.Deserialize(queueItem.response)!; + if (!(receiptResponse.ftSignatures.Any(x => x.ftSignatureType == (long) SignatureTypesES.Huella) && receiptResponse.ftSignatures.Any(x => x.ftSignatureType == (long) SignatureTypesES.IDEmisorFactura))) + { + continue; + } + if (receiptRequest.IsVoid()) + { + if (previousReceiptRequest is null || previousReceiptResponse is null) + { + throw new Exception("There needs to be a previous receipt in the chain to perform a void"); + } + registroFactura.Add( + new RegistroFactura + { + Item = await CreateRegistroFacturacionAnulacionAsync(receiptRequest, receiptResponse, previousReceiptRequest, previousReceiptResponse) + }); + } + else + { + registroFactura.Add( + new RegistroFactura + { + Item = await CreateRegistroFacturacionAltaAsync(receiptRequest, receiptResponse, previousReceiptRequest, previousReceiptResponse) + }); + } + + previousReceiptRequest = receiptRequest; + previousReceiptResponse = receiptResponse; + } + + return CreateRegFactuSistemaFacturacion(registroFactura); + } + + public async Task CreateRegistroFacturacionAnulacionAsync(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse, ReceiptRequest previousReceiptRequest, ReceiptResponse previousReceiptResponse) + { + var previousQueueItems = _queueItemRepository.GetByReceiptReferenceAsync(receiptRequest.cbPreviousReceiptReference); + if (await previousQueueItems.IsEmptyAsync()) + { + throw new Exception($"Receipt with cbReceiptReference {receiptRequest.cbPreviousReceiptReference} not found."); + } + + var voidedQueueItem = await previousQueueItems.SingleOrDefaultAsync() ?? throw new Exception($"Multiple receipts with cbReceiptReference {receiptRequest.cbPreviousReceiptReference} found."); + + var voidedReceiptRequest = JsonSerializer.Deserialize(voidedQueueItem.request)!; + var voidedReceiptResponse = JsonSerializer.Deserialize(voidedQueueItem.response)!; + + var registroFacturacionAnulacion = new RegistroFacturacionAnulacion + { + IDVersion = Version.Item10, + IDFactura = new IDFacturaExpedidaBaja + { + + IDEmisorFacturaAnulada = voidedReceiptResponse.ftSignatures.First(x => x.ftSignatureType == (long) SignatureTypesES.IDEmisorFactura).Data, + NumSerieFacturaAnulada = voidedReceiptResponse.ftReceiptIdentification.Split('#')[1], + FechaExpedicionFacturaAnulada = voidedReceiptRequest.cbReceiptMoment.ToString("dd-MM-yyy") + }, + Encadenamiento = new RegistroFacturacionAnulacionEncadenamiento + { + Item = await GetEncadenamientoFacturaAnteriorAsync(previousReceiptRequest, previousReceiptResponse) + }, + // Which PosSystem from the list should we take? In DE we just take the first one... + // Is this fiskaltrust or the dealer/creator + SistemaInformatico = new SistemaInformatico + { + NombreRazon = "Thomas Steininger", // add real name here... and maybe get that from the config + NombreSistemaInformatico = "fiskaltrust.Middleware", + // Identification code given by the producing person or entity to its computerised invoicing system (RIS) which, once installed, constitutes the RIS used. + // It should distinguish it from any other possible different RIS produced by the same producing person or entity. + // The possible restrictions to its values shall be detailed in the corresponding documentation in the AEAT electronic office (validations document...). + IdSistemaInformatico = "00", // alphanumeric(2) + // VatId of producing company. We don't have that right now. + Item = "M0291081Q", + Version = "1.0.0", // version + NumeroInstalacion = receiptResponse.ftCashBoxIdentification, + TipoUsoPosibleSoloVerifactu = Booleano.N, + TipoUsoPosibleMultiOT = Booleano.N, + IndicadorMultiplesOT = Booleano.N + }, + FechaHoraHusoGenRegistro = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(new DateTimeOffset(receiptResponse.ftReceiptMoment, TimeSpan.Zero), "Europe/Madrid"), + TipoHuella = TipoHuella.Item01, + Huella = null! + }; + + registroFacturacionAnulacion.Huella = registroFacturacionAnulacion.GetHuella(); + + return _certificate is null ? registroFacturacionAnulacion : XmlHelpers.Deserialize(XmlHelpers.SignXmlContentWithXades(XmlHelpers.GetXMLIncludingNamespace(registroFacturacionAnulacion, "sf", "RegistroFacturacionAlta"), _certificate))!; + } + + public async Task CreateRegistroFacturacionAltaAsync(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse, ReceiptRequest? previousReceiptRequest, ReceiptResponse? previousReceiptResponse) + { + var registroFacturacionAlta = new RegistroFacturacionAlta + { + IDVersion = Version.Item10, + IDFactura = new IDFactura + { + IDEmisorFactura = _masterData.Account.VatId, + NumSerieFactura = receiptResponse.ftReceiptIdentification.Split('#')[1], + FechaExpedicionFactura = receiptRequest.cbReceiptMoment.ToString("dd-MM-yyy") + }, + + // "Name and business name of the person required to issue the invoice." + // Not sure how this needs to be formated. Maybe we'll need some extra fields in the master data? + // Should this be the AccountName, or OutletName or sth from the Agencies? + NombreRazonEmisor = _masterData.Account.AccountName, + TipoFactura = (receiptRequest.ftReceiptCase & 0xF000) switch + { + _ => ClaveTipoFactura.F2, // QUESTION: is simplified invoice correct? + // _ => throw new Exception($"Invalid receipt case {receiptRequest.ftReceiptCase}") + }, + DescripcionOperacion = "test", // TODO: add descrpiton?, + // FacturaSinIdentifDestinatarioArt61d = Booleano.S, // TODO: do we need this art. 61d? + Desglose = receiptRequest.cbChargeItems.Select(chargeItem => new Detalle + { + // 01 Value added tax (VAT) + // 02 Tax on Production, Services and Imports (IPSI) for Ceuta and Melilla + // 03 Canary Islands Indirect General Tax (IGIC) + // 05 Other + Impuesto = Impuesto.Item01, + // we'll have to check these in detail + ClaveRegimen = IdOperacionesTrascendenciaTributaria.Item01, + BaseImponibleOimporteNoSujeto = (chargeItem.Amount - chargeItem.GetVATAmount()).ToVeriFactuNumber(), + Item = (chargeItem.ftChargeItemCase & 0xFF00) switch + { + 2 => (chargeItem.ftChargeItemCase & 0x0F00) switch + { + 0 => CalificacionOperacion.N1, // TODO: Document + 1 => CalificacionOperacion.N2, // TODO: Document + _ => throw new Exception($"Invalid charge item case {chargeItem.ftChargeItemCase}") + }, + 3 => (chargeItem.ftChargeItemCase & 0x0F00) switch + { + 0 => OperacionExenta.E1, // TODO: Document + 1 => OperacionExenta.E2, // TODO: Document + 2 => OperacionExenta.E3, // TODO: Document + 3 => OperacionExenta.E4, // TODO: Document + 4 => OperacionExenta.E5, // TODO: Document + 5 => OperacionExenta.E6, // TODO: Document + _ => throw new Exception($"Invalid charge item case {chargeItem.ftChargeItemCase}") + }, + 5 => CalificacionOperacion.S2, + _ => CalificacionOperacion.S1 // If CalificacionOperacion is S1 and BaseImponibleACoste is not filled in, TipoImpositivo and CuotaRepercutida are mandatory. + }, + TipoImpositivo = chargeItem.VATRate.ToVeriFactuNumber(), + CuotaRepercutida = (chargeItem.VATAmount ?? chargeItem.Amount * chargeItem.VATRate).ToVeriFactuNumber() + }).ToArray(), + CuotaTotal = receiptRequest.cbChargeItems.Sum(chargeItem => chargeItem.GetVATAmount()).ToVeriFactuNumber(), + ImporteTotal = (receiptRequest.cbReceiptAmount ?? receiptRequest.cbChargeItems.Sum(chargeItem => chargeItem.Amount)).ToVeriFactuNumber(), + Encadenamiento = new RegistroFacturacionAltaEncadenamiento + { + Item = await GetEncadenamientoFacturaAnteriorAsync(previousReceiptRequest, previousReceiptResponse) + }, + // Which PosSystem from the list should we take? In DE we just take the first one... + // Is this fiskaltrust or the dealer/creator + SistemaInformatico = new SistemaInformatico + { + NombreRazon = "Thomas Steininger", // add real name here... and maybe get that from the config + NombreSistemaInformatico = "fiskaltrust.Middleware", + // Identification code given by the producing person or entity to its computerised invoicing system (RIS) which, once installed, constitutes the RIS used. + // It should distinguish it from any other possible different RIS produced by the same producing person or entity. + // The possible restrictions to its values shall be detailed in the corresponding documentation in the AEAT electronic office (validations document...). + IdSistemaInformatico = "00", // alphanumeric(2) + // VatId of producing company. We don't have that right now. + Item = "M0291081Q", + Version = "1.0.0", // version + NumeroInstalacion = receiptResponse.ftCashBoxIdentification, + TipoUsoPosibleSoloVerifactu = Booleano.N, + TipoUsoPosibleMultiOT = Booleano.N, + IndicadorMultiplesOT = Booleano.N + }, + FechaHoraHusoGenRegistro = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(new DateTimeOffset(receiptResponse.ftReceiptMoment, TimeSpan.Zero), "Europe/Madrid"), + TipoHuella = TipoHuella.Item01, + Huella = null! + }; + + registroFacturacionAlta.Huella = registroFacturacionAlta.GetHuella(); + + return _certificate is null ? registroFacturacionAlta : XmlHelpers.Deserialize(XmlHelpers.SignXmlContentWithXades(XmlHelpers.GetXMLIncludingNamespace(registroFacturacionAlta, "sf", "RegistroFacturacionAlta"), _certificate))!; + } + + private async Task GetEncadenamientoFacturaAnteriorAsync(ReceiptRequest? previousReceiptRequest, ReceiptResponse? previousReceiptResponse) + { + if (previousReceiptRequest is null || previousReceiptResponse is null) + { + return PrimerRegistroCadena.S; + } + + var previousHash = previousReceiptResponse.ftSignatures.First(x => x.ftSignatureType == (long) SignatureTypesES.Huella).Data; + + if (previousReceiptRequest?.IsVoid() ?? false) + { + var previousQueueItems = _queueItemRepository.GetByReceiptReferenceAsync(previousReceiptRequest.cbPreviousReceiptReference); + if (await previousQueueItems.IsEmptyAsync()) + { + throw new Exception($"Receipt with cbReceiptReference {previousReceiptRequest.cbPreviousReceiptReference} not found."); + } + + var voidedQueueItem = await previousQueueItems.SingleOrDefaultAsync() ?? throw new Exception($"Multiple receipts with cbReceiptReference {previousReceiptRequest.cbPreviousReceiptReference} found."); + + var voidedReceiptRequest = JsonSerializer.Deserialize(voidedQueueItem.request)!; + var voidedReceiptResponse = JsonSerializer.Deserialize(voidedQueueItem.response)!; + + return new EncadenamientoFacturaAnterior + { + IDEmisorFactura = voidedReceiptResponse.ftSignatures.First(x => x.ftSignatureType == (long) SignatureTypesES.IDEmisorFactura).Data, + NumSerieFactura = voidedReceiptResponse.ftReceiptIdentification.Split('#')[1], + FechaExpedicionFactura = voidedReceiptRequest!.cbReceiptMoment.ToString("dd-MM-yyy"), + Huella = previousReceiptResponse.ftSignatures.First(x => x.ftSignatureType == (long) SignatureTypesES.Huella).Data + }; + } + else + { + return new EncadenamientoFacturaAnterior + { + IDEmisorFactura = previousReceiptResponse.ftSignatures.First(x => x.ftSignatureType == (long) SignatureTypesES.IDEmisorFactura).Data, + NumSerieFactura = previousReceiptResponse.ftReceiptIdentification.Split('#')[1], + FechaExpedicionFactura = previousReceiptRequest!.cbReceiptMoment.ToString("dd-MM-yyy"), + Huella = previousHash + }; + } + } +} + +public static class HuellaExt +{ + public static string GetHuella(this RegistroFacturacionAlta registroFacturacionAlta) + => registroFacturacionAlta.GetHuella(new List<(string key, Func value)> { + ("IDEmisorFactura", x => x.IDFactura.IDEmisorFactura), + ("NumSerieFactura", x => x.IDFactura.NumSerieFactura), + ("FechaExpedicionFactura", x => x.IDFactura.FechaExpedicionFactura), + ("TipoFactura", x => Enum.GetName(x.TipoFactura)!), + ("CuotaTotal", x => x.CuotaTotal), + ("ImporteTotal", x => x.ImporteTotal), + ("Huella", x => x.Encadenamiento.Item is EncadenamientoFacturaAnterior encadenamiento ? encadenamiento.Huella : ""), + ("FechaHoraHusoGenRegistro", x => x.FechaHoraHusoGenRegistro.ToString("yyyy-MM-ddTHH:mm:sszzz")), + }); + + public static string GetHuella(this RegistroFacturacionAnulacion registroFacturacionAnulacion) + => registroFacturacionAnulacion.GetHuella(new List<(string key, Func value)> { + ("IDEmisorFacturaAnulada", x => x.IDFactura.IDEmisorFacturaAnulada), + ("NumSerieFacturaAnulada", x => x.IDFactura.NumSerieFacturaAnulada), + ("FechaExpedicionFacturaAnulada", x => x.IDFactura.FechaExpedicionFacturaAnulada), + ("Huella", x => x.Encadenamiento.Item is EncadenamientoFacturaAnterior encadenamiento ? encadenamiento.Huella : ""), + ("FechaHoraHusoGenRegistro", x => x.FechaHoraHusoGenRegistro.ToString("yyyy-MM-ddTHH:mm:sszzz")), + }); + + + private static string GetHuella(this T self, List<(string key, Func value)> selectors) + { + var data = new StringBuilder(); + foreach (var (n, (key, value)) in selectors.Select((x, i) => (i + 1, x))) + { + data.AppendFormat(GetValue(key, value(self), false, selectors.Count != n)); + } + var stringData = data.ToString(); + var hash = SHA256.HashData(Encoding.UTF8.GetBytes(stringData)); + + return Convert.ToHexString(hash); + } + + private static string GetValue(string key, string value, bool encoded = false, bool separator = true) + => key + "=" + (encoded ? HttpUtility.UrlEncode(value.Trim()) : value.Trim()) + (separator ? "&" : ""); +} + +public static class XmlExt +{ + public static string XmlSerialize(this T registroFacturacionAlta) + { + var serializer = new XmlSerializer(typeof(T)); + + using var writer = new Utf8StringWriter(); + + serializer.Serialize(writer, registroFacturacionAlta); + + return writer.ToString(); + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Factories/SignaturItemFactory.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Factories/SignaturItemFactory.cs new file mode 100644 index 000000000..e1a486c1f --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Factories/SignaturItemFactory.cs @@ -0,0 +1,76 @@ +using System.Web; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueES.Interface; +using fiskaltrust.Middleware.SCU.ES.Models; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.QueueES.Factories; + +public static class SignaturItemFactory +{ + public static SignatureItem CreateInitialOperationSignature(ftQueue queue) + { + return new SignatureItem() + { + ftSignatureType = (long) SignatureTypesES.InitialOperationReceipt, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + Caption = $"Initial-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}" + }; + } + + public static SignatureItem CreateOutOfOperationSignature(ftQueue queue) + { + return new SignatureItem() + { + ftSignatureType = (long) SignatureTypesES.OutOfOperationReceipt, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + Caption = $"Out-of-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}" + }; + } + + public static SignatureItem CreateESQRCode(string baseUrl, RegistroFacturacionAlta registroFacturacionAlta) + { + var query = HttpUtility.ParseQueryString(String.Empty); + query.Add("nif", registroFacturacionAlta.IDFactura.IDEmisorFactura); + query.Add("numserie", registroFacturacionAlta.IDFactura.NumSerieFactura); + query.Add("fecha", registroFacturacionAlta.IDFactura.FechaExpedicionFactura); + query.Add("importe", registroFacturacionAlta.ImporteTotal); + + var uriBuider = new UriBuilder(baseUrl) + { + Query = query.ToString() + }; + + return new SignatureItem() + { + Caption = "[www.fiskaltrust.es]", + Data = uriBuider.Uri.ToString(), + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.QR_Code, + ftSignatureType = (long) SignatureTypesES.VeriFactu + }; + } + + public static SignatureItem CreateESSignature(byte[] signature) + { + return new SignatureItem() + { + Caption = "Signature", + Data = Convert.ToBase64String(signature), + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Base64, + ftSignatureType = (long) SignatureTypesES.Signature + }; + } + + public static SignatureItem CreateESHuella(string huella) + { + return new SignatureItem() + { + Caption = "Huella", + Data = huella, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = (long) SignatureTypesES.Huella + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Factories/ftActionJournalFactory.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Factories/ftActionJournalFactory.cs new file mode 100644 index 000000000..4792f0d16 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Factories/ftActionJournalFactory.cs @@ -0,0 +1,71 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Contracts.Extensions; +using fiskaltrust.Middleware.Localization.QueueES.Models; +using fiskaltrust.storage.V0; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.Localization.QueueES.Factories; + +public static class ftActionJournalFactory +{ + public static ftActionJournal CreateDailyClosingActionJournal(ftQueue queue, ReceiptRequest request, ReceiptResponse receiptResponse) + { + var ftReceiptCaseHex = request.ftReceiptCase.ToString("X"); + return CreateActionJournal(receiptResponse.ftQueueID, ftReceiptCaseHex, receiptResponse.ftQueueItemID, $"Daily-Closing receipt was processed.", JsonConvert.SerializeObject(new { ftReceiptNumerator = queue.ftReceiptNumerator + 1 })); + } + + public static ftActionJournal CreateMonthlyClosingActionJournal(ftQueue queue, ReceiptRequest request, ReceiptResponse receiptResponse) + { + var ftReceiptCaseHex = request.ftReceiptCase.ToString("X"); + return CreateActionJournal(receiptResponse.ftQueueID, ftReceiptCaseHex, receiptResponse.ftQueueItemID, $"Monthly-Closing receipt was processed.", JsonConvert.SerializeObject(new { ftReceiptNumerator = queue.ftReceiptNumerator + 1 })); + } + + public static ftActionJournal CreateInitialOperationActionJournal(ReceiptRequest request, ReceiptResponse receiptResponse) + { + var notification = new ActivateQueueES + { + CashBoxId = request.ftCashBoxID!.Value, + QueueId = receiptResponse.ftQueueID, + Moment = DateTime.UtcNow, + IsStartReceipt = true, + Version = "V0", + }; + return CreateActionJournal(receiptResponse.ftQueueID, $"{request.ftReceiptCase:X}-{nameof(ActivateQueueES)}", receiptResponse.ftQueueItemID, $"Initial-Operation receipt. Queue-ID: {receiptResponse.ftQueueID}", JsonConvert.SerializeObject(notification)); + } + + public static ftActionJournal CreateWrongStateForInitialOperationActionJournal(ftQueue queue, ReceiptRequest request, ReceiptResponse receiptResponse) + { + return CreateActionJournal(receiptResponse.ftQueueID, $"{request.ftReceiptCase:X}", + receiptResponse.ftQueueItemID, queue.IsDeactivated() + ? $"Queue {queue.ftQueueId} is de-activated, initial-operations-receipt can not be executed." + : $"Queue {queue.ftQueueId} is already activated, initial-operations-receipt can not be executed.", ""); + } + + public static ftActionJournal CreateOutOfOperationActionJournal(ReceiptRequest request, ReceiptResponse receiptResponse) + { + var notification = new DeactivateQueueES + { + CashBoxId = request.ftCashBoxID!.Value, + QueueId = receiptResponse.ftQueueID, + Moment = DateTime.UtcNow, + IsStopReceipt = true, + Version = "V0" + }; + return CreateActionJournal(receiptResponse.ftQueueID, $"{request.ftReceiptCase:X}-{nameof(DeactivateQueueES)}", receiptResponse.ftQueueItemID, $"Out-of-Operation receipt. Queue-ID: {receiptResponse.ftQueueID}", JsonConvert.SerializeObject(notification)); + } + + private static ftActionJournal CreateActionJournal(Guid queueId, string type, Guid queueItemId, string message, string data, int priority = -1) + { + return new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queueId, + ftQueueItemId = queueItemId, + Type = type, + Moment = DateTime.UtcNow, + Message = message, + Priority = priority, + DataJson = data + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Helpers/DecimalHelpers.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Helpers/DecimalHelpers.cs new file mode 100644 index 000000000..d6a8ec437 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Helpers/DecimalHelpers.cs @@ -0,0 +1,8 @@ +using System.Globalization; + +namespace fiskaltrust.Middleware.Localization.QueueES.Helpers; + +public static class DecimalHelpers +{ + public static string ToVeriFactuNumber(this Decimal from) => from.ToString("0.00", CultureInfo.InvariantCulture); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Helpers/Result.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Helpers/Result.cs new file mode 100644 index 000000000..daf687438 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Helpers/Result.cs @@ -0,0 +1,92 @@ +using System.Security.Cryptography; + +namespace fiskaltrust.Middleware.SCU.ES.Helpers; + +public record Result +{ + public record Ok(T Value) : Result(); + public static implicit operator Result(T v) => new Result.Ok(v); + + public record Err(E Error) : Result(); + public static implicit operator Result(E e) => new Result.Err(e); + + private Result() { } // private constructor can prevent derived cases from being defined elsewhere + +#pragma warning disable CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). + public bool IsOk => this switch + { + Ok => true, + Err => false, + }; + + public bool IsErr => this switch + { + Ok => false, + Err => true, + }; + + public T? OkValue => this switch + { + Ok ok => ok.Value, + Err => default, + }; + + public E? ErrValue => this switch + { + Ok => default, + Err err => err.Error, + }; + + public void Match( + Action success, + Action failure + ) + { + Action action = this switch + { + Ok ok => () => success(ok.Value), + Err err => () => failure(err.Error) + }; + + action(); + } + + public R Match( + Func success, + Func failure + ) => this switch + { + Ok ok => success(ok.Value), + Err err => failure(err.Error) + }; + + public Result Map(Func success) => this switch + { + Ok ok => success(ok.Value), + Err err => err.Error + }; + + public async Task> MapAsync(Func> success) => this switch + { + Ok ok => await success(ok.Value), + Err err => err.Error + }; + + public Result MapErr(Func failure) => this switch + { + Ok ok => ok.Value, + Err err => failure(err.Error) + }; + + public Result AndThen(Func> success) => this switch + { + Ok ok => success(ok.Value), + Err err => err.Error + }; + public async Task> AndThenAsync(Func>> success) => this switch + { + Ok ok => await success(ok.Value), + Err err => err.Error + }; +#pragma warning restore CS8509 // The switch expression does not handle all possible values of its input type (it is not exhaustive). +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Helpers/XmlHelpers.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Helpers/XmlHelpers.cs new file mode 100644 index 000000000..9f26b2199 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Helpers/XmlHelpers.cs @@ -0,0 +1,81 @@ +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Serialization; +using FirmaXadesNetCore.Crypto; +using FirmaXadesNetCore.Signature.Parameters; +using FirmaXadesNetCore; +using System.Security.Cryptography.X509Certificates; +using System.Net.NetworkInformation; + +namespace fiskaltrust.Middleware.SCU.ES.Helpers +{ + public class Utf8StringWriter : StringWriter + { + public override Encoding Encoding => Encoding.UTF8; + } + + public static class XmlHelpers + { + public static Stream ToStream(this string content) + { + var stream = new MemoryStream(); + var writer = new StreamWriter(stream, Encoding.UTF8); + writer.Write(content); + writer.Flush(); + stream.Position = 0; + return stream; + } + + public static T? Deserialize(string from) + { + var serializer = new XmlSerializer(typeof(T)); + + return (T?) serializer.Deserialize(from.ToStream()); + } + + public static string Serialize(T from) + { + var serializer = new XmlSerializer(typeof(T)); + using var writer = new Utf8StringWriter(); + + serializer.Serialize(writer, from); + + return writer.ToString(); + } + + public static string GetXMLIncludingNamespace(T request, string prefix, string namespaceUri) + { + var doc = new XmlDocument(); + var nav = doc.CreateNavigator(); + using (var w = nav!.AppendChild()) + { + var namespaces = new XmlSerializerNamespaces(); + namespaces.Add(prefix, namespaceUri); + var ser = new XmlSerializer(typeof(T)); + ser.Serialize(w, request, namespaces); + } + return doc.OuterXml; + } + + public static string SignXmlContentWithXades(string xml, X509Certificate2 certificate) + { + var xadesService = new XadesService(); + var parameters = new SignatureParameters + { + SignaturePackaging = SignaturePackaging.ENVELOPED, + DataFormat = new DataFormat + { + MimeType = "text/xml" + }, + DigestMethod = DigestMethod.SHA256, + Signer = new Signer(certificate) + }; + + var byteArray = Encoding.ASCII.GetBytes(xml); + var stream = new MemoryStream(byteArray); + var signedXmlBytes = xadesService.Sign(stream, parameters).GetDocumentBytes(); + return Encoding.UTF8.GetString(signedXmlBytes, 0, signedXmlBytes.Length); + } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Interface/Cases.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Interface/Cases.cs new file mode 100644 index 000000000..83d59e9dc --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Interface/Cases.cs @@ -0,0 +1,9 @@ +using System.Globalization; + +namespace fiskaltrust.Middleware.Localization.QueueES.Interface; + + +public class Cases +{ + public const long BASE_STATE = 0x4553_2000_0000_0000; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Interface/ErrorMessages.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Interface/ErrorMessages.cs new file mode 100644 index 000000000..a8e8047f5 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Interface/ErrorMessages.cs @@ -0,0 +1,6 @@ +namespace fiskaltrust.Middleware.Localization.QueueES.Interface; + +public class ErrorMessages +{ + public static string UnknownReceiptCase(long caseCode) => $"The given ftReceiptCase 0x{caseCode:x} is not supported. Please refer to docs.fiskaltrust.cloud for supported cases."; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Interface/SignatureTypesES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Interface/SignatureTypesES.cs new file mode 100644 index 000000000..3ab99457c --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Interface/SignatureTypesES.cs @@ -0,0 +1,11 @@ +namespace fiskaltrust.Middleware.Localization.QueueES.Interface; + +public enum SignatureTypesES : long +{ + InitialOperationReceipt = 0x4553_2000_0001_1001, + OutOfOperationReceipt = 0x4553_2000_0001_1002, + VeriFactu = 0x4553_2000_0000_0001, + IDEmisorFactura = 0x4553_2000_0000_0002, + Signature = 0x4553_2000_0000_0003, + Huella = 0x4553_2000_0000_0004 +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/JournalProcessorES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/JournalProcessorES.cs deleted file mode 100644 index 89d444d7a..000000000 --- a/queue/src/fiskaltrust.Middleware.Localization.QueueES/JournalProcessorES.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using fiskaltrust.ifPOS.v1; -using fiskaltrust.Middleware.Contracts.Interfaces; -using Microsoft.Extensions.Logging; - -namespace fiskaltrust.Middleware.Localization.QueueES -{ - public class JournalProcessorES : IMarketSpecificJournalProcessor - { - private readonly ILogger _logger; - - public JournalProcessorES( - ILogger logger) - { - _logger = logger; - } - - public IAsyncEnumerable ProcessAsync(JournalRequest request) - { - throw new NotImplementedException(); - } - } -} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Models/ActivateQueueES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Models/ActivateQueueES.cs new file mode 100644 index 000000000..942d22273 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Models/ActivateQueueES.cs @@ -0,0 +1,10 @@ +namespace fiskaltrust.Middleware.Localization.QueueES.Models; + +public class ActivateQueueES +{ + public Guid CashBoxId { get; set; } + public required Guid QueueId { get; set; } + public required DateTime Moment { get; set; } + public required bool IsStartReceipt { get; set; } + public required string Version { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Models/DeactivateQueueES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Models/DeactivateQueueES.cs new file mode 100644 index 000000000..b82b39e34 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Models/DeactivateQueueES.cs @@ -0,0 +1,10 @@ +namespace fiskaltrust.Middleware.Localization.QueueES.Models; + +public class DeactivateQueueES +{ + public Guid CashBoxId { get; set; } + public Guid QueueId { get; set; } + public DateTime Moment { get; set; } + public bool IsStopReceipt { get; set; } + public required string Version { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/DailyOperationsCommandProcessorES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/DailyOperationsCommandProcessorES.cs new file mode 100644 index 000000000..46c6abbac --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/DailyOperationsCommandProcessorES.cs @@ -0,0 +1,52 @@ +using fiskaltrust.Middleware.Localization.QueueES.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using fiskaltrust.Middleware.Localization.v2.Storage; +using fiskaltrust.Middleware.Localization.QueueES.ESSSCD; +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.QueueES.Processors; + +public class DailyOperationsCommandProcessorES(IESSSCD sscd, IQueueStorageProvider queueStorageProvider) : IDailyOperationsCommandProcessor +{ +#pragma warning disable + private readonly IESSSCD _sscd = sscd; + private readonly IQueueStorageProvider _queueStorageProvider = queueStorageProvider; +#pragma warning restore + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.ZeroReceipt0x2000: + return await ZeroReceipt0x2000Async(request); + case (int) ReceiptCases.OneReceipt0x2001: + return await OneReceipt0x2001Async(request); + case (int) ReceiptCases.ShiftClosing0x2010: + return await ShiftClosing0x2010Async(request); + case (int) ReceiptCases.DailyClosing0x2011: + return await DailyClosing0x2011Async(request); + case (int) ReceiptCases.MonthlyClosing0x2012: + return await MonthlyClosing0x2012Async(request); + case (int) ReceiptCases.YearlyClosing0x2013: + return await YearlyClosing0x2013Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + public async Task ZeroReceipt0x2000Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task OneReceipt0x2001Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task ShiftClosing0x2010Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task DailyClosing0x2011Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task MonthlyClosing0x2012Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task YearlyClosing0x2013Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/InvoiceCommandProcessorES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/InvoiceCommandProcessorES.cs new file mode 100644 index 000000000..1b8782719 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/InvoiceCommandProcessorES.cs @@ -0,0 +1,36 @@ +using fiskaltrust.Middleware.Localization.QueueES.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueueES.Processors; + +public class InvoiceCommandProcessorES : IInvoiceCommandProcessor +{ + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.InvoiceUnknown0x1000: + return await InvoiceUnknown0x1000Async(request); + case (int) ReceiptCases.InvoiceB2C0x1001: + return await InvoiceB2C0x1001Async(request); + case (int) ReceiptCases.InvoiceB2B0x1002: + return await InvoiceB2B0x1002Async(request); + case (int) ReceiptCases.InvoiceB2G0x1003: + return await InvoiceB2G0x1003Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task InvoiceUnknown0x1000Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task InvoiceB2C0x1001Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task InvoiceB2B0x1002Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task InvoiceB2G0x1003Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/JournalProcessorES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/JournalProcessorES.cs new file mode 100644 index 000000000..9fc939d42 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/JournalProcessorES.cs @@ -0,0 +1,35 @@ +using System.Text; +using System.Xml.Serialization; +using fiskaltrust.ifPOS.v1; +using fiskaltrust.Middleware.Contracts.Repositories; +using fiskaltrust.Middleware.Localization.QueueES.Exports; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Storage.ES; +using fiskaltrust.storage.V0; +using fiskaltrust.storage.V0.MasterData; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Processors; + +public class JournalProcessorES : IJournalProcessor +{ + private readonly IMiddlewareReceiptJournalRepository _receiptJournalRepository; + private readonly IMiddlewareQueueItemRepository _queueItemRepository; + private readonly VeriFactuMapping _veriFactuMapping; + + public JournalProcessorES(IMiddlewareReceiptJournalRepository receiptJournalRepository, IMiddlewareQueueItemRepository queueItemRepository, MasterDataConfiguration masterData) + { + _receiptJournalRepository = receiptJournalRepository; + _queueItemRepository = queueItemRepository; + _veriFactuMapping = new VeriFactuMapping(masterData, queueItemRepository); + } + + public async IAsyncEnumerable ProcessAsync(JournalRequest request) + { + var veriFactu = await _veriFactuMapping.CreateRegFactuSistemaFacturacionAsync(_receiptJournalRepository.GetEntriesOnOrAfterTimeStampAsync(0).SelectAwait(async x => await _queueItemRepository.GetAsync(x.ftQueueItemId))); + yield return new JournalResponse + { + Chunk = Encoding.UTF8.GetBytes(veriFactu.XmlSerialize()).ToList() + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/LifecycleCommandProcessorES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/LifecycleCommandProcessorES.cs new file mode 100644 index 000000000..805305024 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/LifecycleCommandProcessorES.cs @@ -0,0 +1,65 @@ +using fiskaltrust.Middleware.Localization.QueueES.Factories; +using fiskaltrust.Middleware.Localization.QueueES.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.Storage; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using fiskaltrust.Middleware.Localization.QueueES.ESSSCD; +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.QueueES.Processors; + +public class LifecycleCommandProcessorES : ILifecycleCommandProcessor +{ + private readonly ILocalizedQueueStorageProvider _queueStorageProvider; + + public LifecycleCommandProcessorES(ILocalizedQueueStorageProvider queueStorageProvider) + { + _queueStorageProvider = queueStorageProvider; + } + + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.InitialOperationReceipt0x4001: + return await InitialOperationReceipt0x4001Async(request); + case (int) ReceiptCases.OutOfOperationReceipt0x4002: + return await OutOfOperationReceipt0x4002Async(request); + case (int) ReceiptCases.InitSCUSwitch0x4011: + return await InitSCUSwitch0x4011Async(request); + case (int) ReceiptCases.FinishSCUSwitch0x4012: + return await FinishSCUSwitch0x4012Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task InitialOperationReceipt0x4001Async(ProcessCommandRequest request) + { + var (queue, receiptRequest, receiptResponse) = request; + var actionJournal = ftActionJournalFactory.CreateInitialOperationActionJournal(receiptRequest, receiptResponse); + await _queueStorageProvider.ActivateQueueAsync(); + + receiptResponse.AddSignatureItem(SignaturItemFactory.CreateInitialOperationSignature(queue)); + return new ProcessCommandResponse(receiptResponse, [actionJournal]); + } + + public async Task OutOfOperationReceipt0x4002Async(ProcessCommandRequest request) + { + var (queue, receiptRequest, receiptResponse) = request; + await _queueStorageProvider.DeactivateQueueAsync(); + + var actionJournal = ftActionJournalFactory.CreateOutOfOperationActionJournal(receiptRequest, receiptResponse); + receiptResponse.AddSignatureItem(SignaturItemFactory.CreateOutOfOperationSignature(queue)); + receiptResponse.MarkAsDisabled(); + return new ProcessCommandResponse(receiptResponse, [actionJournal]); + } + + public async Task InitSCUSwitch0x4011Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task FinishSCUSwitch0x4012Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/ProtocolCommandProcessorES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/ProtocolCommandProcessorES.cs new file mode 100644 index 000000000..5ed3e09fe --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/ProtocolCommandProcessorES.cs @@ -0,0 +1,44 @@ +using fiskaltrust.Middleware.Localization.QueueES.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueueES.Processors; + +public class ProtocolCommandProcessorES : IProtocolCommandProcessor +{ + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.ProtocolUnspecified0x3000: + return await ProtocolUnspecified0x3000Async(request); + case (int) ReceiptCases.ProtocolTechnicalEvent0x3001: + return await ProtocolTechnicalEvent0x3001Async(request); + case (int) ReceiptCases.ProtocolAccountingEvent0x3002: + return await ProtocolAccountingEvent0x3002Async(request); + case (int) ReceiptCases.InternalUsageMaterialConsumption0x3003: + return await InternalUsageMaterialConsumption0x3003Async(request); + case (int) ReceiptCases.Order0x3004: + return await Order0x3004Async(request); + case (int) ReceiptCases.CopyReceiptPrintExistingReceipt0x3010: + return await CopyReceiptPrintExistingReceipt0x3010Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task ProtocolUnspecified0x3000Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task ProtocolTechnicalEvent0x3001Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task ProtocolAccountingEvent0x3002Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task InternalUsageMaterialConsumption0x3003Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task Order0x3004Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task CopyReceiptPrintExistingReceipt0x3010Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/ReceiptCommandProcessorES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/ReceiptCommandProcessorES.cs new file mode 100644 index 000000000..4358f221a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/Processors/ReceiptCommandProcessorES.cs @@ -0,0 +1,70 @@ +using fiskaltrust.Middleware.Localization.QueueES.ESSSCD; +using fiskaltrust.Middleware.Localization.QueueES.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.QueueES.Processors; + +public class ReceiptCommandProcessorES(IESSSCD sscd, Storage.ES.IConfigurationRepository configurationRepository, IReadOnlyQueueItemRepository queueItemRepository) : IReceiptCommandProcessor +{ +#pragma warning disable + private readonly IESSSCD _sscd = sscd; + private readonly Storage.ES.IConfigurationRepository _configurationRepository = configurationRepository; + private readonly IReadOnlyQueueItemRepository _queueItemRepository = queueItemRepository; +#pragma warning restore + + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.UnknownReceipt0x0000: + return await UnknownReceipt0x0000Async(request).ConfigureAwait(false); + case (int) ReceiptCases.PointOfSaleReceipt0x0001: + return await PointOfSaleReceipt0x0001Async(request); + case (int) ReceiptCases.PaymentTransfer0x0002: + return await PaymentTransfer0x0002Async(request); + case (int) ReceiptCases.PointOfSaleReceiptWithoutObligation0x0003: + return await PointOfSaleReceiptWithoutObligation0x0003Async(request); + case (int) ReceiptCases.ECommerce0x0004: + return await ECommerce0x0004Async(request); + case (int) ReceiptCases.Protocol0x0005: + return await Protocol0x0005Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task UnknownReceipt0x0000Async(ProcessCommandRequest request) => await PointOfSaleReceipt0x0001Async(request); + + public async Task PointOfSaleReceipt0x0001Async(ProcessCommandRequest request) + { + var queueES = await _configurationRepository.GetQueueESAsync(request.queue.ftQueueId); + var previousQueueItem = queueES.SSCDSignQueueItemId is not null ? await _queueItemRepository.GetAsync(queueES.SSCDSignQueueItemId.Value) : null; + + var response = await _sscd.ProcessReceiptAsync(new ProcessRequest + { + ReceiptRequest = request.ReceiptRequest, + ReceiptResponse = request.ReceiptResponse, + PreviousReceiptRequest = previousQueueItem is null ? null : JsonSerializer.Deserialize(previousQueueItem.request)!, // handle null case? + PreviousReceiptResponse = previousQueueItem is null ? null : JsonSerializer.Deserialize(previousQueueItem.response)!, + }); + + queueES.SSCDSignQueueItemId = response.ReceiptResponse.ftQueueItemID; + await _configurationRepository.InsertOrUpdateQueueESAsync(queueES); + + return await Task.FromResult(new ProcessCommandResponse(response.ReceiptResponse, new List())).ConfigureAwait(false); + } + + public async Task PaymentTransfer0x0002Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task PointOfSaleReceiptWithoutObligation0x0003Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task ECommerce0x0004Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task Protocol0x0005Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/QueueESBootstrapper.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/QueueESBootstrapper.cs index baa9968ea..c6950218c 100644 --- a/queue/src/fiskaltrust.Middleware.Localization.QueueES/QueueESBootstrapper.cs +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/QueueESBootstrapper.cs @@ -1,15 +1,91 @@ -using fiskaltrust.Middleware.Contracts.Interfaces; -using Microsoft.Extensions.DependencyInjection; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Text.Json; +using fiskaltrust.Middleware.Localization.QueueES.ESSSCD; +using fiskaltrust.Middleware.Localization.QueueES.Processors; +using fiskaltrust.Middleware.Localization.QueuePT.Processors; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Localization.v2.Configuration; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.MasterData; +using fiskaltrust.Middleware.Localization.v2.Storage; +using fiskaltrust.Middleware.Storage.AzureTableStorage; +using fiskaltrust.Middleware.Storage.ES; +using fiskaltrust.storage.V0.MasterData; +using Microsoft.Extensions.Logging; -namespace fiskaltrust.Middleware.Localization.QueueES +namespace fiskaltrust.Middleware.Localization.QueueES; + + +public class QueueESBootstrapper : IV2QueueBootstrapper { - public class QueueESBootstrapper : ILocalizedQueueBootstrapper + private readonly Queue _queue; + + public QueueESBootstrapper(Guid id, ILoggerFactory loggerFactory, Dictionary configuration) { - public void ConfigureServices(IServiceCollection services) + var middlewareConfiguration = MiddlewareConfigurationFactory.CreateMiddlewareConfiguration(id, configuration); + + var signaturCreationUnitES = new ftSignaturCreationUnitES(); + var storageProvider = new AzureStorageProvider(loggerFactory, id, configuration); + var queueStorageProvider = new QueueStorageProvider(id, storageProvider); + + var masterDataService = new MasterDataService(configuration, storageProvider); + storageProvider.Initialized.Wait(); + var masterData = masterDataService.GetCurrentDataAsync().Result; // put this in an async scu init process + var queueESRepository = (IConfigurationRepository) storageProvider.GetConfigurationRepository(); + var queueES = queueESRepository.GetQueueESAsync(id).Result; + if (queueES is null) { - var _ = services - .AddScoped() - .AddScoped(); + queueES = Newtonsoft.Json.JsonConvert.DeserializeObject>(configuration["init_ftQueueES"]!.ToString()!).First(); + queueESRepository.InsertOrUpdateQueueESAsync(queueES); } + var esSSCD = new InMemorySCU( + signaturCreationUnitES, + masterData, + new InMemorySCUConfiguration() + { + Certificate = new X509Certificate2( + Convert.FromBase64String(middlewareConfiguration.Configuration!["certificate"].ToString()!), + middlewareConfiguration.Configuration!["certificatePassword"].ToString()) + }, + storageProvider.GetMiddlewareQueueItemRepository()); + var signProcessorES = new ReceiptProcessor( + loggerFactory.CreateLogger(), + new LifecycleCommandProcessorES( + queueStorageProvider + ), + new ReceiptCommandProcessorES( + esSSCD, + (IConfigurationRepository) storageProvider.GetConfigurationRepository(), + storageProvider.GetMiddlewareQueueItemRepository() + ), + new DailyOperationsCommandProcessorES( + esSSCD, + queueStorageProvider), + new InvoiceCommandProcessorES(), + new ProtocolCommandProcessorES() + ); + var signProcessor = new SignProcessor(loggerFactory.CreateLogger(), queueStorageProvider, signProcessorES.ProcessAsync, queueES.CashBoxIdentification, middlewareConfiguration); + var journalProcessor = new JournalProcessor(storageProvider, new JournalProcessorES(storageProvider.GetMiddlewareReceiptJournalRepository(), storageProvider.GetMiddlewareQueueItemRepository(), masterData), configuration, loggerFactory.CreateLogger()); + _queue = new Queue(signProcessor, journalProcessor, loggerFactory) + { + Id = id, + Configuration = configuration, + }; + } + + public Func> RegisterForSign() + { + return _queue.RegisterForSign(); + } + + public Func> RegisterForEcho() + { + return _queue.RegisterForEcho(); + } + + public Func> RegisterForJournal() + { + return _queue.RegisterForJournal(); } } diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/SignProcessorES.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueES/SignProcessorES.cs deleted file mode 100644 index 7e04a4a6d..000000000 --- a/queue/src/fiskaltrust.Middleware.Localization.QueueES/SignProcessorES.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using fiskaltrust.ifPOS.v1; -using fiskaltrust.Middleware.Contracts.Interfaces; -using fiskaltrust.Middleware.Contracts.Repositories; -using fiskaltrust.storage.V0; -using Microsoft.Extensions.Logging; - -namespace fiskaltrust.Middleware.Localization.QueueES -{ - public class SignProcessorES : IMarketSpecificSignProcessor - { - private readonly ILogger _logger; - - public SignProcessorES( - ILogger logger) - { - _logger = logger; - } - - public Task<(ReceiptResponse receiptResponse, List actionJournals)> ProcessAsync(ReceiptRequest request, ftQueue queue, ftQueueItem queueItem) - { - throw new NotImplementedException(); - } - - public Task GetFtCashBoxIdentificationAsync(ftQueue queue) => throw new NotImplementedException(); - public Task FinalTaskAsync(ftQueue queue, ftQueueItem queueItem, ReceiptRequest request, IMiddlewareActionJournalRepository actionJournalRepository, IMiddlewareQueueItemRepository queueItemRepository, IMiddlewareReceiptJournalRepository receiptJournalRepositor) { return Task.CompletedTask; } - public Task FirstTaskAsync() { return Task.CompletedTask; } - } -} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueES/fiskaltrust.Middleware.Localization.QueueES.csproj b/queue/src/fiskaltrust.Middleware.Localization.QueueES/fiskaltrust.Middleware.Localization.QueueES.csproj index c77b494a6..4feaf8c9c 100644 --- a/queue/src/fiskaltrust.Middleware.Localization.QueueES/fiskaltrust.Middleware.Localization.QueueES.csproj +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueES/fiskaltrust.Middleware.Localization.QueueES.csproj @@ -1,15 +1,28 @@  - - - - - - - - - + + net8 + Latest + enable + enable + + + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Factories/SignaturItemFactory.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Factories/SignaturItemFactory.cs new file mode 100644 index 000000000..14a6b9d6a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Factories/SignaturItemFactory.cs @@ -0,0 +1,41 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.Interface; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.QueueGR.Factories; + +public static class SignaturItemFactory +{ + public static SignatureItem CreateInitialOperationSignature(ftQueue queue) + { + return new SignatureItem() + { + ftSignatureType = (long) SignatureTypesGR.InitialOperationReceipt, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + Caption = $"Initial-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}" + }; + } + + public static SignatureItem CreateOutOfOperationSignature(ftQueue queue) + { + return new SignatureItem() + { + ftSignatureType = (long) SignatureTypesGR.OutOfOperationReceipt, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + Caption = $"Out-of-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}" + }; + } + + public static SignatureItem CreateGRQRCode(string qrCode) + { + return new SignatureItem() + { + Caption = "[www.fiskaltrust.gr]", + Data = qrCode, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.QR_Code, + ftSignatureType = (long) SignatureTypesGR.PosReceipt + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Factories/ftActionJournalFactory.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Factories/ftActionJournalFactory.cs new file mode 100644 index 000000000..4631e8d05 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Factories/ftActionJournalFactory.cs @@ -0,0 +1,71 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.Models; +using fiskaltrust.Middleware.Localization.v2.Helpers; +using fiskaltrust.storage.V0; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.Localization.QueueGR.Factories; + +public static class ftActionJournalFactory +{ + public static ftActionJournal CreateDailyClosingActionJournal(ftQueue queue, ReceiptRequest request, ReceiptResponse receiptResponse) + { + var ftReceiptCaseHex = request.ftReceiptCase.ToString("X"); + return CreateActionJournal(receiptResponse.ftQueueID, ftReceiptCaseHex, receiptResponse.ftQueueItemID, $"Daily-Closing receipt was processed.", JsonConvert.SerializeObject(new { ftReceiptNumerator = queue.ftReceiptNumerator + 1 })); + } + + public static ftActionJournal CreateMonthlyClosingActionJournal(ftQueue queue, ReceiptRequest request, ReceiptResponse receiptResponse) + { + var ftReceiptCaseHex = request.ftReceiptCase.ToString("X"); + return CreateActionJournal(receiptResponse.ftQueueID, ftReceiptCaseHex, receiptResponse.ftQueueItemID, $"Monthly-Closing receipt was processed.", JsonConvert.SerializeObject(new { ftReceiptNumerator = queue.ftReceiptNumerator + 1 })); + } + + public static ftActionJournal CreateInitialOperationActionJournal(ReceiptRequest request, ReceiptResponse receiptResponse) + { + var notification = new ActivateQueueGR + { + CashBoxId = request.ftCashBoxID!.Value, + QueueId = receiptResponse.ftQueueID, + Moment = DateTime.UtcNow, + IsStartReceipt = true, + Version = "V0", + }; + return CreateActionJournal(receiptResponse.ftQueueID, $"{request.ftReceiptCase:X}-{nameof(ActivateQueueGR)}", receiptResponse.ftQueueItemID, $"Initial-Operation receipt. Queue-ID: {receiptResponse.ftQueueID}", JsonConvert.SerializeObject(notification)); + } + + public static ftActionJournal CreateWrongStateForInitialOperationActionJournal(ftQueue queue, ReceiptRequest request, ReceiptResponse receiptResponse) + { + return CreateActionJournal(receiptResponse.ftQueueID, $"{request.ftReceiptCase:X}", + receiptResponse.ftQueueItemID, queue.IsDeactivated() + ? $"Queue {queue.ftQueueId} is de-activated, initial-operations-receipt can not be executed." + : $"Queue {queue.ftQueueId} is already activated, initial-operations-receipt can not be executed.", ""); + } + + public static ftActionJournal CreateOutOfOperationActionJournal(ReceiptRequest request, ReceiptResponse receiptResponse) + { + var notification = new DeactivateQueueGR + { + CashBoxId = request.ftCashBoxID!.Value, + QueueId = receiptResponse.ftQueueID, + Moment = DateTime.UtcNow, + IsStopReceipt = true, + Version = "V0" + }; + return CreateActionJournal(receiptResponse.ftQueueID, $"{request.ftReceiptCase:X}-{nameof(DeactivateQueueGR)}", receiptResponse.ftQueueItemID, $"Out-of-Operation receipt. Queue-ID: {receiptResponse.ftQueueID}", JsonConvert.SerializeObject(notification)); + } + + private static ftActionJournal CreateActionJournal(Guid queueId, string type, Guid queueItemId, string message, string data, int priority = -1) + { + return new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queueId, + ftQueueItemId = queueItemId, + Type = type, + Moment = DateTime.UtcNow, + Message = message, + Priority = priority, + DataJson = data + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/AADEFactory.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/AADEFactory.cs new file mode 100644 index 000000000..42d78181a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/AADEFactory.cs @@ -0,0 +1,485 @@ +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Web; +using System.Xml.Serialization; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE.Models; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.myDataSCU; +using fiskaltrust.Middleware.Localization.v2.Helpers; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.storage.V0; +using fiskaltrust.storage.V0.MasterData; + +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE; + +public class AADEFactory +{ + private readonly MasterDataConfiguration _masterDataConfiguration; + + public AADEFactory(MasterDataConfiguration masterDataConfiguration) + { + _masterDataConfiguration = masterDataConfiguration; + } + + public void ValidateReceiptRequest(ReceiptRequest receiptRequest) + { + if (receiptRequest.cbChargeItems.Any(x => x.IsAgencyBusiness()) && !receiptRequest.cbChargeItems.All(x => x.IsAgencyBusiness())) + { + throw new Exception("It is not allowed to mix agency and non agency receipts."); + } + + if (receiptRequest.cbChargeItems.Sum(x => x.Amount) != receiptRequest.cbPayItems.Sum(x => x.Amount)) + { + throw new Exception("The sum of the charge items must be equal to the sum of the pay items."); + } + } + + public InvoicesDoc MapToInvoicesDoc(List queueItems) + { + var receiptRequests = queueItems.Where(x => !string.IsNullOrEmpty(x.request) && !string.IsNullOrEmpty(x.response)).Select(x => (receiptRequest: JsonSerializer.Deserialize(x.request)!, receiptResponse: JsonSerializer.Deserialize(x.response))).ToList(); + var actualReceiptRequests = receiptRequests.Where(x => x.receiptResponse != null && ((long) x.receiptResponse.ftState & 0xFF) == 0x00).Cast<(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse)>().ToList(); + actualReceiptRequests = actualReceiptRequests.Where(x => + { + var mark = x.receiptResponse.ftSignatures.FirstOrDefault(x => x.Caption == "invoiceMark")?.Data; + if (mark == null) + { + return false; + } + + try + { + AADEMappings.GetInvoiceType(x.receiptRequest); + return true; + } + catch + { + return false; + } + }).ToList(); + var doc = new InvoicesDoc + { + invoice = actualReceiptRequests.Select(x => CreateInvoiceDocType(x.receiptRequest, x.receiptResponse)).ToArray() + }; + return doc; + } + + public InvoicesDoc MapToInvoicesDoc(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) + { + ValidateReceiptRequest(receiptRequest); + + var inv = CreateInvoiceDocType(receiptRequest, receiptResponse); + var doc = new InvoicesDoc + { + invoice = [inv] + }; + return doc; + } + + private AadeBookInvoiceType CreateInvoiceDocType(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) + { + var invoiceDetails = GetInvoiceDetails(receiptRequest); + var incomeClassificationGroups = invoiceDetails.Where(x => x.incomeClassification != null).SelectMany(x => x.incomeClassification).Where(x => x.classificationTypeSpecified).GroupBy(x => (x.classificationCategory, x.classificationType)).Select(x => new IncomeClassificationType + { + amount = x.Sum(y => y.amount), + classificationCategory = x.Key.classificationCategory, + classificationType = x.Key.classificationType, + classificationTypeSpecified = true + }).ToList(); + incomeClassificationGroups.AddRange(invoiceDetails.Where(x => x.incomeClassification != null).SelectMany(x => x.incomeClassification).Where(x => !x.classificationTypeSpecified).GroupBy(x => x.classificationCategory).Select(x => new IncomeClassificationType + { + amount = x.Sum(y => y.amount), + classificationCategory = x.Key, + }).ToList()); + + var expensesClassificationGroups = invoiceDetails.Where(x => x.expensesClassification != null).SelectMany(x => x.expensesClassification).Where(x => x.classificationTypeSpecified & x.classificationCategorySpecified).GroupBy(x => (x.classificationCategory, x.classificationType)).Select(x => new ExpensesClassificationType + { + amount = x.Sum(y => y.amount), + classificationCategory = x.Key.classificationCategory, + classificationCategorySpecified = true, + classificationType = x.Key.classificationType, + classificationTypeSpecified = true + }).ToList(); + expensesClassificationGroups.AddRange(invoiceDetails.Where(x => x.expensesClassification != null).SelectMany(x => x.expensesClassification).Where(x => !x.classificationTypeSpecified && x.classificationCategorySpecified).GroupBy(x => x.classificationCategory).Select(x => new ExpensesClassificationType + { + amount = x.Sum(y => y.amount), + classificationCategorySpecified = true, + classificationCategory = x.Key, + }).ToList()); + expensesClassificationGroups.AddRange(invoiceDetails.Where(x => x.expensesClassification != null).SelectMany(x => x.expensesClassification).Where(x => x.classificationTypeSpecified && !x.classificationCategorySpecified).GroupBy(x => x.classificationType).Select(x => new ExpensesClassificationType + { + amount = x.Sum(y => y.amount), + classificationTypeSpecified = true, + classificationType = x.Key, + }).ToList()); + + var identification = long.Parse(receiptResponse.ftReceiptIdentification.Replace("ft", "").Split("#")[0], System.Globalization.NumberStyles.HexNumber); + var paymentMethods = GetPayments(receiptRequest); + var issuer = CreateIssuer(); + //if (receiptRequest.IsSelfPricingOperation()) + //{ + + // var customer = receiptRequest.GetCustomerOrNull(); + // issuer = new PartyType + // { + // vatNumber = customer?.CustomerVATId, + // country = CountryType.GR, + // branch = 0, + // }; + //} + + var inv = new AadeBookInvoiceType + { + issuer = issuer, + paymentMethods = [.. paymentMethods], + invoiceHeader = new InvoiceHeaderType + { + series = "0", + aa = identification.ToString(), + issueDate = receiptRequest.cbReceiptMoment, + invoiceType = AADEMappings.GetInvoiceType(receiptRequest), + selfPricing = receiptRequest.IsSelfPricingOperation(), + selfPricingSpecified = receiptRequest.IsSelfPricingOperation(), + currency = CurrencyType.EUR, + currencySpecified = true + }, + invoiceDetails = [.. invoiceDetails], + invoiceSummary = new InvoiceSummaryType + { + totalNetValue = invoiceDetails.Sum(x => x.netValue), + totalVatAmount = invoiceDetails.Sum(x => x.vatAmount), + totalWithheldAmount = invoiceDetails.Sum(x => x.withheldAmount), + totalFeesAmount = invoiceDetails.Sum(x => x.feesAmount), + totalStampDutyAmount = invoiceDetails.Sum(x => x.stampDutyAmount), + totalOtherTaxesAmount = invoiceDetails.Sum(x => x.otherTaxesAmount), + totalDeductionsAmount = invoiceDetails.Sum(x => x.deductionsAmount), + incomeClassification = [.. incomeClassificationGroups], + expensesClassification = [.. expensesClassificationGroups], + } + }; + inv.invoiceSummary.totalGrossValue = inv.invoiceSummary.totalNetValue + inv.invoiceSummary.totalVatAmount - inv.invoiceSummary.totalWithheldAmount + inv.invoiceSummary.totalFeesAmount + inv.invoiceSummary.totalStampDutyAmount + inv.invoiceSummary.totalOtherTaxesAmount - inv.invoiceSummary.totalDeductionsAmount; + if (!string.IsNullOrEmpty(receiptRequest.cbPreviousReceiptReference)) + { + inv.invoiceHeader.correlatedInvoices = [long.Parse(receiptRequest.cbPreviousReceiptReference)]; + } + AddCounterpart(receiptRequest, inv); + SetValuesIfExistent(receiptRequest, receiptResponse, inv); + return inv; + } + + private static List GetInvoiceDetails(ReceiptRequest receiptRequest) + { + return receiptRequest.cbChargeItems.Select(x => + { + var vatAmount = x.GetVATAmount(); + var invoiceRow = new InvoiceRowType + { + quantity = receiptRequest.IsRefund() ? -x.Quantity : x.Quantity, + lineNumber = (int) x.Position, + vatAmount = receiptRequest.IsRefund() ? -vatAmount : vatAmount, + netValue = receiptRequest.IsRefund() ? (-x.Amount - -vatAmount) : x.Amount - vatAmount, + vatCategory = AADEMappings.GetVATCategory(x), + }; + + if ((x.ftChargeItemCase & 0xFF00) == NatureExemptions.EndOfClimateCrisesNature) + { + invoiceRow.netValue = 0; + invoiceRow.otherTaxesAmount = x.Amount; + invoiceRow.otherTaxesAmountSpecified = true; + invoiceRow.otherTaxesPercentCategory = 9; + invoiceRow.otherTaxesPercentCategorySpecified = true; + invoiceRow.incomeClassification = []; + invoiceRow.vatCategory = 8; + } + else if (receiptRequest.IsSelfPricingOperation()) + { + if (invoiceRow.vatCategory == MyDataVatCategory.ExcludingVat) + { + invoiceRow.vatExemptionCategorySpecified = true; + invoiceRow.vatExemptionCategory = 1; + } + + if (receiptRequest.cbChargeItems.Any(x => (x.ftChargeItemCase & 0xF0) == 0x90)) + { + if (receiptRequest.cbChargeItems.Any(x => (x.ftChargeItemCase & 0xF0) == 0x90) && (x.ftChargeItemCase & 0xF0) != 0x90) + { + invoiceRow.invoiceDetailType = 2; + invoiceRow.invoiceDetailTypeSpecified = true; + invoiceRow.incomeClassification = []; + invoiceRow.expensesClassification = [ + new ExpensesClassificationType { + amount = invoiceRow.netValue, + classificationCategorySpecified = true, + + classificationCategory = ExpensesClassificationCategoryType.category2_9 + } + ]; + } + else if ((x.ftChargeItemCase & 0xF0) == 0x90) + { + invoiceRow.invoiceDetailType = 1; + invoiceRow.invoiceDetailTypeSpecified = true; + invoiceRow.expensesClassification = []; + } + } + else + { + invoiceRow.expensesClassification = [ + new ExpensesClassificationType { + amount = invoiceRow.netValue, + classificationCategorySpecified = true, + classificationType = ExpensesClassificationTypeClassificationType.E3_102_001, + classificationTypeSpecified = true, + classificationCategory = ExpensesClassificationCategoryType.category2_1 + }, + new ExpensesClassificationType { + amount = invoiceRow.netValue, + classificationType = ExpensesClassificationTypeClassificationType.VAT_361, + classificationTypeSpecified = true + }, + ]; + } + } + else if (receiptRequest.GetCasePart() == 0x0003) + { + invoiceRow.incomeClassification = []; + } + else + { + if (receiptRequest.cbChargeItems.Any(x => (x.ftChargeItemCase & 0xF0) == 0x90)) + { + if (receiptRequest.cbChargeItems.Any(x => (x.ftChargeItemCase & 0xF0) == 0x90) && (x.ftChargeItemCase & 0xF0) != 0x90) + { + invoiceRow.invoiceDetailType = 2; + invoiceRow.invoiceDetailTypeSpecified = true; + invoiceRow.incomeClassification = [ + new IncomeClassificationType { + amount = invoiceRow.netValue, + classificationCategory = AADEMappings.GetIncomeClassificationCategoryType(receiptRequest, x), + classificationType = AADEMappings.GetIncomeClassificationValueType(receiptRequest, x), + classificationTypeSpecified = true + } + ]; + } + else if ((x.ftChargeItemCase & 0xF0) == 0x90) + { + invoiceRow.invoiceDetailType = 1; + invoiceRow.invoiceDetailTypeSpecified = true; + invoiceRow.expensesClassification = [ + new ExpensesClassificationType { + amount = invoiceRow.netValue, + classificationCategorySpecified = true, + classificationCategory = ExpensesClassificationCategoryType.category2_9 + } + ]; + } + } + else + { + if (invoiceRow.vatCategory == MyDataVatCategory.ExcludingVat) + { + invoiceRow.vatExemptionCategorySpecified = true; + invoiceRow.vatExemptionCategory = 1; + } + invoiceRow.incomeClassification = [AADEMappings.GetIncomeClassificationType(receiptRequest, x)]; + } + } + if (x.ftChargeItemCaseData != null) + { + var chargeItem = JsonSerializer.Deserialize(JsonSerializer.Serialize(x.ftChargeItemCaseData)); + if (chargeItem != null) + { + invoiceRow.withheldAmountSpecified = true; + invoiceRow.withheldAmount = chargeItem.WithHoldingAmount; + invoiceRow.withheldPercentCategory = 3; + invoiceRow.withheldPercentCategorySpecified = true; + } + } + return invoiceRow; + }).ToList(); + } + + private static void SetValuesIfExistent(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse, AadeBookInvoiceType inv) + { + if (receiptResponse.ftSignatures.Count > 0) + { + var invoiceUid = receiptResponse.ftSignatures.FirstOrDefault(x => x.Caption == "invoiceUid")?.Data; + var invoiceMarkText = receiptResponse.ftSignatures.FirstOrDefault(x => x.Caption == "invoiceMark")?.Data; + var authenticationCode = receiptResponse.ftSignatures.FirstOrDefault(x => x.Caption == "authenticationCode")?.Data; + var qrCode = receiptResponse.ftSignatures.FirstOrDefault(x => x.Caption == "authenticationCode")?.Data; + if (long.TryParse(invoiceMarkText, out var invoiceMark)) + { + inv.uid = invoiceUid; + inv.authenticationCode = authenticationCode; + inv.mark = invoiceMark; + inv.markSpecified = true; + inv.qrCodeUrl = $"https://receipts-sandbox.fiskaltrust.eu/{receiptResponse.ftQueueID}/{receiptResponse.ftQueueItemID}"; + } + else + { + invoiceMark = -1; + } + + if (receiptRequest.IsLateSigning()) + { + inv.transmissionFailureSpecified = true; + inv.transmissionFailure = 1; + } + var transmissionFailure1 = receiptResponse.ftSignatures.FirstOrDefault(x => x.Caption == "Transmission Failure_1")?.Data; + if (transmissionFailure1 != null) + { + + } + + var transmissionFailure2 = receiptResponse.ftSignatures.FirstOrDefault(x => x.Caption == "Transmission Failure_2")?.Data; + if (transmissionFailure2 != null) + { + inv.transmissionFailureSpecified = true; + inv.transmissionFailure = 2; + } + } + } + + private static List GetPayments(ReceiptRequest receiptRequest) + { + return receiptRequest.cbPayItems.Where(x => (x.ftPayItemCase & ((long) 0xFF)) != 0x99).Where(x => x.ftPayItemCase != 0x4752_2000_0040_000E && x.ftPayItemCase != 0x4752_2000_0040_0004).Select(x => + { + var payment = new PaymentMethodDetailType + { + type = AADEMappings.GetPaymentType(x), + amount = receiptRequest.IsRefund() ? -x.Amount : x.Amount, + paymentMethodInfo = x.Description, + }; + var tipPayment = receiptRequest.cbPayItems.FirstOrDefault(x => (x.ftPayItemCase & 0x0000_0000_0040_0000) == 0x0000_0000_0040_0000); + if (tipPayment != null) + { + payment.tipAmount = tipPayment.Amount; + payment.tipAmountSpecified = true; + } + + if (x.ftPayItemCaseData != null) + { + var providerData = JsonSerializer.Deserialize(JsonSerializer.Serialize(x.ftPayItemCaseData)); + if (providerData != null && providerData.Provider != null && providerData.Provider.ProtocolRequest is JsonElement dat && dat.ValueKind == JsonValueKind.String) + { + var app2AppApi = JsonSerializer.Deserialize(JsonSerializer.Serialize(x.ftPayItemCaseData))!; + if (app2AppApi.Provider is PayItemCaseProviderVivaWalletApp2APp vivaAppToApp) + { + var requestUri = HttpUtility.ParseQueryString(new Uri(vivaAppToApp.ProtocolRequest).Query); + var responesUri = HttpUtility.ParseQueryString(new Uri(vivaAppToApp.ProtocolResponse).Query); + payment.transactionId = responesUri["aadeTransactionId"]; + + payment.ProvidersSignature = new ProviderSignatureType + { + Signature = requestUri["aadeProviderSignature"], + SigningAuthor = "viva.com", // need to be filled?? + }; + } + } + else if (providerData != null && providerData.Provider != null && providerData.Provider.ProtocolRequest is JsonElement datS && datS.ValueKind == JsonValueKind.Object) + { + var providerCloudRestApi = JsonSerializer.Deserialize(JsonSerializer.Serialize(x.ftPayItemCaseData))!; + if (providerCloudRestApi.Provider is PayItemCaseProviderVivaWallet vivaPayment) + { + + payment.transactionId = vivaPayment.ProtocolResponse?.aadeTransactionId; + payment.ProvidersSignature = new ProviderSignatureType + { + Signature = vivaPayment.ProtocolRequest?.aadeProviderSignature, + SigningAuthor = "viva.com", // need to be filled?? + }; + } + } + } + return payment; + }).ToList(); + } + + private void AddCounterpart(ReceiptRequest receiptRequest, AadeBookInvoiceType inv) + { + if (!receiptRequest.ContainsCustomerInfo()) + { + if (AADEMappings.RequiresCustomerInfo(inv.invoiceHeader.invoiceType)) + { + throw new Exception("Customer info is required for this invoice type"); + } + return; + } + + var customer = receiptRequest.GetCustomerOrNull(); + if (receiptRequest.HasGreeceCountryCode()) + { + inv.counterpart = new PartyType + { + vatNumber = customer?.CustomerVATId, + country = CountryType.GR, + branch = 0, + }; + if (receiptRequest.GetCasePart() == 0x0003 || inv.invoiceHeader.invoiceType == InvoiceType.Item14 || inv.invoiceHeader.invoiceType == InvoiceType.Item71) + { + inv.counterpart.address = new AddressType + { + street = customer?.CustomerStreet, + city = customer?.CustomerCity, + postalCode = customer?.CustomerZip + }; + } + } + else if (receiptRequest.HasEUCountryCode()) + { + + inv.counterpart = new PartyType + { + vatNumber = customer?.CustomerVATId, + country = customer?.CustomerCountry == "GR" ? CountryType.GR : CountryType.AT, + name = customer?.CustomerName, + address = new AddressType + { + //number = "0", + street = customer?.CustomerStreet, + city = customer?.CustomerCity, + postalCode = customer?.CustomerZip + }, + branch = 0, + }; + } + else if (receiptRequest.HasNonEUCountryCode()) + { + inv.counterpart = new PartyType + { + vatNumber = customer?.CustomerVATId, + country = CountryType.GB, + name = customer?.CustomerName, + address = new AddressType + { + street = customer?.CustomerStreet?.Replace(", United Kingdom", ""), + city = customer?.CustomerCity?.Replace(", United Kingdom", ""), + postalCode = customer?.CustomerZip + }, + branch = 0, + }; + } + } + + private PartyType CreateIssuer() + { + return new PartyType + { + vatNumber = _masterDataConfiguration.Account.VatId, + country = CountryType.GR, + branch = 0, + }; + } + + public string GetUid(AadeBookInvoiceType invoice) => BitConverter.ToString(SHA1.HashData(Encoding.UTF8.GetBytes($"{invoice.issuer.vatNumber}-{invoice.invoiceHeader.issueDate.ToString("yyyy-MM-dd")}-{invoice.issuer.branch}-{invoice.invoiceHeader.invoiceType.GetXmlEnumAttributeValueFromEnum() ?? ""}-{invoice.invoiceHeader.series}-{invoice.invoiceHeader.aa}"))).Replace("-", ""); + + public string GenerateInvoicePayload(InvoicesDoc doc) + { + var xmlSerializer = new XmlSerializer(typeof(InvoicesDoc)); + using var stringWriter = new StringWriter(); + xmlSerializer.Serialize(stringWriter, doc); + var xmlContent = stringWriter.ToString(); + return xmlContent; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/AADEMappings.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/AADEMappings.cs new file mode 100644 index 000000000..2be043264 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/AADEMappings.cs @@ -0,0 +1,382 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.myDataSCU; +using fiskaltrust.Middleware.Localization.v2.Helpers; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE; + +public static class AADEMappings +{ + public static IncomeClassificationType GetIncomeClassificationType(ReceiptRequest receiptRequest, ChargeItem chargeItem) + { + var vatAmount = chargeItem.GetVATAmount(); + var netAmount = receiptRequest.IsRefund() ? (-chargeItem.Amount - -vatAmount) : chargeItem.Amount - vatAmount; + if ((receiptRequest.ftReceiptCase & 0xFFFF) == 0x3004) + { + return new IncomeClassificationType + { + amount = netAmount, + classificationCategory = IncomeClassificationCategoryType.category1_95 + }; + } + + return new IncomeClassificationType + { + amount = netAmount, + classificationCategory = AADEMappings.GetIncomeClassificationCategoryType(receiptRequest, chargeItem), + classificationType = AADEMappings.GetIncomeClassificationValueType(receiptRequest, chargeItem), + classificationTypeSpecified = true + }; + } + + /// + /// E3_106 Intrinsic production of fixed assets -Self - deliveries - Inventory destruction / Goods + /// E3_205 Intrinsic reproduction of fixed assets -Self - production - Destruction of inventories / Raw materials and other materials + /// E3_210 Proprietary assets - Self - production - Inventory destruction / Products and production in progress + /// E3_305 Intrinsic reproduction of fixed assets -Self - production - Destruction of inventories / Raw materials and other materials + /// E3_310 Proprietary assets - Self - production - Inventory destruction / Products and production in progress + /// E3_318 Proprietary production of fixed assets -Self - deliveries - Inventory losses / Production costs + /// E3_561_001 Sales of goods and services Wholesale - Professionals + /// E3_561_002 Sales of goods and services Wholesale under article 39a par 5 of the VAT Code(Law 2859 / 2000) + /// E3_561_003 Sales of goods and services Retail - Private customers + /// E3_561_004 Retail sales of goods and services under article 39a par 5 of the VAT Code(Law 2859 / 2000) + /// E3_561_005 Foreign sales of goods and services Intra - Community sales + /// E3_561_006 Foreign sales of goods and services Third countries + /// E3_561_007 Sales of goods and services Other + /// E3_562 Other ordinary income + /// E3_563 Interest on loans and related income + /// E3_564 Credit and exchange rate differences + /// E3_565 Revenue from participations + /// E3_566 Gains on disposal of non - current assets + /// E3_567 Gains from reversal of provisions and impairments + /// E3_568 Gains from measurement at fair value + /// E3_570 Unusual income and gains + /// E3_595 Expenditure on own - account production + /// E3_596 Subsidies - Grants + /// E3_597 Grants - Grants for investment purposes - Covering expenditure + /// E3_880_001 Wholesale sales of fixed assets + /// E3_880_002 Retail sales of fixed assets + /// E3_880_003 Foreign sales of fixed assets Intra-Community sales + /// E3_880_004 Sales of Foreign Fixed Assets Third Countries + /// E3_881_001 Sales for third party accounts Wholesale + /// E3_881_002 Sales for third party accounts Retail + /// E3_881_003 Sales for third party accounts Abroad Intra - Community + /// E3_881_004 Sales for foreign account Third Countries Third Countries + /// E3_598_001 Sales of goods subject to VAT + /// E3_598_003 Sales on behalf of farmers through an agricultural cooperative, etc. + /// + public static IncomeClassificationValueType GetIncomeClassificationValueType(ReceiptRequest receiptRequest, ChargeItem chargeItem) + { + if ((receiptRequest.ftReceiptCase & 0xFFFF) == 0x0005) + { + return IncomeClassificationValueType.E3_561_001; + } + if ((receiptRequest.ftReceiptCase & 0xFFFF) == 0x3005) + { + return IncomeClassificationValueType.E3_562; + } + if ((receiptRequest.ftReceiptCase & 0xFFFF) == 0x3003) + { + return IncomeClassificationValueType.E3_595; + } + + if (chargeItem.IsAgencyBusiness()) + { + if (receiptRequest.IsReceiptOperation()) + { + if ((chargeItem.ftChargeItemCase & 0xFF00) == NatureExemptions.EndOfClimateCrisesNature) + { + return IncomeClassificationValueType.E3_881_001; + } + + if (receiptRequest.HasGreeceCountryCode()) + { + return IncomeClassificationValueType.E3_881_002; + } + else if (receiptRequest.HasEUCountryCode()) + { + return IncomeClassificationValueType.E3_881_003; + } + else + { + return IncomeClassificationValueType.E3_881_004; + + } + } + else + { + if (receiptRequest.HasGreeceCountryCode()) + { + return IncomeClassificationValueType.E3_881_001; + } + else if (receiptRequest.HasEUCountryCode()) + { + return IncomeClassificationValueType.E3_881_003; + } + else + { + throw new Exception("Agency business with non EU customer is not supported"); + } + } + } + + if (receiptRequest.IsInvoiceOperation()) + { + if (receiptRequest.HasGreeceCountryCode()) + { + return (chargeItem.ftChargeItemCase & 0xF0) switch + { + 0x00 => IncomeClassificationValueType.E3_561_001, + 0x10 => IncomeClassificationValueType.E3_561_001, + 0x20 => IncomeClassificationValueType.E3_561_001, + _ => IncomeClassificationValueType.E3_561_007, + }; + } + else if (receiptRequest.HasEUCountryCode()) + { + return IncomeClassificationValueType.E3_561_005; + } + else + { + return IncomeClassificationValueType.E3_561_006; + } + } + else if (receiptRequest.IsReceiptOperation()) + { + return (chargeItem.ftChargeItemCase & 0xF0) switch + { + 0x00 => IncomeClassificationValueType.E3_561_003, + 0x10 => IncomeClassificationValueType.E3_561_003, + 0x20 => IncomeClassificationValueType.E3_561_003, + _ => IncomeClassificationValueType.E3_561_007, + }; + } + + if ((receiptRequest.ftReceiptCase & 0xFFFF) == 0x3006) + { + return (chargeItem.ftChargeItemCase & 0xF0) switch + { + 0x00 => IncomeClassificationValueType.E3_561_001, + _ => IncomeClassificationValueType.E3_561_007, + }; + } + return (chargeItem.ftChargeItemCase & 0xF0) switch + { + 0x00 => IncomeClassificationValueType.E3_561_003, + 0x10 => IncomeClassificationValueType.E3_561_003, + 0x20 => IncomeClassificationValueType.E3_561_003, + _ => IncomeClassificationValueType.E3_561_007, + }; + } + + /// + /// The following income classifications belong to myDATA API + /// + /// category1_1 => Revenue from Sales of Goods (+ / -) + /// category1_2 => Revenue from Sales of Products (+ / -) + /// category1_3 => Revenue from Sales of Services (+ / -) + /// category1_4 => Proceeds from Sale of Assets (+ / -) + /// category1_5 => Other income/profit (+ / -) + /// category1_6 => Self-delivery / Self-use (+ / -) + /// category1_7 => Revenue for third parties (+ / -) + /// category1_8 => Revenue from previous years (+ / -) + /// category1_9 => Deferred income (+ / -) + /// category1_10 => Other revenue adjustment entries (+ / -) + /// category1_95 => Other revenue Information (+ / -) + /// category3 => Movement + /// + public static IncomeClassificationCategoryType GetIncomeClassificationCategoryType(ReceiptRequest receiptRequest, ChargeItem chargeItem) + { + if ((receiptRequest.ftReceiptCase & 0xFFFF) == 0x3003) + { + return IncomeClassificationCategoryType.category1_6; + } + + if ((receiptRequest.ftReceiptCase & 0xFFFF) == 0x3005) + { + return IncomeClassificationCategoryType.category1_5; + } + + return (chargeItem.ftChargeItemCase & 0xF0) switch + { + 0x00 => IncomeClassificationCategoryType.category1_2, + 0x10 => IncomeClassificationCategoryType.category1_2, + 0x20 => IncomeClassificationCategoryType.category1_3, + 0x60 => IncomeClassificationCategoryType.category1_7, + _ => IncomeClassificationCategoryType.category1_2, + }; + } + + public static InvoiceType GetInvoiceType(ReceiptRequest receiptRequest) + { + if (receiptRequest.IsReceiptOperation()) + { + + if (receiptRequest.GetCasePart() == 0x0003) + { + if(!string.IsNullOrEmpty(receiptRequest.ftReceiptCaseData?.ToString())) + { + return InvoiceType.Item32; + } + return InvoiceType.Item31; + } + + if (receiptRequest.GetCasePart() == 0x0005) + { + return InvoiceType.Item114; + } + + if (receiptRequest.cbChargeItems.All(x => x.IsAgencyBusiness())) + { + if (receiptRequest.cbChargeItems.Any(x => (x.ftChargeItemCase & 0xFF00) == NatureExemptions.EndOfClimateCrisesNature)) + { + return InvoiceType.Item82; + } + + return InvoiceType.Item115; + } + else if (receiptRequest.cbReceiptAmount < 100m) + { + return InvoiceType.Item113; + } + else if (receiptRequest.HasOnlyServiceItems()) + { + return InvoiceType.Item112; + } + else + { + return InvoiceType.Item111; + } + } + + if (receiptRequest.IsInvoiceOperation()) + { + if (receiptRequest.GetCasePart() == 0x1004) + { + return !string.IsNullOrEmpty(receiptRequest.cbPreviousReceiptReference) ? InvoiceType.Item51 : InvoiceType.Item52; + } + + if (receiptRequest.IsInvoiceB2COperation() && !receiptRequest.ContainsCustomerInfo()) + { + // in this case we don't know the customer so we cannot add the VAT. The invoice is handled as a Μη Αντικριζόμενα operation ( non facing) + if (receiptRequest.cbChargeItems.All(x => (x.ftChargeItemCase & 0xF0) == 0x20)) + { + return InvoiceType.Item112; + } + else + { + return InvoiceType.Item111; + } + } + if (receiptRequest.cbChargeItems.Any(x => (x.ftChargeItemCase & 0xF0) == 0x90)) + { + return InvoiceType.Item15; + } + if (receiptRequest.cbChargeItems.Any(x => x.IsAgencyBusiness())) + { + return InvoiceType.Item14; + } + else if (receiptRequest.IsInvoiceOperation() && receiptRequest.cbChargeItems.All(x => (x.ftChargeItemCase & 0xF0) == 0x20)) + { + if (!string.IsNullOrEmpty(receiptRequest.cbPreviousReceiptReference)) + { + return InvoiceType.Item24; + } + + if (receiptRequest.HasEUCountryCode()) + { + return InvoiceType.Item22; + } + else if (receiptRequest.HasNonEUCountryCode()) + { + return InvoiceType.Item23; + } + else + { + return InvoiceType.Item21; + } + } + else + { + if (!string.IsNullOrEmpty(receiptRequest.cbPreviousReceiptReference)) + { + return InvoiceType.Item16; + } + + if (receiptRequest.HasGreeceCountryCode()) + { + return InvoiceType.Item11; + } + else if (receiptRequest.HasEUCountryCode()) + { + return InvoiceType.Item12; + } + else + { + return InvoiceType.Item13; + } + } + } + + if (receiptRequest.IsProtocolOperation()) + { + switch (receiptRequest.GetCasePart()) + { + case 0x3003: + return receiptRequest.HasOnlyServiceItems() ? InvoiceType.Item62 : InvoiceType.Item61; + case 0x3004: + return receiptRequest.IsRefund() ? InvoiceType.Item85 : InvoiceType.Item84; + case 0x3005: + return InvoiceType.Item81; + case 0x3006: + return InvoiceType.Item71; + } + } + throw new Exception("Unknown type of receipt " + receiptRequest.ftReceiptCase.ToString("x")); + } + + public static int GetVATCategory(ChargeItem chargeItem) => (chargeItem.ftChargeItemCase & 0x0F) switch + { + (long) ChargeItemCaseVat.NormalVatRate => MyDataVatCategory.VatRate24, // Normal 24% + (long) ChargeItemCaseVat.DiscountedVatRate1 => MyDataVatCategory.VatRate13, // Discounted-1 13& + (long) ChargeItemCaseVat.DiscountedVatRate2 => MyDataVatCategory.VatRate6, // Discounted-2 6% + (long) ChargeItemCaseVat.SuperReducedVatRate1 => MyDataVatCategory.VatRate17, // Super reduced 1 17% + (long) ChargeItemCaseVat.SuperReducedVatRate2 => MyDataVatCategory.VatRate9, // Super reduced 2 9% + (long) ChargeItemCaseVat.ParkingVatRate => MyDataVatCategory.VatRate4, // Parking VAT 4% + (long) ChargeItemCaseVat.NotTaxable => MyDataVatCategory.RegistrationsWithoutVat, // Not Taxable + (long) ChargeItemCaseVat.ZeroVatRate => MyDataVatCategory.ExcludingVat, // Zero + _ => throw new Exception($"The VAT type {chargeItem.ftChargeItemCase & 0xF} of ChargeItem with the case {chargeItem.ftChargeItemCase} is not supported."), + }; + + public static int GetPaymentType(PayItem payItem) => (payItem.ftPayItemCase & 0xF) switch + { + (long) PayItemCases.UnknownPaymentType => MyDataPaymentMethods.Cash, + (long) PayItemCases.CashPayment => MyDataPaymentMethods.Cash, + (long) PayItemCases.NonCash => MyDataPaymentMethods.Cash, + (long) PayItemCases.CrossedCheque => MyDataPaymentMethods.Cheque, + (long) PayItemCases.DebitCardPayment => MyDataPaymentMethods.PosEPos, + (long) PayItemCases.CreditCardPayment => MyDataPaymentMethods.PosEPos, + (long) PayItemCases.VoucherPaymentCouponVoucherByMoneyValue => -1, + (long) PayItemCases.OnlinePayment => MyDataPaymentMethods.WebBanking, + (long) PayItemCases.LoyaltyProgramCustomerCardPayment => -1, + (long) PayItemCases.AccountsReceivable => -1, + (long) PayItemCases.SEPATransfer => -1, + (long) PayItemCases.OtherBankTransfer => -1, + (long) PayItemCases.TransferToCashbookVaultOwnerEmployee => -1, + (long) PayItemCases.InternalMaterialConsumption => -1, + (long) PayItemCases.Grant => MyDataPaymentMethods.OnCredit, + (long) PayItemCases.TicketRestaurant => -1, + _ => throw new Exception($"The Payment type {payItem.ftPayItemCase & 0xF} of PayItem with the case {payItem.ftPayItemCase} is not supported."), + }; + + public static bool RequiresCustomerInfo(InvoiceType invoiceType) + { + return invoiceType switch + { + InvoiceType.Item11 or InvoiceType.Item12 or InvoiceType.Item13 or InvoiceType.Item14 or InvoiceType.Item15 or InvoiceType.Item16 or InvoiceType.Item21 or InvoiceType.Item22 or InvoiceType.Item23 or InvoiceType.Item24 or InvoiceType.Item51 or InvoiceType.Item52 or InvoiceType.Item31 or InvoiceType.Item32 or InvoiceType.Item61 or InvoiceType.Item62 or InvoiceType.Item71 or InvoiceType.Item81 => true, + InvoiceType.Item82 or InvoiceType.Item84 or InvoiceType.Item85 or InvoiceType.Item111 or InvoiceType.Item112 or InvoiceType.Item113 or InvoiceType.Item114 or InvoiceType.Item115 => false, + _ => throw new NotSupportedException($"The invoice type '{invoiceType.GetXmlEnumAttributeValueFromEnum()}' is not supported"), + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/Models/WithHoldingChargeItem.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/Models/WithHoldingChargeItem.cs new file mode 100644 index 000000000..bad48cdaa --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/Models/WithHoldingChargeItem.cs @@ -0,0 +1,8 @@ +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE.Models +{ + public class WithHoldingChargeItem + { + public decimal WithHoldingPercentage { get; set; } + public decimal WithHoldingAmount { get; set; } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/NatureExemptions.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/NatureExemptions.cs new file mode 100644 index 000000000..c0d79018f --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/NatureExemptions.cs @@ -0,0 +1,7 @@ +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE +{ + public class NatureExemptions + { + public static long EndOfClimateCrisesNature = 0x1100; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/ReceiptRequestExtensions.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/ReceiptRequestExtensions.cs new file mode 100644 index 000000000..12f70aeec --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/ReceiptRequestExtensions.cs @@ -0,0 +1,91 @@ +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.SAFT.CLI; + +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE +{ + + public static class ChargeItemExtensions + { + public static bool IsAgencyBusiness(this ChargeItem chargeItem) => (chargeItem.ftChargeItemCase & 0xF0) == 0x60; + } + + public static class ReceiptRequestExtensions + { + public static bool ContainsCustomerInfo(this ReceiptRequest receiptRequest) + { + if (receiptRequest.cbCustomer != null) + { + return JsonSerializer.Deserialize(JsonSerializer.Serialize(receiptRequest.cbCustomer)) != null; + } + return false; + } + + public static MiddlewareCustomer? GetCustomerOrNull(this ReceiptRequest receiptRequest) + { + if (receiptRequest.cbCustomer != null) + { + return JsonSerializer.Deserialize(JsonSerializer.Serialize(receiptRequest.cbCustomer)); + } + return null; + } + + public static bool HasGreeceCustomer(this ReceiptRequest receiptRequest) + { + var customer = receiptRequest.GetCustomerOrNull(); + if (customer != null) + { + if (customer.CustomerCountry == "GR" || string.IsNullOrEmpty(customer.CustomerCountry)) + { + return true; + } + } + return false; + } + + public static bool HasGreeceCountryCode(this ReceiptRequest receiptRequest) + { + return ((ulong) receiptRequest.ftReceiptCase & 0xFFFF_0000_0000_0000) == 0x4752_0000_0000_0000; + } + + public static bool HasNonEUCountryCode(this ReceiptRequest receiptRequest) + { + return ((ulong) receiptRequest.ftReceiptCase & 0xFFFF_0000_0000_0000) == 0x0000_0000_0000_0000; + } + + public static bool HasOnlyServiceItems(this ReceiptRequest receiptRequest) => receiptRequest.cbChargeItems.All(x => (x.ftChargeItemCase & 0xF0) == 0x20); + + public static bool HasEUCountryCode(this ReceiptRequest receiptRequest) + { + return EU_CountryCodes.Contains((ulong) receiptRequest.ftReceiptCase & 0xFFFF_0000_0000_0000); + } + + public static List EU_CountryCodes = new List { 0x4555_0000_0000_0000, 0x4752_0000_0000_0000, 0x4154_0000_0000_0000 }; + + public static bool HasEUCustomer(this ReceiptRequest receiptRequest) + { + var customer = receiptRequest.GetCustomerOrNull(); + if (customer != null) + { + if (customer.CustomerCountry == "AT") + { + return true; + } + } + return false; + } + + public static bool HasNonEUCustomer(this ReceiptRequest receiptRequest) + { + var customer = receiptRequest.GetCustomerOrNull(); + if (customer != null) + { + if (customer.CustomerCountry == "US") + { + return true; + } + } + return false; + } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/XmlHelpers.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/XmlHelpers.cs new file mode 100644 index 000000000..b2746b7eb --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/AADE/XmlHelpers.cs @@ -0,0 +1,29 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE; + +public static class XmlHelpers +{ + public static string? GetXmlEnumAttributeValueFromEnum(this TEnum value) where TEnum : struct, IConvertible + { + var enumType = typeof(TEnum); + if (!enumType.IsEnum) + { + return null;//or string.Empty, or throw exception + } + + var member = enumType.GetMember(value.ToString() ?? "").FirstOrDefault(); + if (member == null) + { + return null;//or string.Empty, or throw exception + } + + var attribute = member.GetCustomAttributes(false).OfType().FirstOrDefault(); + if (attribute == null) + { + return null;//or string.Empty, or throw exception + } + + return attribute.Name; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/IGRSSCD.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/IGRSSCD.cs new file mode 100644 index 000000000..1bfa1c554 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/IGRSSCD.cs @@ -0,0 +1,22 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD; + +public interface IGRSSCD +{ + Task ProcessReceiptAsync(ProcessRequest request); + + Task GetInfoAsync(); +} + +public class ProcessRequest +{ + public required ReceiptRequest ReceiptRequest { get; set; } + + public required ReceiptResponse ReceiptResponse { get; set; } +} + +public class ProcessResponse +{ + public required ReceiptResponse ReceiptResponse { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/InMemorySCU.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/InMemorySCU.cs new file mode 100644 index 000000000..70d564266 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/InMemorySCU.cs @@ -0,0 +1,16 @@ +using fiskaltrust.ifPOS.v1.it; +using fiskaltrust.ifPOS.v1.me; +using Org.BouncyCastle.Asn1.Crmf; + +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD; + +public class GRSSCDInfo +{ +} + +public class InMemorySCU : IGRSSCD +{ + public Task ProcessReceiptAsync(ProcessRequest request) => throw new NotImplementedException(); + + public Task GetInfoAsync() => throw new NotImplementedException(); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/MiddlewareCustomer.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/MiddlewareCustomer.cs new file mode 100644 index 000000000..eeec5b0c6 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/MiddlewareCustomer.cs @@ -0,0 +1,13 @@ +namespace fiskaltrust.SAFT.CLI; +#pragma warning disable +public class MiddlewareCustomer +{ + public string CustomerName { get; set; } + public string CustomerId { get; set; } + public string CustomerType { get; set; } + public string CustomerStreet { get; set; } + public string CustomerZip { get; set; } + public string CustomerCity { get; set; } + public string CustomerCountry { get; set; } + public string CustomerVATId { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/InvoicesDoc-v1.0.9.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/InvoicesDoc-v1.0.9.xsd new file mode 100644 index 000000000..8d2d1dc69 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/InvoicesDoc-v1.0.9.xsd @@ -0,0 +1,1445 @@ + + + + + + + Παραστατικό ΑΑΔΕ + + + + + + + + + + + + Αναγνωριστικό Παραστατικού + + + + + Μοναδικός Αριθμός Καταχώρησης Παραστατικού + + + + + Μοναδικός Αριθμός Καταχώρησης Ακυρωτικού + + + + + Συμβολοσειρά Αυθεντικοποίησης Παρόχου + + + + + Αδυναμία Επικοινωνίας Παρόχου ή Αδυναμία διαβίβασης ERP + + + + + + + + + + + Στοιχεία Εκδότη + + + + + Στοιχεία Λήπτη + + + + + Γενικά Στοιχεία + + + + + Πληρωμές + + + + + + Στοιχεία Πληρωμών + + + + + + + + Λεπτομέρειες Παραστατικού + + + + + Σύνολα Φόρων + + + + + + + + + + Συγκεντρωτικά Στοιχεία + + + + + QR Code Url + + + + + Λοιπές Λεπτομέρειες Διακίνησης (Ορισμός - Αλλαγή Μτφ Μέσων, Μεταφορτώσεις, κλπ) + + + + + + + + + Σειρά Παραστατικού + + + + + + + + + + ΑΑ Παραστατικού + + + + + + + + + + Ημερομηνία Έκδοσης + + + + + Είδος Παραστατικού + + + + + Αναστολή Καταβολής ΦΠΑ + + + + + Νόμισμα + + + + + Ισοτιμία + + + + + Συσχετιζόμενα Παραστατικά + + + + + Ένδειξη Αυτοτιμολόγησης + + + + + Ημερομηνία Έναρξης Αποστολής + + + + + Ώρα Έναρξης Αποστολής + + + + + Αριθμός Οχήματος + + + + Αριθμός Μεταφορικού Μέσου + + + + + + + + + Σκοπός Διακίνησης + + + + + + + + + + + Παραστατικό Καυσίμων + + + + + Ειδική Κατηγορία Παραστατικού + + + + + Τύπος Απόκλισης Παραστατικού + + + + + Λοιπές συσχετιζόμενες οντοτήτες + + + + + Λοιπά Γενικά Στοιχεία Διακίνησης + + + + + Ένδειξη Παραστατικού Διακίνησης + + + + + Τίτλος της Λοιπής Αιτίας Διακίνησης + + + + + + + + + + Ένδειξη Εισπράξης Τρίτων + + + + + Πολλαπλά Συνδεόμενα MARKs + + + + + AA ΤΡΑΠΕΖΙOY (για Δελτία Παραγγελίας Εστίασης) + + + + + + + + + + Ένδειξη συνολικής αναίρεσης Δελτίων Παραγελίας + + + + + + + + + ΑΑ Γραμμής + + + + + + + + + + Είδος Γραμμής + + + + + + + + + + + Κωδικός Taric + + + + + + + + + + Κωδικός Είδους + + + + + + + + + + Περιγραφή Είδους + + + + + + + + + + Κωδικός Καυσίμου + + + + + Ποσότητα + + + + + + + + + + Είδος Ποσότητας + + + + + Επισήμανση + + + + + Καθαρή Αξία + + + + + Κατηγορία ΦΠΑ + + + + + Ποσό ΦΠΑ + + + + + Κατηγορία Αιτίας Εξαίρεσης ΦΠΑ + + + + + ΠΟΛ 1177/2018 Αρ. 27 + + + + + Δικαίωμα Έκπτωσης + + + + + Ποσό Παρ. Φόρου + + + + + Κατηγορία Συντελεστή Παρ. Φόρου + + + + + Ποσό Χαρτοσήμου + + + + + Κατηγορία Συντελεστή Χαρτοσήμου + + + + + Ποσό Τελών + + + + + Κατηγορία Συντελεστή Τελών + + + + + Κατηγορία Συντελεστή Λοιπών Φόρων + + + + + Ποσό Φόρου Διαμονης + + + + + Ποσό Κρατήσεων + + + + + Σχόλια Γραμμής + + + + Σχόλια + + + + + + + + + Λίστα Χαρακτηρισμών Εσόδων + + + + + Λίστα Χαρακτηρισμού Εξόδων + + + + + Ποσότητα Θερμοκρασίας 15 βαθμών + + + + + + + + + + Πλήθος Μονάδας Μέτρησης Τεμάχια Άλλα + + + + + Τίτλος Μονάδας Μέτρησης Τεμάχια Άλλα + + + + + + + + + + Ένδειξη μη συμμετοχής στο ΦΠΑ (έσοδα – εκροές) + + + + + + + + + Σύνολο Καθαρής Αξίας + + + + + Σύνολο ΦΠΑ + + + + + Σύνολο Παρ. Φόρων + + + + + Σύνολο Τελών + + + + + Σύνολο Χαρτοσήμου + + + + + Σύνολο Λοιπών Φόρων + + + + + Σύνολο Κρατήσεων + + + + + Συνολική Αξία + + + + + Λίστα Χαρακτηρισμών Εσόδων + + + + + + + + + + + ΑΦΜ + + + + + + + + + Κωδ. Χώρας + + + + + Αρ. Εγκατάστασης + + + + + + Επωνυμία + + + + + + + + + Διεύθυνση + + + + + + Αριθμός επίσημου εγγράφου + + + + + + + + + Αρ. Παροχής Ηλ. Ρεύματος + + + + + + + + + + Κωδ. Χώρας Έκδοσης Επίσημου Εγγράφου + + + + + + + + + + Όνομα + + + + + + + + + Αριθμός + + + + + ΤΚ + + + + + + Πόλη + + + + + + + + + + + + + Αριθμός Δήλωσης διενέργειας δραστηριότητας + + + + + Ημερομηνία Δήλωσης + + + + + + ΔΟΥ Δήλωσης + + + + + + + + + Στοιχεία Πλοίου + + + + + + + + + Τύπος Πληρωμής + + + + + + + + + + + Αναλογούν Ποσό + + + + + Λοιπές Πληροφορίες + + + + + Φιλοδώρημα + + + + + Μοναδική Ταυτότητα Πληρωμής + + + + + tid POS + + + + + + + + + + Υπογραφή Πληρωμής Παρόχου + + + + + Υπογραφή Πληρωμής ΦΗΜ με σύστημα λογισμικού (ERP) + + + + + + + + + Είδος Φόρου + + + + + + + + + + + Κατηγορία Φόρου + + + + + + + + + + Υποκείμενη Αξία + + + + + Ποσό Φόρου + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Κατηγορία + + + + + + + + + + + Στοιχεία Οντότητας + + + + + + + + + Αριθμός Μεταφορικού Μέσου + + + + + + + + + + + + + + Διεύθυνση Φόρτωσης + + + + + Διεύθυνση Παράδοσης + + + + + Εγκατάσταση έναρξης διακίνησης (Εκδότη) + + + + + Εγκατάσταση ολοκλήρωσης διακίνησης (Λήπτη) + + + + + + + + + Provider’s Id + + + + + + + + + + Υπογραφή + + + + + + + + + ECR id: Αριθμός μητρώου του φορολογικού μηχανισμού + + + + + + + + + + Μοναδικός 6-ψήφιος κωδικός που χαρακτηρίζει την κάθε συναλλαγή + + + + + + + + + + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/RequestVatInfoResponse-v1.0.9.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/RequestVatInfoResponse-v1.0.9.xsd new file mode 100644 index 000000000..2b5689b97 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/RequestVatInfoResponse-v1.0.9.xsd @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/RequestedProviderDoc-v1.0.9.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/RequestedProviderDoc-v1.0.9.xsd new file mode 100644 index 000000000..b974d4bd5 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/RequestedProviderDoc-v1.0.9.xsd @@ -0,0 +1,44 @@ + + + + + Παραστατικά από Πάροχο + + + + + + + + + + + + + + + + + + + ΑΦΜ Εκδότη + + + + + Μοναδικός Αριθμός Καταχώρησης παραστατικού Παρόχου + + + + + Αναγνωριστικό οντότητας + + + + + Συμβολοσειρά Αυθεντικοποίησης Παραστατικού Παρόχου + + + + + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/expensesClassification-v1.0.9.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/expensesClassification-v1.0.9.xsd new file mode 100644 index 000000000..bf15a1379 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/expensesClassification-v1.0.9.xsd @@ -0,0 +1,245 @@ + + + + + Χαρατηρισμοί Εξόδων Πρότυπων Παραστατικών ΑΑΔΕ + + + + + + + + + + + + Μοναδικός Αριθμός Καταχώρησης Παραστατικού + + + + + Αποδεικτικό Λήψης Χαρακτηρισμού Εξόδων. Συμπληρώνεται από την Υπηρεσία + + + + + ΑΦΜ Οντότητας Αναφοράς + + + + + + Αιτιολογία Συναλλαγής + + + + + + + + + + + + + Μέθοδος Υποβολής Χαρακτηρισμού + + + + + + + + + + + + + + + Γραμμή Παραστατικού + + + + + Λίστα Χαρακτηρισμών Εσόδων + + + + + + + + + Κωδικός Χαρακτηρισμού + + + + + + + + Κατηγορία Χαρακτηρισμού + + + + + Ποσό Χαρακτηρισμού + + + + + Πόσο Φόρου + + + + + Κατηγορία ΦΠΑ + + + + + Κατηγορία Εξαίρεσης ΦΠΑ + + + + + Μοναδικός Αριθμός Χαρακτηρισμού + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/incomeClassification-v1.0.9.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/incomeClassification-v1.0.9.xsd new file mode 100644 index 000000000..00d701444 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/incomeClassification-v1.0.9.xsd @@ -0,0 +1,145 @@ + + + + + Χαρατηρισμοί Εσόδων Πρότυπων Παραστατικών ΑΑΔΕ + + + + + + + + + + + + Μοναδικός Αριθμός Καταχώρησης Παραστατικού + + + + + Αποδεικτικό Λήψης Χαρακτηρισμού Εσόδων. Συμπληρώνεται από την Υπηρεσία + + + + + ΑΦΜ Οντότητας Αναφοράς + + + + + + Αιτιολογία Συναλλαγής + + + + + + + + + + + + + + + + + Γραμμή Παραστατικού + + + + + Λίστα Χαρακτηρισμών Εσόδων + + + + + + + + + Κωδικός Χαρακτηρισμού + + + + + Κατηγορία Χαρακτηρισμού + + + + + Ποσό Χαρακτηρισμού + + + + + Μοναδικός Αριθμός Χαρακτηρισμού + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/paymentMethods-v1.0.9.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/paymentMethods-v1.0.9.xsd new file mode 100644 index 000000000..333c6cadb --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/paymentMethods-v1.0.9.xsd @@ -0,0 +1,40 @@ + + + + + + Μέθοδοι Πληρωμής + + + + + + + + + + + + Μοναδικός Αριθμός Καταχώρησης Παραστατικού + + + + + Αποδεικτικό Λήψης Τρόπων Πληρωμής. Συμπληρώνεται από την Υπηρεσία + + + + + ΑΦΜ Οντότητας Αναφοράς + + + + + + + + + + + + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/requestedInvoicesDoc-v1.0.9.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/requestedInvoicesDoc-v1.0.9.xsd new file mode 100644 index 000000000..1b3796673 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/requestedInvoicesDoc-v1.0.9.xsd @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Μοναδικός Αριθμός Καταχώρησης του ακυρωμένου Παραστατικού + + + + + Μοναδικός Αριθμός Καταχώρησης της Ακύρωσης + + + + + Ημερομηνία Ακύρωσης Παραστατικού + + + + + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/response-v1.0.9.xsd b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/response-v1.0.9.xsd new file mode 100644 index 000000000..1624ec7f0 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/response-v1.0.9.xsd @@ -0,0 +1,127 @@ + + + + + Comment describing your root element + + + + + + + + + + + + ΑΑ γραμμής οντότητας + + + + + + + Αναγνωριστικό οντότητας + + + + + Μοναδικός Αριθμός Καταχώρησης παραστατικού + + + + + QR Code Url + + + + + Μοναδικός Αριθμός Παραλαβής Χαρακτηρισμού + + + + + Μοναδικός Αριθμός Ακύρωσης + + + + + Μοναδικός Αριθμός Παραλαβής Τρόπου Πληρωμής + + + + + Συμβολοσειρά Αυθεντικοποίησης Παρόχου + + + + + Πάροχοι Λήπτη + + + + + Email Παραλαβής + + + + + + Λίστα Σφαλμάτων + + + + + + + + + + + Κωδικός αποτελέσματος + + + + + + + + + Μήνυμα Σφάλματος + + + + + Κωδικός Σφάλαματος + + + + + + + + + Πληροφορίες Παρόχου + + + + + + + + + ΑΦΜ + + + + + + + + + Email + + + + + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/response-v1_0_9.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/response-v1_0_9.cs new file mode 100644 index 000000000..c8673cad9 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/Models/response-v1_0_9.cs @@ -0,0 +1,6537 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System.Xml.Serialization; + +// +// This source code was auto-generated by xsd, Version=4.8.3928.0. +// + + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="https://www.aade.gr/myDATA/expensesClassificaton/v1.0")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="https://www.aade.gr/myDATA/expensesClassificaton/v1.0", IsNullable=false)] +public partial class ExpensesClassificationsDoc { + + private InvoiceExpensesClassificationType[] expensesInvoiceClassificationField; + + /// + [System.Xml.Serialization.XmlElementAttribute("expensesInvoiceClassification")] + public InvoiceExpensesClassificationType[] expensesInvoiceClassification { + get { + return this.expensesInvoiceClassificationField; + } + set { + this.expensesInvoiceClassificationField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/expensesClassificaton/v1.0")] +public partial class InvoiceExpensesClassificationType { + + private long invoiceMarkField; + + private long classificationMarkField; + + private bool classificationMarkFieldSpecified; + + private string entityVatNumberField; + + private object[] itemsField; + + private sbyte classificationPostModeField; + + private bool classificationPostModeFieldSpecified; + + /// + public long invoiceMark { + get { + return this.invoiceMarkField; + } + set { + this.invoiceMarkField = value; + } + } + + /// + public long classificationMark { + get { + return this.classificationMarkField; + } + set { + this.classificationMarkField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool classificationMarkSpecified { + get { + return this.classificationMarkFieldSpecified; + } + set { + this.classificationMarkFieldSpecified = value; + } + } + + /// + public string entityVatNumber { + get { + return this.entityVatNumberField; + } + set { + this.entityVatNumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("invoicesExpensesClassificationDetails", typeof(InvoicesExpensesClassificationDetailType))] + [System.Xml.Serialization.XmlElementAttribute("transactionMode", typeof(int))] + public object[] Items { + get { + return this.itemsField; + } + set { + this.itemsField = value; + } + } + + /// + public sbyte classificationPostMode { + get { + return this.classificationPostModeField; + } + set { + this.classificationPostModeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool classificationPostModeSpecified { + get { + return this.classificationPostModeFieldSpecified; + } + set { + this.classificationPostModeFieldSpecified = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/expensesClassificaton/v1.0")] +public partial class InvoicesExpensesClassificationDetailType { + + private int lineNumberField; + + private ExpensesClassificationType[] expensesClassificationDetailDataField; + + /// + public int lineNumber { + get { + return this.lineNumberField; + } + set { + this.lineNumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("expensesClassificationDetailData")] + public ExpensesClassificationType[] expensesClassificationDetailData { + get { + return this.expensesClassificationDetailDataField; + } + set { + this.expensesClassificationDetailDataField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/expensesClassificaton/v1.0")] +public partial class ExpensesClassificationType { + + private ExpensesClassificationTypeClassificationType classificationTypeField; + + private bool classificationTypeFieldSpecified; + + private ExpensesClassificationCategoryType classificationCategoryField; + + private bool classificationCategoryFieldSpecified; + + private decimal amountField; + + private decimal vatAmountField; + + private bool vatAmountFieldSpecified; + + private int vatCategoryField; + + private bool vatCategoryFieldSpecified; + + private int vatExemptionCategoryField; + + private bool vatExemptionCategoryFieldSpecified; + + private sbyte idField; + + private bool idFieldSpecified; + + /// + public ExpensesClassificationTypeClassificationType classificationType { + get { + return this.classificationTypeField; + } + set { + this.classificationTypeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool classificationTypeSpecified { + get { + return this.classificationTypeFieldSpecified; + } + set { + this.classificationTypeFieldSpecified = value; + } + } + + /// + public ExpensesClassificationCategoryType classificationCategory { + get { + return this.classificationCategoryField; + } + set { + this.classificationCategoryField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool classificationCategorySpecified { + get { + return this.classificationCategoryFieldSpecified; + } + set { + this.classificationCategoryFieldSpecified = value; + } + } + + /// + public decimal amount { + get { + return this.amountField; + } + set { + this.amountField = value; + } + } + + /// + public decimal vatAmount { + get { + return this.vatAmountField; + } + set { + this.vatAmountField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool vatAmountSpecified { + get { + return this.vatAmountFieldSpecified; + } + set { + this.vatAmountFieldSpecified = value; + } + } + + /// + public int vatCategory { + get { + return this.vatCategoryField; + } + set { + this.vatCategoryField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool vatCategorySpecified { + get { + return this.vatCategoryFieldSpecified; + } + set { + this.vatCategoryFieldSpecified = value; + } + } + + /// + public int vatExemptionCategory { + get { + return this.vatExemptionCategoryField; + } + set { + this.vatExemptionCategoryField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool vatExemptionCategorySpecified { + get { + return this.vatExemptionCategoryFieldSpecified; + } + set { + this.vatExemptionCategoryFieldSpecified = value; + } + } + + /// + public sbyte id { + get { + return this.idField; + } + set { + this.idField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool idSpecified { + get { + return this.idFieldSpecified; + } + set { + this.idFieldSpecified = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/expensesClassificaton/v1.0")] +public enum ExpensesClassificationTypeClassificationType { + + /// + E3_101, + + /// + E3_102_001, + + /// + E3_102_002, + + /// + E3_102_003, + + /// + E3_102_004, + + /// + E3_102_005, + + /// + E3_102_006, + + /// + E3_104, + + /// + E3_201, + + /// + E3_202_001, + + /// + E3_202_002, + + /// + E3_202_003, + + /// + E3_202_004, + + /// + E3_202_005, + + /// + E3_204, + + /// + E3_207, + + /// + E3_209, + + /// + E3_301, + + /// + E3_302_001, + + /// + E3_302_002, + + /// + E3_302_003, + + /// + E3_302_004, + + /// + E3_302_005, + + /// + E3_304, + + /// + E3_307, + + /// + E3_309, + + /// + E3_312, + + /// + E3_313_001, + + /// + E3_313_002, + + /// + E3_313_003, + + /// + E3_313_004, + + /// + E3_313_005, + + /// + E3_315, + + /// + E3_581_001, + + /// + E3_581_002, + + /// + E3_581_003, + + /// + E3_582, + + /// + E3_583, + + /// + E3_584, + + /// + E3_585_001, + + /// + E3_585_002, + + /// + E3_585_003, + + /// + E3_585_004, + + /// + E3_585_005, + + /// + E3_585_006, + + /// + E3_585_007, + + /// + E3_585_008, + + /// + E3_585_009, + + /// + E3_585_010, + + /// + E3_585_011, + + /// + E3_585_012, + + /// + E3_585_013, + + /// + E3_585_014, + + /// + E3_585_015, + + /// + E3_585_016, + + /// + E3_586, + + /// + E3_587, + + /// + E3_588, + + /// + E3_589, + + /// + E3_881_001, + + /// + E3_881_002, + + /// + E3_881_003, + + /// + E3_881_004, + + /// + E3_882_001, + + /// + E3_882_002, + + /// + E3_882_003, + + /// + E3_882_004, + + /// + E3_883_001, + + /// + E3_883_002, + + /// + E3_883_003, + + /// + E3_883_004, + + /// + VAT_361, + + /// + VAT_362, + + /// + VAT_363, + + /// + VAT_364, + + /// + VAT_365, + + /// + VAT_366, + + /// + E3_103, + + /// + E3_203, + + /// + E3_303, + + /// + E3_208, + + /// + E3_308, + + /// + E3_314, + + /// + E3_106, + + /// + E3_205, + + /// + E3_305, + + /// + E3_210, + + /// + E3_310, + + /// + E3_318, + + /// + E3_598_002, + + /// + NOT_VAT_295, +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/expensesClassificaton/v1.0")] +public enum ExpensesClassificationCategoryType { + + /// + category2_1, + + /// + category2_2, + + /// + category2_3, + + /// + category2_4, + + /// + category2_5, + + /// + category2_6, + + /// + category2_7, + + /// + category2_8, + + /// + category2_9, + + /// + category2_10, + + /// + category2_11, + + /// + category2_12, + + /// + category2_13, + + /// + category2_14, + + /// + category2_95, +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="https://www.aade.gr/myDATA/incomeClassificaton/v1.0")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="https://www.aade.gr/myDATA/incomeClassificaton/v1.0", IsNullable=false)] +public partial class IncomeClassificationsDoc { + + private InvoiceIncomeClassificationType[] incomeInvoiceClassificationField; + + /// + [System.Xml.Serialization.XmlElementAttribute("incomeInvoiceClassification")] + public InvoiceIncomeClassificationType[] incomeInvoiceClassification { + get { + return this.incomeInvoiceClassificationField; + } + set { + this.incomeInvoiceClassificationField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/incomeClassificaton/v1.0")] +public partial class InvoiceIncomeClassificationType { + + private long invoiceMarkField; + + private long classificationMarkField; + + private bool classificationMarkFieldSpecified; + + private string entityVatNumberField; + + private object[] itemsField; + + /// + public long invoiceMark { + get { + return this.invoiceMarkField; + } + set { + this.invoiceMarkField = value; + } + } + + /// + public long classificationMark { + get { + return this.classificationMarkField; + } + set { + this.classificationMarkField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool classificationMarkSpecified { + get { + return this.classificationMarkFieldSpecified; + } + set { + this.classificationMarkFieldSpecified = value; + } + } + + /// + public string entityVatNumber { + get { + return this.entityVatNumberField; + } + set { + this.entityVatNumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("invoicesIncomeClassificationDetails", typeof(InvoicesIncomeClassificationDetailType))] + [System.Xml.Serialization.XmlElementAttribute("transactionMode", typeof(int))] + public object[] Items { + get { + return this.itemsField; + } + set { + this.itemsField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/incomeClassificaton/v1.0")] +public partial class InvoicesIncomeClassificationDetailType { + + private int lineNumberField; + + private IncomeClassificationType[] incomeClassificationDetailDataField; + + /// + public int lineNumber { + get { + return this.lineNumberField; + } + set { + this.lineNumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("incomeClassificationDetailData")] + public IncomeClassificationType[] incomeClassificationDetailData { + get { + return this.incomeClassificationDetailDataField; + } + set { + this.incomeClassificationDetailDataField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/incomeClassificaton/v1.0")] +public partial class IncomeClassificationType { + + private IncomeClassificationValueType classificationTypeField; + + private bool classificationTypeFieldSpecified; + + private IncomeClassificationCategoryType classificationCategoryField; + + private decimal amountField; + + private sbyte idField; + + private bool idFieldSpecified; + + /// + public IncomeClassificationValueType classificationType { + get { + return this.classificationTypeField; + } + set { + this.classificationTypeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool classificationTypeSpecified { + get { + return this.classificationTypeFieldSpecified; + } + set { + this.classificationTypeFieldSpecified = value; + } + } + + /// + public IncomeClassificationCategoryType classificationCategory { + get { + return this.classificationCategoryField; + } + set { + this.classificationCategoryField = value; + } + } + + /// + public decimal amount { + get { + return this.amountField; + } + set { + this.amountField = value; + } + } + + /// + public sbyte id { + get { + return this.idField; + } + set { + this.idField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool idSpecified { + get { + return this.idFieldSpecified; + } + set { + this.idFieldSpecified = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/incomeClassificaton/v1.0")] +public enum IncomeClassificationValueType { + + /// + E3_106, + + /// + E3_205, + + /// + E3_210, + + /// + E3_305, + + /// + E3_310, + + /// + E3_318, + + /// + E3_561_001, + + /// + E3_561_002, + + /// + E3_561_003, + + /// + E3_561_004, + + /// + E3_561_005, + + /// + E3_561_006, + + /// + E3_561_007, + + /// + E3_562, + + /// + E3_563, + + /// + E3_564, + + /// + E3_565, + + /// + E3_566, + + /// + E3_567, + + /// + E3_568, + + /// + E3_570, + + /// + E3_595, + + /// + E3_596, + + /// + E3_597, + + /// + E3_880_001, + + /// + E3_880_002, + + /// + E3_880_003, + + /// + E3_880_004, + + /// + E3_881_001, + + /// + E3_881_002, + + /// + E3_881_003, + + /// + E3_881_004, + + /// + E3_598_001, + + /// + E3_598_003, +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/incomeClassificaton/v1.0")] +public enum IncomeClassificationCategoryType { + + /// + category1_1, + + /// + category1_2, + + /// + category1_3, + + /// + category1_4, + + /// + category1_5, + + /// + category1_6, + + /// + category1_7, + + /// + category1_8, + + /// + category1_9, + + /// + category1_10, + + /// + category1_95, + + /// + category3, +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0", IsNullable=false)] +public partial class InvoicesDoc { + + private AadeBookInvoiceType[] invoiceField; + + /// + [System.Xml.Serialization.XmlElementAttribute("invoice")] + public AadeBookInvoiceType[] invoice { + get { + return this.invoiceField; + } + set { + this.invoiceField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class AadeBookInvoiceType { + + private string uidField; + + private long markField; + + private bool markFieldSpecified; + + private long cancelledByMarkField; + + private bool cancelledByMarkFieldSpecified; + + private string authenticationCodeField; + + private sbyte transmissionFailureField; + + private bool transmissionFailureFieldSpecified; + + private PartyType issuerField; + + private PartyType counterpartField; + + private InvoiceHeaderType invoiceHeaderField; + + private PaymentMethodDetailType[] paymentMethodsField; + + private InvoiceRowType[] invoiceDetailsField; + + private TaxTotalsType[] taxesTotalsField; + + private InvoiceSummaryType invoiceSummaryField; + + private string qrCodeUrlField; + + private TransportDetailType[] otherTransportDetailsField; + + /// + public string uid { + get { + return this.uidField; + } + set { + this.uidField = value; + } + } + + /// + public long mark { + get { + return this.markField; + } + set { + this.markField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool markSpecified { + get { + return this.markFieldSpecified; + } + set { + this.markFieldSpecified = value; + } + } + + /// + public long cancelledByMark { + get { + return this.cancelledByMarkField; + } + set { + this.cancelledByMarkField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool cancelledByMarkSpecified { + get { + return this.cancelledByMarkFieldSpecified; + } + set { + this.cancelledByMarkFieldSpecified = value; + } + } + + /// + public string authenticationCode { + get { + return this.authenticationCodeField; + } + set { + this.authenticationCodeField = value; + } + } + + /// + public sbyte transmissionFailure { + get { + return this.transmissionFailureField; + } + set { + this.transmissionFailureField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool transmissionFailureSpecified { + get { + return this.transmissionFailureFieldSpecified; + } + set { + this.transmissionFailureFieldSpecified = value; + } + } + + /// + public PartyType issuer { + get { + return this.issuerField; + } + set { + this.issuerField = value; + } + } + + /// + public PartyType counterpart { + get { + return this.counterpartField; + } + set { + this.counterpartField = value; + } + } + + /// + public InvoiceHeaderType invoiceHeader { + get { + return this.invoiceHeaderField; + } + set { + this.invoiceHeaderField = value; + } + } + + /// + [System.Xml.Serialization.XmlArrayItemAttribute("paymentMethodDetails", IsNullable=false)] + public PaymentMethodDetailType[] paymentMethods { + get { + return this.paymentMethodsField; + } + set { + this.paymentMethodsField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("invoiceDetails")] + public InvoiceRowType[] invoiceDetails { + get { + return this.invoiceDetailsField; + } + set { + this.invoiceDetailsField = value; + } + } + + /// + [System.Xml.Serialization.XmlArrayItemAttribute("taxes", IsNullable=false)] + public TaxTotalsType[] taxesTotals { + get { + return this.taxesTotalsField; + } + set { + this.taxesTotalsField = value; + } + } + + /// + public InvoiceSummaryType invoiceSummary { + get { + return this.invoiceSummaryField; + } + set { + this.invoiceSummaryField = value; + } + } + + /// + public string qrCodeUrl { + get { + return this.qrCodeUrlField; + } + set { + this.qrCodeUrlField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("otherTransportDetails")] + public TransportDetailType[] otherTransportDetails { + get { + return this.otherTransportDetailsField; + } + set { + this.otherTransportDetailsField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class PartyType { + + private string vatNumberField; + + private CountryType countryField; + + private int branchField; + + private string nameField; + + private AddressType addressField; + + private string documentIdNoField; + + private string supplyAccountNoField; + + private CountryType countryDocumentIdField; + + private bool countryDocumentIdFieldSpecified; + + /// + public string vatNumber { + get { + return this.vatNumberField; + } + set { + this.vatNumberField = value; + } + } + + /// + public CountryType country { + get { + return this.countryField; + } + set { + this.countryField = value; + } + } + + /// + public int branch { + get { + return this.branchField; + } + set { + this.branchField = value; + } + } + + /// + public string name { + get { + return this.nameField; + } + set { + this.nameField = value; + } + } + + /// + public AddressType address { + get { + return this.addressField; + } + set { + this.addressField = value; + } + } + + /// + public string documentIdNo { + get { + return this.documentIdNoField; + } + set { + this.documentIdNoField = value; + } + } + + /// + public string supplyAccountNo { + get { + return this.supplyAccountNoField; + } + set { + this.supplyAccountNoField = value; + } + } + + /// + public CountryType countryDocumentId { + get { + return this.countryDocumentIdField; + } + set { + this.countryDocumentIdField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool countryDocumentIdSpecified { + get { + return this.countryDocumentIdFieldSpecified; + } + set { + this.countryDocumentIdFieldSpecified = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public enum CountryType { + + /// + AD, + + /// + AE, + + /// + AF, + + /// + AG, + + /// + AI, + + /// + AL, + + /// + AM, + + /// + AN, + + /// + AO, + + /// + AQ, + + /// + AR, + + /// + AS, + + /// + AT, + + /// + AU, + + /// + AW, + + /// + AX, + + /// + AZ, + + /// + BA, + + /// + BB, + + /// + BD, + + /// + BE, + + /// + BF, + + /// + BG, + + /// + BH, + + /// + BI, + + /// + BJ, + + /// + BL, + + /// + BM, + + /// + BN, + + /// + BO, + + /// + BR, + + /// + BS, + + /// + BT, + + /// + BV, + + /// + BW, + + /// + BY, + + /// + BZ, + + /// + CA, + + /// + CC, + + /// + CD, + + /// + CF, + + /// + CG, + + /// + CH, + + /// + CI, + + /// + CK, + + /// + CL, + + /// + CM, + + /// + CN, + + /// + CO, + + /// + CR, + + /// + CU, + + /// + CV, + + /// + CX, + + /// + CY, + + /// + CZ, + + /// + DE, + + /// + DJ, + + /// + DK, + + /// + DM, + + /// + DO, + + /// + DZ, + + /// + EC, + + /// + EE, + + /// + EG, + + /// + EH, + + /// + ER, + + /// + ES, + + /// + ET, + + /// + FI, + + /// + FJ, + + /// + FK, + + /// + FM, + + /// + FO, + + /// + FR, + + /// + GA, + + /// + GB, + + /// + GD, + + /// + GE, + + /// + GF, + + /// + GG, + + /// + GH, + + /// + GI, + + /// + GL, + + /// + GM, + + /// + GN, + + /// + GP, + + /// + GQ, + + /// + GR, + + /// + GS, + + /// + GT, + + /// + GU, + + /// + GW, + + /// + GY, + + /// + HK, + + /// + HM, + + /// + HN, + + /// + HR, + + /// + HT, + + /// + HU, + + /// + ID, + + /// + IE, + + /// + IL, + + /// + IM, + + /// + IN, + + /// + IO, + + /// + IQ, + + /// + IR, + + /// + IS, + + /// + IT, + + /// + JE, + + /// + JM, + + /// + JO, + + /// + JP, + + /// + KE, + + /// + KG, + + /// + KH, + + /// + KI, + + /// + KM, + + /// + KN, + + /// + KP, + + /// + KR, + + /// + KW, + + /// + KY, + + /// + KZ, + + /// + LA, + + /// + LB, + + /// + LC, + + /// + LI, + + /// + LK, + + /// + LR, + + /// + LS, + + /// + LT, + + /// + LU, + + /// + LV, + + /// + LY, + + /// + MA, + + /// + MC, + + /// + MD, + + /// + ME, + + /// + MF, + + /// + MG, + + /// + MH, + + /// + MK, + + /// + ML, + + /// + MM, + + /// + MN, + + /// + MO, + + /// + MP, + + /// + MQ, + + /// + MR, + + /// + MS, + + /// + MT, + + /// + MU, + + /// + MV, + + /// + MW, + + /// + MX, + + /// + MY, + + /// + MZ, + + /// + NA, + + /// + NC, + + /// + NE, + + /// + NF, + + /// + NG, + + /// + NI, + + /// + NL, + + /// + NO, + + /// + NP, + + /// + NR, + + /// + NU, + + /// + NZ, + + /// + OC, + + /// + OM, + + /// + PA, + + /// + PE, + + /// + PF, + + /// + PG, + + /// + PH, + + /// + PK, + + /// + PL, + + /// + PM, + + /// + PN, + + /// + PR, + + /// + PS, + + /// + PT, + + /// + PW, + + /// + PY, + + /// + QA, + + /// + RE, + + /// + RO, + + /// + RS, + + /// + RU, + + /// + RW, + + /// + SA, + + /// + SB, + + /// + SC, + + /// + SD, + + /// + SE, + + /// + SG, + + /// + SH, + + /// + SI, + + /// + SJ, + + /// + SK, + + /// + SL, + + /// + SM, + + /// + SN, + + /// + SO, + + /// + SR, + + /// + ST, + + /// + SV, + + /// + SY, + + /// + SZ, + + /// + TC, + + /// + TD, + + /// + TF, + + /// + TG, + + /// + TH, + + /// + TJ, + + /// + TK, + + /// + TL, + + /// + TM, + + /// + TN, + + /// + TO, + + /// + TR, + + /// + TT, + + /// + TV, + + /// + TW, + + /// + TZ, + + /// + UA, + + /// + UG, + + /// + UM, + + /// + US, + + /// + UY, + + /// + UZ, + + /// + VA, + + /// + VC, + + /// + VE, + + /// + VG, + + /// + VI, + + /// + VN, + + /// + VU, + + /// + WF, + + /// + WS, + + /// + YE, + + /// + YT, + + /// + ZA, + + /// + ZM, + + /// + ZW, +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class AddressType { + + private string streetField; + + private string numberField; + + private string postalCodeField; + + private string cityField; + + /// + public string street { + get { + return this.streetField; + } + set { + this.streetField = value; + } + } + + /// + public string number { + get { + return this.numberField; + } + set { + this.numberField = value; + } + } + + /// + public string postalCode { + get { + return this.postalCodeField; + } + set { + this.postalCodeField = value; + } + } + + /// + public string city { + get { + return this.cityField; + } + set { + this.cityField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class InvoiceHeaderType { + + private string seriesField; + + private string aaField; + + private System.DateTime issueDateField; + + private InvoiceType invoiceTypeField; + + private bool vatPaymentSuspensionField; + + private bool vatPaymentSuspensionFieldSpecified; + + private CurrencyType currencyField; + + private bool currencyFieldSpecified; + + private decimal exchangeRateField; + + private bool exchangeRateFieldSpecified; + + private long[] correlatedInvoicesField; + + private bool selfPricingField; + + private bool selfPricingFieldSpecified; + + private System.DateTime dispatchDateField; + + private bool dispatchDateFieldSpecified; + + private System.DateTime dispatchTimeField; + + private bool dispatchTimeFieldSpecified; + + private string vehicleNumberField; + + private int movePurposeField; + + private bool movePurposeFieldSpecified; + + private bool fuelInvoiceField; + + private bool fuelInvoiceFieldSpecified; + + private int specialInvoiceCategoryField; + + private bool specialInvoiceCategoryFieldSpecified; + + private int invoiceVariationTypeField; + + private bool invoiceVariationTypeFieldSpecified; + + private EntityType[] otherCorrelatedEntitiesField; + + private OtherDeliveryNoteHeaderType otherDeliveryNoteHeaderField; + + private bool isDeliveryNoteField; + + private bool isDeliveryNoteFieldSpecified; + + private string otherMovePurposeTitleField; + + private bool thirdPartyCollectionField; + + private bool thirdPartyCollectionFieldSpecified; + + private long[] multipleConnectedMarksField; + + private string tableAAField; + + private bool totalCancelDeliveryOrdersField; + + private bool totalCancelDeliveryOrdersFieldSpecified; + + /// + public string series { + get { + return this.seriesField; + } + set { + this.seriesField = value; + } + } + + /// + public string aa { + get { + return this.aaField; + } + set { + this.aaField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="date")] + public System.DateTime issueDate { + get { + return this.issueDateField; + } + set { + this.issueDateField = value; + } + } + + /// + public InvoiceType invoiceType { + get { + return this.invoiceTypeField; + } + set { + this.invoiceTypeField = value; + } + } + + /// + public bool vatPaymentSuspension { + get { + return this.vatPaymentSuspensionField; + } + set { + this.vatPaymentSuspensionField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool vatPaymentSuspensionSpecified { + get { + return this.vatPaymentSuspensionFieldSpecified; + } + set { + this.vatPaymentSuspensionFieldSpecified = value; + } + } + + /// + public CurrencyType currency { + get { + return this.currencyField; + } + set { + this.currencyField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool currencySpecified { + get { + return this.currencyFieldSpecified; + } + set { + this.currencyFieldSpecified = value; + } + } + + /// + public decimal exchangeRate { + get { + return this.exchangeRateField; + } + set { + this.exchangeRateField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool exchangeRateSpecified { + get { + return this.exchangeRateFieldSpecified; + } + set { + this.exchangeRateFieldSpecified = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("correlatedInvoices")] + public long[] correlatedInvoices { + get { + return this.correlatedInvoicesField; + } + set { + this.correlatedInvoicesField = value; + } + } + + /// + public bool selfPricing { + get { + return this.selfPricingField; + } + set { + this.selfPricingField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool selfPricingSpecified { + get { + return this.selfPricingFieldSpecified; + } + set { + this.selfPricingFieldSpecified = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="date")] + public System.DateTime dispatchDate { + get { + return this.dispatchDateField; + } + set { + this.dispatchDateField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool dispatchDateSpecified { + get { + return this.dispatchDateFieldSpecified; + } + set { + this.dispatchDateFieldSpecified = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="time")] + public System.DateTime dispatchTime { + get { + return this.dispatchTimeField; + } + set { + this.dispatchTimeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool dispatchTimeSpecified { + get { + return this.dispatchTimeFieldSpecified; + } + set { + this.dispatchTimeFieldSpecified = value; + } + } + + /// + public string vehicleNumber { + get { + return this.vehicleNumberField; + } + set { + this.vehicleNumberField = value; + } + } + + /// + public int movePurpose { + get { + return this.movePurposeField; + } + set { + this.movePurposeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool movePurposeSpecified { + get { + return this.movePurposeFieldSpecified; + } + set { + this.movePurposeFieldSpecified = value; + } + } + + /// + public bool fuelInvoice { + get { + return this.fuelInvoiceField; + } + set { + this.fuelInvoiceField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool fuelInvoiceSpecified { + get { + return this.fuelInvoiceFieldSpecified; + } + set { + this.fuelInvoiceFieldSpecified = value; + } + } + + /// + public int specialInvoiceCategory { + get { + return this.specialInvoiceCategoryField; + } + set { + this.specialInvoiceCategoryField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool specialInvoiceCategorySpecified { + get { + return this.specialInvoiceCategoryFieldSpecified; + } + set { + this.specialInvoiceCategoryFieldSpecified = value; + } + } + + /// + public int invoiceVariationType { + get { + return this.invoiceVariationTypeField; + } + set { + this.invoiceVariationTypeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool invoiceVariationTypeSpecified { + get { + return this.invoiceVariationTypeFieldSpecified; + } + set { + this.invoiceVariationTypeFieldSpecified = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("otherCorrelatedEntities")] + public EntityType[] otherCorrelatedEntities { + get { + return this.otherCorrelatedEntitiesField; + } + set { + this.otherCorrelatedEntitiesField = value; + } + } + + /// + public OtherDeliveryNoteHeaderType otherDeliveryNoteHeader { + get { + return this.otherDeliveryNoteHeaderField; + } + set { + this.otherDeliveryNoteHeaderField = value; + } + } + + /// + public bool isDeliveryNote { + get { + return this.isDeliveryNoteField; + } + set { + this.isDeliveryNoteField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool isDeliveryNoteSpecified { + get { + return this.isDeliveryNoteFieldSpecified; + } + set { + this.isDeliveryNoteFieldSpecified = value; + } + } + + /// + public string otherMovePurposeTitle { + get { + return this.otherMovePurposeTitleField; + } + set { + this.otherMovePurposeTitleField = value; + } + } + + /// + public bool thirdPartyCollection { + get { + return this.thirdPartyCollectionField; + } + set { + this.thirdPartyCollectionField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool thirdPartyCollectionSpecified { + get { + return this.thirdPartyCollectionFieldSpecified; + } + set { + this.thirdPartyCollectionFieldSpecified = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("multipleConnectedMarks")] + public long[] multipleConnectedMarks { + get { + return this.multipleConnectedMarksField; + } + set { + this.multipleConnectedMarksField = value; + } + } + + /// + public string tableAA { + get { + return this.tableAAField; + } + set { + this.tableAAField = value; + } + } + + /// + public bool totalCancelDeliveryOrders { + get { + return this.totalCancelDeliveryOrdersField; + } + set { + this.totalCancelDeliveryOrdersField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool totalCancelDeliveryOrdersSpecified { + get { + return this.totalCancelDeliveryOrdersFieldSpecified; + } + set { + this.totalCancelDeliveryOrdersFieldSpecified = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public enum InvoiceType { + + /// + [System.Xml.Serialization.XmlEnumAttribute("1.1")] + Item11, + + /// + [System.Xml.Serialization.XmlEnumAttribute("1.2")] + Item12, + + /// + [System.Xml.Serialization.XmlEnumAttribute("1.3")] + Item13, + + /// + [System.Xml.Serialization.XmlEnumAttribute("1.4")] + Item14, + + /// + [System.Xml.Serialization.XmlEnumAttribute("1.5")] + Item15, + + /// + [System.Xml.Serialization.XmlEnumAttribute("1.6")] + Item16, + + /// + [System.Xml.Serialization.XmlEnumAttribute("2.1")] + Item21, + + /// + [System.Xml.Serialization.XmlEnumAttribute("2.2")] + Item22, + + /// + [System.Xml.Serialization.XmlEnumAttribute("2.3")] + Item23, + + /// + [System.Xml.Serialization.XmlEnumAttribute("2.4")] + Item24, + + /// + [System.Xml.Serialization.XmlEnumAttribute("3.1")] + Item31, + + /// + [System.Xml.Serialization.XmlEnumAttribute("3.2")] + Item32, + + /// + [System.Xml.Serialization.XmlEnumAttribute("4")] + Item4, + + /// + [System.Xml.Serialization.XmlEnumAttribute("5.1")] + Item51, + + /// + [System.Xml.Serialization.XmlEnumAttribute("5.2")] + Item52, + + /// + [System.Xml.Serialization.XmlEnumAttribute("6.1")] + Item61, + + /// + [System.Xml.Serialization.XmlEnumAttribute("6.2")] + Item62, + + /// + [System.Xml.Serialization.XmlEnumAttribute("7.1")] + Item71, + + /// + [System.Xml.Serialization.XmlEnumAttribute("8.1")] + Item81, + + /// + [System.Xml.Serialization.XmlEnumAttribute("8.2")] + Item82, + + /// + [System.Xml.Serialization.XmlEnumAttribute("8.4")] + Item84, + + /// + [System.Xml.Serialization.XmlEnumAttribute("8.5")] + Item85, + + /// + [System.Xml.Serialization.XmlEnumAttribute("8.6")] + Item86, + + /// + [System.Xml.Serialization.XmlEnumAttribute("9.3")] + Item93, + + /// + [System.Xml.Serialization.XmlEnumAttribute("11.1")] + Item111, + + /// + [System.Xml.Serialization.XmlEnumAttribute("11.2")] + Item112, + + /// + [System.Xml.Serialization.XmlEnumAttribute("11.3")] + Item113, + + /// + [System.Xml.Serialization.XmlEnumAttribute("11.4")] + Item114, + + /// + [System.Xml.Serialization.XmlEnumAttribute("11.5")] + Item115, + + /// + [System.Xml.Serialization.XmlEnumAttribute("12")] + Item121, + + /// + [System.Xml.Serialization.XmlEnumAttribute("13.1")] + Item131, + + /// + [System.Xml.Serialization.XmlEnumAttribute("13.2")] + Item132, + + /// + [System.Xml.Serialization.XmlEnumAttribute("13.3")] + Item133, + + /// + [System.Xml.Serialization.XmlEnumAttribute("13.4")] + Item134, + + /// + [System.Xml.Serialization.XmlEnumAttribute("13.30")] + Item1330, + + /// + [System.Xml.Serialization.XmlEnumAttribute("13.31")] + Item1331, + + /// + [System.Xml.Serialization.XmlEnumAttribute("14.1")] + Item141, + + /// + [System.Xml.Serialization.XmlEnumAttribute("14.2")] + Item142, + + /// + [System.Xml.Serialization.XmlEnumAttribute("14.3")] + Item143, + + /// + [System.Xml.Serialization.XmlEnumAttribute("14.4")] + Item144, + + /// + [System.Xml.Serialization.XmlEnumAttribute("14.5")] + Item145, + + /// + [System.Xml.Serialization.XmlEnumAttribute("14.30")] + Item1430, + + /// + [System.Xml.Serialization.XmlEnumAttribute("14.31")] + Item1431, + + /// + [System.Xml.Serialization.XmlEnumAttribute("15.1")] + Item151, + + /// + [System.Xml.Serialization.XmlEnumAttribute("16.1")] + Item161, + + /// + [System.Xml.Serialization.XmlEnumAttribute("17.1")] + Item171, + + /// + [System.Xml.Serialization.XmlEnumAttribute("17.2")] + Item172, + + /// + [System.Xml.Serialization.XmlEnumAttribute("17.3")] + Item173, + + /// + [System.Xml.Serialization.XmlEnumAttribute("17.4")] + Item174, + + /// + [System.Xml.Serialization.XmlEnumAttribute("17.5")] + Item175, + + /// + [System.Xml.Serialization.XmlEnumAttribute("17.6")] + Item176, +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public enum CurrencyType { + + /// + AED, + + /// + AFN, + + /// + ALL, + + /// + AMD, + + /// + ANG, + + /// + AOA, + + /// + ARS, + + /// + AUD, + + /// + AWG, + + /// + AZN, + + /// + BAM, + + /// + BBD, + + /// + BDT, + + /// + BGN, + + /// + BHD, + + /// + BIF, + + /// + BMD, + + /// + BND, + + /// + BOB, + + /// + BOV, + + /// + BRL, + + /// + BSD, + + /// + BTN, + + /// + BWP, + + /// + BYR, + + /// + BZD, + + /// + CAD, + + /// + CDF, + + /// + CHF, + + /// + CLF, + + /// + CLP, + + /// + CNY, + + /// + COP, + + /// + COU, + + /// + CRC, + + /// + CUC, + + /// + CUP, + + /// + CVE, + + /// + CZK, + + /// + DJF, + + /// + DKK, + + /// + DOP, + + /// + DZD, + + /// + EEK, + + /// + EGP, + + /// + ERN, + + /// + ETB, + + /// + EUR, + + /// + FJD, + + /// + FKP, + + /// + GBP, + + /// + GEL, + + /// + GHS, + + /// + GIP, + + /// + GMD, + + /// + GNF, + + /// + GTQ, + + /// + GWP, + + /// + GYD, + + /// + HKD, + + /// + HNL, + + /// + HRK, + + /// + HTG, + + /// + HUF, + + /// + IDR, + + /// + ILS, + + /// + INR, + + /// + IQD, + + /// + IRR, + + /// + ISK, + + /// + JMD, + + /// + JOD, + + /// + JPY, + + /// + KES, + + /// + KGS, + + /// + KHR, + + /// + KMF, + + /// + KPW, + + /// + KRW, + + /// + KWD, + + /// + KYD, + + /// + KZT, + + /// + LAK, + + /// + LBP, + + /// + LKR, + + /// + LRD, + + /// + LSL, + + /// + LTL, + + /// + LVL, + + /// + LYD, + + /// + MAD, + + /// + MDL, + + /// + MGA, + + /// + MKD, + + /// + MMK, + + /// + MNT, + + /// + MOP, + + /// + MRO, + + /// + MUR, + + /// + MVR, + + /// + MWK, + + /// + MXN, + + /// + MXV, + + /// + MYR, + + /// + MZN, + + /// + NAD, + + /// + NGN, + + /// + NIO, + + /// + NOK, + + /// + NPR, + + /// + NZD, + + /// + OMR, + + /// + PAB, + + /// + PEN, + + /// + PGK, + + /// + PHP, + + /// + PKR, + + /// + PLN, + + /// + PYG, + + /// + QAR, + + /// + RON, + + /// + RSD, + + /// + RUB, + + /// + RWF, + + /// + SAR, + + /// + SBD, + + /// + SCR, + + /// + SDG, + + /// + SEK, + + /// + SGD, + + /// + SHP, + + /// + SLL, + + /// + SOS, + + /// + SRD, + + /// + STD, + + /// + SVC, + + /// + SYP, + + /// + SZL, + + /// + THB, + + /// + TJS, + + /// + TMT, + + /// + TND, + + /// + TOP, + + /// + TRY, + + /// + TTD, + + /// + TVD, + + /// + TWD, + + /// + TZS, + + /// + UAH, + + /// + UGX, + + /// + USD, + + /// + UYU, + + /// + UZS, + + /// + VEF, + + /// + VND, + + /// + VUV, + + /// + WST, + + /// + XAF, + + /// + XCD, + + /// + XOF, + + /// + XPD, + + /// + XPF, + + /// + YER, + + /// + ZAR, + + /// + ZMK, + + /// + ZWL, +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class EntityType { + + private int typeField; + + private PartyType entityDataField; + + /// + public int type { + get { + return this.typeField; + } + set { + this.typeField = value; + } + } + + /// + public PartyType entityData { + get { + return this.entityDataField; + } + set { + this.entityDataField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class OtherDeliveryNoteHeaderType { + + private AddressType loadingAddressField; + + private AddressType deliveryAddressField; + + private int startShippingBranchField; + + private bool startShippingBranchFieldSpecified; + + private int completeShippingBranchField; + + private bool completeShippingBranchFieldSpecified; + + /// + public AddressType loadingAddress { + get { + return this.loadingAddressField; + } + set { + this.loadingAddressField = value; + } + } + + /// + public AddressType deliveryAddress { + get { + return this.deliveryAddressField; + } + set { + this.deliveryAddressField = value; + } + } + + /// + public int startShippingBranch { + get { + return this.startShippingBranchField; + } + set { + this.startShippingBranchField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool startShippingBranchSpecified { + get { + return this.startShippingBranchFieldSpecified; + } + set { + this.startShippingBranchFieldSpecified = value; + } + } + + /// + public int completeShippingBranch { + get { + return this.completeShippingBranchField; + } + set { + this.completeShippingBranchField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool completeShippingBranchSpecified { + get { + return this.completeShippingBranchFieldSpecified; + } + set { + this.completeShippingBranchFieldSpecified = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class PaymentMethodDetailType { + + private int typeField; + + private decimal amountField; + + private string paymentMethodInfoField; + + private decimal tipAmountField; + + private bool tipAmountFieldSpecified; + + private string transactionIdField; + + private string tidField; + + private ProviderSignatureType providersSignatureField; + + private ECRTokenType eCRTokenField; + + /// + public int type { + get { + return this.typeField; + } + set { + this.typeField = value; + } + } + + /// + public decimal amount { + get { + return this.amountField; + } + set { + this.amountField = value; + } + } + + /// + public string paymentMethodInfo { + get { + return this.paymentMethodInfoField; + } + set { + this.paymentMethodInfoField = value; + } + } + + /// + public decimal tipAmount { + get { + return this.tipAmountField; + } + set { + this.tipAmountField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool tipAmountSpecified { + get { + return this.tipAmountFieldSpecified; + } + set { + this.tipAmountFieldSpecified = value; + } + } + + /// + public string transactionId { + get { + return this.transactionIdField; + } + set { + this.transactionIdField = value; + } + } + + /// + public string tid { + get { + return this.tidField; + } + set { + this.tidField = value; + } + } + + /// + public ProviderSignatureType ProvidersSignature { + get { + return this.providersSignatureField; + } + set { + this.providersSignatureField = value; + } + } + + /// + public ECRTokenType ECRToken { + get { + return this.eCRTokenField; + } + set { + this.eCRTokenField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class ProviderSignatureType { + + private string signingAuthorField; + + private string signatureField; + + /// + public string SigningAuthor { + get { + return this.signingAuthorField; + } + set { + this.signingAuthorField = value; + } + } + + /// + public string Signature { + get { + return this.signatureField; + } + set { + this.signatureField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class ECRTokenType { + + private string signingAuthorField; + + private string sessionNumberField; + + /// + public string SigningAuthor { + get { + return this.signingAuthorField; + } + set { + this.signingAuthorField = value; + } + } + + /// + public string SessionNumber { + get { + return this.sessionNumberField; + } + set { + this.sessionNumberField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class InvoiceRowType { + + private int lineNumberField; + + private int recTypeField; + + private bool recTypeFieldSpecified; + + private string taricNoField; + + private string itemCodeField; + + private string itemDescrField; + + private FuelCodes fuelCodeField; + + private bool fuelCodeFieldSpecified; + + private decimal quantityField; + + private bool quantityFieldSpecified; + + private int measurementUnitField; + + private bool measurementUnitFieldSpecified; + + private int invoiceDetailTypeField; + + private bool invoiceDetailTypeFieldSpecified; + + private decimal netValueField; + + private int vatCategoryField; + + private decimal vatAmountField; + + private int vatExemptionCategoryField; + + private bool vatExemptionCategoryFieldSpecified; + + private ShipType dienergiaField; + + private bool discountOptionField; + + private bool discountOptionFieldSpecified; + + private decimal withheldAmountField; + + private bool withheldAmountFieldSpecified; + + private int withheldPercentCategoryField; + + private bool withheldPercentCategoryFieldSpecified; + + private decimal stampDutyAmountField; + + private bool stampDutyAmountFieldSpecified; + + private int stampDutyPercentCategoryField; + + private bool stampDutyPercentCategoryFieldSpecified; + + private decimal feesAmountField; + + private bool feesAmountFieldSpecified; + + private int feesPercentCategoryField; + + private bool feesPercentCategoryFieldSpecified; + + private int otherTaxesPercentCategoryField; + + private bool otherTaxesPercentCategoryFieldSpecified; + + private decimal otherTaxesAmountField; + + private bool otherTaxesAmountFieldSpecified; + + private decimal deductionsAmountField; + + private bool deductionsAmountFieldSpecified; + + private string lineCommentsField; + + private IncomeClassificationType[] incomeClassificationField; + + private ExpensesClassificationType[] expensesClassificationField; + + private decimal quantity15Field; + + private bool quantity15FieldSpecified; + + private int otherMeasurementUnitQuantityField; + + private bool otherMeasurementUnitQuantityFieldSpecified; + + private string otherMeasurementUnitTitleField; + + private bool notVAT195Field; + + private bool notVAT195FieldSpecified; + + /// + public int lineNumber { + get { + return this.lineNumberField; + } + set { + this.lineNumberField = value; + } + } + + /// + public int recType { + get { + return this.recTypeField; + } + set { + this.recTypeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool recTypeSpecified { + get { + return this.recTypeFieldSpecified; + } + set { + this.recTypeFieldSpecified = value; + } + } + + /// + public string TaricNo { + get { + return this.taricNoField; + } + set { + this.taricNoField = value; + } + } + + /// + public string itemCode { + get { + return this.itemCodeField; + } + set { + this.itemCodeField = value; + } + } + + /// + public string itemDescr { + get { + return this.itemDescrField; + } + set { + this.itemDescrField = value; + } + } + + /// + public FuelCodes fuelCode { + get { + return this.fuelCodeField; + } + set { + this.fuelCodeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool fuelCodeSpecified { + get { + return this.fuelCodeFieldSpecified; + } + set { + this.fuelCodeFieldSpecified = value; + } + } + + /// + public decimal quantity { + get { + return this.quantityField; + } + set { + this.quantityField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool quantitySpecified { + get { + return this.quantityFieldSpecified; + } + set { + this.quantityFieldSpecified = value; + } + } + + /// + public int measurementUnit { + get { + return this.measurementUnitField; + } + set { + this.measurementUnitField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool measurementUnitSpecified { + get { + return this.measurementUnitFieldSpecified; + } + set { + this.measurementUnitFieldSpecified = value; + } + } + + /// + public int invoiceDetailType { + get { + return this.invoiceDetailTypeField; + } + set { + this.invoiceDetailTypeField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool invoiceDetailTypeSpecified { + get { + return this.invoiceDetailTypeFieldSpecified; + } + set { + this.invoiceDetailTypeFieldSpecified = value; + } + } + + /// + public decimal netValue { + get { + return this.netValueField; + } + set { + this.netValueField = value; + } + } + + /// + public int vatCategory { + get { + return this.vatCategoryField; + } + set { + this.vatCategoryField = value; + } + } + + /// + public decimal vatAmount { + get { + return this.vatAmountField; + } + set { + this.vatAmountField = value; + } + } + + /// + public int vatExemptionCategory { + get { + return this.vatExemptionCategoryField; + } + set { + this.vatExemptionCategoryField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool vatExemptionCategorySpecified { + get { + return this.vatExemptionCategoryFieldSpecified; + } + set { + this.vatExemptionCategoryFieldSpecified = value; + } + } + + /// + public ShipType dienergia { + get { + return this.dienergiaField; + } + set { + this.dienergiaField = value; + } + } + + /// + public bool discountOption { + get { + return this.discountOptionField; + } + set { + this.discountOptionField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool discountOptionSpecified { + get { + return this.discountOptionFieldSpecified; + } + set { + this.discountOptionFieldSpecified = value; + } + } + + /// + public decimal withheldAmount { + get { + return this.withheldAmountField; + } + set { + this.withheldAmountField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool withheldAmountSpecified { + get { + return this.withheldAmountFieldSpecified; + } + set { + this.withheldAmountFieldSpecified = value; + } + } + + /// + public int withheldPercentCategory { + get { + return this.withheldPercentCategoryField; + } + set { + this.withheldPercentCategoryField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool withheldPercentCategorySpecified { + get { + return this.withheldPercentCategoryFieldSpecified; + } + set { + this.withheldPercentCategoryFieldSpecified = value; + } + } + + /// + public decimal stampDutyAmount { + get { + return this.stampDutyAmountField; + } + set { + this.stampDutyAmountField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool stampDutyAmountSpecified { + get { + return this.stampDutyAmountFieldSpecified; + } + set { + this.stampDutyAmountFieldSpecified = value; + } + } + + /// + public int stampDutyPercentCategory { + get { + return this.stampDutyPercentCategoryField; + } + set { + this.stampDutyPercentCategoryField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool stampDutyPercentCategorySpecified { + get { + return this.stampDutyPercentCategoryFieldSpecified; + } + set { + this.stampDutyPercentCategoryFieldSpecified = value; + } + } + + /// + public decimal feesAmount { + get { + return this.feesAmountField; + } + set { + this.feesAmountField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool feesAmountSpecified { + get { + return this.feesAmountFieldSpecified; + } + set { + this.feesAmountFieldSpecified = value; + } + } + + /// + public int feesPercentCategory { + get { + return this.feesPercentCategoryField; + } + set { + this.feesPercentCategoryField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool feesPercentCategorySpecified { + get { + return this.feesPercentCategoryFieldSpecified; + } + set { + this.feesPercentCategoryFieldSpecified = value; + } + } + + /// + public int otherTaxesPercentCategory { + get { + return this.otherTaxesPercentCategoryField; + } + set { + this.otherTaxesPercentCategoryField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool otherTaxesPercentCategorySpecified { + get { + return this.otherTaxesPercentCategoryFieldSpecified; + } + set { + this.otherTaxesPercentCategoryFieldSpecified = value; + } + } + + /// + public decimal otherTaxesAmount { + get { + return this.otherTaxesAmountField; + } + set { + this.otherTaxesAmountField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool otherTaxesAmountSpecified { + get { + return this.otherTaxesAmountFieldSpecified; + } + set { + this.otherTaxesAmountFieldSpecified = value; + } + } + + /// + public decimal deductionsAmount { + get { + return this.deductionsAmountField; + } + set { + this.deductionsAmountField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool deductionsAmountSpecified { + get { + return this.deductionsAmountFieldSpecified; + } + set { + this.deductionsAmountFieldSpecified = value; + } + } + + /// + public string lineComments { + get { + return this.lineCommentsField; + } + set { + this.lineCommentsField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("incomeClassification")] + public IncomeClassificationType[] incomeClassification { + get { + return this.incomeClassificationField; + } + set { + this.incomeClassificationField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("expensesClassification")] + public ExpensesClassificationType[] expensesClassification { + get { + return this.expensesClassificationField; + } + set { + this.expensesClassificationField = value; + } + } + + /// + public decimal quantity15 { + get { + return this.quantity15Field; + } + set { + this.quantity15Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool quantity15Specified { + get { + return this.quantity15FieldSpecified; + } + set { + this.quantity15FieldSpecified = value; + } + } + + /// + public int otherMeasurementUnitQuantity { + get { + return this.otherMeasurementUnitQuantityField; + } + set { + this.otherMeasurementUnitQuantityField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool otherMeasurementUnitQuantitySpecified { + get { + return this.otherMeasurementUnitQuantityFieldSpecified; + } + set { + this.otherMeasurementUnitQuantityFieldSpecified = value; + } + } + + /// + public string otherMeasurementUnitTitle { + get { + return this.otherMeasurementUnitTitleField; + } + set { + this.otherMeasurementUnitTitleField = value; + } + } + + /// + public bool notVAT195 { + get { + return this.notVAT195Field; + } + set { + this.notVAT195Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool notVAT195Specified { + get { + return this.notVAT195FieldSpecified; + } + set { + this.notVAT195FieldSpecified = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public enum FuelCodes { + + /// + [System.Xml.Serialization.XmlEnumAttribute("10")] + Item10, + + /// + [System.Xml.Serialization.XmlEnumAttribute("11")] + Item11, + + /// + [System.Xml.Serialization.XmlEnumAttribute("12")] + Item12, + + /// + [System.Xml.Serialization.XmlEnumAttribute("13")] + Item13, + + /// + [System.Xml.Serialization.XmlEnumAttribute("14")] + Item14, + + /// + [System.Xml.Serialization.XmlEnumAttribute("15")] + Item15, + + /// + [System.Xml.Serialization.XmlEnumAttribute("20")] + Item20, + + /// + [System.Xml.Serialization.XmlEnumAttribute("21")] + Item21, + + /// + [System.Xml.Serialization.XmlEnumAttribute("30")] + Item30, + + /// + [System.Xml.Serialization.XmlEnumAttribute("31")] + Item31, + + /// + [System.Xml.Serialization.XmlEnumAttribute("32")] + Item32, + + /// + [System.Xml.Serialization.XmlEnumAttribute("33")] + Item33, + + /// + [System.Xml.Serialization.XmlEnumAttribute("34")] + Item34, + + /// + [System.Xml.Serialization.XmlEnumAttribute("35")] + Item35, + + /// + [System.Xml.Serialization.XmlEnumAttribute("36")] + Item36, + + /// + [System.Xml.Serialization.XmlEnumAttribute("37")] + Item37, + + /// + [System.Xml.Serialization.XmlEnumAttribute("38")] + Item38, + + /// + [System.Xml.Serialization.XmlEnumAttribute("40")] + Item40, + + /// + [System.Xml.Serialization.XmlEnumAttribute("41")] + Item41, + + /// + [System.Xml.Serialization.XmlEnumAttribute("42")] + Item42, + + /// + [System.Xml.Serialization.XmlEnumAttribute("43")] + Item43, + + /// + [System.Xml.Serialization.XmlEnumAttribute("44")] + Item44, + + /// + [System.Xml.Serialization.XmlEnumAttribute("50")] + Item50, + + /// + [System.Xml.Serialization.XmlEnumAttribute("60")] + Item60, + + /// + [System.Xml.Serialization.XmlEnumAttribute("61")] + Item61, + + /// + [System.Xml.Serialization.XmlEnumAttribute("70")] + Item70, + + /// + [System.Xml.Serialization.XmlEnumAttribute("71")] + Item71, + + /// + [System.Xml.Serialization.XmlEnumAttribute("72")] + Item72, + + /// + [System.Xml.Serialization.XmlEnumAttribute("999")] + Item999, +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class ShipType { + + private string applicationIdField; + + private System.DateTime applicationDateField; + + private string doyField; + + private string shipIdField; + + /// + public string applicationId { + get { + return this.applicationIdField; + } + set { + this.applicationIdField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="date")] + public System.DateTime applicationDate { + get { + return this.applicationDateField; + } + set { + this.applicationDateField = value; + } + } + + /// + public string doy { + get { + return this.doyField; + } + set { + this.doyField = value; + } + } + + /// + public string shipId { + get { + return this.shipIdField; + } + set { + this.shipIdField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class TaxTotalsType { + + private sbyte taxTypeField; + + private int taxCategoryField; + + private bool taxCategoryFieldSpecified; + + private decimal underlyingValueField; + + private bool underlyingValueFieldSpecified; + + private decimal taxAmountField; + + private sbyte idField; + + private bool idFieldSpecified; + + /// + public sbyte taxType { + get { + return this.taxTypeField; + } + set { + this.taxTypeField = value; + } + } + + /// + public int taxCategory { + get { + return this.taxCategoryField; + } + set { + this.taxCategoryField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool taxCategorySpecified { + get { + return this.taxCategoryFieldSpecified; + } + set { + this.taxCategoryFieldSpecified = value; + } + } + + /// + public decimal underlyingValue { + get { + return this.underlyingValueField; + } + set { + this.underlyingValueField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool underlyingValueSpecified { + get { + return this.underlyingValueFieldSpecified; + } + set { + this.underlyingValueFieldSpecified = value; + } + } + + /// + public decimal taxAmount { + get { + return this.taxAmountField; + } + set { + this.taxAmountField = value; + } + } + + /// + public sbyte id { + get { + return this.idField; + } + set { + this.idField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool idSpecified { + get { + return this.idFieldSpecified; + } + set { + this.idFieldSpecified = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class InvoiceSummaryType { + + private decimal totalNetValueField; + + private decimal totalVatAmountField; + + private decimal totalWithheldAmountField; + + private decimal totalFeesAmountField; + + private decimal totalStampDutyAmountField; + + private decimal totalOtherTaxesAmountField; + + private decimal totalDeductionsAmountField; + + private decimal totalGrossValueField; + + private IncomeClassificationType[] incomeClassificationField; + + private ExpensesClassificationType[] expensesClassificationField; + + /// + public decimal totalNetValue { + get { + return this.totalNetValueField; + } + set { + this.totalNetValueField = value; + } + } + + /// + public decimal totalVatAmount { + get { + return this.totalVatAmountField; + } + set { + this.totalVatAmountField = value; + } + } + + /// + public decimal totalWithheldAmount { + get { + return this.totalWithheldAmountField; + } + set { + this.totalWithheldAmountField = value; + } + } + + /// + public decimal totalFeesAmount { + get { + return this.totalFeesAmountField; + } + set { + this.totalFeesAmountField = value; + } + } + + /// + public decimal totalStampDutyAmount { + get { + return this.totalStampDutyAmountField; + } + set { + this.totalStampDutyAmountField = value; + } + } + + /// + public decimal totalOtherTaxesAmount { + get { + return this.totalOtherTaxesAmountField; + } + set { + this.totalOtherTaxesAmountField = value; + } + } + + /// + public decimal totalDeductionsAmount { + get { + return this.totalDeductionsAmountField; + } + set { + this.totalDeductionsAmountField = value; + } + } + + /// + public decimal totalGrossValue { + get { + return this.totalGrossValueField; + } + set { + this.totalGrossValueField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("incomeClassification")] + public IncomeClassificationType[] incomeClassification { + get { + return this.incomeClassificationField; + } + set { + this.incomeClassificationField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("expensesClassification")] + public ExpensesClassificationType[] expensesClassification { + get { + return this.expensesClassificationField; + } + set { + this.expensesClassificationField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class TransportDetailType { + + private string vehicleNumberField; + + /// + public string vehicleNumber { + get { + return this.vehicleNumberField; + } + set { + this.vehicleNumberField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="https://www.aade.gr/myDATA/paymentMethod/v1.0")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="https://www.aade.gr/myDATA/paymentMethod/v1.0", IsNullable=false)] +public partial class PaymentMethodsDoc { + + private PaymentMethodType[] paymentMethodsField; + + /// + [System.Xml.Serialization.XmlElementAttribute("paymentMethods")] + public PaymentMethodType[] paymentMethods { + get { + return this.paymentMethodsField; + } + set { + this.paymentMethodsField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="https://www.aade.gr/myDATA/paymentMethod/v1.0")] +public partial class PaymentMethodType { + + private long invoiceMarkField; + + private long paymentMethodMarkField; + + private bool paymentMethodMarkFieldSpecified; + + private string entityVatNumberField; + + private PaymentMethodDetailType[] paymentMethodDetailsField; + + /// + public long invoiceMark { + get { + return this.invoiceMarkField; + } + set { + this.invoiceMarkField = value; + } + } + + /// + public long paymentMethodMark { + get { + return this.paymentMethodMarkField; + } + set { + this.paymentMethodMarkField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool paymentMethodMarkSpecified { + get { + return this.paymentMethodMarkFieldSpecified; + } + set { + this.paymentMethodMarkFieldSpecified = value; + } + } + + /// + public string entityVatNumber { + get { + return this.entityVatNumberField; + } + set { + this.entityVatNumberField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("paymentMethodDetails")] + public PaymentMethodDetailType[] paymentMethodDetails { + get { + return this.paymentMethodDetailsField; + } + set { + this.paymentMethodDetailsField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +[System.Xml.Serialization.XmlRootAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0", IsNullable=false)] +public partial class RequestedDoc { + + private continuationTokenType continuationTokenField; + + private RequestedDocInvoicesDoc invoicesDocField; + + private RequestedDocCancelledInvoicesDoc cancelledInvoicesDocField; + + private RequestedDocIncomeClassificationsDoc incomeClassificationsDocField; + + private RequestedDocExpensesClassificationsDoc expensesClassificationsDocField; + + private RequestedDocPaymentMethodsDoc paymentMethodsDocField; + + /// + public continuationTokenType continuationToken { + get { + return this.continuationTokenField; + } + set { + this.continuationTokenField = value; + } + } + + /// + public RequestedDocInvoicesDoc invoicesDoc { + get { + return this.invoicesDocField; + } + set { + this.invoicesDocField = value; + } + } + + /// + public RequestedDocCancelledInvoicesDoc cancelledInvoicesDoc { + get { + return this.cancelledInvoicesDocField; + } + set { + this.cancelledInvoicesDocField = value; + } + } + + /// + public RequestedDocIncomeClassificationsDoc incomeClassificationsDoc { + get { + return this.incomeClassificationsDocField; + } + set { + this.incomeClassificationsDocField = value; + } + } + + /// + public RequestedDocExpensesClassificationsDoc expensesClassificationsDoc { + get { + return this.expensesClassificationsDocField; + } + set { + this.expensesClassificationsDocField = value; + } + } + + /// + public RequestedDocPaymentMethodsDoc paymentMethodsDoc { + get { + return this.paymentMethodsDocField; + } + set { + this.paymentMethodsDocField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class continuationTokenType { + + private string nextPartitionKeyField; + + private string nextRowKeyField; + + /// + public string nextPartitionKey { + get { + return this.nextPartitionKeyField; + } + set { + this.nextPartitionKeyField = value; + } + } + + /// + public string nextRowKey { + get { + return this.nextRowKeyField; + } + set { + this.nextRowKeyField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class RequestedDocInvoicesDoc { + + private AadeBookInvoiceType[] invoiceField; + + /// + [System.Xml.Serialization.XmlElementAttribute("invoice")] + public AadeBookInvoiceType[] invoice { + get { + return this.invoiceField; + } + set { + this.invoiceField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class RequestedDocCancelledInvoicesDoc { + + private CancelledInvoiceType[] cancelledInvoiceField; + + /// + [System.Xml.Serialization.XmlElementAttribute("cancelledInvoice")] + public CancelledInvoiceType[] cancelledInvoice { + get { + return this.cancelledInvoiceField; + } + set { + this.cancelledInvoiceField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class CancelledInvoiceType { + + private long invoiceMarkField; + + private long cancellationMarkField; + + private System.DateTime cancellationDateField; + + /// + public long invoiceMark { + get { + return this.invoiceMarkField; + } + set { + this.invoiceMarkField = value; + } + } + + /// + public long cancellationMark { + get { + return this.cancellationMarkField; + } + set { + this.cancellationMarkField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute(DataType="date")] + public System.DateTime cancellationDate { + get { + return this.cancellationDateField; + } + set { + this.cancellationDateField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class RequestedDocIncomeClassificationsDoc { + + private InvoiceIncomeClassificationType[] incomeInvoiceClassificationField; + + /// + [System.Xml.Serialization.XmlElementAttribute("incomeInvoiceClassification")] + public InvoiceIncomeClassificationType[] incomeInvoiceClassification { + get { + return this.incomeInvoiceClassificationField; + } + set { + this.incomeInvoiceClassificationField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class RequestedDocExpensesClassificationsDoc { + + private InvoiceExpensesClassificationType[] expensesInvoiceClassificationField; + + /// + [System.Xml.Serialization.XmlElementAttribute("expensesInvoiceClassification")] + public InvoiceExpensesClassificationType[] expensesInvoiceClassification { + get { + return this.expensesInvoiceClassificationField; + } + set { + this.expensesInvoiceClassificationField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class RequestedDocPaymentMethodsDoc { + + private PaymentMethodType[] paymentMethodsField; + + /// + [System.Xml.Serialization.XmlElementAttribute("paymentMethods")] + public PaymentMethodType[] paymentMethods { + get { + return this.paymentMethodsField; + } + set { + this.paymentMethodsField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)] +[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)] +public partial class RequestedProviderDoc { + + private continuationTokenType1[] continuationTokenField; + + private InvoiceProviderType[] invoiceProviderTypeField; + + /// + [System.Xml.Serialization.XmlElementAttribute("continuationToken")] + public continuationTokenType1[] continuationToken { + get { + return this.continuationTokenField; + } + set { + this.continuationTokenField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("InvoiceProviderType")] + public InvoiceProviderType[] InvoiceProviderType { + get { + return this.invoiceProviderTypeField; + } + set { + this.invoiceProviderTypeField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(TypeName="continuationTokenType")] +public partial class continuationTokenType1 { + + private string nextPartitionKeyField; + + private string nextRowKeyField; + + /// + public string nextPartitionKey { + get { + return this.nextPartitionKeyField; + } + set { + this.nextPartitionKeyField = value; + } + } + + /// + public string nextRowKey { + get { + return this.nextRowKeyField; + } + set { + this.nextRowKeyField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +public partial class InvoiceProviderType { + + private string issuerVATField; + + private long invoiceProviderMarkField; + + private string invoiceUidField; + + private string authenticationCodeField; + + /// + public string issuerVAT { + get { + return this.issuerVATField; + } + set { + this.issuerVATField = value; + } + } + + /// + public long invoiceProviderMark { + get { + return this.invoiceProviderMarkField; + } + set { + this.invoiceProviderMarkField = value; + } + } + + /// + public string invoiceUid { + get { + return this.invoiceUidField; + } + set { + this.invoiceUidField = value; + } + } + + /// + public string authenticationCode { + get { + return this.authenticationCodeField; + } + set { + this.authenticationCodeField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +[System.Xml.Serialization.XmlRootAttribute("RequestedVatInfo", Namespace="http://www.aade.gr/myDATA/invoice/v1.0", IsNullable=false)] +public partial class RequestedVatInfoType { + + private ContinuationTokenType continuationTokenField; + + private InvoiceVatDetailType[] vatInfoField; + + /// + public ContinuationTokenType continuationToken { + get { + return this.continuationTokenField; + } + set { + this.continuationTokenField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("VatInfo")] + public InvoiceVatDetailType[] VatInfo { + get { + return this.vatInfoField; + } + set { + this.vatInfoField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class ContinuationTokenType { + + private string nextPartitionKeyField; + + private string nextRowKeyField; + + /// + public string nextPartitionKey { + get { + return this.nextPartitionKeyField; + } + set { + this.nextPartitionKeyField = value; + } + } + + /// + public string nextRowKey { + get { + return this.nextRowKeyField; + } + set { + this.nextRowKeyField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://www.aade.gr/myDATA/invoice/v1.0")] +public partial class InvoiceVatDetailType { + + private string markField; + + private bool isCancelledField; + + private bool isCancelledFieldSpecified; + + private System.DateTime issueDateField; + + private decimal vat301Field; + + private bool vat301FieldSpecified; + + private decimal vat302Field; + + private bool vat302FieldSpecified; + + private decimal vat303Field; + + private bool vat303FieldSpecified; + + private decimal vat304Field; + + private bool vat304FieldSpecified; + + private decimal vat305Field; + + private bool vat305FieldSpecified; + + private decimal vat306Field; + + private bool vat306FieldSpecified; + + private decimal vat331Field; + + private bool vat331FieldSpecified; + + private decimal vat332Field; + + private bool vat332FieldSpecified; + + private decimal vat333Field; + + private bool vat333FieldSpecified; + + private decimal vat334Field; + + private bool vat334FieldSpecified; + + private decimal vat335Field; + + private bool vat335FieldSpecified; + + private decimal vat336Field; + + private bool vat336FieldSpecified; + + private decimal vat361Field; + + private bool vat361FieldSpecified; + + private decimal vat362Field; + + private bool vat362FieldSpecified; + + private decimal vat363Field; + + private bool vat363FieldSpecified; + + private decimal vat364Field; + + private bool vat364FieldSpecified; + + private decimal vat365Field; + + private bool vat365FieldSpecified; + + private decimal vat366Field; + + private bool vat366FieldSpecified; + + private decimal vat381Field; + + private bool vat381FieldSpecified; + + private decimal vat382Field; + + private bool vat382FieldSpecified; + + private decimal vat383Field; + + private bool vat383FieldSpecified; + + private decimal vat384Field; + + private bool vat384FieldSpecified; + + private decimal vat385Field; + + private bool vat385FieldSpecified; + + private decimal vat386Field; + + private bool vat386FieldSpecified; + + private decimal vat342Field; + + private bool vat342FieldSpecified; + + private decimal vat345Field; + + private bool vat345FieldSpecified; + + private decimal vat348Field; + + private bool vat348FieldSpecified; + + private decimal vat349Field; + + private bool vat349FieldSpecified; + + private decimal vat310Field; + + private bool vat310FieldSpecified; + + private decimal vat402Field; + + private bool vat402FieldSpecified; + + private decimal vat407Field; + + private bool vat407FieldSpecified; + + private decimal vat411Field; + + private bool vat411FieldSpecified; + + private decimal vat423Field; + + private bool vat423FieldSpecified; + + private decimal vat422Field; + + private bool vat422FieldSpecified; + + private decimal vatUnclassified361Field; + + private bool vatUnclassified361FieldSpecified; + + private decimal vatUnclassified381Field; + + private bool vatUnclassified381FieldSpecified; + + /// + public string Mark { + get { + return this.markField; + } + set { + this.markField = value; + } + } + + /// + public bool IsCancelled { + get { + return this.isCancelledField; + } + set { + this.isCancelledField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool IsCancelledSpecified { + get { + return this.isCancelledFieldSpecified; + } + set { + this.isCancelledFieldSpecified = value; + } + } + + /// + public System.DateTime IssueDate { + get { + return this.issueDateField; + } + set { + this.issueDateField = value; + } + } + + /// + public decimal Vat301 { + get { + return this.vat301Field; + } + set { + this.vat301Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat301Specified { + get { + return this.vat301FieldSpecified; + } + set { + this.vat301FieldSpecified = value; + } + } + + /// + public decimal Vat302 { + get { + return this.vat302Field; + } + set { + this.vat302Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat302Specified { + get { + return this.vat302FieldSpecified; + } + set { + this.vat302FieldSpecified = value; + } + } + + /// + public decimal Vat303 { + get { + return this.vat303Field; + } + set { + this.vat303Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat303Specified { + get { + return this.vat303FieldSpecified; + } + set { + this.vat303FieldSpecified = value; + } + } + + /// + public decimal Vat304 { + get { + return this.vat304Field; + } + set { + this.vat304Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat304Specified { + get { + return this.vat304FieldSpecified; + } + set { + this.vat304FieldSpecified = value; + } + } + + /// + public decimal Vat305 { + get { + return this.vat305Field; + } + set { + this.vat305Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat305Specified { + get { + return this.vat305FieldSpecified; + } + set { + this.vat305FieldSpecified = value; + } + } + + /// + public decimal Vat306 { + get { + return this.vat306Field; + } + set { + this.vat306Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat306Specified { + get { + return this.vat306FieldSpecified; + } + set { + this.vat306FieldSpecified = value; + } + } + + /// + public decimal Vat331 { + get { + return this.vat331Field; + } + set { + this.vat331Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat331Specified { + get { + return this.vat331FieldSpecified; + } + set { + this.vat331FieldSpecified = value; + } + } + + /// + public decimal Vat332 { + get { + return this.vat332Field; + } + set { + this.vat332Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat332Specified { + get { + return this.vat332FieldSpecified; + } + set { + this.vat332FieldSpecified = value; + } + } + + /// + public decimal Vat333 { + get { + return this.vat333Field; + } + set { + this.vat333Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat333Specified { + get { + return this.vat333FieldSpecified; + } + set { + this.vat333FieldSpecified = value; + } + } + + /// + public decimal Vat334 { + get { + return this.vat334Field; + } + set { + this.vat334Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat334Specified { + get { + return this.vat334FieldSpecified; + } + set { + this.vat334FieldSpecified = value; + } + } + + /// + public decimal Vat335 { + get { + return this.vat335Field; + } + set { + this.vat335Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat335Specified { + get { + return this.vat335FieldSpecified; + } + set { + this.vat335FieldSpecified = value; + } + } + + /// + public decimal Vat336 { + get { + return this.vat336Field; + } + set { + this.vat336Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat336Specified { + get { + return this.vat336FieldSpecified; + } + set { + this.vat336FieldSpecified = value; + } + } + + /// + public decimal Vat361 { + get { + return this.vat361Field; + } + set { + this.vat361Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat361Specified { + get { + return this.vat361FieldSpecified; + } + set { + this.vat361FieldSpecified = value; + } + } + + /// + public decimal Vat362 { + get { + return this.vat362Field; + } + set { + this.vat362Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat362Specified { + get { + return this.vat362FieldSpecified; + } + set { + this.vat362FieldSpecified = value; + } + } + + /// + public decimal Vat363 { + get { + return this.vat363Field; + } + set { + this.vat363Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat363Specified { + get { + return this.vat363FieldSpecified; + } + set { + this.vat363FieldSpecified = value; + } + } + + /// + public decimal Vat364 { + get { + return this.vat364Field; + } + set { + this.vat364Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat364Specified { + get { + return this.vat364FieldSpecified; + } + set { + this.vat364FieldSpecified = value; + } + } + + /// + public decimal Vat365 { + get { + return this.vat365Field; + } + set { + this.vat365Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat365Specified { + get { + return this.vat365FieldSpecified; + } + set { + this.vat365FieldSpecified = value; + } + } + + /// + public decimal Vat366 { + get { + return this.vat366Field; + } + set { + this.vat366Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat366Specified { + get { + return this.vat366FieldSpecified; + } + set { + this.vat366FieldSpecified = value; + } + } + + /// + public decimal Vat381 { + get { + return this.vat381Field; + } + set { + this.vat381Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat381Specified { + get { + return this.vat381FieldSpecified; + } + set { + this.vat381FieldSpecified = value; + } + } + + /// + public decimal Vat382 { + get { + return this.vat382Field; + } + set { + this.vat382Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat382Specified { + get { + return this.vat382FieldSpecified; + } + set { + this.vat382FieldSpecified = value; + } + } + + /// + public decimal Vat383 { + get { + return this.vat383Field; + } + set { + this.vat383Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat383Specified { + get { + return this.vat383FieldSpecified; + } + set { + this.vat383FieldSpecified = value; + } + } + + /// + public decimal Vat384 { + get { + return this.vat384Field; + } + set { + this.vat384Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat384Specified { + get { + return this.vat384FieldSpecified; + } + set { + this.vat384FieldSpecified = value; + } + } + + /// + public decimal Vat385 { + get { + return this.vat385Field; + } + set { + this.vat385Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat385Specified { + get { + return this.vat385FieldSpecified; + } + set { + this.vat385FieldSpecified = value; + } + } + + /// + public decimal Vat386 { + get { + return this.vat386Field; + } + set { + this.vat386Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat386Specified { + get { + return this.vat386FieldSpecified; + } + set { + this.vat386FieldSpecified = value; + } + } + + /// + public decimal Vat342 { + get { + return this.vat342Field; + } + set { + this.vat342Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat342Specified { + get { + return this.vat342FieldSpecified; + } + set { + this.vat342FieldSpecified = value; + } + } + + /// + public decimal Vat345 { + get { + return this.vat345Field; + } + set { + this.vat345Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat345Specified { + get { + return this.vat345FieldSpecified; + } + set { + this.vat345FieldSpecified = value; + } + } + + /// + public decimal Vat348 { + get { + return this.vat348Field; + } + set { + this.vat348Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat348Specified { + get { + return this.vat348FieldSpecified; + } + set { + this.vat348FieldSpecified = value; + } + } + + /// + public decimal Vat349 { + get { + return this.vat349Field; + } + set { + this.vat349Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat349Specified { + get { + return this.vat349FieldSpecified; + } + set { + this.vat349FieldSpecified = value; + } + } + + /// + public decimal Vat310 { + get { + return this.vat310Field; + } + set { + this.vat310Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat310Specified { + get { + return this.vat310FieldSpecified; + } + set { + this.vat310FieldSpecified = value; + } + } + + /// + public decimal Vat402 { + get { + return this.vat402Field; + } + set { + this.vat402Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat402Specified { + get { + return this.vat402FieldSpecified; + } + set { + this.vat402FieldSpecified = value; + } + } + + /// + public decimal Vat407 { + get { + return this.vat407Field; + } + set { + this.vat407Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat407Specified { + get { + return this.vat407FieldSpecified; + } + set { + this.vat407FieldSpecified = value; + } + } + + /// + public decimal Vat411 { + get { + return this.vat411Field; + } + set { + this.vat411Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat411Specified { + get { + return this.vat411FieldSpecified; + } + set { + this.vat411FieldSpecified = value; + } + } + + /// + public decimal Vat423 { + get { + return this.vat423Field; + } + set { + this.vat423Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat423Specified { + get { + return this.vat423FieldSpecified; + } + set { + this.vat423FieldSpecified = value; + } + } + + /// + public decimal Vat422 { + get { + return this.vat422Field; + } + set { + this.vat422Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool Vat422Specified { + get { + return this.vat422FieldSpecified; + } + set { + this.vat422FieldSpecified = value; + } + } + + /// + public decimal VatUnclassified361 { + get { + return this.vatUnclassified361Field; + } + set { + this.vatUnclassified361Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool VatUnclassified361Specified { + get { + return this.vatUnclassified361FieldSpecified; + } + set { + this.vatUnclassified361FieldSpecified = value; + } + } + + /// + public decimal VatUnclassified381 { + get { + return this.vatUnclassified381Field; + } + set { + this.vatUnclassified381Field = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool VatUnclassified381Specified { + get { + return this.vatUnclassified381FieldSpecified; + } + set { + this.vatUnclassified381FieldSpecified = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)] +[System.Xml.Serialization.XmlRootAttribute(Namespace="", IsNullable=false)] +public partial class ResponseDoc { + + private ResponseType[] responseField; + + /// + [System.Xml.Serialization.XmlElementAttribute("response")] + public ResponseType[] response { + get { + return this.responseField; + } + set { + this.responseField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +public partial class ResponseType { + + private int indexField; + + private bool indexFieldSpecified; + + private object[] itemsField; + + private ItemsChoiceType[] itemsElementNameField; + + private string statusCodeField; + + /// + public int index { + get { + return this.indexField; + } + set { + this.indexField = value; + } + } + + /// + [System.Xml.Serialization.XmlIgnoreAttribute()] + public bool indexSpecified { + get { + return this.indexFieldSpecified; + } + set { + this.indexFieldSpecified = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("authenticationCode", typeof(string))] + [System.Xml.Serialization.XmlElementAttribute("cancellationMark", typeof(long))] + [System.Xml.Serialization.XmlElementAttribute("classificationMark", typeof(long))] + [System.Xml.Serialization.XmlElementAttribute("errors", typeof(ResponseTypeErrors))] + [System.Xml.Serialization.XmlElementAttribute("invoiceMark", typeof(long))] + [System.Xml.Serialization.XmlElementAttribute("invoiceUid", typeof(string))] + [System.Xml.Serialization.XmlElementAttribute("paymentMethodMark", typeof(long))] + [System.Xml.Serialization.XmlElementAttribute("qrUrl", typeof(string))] + [System.Xml.Serialization.XmlElementAttribute("receptionEmails", typeof(receptionEmailsType))] + [System.Xml.Serialization.XmlElementAttribute("receptionProviders", typeof(receptionProvidersType))] + [System.Xml.Serialization.XmlChoiceIdentifierAttribute("ItemsElementName")] + public object[] Items { + get { + return this.itemsField; + } + set { + this.itemsField = value; + } + } + + /// + [System.Xml.Serialization.XmlElementAttribute("ItemsElementName")] + [System.Xml.Serialization.XmlIgnoreAttribute()] + public ItemsChoiceType[] ItemsElementName { + get { + return this.itemsElementNameField; + } + set { + this.itemsElementNameField = value; + } + } + + /// + public string statusCode { + get { + return this.statusCodeField; + } + set { + this.statusCodeField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true)] +public partial class ResponseTypeErrors { + + private ErrorType[] errorField; + + /// + [System.Xml.Serialization.XmlElementAttribute("error")] + public ErrorType[] error { + get { + return this.errorField; + } + set { + this.errorField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +public partial class ErrorType { + + private string messageField; + + private string codeField; + + /// + public string message { + get { + return this.messageField; + } + set { + this.messageField = value; + } + } + + /// + public string code { + get { + return this.codeField; + } + set { + this.codeField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +public partial class receptionEmailsType { + + private string[] emailField; + + /// + [System.Xml.Serialization.XmlElementAttribute("email")] + public string[] email { + get { + return this.emailField; + } + set { + this.emailField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +public partial class receptionProvidersType { + + private ProviderInfoType[] providerInfoField; + + /// + [System.Xml.Serialization.XmlElementAttribute("ProviderInfo")] + public ProviderInfoType[] ProviderInfo { + get { + return this.providerInfoField; + } + set { + this.providerInfoField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Diagnostics.DebuggerStepThroughAttribute()] +[System.ComponentModel.DesignerCategoryAttribute("code")] +public partial class ProviderInfoType { + + private string[] vATNumberField; + + /// + [System.Xml.Serialization.XmlElementAttribute("VATNumber")] + public string[] VATNumber { + get { + return this.vATNumberField; + } + set { + this.vATNumberField = value; + } + } +} + +/// +[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.8.3928.0")] +[System.SerializableAttribute()] +[System.Xml.Serialization.XmlTypeAttribute(IncludeInSchema=false)] +public enum ItemsChoiceType { + + /// + authenticationCode, + + /// + cancellationMark, + + /// + classificationMark, + + /// + errors, + + /// + invoiceMark, + + /// + invoiceUid, + + /// + paymentMethodMark, + + /// + qrUrl, + + /// + receptionEmails, + + /// + receptionProviders, +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/MyDataApiClient.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/MyDataApiClient.cs new file mode 100644 index 000000000..5fe973570 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/MyDataApiClient.cs @@ -0,0 +1,189 @@ +using System.Net.Http.Headers; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using System.Xml.Serialization; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE; +using fiskaltrust.Middleware.Localization.QueueGR.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using Org.BouncyCastle.Asn1.Ocsp; + +#pragma warning disable +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.myDataSCU; + +public class MyDataApiClient : IGRSSCD +{ + private readonly HttpClient _httpClient; + private readonly string _prodBaseUrl = "https://mydataapi.aade.gr/"; + private readonly string _devBaseUrl = "https://mydataapidev.aade.gr/"; + + private readonly bool _iseinvoiceProvider; + + public static MyDataApiClient CreateClient(Dictionary configuration) + { + var iseinvoiceProvider = false; + if (configuration.TryGetValue("iseinvoiceProvider", out var data) && bool.TryParse(data?.ToString(), out iseinvoiceProvider)) + { } + return new MyDataApiClient(configuration["aade-user-id"].ToString(), configuration["ocp-apim-subscription-key"].ToString(), iseinvoiceProvider); + } + + public MyDataApiClient(string username, string subscriptionKey, bool iseinvoiceProvider) + { + _iseinvoiceProvider = iseinvoiceProvider; + _httpClient = new HttpClient() + { + BaseAddress = new Uri(_devBaseUrl) + }; + _httpClient.DefaultRequestHeaders.Add("aade-user-id", username); + _httpClient.DefaultRequestHeaders.Add("ocp-apim-subscription-key", subscriptionKey); + } + + public async Task GetInfoAsync() => await Task.FromResult(new GRSSCDInfo()); + + public async Task ProcessReceiptAsync(ProcessRequest request) + { + var aadFactory = new AADEFactory(new storage.V0.MasterData.MasterDataConfiguration + { + Account = new storage.V0.MasterData.AccountMasterData + { + VatId = "112545020" + } + }); + var doc = aadFactory.MapToInvoicesDoc(request.ReceiptRequest, request.ReceiptResponse); + if (request.ReceiptRequest.IsLateSigning()) + { + foreach (var item in doc.invoice) + { + item.transmissionFailureSpecified = true; + item.transmissionFailure = 1; + } + + request.ReceiptResponse.AddSignatureItem(new SignatureItem + { + Data = $"Απώλεια Διασύνδεσης Οντότητας - Παρόχου", + Caption = "Transmission Failure_1", + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = (long) SignatureTypesGR.MyDataInfo + }); + } + + var payload = aadFactory.GenerateInvoicePayload(doc); + var path = _iseinvoiceProvider ? "/myDataProvider/SendInvoices" : "/SendReceipts"; + var response = await _httpClient.PostAsync(path, new StringContent(payload, Encoding.UTF8, "application/xml")); + var content = await response.Content.ReadAsStringAsync(); + if((int) response.StatusCode >= 500) + { + foreach (var item in doc.invoice) + { + item.transmissionFailureSpecified = true; + item.transmissionFailure = 2; + } + if (_iseinvoiceProvider) + { + request.ReceiptResponse.AddSignatureItem(CreateGRQRCode($"https://receipts-sandbox.fiskaltrust.eu/{request.ReceiptResponse.ftQueueID}/{request.ReceiptResponse.ftQueueItemID}")); + } + request.ReceiptResponse.AddSignatureItem(new SignatureItem + { + Data = $"Απώλεια Διασύνδεσης Παρόχου – ΑΑΔΕ", + Caption = "Transmission Failure_2", + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = (long) SignatureTypesGR.MyDataInfo + }); + return new ProcessResponse + { + ReceiptResponse = request.ReceiptResponse + }; + } + if (response.IsSuccessStatusCode) + { + var ersult = GetResponse(content); + if (ersult != null) + { + var data = ersult.response[0]; + if (data.statusCode.ToLower() == "success") + { + for (var i = 0; i < data.ItemsElementName.Length; i++) + { + if (data.ItemsElementName[i] == ItemsChoiceType.qrUrl) + { + request.ReceiptResponse.AddSignatureItem(CreateGRQRCode(data.Items[i].ToString())); + } + else + { + request.ReceiptResponse.AddSignatureItem(new SignatureItem + { + Data = data.Items[i].ToString(), + Caption = data.ItemsElementName[i].ToString(), + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = (long) SignatureTypesGR.MyDataInfo + }); + } + } + if (_iseinvoiceProvider) + { + request.ReceiptResponse.AddSignatureItem(CreateGRQRCode($"https://receipts-sandbox.fiskaltrust.eu/{request.ReceiptResponse.ftQueueID}/{request.ReceiptResponse.ftQueueItemID}")); + } + + request.ReceiptResponse.ftReceiptIdentification += $"{doc.invoice[0].invoiceHeader.series}-{doc.invoice[0].invoiceHeader.aa}"; + + request.ReceiptResponse.AddSignatureItem(new SignatureItem + { + Data = $"{doc.invoice[0].issuer.vatNumber}|{doc.invoice[0].invoiceHeader.issueDate.ToString("dd/MM/yyyy")}|{doc.invoice[0].issuer.branch}|{doc.invoice[0].invoiceHeader.invoiceType}|{doc.invoice[0].invoiceHeader.series}|{doc.invoice[0].invoiceHeader.aa}", + Caption = "Μοναδικός αριιθμός παραστατικού", + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = (long) SignatureTypesGR.MyDataInfo + }); + } + else + { + var errors = data.Items.Cast().SelectMany(x => x.error); + request.ReceiptResponse.SetReceiptResponseError(JsonSerializer.Serialize(new AADEEErrorResponse + { + AADEError = data.statusCode, + Errors = errors.ToList() + })); + } + } + else + { + request.ReceiptResponse.SetReceiptResponseError(content); + } + } + else + { + request.ReceiptResponse.SetReceiptResponseError(content); + } + + + return new ProcessResponse + { + ReceiptResponse = request.ReceiptResponse + }; + } + + public class AADEEErrorResponse + { + public string AADEError { get; set; } + public List Errors { get; set; } + } + + public static SignatureItem CreateGRQRCode(string qrCode) + { + return new SignatureItem() + { + Caption = "[www.fiskaltrust.gr]", + Data = qrCode, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.QR_Code, + ftSignatureType = (long) SignatureTypesGR.PosReceipt + }; + } + + public ResponseDoc GetResponse(string xmlContent) + { + var xmlSerializer = new XmlSerializer(typeof(ResponseDoc)); + using var stringReader = new StringReader(xmlContent); + return (ResponseDoc) xmlSerializer.Deserialize(stringReader); + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/MyDataPaymentMethods.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/MyDataPaymentMethods.cs new file mode 100644 index 000000000..57cf9452c --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/MyDataPaymentMethods.cs @@ -0,0 +1,14 @@ +#pragma warning disable +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.myDataSCU; + +public class MyDataPaymentMethods +{ + public const int DomesticPaymentAccount = 1; + public const int ForeignPaymentsSpecialAccount = 2; + public const int Cash = 3; + public const int Cheque = 4; + public const int OnCredit = 5; + public const int WebBanking = 6; + public const int PosEPos = 7; + public const int IrisDirectPayments = 8; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/MyDataVatCategory.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/MyDataVatCategory.cs new file mode 100644 index 000000000..16b2e304d --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/GRSSCD/myDataSCU/MyDataVatCategory.cs @@ -0,0 +1,16 @@ +#pragma warning disable +namespace fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.myDataSCU; + +public class MyDataVatCategory +{ + public const int VatRate24 = 1; // 24% VAT + public const int VatRate13 = 2; // 13% VAT + public const int VatRate6 = 3; // 6% VAT + public const int VatRate17 = 4; // 17% VAT + public const int VatRate9 = 5; // 9% VAT + public const int VatRate4 = 6; // 4% VAT + public const int ExcludingVat = 7; // Excluding VAT 0% + public const int RegistrationsWithoutVat = 8; // Registrations without VAT + public const int VatRate3 = 9; // 3% VAT (ν.5057/2023) + public const int VatRate4New = 10; // 4% VAT (ν.5057/2023) +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Interface/Cases.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Interface/Cases.cs new file mode 100644 index 000000000..e81a632d1 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Interface/Cases.cs @@ -0,0 +1,9 @@ +using System.Globalization; + +namespace fiskaltrust.Middleware.Localization.QueueGR.Interface; + + +public class Cases +{ + public const long BASE_STATE = 0x4752_2000_0000_0000; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Interface/ErrorMessages.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Interface/ErrorMessages.cs new file mode 100644 index 000000000..f3cdc529a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Interface/ErrorMessages.cs @@ -0,0 +1,6 @@ +namespace fiskaltrust.Middleware.Localization.QueueGR.Interface; + +public class ErrorMessages +{ + public static string UnknownReceiptCase(long caseCode) => $"The given ftReceiptCase 0x{caseCode:x} is not supported. Please refer to docs.fiskaltrust.cloud for supported cases."; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Interface/SignatureTypesGR.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Interface/SignatureTypesGR.cs new file mode 100644 index 000000000..d2a5fdb4f --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Interface/SignatureTypesGR.cs @@ -0,0 +1,10 @@ +namespace fiskaltrust.Middleware.Localization.QueueGR.Interface; + +public enum SignatureTypesGR : long +{ + InitialOperationReceipt = 0x4752_2000_0001_1001, + OutOfOperationReceipt = 0x4752_2000_0001_1002, + PosReceipt = 0x4752_2000_0000_0001, + MyDataInfo = 0x4752_2000_0000_0010, + // TBD define signaturetypes => interface ?? +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Models/ActivateQueueGR.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Models/ActivateQueueGR.cs new file mode 100644 index 000000000..9da62e0bc --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Models/ActivateQueueGR.cs @@ -0,0 +1,10 @@ +namespace fiskaltrust.Middleware.Localization.QueueGR.Models; + +public class ActivateQueueGR +{ + public Guid CashBoxId { get; set; } + public required Guid QueueId { get; set; } + public required DateTime Moment { get; set; } + public required bool IsStartReceipt { get; set; } + public required string Version { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Models/DeactivateQueueGR.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Models/DeactivateQueueGR.cs new file mode 100644 index 000000000..1dad2a542 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Models/DeactivateQueueGR.cs @@ -0,0 +1,10 @@ +namespace fiskaltrust.Middleware.Localization.QueueGR.Models; + +public class DeactivateQueueGR +{ + public Guid CashBoxId { get; set; } + public Guid QueueId { get; set; } + public DateTime Moment { get; set; } + public bool IsStopReceipt { get; set; } + public required string Version { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Models/PayItemCaseData.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Models/PayItemCaseData.cs new file mode 100644 index 000000000..f0f1aa94a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Models/PayItemCaseData.cs @@ -0,0 +1,160 @@ +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace fiskaltrust.Api.POS.Models.ifPOS.v2 +{ + public class PaymentProvierResponseBase + { + [JsonExtensionData] + public Dictionary? AdditionalProperties { get; set; } + } + + public class VivaPaymentSession : PaymentProvierResponseBase + { + public Guid? sessionId { get; set; } + public string? terminalId { get; set; } + public string? cashRegisterId { get; set; } + public int? amount { get; set; } + public string? currencyCode { get; set; } + public string? merchantReference { get; set; } + public string? customerTrns { get; set; } + public int? tipAmount { get; set; } + public string? aid { get; set; } + public bool? showTransactionResult { get; set; } + public bool? showReceipt { get; set; } + public bool? success { get; set; } + public int? eventId { get; set; } + public string? authorizationId { get; set; } + public string? transactionId { get; set; } + public int? transactionTypeId { get; set; } + public long? retrievalReferenceNumber { get; set; } + public string? panEntryMode { get; set; } + public string? applicationLabel { get; set; } + public string? primaryAccountNumberMasked { get; set; } + public DateTime? transactionDateTime { get; set; } + public bool? abortOperation { get; set; } + public string? abortAckTime { get; set; } + public bool? abortSuccess { get; set; } + public object? loyaltyInfo { get; set; } // TBD define + public string? verificationMethod { get; set; } + public string? tid { get; set; } + public string? shortOrderCode { get; set; } + public int? installments { get; set; } + public string? message { get; set; } + public bool? preauth { get; set; } + public int? referenceNumber { get; set; } + public string? orderCode { get; set; } + public string? aadeTransactionId { get; set; } + public object? dccDetails { get; set; } // Todo + public object? surchargeAmount { get; set; } + } + +# pragma warning disable + public class VivaWalletPayment + { + public string sessionId { get; set; } + public string terminalId { get; set; } + public string cashRegisterId { get; set; } + public int amount { get; set; } + public string currencyCode { get; set; } + public string merchantReference { get; set; } + public string aadeProviderId { get; set; } + public string aadeProviderSignatureData { get; set; } + public string aadeProviderSignature { get; set; } + public int tipAmount { get; set; } + } + + public class PayItemCaseProviderVivaWalletApp2APp : PayItemCaseProviderData + { + [JsonPropertyName("ProtocolRequest")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string ProtocolRequest { get; set; } + + [JsonPropertyName("ProtocolResponse")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string ProtocolResponse { get; set; } + } + + public class PayItemCaseProviderVivaWallet : PayItemCaseProviderData + { + [JsonPropertyName("ProtocolRequest")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public VivaWalletPayment? ProtocolRequest { get; set; } + + [JsonPropertyName("ProtocolResponse")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public VivaPaymentSession? ProtocolResponse { get; set; } + } + + [JsonDerivedType(typeof(PayItemCaseProviderVivaWallet))] + public class PayItemCaseProviderData + { + [JsonPropertyName("Protocol")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required string Protocol { get; set; } + + [JsonPropertyName("ProtocolVersion")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? ProtocolVersion { get; set; } + + [JsonPropertyName("Action")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required string Action { get; set; } + + [JsonPropertyName("ProtocolRequest")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public object ProtocolRequest { get; set; } + + [JsonPropertyName("ProtocolResponse")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public object ProtocolResponse { get; set; } + } + + public class PayItemCaseDataApp2App + { + [JsonPropertyName("Provider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public PayItemCaseProviderVivaWalletApp2APp? Provider { get; set; } + + [JsonPropertyName("Receipt")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public List? Receipt { get; set; } + } + + public class PayItemCaseDataCloudApi + { + [JsonPropertyName("Provider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public PayItemCaseProviderVivaWallet? Provider { get; set; } + + [JsonPropertyName("Receipt")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public List? Receipt { get; set; } + } + + public class GenericPaymentPayload + { + [JsonPropertyName("Provider")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public PayItemCaseProviderData? Provider { get; set; } + + [JsonPropertyName("Receipt")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public List? Receipt { get; set; } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/DailyOperationsCommandProcessorGR.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/DailyOperationsCommandProcessorGR.cs new file mode 100644 index 000000000..54dc6251b --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/DailyOperationsCommandProcessorGR.cs @@ -0,0 +1,44 @@ +using fiskaltrust.Middleware.Localization.QueueGR.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueueGR.Processors; + +public class DailyOperationsCommandProcessorGR : IDailyOperationsCommandProcessor +{ + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.ZeroReceipt0x2000: + return await ZeroReceipt0x2000Async(request); + case (int) ReceiptCases.OneReceipt0x2001: + return await OneReceipt0x2001Async(request); + case (int) ReceiptCases.ShiftClosing0x2010: + return await ShiftClosing0x2010Async(request); + case (int) ReceiptCases.DailyClosing0x2011: + return await DailyClosing0x2011Async(request); + case (int) ReceiptCases.MonthlyClosing0x2012: + return await MonthlyClosing0x2012Async(request); + case (int) ReceiptCases.YearlyClosing0x2013: + return await YearlyClosing0x2013Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task ZeroReceipt0x2000Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task OneReceipt0x2001Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task ShiftClosing0x2010Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task DailyClosing0x2011Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task MonthlyClosing0x2012Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task YearlyClosing0x2013Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/InvoiceCommandProcessorGR.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/InvoiceCommandProcessorGR.cs new file mode 100644 index 000000000..7dddfc2e6 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/InvoiceCommandProcessorGR.cs @@ -0,0 +1,54 @@ +using fiskaltrust.Middleware.Localization.QueueGR.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD; +using fiskaltrust.Middleware.Storage.GR; + +namespace fiskaltrust.Middleware.Localization.QueueGR.Processors; + +public class InvoiceCommandProcessorGR(IGRSSCD sscd, ftQueueGR queueGR, ftSignaturCreationUnitGR signaturCreationUnitGR) : IInvoiceCommandProcessor +{ +#pragma warning disable + private readonly IGRSSCD _sscd = sscd; + private readonly ftQueueGR _queueGR = queueGR; + private readonly ftSignaturCreationUnitGR _signaturCreationUnitGR = signaturCreationUnitGR; +#pragma warning restore + + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.InvoiceUnknown0x1000: + return await InvoiceUnknown0x1000Async(request); + case (int) ReceiptCases.InvoiceB2C0x1001: + return await InvoiceB2C0x1001Async(request); + case (int) ReceiptCases.InvoiceB2B0x1002: + return await InvoiceB2B0x1002Async(request); + case (int) ReceiptCases.InvoiceB2G0x1003: + return await InvoiceB2G0x1003Async(request); + case 0x1004: + return await InvoiceUnknown0x1000Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task InvoiceUnknown0x1000Async(ProcessCommandRequest request) + { + var response = await _sscd.ProcessReceiptAsync(new ProcessRequest + { + ReceiptRequest = request.ReceiptRequest, + ReceiptResponse = request.ReceiptResponse, + }); + return await Task.FromResult(new ProcessCommandResponse(response.ReceiptResponse, new List())).ConfigureAwait(false); + } + + public async Task InvoiceB2C0x1001Async(ProcessCommandRequest request) => await InvoiceUnknown0x1000Async(request); + + public async Task InvoiceB2B0x1002Async(ProcessCommandRequest request) => await InvoiceUnknown0x1000Async(request); + + public async Task InvoiceB2G0x1003Async(ProcessCommandRequest request) => await InvoiceUnknown0x1000Async(request); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/JournalProcessorGR.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/JournalProcessorGR.cs new file mode 100644 index 000000000..885b9013f --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/JournalProcessorGR.cs @@ -0,0 +1,63 @@ +using System.Xml.Serialization; +using fiskaltrust.ifPOS.v1; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.storage.V0; +using fiskaltrust.storage.V0.MasterData; + +namespace fiskaltrust.Middleware.Localization.QueueGR.Processors; + +#pragma warning disable +public class JournalProcessorGR : IJournalProcessor +{ + private readonly IStorageProvider _storageProvider; + + public JournalProcessorGR(IStorageProvider storageProvider) + { + _storageProvider = storageProvider; + } + + public async IAsyncEnumerable ProcessAsync(JournalRequest request) + { + var masterData = new AccountMasterData + { + AccountId = Guid.NewGuid(), + AccountName = "fiskaltrust ", + Street = "TEST STRET", + Zip = "1111-2222", + City = "Test", + Country = "PT", + TaxId = "199999999" + }; + var queueItems = new List(); + if (request.From > 0) + { + queueItems = (_storageProvider.GetMiddlewareQueueItemRepository().GetEntriesOnOrAfterTimeStampAsync(request.From).ToBlockingEnumerable()).ToList(); + } + else + { + queueItems = (await _storageProvider.GetMiddlewareQueueItemRepository().GetAsync()).ToList(); + } + var aadFactory = new AADEFactory(new storage.V0.MasterData.MasterDataConfiguration + { + Account = new storage.V0.MasterData.AccountMasterData + { + VatId = "112545020" + } + }); + using var memoryStream = new MemoryStream(); + var invoiecDoc = aadFactory.MapToInvoicesDoc(queueItems.ToList()); + if(request.To == -1) + { + invoiecDoc.invoice = invoiecDoc.invoice.OrderByDescending(x => x.mark).Take(1).ToArray(); + } + var xmlSerializer = new XmlSerializer(typeof(InvoicesDoc)); + xmlSerializer.Serialize(memoryStream, invoiecDoc); + memoryStream.Position = 0; + yield return new JournalResponse + { + Chunk = memoryStream.ToArray().ToList() + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/LifecycleCommandProcessorGR.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/LifecycleCommandProcessorGR.cs new file mode 100644 index 000000000..9e1626b0f --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/LifecycleCommandProcessorGR.cs @@ -0,0 +1,60 @@ +using fiskaltrust.Middleware.Localization.QueueGR.Factories; +using fiskaltrust.Middleware.Localization.QueueGR.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.Storage; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueueGR.Processors; + +public class LifecycleCommandProcessorGR : ILifecycleCommandProcessor +{ + private readonly ILocalizedQueueStorageProvider _localizedQueueStorageProvider; + + public LifecycleCommandProcessorGR(ILocalizedQueueStorageProvider localizedQueueStorageProvider) + { + _localizedQueueStorageProvider = localizedQueueStorageProvider; + } + + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.InitialOperationReceipt0x4001: + return await InitialOperationReceipt0x4001Async(request); + case (int) ReceiptCases.OutOfOperationReceipt0x4002: + return await OutOfOperationReceipt0x4002Async(request); + case (int) ReceiptCases.InitSCUSwitch0x4011: + return await InitSCUSwitch0x4011Async(request); + case (int) ReceiptCases.FinishSCUSwitch0x4012: + return await FinishSCUSwitch0x4012Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task InitialOperationReceipt0x4001Async(ProcessCommandRequest request) + { + var (queue, receiptRequest, receiptResponse) = request; + var actionJournal = ftActionJournalFactory.CreateInitialOperationActionJournal(receiptRequest, receiptResponse); + await _localizedQueueStorageProvider.ActivateQueueAsync(); + receiptResponse.AddSignatureItem(SignaturItemFactory.CreateInitialOperationSignature(queue)); + return new ProcessCommandResponse(receiptResponse, [actionJournal]); + } + + public async Task OutOfOperationReceipt0x4002Async(ProcessCommandRequest request) + { + var (queue, receiptRequest, receiptResponse) = request; + await _localizedQueueStorageProvider.DeactivateQueueAsync(); + var actionJournal = ftActionJournalFactory.CreateOutOfOperationActionJournal(receiptRequest, receiptResponse); + receiptResponse.AddSignatureItem(SignaturItemFactory.CreateOutOfOperationSignature(queue)); + receiptResponse.MarkAsDisabled(); + return new ProcessCommandResponse(receiptResponse, [actionJournal]); + } + + public async Task InitSCUSwitch0x4011Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task FinishSCUSwitch0x4012Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/ProtocolCommandProcessorGR.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/ProtocolCommandProcessorGR.cs new file mode 100644 index 000000000..16c7ffba8 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/ProtocolCommandProcessorGR.cs @@ -0,0 +1,72 @@ +using fiskaltrust.Middleware.Localization.QueueGR.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD; +using fiskaltrust.Middleware.Storage.GR; + +namespace fiskaltrust.Middleware.Localization.QueueGR.Processors; + +public class ProtocolCommandProcessorGR(IGRSSCD sscd, ftQueueGR queueGR, ftSignaturCreationUnitGR signaturCreationUnitGR) : IProtocolCommandProcessor +{ +#pragma warning disable + private readonly IGRSSCD _sscd = sscd; + private readonly ftQueueGR _queueGR = queueGR; + private readonly ftSignaturCreationUnitGR _signaturCreationUnitGR = signaturCreationUnitGR; +#pragma warning restore + + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.ProtocolUnspecified0x3000: + return await ProtocolUnspecified0x3000Async(request); + case (int) ReceiptCases.ProtocolTechnicalEvent0x3001: + return await ProtocolTechnicalEvent0x3001Async(request); + case (int) ReceiptCases.ProtocolAccountingEvent0x3002: + return await ProtocolAccountingEvent0x3002Async(request); + case (int) ReceiptCases.InternalUsageMaterialConsumption0x3003: + return await InternalUsageMaterialConsumption0x3003Async(request); + case (int) ReceiptCases.Order0x3004: + return await Order0x3004Async(request); + case (int) ReceiptCases.CopyReceiptPrintExistingReceipt0x3010: + return await CopyReceiptPrintExistingReceipt0x3010Async(request); + case 0x3005: + return await Order0x3004Async(request); + case 0x3006: + return await Order0x3004Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task ProtocolUnspecified0x3000Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task ProtocolTechnicalEvent0x3001Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task ProtocolAccountingEvent0x3002Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task InternalUsageMaterialConsumption0x3003Async(ProcessCommandRequest request) + { + var response = await _sscd.ProcessReceiptAsync(new ProcessRequest + { + ReceiptRequest = request.ReceiptRequest, + ReceiptResponse = request.ReceiptResponse, + }); + return await Task.FromResult(new ProcessCommandResponse(response.ReceiptResponse, new List())).ConfigureAwait(false); + } + + public async Task Order0x3004Async(ProcessCommandRequest request) + { + var response = await _sscd.ProcessReceiptAsync(new ProcessRequest + { + ReceiptRequest = request.ReceiptRequest, + ReceiptResponse = request.ReceiptResponse, + }); + return await Task.FromResult(new ProcessCommandResponse(response.ReceiptResponse, new List())).ConfigureAwait(false); + } + + public async Task CopyReceiptPrintExistingReceipt0x3010Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/ReceiptCommandProcessorGR.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/ReceiptCommandProcessorGR.cs new file mode 100644 index 000000000..6f64a8ba3 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/Processors/ReceiptCommandProcessorGR.cs @@ -0,0 +1,92 @@ +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD; +using fiskaltrust.Middleware.Localization.QueueGR.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Storage.GR; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueueGR.Processors; + +public class ReceiptCommandProcessorGR(IGRSSCD sscd, ftQueueGR queueGR, ftSignaturCreationUnitGR signaturCreationUnitGR) : IReceiptCommandProcessor +{ +#pragma warning disable + private readonly IGRSSCD _sscd = sscd; + private readonly ftQueueGR _queueGR = queueGR; + private readonly ftSignaturCreationUnitGR _signaturCreationUnitGR = signaturCreationUnitGR; +#pragma warning restore + + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.UnknownReceipt0x0000: + return await UnknownReceipt0x0000Async(request); + case (int) ReceiptCases.PointOfSaleReceipt0x0001: + return await PointOfSaleReceipt0x0001Async(request); + case (int) ReceiptCases.PaymentTransfer0x0002: + return await PaymentTransfer0x0002Async(request); + case (int) ReceiptCases.PointOfSaleReceiptWithoutObligation0x0003: + return await PointOfSaleReceiptWithoutObligation0x0003Async(request); + case (int) ReceiptCases.ECommerce0x0004: + return await ECommerce0x0004Async(request); + case (int) ReceiptCases.Protocol0x0005: + return await Protocol0x0005Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task UnknownReceipt0x0000Async(ProcessCommandRequest request) => await PointOfSaleReceipt0x0001Async(request); + + public async Task PointOfSaleReceipt0x0001Async(ProcessCommandRequest request) + { + var response = await _sscd.ProcessReceiptAsync(new ProcessRequest + { + ReceiptRequest = request.ReceiptRequest, + ReceiptResponse = request.ReceiptResponse, + }); + return await Task.FromResult(new ProcessCommandResponse(response.ReceiptResponse, new List())).ConfigureAwait(false); + } + + public async Task PaymentTransfer0x0002Async(ProcessCommandRequest request) + { + var response = await _sscd.ProcessReceiptAsync(new ProcessRequest + { + ReceiptRequest = request.ReceiptRequest, + ReceiptResponse = request.ReceiptResponse, + }); + return await Task.FromResult(new ProcessCommandResponse(response.ReceiptResponse, new List())).ConfigureAwait(false); + } + + public async Task PointOfSaleReceiptWithoutObligation0x0003Async(ProcessCommandRequest request) + { + var response = await _sscd.ProcessReceiptAsync(new ProcessRequest + { + ReceiptRequest = request.ReceiptRequest, + ReceiptResponse = request.ReceiptResponse, + }); + return await Task.FromResult(new ProcessCommandResponse(response.ReceiptResponse, new List())).ConfigureAwait(false); + } + + public async Task ECommerce0x0004Async(ProcessCommandRequest request) + { + var response = await _sscd.ProcessReceiptAsync(new ProcessRequest + { + ReceiptRequest = request.ReceiptRequest, + ReceiptResponse = request.ReceiptResponse, + }); + return await Task.FromResult(new ProcessCommandResponse(response.ReceiptResponse, new List())).ConfigureAwait(false); + } + + public async Task Protocol0x0005Async(ProcessCommandRequest request) + { + var response = await _sscd.ProcessReceiptAsync(new ProcessRequest + { + ReceiptRequest = request.ReceiptRequest, + ReceiptResponse = request.ReceiptResponse, + }); + return await Task.FromResult(new ProcessCommandResponse(response.ReceiptResponse, new List())).ConfigureAwait(false); + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/QueueGRBootstrapper.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/QueueGRBootstrapper.cs new file mode 100644 index 000000000..bda5b91cd --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/QueueGRBootstrapper.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.myDataSCU; +using fiskaltrust.Middleware.Localization.QueueGR.Processors; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Localization.v2.Configuration; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.Storage; +using fiskaltrust.Middleware.Storage.GR; +using Microsoft.Extensions.Logging; + +namespace fiskaltrust.Middleware.Localization.QueueGR; + +public class QueueGRBootstrapper : IV2QueueBootstrapper +{ + private readonly Queue _queue; + + public QueueGRBootstrapper(Guid id, ILoggerFactory loggerFactory, Dictionary configuration) + { + var middlewareConfiguration = MiddlewareConfigurationFactory.CreateMiddlewareConfiguration(id, configuration); + var queueGR = Newtonsoft.Json.JsonConvert.DeserializeObject>(configuration["init_ftQueueGR"]!.ToString()!).First(); + var signaturCreationUnitGR = new ftSignaturCreationUnitGR(); + var grSSCD = MyDataApiClient.CreateClient(configuration); + //var storageProvider = new AzureStorageProvider(loggerFactory, id, Newtonsoft.Json.JsonConvert.DeserializeObject>(JsonSerializer.Serialize(configuration))); + var storageProvider = new AzureStorageProvider(loggerFactory, id, configuration); + var queueStorageProvider = new QueueStorageProvider(id, storageProvider); + + var signProcessorGR = new ReceiptProcessor(loggerFactory.CreateLogger(), new LifecycleCommandProcessorGR(queueStorageProvider), new ReceiptCommandProcessorGR(grSSCD, queueGR, signaturCreationUnitGR), new DailyOperationsCommandProcessorGR(), new InvoiceCommandProcessorGR(grSSCD, queueGR, signaturCreationUnitGR), new ProtocolCommandProcessorGR(grSSCD, queueGR, signaturCreationUnitGR)); + var signProcessor = new SignProcessor(loggerFactory.CreateLogger(), queueStorageProvider, signProcessorGR.ProcessAsync, queueGR.CashBoxIdentification, middlewareConfiguration); + var journalProcessor = new JournalProcessor(storageProvider, new JournalProcessorGR(storageProvider), configuration, loggerFactory.CreateLogger()); + _queue = new Queue(signProcessor, journalProcessor, loggerFactory) + { + Id = id, + Configuration = configuration, + }; + } + + public Func> RegisterForSign() + { + return _queue.RegisterForSign(); + } + + public Func> RegisterForEcho() + { + return _queue.RegisterForEcho(); + } + + public Func> RegisterForJournal() + { + return _queue.RegisterForJournal(); + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/QueueGRConfiguration.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/QueueGRConfiguration.cs new file mode 100644 index 000000000..bc4aebf16 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/QueueGRConfiguration.cs @@ -0,0 +1,11 @@ +using fiskaltrust.Middleware.Localization.v2.Configuration; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.Localization.QueueGR; + +public class QueueGRConfiguration +{ + public bool Sandbox { get; set; } = false; + + public static QueueGRConfiguration FromMiddlewareConfiguration(MiddlewareConfiguration middlewareConfiguration) => JsonConvert.DeserializeObject(JsonConvert.SerializeObject(middlewareConfiguration.Configuration)); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueGR/fiskaltrust.Middleware.Localization.QueueGR.csproj b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/fiskaltrust.Middleware.Localization.QueueGR.csproj new file mode 100644 index 000000000..39e3cd2e7 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueGR/fiskaltrust.Middleware.Localization.QueueGR.csproj @@ -0,0 +1,21 @@ + + + + net8 + Latest + enable + enable + + + + + + + + + + + + + + \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueIT/QueueITBootstrapper.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueIT/QueueITBootstrapper.cs index b858bcae7..a6282fd9d 100644 --- a/queue/src/fiskaltrust.Middleware.Localization.QueueIT/QueueITBootstrapper.cs +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueIT/QueueITBootstrapper.cs @@ -16,7 +16,7 @@ public void ConfigureServices(IServiceCollection services) .AddScoped() .AddScoped() .AddScoped() - .AddScoped() + .AddScoped() .AddScoped() .AddScoped() .AddSingleton(sp => QueueITConfiguration.FromMiddlewareConfiguration(sp.GetRequiredService())) diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueIT/SignProcessorIT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueIT/SignProcessorIT.cs index 61adb49c6..fba20a926 100644 --- a/queue/src/fiskaltrust.Middleware.Localization.QueueIT/SignProcessorIT.cs +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueIT/SignProcessorIT.cs @@ -13,14 +13,14 @@ namespace fiskaltrust.Middleware.Localization.QueueIT public class SignProcessorIT { protected readonly IConfigurationRepository _configurationRepository; - private readonly LifecyclCommandProcessorIT _lifecyclCommandProcessorIT; + private readonly LifecycleCommandProcessorIT _lifecyclCommandProcessorIT; private readonly ReceiptCommandProcessorIT _receiptCommandProcessorIT; private readonly DailyOperationsCommandProcessorIT _dailyOperationsCommandProcessorIT; private readonly InvoiceCommandProcessorIT _invoiceCommandProcessorIT; private readonly ProtocolCommandProcessorIT _protocolCommandProcessorIT; private readonly ILogger _logger; - public SignProcessorIT(ILogger logger, IConfigurationRepository configurationRepository, LifecyclCommandProcessorIT lifecyclCommandProcessorIT, ReceiptCommandProcessorIT receiptCommandProcessorIT, DailyOperationsCommandProcessorIT dailyOperationsCommandProcessorIT, InvoiceCommandProcessorIT invoiceCommandProcessorIT, ProtocolCommandProcessorIT protocolCommandProcessorIT) + public SignProcessorIT(ILogger logger, IConfigurationRepository configurationRepository, LifecycleCommandProcessorIT lifecyclCommandProcessorIT, ReceiptCommandProcessorIT receiptCommandProcessorIT, DailyOperationsCommandProcessorIT dailyOperationsCommandProcessorIT, InvoiceCommandProcessorIT invoiceCommandProcessorIT, ProtocolCommandProcessorIT protocolCommandProcessorIT) { _configurationRepository = configurationRepository; _lifecyclCommandProcessorIT = lifecyclCommandProcessorIT; diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueueIT/v2/LifecyclCommandProcessorIT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueueIT/v2/LifecycleCommandProcessorIT.cs similarity index 96% rename from queue/src/fiskaltrust.Middleware.Localization.QueueIT/v2/LifecyclCommandProcessorIT.cs rename to queue/src/fiskaltrust.Middleware.Localization.QueueIT/v2/LifecycleCommandProcessorIT.cs index 8375c2176..d787c7fa8 100644 --- a/queue/src/fiskaltrust.Middleware.Localization.QueueIT/v2/LifecyclCommandProcessorIT.cs +++ b/queue/src/fiskaltrust.Middleware.Localization.QueueIT/v2/LifecycleCommandProcessorIT.cs @@ -12,13 +12,13 @@ namespace fiskaltrust.Middleware.Localization.QueueIT.v2 { - public class LifecyclCommandProcessorIT + public class LifecycleCommandProcessorIT { private readonly IITSSCDProvider _itSSCDProvider; private readonly IJournalITRepository _journalITRepository; private readonly IConfigurationRepository _configurationRepository; - public LifecyclCommandProcessorIT(IITSSCDProvider itSSCDProvider, IJournalITRepository journalITRepository, IConfigurationRepository configurationRepository) + public LifecycleCommandProcessorIT(IITSSCDProvider itSSCDProvider, IJournalITRepository journalITRepository, IConfigurationRepository configurationRepository) { _itSSCDProvider = itSSCDProvider; _journalITRepository = journalITRepository; diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/Helpers.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/Helpers.cs new file mode 100644 index 000000000..985188a3f --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/Helpers.cs @@ -0,0 +1,12 @@ +using System.Globalization; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Exports.SAFTPT; + +public static class Helpers +{ + public static decimal CreateTwoDigitMonetaryValue(decimal value) => decimal.Parse(value.ToString("F2", CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + public static decimal CreateMonetaryValue(decimal value) => decimal.Parse(value.ToString("F6", CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); + + public static decimal CreateMonetaryValue(decimal? value) => decimal.Parse(value.GetValueOrDefault().ToString("F6", CultureInfo.InvariantCulture), CultureInfo.InvariantCulture); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/MiddlewareCustomer.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/MiddlewareCustomer.cs new file mode 100644 index 000000000..eeec5b0c6 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/MiddlewareCustomer.cs @@ -0,0 +1,13 @@ +namespace fiskaltrust.SAFT.CLI; +#pragma warning disable +public class MiddlewareCustomer +{ + public string CustomerName { get; set; } + public string CustomerId { get; set; } + public string CustomerType { get; set; } + public string CustomerStreet { get; set; } + public string CustomerZip { get; set; } + public string CustomerCity { get; set; } + public string CustomerCountry { get; set; } + public string CustomerVATId { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/AuditFile.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/AuditFile.cs new file mode 100644 index 000000000..2e390903d --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/AuditFile.cs @@ -0,0 +1,25 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "AuditFile", Namespace = "urn:OECD:StandardAuditFile-Tax:PT_1.04_01")] +public class AuditFile +{ + [XmlElement(ElementName = "Header")] + public required Header Header { get; set; } + + /// + /// Master Files 2.1, 2.2, 2.3, 2.4 and 2.5 are required under the conditions stated in f), g), h) and i) of paragraph 1 of this Annex. + /// + [XmlElement(ElementName = "MasterFiles")] + public required MasterFiles MasterFiles { get; set; } + + /// + /// Lines without fiscal relevance must not be exported, in particular technical descriptions, installation instructions and guarantee conditions. + /// + /// The internal code of the document type cannot be used in different document types, regardless of the table in which it is to be exported. + /// + [XmlElement(ElementName = "SourceDocuments")] + public SourceDocuments? SourceDocuments { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/Header/CompanyAddress.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/Header/CompanyAddress.cs new file mode 100644 index 000000000..426134a93 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/Header/CompanyAddress.cs @@ -0,0 +1,80 @@ +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "CompanyAddress")] +public class CompanyAddress +{ + /// + /// TODO: Add documentation + /// + /// Max-length 10 + /// + [XmlElement(ElementName = "BuildingNumber")] + [MaxLength(10)] + public string? BuildingNumber { get; set; } + + /// + /// TODO: Add documentation + /// + /// Max-length 200 + /// + [XmlElement(ElementName = "StreetName")] + [MaxLength(200)] + public string? StreetName { get; set; } + + /// + /// Shall include street name, building number and floor, if applicable. + /// + /// Max-length 210 + /// Required + /// + [XmlElement(ElementName = "AddressDetail")] + [MaxLength(210)] + [Required] + public required string AddressDetail { get; set; } + + /// + /// TODO: Add documentation + /// + /// Max-length 50 + /// Required + /// + [XmlElement(ElementName = "City")] + [MaxLength(50)] + [Required] + public required string City { get; set; } + + /// + /// TODO: Add documentation + /// + /// Max-length 8 + /// Required + /// + [XmlElement(ElementName = "PostalCode")] + [MaxLength(8)] + [Required] + public required string PostalCode { get; set; } + + /// + /// TODO: Add documentation + /// + /// Max-length 50 + /// + [XmlElement(ElementName = "Region")] + [MaxLength(50)] + public string? Region { get; set; } + + /// + /// Fill in with "PT". + /// + /// Max-length 2 + /// Required + /// + [XmlElement(ElementName = "Country")] + [MaxLength(2)] + [Required] + public required string Country { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/Header/Header.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/Header/Header.cs new file mode 100644 index 000000000..af26b6bee --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/Header/Header.cs @@ -0,0 +1,262 @@ +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "Header")] +public class Header +{ + /// + /// The version of XML scheme to be used is the one available on http://www.portaldasfinancas.gov.pt + /// String + /// Max-length 10 + /// Required + /// + [XmlElement(ElementName = "AuditFileVersion")] + [MaxLength(10)] + [Required] + public required string AuditFileVersion { get; set; } + + /// + /// It is obtained by linking together the name of the commercial registry office and the commercial registration number, separated by a space. + /// When there is no commercial registration, the Tax Registration Number shall be inserted. + /// String + /// Max-length 50 + /// Required + /// + [XmlElement(ElementName = "CompanyID")] + [MaxLength(50)] + [Required] + public required string CompanyID { get; set; } + + /// + /// To be filled in with the Portuguese Tax Identification Number/Tax Registration Number without spaces and without country prefixes. + /// Integer + /// Max-length 9 + /// Required + /// + [XmlElement(ElementName = "TaxRegistrationNumber")] + [MaxLength(9)] + [Required] + public required int TaxRegistrationNumber { get; set; } + + /// + /// Shall be filled in with the type of program, indicating the applicable data (including the transport documents, conference documents and issued receipts, if any): + /// "C" - Accounting; + /// "E" - Invoices issued by third parties; + /// "F" - Invoicing; + /// "I" - Invoicing and accounting integrated data; + /// "P" - Invoicing partial data. + /// "R" - Receipts (a); + /// "S" - Self-billing; + /// "T" - Transport documents (a). + /// + /// (a) Type of program should be indicated, in case only this type of documents are issued. If not, fill in with type “C”, “F” or “I”. + /// + /// Max-length + /// Required + /// + [XmlElement(ElementName = "TaxAccountingBasis")] + [MaxLength(1)] + [Required] + public required TaxAccountingBasis TaxAccountingBasis { get; set; } + + /// + /// Social designation of the company or taxpayer’s name. + /// + /// Max-length 100 + /// Required + /// + [XmlElement(ElementName = "CompanyName")] + [MaxLength(100)] + [Required] + public required string CompanyName { get; set; } + + /// + /// Commercial designation of the taxpayer. + /// + /// Max-length 60 + /// + [XmlElement(ElementName = "BusinessName")] + [MaxLength(60)] + public string? BusinessName { get; set; } + + /// + /// Social designation of the company or taxpayer’s name. + /// + /// Required + /// + [XmlElement(ElementName = "CompanyAddress")] + [Required] + public required CompanyAddress CompanyAddress { get; set; } + + /// + /// Use Corporate Income Tax Code rules, in the case of accounting periods that do not coincide with the calendar year. + /// + /// (E.g. taxation period from 01.10.2012 to 30.09.2013 corresponds to the Fiscal year = 2012). + /// + /// Max-length 4 + /// Required + /// + [XmlElement(ElementName = "FiscalYear")] + [Required] + public required int FiscalYear { get; set; } + + /// + /// TODO: Add description + /// + /// Required + /// + [XmlIgnore] + [Required] + public required DateTime StartDate { get; set; } + + [XmlElement(ElementName = "StartDate")] + public string StartDateString + { + get { return StartDate.ToString("yyyy-MM-dd"); } + set { StartDate = DateTime.Parse(value); } + } + + /// + /// TODO: Add description + /// + /// Required + /// + [XmlIgnore] + [Required] + public required DateTime EndDate { get; set; } + + [XmlElement(ElementName = "EndDate")] + public string EndDateString + { + get { return EndDate.ToString("yyyy-MM-dd"); } + set { EndDate = DateTime.Parse(value); } + } + + /// + /// Identifies the default currency to use in the monetary type fields in the file. + /// + /// Fill in with "EUR". + /// + /// Required + /// + [XmlElement(ElementName = "CurrencyCode")] + [Required] + public required string CurrencyCode { get; set; } + + /// + /// Date of creation of file XML of SAF-T (PT) + /// + /// Required + /// + [XmlIgnore] + [Required] + public required DateTime DateCreated { get; set; } + + [XmlElement(ElementName = "DateCreated")] + public string DateCreatedString + { + get { return DateCreated.ToString("yyyy-MM-dd"); } + set { DateCreated = DateTime.Parse(value); } + } + + /// + /// In the case of an invoicing file, it shall be specified which establishment the produced file refers to, if applicable, otherwise it must be filled in with the specification "Global". + /// + /// In the case of an accounting file or integrated file this field must be filled in with the specification "Sede". + /// + /// Max-length: 20 + /// Required + /// + [XmlElement(ElementName = "TaxEntity")] + [MaxLength(20)] + [Required] + public required string TaxEntity { get; set; } + + /// + /// Fill in with the Tax Identification Number/Tax Registration Number of the entity that produced the software. + /// + /// Max-length: 20 + /// Required + /// + [XmlElement(ElementName = "ProductCompanyTaxID")] + [MaxLength(20)] + [Required] + public required int ProductCompanyTaxID { get; set; } + + /// + /// Number of the software certificate allocated to the entity that created the software, pursuant to Ordinance No. 363/2010, of 23th June. + /// + /// If it doesn’t apply, the field must be filled in with "0" (zero). + /// + [XmlElement(ElementName = "SoftwareCertificateNumber")] + [Required] + public required int SoftwareCertificateNumber { get; set; } + + /// + /// Name of the product that generates the SAF-T (PT). + /// + /// The commercial name of the software as well as the name of the company that produced it shall be indicated in the format "Product name/company name". + /// + /// Max-length: 255 + /// Required + /// + [XmlElement(ElementName = "ProductID")] + [MaxLength(255)] + [Required] + public required string ProductID { get; set; } + + /// + /// The product version shall be indicated + /// + /// Max-length: 30 + /// Required + /// + [XmlElement(ElementName = "ProductVersion")] + [MaxLength(30)] + public required string ProductVersion { get; set; } + + /// + /// TODO: Add description + /// + /// Max-length: 255 + /// + [XmlElement(ElementName = "HeaderComment")] + [MaxLength(255)] + public string? HeaderComment { get; set; } + + /// + /// TODO: Add description + /// + /// Max-length: 20 + /// + [XmlElement(ElementName = "Telephone")] + [MaxLength(20)] + public string? Telephone { get; set; } + + /// + /// TODO: Add description + /// + /// Max-length: 20 + /// + [XmlElement(ElementName = "Fax")] + public string? Fax { get; set; } + + /// + /// TODO: Add description + /// + /// Max-length: 254 + /// + [XmlElement(ElementName = "Email")] + public string? Email { get; set; } + + /// + /// TODO: Add description + /// + /// Max-length: 60 + /// + [XmlElement(ElementName = "Website")] + public string? Website { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/Header/TaxAccountingBasis.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/Header/TaxAccountingBasis.cs new file mode 100644 index 000000000..7722955e4 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/Header/TaxAccountingBasis.cs @@ -0,0 +1,24 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +public enum TaxAccountingBasis +{ + [XmlEnum(Name="C")] + Accounting, + [XmlEnum(Name="E")] + InvoicesIssuedByThirdParties, + [XmlEnum(Name="F")] + Invoicing, + [XmlEnum(Name="I")] + InvoicingAndAccountingIntegratedData, + [XmlEnum(Name="P")] + InvoicingPartialData, + [XmlEnum(Name="R")] + Receipts, + [XmlEnum(Name="S")] + SelfBilling, + [XmlEnum(Name="T")] + TransportDocuments +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/BillingAddress.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/BillingAddress.cs new file mode 100644 index 000000000..54109d220 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/BillingAddress.cs @@ -0,0 +1,97 @@ +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "BillingAddress")] +public class BillingAddress +{ + + /// + /// TODO: Add documentation + /// + /// Max-length 10 + /// + [XmlElement(ElementName = "BuildingNumber")] + [MaxLength(10)] + public string? BuildingNumber { get; set; } + + /// + /// TODO: Add documentation + /// + /// Max-length 200 + /// + [XmlElement(ElementName = "StreetName")] + [MaxLength(200)] + public string? StreetName { get; set; } + + /// + /// The field shall include the street name, the building number and floor, if applicable. + /// + /// The field shall be filled in with the designation "Desconhecido" (Unknown) in the following cases: + /// - Non-integrated systems, if information is not known; + /// - Operations carried out with the "Consumidor final" (Final Consumer). + /// + /// Max-length 210 + /// Required + /// + [MaxLength(210)] + [XmlElement(ElementName = "AddressDetail")] + public required string AddressDetail { get; set; } + + /// + /// TODO: Add documentation + /// + /// The field shall be filled in with the designation "Desconhecido" (Unknown) in the following cases: + /// - Non-integrated systems, if information is not known; + /// - Operations carried out with the "Consumidor final" (Final Consumer). + /// + /// Max-length 50 + /// Required + /// + [XmlElement(ElementName = "City")] + [MaxLength(50)] + [Required] + public required string City { get; set; } + + /// + /// TODO: Add documentation + /// + /// The field shall be filled in with the designation "Desconhecido" (Unknown) in the following cases: + /// - Non-integrated systems, if information is not known; + /// - Operations carried out with the "Consumidor final" (Final Consumer). + /// + /// Max-length 20 + /// Required + /// + [XmlElement(ElementName = "PostalCode")] + [MaxLength(20)] + [Required] + public required string PostalCode { get; set; } + + /// + /// TODO: Add documentation + /// + /// Max-length 50 + /// + [XmlElement(ElementName = "Region")] + [MaxLength(50)] + public string? Region { get; set; } + + /// + /// If it is known, the field shall be filled in according to norm ISO 3166-1-alpha-2. + /// + /// The field shall include the street name, the building number and floor, if applicable. + /// + /// The field shall be filled in with the designation "Desconhecido" (Unknown) in the following cases: + /// - Non-integrated systems, if information is not known; + /// - Operations carried out with the "Consumidor final" (Final Consumer). + /// + /// Max-length 12 + /// Required + /// + [XmlElement(ElementName = "Country")] + [MaxLength(12)] + [Required] + public required string Country { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/Customer.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/Customer.cs new file mode 100644 index 000000000..3b5629da2 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/Customer.cs @@ -0,0 +1,119 @@ +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +#pragma warning disable +[XmlRoot(ElementName = "Customer")] +public class Customer +{ + /// + /// In the list of clients cannot exist more than one registration with the same CustomerID. In the case of final consumers, a generic client with the designation of “Consumidor final” (Final Consumer) shall be created. + /// + /// Max-length 30 + /// Required + /// + [XmlElement(ElementName = "CustomerID")] + [MaxLength(30)] + [Required] + public required string CustomerID { get; set; } + + /// + /// The respective client’s current account must be indicated in the general accounting plan, if it is defined. Otherwise the field shall be filled in with the designation "Desconhecido" (Unknown). + /// + /// Max-length 30 + /// Required + /// + [XmlElement(ElementName = "AccountID")] + [MaxLength(30)] + [Required] + public required string AccountID { get; set; } + + /// + /// It must be indicated without the country’s prefix. + /// + /// The generic client, corresponding to the aforementioned "Consumidor final" (Final consumer) shall be identified with the Tax Identification Number "999999990". + /// + /// Max-length 30 + /// Required + /// + [XmlElement(ElementName = "CustomerTaxID")] + [MaxLength(30)] + [Required] + public required string CustomerTaxID { get; set; } + + /// + /// The generic client shall be identified with the designation “Consumidor final” (Final Consumer). + /// + /// Max-length 100 + /// Required + /// + [XmlElement(ElementName = "CompanyName")] + [MaxLength(100)] + [Required] + public required string CompanyName { get; set; } + + /// + /// TODO: Add documentation + /// + /// Max-length 50 + /// + [XmlElement(ElementName = "Contact")] + [MaxLength(30)] + public string? Contact { get; set; } + + /// + /// Head office address or the fixed /permanent establishment address, located on Portuguese territory. + /// + /// Required + /// + [XmlElement(ElementName = "BillingAddress")] + [Required] + public required BillingAddress BillingAddress { get; set; } + + /// + /// TODO: Add documentation + /// + /// Max-length: 20 + /// + [XmlElement(ElementName = "Telephone")] + [MaxLength(20)] + public string? Telephone { get; set; } + + /// + /// TODO: Add documentation + /// + /// Max-length: 20 + /// + [XmlElement(ElementName = "Fax")] + [MaxLength(20)] + public string? Fax { get; set; } + + /// + /// Companies e-mail address. + /// + /// Max-length: 254 + /// + [XmlElement(ElementName = "Email")] + [MaxLength(254)] + public string Email { get; set; } + + /// + /// Companies Website + /// + /// Max-length: 60 + /// + [XmlElement(ElementName = "Website")] + [MaxLength(60)] + public string? Website { get; set; } + + /// + /// Indicator of the existence of a self-billing agreement between the customer and the supplier. + /// The field shall be filled in with "1" if there is an agreement and with "0" (zero) if there is not one. + /// + /// Required + /// + [XmlElement(ElementName = "SelfBillingIndicator")] + [Required] + public int SelfBillingIndicator { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/CustomsDetails.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/CustomsDetails.cs new file mode 100644 index 000000000..eb589b519 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/CustomsDetails.cs @@ -0,0 +1,30 @@ +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "CustomsDetails")] +public class CustomsDetails +{ + + /// + /// Fill in with the European Union Combined Nomenclature code. + /// + /// If there is a need to make more than one reference, this field can be generated as many times as necessary. + /// + /// Max-length 8 + /// + [XmlElement(ElementName = "CNCode")] + [MaxLength(8)] + public string? CNCode { get; set; } + + /// + /// Fill in with the UN [United Nations] number for dangerous products. + /// If there is a need to make more than one reference, this field can be generated as many times as necessary. + /// + /// Max-length 4 + /// + [XmlElement(ElementName = "UNNumber")] + [MaxLength(4)] + public string? UNNumber { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/MasterFiles.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/MasterFiles.cs new file mode 100644 index 000000000..bea87d341 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/MasterFiles.cs @@ -0,0 +1,44 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "MasterFiles")] +public class MasterFiles +{ + /* + The table of the General Ledger to be exported is the one mentioned in the accounting normalization system and other legal provisions in force for the relevant sector of activity. + The records of accounting classes shall not be exported. + In case of aggregating accounts containing sub-accounts with debit balances and sub-accounts with credit balances, the debit and credit balances shall be shown in the aggregating account. + + [XmlElement(ElementName = "GeneralLedgerAccounts")] + public object GeneralLedgerAccounts { get; set; } + */ + + /// + /// This table shall contain all the existing records operated during the taxation period in the relevant customers’ file, as well as those which may be implicit in the operations and do not exist in the relevant file. If, for instance, there is a sale for cash showing only the customer’s taxpayer registration number or his name, and not included in the customers file of the application, this client’s data shall be exported as client in the SAF-T (PT). + /// + [XmlElement(ElementName = "Customer")] + public List? Customer { get; set; } + + /* + This table shall contain all the records operated during the tax period in the relevant database. + + [XmlElement(ElementName = "Supplier")] + public object Supplier { get; set; } + */ + + /// + /// This table shall present the catalogue of products and types of services used in the invoicing system, which have been operated, and also the records, which are implicit in the operations and do not exist in the table of products/services of the application. + /// If, for instance, there is an invoice with a line of freights that does not exist in the articles’ file of the application, this file shall be exported and represented as a product in the SAF-T (PT). + /// This table shall also show taxes, tax rates, eco taxes, parafiscal charges mentioned in the invoice and contributing or not to the taxable basis for VAT or Stamp Duty - except VAT and Stamp duty, which shall be showed in 2.5. – TaxTable (Table of taxes). + /// + [XmlElement(ElementName = "Product")] + public List? Product { get; set; } + + /// + /// This table shows the VAT regimes applied in each fiscal area and the different types of stamp duty to be paid, applicable to the lines of documents recorded in Table 4. – SourceDocuments. + /// + [XmlElement(ElementName = "TaxTable")] + public TaxTable? TaxTable { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/Product.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/Product.cs new file mode 100644 index 000000000..46c1d243a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/Product.cs @@ -0,0 +1,74 @@ +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "Product")] +public class Product +{ + /// + /// The field shall be filled in with: + /// "P" - Products; + /// "S" - Services; + /// "O" - Others (e.g. charged freights, advance payments received or sale of assets); + /// "E" - Excise duties - (e.g. IABA, ISP, IT); + /// "I" - Taxes, tax rates and parafiscal charges except VAT and Stamp Duty which shall appear in table 2.5. – TaxTable and Excise Duties which shall be filled in with the "E" code. + /// + /// Max-length: 1 + /// + [XmlElement(ElementName = "ProductType")] + [MaxLength(1)] + [Required] + public required string ProductType { get; set; } + + /// + /// The unique code in the list of products. + /// + /// Max-length: 60 + /// + [XmlElement(ElementName = "ProductCode")] + [MaxLength(60)] + [Required] + public required string ProductCode { get; set; } + + + /// + /// TODO: Add documentation + /// + /// Max-length: 50 + /// + [XmlElement(ElementName = "ProductGroup")] + [MaxLength(50)] + public string? ProductGroup { get; set; } + + /// + /// It shall correspond to the usual name of the goods or services provided, specifying the elements necessary to determine the applicable tax rate. + /// + /// Max-length: 200 + /// Required + /// + [XmlElement(ElementName = "ProductDescription")] + [MaxLength(200)] + [Required] + public required string ProductDescription { get; set; } + + /// + /// The product’s EAN Code (bar code) shall be used. + /// + /// If the EAN Code does not exist, fill in with the content of field 2.4.2. – ProductCode. + /// + /// Max-length: 60 + /// Required + /// + [XmlElement(ElementName = "ProductNumberCode")] + [MaxLength(200)] + [Required] + public required string ProductNumberCode { get; set; } + + /// + /// TODO: Add documentation + /// + [XmlElement(ElementName = "CustomsDetails")] + [MaxLength(50)] + public CustomsDetails? CustomsDetails { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/TaxTable.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/TaxTable.cs new file mode 100644 index 000000000..323c06009 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/TaxTable.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "TaxTable")] +public class TaxTable +{ + /// + /// Tax Table record + /// + [XmlElement(ElementName = "TaxTableEntry")] + [Required] + public required List TaxTableEntry { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/TaxTableEntry.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/TaxTableEntry.cs new file mode 100644 index 000000000..0a7205f02 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/MasterFiles/TaxTableEntry.cs @@ -0,0 +1,158 @@ +using System.ComponentModel.DataAnnotations; +using System.Globalization; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +#pragma warning disable +[XmlRoot(ElementName = "TaxTableEntry")] +public class TaxTableEntry +{ + /// + /// This field shall be filled in with the tax type: + /// "IVA" – Value Added Tax; + /// "IS" – Stamp Duty; + /// "NS" – Not subject to VAT or Stamp Duty. + /// + /// Max-length: 3 + /// Required + /// + [XmlElement(ElementName = "TaxType")] + [MaxLength(3)] + [Required] + public required string TaxType { get; set; } + + /// + /// This field must be filled in with the norm ISO 3166-1-alpha-2. + /// + /// In the case of the Autonomous Regions of the Azores and Madeira Island the field must be filled in with: + /// + /// - "PT-AC" – Fiscal area of the Autonomous Region of the Azores; + /// - "PT-MA" – Fiscal area of the Autonomous Region of the Madeira Island. + /// + /// Max-length: 5 + /// Required + /// + [XmlElement(ElementName = "TaxCountryRegion")] + [MaxLength(5)] + [Required] + public required string TaxCountryRegion { get; set; } + + /// + /// In case field 2.5.1.1. – TaxType = IVA, the field must be filled in with: + /// + /// "RED" – Reduced tax rate; + /// "INT" – Intermediate tax rate; + /// "NOR" – Normal tax rate; + /// "ISE" – Exempted; + /// "OUT" – Others, applicable to the special VAT regimes. + /// + /// In case field 2.5.1.1. – TaxType = IS, it shall be filled in with: + /// - The correspondent code of the Stamp Duty’s table; + /// - "ISE" – Exempted. + /// + /// In case it is not subject to tax it shall be filled in with "NS". + /// + /// In receipts issued without tax discriminated it shall be filled in with "NA". + /// + /// Max-length: 10 + /// Required + /// + [XmlElement(ElementName = "TaxCode")] + [MaxLength(10)] + [Required] + public required string TaxCode { get; set; } + + /// + /// In the case of Stamp Duty, the field shall be filled in with the respective table code description. + /// + /// Max-length: 255 + /// Required + /// + [XmlElement(ElementName = "Description")] + [MaxLength(10)] + [Required] + public required string Description { get; set; } + + /// + /// The last legal date to apply the tax rate, in the case of alteration of the same, at the time of the taxation period in force. + /// + [XmlIgnore] + public DateTime? TaxExpirationDate { get; set; } + + [XmlElement("TaxExpirationDate", IsNullable = false)] + public object TaxExpirationDatetProperty { + get + { + return TaxExpirationDate; + } + set + { + if (value == null) + { + TaxExpirationDate = null; + } + else if (value is DateTime || value is DateTime?) + { + TaxExpirationDate = (DateTime)value; + } + else + { + TaxExpirationDate = DateTime.Parse(value.ToString()); + } + } + } + /// + /// It is required to fill in this field, if we are dealing with a tax percentage. + /// + /// In case of exemption or not subject to tax, fill in with “0” (zero). + /// + /// Percentage + /// Required + /// + [XmlIgnore] + public decimal? TaxPercentage { get; set; } + + [XmlElement("TaxPercentage", IsNullable = false)] + public string TaxPercentageString + { + get => TaxPercentage?.ToString("F6", CultureInfo.InvariantCulture); + set + { + if (value == null) + { + TaxPercentage = null; + } + else + { + TaxPercentage = decimal.Parse(value.ToString()); + } + } + } + + /// + /// It is required to fill in this field, if it is a fixed stamp duty amount. + /// + /// Monetary + /// Required + /// + [XmlIgnore()] + public decimal? TaxAmount { get; set; } + + [XmlElement("TaxAmount", IsNullable = false)] + public object TaxAmountProperty + { + get => TaxAmount; + set + { + if (value == null) + { + TaxAmount = null; + } + else + { + TaxAmount = decimal.Parse(value.ToString()); + } + } + } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SAFTMapping.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SAFTMapping.cs new file mode 100644 index 000000000..884d5b614 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SAFTMapping.cs @@ -0,0 +1,520 @@ +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT.Exports.SAFTPT; +using fiskaltrust.Middleware.Localization.QueuePT.Interface; +using fiskaltrust.storage.V0; +using fiskaltrust.storage.V0.MasterData; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +public static class SAFTMapping +{ + public static AuditFile CreateAuditFile(AccountMasterData accountMasterData, List queueItems, int to) + { + var receiptRequests = queueItems.Select(x => (receiptRequest: JsonSerializer.Deserialize(x.request)!, receiptResponse: JsonSerializer.Deserialize(x.response))).ToList(); + var actualReceiptRequests = receiptRequests.Where(x => x.receiptResponse != null && ((long) x.receiptResponse.ftState & 0xFF) == 0x00).Cast<(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse)>().ToList(); + if (to < 0) + { + actualReceiptRequests = actualReceiptRequests.Take(-to).ToList(); + } + var invoices = actualReceiptRequests.Select(x => SAFTMapping.GetInvoiceForReceiptRequest(accountMasterData, x)).Where(x => x != null).ToList(); + + return new AuditFile + { + Header = GetHeader(accountMasterData), + MasterFiles = new MasterFiles + { + Customer = GetCustomers(actualReceiptRequests.Select(x => x.receiptRequest).ToList()), + Product = GetProducts(actualReceiptRequests.Select(x => x.receiptRequest).ToList()), + TaxTable = GetTaxTable(actualReceiptRequests.Select(x => x.receiptRequest).ToList()) + }, + SourceDocuments = new SourceDocuments + { + SalesInvoices = new SalesInvoices + { + NumberOfEntries = invoices.Count, + TotalDebit = invoices.SelectMany(x => x!.Line).Sum(x => x.DebitAmount ?? 0.0m), + TotalCredit = invoices.SelectMany(x => x!.Line).Sum(x => x.CreditAmount), + Invoice = invoices! + } + } + }; + } + + private static List GetProducts(List receiptRequest) + { + return receiptRequest.SelectMany(x => x.cbChargeItems).Select(x => + { + return new Product + { + ProductType = GetProductType(x), + ProductCode = x.ProductNumber ?? Convert.ToBase64String(SHA256.HashData(Encoding.UTF8.GetBytes(x.Description))), + ProductGroup = x.ProductGroup, + ProductDescription = x.Description, + ProductNumberCode = x.ProductNumber ?? Convert.ToBase64String(SHA256.HashData(Encoding.UTF8.GetBytes(x.Description))), + }; + }).DistinctBy(x => x.ProductCode).ToList(); + } + + private static TaxTable GetTaxTable(List receiptRequest) + { + var lines = receiptRequest.SelectMany(x => x.cbChargeItems).Select(GetLine); + var taxTableEntries = lines.Select(x => new TaxTableEntry + { + TaxType = x.Tax.TaxType, + TaxCountryRegion = x.Tax.TaxCountryRegion, + TaxCode = x.Tax.TaxCode, + Description = "", + TaxPercentage = x.Tax.TaxPercentage + }).DistinctBy(x => x.TaxCode).ToList(); + return new TaxTable + { + TaxTableEntry = [ + new TaxTableEntry + { + TaxType = "IS", + TaxCountryRegion = "PT", + TaxCode = "1731", + Description = "Juros desc. letras e bil. tesouro", + TaxPercentage = 4.000000m, + }, + new TaxTableEntry + { + TaxType = "IS", + TaxCountryRegion = "PT", + TaxCode = "1734", + Description = "Outras comis./contrapresta es", + TaxPercentage = 4.000000m, + }, + new TaxTableEntry + { + TaxType = "IS", + TaxCountryRegion = "PT-AC", + TaxCode = "1731", + Description = "Juros desc. letras e bil. tesouro", + TaxPercentage = 4.000000m, + }, + new TaxTableEntry + { + TaxType = "IS", + TaxCountryRegion = "PT-AC", + TaxCode = "1734", + Description = "Outras comis./contrapresta es", + TaxPercentage = 4.000000m, + }, + new TaxTableEntry + { + TaxType = "IS", + TaxCountryRegion = "PT-MA", + TaxCode = "1731", + Description = "Juros desc. letras e bil. tesouro", + TaxPercentage = 4.000000m, + }, + new TaxTableEntry + { + TaxType = "IS", + TaxCountryRegion = "PT-MA", + TaxCode = "1734", + Description = "Outras comis./contrapresta es", + TaxPercentage = 4.000000m, + }, + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT", + TaxCode = "INT", + Description = "Taxa Interm dia", + TaxPercentage = 13.000000m, + }, + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT", + TaxCode = "ISE", + Description = "Isento", + TaxPercentage = 0.000000m, + }, + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT", + TaxCode = "NOR", + Description = "Taxa Normal", + TaxPercentage = 23.000000m, + }, + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT", + TaxCode = "RED", + Description = "Taxa Reduzida", + TaxPercentage = 6.000000m, + }, + + + + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT-AC", + TaxCode = "INT", + Description = "Taxa Interm dia", + TaxPercentage = 9.000000m, + }, + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT-AC", + TaxCode = "ISE", + Description = "Isento", + TaxPercentage = 0.000000m, + }, + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT-AC", + TaxCode = "NOR", + Description = "Taxa Normal", + TaxPercentage = 16.000000m, + }, + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT-AC", + TaxCode = "RED", + Description = "Taxa Reduzida", + TaxPercentage = 4.000000m, + }, + + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT-MA", + TaxCode = "INT", + Description = "Taxa Interm dia", + TaxPercentage = 12.000000m, + }, + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT-MA", + TaxCode = "ISE", + Description = "Isento", + TaxPercentage = 0.000000m, + }, + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT-MA", + TaxCode = "NOR", + Description = "Taxa Normal", + TaxPercentage = 22.000000m, + }, + new TaxTableEntry + { + TaxType = "IVA", + TaxCountryRegion = "PT-MA", + TaxCode = "RED", + Description = "Taxa Reduzida", + TaxPercentage = 5.000000m, + }, + ], + }; + } + + private static List GetCustomers(List receiptRequest) + { + var customerData = receiptRequest.Where(x => x.cbCustomer != null).Select(x => + { + var middlewareCustomer = GetCustomerIfIncludeded(x)!; + var customer = new Customer + { + CustomerID = middlewareCustomer.CustomerId, + AccountID = "Desconhecido", + CustomerTaxID = middlewareCustomer.CustomerVATId, + CompanyName = middlewareCustomer.CustomerName, + BillingAddress = new BillingAddress + { + BuildingNumber = "Desconheci", + StreetName = middlewareCustomer.CustomerStreet, + AddressDetail = $"{middlewareCustomer.CustomerName} {middlewareCustomer.CustomerStreet} {middlewareCustomer.CustomerZip} {middlewareCustomer.CustomerCity}", + City = middlewareCustomer.CustomerCity, + PostalCode = middlewareCustomer.CustomerZip, + Region = "Desconhecido", + Country = middlewareCustomer.CustomerCountry, + } + }; + return customer; + }).DistinctBy(x => x.CustomerID).ToList(); + + if (receiptRequest.Any(x => x.cbCustomer == null)) + { + customerData.Add(new Customer + { + CustomerID = "0", + AccountID = "Desconhecido", + CustomerTaxID = "999999990", + CompanyName = "Consumidor final", + BillingAddress = new BillingAddress + { + AddressDetail = "Desconhecido", + City = "Desconhecido", + PostalCode = "Desconhecido", + Country = "Desconhecido" + } + }); + } + return customerData; + } + + public static MiddlewareCustomer? GetCustomerIfIncludeded(ReceiptRequest receiptRequest) + { + if (receiptRequest.cbCustomer == null) + { + return null; + } + return JsonSerializer.Deserialize(JsonSerializer.Serialize(receiptRequest.cbCustomer)); + } + + public static Header GetHeader(AccountMasterData accountMasterData) + { + return new Header + { + AuditFileVersion = "1.04_01", + CompanyID = accountMasterData.TaxId, + TaxRegistrationNumber = int.Parse(accountMasterData.TaxId), + TaxAccountingBasis = TaxAccountingBasis.Invoicing, + CompanyName = accountMasterData.AccountName, + //BusinessName = null, + CompanyAddress = new CompanyAddress + { + StreetName = accountMasterData.Street, + AddressDetail = $"{accountMasterData.AccountName} {accountMasterData.Street} {accountMasterData.Zip} {accountMasterData.City}", + City = accountMasterData.City, + PostalCode = accountMasterData.Zip, + Region = "Desconhecido", + Country = accountMasterData.Country, + }, + FiscalYear = DateTime.UtcNow.Year, + StartDate = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, 01), + EndDate = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.DaysInMonth(DateTime.UtcNow.Year, DateTime.UtcNow.Month)), + CurrencyCode = "EUR", + DateCreated = new DateTime(2024, 06, 27), + TaxEntity = "GLOBAL", + ProductCompanyTaxID = 00000000, + SoftwareCertificateNumber = 0000, + ProductID = "fiskaltrust.Middleware", + ProductVersion = "1.3", + }; + } + + public static Invoice? GetInvoiceForReceiptRequest(AccountMasterData accountMasterData, (ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) receipt) + { + var receiptRequest = receipt.receiptRequest; + var lines = receiptRequest.cbChargeItems.Select(GetLine).ToList(); + if(lines.Count == 0) + { + return null; + } + + var taxable = receiptRequest.cbChargeItems.Sum(x => x.VATAmount.GetValueOrDefault()); + var grossAmount = receiptRequest.cbChargeItems.Sum(x => x.Amount); + var hashSignature = receipt.receiptResponse.ftSignatures.Where(x => x.ftSignatureType == (long) SignatureTypesPT.Hash).FirstOrDefault(); + var atcudSignature = receipt.receiptResponse.ftSignatures.Where(x => x.ftSignatureType == (long) SignatureTypesPT.ATCUD).FirstOrDefault(); + var netAmount = grossAmount - taxable; + var invoiceType = GetInvoiceType(receiptRequest); + if (hashSignature == null || atcudSignature == null) + { + return null; + } + + var invoice = new Invoice + { + InvoiceNo = receipt.receiptResponse.ftReceiptIdentification, + ATCUD = atcudSignature.Data, + DocumentStatus = new DocumentStatus + { + InvoiceStatus = "N", + InvoiceStatusDate = receiptRequest.cbReceiptMoment, + SourceID = JsonSerializer.Serialize(receiptRequest.cbUser), + SourceBilling = "P", + }, + Hash = hashSignature.Data, + HashControl = 1, + Period = receiptRequest.cbReceiptMoment.Month, + InvoiceDate = receiptRequest.cbReceiptMoment, + InvoiceType = GetInvoiceType(receiptRequest), + SpecialRegimes = new SpecialRegimes + { + SelfBillingIndicator = 0, + CashVATSchemeIndicator = 0, + ThirdPartiesBillingIndicator = 0, + }, + SourceID = JsonSerializer.Serialize(receiptRequest.cbUser), + SystemEntryDate = receiptRequest.cbReceiptMoment, + CustomerID = "0", + Line = lines, + DocumentTotals = new DocumentTotals + { + TaxPayable = Helpers.CreateTwoDigitMonetaryValue(taxable), + NetTotal = Helpers.CreateTwoDigitMonetaryValue(netAmount), + GrossTotal = Helpers.CreateTwoDigitMonetaryValue(grossAmount), + } + }; + var customer = GetCustomerIfIncludeded(receiptRequest); + if (customer != null) + { + invoice.CustomerID = customer.CustomerId; + } + if (receiptRequest.cbChargeItems.Any(x => GetProductType(x) == "P")) + { + if (customer != null) + { + invoice.ShipTo = new ShipTo + { + Address = new Address + { + StreetName = customer.CustomerStreet, + AddressDetail = $"{customer.CustomerName} {customer.CustomerStreet} {customer.CustomerZip} {customer.CustomerCity}", + City = customer.CustomerCity, + PostalCode = customer.CustomerZip, + Region = "Desconhecido", + Country = customer.CustomerCountry, + }, + }; + } + invoice.ShipFrom = new ShipFrom + { + Address = new Address + { + StreetName = accountMasterData.Street, + AddressDetail = $"{accountMasterData.AccountName} {accountMasterData.Street} {accountMasterData.Zip} {accountMasterData.City}", + City = accountMasterData.City, + PostalCode = accountMasterData.Zip, + Region = "Desconhecido", + Country = accountMasterData.Country, + } + }; + } + + invoice.DocumentTotals.Payment = receiptRequest.cbPayItems.Select(x => new Payment + { + PaymentAmount = x.Amount, + PaymentDate = x.Moment, + PaymentMechanism = GetPaymentMecahnism(x), + }).ToList(); + return invoice; + } + + private static string GetInvoiceType(ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0xFFFF) switch + { + 0x0000 => "FS", + 0x0001 => "FS", + 0x0002 => "FS", + 0x0003 => "FS", + 0x0004 => "FS", + 0x0005 => "FS", // no invoicetype.. workign document? + 0x1000 => "FT", + 0x1001 => "FT", + 0x1002 => "FT", + 0x1003 => "FT", + _ => "FS" + }; + + public static Line GetLine(ChargeItem chargeItem) + { + var tax = new Tax + { + TaxType = "IVA", // one of IVA => vat; IS => stamp duty; NS => Not subject to VAT or Stamp Duty. + TaxCountryRegion = "PT", // will depend on the location of the taxpayer.. autonomous regions madeira and azores + TaxCode = GetIVATAxCode(chargeItem), + TaxPercentage = Helpers.CreateMonetaryValue(chargeItem.VATRate), + }; + + var unitPrice = chargeItem.UnitPrice; + if (!unitPrice.HasValue) + { + if (chargeItem.Amount == 0 || chargeItem.Quantity == 0) + { + unitPrice = 0m; + } + else + { + unitPrice = chargeItem.Amount / chargeItem.Quantity; + } + } + + return new Line + { + LineNumber = (long) chargeItem.Position, + ProductCode = chargeItem.ProductNumber ?? "", + ProductDescription = chargeItem.Description, + Quantity = Helpers.CreateMonetaryValue(chargeItem.Quantity), + UnitOfMeasure = chargeItem.Unit ?? "", + UnitPrice = Helpers.CreateMonetaryValue(unitPrice), + TaxPointDate = chargeItem.Moment.GetValueOrDefault(), // need some more checks here.. fallback? + Description = chargeItem.Description, + CreditAmount = Helpers.CreateMonetaryValue(chargeItem.Amount - chargeItem.VATAmount), + Tax = tax, + //TaxExemptionReason = GetTaxExemptionReason(chargeItem), + //TaxExemptionCode = GetTaxExemptionCode(chargeItem) + }; + } + + // https://taxfoundation.org/data/all/eu/value-added-tax-2024-vat-rates-europe/ + public static string GetIVATAxCode(ChargeItem chargeItem) => (chargeItem.ftChargeItemCase & 0xF) switch + { + 0x0 => throw new NotImplementedException("There is no unkown rate in Portugal"), + 0x1 => "RED", + 0x2 => throw new NotImplementedException("There is no reduced-2 rate in Portugal"), + 0x3 => "NOR", + 0x4 => throw new NotImplementedException("There is no super-reduced-1 rate in Portugal"), + 0x5 => throw new NotImplementedException("There is no super-reduced-2 rate in Portugal"), + 0x6 => "INT", + 0x7 => throw new NotImplementedException("There is no zero rate in Portugal"), + 0x8 => "ISE", + _ => throw new NotImplementedException("The given tax scheme is not supported in Portugal"), + }; + + public static string GetTaxExemptionCode(ChargeItem chargeItem) => (chargeItem.ftChargeItemCase & 0xFF00) switch + { + _ => "M16", + }; + + public static string GetTaxExemptionReason(ChargeItem chargeItem) => (chargeItem.ftChargeItemCase & 0xFF00) switch + { + _ => "Isento Artigo 14. do RITI (ou similar)", + }; + + public static string GetPaymentMecahnism(PayItem payItem) => (payItem.ftPayItemCase & 0xF) switch + { + 0x0 => "OU", // Unknown � Other means not mentioned + 0x1 => "NU", // Cash + 0x2 => "OU", // Non Cash � Other means not mentioned + 0x3 => "CH", // Bank cheque + 0x4 => "CD", // Debit Card + 0x5 => "CC", // Credit Card + 0x6 => "CO", // Voucher Gift cheque or gift card; + 0x7 => "OU", // Online payment � Other means not mentioned + 0x8 => "OU", // Online payment � Other means not mentioned + _ => "OU", // Other � Other means not mentioned + }; + + public static string GetProductType(ChargeItem chargeItem) => (chargeItem.ftChargeItemCase & 0xF0) switch + { + 0x00 => "O", // Unknown type of service / - Others (e.g. charged freights, advance payments received or sale of assets); + 0x10 => "P", // Delivery (supply of goods) / Products + 0x20 => "S", // Other service (supply of service) / Services + 0x30 => "S", // Tip / Services + 0x40 => "?", // Voucher / ??? + 0x50 => "S", // Catalog Service / Services + 0x60 => "?", // Not own sales Agency busines / ??? + 0x70 => "?", // Own Consumption / ??? + 0x80 => "?", // Grant / ??? + 0x90 => "?", // Receivable / ??? + 0xA0 => "?", // Receivable / ??? + _ => throw new NotImplementedException($"The given ChargeItemCase {chargeItem.ftChargeItemCase} type is not supported"), + }; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Address.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Address.cs new file mode 100644 index 000000000..c33106a37 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Address.cs @@ -0,0 +1,29 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "Address")] +public class Address +{ + [XmlElement(ElementName = "BuildingNumber")] + public string? BuildingNumber { get; set; } + + [XmlElement(ElementName = "StreetName")] + public string? StreetName { get; set; } + + [XmlElement(ElementName = "AddressDetail")] + public required string AddressDetail { get; set; } + + [XmlElement(ElementName = "City")] + public required string City { get; set; } + + [XmlElement(ElementName = "PostalCode")] + public required string PostalCode { get; set; } + + [XmlElement(ElementName = "Region")] + public string? Region { get; set; } + + [XmlElement(ElementName = "Country")] + public required string Country { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Currency.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Currency.cs new file mode 100644 index 000000000..faaf34bd0 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Currency.cs @@ -0,0 +1,16 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "Currency")] +public class Currency +{ + [XmlElement(ElementName = "CurrencyCode")] + public string? CurrencyCode { get; set; } + + [XmlElement(ElementName = "CurrencyAmount")] + public decimal? CurrencyAmount { get; set; } + + [XmlElement(ElementName = "ExchangeRate")] + public decimal? ExchangeRate { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/CustomsInformation.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/CustomsInformation.cs new file mode 100644 index 000000000..4c397ed15 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/CustomsInformation.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "CustomsInformation")] +public class CustomsInformation +{ + [XmlElement(ElementName = "ARCNo")] + public string? ARCNo { get; set; } + + [XmlElement(ElementName = "IECAmount")] + public decimal? IECAmount { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/DocumentStatus.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/DocumentStatus.cs new file mode 100644 index 000000000..1a41b5d04 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/DocumentStatus.cs @@ -0,0 +1,23 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "DocumentStatus")] +public class DocumentStatus +{ + [XmlElement(ElementName = "InvoiceStatus")] + public required string InvoiceStatus { get; set; } + + [XmlElement(ElementName = "InvoiceStatusDate")] + public required DateTime InvoiceStatusDate { get; set; } + + [XmlElement(ElementName = "Reason")] + public string? Reason { get; set; } + + [XmlElement(ElementName = "SourceID")] + public required string SourceID { get; set; } + + [XmlElement(ElementName = "SourceBilling")] + public required string SourceBilling { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/DocumentTotals.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/DocumentTotals.cs new file mode 100644 index 000000000..7f8933a01 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/DocumentTotals.cs @@ -0,0 +1,27 @@ +using System.Globalization; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "DocumentTotals")] +public class DocumentTotals +{ + [XmlElement(ElementName = "TaxPayable")] + public required decimal TaxPayable { get; set; } + + [XmlElement("NetTotal")] + public required decimal NetTotal { get; set; } + + [XmlElement("GrossTotal")] + public required decimal GrossTotal { get; set; } + + [XmlElement(ElementName = "Currency")] + public Currency? Currency { get; set; } + + [XmlElement(ElementName = "Settlement")] + public Settlement? Settlement { get; set; } + + [XmlElement(ElementName = "Payment")] + public List? Payment { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Invoice.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Invoice.cs new file mode 100644 index 000000000..92cf0cc61 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Invoice.cs @@ -0,0 +1,106 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +#pragma warning disable +[XmlRoot(ElementName = "Invoice")] +public class Invoice +{ + /// + /// It is made of the document type internal code, followed by a space, followed by the identifier of the document series, followed by (/) and by a sequential number of the document within the series. + /// + /// In this field cannot exist records with the same identification. + /// + /// The same document type internal code cannot be used for different types of documents. + /// + [XmlElement(ElementName = "InvoiceNo")] + public required string InvoiceNo { get; set; } + + [XmlElement(ElementName = "ATCUD")] + public required string ATCUD { get; set; } + + [XmlElement(ElementName = "DocumentStatus")] + public required DocumentStatus DocumentStatus { get; set; } + + [XmlElement(ElementName = "Hash")] + public required string Hash { get; set; } + + [XmlElement(ElementName = "HashControl")] + public required int HashControl { get; set; } + + [XmlElement(ElementName = "Period")] + public int? Period { get; set; } + + [XmlIgnore()] + public required DateTime InvoiceDate { get; set; } + + [XmlElement(ElementName = "InvoiceDate")] + public string InvoiceDateString + { + get { return InvoiceDate.ToString("yyyy-MM-dd"); } + set { InvoiceDate = DateTime.Parse(value); } + } + + [XmlElement(ElementName = "InvoiceType")] + public required string InvoiceType { get; set; } + + [XmlElement(ElementName = "SpecialRegimes")] + public required SpecialRegimes SpecialRegimes { get; set; } + + [XmlElement(ElementName = "SourceID")] + public required string SourceID { get; set; } + + [XmlElement(ElementName = "EACCode")] + public string? EACCode { get; set; } + + [XmlElement(ElementName = "SystemEntryDate")] + public required DateTime SystemEntryDate { get; set; } + + [XmlElement(ElementName = "TransactionID")] + public string? TransactionID { get; set; } + + [XmlElement(ElementName = "CustomerID")] + public required string CustomerID { get; set; } + + [XmlElement(ElementName = "ShipTo")] + public ShipTo? ShipTo { get; set; } + + [XmlElement(ElementName = "ShipFrom")] + public ShipFrom? ShipFrom { get; set; } + + [XmlIgnore] + public DateTime? MovementStartTime { get; set; } + + [XmlElement("MovementStartTime", IsNullable = false)] + public object MovementStartTimeProperty + { + get + { + return MovementStartTime; + } + set + { + if (value == null) + { + MovementStartTime = null; + } + else if (value is DateTime || value is DateTime?) + { + MovementStartTime = (DateTime)value; + } + else + { + MovementStartTime = DateTime.Parse(value.ToString()); + } + } + } + + [XmlElement(ElementName = "Line")] + public required List Line { get; set; } + + [XmlElement(ElementName = "DocumentTotals")] + public DocumentTotals? DocumentTotals { get; set; } + + [XmlElement(ElementName = "WithholdingTax")] + public WithholdingTax? WithholdingTax { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Line.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Line.cs new file mode 100644 index 000000000..583bddbcb --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Line.cs @@ -0,0 +1,123 @@ +using System.Globalization; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +#pragma warning disable +[XmlRoot(ElementName = "Line")] +public class Line +{ + [XmlElement(ElementName = "LineNumber")] + public required long LineNumber { get; set; } + + [XmlElement(ElementName = "OrderReferences")] + public OrderReferences? OrderReferences { get; set; } + + [XmlElement(ElementName = "ProductCode")] + public required string ProductCode { get; set; } + + [XmlElement(ElementName = "ProductDescription")] + public required string ProductDescription { get; set; } + + [XmlElement(ElementName = "Quantity")] + public required decimal Quantity { get; set; } + + [XmlElement(ElementName = "UnitOfMeasure")] + public required string UnitOfMeasure { get; set; } + + [XmlElement(ElementName = "UnitPrice")] + public required decimal UnitPrice { get; set; } + + [XmlIgnore] + public decimal? TaxBase { get; set; } + + [XmlElement("TaxBase", IsNullable = false)] + public object TaxBaseProperty + { + get => TaxBase; + set + { + if (value == null) + { + TaxBase = null; + } + else + { + TaxBase = decimal.Parse(value.ToString()); + } + } + } + + [XmlIgnore] + public required DateTime TaxPointDate { get; set; } + + [XmlElement(ElementName = "TaxPointDate")] + public string TaxPointDateString + { + get { return TaxPointDate.ToString("yyyy-MM-dd"); } + set { TaxPointDate = DateTime.Parse(value); } + } + + [XmlElement(ElementName = "References")] + public References? References { get; set; } + + [XmlElement(ElementName = "Description")] + public required string Description { get; set; } + + [XmlElement(ElementName = "ProductSerialNumber")] + public ProductSerialNumber? ProductSerialNumber { get; set; } + + [XmlIgnore] + public decimal? DebitAmount { get; set; } + + [XmlElement("DebitAmount", IsNullable = false)] + public object DebitAmountProperty + { + get => DebitAmount; + set + { + if (value == null) + { + DebitAmount = null; + } + else + { + DebitAmount = decimal.Parse(value.ToString()); + } + } + } + + [XmlElement(ElementName = "CreditAmount")] + public required decimal CreditAmount { get; set; } + + [XmlElement(ElementName = "Tax")] + public required Tax Tax { get; set; } + + [XmlElement(ElementName = "TaxExemptionReason")] + public string? TaxExemptionReason { get; set; } + + [XmlElement(ElementName = "TaxExemptionCode")] + public string? TaxExemptionCode { get; set; } + + [XmlIgnore] + public decimal? SettlementAmount { get; set; } + + [XmlElement("SettlementAmount", IsNullable = false)] + public string? SettlementAmountString + { + get => SettlementAmount?.ToString("F6", CultureInfo.InvariantCulture); + set + { + if (value == null) + { + SettlementAmount = null; + } + else + { + SettlementAmount = decimal.Parse(value.ToString()); + } + } + } + + [XmlElement(ElementName = "CustomsInformation")] + public CustomsInformation? CustomsInformation { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/OrderReferences.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/OrderReferences.cs new file mode 100644 index 000000000..583c7b4b4 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/OrderReferences.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "OrderReferences")] +public class OrderReferences +{ + [XmlElement(ElementName = "OriginatingON")] + public string? OriginatingON { get; set; } + + [XmlElement(ElementName = "OrderDate")] + public DateTime? OrderDate { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Payment.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Payment.cs new file mode 100644 index 000000000..67c17db0e --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Payment.cs @@ -0,0 +1,23 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +#pragma warning disable +[XmlRoot(ElementName = "Payment")] +public class Payment +{ + [XmlElement(ElementName = "PaymentMechanism")] + public string? PaymentMechanism { get; set; } + + [XmlElement(ElementName = "PaymentAmount")] + public decimal? PaymentAmount { get; set; } + + [XmlIgnore] + public DateTime? PaymentDate { get; set; } + + [XmlElement(ElementName = "PaymentDate")] + public string PaymentDateString + { + get { return PaymentDate?.ToString("yyyy-MM-dd"); } + set { PaymentDate = DateTime.Parse(value); } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/ProductSerialNumber.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/ProductSerialNumber.cs new file mode 100644 index 000000000..6807ee428 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/ProductSerialNumber.cs @@ -0,0 +1,10 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "ProductSerialNumber")] +public class ProductSerialNumber +{ + [XmlElement(ElementName = "SerialNumber")] + public required string SerialNumber { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/References.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/References.cs new file mode 100644 index 000000000..9ac076bdf --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/References.cs @@ -0,0 +1,13 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "References")] +public class References +{ + [XmlElement(ElementName = "Reference")] + public string? Reference { get; set; } + + [XmlElement(ElementName = "Reason")] + public string? Reason { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/SalesInvoices.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/SalesInvoices.cs new file mode 100644 index 000000000..354381a93 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/SalesInvoices.cs @@ -0,0 +1,45 @@ +using System.ComponentModel.DataAnnotations; +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "SalesInvoices")] +public class SalesInvoices +{ + /// + /// The field shall contain the total number of documents, including the documents which content in field 4.1.4.3.1. - InvoiceStatus is "A" or "F". + /// + /// int + /// Required + /// + [XmlElement(ElementName = "NumberOfEntries")] + [Required] + public required int NumberOfEntries { get; set; } + + /// + /// The field shall contain the control sum of field 4.1.4.19.13. - DebitAmount, excluding the documents which content in field 4.1.4.3.1. - InvoiceStatus is "A" or "F". + /// + /// Monetary + /// Required + /// + [XmlElement(ElementName = "TotalDebit")] + [Required] + public required decimal TotalDebit { get; set; } + + /// + /// The field shall contain the control sum of field 4.1.4.19.14. – CreditAmount, excluding the documents which content in field 4.1.4.3.1. - InvoiceStatus is "A" or "F". + /// + /// Monetary + /// Required + /// + [XmlElement(ElementName = "TotalCredit")] + [Required] + public required decimal TotalCredit { get; set; } + + /// + /// TODO: Add documentation + /// + [XmlElement(ElementName = "Invoice")] + public List? Invoice { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Settlement.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Settlement.cs new file mode 100644 index 000000000..3fbb44be0 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Settlement.cs @@ -0,0 +1,19 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "Settlement")] +public class Settlement +{ + [XmlElement(ElementName = "SettlementDiscount")] + public string? SettlementDiscount { get; set; } + + [XmlElement(ElementName = "SettlementAmount")] + public decimal? SettlementAmount { get; set; } + + [XmlElement(ElementName = "SettlementDate")] + public DateTime? SettlementDate { get; set; } + + [XmlElement(ElementName = "PaymentTerms")] + public string? PaymentTerms { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/ShipFrom.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/ShipFrom.cs new file mode 100644 index 000000000..d1b2b9fc7 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/ShipFrom.cs @@ -0,0 +1,40 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +#pragma warning disable +[XmlRoot(ElementName = "ShipFrom")] +public class ShipFrom +{ + [XmlElement(ElementName = "DeliveryID")] + public string? DeliveryID { get; set; } + + [XmlIgnore] + public DateTime? DeliveryDate { get; set; } + + [XmlElement(ElementName = "DeliveryDate", IsNullable = false)] + public object DeliveryDateProperty + { + get => DeliveryDate; + set + { + if (value == null) + { + DeliveryDate = null; + } + else + { + DeliveryDate = DateTime.Parse(value.ToString()); + } + } + } + + [XmlElement(ElementName = "WarehouseID")] + public string? WarehouseID { get; set; } + + [XmlElement(ElementName = "LocationID")] + public string? LocationID { get; set; } + + [XmlElement(ElementName = "Address")] + public Address? Address { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/ShipTo.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/ShipTo.cs new file mode 100644 index 000000000..c0c2f1aa4 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/ShipTo.cs @@ -0,0 +1,40 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +#pragma warning disable +[XmlRoot(ElementName = "ShipTo")] +public class ShipTo +{ + [XmlElement(ElementName = "DeliveryID")] + public string? DeliveryID { get; set; } + + [XmlIgnore] + public DateTime? DeliveryDate { get; set; } + + [XmlElement(ElementName = "DeliveryDate", IsNullable = false)] + public object DeliveryDateProperty + { + get => DeliveryDate; + set + { + if (value == null) + { + DeliveryDate = null; + } + else + { + DeliveryDate = DateTime.Parse(value.ToString()); + } + } + } + + [XmlElement(ElementName = "WarehouseID")] + public string? WarehouseID { get; set; } + + [XmlElement(ElementName = "LocationID")] + public string? LocationID { get; set; } + + [XmlElement(ElementName = "Address")] + public Address? Address { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/SourceDocuments.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/SourceDocuments.cs new file mode 100644 index 000000000..b4e108b71 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/SourceDocuments.cs @@ -0,0 +1,36 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "SourceDocuments")] +public class SourceDocuments +{ + /// + /// This table shall present all sales documents and correcting documents issued by the company, including cancelled documents, duly marked, enabling a verification of the documents’ numbering sequence within each documental series, which should have an annual numbering at least. + /// + /// Type of documents to be exported: all documents mentioned in field 4.1.4.8. – InvoiceType + /// + [XmlElement(ElementName = "SalesInvoices")] + public SalesInvoices? SalesInvoices { get; set; } + + /// + /// MovementOfGoods + /// + //[XmlElement(ElementName = "MovementOfGoods")] + //public object? MovementOfGoods { get; set; } + + /// + /// In this table shall be exported any other documents issued, apart from its designation, likely to be presented to the costumer for the purpose of checking goods or provision of services, even when subject to later invoicing. + /// + /// This table shall not include the documents required to be exported in Tables 4.1. – SalesInvoices or 4.2 – MovementOfGoods. + /// + //[XmlElement(ElementName = "WorkingDocuments.")] + //public object? WorkingDocuments. { get; set; } + + /// + /// Receipts issued after the entry into force of this structure should be exported on this table. + /// + //[XmlElement(ElementName = "Payments.")] + //public object? Payments. { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/SpecialRegimes.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/SpecialRegimes.cs new file mode 100644 index 000000000..98d5e78fa --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/SpecialRegimes.cs @@ -0,0 +1,18 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "SpecialRegimes")] +public class SpecialRegimes +{ + + [XmlElement(ElementName = "SelfBillingIndicator")] + public required int SelfBillingIndicator { get; set; } + + [XmlElement(ElementName = "CashVATSchemeIndicator")] + public required int CashVATSchemeIndicator { get; set; } + + [XmlElement(ElementName = "ThirdPartiesBillingIndicator")] + public required int ThirdPartiesBillingIndicator { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Tax.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Tax.cs new file mode 100644 index 000000000..2310f9886 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/Tax.cs @@ -0,0 +1,40 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +#pragma warning disable +[XmlRoot(ElementName = "Tax")] +public class Tax +{ + [XmlElement(ElementName = "TaxType")] + public required string TaxType { get; set; } + + [XmlElement(ElementName = "TaxCountryRegion")] + public required string TaxCountryRegion { get; set; } + + [XmlElement(ElementName = "TaxCode")] + public required string TaxCode { get; set; } + + [XmlElement(ElementName = "TaxPercentage")] + public decimal? TaxPercentage { get; set; } + + [XmlIgnore] + public decimal? TaxAmount { get; set; } + + [XmlElement("TaxAmount", IsNullable = false)] + public object TaxAmountProperty + { + get => TaxAmount; + set + { + if (value == null) + { + TaxAmount = null; + } + else + { + TaxAmount = decimal.Parse(value.ToString()); + } + } + } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/WithholdingTax.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/WithholdingTax.cs new file mode 100644 index 000000000..0da7550d3 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/SAFTSchemaPT10401/SourceDocuments/WithholdingTax.cs @@ -0,0 +1,17 @@ +using System.Xml.Serialization; + +namespace fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +[XmlRoot(ElementName = "WithholdingTax")] +public class WithholdingTax +{ + [XmlElement(ElementName = "WithholdingTaxType")] + public string? WithholdingTaxType { get; set; } + + [XmlElement(ElementName = "WithholdingTaxDescription")] + public string? WithholdingTaxDescription { get; set; } + + [XmlElement(ElementName = "WithholdingTaxAmount")] + public required decimal WithholdingTaxAmount { get; set; } +} + diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/XmlHelpers.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/XmlHelpers.cs new file mode 100644 index 000000000..65b753b51 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Exports/SAFTPT/XmlHelpers.cs @@ -0,0 +1,15 @@ +using System.IO; +using System.Xml.Serialization; +using fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Exports.SAFTPT; + +public static class XmlHelpers +{ + public static void SerializeAuditFile(AuditFile auditFile, string path) + { + var serializer = new XmlSerializer(typeof(AuditFile)); + using var reader = File.CreateText(path); + serializer.Serialize(reader, auditFile); + } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Factories/PortugalReceiptCalculations.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Factories/PortugalReceiptCalculations.cs new file mode 100644 index 000000000..3a76090b7 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Factories/PortugalReceiptCalculations.cs @@ -0,0 +1,76 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT.Models; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Storage.PT; +using Org.BouncyCastle.Asn1.Ocsp; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Factories; + +public static class InvoiceType +{ + public const string Invoice = "FT"; + public const string SimplifiedInvoice = "FS"; + public const string Receipt = "FR"; + public const string DebitNote = "ND"; + public const string CreditNote = "NC"; +} + +public static class InvoiceStatus +{ + public const string Normal = "N"; + public const string Cancelled = "A"; + public const string SelfBilling = "S"; + public const string SummaryDocumentForOtherDocuments = "R"; + public const string InvoicedDocument = "F"; +} + +public static class PortugalReceiptCalculations +{ + public static string CreateSimplifiedInvoiceQRCodeAnonymousCustomer(string hash, ftQueuePT queuePT, ftSignaturCreationUnitPT signaturCreationUnitPT, ReceiptRequest request, ReceiptResponse receiptResponse) + { + var taxGroups = request.cbChargeItems.GroupBy(GetIVATAxCode); + var normalChargeItems = request.cbChargeItems.Where(x => GetIVATAxCode(x) == "NOR").ToList(); + var reducedChargeItems = request.cbChargeItems.Where(x => GetIVATAxCode(x) == "RED").ToList(); + var intermediateChargeItems = request.cbChargeItems.Where(x => GetIVATAxCode(x) == "INT").ToList(); + var exemptChargeItems = request.cbChargeItems.Where(x => GetIVATAxCode(x) == "ISE").ToList(); + + return new PTQrCode + { + IssuerTIN = queuePT.IssuerTIN, + CustomerTIN = PTQrCode.CUSTOMER_TIN_ANONYMOUS, + CustomerCountry = PTQrCode.CUSTOMER_COUNTRY_ANONYMOUS, + DocumentType = InvoiceType.SimplifiedInvoice, + DocumentStatus = InvoiceStatus.Normal, + DocumentDate = request.cbReceiptMoment, + UniqueIdentificationOfTheDocument = receiptResponse.ftReceiptIdentification, + ATCUD = queuePT.ATCUD, + TaxCountryRegion = queuePT.TaxRegion, + TaxableBasisOfVAT_ExemptRate = exemptChargeItems.Sum(x => x.Amount), + TaxableBasisOfVAT_ReducedRate = reducedChargeItems.Sum(x => x.Amount - x.VATAmount ?? 0.0m), + TotalVAT_ReducedRate = reducedChargeItems.Sum(x => x.VATAmount ?? 0.0m), + TaxableBasisOfVAT_IntermediateRate = intermediateChargeItems.Sum(x => x.Amount - x.VATAmount ?? 0.0m), + TotalVAT_IntermediateRate = intermediateChargeItems.Sum(x => x.VATAmount ?? 0.0m), + TaxableBasisOfVAT_StandardRate = normalChargeItems.Sum(x => x.Amount - x.VATAmount ?? 0.0m), + TotalVAT_StandardRate = normalChargeItems.Sum(x => x.VATAmount ?? 0.0m), + TotalTaxes = request.cbChargeItems.Sum(x => x.VATAmount ?? 0.0m), + GrossTotal = request.cbChargeItems.Sum(x => x.Amount), + Hash = hash[..4], + SoftwareCertificateNumber = signaturCreationUnitPT.SoftwareCertificateNumber, + OtherInformation = "ftQueueId=" + receiptResponse.ftQueueID + ";ftQueueItemId=" + receiptResponse.ftQueueItemID + }.GenerateQRCode(); + } + + public static string GetIVATAxCode(ChargeItem chargeItem) => (chargeItem.ftChargeItemCase & 0xF) switch + { + 0x0 => "", + 0x1 => "RED", + 0x2 => "", + 0x3 => "NOR", + 0x4 => "", + 0x5 => "", + 0x6 => "INT", + 0x7 => "", + 0x8 => "ISE", + _ => "" + }; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Factories/SignaturItemFactory.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Factories/SignaturItemFactory.cs new file mode 100644 index 000000000..de54c3bd4 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Factories/SignaturItemFactory.cs @@ -0,0 +1,41 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT.Interface; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Factories; + +public static class SignaturItemFactory +{ + public static SignatureItem CreateInitialOperationSignature(ftQueue queue) + { + return new SignatureItem() + { + ftSignatureType = (long) SignatureTypesPT.InitialOperationReceipt, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + Caption = $"Initial-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}" + }; + } + + public static SignatureItem CreateOutOfOperationSignature(ftQueue queue) + { + return new SignatureItem() + { + ftSignatureType = (long) SignatureTypesPT.OutOfOperationReceipt, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + Caption = $"Out-of-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}" + }; + } + + public static SignatureItem CreatePTQRCode(string qrCode) + { + return new SignatureItem() + { + Caption = "[www.fiskaltrust.pt]", + Data = qrCode, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.QR_Code, + ftSignatureType = (long) SignatureTypesPT.PosReceipt + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Factories/ftActionJournalFactory.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Factories/ftActionJournalFactory.cs new file mode 100644 index 000000000..27f1e0b31 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Factories/ftActionJournalFactory.cs @@ -0,0 +1,71 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Contracts.Extensions; +using fiskaltrust.Middleware.Localization.QueuePT.Models; +using fiskaltrust.storage.V0; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Factories; + +public static class ftActionJournalFactory +{ + public static ftActionJournal CreateDailyClosingActionJournal(ftQueue queue, ReceiptRequest request, ReceiptResponse receiptResponse) + { + var ftReceiptCaseHex = request.ftReceiptCase.ToString("X"); + return CreateActionJournal(receiptResponse.ftQueueID, ftReceiptCaseHex, receiptResponse.ftQueueItemID, $"Daily-Closing receipt was processed.", JsonConvert.SerializeObject(new { ftReceiptNumerator = queue.ftReceiptNumerator + 1 })); + } + + public static ftActionJournal CreateMonthlyClosingActionJournal(ftQueue queue, ReceiptRequest request, ReceiptResponse receiptResponse) + { + var ftReceiptCaseHex = request.ftReceiptCase.ToString("X"); + return CreateActionJournal(receiptResponse.ftQueueID, ftReceiptCaseHex, receiptResponse.ftQueueItemID, $"Monthly-Closing receipt was processed.", JsonConvert.SerializeObject(new { ftReceiptNumerator = queue.ftReceiptNumerator + 1 })); + } + + public static ftActionJournal CreateInitialOperationActionJournal(ReceiptRequest request, ReceiptResponse receiptResponse) + { + var notification = new ActivateQueuePT + { + CashBoxId = request.ftCashBoxID!.Value, + QueueId = receiptResponse.ftQueueID, + Moment = DateTime.UtcNow, + IsStartReceipt = true, + Version = "V0", + }; + return CreateActionJournal(receiptResponse.ftQueueID, $"{request.ftReceiptCase:X}-{nameof(ActivateQueuePT)}", receiptResponse.ftQueueItemID, $"Initial-Operation receipt. Queue-ID: {receiptResponse.ftQueueID}", JsonConvert.SerializeObject(notification)); + } + + public static ftActionJournal CreateWrongStateForInitialOperationActionJournal(ftQueue queue, ReceiptRequest request, ReceiptResponse receiptResponse) + { + return CreateActionJournal(receiptResponse.ftQueueID, $"{request.ftReceiptCase:X}", + receiptResponse.ftQueueItemID, queue.IsDeactivated() + ? $"Queue {queue.ftQueueId} is de-activated, initial-operations-receipt can not be executed." + : $"Queue {queue.ftQueueId} is already activated, initial-operations-receipt can not be executed.", ""); + } + + public static ftActionJournal CreateOutOfOperationActionJournal(ReceiptRequest request, ReceiptResponse receiptResponse) + { + var notification = new DeactivateQueuePT + { + CashBoxId = request.ftCashBoxID!.Value, + QueueId = receiptResponse.ftQueueItemID, + Moment = DateTime.UtcNow, + IsStopReceipt = true, + Version = "V0" + }; + return CreateActionJournal(receiptResponse.ftQueueID, $"{request.ftReceiptCase:X}-{nameof(DeactivateQueuePT)}", receiptResponse.ftQueueItemID, $"Out-of-Operation receipt. Queue-ID: {receiptResponse.ftQueueID}", JsonConvert.SerializeObject(notification)); + } + + private static ftActionJournal CreateActionJournal(Guid queueId, string type, Guid queueItemId, string message, string data, int priority = -1) + { + return new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queueId, + ftQueueItemId = queueItemId, + Type = type, + Moment = DateTime.UtcNow, + Message = message, + Priority = priority, + DataJson = data + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Interface/Cases.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Interface/Cases.cs new file mode 100644 index 000000000..cc2e1fa2f --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Interface/Cases.cs @@ -0,0 +1,9 @@ +using System.Globalization; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Interface; + + +public class Cases +{ + public const long BASE_STATE = 0x5054_2000_0000_0000; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Interface/ErrorMessages.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Interface/ErrorMessages.cs new file mode 100644 index 000000000..e817932ad --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Interface/ErrorMessages.cs @@ -0,0 +1,8 @@ +using Org.BouncyCastle.Asn1.Ocsp; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Interface; + +public class ErrorMessages +{ + public static string UnknownReceiptCase(long caseCode) => $"The given ftReceiptCase 0x{caseCode:x} is not supported. Please refer to docs.fiskaltrust.cloud for supported cases."; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Interface/SignatureTypesPT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Interface/SignatureTypesPT.cs new file mode 100644 index 000000000..e4b39ab92 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Interface/SignatureTypesPT.cs @@ -0,0 +1,14 @@ +namespace fiskaltrust.Middleware.Localization.QueuePT.Interface; + +// _CCCC_vlll_gggg_tsss +public enum SignatureTypesPT : long +{ + InitialOperationReceipt = 0x5054_2000_0001_1001, + OutOfOperationReceipt = 0x5054_2000_0001_1002, + PosReceipt = 0x5054_2000_0000_0001, + + ATCUD = 0x5054_2000_0000_0010, + Hash = 0x5054_2000_0000_0012, + HashPrint = 0x5054_2000_0000_0013, + // TBD define signaturetypes => interface ?? +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/ActivateQueuePT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/ActivateQueuePT.cs new file mode 100644 index 000000000..71a1f0707 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/ActivateQueuePT.cs @@ -0,0 +1,10 @@ +namespace fiskaltrust.Middleware.Localization.QueuePT.Models; + +public class ActivateQueuePT +{ + public Guid CashBoxId { get; set; } + public required Guid QueueId { get; set; } + public required DateTime Moment { get; set; } + public required bool IsStartReceipt { get; set; } + public required string Version { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/DeactivateQueuePT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/DeactivateQueuePT.cs new file mode 100644 index 000000000..2ab48eed5 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/DeactivateQueuePT.cs @@ -0,0 +1,10 @@ +namespace fiskaltrust.Middleware.Localization.QueuePT.Models; + +public class DeactivateQueuePT +{ + public Guid CashBoxId { get; set; } + public Guid QueueId { get; set; } + public DateTime Moment { get; set; } + public bool IsStopReceipt { get; set; } + public required string Version { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/PTInvoiceElement.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/PTInvoiceElement.cs new file mode 100644 index 000000000..27b4c4e77 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/PTInvoiceElement.cs @@ -0,0 +1,10 @@ +namespace fiskaltrust.Middleware.Localization.QueuePT.Models; + +public class PTInvoiceElement +{ + public DateTime InvoiceDate { get; set; } // AAAA-MM-DD + public DateTime SystemEntryDate { get; set; } // AAAA-MM-DDTHH:MM:SS + public required string InvoiceNo { get; set; } // Composed by the internal document code followed by a space, followed by an identifier of the series of the document (mandatory), followed by a bar (/) and by a sequential number of the document within the series. [^ ]+ [^/^ ]+/[0-9]+ + public decimal GrossTotal { get; set; } //Numerical field with two decimal points, decimal separator “.” (dot) and without any separator for the thousands. + public required string Hash { get; set; } // Base 64 +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/PTQrCode.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/PTQrCode.cs new file mode 100644 index 000000000..7c1a30e1e --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Models/PTQrCode.cs @@ -0,0 +1,183 @@ +using System.Globalization; +using System.Text; +using System.Xml.Linq; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Models; + +public class PTQrCode +{ + public const string CUSTOMER_TIN_ANONYMOUS = "999999990"; + public const string CUSTOMER_COUNTRY_ANONYMOUS = "PT"; + + /// + /// Fill with the issuer’s TIN without blanks and withoutcountry prefix, according to the TaxRegistrationNumber field of the SAF-T (PT). + /// + public required string IssuerTIN { get; set; } + + /// + /// Fill with the customer TIN without country prefix, according to the CustomerTaxID field of the SAF-T (PT). When issuing a document to a “Consumidor final” (Final Consumer) fill with 999999990. + /// + public required string CustomerTIN { get; set; } + + /// + /// Fill according to the Country field of the SAF-T (PT) customer table. + /// + public required string CustomerCountry { get; set; } + + /// + /// Fill according to the typology of the SAF-T (PT) - InvoiceType, MovementType, WorkType or PaymentType fields. + /// + public required string DocumentType { get; set; } + + /// + /// Fill according to the typology of the SAF-T (PT) - InvoiceStatus, MovementStatus, WorkStatus or PaymentStatus fields. + /// + public required string DocumentStatus { get; set; } + + /// + /// Use YYYYMMDD format. Corresponds to SAF-T (PT) InvoiceDate, MovementDate, WorkDate or TransactionDate fields without hyphens. + /// + public required DateTime DocumentDate { get; set; } + + /// + /// Fill according to the typology of the SAF-T (PT) - InvoiceNo, DocumentNumber or PaymentRefNo fields. + /// + public required string UniqueIdentificationOfTheDocument { get; set; } + + /// + /// Fill with the document unique code, according to the ATCUD fields of the SAF-T (PT). + /// + public required string ATCUD { get; set; } // will put 0 right now + + /// + /// Fill according to the technicalnotes of the TaxCountryRegion field of SAF-T (PT). In case of a document without an indication of the VAT rate, which must be shown in table 4.2, 4.3 or 4.4 of the SAF-T (PT), fill in with «0» (I1:0) + /// + public required string TaxCountryRegion { get; set; } + + /// + /// Total amount of the VAT exempt tax base, including transactions liable to stamp duty (whether or not exempt from stamp duty). Format with two decimal places, with “.” as decimal separator and without separator of thousands. + /// + public decimal? TaxableBasisOfVAT_ExemptRate { get; set; } + + /// + /// Total amount of the tax base subject to the reduced rate of VAT. Format with two decimal places, with “.” as decimal separator and without separator of thousands. + /// + public decimal? TaxableBasisOfVAT_ReducedRate { get; set; } + + /// + /// Total amount of VAT at the reduced rate in the document. Format with two decimal places, with “.” as decimal separator and without separator of thousands. + /// + public decimal? TotalVAT_ReducedRate { get; set; } + + /// + /// Total amount of the tax base subject to the intermediate rate of VAT. Format with two decimal places, with “.” as decimal separator and without separator of thousands. + /// + public decimal? TaxableBasisOfVAT_IntermediateRate { get; set; } + + /// + /// Total amount of VAT at the intermediate rate in the document. Format with two decimal places, with “.” as decimal separator and without separator of thousands. + /// + public decimal? TotalVAT_IntermediateRate { get; set; } + + /// + /// Total amount of the tax base subject to the standard rate of VAT. Format with two decimal places, with “.” as decimal separator and without separator of thousands. + /// + public decimal? TaxableBasisOfVAT_StandardRate { get; set; } + + /// + /// Total amount of VAT at the standard rate in the document. Format with two decimal places, with “.” as decimal separator and without separator of thousands. + /// + public decimal? TotalVAT_StandardRate { get; set; } + + /// + /// Total amount of VAT and Stamp duty - TaxPayable field of SAF-T (PT). Format with two decimal places, with “.” as decimal separator and without separator of thousands. + /// + public required decimal TotalTaxes { get; set; } + + /// + /// Total amount of the document– GrossTotal field of SAF-T (PT). Format with two decimal places, with “.” as decimal separator and without separator of thousands. + /// + public required decimal GrossTotal { get; set; } + + /// + /// Complete in accordance with Article 6(3)(a) of Ordinance No. 363/2010 of June 23rd. + /// + public required string Hash { get; set; } + + /// + /// Fill with the certificate number assigned by the Tax and Customs Authority, according to the SoftwareCertificateNumber field of the SAF-T (PT). + /// + public required string SoftwareCertificateNumber { get; set; } + + /// Free fill-in field, in which, for example, payment information can be indicated (e.g.: from IBAN or ATM Ref.,with the separator «;»). This field shall not contain the asterisk character (*). + /// + public string? OtherInformation { get; set; } + + public static string CreateCurrencyValue(decimal value) + { + return value.ToString("F2", CultureInfo.InvariantCulture); + } + + public string GenerateQRCode() + { + var sb = new StringBuilder(); + sb.Append($"A:{IssuerTIN}*"); + sb.Append($"B:{CustomerTIN}*"); + sb.Append($"C:{CustomerCountry}*"); + sb.Append($"D:{DocumentType}*"); + sb.Append($"E:{DocumentStatus}*"); + sb.Append($"F:{DocumentDate:yyyyMMdd}*"); + sb.Append($"G:{UniqueIdentificationOfTheDocument}*"); + sb.Append($"H:{ATCUD}*"); + + sb.Append($"I1:{TaxCountryRegion}*"); + + if (TaxableBasisOfVAT_ExemptRate.HasValue) + { + sb.Append($"I2:{CreateCurrencyValue(TaxableBasisOfVAT_ExemptRate.Value)}*"); + } + + if (TaxableBasisOfVAT_ReducedRate.HasValue) + { + sb.Append($"I3:{CreateCurrencyValue(TaxableBasisOfVAT_ReducedRate.Value)}*"); + } + + if (TotalVAT_ReducedRate.HasValue) + { + sb.Append($"I4:{CreateCurrencyValue(TotalVAT_ReducedRate.Value)}*"); + } + + if (TaxableBasisOfVAT_IntermediateRate.HasValue) + { + sb.Append($"I5:{CreateCurrencyValue(TaxableBasisOfVAT_IntermediateRate.Value)}*"); + } + + if (TotalVAT_IntermediateRate.HasValue) + { + sb.Append($"I6:{CreateCurrencyValue(TotalVAT_IntermediateRate.Value)}*"); + } + + if (TaxableBasisOfVAT_StandardRate.HasValue) + { + sb.Append($"I7:{CreateCurrencyValue(TaxableBasisOfVAT_StandardRate.Value)}*"); + } + + if (TotalVAT_StandardRate.HasValue) + { + sb.Append($"I8:{CreateCurrencyValue(TotalVAT_StandardRate.Value)}*"); + } + + sb.Append($"N:{CreateCurrencyValue(TotalTaxes)}*"); + sb.Append($"O:{CreateCurrencyValue(GrossTotal)}*"); + sb.Append($"Q:{Hash}*"); + sb.Append($"R:{SoftwareCertificateNumber}*"); + + if (!string.IsNullOrEmpty(OtherInformation)) + { + sb.Append($"S:{OtherInformation}"); + } + + return sb.ToString().TrimEnd('*'); // Removes trailing '*' + } + +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/PTSSCD/IPTSSCD.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/PTSSCD/IPTSSCD.cs new file mode 100644 index 000000000..72d04b0e5 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/PTSSCD/IPTSSCD.cs @@ -0,0 +1,24 @@ +using System.Runtime.Serialization; +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.QueuePT.PTSSCD; + +public interface IPTSSCD +{ + Task<(ProcessResponse response, string hash)> ProcessReceiptAsync(ProcessRequest request, string lastHash); + + Task GetInfoAsync(); +} + + +public class ProcessRequest +{ + public required ReceiptRequest ReceiptRequest { get; set; } + + public required ReceiptResponse ReceiptResponse { get; set; } +} + +public class ProcessResponse +{ + public required ReceiptResponse ReceiptResponse { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/PTSSCD/InMemorySCU.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/PTSSCD/InMemorySCU.cs new file mode 100644 index 000000000..ff856a17a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/PTSSCD/InMemorySCU.cs @@ -0,0 +1,63 @@ +using System.Security.Cryptography; +using System.Text; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT.Models; +using fiskaltrust.Middleware.Storage.PT; +using fiskaltrust.storage.V0; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.Localization.QueuePT.PTSSCD; + +public class PTSSCDInfo +{ +} + +public class InMemorySCUConfiguration +{ + +} + +public class InMemorySCU : IPTSSCD +{ + private readonly ftSignaturCreationUnitPT _signaturCreationUnitPT; + + public InMemorySCU(ftSignaturCreationUnitPT signaturCreationUnitPT) + { + _signaturCreationUnitPT = signaturCreationUnitPT; + } + + public PTInvoiceElement GetPTInvoiceElementFromReceiptRequest(ReceiptRequest receipt, string lastHash) + { + return new PTInvoiceElement + { + InvoiceDate = receipt.cbReceiptMoment, + SystemEntryDate = receipt.cbReceiptMoment, // wrong + InvoiceNo = receipt.cbReceiptReference ?? "", // wrong + GrossTotal = receipt.cbChargeItems.Sum(x => x.Amount), + Hash = lastHash + }; + } + + public string GetHashForItem(PTInvoiceElement element) + { + return $"{element.InvoiceDate:yyyy-MM-dd};" + + $"{element.SystemEntryDate:yyyy-MM-ddTHH:mm:ss};" + + $"{element.InvoiceNo};" + + $"{element.GrossTotal:0.00};" + + $"{element.Hash}"; + } + + public async Task<(ProcessResponse, string)> ProcessReceiptAsync(ProcessRequest request, string lastHash) + { + var rsa = RSA.Create(); + rsa.ImportFromPem(_signaturCreationUnitPT.PrivateKey); + var hash1 = GetHashForItem(GetPTInvoiceElementFromReceiptRequest(request.ReceiptRequest, lastHash)); + var signature1 = rsa.SignData(Encoding.UTF8.GetBytes(hash1), HashAlgorithmName.SHA1, RSASignaturePadding.Pkcs1); + return await Task.FromResult((new ProcessResponse + { + ReceiptResponse = request.ReceiptResponse, + }, Convert.ToBase64String(signature1))); + } + + public Task GetInfoAsync() => throw new NotImplementedException(); +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/DailyOperationsCommandProcessorPT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/DailyOperationsCommandProcessorPT.cs new file mode 100644 index 000000000..987fe1d6d --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/DailyOperationsCommandProcessorPT.cs @@ -0,0 +1,44 @@ +using fiskaltrust.Middleware.Localization.QueuePT.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Processors; + +public class DailyOperationsCommandProcessorPT : IDailyOperationsCommandProcessor +{ + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.ZeroReceipt0x2000: + return await ZeroReceipt0x2000Async(request); + case (int) ReceiptCases.OneReceipt0x2001: + return await OneReceipt0x2001Async(request); + case (int) ReceiptCases.ShiftClosing0x2010: + return await ShiftClosing0x2010Async(request); + case (int) ReceiptCases.DailyClosing0x2011: + return await DailyClosing0x2011Async(request); + case (int) ReceiptCases.MonthlyClosing0x2012: + return await MonthlyClosing0x2012Async(request); + case (int) ReceiptCases.YearlyClosing0x2013: + return await YearlyClosing0x2013Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task ZeroReceipt0x2000Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task OneReceipt0x2001Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task ShiftClosing0x2010Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task DailyClosing0x2011Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task MonthlyClosing0x2012Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); + + public async Task YearlyClosing0x2013Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/InvoiceCommandProcessorPT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/InvoiceCommandProcessorPT.cs new file mode 100644 index 000000000..ead8c48c8 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/InvoiceCommandProcessorPT.cs @@ -0,0 +1,36 @@ +using fiskaltrust.Middleware.Localization.QueuePT.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Processors; + +public class InvoiceCommandProcessorPT : IInvoiceCommandProcessor +{ + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.InvoiceUnknown0x1000: + return await InvoiceUnknown0x1000Async(request); + case (int) ReceiptCases.InvoiceB2C0x1001: + return await InvoiceB2C0x1001Async(request); + case (int) ReceiptCases.InvoiceB2B0x1002: + return await InvoiceB2B0x1002Async(request); + case (int) ReceiptCases.InvoiceB2G0x1003: + return await InvoiceB2G0x1003Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task InvoiceUnknown0x1000Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task InvoiceB2C0x1001Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task InvoiceB2B0x1002Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task InvoiceB2G0x1003Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/JournalProcessorPT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/JournalProcessorPT.cs new file mode 100644 index 000000000..675ed7ff5 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/JournalProcessorPT.cs @@ -0,0 +1,53 @@ +using System.Xml.Serialization; +using fiskaltrust.ifPOS.v1; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Storage.PT; +using fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +using fiskaltrust.storage.V0; +using fiskaltrust.storage.V0.MasterData; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Processors; + +public class JournalProcessorPT : IJournalProcessor +{ + private readonly IStorageProvider _storageProvider; + + public JournalProcessorPT(IStorageProvider storageProvider) + { + _storageProvider = storageProvider; + } + + public async IAsyncEnumerable ProcessAsync(JournalRequest request) + { + var masterData = new AccountMasterData + { + AccountId = Guid.NewGuid(), + AccountName = "fiskaltrust ", + Street = "TEST STRET", + Zip = "1111-2222", + City = "Test", + Country = "PT", + TaxId = "199999999" + }; + + List queueItems; + if (request.From > 0) + { + queueItems = _storageProvider.GetMiddlewareQueueItemRepository().GetEntriesOnOrAfterTimeStampAsync(request.From).ToBlockingEnumerable().ToList(); + } + else + { + queueItems = (await _storageProvider.GetMiddlewareQueueItemRepository().GetAsync()).ToList(); + } + var data = SAFTMapping.CreateAuditFile(masterData, queueItems, (int) request.To); + using var memoryStream = new MemoryStream(); + var serializer = new XmlSerializer(typeof(AuditFile)); + serializer.Serialize(memoryStream, data); + memoryStream.Position = 0; + yield return new JournalResponse + { + Chunk = memoryStream.ToArray().ToList() + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/LifecycleCommandProcessorPT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/LifecycleCommandProcessorPT.cs new file mode 100644 index 000000000..f458b77cb --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/LifecycleCommandProcessorPT.cs @@ -0,0 +1,62 @@ +using fiskaltrust.Middleware.Localization.QueuePT.Factories; +using fiskaltrust.Middleware.Localization.QueuePT.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Processors; + +public class LifecycleCommandProcessorPT : ILifecycleCommandProcessor +{ +#pragma warning disable + private readonly IConfigurationRepository _configurationRepository; + + public LifecycleCommandProcessorPT(IConfigurationRepository configurationRepository) + { + _configurationRepository = configurationRepository; + } + + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.InitialOperationReceipt0x4001: + return await InitialOperationReceipt0x4001Async(request); + case (int) ReceiptCases.OutOfOperationReceipt0x4002: + return await OutOfOperationReceipt0x4002Async(request); + case (int) ReceiptCases.InitSCUSwitch0x4011: + return await InitSCUSwitch0x4011Async(request); + case (int) ReceiptCases.FinishSCUSwitch0x4012: + return await FinishSCUSwitch0x4012Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task InitialOperationReceipt0x4001Async(ProcessCommandRequest request) + { + var (queue, receiptRequest, receiptResponse) = request; + var actionJournal = ftActionJournalFactory.CreateInitialOperationActionJournal(receiptRequest, receiptResponse); + queue.StartMoment = DateTime.UtcNow; + await _configurationRepository.InsertOrUpdateQueueAsync(queue).ConfigureAwait(false); + receiptResponse.AddSignatureItem(SignaturItemFactory.CreateInitialOperationSignature(queue)); + return new ProcessCommandResponse(receiptResponse, [actionJournal]); + } + + public async Task OutOfOperationReceipt0x4002Async(ProcessCommandRequest request) + { + var (queue, receiptRequest, receiptResponse) = request; + queue.StopMoment = DateTime.UtcNow; + await _configurationRepository.InsertOrUpdateQueueAsync(queue); + var actionJournal = ftActionJournalFactory.CreateOutOfOperationActionJournal(receiptRequest, receiptResponse); + receiptResponse.AddSignatureItem(SignaturItemFactory.CreateOutOfOperationSignature(queue)); + receiptResponse.MarkAsDisabled(); + return new ProcessCommandResponse(receiptResponse, [actionJournal]); + } + + public async Task InitSCUSwitch0x4011Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task FinishSCUSwitch0x4012Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/ProtocolCommandProcessorPT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/ProtocolCommandProcessorPT.cs new file mode 100644 index 000000000..b10591507 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/ProtocolCommandProcessorPT.cs @@ -0,0 +1,44 @@ +using fiskaltrust.Middleware.Localization.QueuePT.Interface; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Processors; + +public class ProtocolCommandProcessorPT : IProtocolCommandProcessor +{ + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.ProtocolUnspecified0x3000: + return await ProtocolUnspecified0x3000Async(request); + case (int) ReceiptCases.ProtocolTechnicalEvent0x3001: + return await ProtocolTechnicalEvent0x3001Async(request); + case (int) ReceiptCases.ProtocolAccountingEvent0x3002: + return await ProtocolAccountingEvent0x3002Async(request); + case (int) ReceiptCases.InternalUsageMaterialConsumption0x3003: + return await InternalUsageMaterialConsumption0x3003Async(request); + case (int) ReceiptCases.Order0x3004: + return await Order0x3004Async(request); + case (int) ReceiptCases.CopyReceiptPrintExistingReceipt0x3010: + return await CopyReceiptPrintExistingReceipt0x3010Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task ProtocolUnspecified0x3000Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task ProtocolTechnicalEvent0x3001Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task ProtocolAccountingEvent0x3002Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task InternalUsageMaterialConsumption0x3003Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task Order0x3004Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task CopyReceiptPrintExistingReceipt0x3010Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/ReceiptCommandProcessorPT.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/ReceiptCommandProcessorPT.cs new file mode 100644 index 000000000..438beaeed --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/Processors/ReceiptCommandProcessorPT.cs @@ -0,0 +1,84 @@ +using fiskaltrust.Middleware.Localization.QueuePT.Factories; +using fiskaltrust.Middleware.Localization.QueuePT.Interface; +using fiskaltrust.Middleware.Localization.QueuePT.PTSSCD; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Storage.PT; +using fiskaltrust.storage.V0; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Processors; + +public class ReceiptCommandProcessorPT(IPTSSCD sscd, ftQueuePT queuePT, ftSignaturCreationUnitPT signaturCreationUnitPT) : IReceiptCommandProcessor +{ + private readonly IPTSSCD _sscd = sscd; + private readonly ftQueuePT _queuePT = queuePT; + private readonly ftSignaturCreationUnitPT _signaturCreationUnitPT = signaturCreationUnitPT; + + public async Task ProcessReceiptAsync(ProcessCommandRequest request) + { + var receiptCase = request.ReceiptRequest.ftReceiptCase & 0xFFFF; + switch (receiptCase) + { + case (int) ReceiptCases.UnknownReceipt0x0000: + return await UnknownReceipt0x0000Async(request); + case (int) ReceiptCases.PointOfSaleReceipt0x0001: + return await PointOfSaleReceipt0x0001Async(request); + case (int) ReceiptCases.PaymentTransfer0x0002: + return await PaymentTransfer0x0002Async(request); + case (int) ReceiptCases.PointOfSaleReceiptWithoutObligation0x0003: + return await PointOfSaleReceiptWithoutObligation0x0003Async(request); + case (int) ReceiptCases.ECommerce0x0004: + return await ECommerce0x0004Async(request); + case (int) ReceiptCases.Protocol0x0005: + return await Protocol0x0005Async(request); + } + request.ReceiptResponse.SetReceiptResponseError(ErrorMessages.UnknownReceiptCase(request.ReceiptRequest.ftReceiptCase)); + return new ProcessCommandResponse(request.ReceiptResponse, []); + } + + public async Task UnknownReceipt0x0000Async(ProcessCommandRequest request) => await PointOfSaleReceipt0x0001Async(request); + + public async Task PointOfSaleReceipt0x0001Async(ProcessCommandRequest request) + { + var (response, hash) = await _sscd.ProcessReceiptAsync(new ProcessRequest + { + ReceiptRequest = request.ReceiptRequest, + ReceiptResponse = request.ReceiptResponse, + }, _queuePT.LastHash); + response.ReceiptResponse.ftReceiptIdentification = "FS " + _queuePT.SimplifiedInvoiceSeries + "/" + (++_queuePT.SimplifiedInvoiceSeriesNumerator).ToString().PadLeft(4, '0'); + var qrCode = PortugalReceiptCalculations.CreateSimplifiedInvoiceQRCodeAnonymousCustomer(hash, _queuePT, _signaturCreationUnitPT, request.ReceiptRequest, response.ReceiptResponse); + response.ReceiptResponse.AddSignatureItem(new Api.POS.Models.ifPOS.v2.SignatureItem + { + Caption = "ATCUD", + Data = _queuePT.ATCUD, + ftSignatureFormat = 0x0001, + ftSignatureType = (long) SignatureTypesPT.ATCUD, + }); + response.ReceiptResponse.AddSignatureItem(new Api.POS.Models.ifPOS.v2.SignatureItem + { + Caption = "Hash", + Data = hash, + ftSignatureFormat = 0x0000_0000_0001_0001, + ftSignatureType = (long) SignatureTypesPT.Hash, + }); + response.ReceiptResponse.AddSignatureItem(new Api.POS.Models.ifPOS.v2.SignatureItem + { + Caption = "Hash", + Data = hash, + ftSignatureFormat = 0x0001, + ftSignatureType = (long) SignatureTypesPT.HashPrint, + }); + response.ReceiptResponse.AddSignatureItem(SignaturItemFactory.CreatePTQRCode(qrCode)); + _queuePT.LastHash = hash; + return await Task.FromResult(new ProcessCommandResponse(response.ReceiptResponse, new List())).ConfigureAwait(false); + } + + public async Task PaymentTransfer0x0002Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task PointOfSaleReceiptWithoutObligation0x0003Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task ECommerce0x0004Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); + + public async Task Protocol0x0005Async(ProcessCommandRequest request) => await Task.FromResult(new ProcessCommandResponse(request.ReceiptResponse, new List())).ConfigureAwait(false); +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/QueuePTBootstrapper.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/QueuePTBootstrapper.cs new file mode 100644 index 000000000..270564366 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/QueuePTBootstrapper.cs @@ -0,0 +1,48 @@ +using fiskaltrust.Middleware.Localization.QueuePT.Processors; +using fiskaltrust.Middleware.Localization.QueuePT.PTSSCD; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Localization.v2.Configuration; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.Storage; +using fiskaltrust.Middleware.Storage.PT; +using Microsoft.Extensions.Logging; + +namespace fiskaltrust.Middleware.Localization.QueuePT; + +public class QueuePTBootstrapper : IV2QueueBootstrapper +{ + private readonly Queue _queue; + + public QueuePTBootstrapper(Guid id, ILoggerFactory loggerFactory, Dictionary configuration) + { + var middlewareConfiguration = MiddlewareConfigurationFactory.CreateMiddlewareConfiguration(id, configuration); + var queuePT = Newtonsoft.Json.JsonConvert.DeserializeObject>(configuration["init_ftQueuePT"]!.ToString()!).First(); + var storageProvider = new AzureStorageProvider(loggerFactory, id, configuration); + var signaturCreationUnitPT = Newtonsoft.Json.JsonConvert.DeserializeObject>(configuration["init_ftSignaturCreationUnitPT"]!.ToString()!).First(); + var ptSSCD = new InMemorySCU(signaturCreationUnitPT); + var queueStorageProvider = new QueueStorageProvider(id, storageProvider); + var signProcessorPT = new ReceiptProcessor(loggerFactory.CreateLogger(), new LifecycleCommandProcessorPT(storageProvider.GetConfigurationRepository()), new ReceiptCommandProcessorPT(ptSSCD, queuePT, signaturCreationUnitPT), new DailyOperationsCommandProcessorPT(), new InvoiceCommandProcessorPT(), new ProtocolCommandProcessorPT()); + var signProcessor = new SignProcessor(loggerFactory.CreateLogger(), queueStorageProvider, signProcessorPT.ProcessAsync, queuePT.CashBoxIdentification, middlewareConfiguration); + var journalProcessor = new JournalProcessor(storageProvider, new JournalProcessorPT(storageProvider), configuration, loggerFactory.CreateLogger()); + _queue = new Queue(signProcessor, journalProcessor, loggerFactory) + { + Id = id, + Configuration = configuration, + }; + } + + public Func> RegisterForSign() + { + return _queue.RegisterForSign(); + } + + public Func> RegisterForEcho() + { + return _queue.RegisterForEcho(); + } + + public Func> RegisterForJournal() + { + return _queue.RegisterForJournal(); + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/QueuePTConfiguration.cs b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/QueuePTConfiguration.cs new file mode 100644 index 000000000..177f749e3 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/QueuePTConfiguration.cs @@ -0,0 +1,11 @@ +using fiskaltrust.Middleware.Contracts.Models; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.Localization.QueuePT; + +public class QueuePTConfiguration +{ + public bool Sandbox { get; set; } = false; + + public static QueuePTConfiguration FromMiddlewareConfiguration(MiddlewareConfiguration middlewareConfiguration) => JsonConvert.DeserializeObject(JsonConvert.SerializeObject(middlewareConfiguration.Configuration)); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.QueuePT/fiskaltrust.Middleware.Localization.QueuePT.csproj b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/fiskaltrust.Middleware.Localization.QueuePT.csproj new file mode 100644 index 000000000..21b3ed487 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.QueuePT/fiskaltrust.Middleware.Localization.QueuePT.csproj @@ -0,0 +1,20 @@ + + + + net8 + Latest + enable + enable + + + + + + + + + + + + + \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/AzureStorageProvider.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/AzureStorageProvider.cs new file mode 100644 index 000000000..45e0797bd --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/AzureStorageProvider.cs @@ -0,0 +1,116 @@ +using System.Collections.ObjectModel; +using System.Text; +using Azure.Data.Tables; +using Azure.Identity; +using Azure.Storage.Blobs; +using fiskaltrust.Middleware.Abstractions; +using fiskaltrust.Middleware.Contracts.Repositories; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Storage.AzureTableStorage; +using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories; +using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.MasterData; +using fiskaltrust.Middleware.Storage.Base; +using fiskaltrust.storage.encryption.V0; +using fiskaltrust.storage.V0; +using fiskaltrust.storage.V0.MasterData; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.Localization.v2; + +public class AzureStorageProvider : BaseStorageBootStrapper, IStorageProvider +{ + private readonly QueueConfiguration _queueConfiguration; + private readonly ILogger _logger; + private readonly Dictionary _configuration; + private readonly AzureTableStorageConfiguration _tableStorageConfiguration; + private readonly TableServiceClient _tableServiceClient; + private readonly BlobServiceClient _blobServiceClient; + + private readonly TaskCompletionSource _initializedCompletionSource; + public Task Initialized => _initializedCompletionSource.Task; + + public AzureStorageProvider(ILoggerFactory loggerFactory, Guid id, Dictionary configuration) + { + _configuration = configuration; + _initializedCompletionSource = new TaskCompletionSource(); + _tableStorageConfiguration = AzureTableStorageConfiguration.FromConfigurationDictionary(configuration); + _queueConfiguration = new QueueConfiguration { QueueId = id }; + _logger = loggerFactory.CreateLogger(); + + if (!string.IsNullOrEmpty(_tableStorageConfiguration.StorageAccountName)) + { + Uri tableUri; + Uri blobUri; + try + { + tableUri = new Uri($"https://{_tableStorageConfiguration.StorageAccountName}.table.core.windows.net/"); + blobUri = new Uri($"https://{_tableStorageConfiguration.StorageAccountName}.blob.core.windows.net/"); + } + catch (Exception e) + { + throw new Exception($"The value for the queue parameter storageaccountname '{_tableStorageConfiguration.StorageAccountName}' is not valid.", e); + } + _tableServiceClient = new TableServiceClient(tableUri, new ChainedTokenCredential(new VisualStudioCredential(), new AzureCliCredential(), new DefaultAzureCredential())); + _blobServiceClient = new BlobServiceClient(blobUri, new ChainedTokenCredential(new VisualStudioCredential(), new AzureCliCredential(), new DefaultAzureCredential())); + } + else if (!string.IsNullOrEmpty(_tableStorageConfiguration.ConnectionString)) + { + string connectionString; + if (_tableStorageConfiguration.ConnectionString.StartsWith("raw:")) + { + connectionString = _tableStorageConfiguration.ConnectionString.Substring("raw:".Length); + } + else + { + connectionString = Encoding.UTF8.GetString(Encryption.Decrypt(Convert.FromBase64String(_tableStorageConfiguration.ConnectionString), _queueConfiguration.QueueId.ToByteArray())); + } + _tableServiceClient = new TableServiceClient(connectionString); + _blobServiceClient = new BlobServiceClient(connectionString); + } + else if (!string.IsNullOrEmpty(_tableStorageConfiguration.StorageConnectionString)) + { + _logger.LogWarning("The queue parameter 'storageconnectionstring' is deprecated. Please use 'storageaccountname' or 'connectionstring' instead."); + _tableServiceClient = new TableServiceClient(_tableStorageConfiguration.StorageConnectionString); + _blobServiceClient = new BlobServiceClient(_tableStorageConfiguration.StorageConnectionString); + } + else + { + throw new Exception("Either the parameter 'storageaccountname' or 'storageconnectionstring' needs to be defined."); + } + + Task.Run(() => InitAsync()); + } + + public IConfigurationRepository GetConfigurationRepository() => new AzureTableStorageConfigurationRepository(_queueConfiguration, _tableServiceClient); + public IMiddlewareActionJournalRepository GetMiddlewareActionJournalRepository() => new AzureTableStorageActionJournalRepository(_queueConfiguration, _tableServiceClient); + public IMiddlewareQueueItemRepository GetMiddlewareQueueItemRepository() => new AzureTableStorageQueueItemRepository(_queueConfiguration, _tableServiceClient, new AzureTableStorageReceiptReferenceIndexRepository(_queueConfiguration, _tableServiceClient)); + public IMiddlewareReceiptJournalRepository GetMiddlewareReceiptJournalRepository() => new AzureTableStorageReceiptJournalRepository(_queueConfiguration, _tableServiceClient); + + public IMasterDataRepository GetAccountMasterDataRepository() => new AzureTableStorageAccountMasterDataRepository(_queueConfiguration, _tableServiceClient); + public IMasterDataRepository GetOutletMasterDataRepository() => new AzureTableStorageOutletMasterDataRepository(_queueConfiguration, _tableServiceClient); + public IMasterDataRepository GetPosSystemMasterDataRepository() => new AzureTableStoragePosSystemMasterDataRepository(_queueConfiguration, _tableServiceClient); + public IMasterDataRepository GetAgencyMasterDataRepository() => new AzureTableStorageAgencyMasterDataRepository(_queueConfiguration, _tableServiceClient); + public async Task InitAsync() + { + try + { + var databaseMigrator = new DatabaseMigrator(_logger, _tableServiceClient, _blobServiceClient, _queueConfiguration); + await databaseMigrator.MigrateAsync().ConfigureAwait(false); + + var configurationRepository = new AzureTableStorageConfigurationRepository(_queueConfiguration, _tableServiceClient); + var baseStorageConfig = ParseStorageConfiguration(_configuration); + + await PersistMasterDataAsync(baseStorageConfig, configurationRepository, + new AzureTableStorageAccountMasterDataRepository(_queueConfiguration, _tableServiceClient), new AzureTableStorageOutletMasterDataRepository(_queueConfiguration, _tableServiceClient), + new AzureTableStorageAgencyMasterDataRepository(_queueConfiguration, _tableServiceClient), new AzureTableStoragePosSystemMasterDataRepository(_queueConfiguration, _tableServiceClient)).ConfigureAwait(false); + await PersistConfigurationAsync(baseStorageConfig, configurationRepository, _logger).ConfigureAwait(false); + _initializedCompletionSource.SetResult(); + } + catch (Exception e) + { + _logger.LogError(e, "Error during initialization of the AzureStorageProvider."); + _initializedCompletionSource.SetException(e); + } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/MiddlewareConfiguration.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/MiddlewareConfiguration.cs new file mode 100644 index 000000000..754f0fe84 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/MiddlewareConfiguration.cs @@ -0,0 +1,16 @@ +namespace fiskaltrust.Middleware.Localization.v2.Configuration; + +public class MiddlewareConfiguration +{ + public Guid QueueId { get; set; } + public Guid CashBoxId { get; set; } + public int ReceiptRequestMode { get; set; } + public int TarFileChunkSize { get; set; } = 1024 * 1024; // 1 MB + public int JournalChunkSize { get; set; } = 1024 * 1024; // 1 MB + public bool AllowUnsafeScuSwitch { get; set; } + public bool IsSandbox { get; set; } + public string? ServiceFolder { get; set; } + public Action? OnMessage { get; set; } + public Dictionary? Configuration { get; set; } + public Dictionary? PreviewFeatures { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/MiddlewareConfigurationFactory.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/MiddlewareConfigurationFactory.cs new file mode 100644 index 000000000..ff35716fc --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/MiddlewareConfigurationFactory.cs @@ -0,0 +1,35 @@ +using fiskaltrust.storage.V0; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.Localization.v2.Configuration; + +public static class MiddlewareConfigurationFactory +{ + public static MiddlewareConfiguration CreateMiddlewareConfiguration(Guid id, Dictionary configuration) + { + return new MiddlewareConfiguration + { + CashBoxId = GetQueueCashbox(id, configuration), + QueueId = id, + IsSandbox = configuration.TryGetValue("sandbox", out var sandbox) && bool.TryParse(sandbox.ToString(), out var sandboxBool) && sandboxBool, + ServiceFolder = configuration.TryGetValue("servicefolder", out var val) ? val.ToString() : GetServiceFolder(), + Configuration = configuration + }; + } + + private static string GetServiceFolder() => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "fiskaltrust", "service"); + + private static Guid GetQueueCashbox(Guid queueId, Dictionary configuration) + { + var key = "init_ftQueue"; + if (configuration.ContainsKey(key)) + { + var queues = JsonConvert.DeserializeObject>(configuration[key]!.ToString()!); + return queues.Where(q => q.ftQueueId == queueId).First().ftCashBoxId; + } + else + { + throw new ArgumentException("Configuration must contain 'init_ftQueue' parameter."); + } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/PackageConfiguration.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/PackageConfiguration.cs new file mode 100644 index 000000000..4f62b23a9 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/PackageConfiguration.cs @@ -0,0 +1,30 @@ +using System.Text.Json.Serialization; + +namespace fiskaltrust.Middleware.Localization.v2.Configuration; + +public class PackageConfiguration +{ + [JsonPropertyName("Id")] + public Guid Id { get; set; } + + [JsonPropertyName("Package")] + public string Package { get; set; } + + [JsonPropertyName("Version")] + public string Version { get; set; } + + [JsonPropertyName("Configuration")] + public Dictionary? Configuration { get; set; } + + [JsonPropertyName("Url")] + public List? Url { get; set; } + + public PackageConfiguration() + { + Id = Guid.Empty; + Package = string.Empty; + Version = string.Empty; + Configuration = null; + Url = null; + } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/ftCashBoxConfiguration.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/ftCashBoxConfiguration.cs new file mode 100644 index 000000000..e90a7d35c --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Configuration/ftCashBoxConfiguration.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace fiskaltrust.Middleware.Localization.v2.Configuration; + +public class ftCashBoxConfiguration +{ + [JsonPropertyName("helpers")] + public List? helpers { get; set; } + + [JsonPropertyName("ftCashBoxId")] + public Guid ftCashBoxId { get; private set; } + + [JsonPropertyName("ftSignaturCreationDevices")] + public List? ftSignaturCreationDevices { get; set; } + + [JsonPropertyName("ftQueues")] + public List? ftQueues { get; set; } + + [JsonPropertyName("PackTimeStampage")] + public long TimeStamp { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/EchoProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/EchoProcessor.cs new file mode 100644 index 000000000..4bb75ed39 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/EchoProcessor.cs @@ -0,0 +1,15 @@ +using fiskaltrust.ifPOS.v1; +using fiskaltrust.Middleware.Localization.v2.Interface; + +namespace fiskaltrust.Middleware.Localization.v2; + +public class EchoProcessor : IEchoProcessor +{ + public async Task ProcessAsync(EchoRequest echoRequest) + { + return await Task.FromResult(new EchoResponse + { + Message = echoRequest.Message, + }); + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/ChargeItemExtensions.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/ChargeItemExtensions.cs new file mode 100644 index 000000000..e17c97301 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/ChargeItemExtensions.cs @@ -0,0 +1,8 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.v2.Helpers; + +public static class ChargeItemExt +{ + public static decimal GetVATAmount(this ChargeItem chargeItem) => chargeItem.VATAmount ?? (chargeItem.Amount / (100 + chargeItem.VATRate) * chargeItem.VATRate); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/CryptoHelper.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/CryptoHelper.cs new file mode 100644 index 000000000..bc80471f3 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/CryptoHelper.cs @@ -0,0 +1,71 @@ +using System.Security.Cryptography; +using System.Text; +using fiskaltrust.storage.encryption.V0; +using fiskaltrust.storage.V0; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace fiskaltrust.Middleware.Localization.v2.Helpers; +public class CryptoHelper +{ + private const string ES256_JWS_HEADER = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9"; + + private static readonly X9ECParameters _curve = SecNamedCurves.GetByName("secp256r1"); + private static readonly ECDomainParameters _domainParameters = new ECDomainParameters(_curve.Curve, _curve.G, _curve.N, _curve.H); + + public (string hashBase64, string jwsData) CreateJwsToken(string payload, string privateKeyBase64, byte[] encryptionKey) + { + var jwsPayload = StringUtilities.ToBase64UrlString(Encoding.UTF8.GetBytes(payload)); + var data = Encoding.UTF8.GetBytes($"{ES256_JWS_HEADER}.{jwsPayload}"); + var sha256 = new Sha256Digest(); + sha256.Reset(); + sha256.BlockUpdate(data, 0, data.Length); + + var hash = new byte[sha256.GetDigestSize()]; + sha256.DoFinal(hash, 0); + + var decrypted = Convert.FromBase64String(Encoding.UTF8.GetString(Encryption.Decrypt(Convert.FromBase64String(privateKeyBase64), encryptionKey))); + var privKeyParams = new ECPrivateKeyParameters(new Org.BouncyCastle.Math.BigInteger(decrypted), _domainParameters); + + var signer = SignerUtilities.GetSigner("SHA-256withECDSA"); + signer.Init(true, privKeyParams); + signer.BlockUpdate(data, 0, data.Length); + var signature = signer.GenerateSignature(); + var jwsSignature = StringUtilities.ToBase64UrlString(signature); + + return (Convert.ToBase64String(hash), $"{ES256_JWS_HEADER}.{jwsPayload}.{jwsSignature}"); + } + + public string GenerateBase64ChainHash(string previousReceiptHash, ftReceiptJournal receiptJournal, ftQueueItem queueItem) + { + using (var sha256 = SHA256.Create()) + { + var input = new List(); + + if (!string.IsNullOrWhiteSpace(previousReceiptHash)) + { + input.AddRange(Convert.FromBase64String(previousReceiptHash)); + } + input.AddRange(receiptJournal.ftReceiptJournalId.ToByteArray()); + input.AddRange(BitConverter.GetBytes(receiptJournal.ftReceiptMoment.Ticks)); + input.AddRange(BitConverter.GetBytes(receiptJournal.ftReceiptNumber)); + input.AddRange(Convert.FromBase64String(queueItem.requestHash)); + input.AddRange(Convert.FromBase64String(queueItem.responseHash)); + + var hash = sha256.ComputeHash(input.ToArray()); + return Convert.ToBase64String(hash); + } + } + + public string GenerateBase64Hash(string content) + { + using (var sha256 = SHA256.Create()) + { + var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(content)); + return Convert.ToBase64String(hash); + } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/QueueExtensions.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/QueueExtensions.cs new file mode 100644 index 000000000..082453319 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/QueueExtensions.cs @@ -0,0 +1,12 @@ +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.v2.Helpers; + +public static class QueueExtensions +{ + public static bool IsActive(this ftQueue queue) => queue.StartMoment.HasValue && !queue.StopMoment.HasValue; + + public static bool IsNew(this ftQueue queue) => !queue.StartMoment.HasValue && !queue.StopMoment.HasValue; + + public static bool IsDeactivated(this ftQueue queue) => queue.StartMoment.HasValue && queue.StopMoment.HasValue; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/StringUtilities.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/StringUtilities.cs new file mode 100644 index 000000000..545e60260 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/StringUtilities.cs @@ -0,0 +1,12 @@ +using System; + +namespace fiskaltrust.Middleware.Localization.v2.Helpers; + +public static class StringUtilities +{ + public static string ToBase64UrlString(byte[] bytes) + { + var base64 = Convert.ToBase64String(bytes); + return base64.TrimEnd(new char[] { '=' }).Replace('+', '-').Replace('/', '_'); + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/ftQueueItemExtensions.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/ftQueueItemExtensions.cs new file mode 100644 index 000000000..6065b4719 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Helpers/ftQueueItemExtensions.cs @@ -0,0 +1,56 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.v2.Helpers; + +public static class ftQueueItemExtensions +{ + public static bool IsReceiptRequestFinished(this ftQueueItem item) => item.ftDoneMoment != null && !string.IsNullOrWhiteSpace(item.response) && !string.IsNullOrWhiteSpace(item.responseHash); + + public static bool IsContentOfQueueItemEqualWithGivenRequest(this ftQueueItem item, ReceiptRequest data) + { + var itemRequest = System.Text.Json.JsonSerializer.Deserialize(item.request); + if (itemRequest == null) + { + return false; + } + if (itemRequest.cbChargeItems.Count == data.cbChargeItems.Count && itemRequest.cbPayItems.Count == data.cbPayItems.Count) + { + for (var i = 0; i < itemRequest.cbChargeItems.Count; i++) + { + if (itemRequest.cbChargeItems[i].Amount != data.cbChargeItems[i].Amount) + { + return false; + } + if (itemRequest.cbChargeItems[i].ftChargeItemCase != data.cbChargeItems[i].ftChargeItemCase) + { + return false; + } + if (itemRequest.cbChargeItems[i].Moment != data.cbChargeItems[i].Moment) + { + return false; + } + } + for (var i = 0; i < itemRequest.cbPayItems.Count; i++) + { + if (itemRequest.cbPayItems[i].Amount != data.cbPayItems[i].Amount) + { + return false; + } + if (itemRequest.cbPayItems[i].ftPayItemCase != data.cbPayItems[i].ftPayItemCase) + { + return false; + } + if (itemRequest.cbPayItems[i].Moment != data.cbPayItems[i].Moment) + { + return false; + } + } + } + else + { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IEchoProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IEchoProcessor.cs new file mode 100644 index 000000000..eeba8ed3d --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IEchoProcessor.cs @@ -0,0 +1,8 @@ +using fiskaltrust.ifPOS.v1; + +namespace fiskaltrust.Middleware.Localization.v2.Interface; + +public interface IEchoProcessor +{ + Task ProcessAsync(EchoRequest request); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IReceiptProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IReceiptProcessor.cs new file mode 100644 index 000000000..c29bc49ca --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IReceiptProcessor.cs @@ -0,0 +1,9 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.v2.Interface; + +public interface IReceiptProcessor +{ + Task<(ReceiptResponse receiptResponse, List actionJournals)> ProcessAsync(ReceiptRequest request, ReceiptResponse receiptResponse, ftQueue queue, ftQueueItem queueItem); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/ISignProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/ISignProcessor.cs new file mode 100644 index 000000000..9118a3d51 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/ISignProcessor.cs @@ -0,0 +1,8 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.v2.Interface; + +public interface ISignProcessor +{ + Task ProcessAsync(ReceiptRequest request); +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IStorageProvider.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IStorageProvider.cs new file mode 100644 index 000000000..2225057ec --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IStorageProvider.cs @@ -0,0 +1,19 @@ +using fiskaltrust.Middleware.Contracts.Repositories; +using fiskaltrust.storage.V0; +using fiskaltrust.storage.V0.MasterData; + +namespace fiskaltrust.Middleware.Localization.v2.Interface; + +public interface IStorageProvider +{ + Task Initialized { get; } + IConfigurationRepository GetConfigurationRepository(); + IMiddlewareQueueItemRepository GetMiddlewareQueueItemRepository(); + IMiddlewareReceiptJournalRepository GetMiddlewareReceiptJournalRepository(); + IMiddlewareActionJournalRepository GetMiddlewareActionJournalRepository(); + + IMasterDataRepository GetAccountMasterDataRepository(); + IMasterDataRepository GetOutletMasterDataRepository(); + IMasterDataRepository GetPosSystemMasterDataRepository(); + IMasterDataRepository GetAgencyMasterDataRepository(); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IV2QueueBootstrapper.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IV2QueueBootstrapper.cs new file mode 100644 index 000000000..d1abe9ef7 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/IV2QueueBootstrapper.cs @@ -0,0 +1,10 @@ +namespace fiskaltrust.Middleware.Localization.v2.Interface; + +public interface IV2QueueBootstrapper +{ + Func> RegisterForSign(); + + Func> RegisterForEcho(); + + Func> RegisterForJournal(); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/ReceiptRequestExtensions.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/ReceiptRequestExtensions.cs new file mode 100644 index 000000000..140acd821 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/ReceiptRequestExtensions.cs @@ -0,0 +1,42 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.v2.Interface; + +public static class ReceiptRequestExtensions +{ + public static long GetCasePart(this ReceiptRequest receiptRequest) => receiptRequest.ftReceiptCase & 0x0000_0000_0000_FFFF; + + public static bool IsVoid(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0000_0004_0000) > 0; + + public static bool IsRefund(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0000_0100_0000) > 0; + + public static bool IsInitialOperation(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0000_0000_FFFF) == 0x4001; + + public static bool IsLateSigning(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0000_0001_0000) == 0x0000_0000_0001_0000; + + public static bool IsReceiptOperation(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0000_0000_F000) == 0x0000; + + public static bool IsInvoiceOperation(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0000_0000_F000) == 0x1000; + + public static bool IsInvoiceB2COperation(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0000_0000_F00F) == 0x1001; + + public static bool IsDailyOperation(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0000_0000_F000) == 0x2000; + + public static bool IsSelfPricingOperation(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0F00_0000_0000) == 0x0000_0100_0000_0000; + + public static bool IsProtocolOperation(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0000_0000_F000) == 0x3000; + + public static bool IsLifeCycleOperation(this ReceiptRequest receiptRequest) => (receiptRequest.ftReceiptCase & 0x0000_0000_0000_F000) == 0x4000; + + public static string GetCountry(this ReceiptRequest data) + { + return (0xFFFF000000000000 & (ulong) data.ftReceiptCase) switch + { + 0x4445000000000000 => "DE", + 0x4652000000000000 => "FR", + 0x4D45000000000000 => "ME", + 0x4954000000000000 => "IT", + _ => "AT", + }; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/ReceiptResponseHelper.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/ReceiptResponseHelper.cs new file mode 100644 index 000000000..2f2966df1 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/ReceiptResponseHelper.cs @@ -0,0 +1,37 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.v2.Interface; + +public static class ReceiptResponseHelper +{ + public static void SetReceiptResponseError(this ReceiptResponse receiptResponse, string errorMessage) + { + receiptResponse.ftState |= 0xEEEE_EEEE; + receiptResponse.ftSignatures = []; + receiptResponse.AddSignatureItem(new SignatureItem + { + Caption = "FAILURE", + Data = errorMessage, + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = (long) ((ulong) receiptResponse.ftState & (ulong) 0xFFFF_F000_0000_0000) | 0x3000 + }); + } + + public static void MarkAsDisabled(this ReceiptResponse receiptResponse) + { + receiptResponse.ftState += ftStatesFlags.SECURITY_MECHAMISN_DEACTIVATED; + } + + public static void InsertSignatureItems(this ReceiptResponse receiptResponse, List signaturItems) + { + receiptResponse.ftSignatures.InsertRange(0, signaturItems); + } + + public static void AddSignatureItem(this ReceiptResponse receiptResponse, SignatureItem signaturItem) + { + receiptResponse.ftSignatures.Add(signaturItem); + } + + public static bool HasFailed(this ReceiptResponse receiptRespons) => (receiptRespons.ftState & 0xFFFF_FFFF) == 0xEEEE_EEEE; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/SignatureFactory.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/SignatureFactory.cs new file mode 100644 index 000000000..299f5389a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Interface/SignatureFactory.cs @@ -0,0 +1,15 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.v2.Interface; + +public static class SignatureFactory +{ + public static SignatureItem CreateSandboxSignature(Guid queueId) => + new SignatureItem + { + Caption = "S A N D B O X", + Data = queueId.ToString(), + ftSignatureFormat = (long) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = (long) ifPOS.v1.SignaturItem.Types.Unknown + }; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/JournalProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/JournalProcessor.cs new file mode 100644 index 000000000..c9da6a8bc --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/JournalProcessor.cs @@ -0,0 +1,176 @@ +using System.Text; +using fiskaltrust.ifPOS.v1; +using fiskaltrust.Middleware.Contracts.Constants; +using fiskaltrust.Middleware.Contracts.Interfaces; +using fiskaltrust.Middleware.Contracts.Repositories; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Storage.ES; +using fiskaltrust.Middleware.Storage.GR; +using fiskaltrust.Middleware.Storage.PT; +using fiskaltrust.storage.V0; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.Localization.v2; + +public interface IJournalProcessor +{ + IAsyncEnumerable ProcessAsync(JournalRequest request); +} + +public class JournalProcessor : IJournalProcessor +{ + private readonly storage.V0.IReadOnlyConfigurationRepository _configurationRepository; + private readonly IMiddlewareRepository _queueItemRepository; + private readonly IMiddlewareRepository _receiptJournalRepository; + private readonly IMiddlewareRepository _actionJournalRepository; + private readonly IJournalProcessor _marketSpecificJournalProcessor; + private readonly ILogger _logger; + private readonly Dictionary _configuration; + + public JournalProcessor( + IStorageProvider storageProvider, + IJournalProcessor marketSpecificJournalProcessor, + Dictionary configuration, + ILogger logger) + { + _configurationRepository = storageProvider.GetConfigurationRepository(); + _queueItemRepository = storageProvider.GetMiddlewareQueueItemRepository(); + _receiptJournalRepository = storageProvider.GetMiddlewareReceiptJournalRepository(); + _actionJournalRepository = storageProvider.GetMiddlewareActionJournalRepository(); + _marketSpecificJournalProcessor = marketSpecificJournalProcessor; + _configuration = configuration; + _logger = logger; + } + + public IAsyncEnumerable ProcessAsync(JournalRequest request) + { + try + { + if ((0xFFFF000000000000 & (ulong) request.ftJournalType) != 0) + { + return _marketSpecificJournalProcessor.ProcessAsync(request); + } + + return request.ftJournalType switch + { + (long) JournalTypes.ActionJournal => ToJournalResponseAsync(GetEntitiesAsync(_actionJournalRepository, request), request.MaxChunkSize), + (long) JournalTypes.ReceiptJournal => ToJournalResponseAsync(GetEntitiesAsync(_receiptJournalRepository, request), request.MaxChunkSize), + (long) JournalTypes.QueueItem => ToJournalResponseAsync(GetEntitiesAsync(_queueItemRepository, request), request.MaxChunkSize), + (long) JournalTypes.Configuration => new List { + new JournalResponse + { + Chunk = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(GetConfiguration().Result)).ToList() + } + }.ToAsyncEnumerable(), + _ => new List { + new JournalResponse + { + Chunk = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new + { + Assembly = typeof(JournalProcessor).Assembly.GetName().FullName, + typeof(JournalProcessor).Assembly.GetName().Version + } + )).ToList() + } + }.ToAsyncEnumerable() + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occured while processing the Journal request."); + throw; + } + } + + private async Task GetConfiguration() + { + return new + { + Assembly = typeof(JournalProcessor).Assembly.GetName().FullName, + typeof(JournalProcessor).Assembly.GetName().Version, + CashBoxList = await _configurationRepository.GetCashBoxListAsync().ConfigureAwait(false), + QueueList = await _configurationRepository.GetQueueListAsync().ConfigureAwait(false), + QueueATList = await _configurationRepository.GetQueueATListAsync().ConfigureAwait(false), + QueueDEList = await _configurationRepository.GetQueueDEListAsync().ConfigureAwait(false), + QueueESList = GetConfigurationFromDictionary("init_ftQueueES"), + QueueFRList = await _configurationRepository.GetQueueFRListAsync().ConfigureAwait(false), + QueueGRList = GetConfigurationFromDictionary("init_ftQueueGR"), + QueueITList = await _configurationRepository.GetQueueITListAsync().ConfigureAwait(false), + QueueMEList = await _configurationRepository.GetQueueMEListAsync().ConfigureAwait(false), + QueuePTList = GetConfigurationFromDictionary("init_ftQueuePT"), + SignaturCreationUnitATList = await _configurationRepository.GetSignaturCreationUnitATListAsync().ConfigureAwait(false), + SignaturCreationUnitDEList = await _configurationRepository.GetSignaturCreationUnitDEListAsync().ConfigureAwait(false), + SignaturCreationUnitESList = GetConfigurationFromDictionary("init_ftSignaturCreationUnitES"), + SignaturCreationUnitFRList = await _configurationRepository.GetSignaturCreationUnitFRListAsync().ConfigureAwait(false), + SignaturCreationUnitGRList = GetConfigurationFromDictionary("init_ftSignaturCreationUnitGR"), + SignaturCreationUnitITList = await _configurationRepository.GetSignaturCreationUnitITListAsync().ConfigureAwait(false), + SignaturCreationUnitMEList = await _configurationRepository.GetSignaturCreationUnitMEListAsync().ConfigureAwait(false), + SignaturCreationUnitPTList = GetConfigurationFromDictionary("init_ftSignaturCreationUnitPT"), + }; + } + + private List GetConfigurationFromDictionary(string key) + { + try + { + if (_configuration.ContainsKey(key)) + { + return JsonConvert.DeserializeObject>(_configuration[key]!.ToString()!); + } + } + catch (Exception ex) + { + _logger.LogError(ex, "An error occured while processing the Journal request."); + } + return []; + + } + + private async IAsyncEnumerable ToJournalResponseAsync(IAsyncEnumerable asyncEnumerable, int chunkSize) + { + using var memoryStream = new MemoryStream(); + using var writer = new StreamWriter(memoryStream); + using var jsonWriter = new JsonTextWriter(writer); + var serializer = new JsonSerializer(); + serializer.Serialize(jsonWriter, await asyncEnumerable.ToArrayAsync().ConfigureAwait(false)); + jsonWriter.Flush(); + if (memoryStream.Length < chunkSize) + { + yield return new JournalResponse + { + Chunk = memoryStream.ToArray().ToList() + }; + } + else + { + memoryStream.Seek(0, SeekOrigin.Begin); + var buffer = new byte[chunkSize]; + int readAmount; + while ((readAmount = await memoryStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) > 0) + { + yield return new JournalResponse + { + Chunk = buffer.Take(readAmount).ToList() + }; + buffer = new byte[chunkSize]; + } + } + } + + private IAsyncEnumerable GetEntitiesAsync(IMiddlewareRepository repository, JournalRequest request) + { + if (request.To < 0) + { + return repository.GetEntriesOnOrAfterTimeStampAsync(request.From, take: (int) -request.To); + } + else if (request.To == 0) + { + return repository.GetEntriesOnOrAfterTimeStampAsync(request.From); + } + else + { + return repository.GetByTimeStampRangeAsync(request.From, request.To); + } + } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/MasterData/IMasterDataService.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/MasterData/IMasterDataService.cs new file mode 100644 index 000000000..e665e297a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/MasterData/IMasterDataService.cs @@ -0,0 +1,11 @@ +using fiskaltrust.storage.V0.MasterData; + +namespace fiskaltrust.Middleware.Localization.v2.MasterData; + +public interface IMasterDataService +{ + Task GetCurrentDataAsync(); + Task PersistConfigurationAsync(); + Task HasDataChangedAsync(); + MasterDataConfiguration? GetFromConfig(); +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/MasterData/MasterDataService.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/MasterData/MasterDataService.cs new file mode 100644 index 000000000..c461b3a42 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/MasterData/MasterDataService.cs @@ -0,0 +1,92 @@ +using fiskaltrust.Middleware.Contracts.Models; +using fiskaltrust.Middleware.Contracts.Repositories; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.storage.V0.MasterData; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.Localization.v2.MasterData; +public class MasterDataService : IMasterDataService +{ + public const string CONFIG_KEY = "init_masterData"; + + private readonly Dictionary _configuration; + private readonly IMasterDataRepository _accountMasterDataRepository; + private readonly IMasterDataRepository _outletMasterDataRepository; + private readonly IMasterDataRepository _posSystemMasterDataRepository; + private readonly IMasterDataRepository _agencyMasterDataRepository; + + public MasterDataService(Dictionary configuration, IStorageProvider storageProvider) + { + _configuration = configuration; + _accountMasterDataRepository = storageProvider.GetAccountMasterDataRepository(); + _outletMasterDataRepository = storageProvider.GetOutletMasterDataRepository(); + _posSystemMasterDataRepository = storageProvider.GetPosSystemMasterDataRepository(); + _agencyMasterDataRepository = storageProvider.GetAgencyMasterDataRepository(); + } + + public async Task GetCurrentDataAsync() + { + return new MasterDataConfiguration + { + Account = (await _accountMasterDataRepository.GetAsync().ConfigureAwait(false))?.FirstOrDefault(), + Outlet = (await _outletMasterDataRepository.GetAsync().ConfigureAwait(false))?.FirstOrDefault(), + Agencies = await _agencyMasterDataRepository.GetAsync().ConfigureAwait(false), + PosSystems = await _posSystemMasterDataRepository.GetAsync().ConfigureAwait(false) + }; + } + + public async Task HasDataChangedAsync() + { + if (!_configuration.ContainsKey(CONFIG_KEY) || string.IsNullOrEmpty(_configuration[CONFIG_KEY]?.ToString())) + { + return false; + } + + var currentJson = JsonConvert.SerializeObject(await GetCurrentDataAsync().ConfigureAwait(false)); + var nextJson = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(_configuration[CONFIG_KEY].ToString()!)); + + return currentJson != nextJson; + } + + public MasterDataConfiguration? GetFromConfig() + { + if (!_configuration.ContainsKey(CONFIG_KEY) || string.IsNullOrEmpty(_configuration[CONFIG_KEY]?.ToString())) + { + return null; + } + + return _configuration.ContainsKey(CONFIG_KEY) + ? JsonConvert.DeserializeObject(_configuration[CONFIG_KEY].ToString()!) + : null; + } + + public async Task PersistConfigurationAsync() + { + if (!_configuration.ContainsKey(CONFIG_KEY) || string.IsNullOrEmpty(_configuration[CONFIG_KEY]?.ToString())) + { + return; + } + + var masterdata = JsonConvert.DeserializeObject(_configuration[CONFIG_KEY].ToString()!); + if (masterdata != null) + { + await _accountMasterDataRepository.ClearAsync().ConfigureAwait(false); + await _accountMasterDataRepository.CreateAsync(masterdata.Account).ConfigureAwait(false); + + await _outletMasterDataRepository.ClearAsync().ConfigureAwait(false); + await _outletMasterDataRepository.CreateAsync(masterdata.Outlet).ConfigureAwait(false); + + await _agencyMasterDataRepository.ClearAsync().ConfigureAwait(false); + foreach (var agency in masterdata.Agencies ?? Enumerable.Empty()) + { + await _agencyMasterDataRepository.CreateAsync(agency).ConfigureAwait(false); + } + + await _posSystemMasterDataRepository.ClearAsync().ConfigureAwait(false); + foreach (var posSystem in masterdata.PosSystems ?? Enumerable.Empty()) + { + await _posSystemMasterDataRepository.CreateAsync(posSystem).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/ChargeItemCaseVat.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/ChargeItemCaseVat.cs new file mode 100644 index 000000000..6a67e8eef --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/ChargeItemCaseVat.cs @@ -0,0 +1,14 @@ +namespace fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +public enum ChargeItemCaseVat +{ + UnknownService = 0, // Unknown type of service for IT (1.3.45) + DiscountedVatRate1 = 1, // Discounted-1 VAT rate (as of 1.1.2022, this is 10%) (1.3.45) + DiscountedVatRate2 = 2, // Discounted 2 VAT rate (as of 1.1.2022, this is 5%) (1.3.45) + NormalVatRate = 3, // Normal VAT rate (as of 1.1.2022, this is 22%) (1.3.45) + SuperReducedVatRate1 = 4, // Super reduced 1 VAT rate (1.3.45) + SuperReducedVatRate2 = 5, // Super reduced 2 VAT rate (1.3.45) + ParkingVatRate = 6, // Parking VAT rate, Reversal of tax liability (1.3.45) + ZeroVatRate = 7, // Zero VAT rate (1.3.45) + NotTaxable = 8 // Not taxable (for processing, see 0x4954000000000001) (1.3.45) +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/PayItemCases.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/PayItemCases.cs new file mode 100644 index 000000000..e0e85f9c1 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/PayItemCases.cs @@ -0,0 +1,21 @@ +namespace fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +public enum PayItemCases : long +{ + UnknownPaymentType = 0x00, + CashPayment = 0x01, + NonCash = 0x02, + CrossedCheque = 0x03, + DebitCardPayment = 0x04, + CreditCardPayment = 0x05, + VoucherPaymentCouponVoucherByMoneyValue = 0x06, + OnlinePayment = 0x07, + LoyaltyProgramCustomerCardPayment = 0x08, + AccountsReceivable = 0x09, + SEPATransfer = 0x0A, + OtherBankTransfer = 0x0B, + TransferToCashbookVaultOwnerEmployee = 0x0C, + InternalMaterialConsumption = 0x0D, + Grant = 0x0E, + TicketRestaurant = 0x0F +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/ReceiptCases.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/ReceiptCases.cs new file mode 100644 index 000000000..b3561d5cf --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/ReceiptCases.cs @@ -0,0 +1,35 @@ +namespace fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +public enum ReceiptCases : long +{ + UnknownReceipt0x0000 = 0x0000, + PointOfSaleReceipt0x0001 = 0x0001, + PaymentTransfer0x0002 = 0x0002, + PointOfSaleReceiptWithoutObligation0x0003 = 0x0003, + ECommerce0x0004 = 0x0004, + Protocol0x0005 = 0x0005, + + InvoiceUnknown0x1000 = 0x1000, + InvoiceB2C0x1001 = 0x1001, + InvoiceB2B0x1002 = 0x1002, + InvoiceB2G0x1003 = 0x1003, + + ZeroReceipt0x2000 = 0x2000, + OneReceipt0x2001 = 0x2001, + ShiftClosing0x2010 = 0x2010, + DailyClosing0x2011 = 0x2011, + MonthlyClosing0x2012 = 0x2012, + YearlyClosing0x2013 = 0x2013, + + ProtocolUnspecified0x3000 = 0x3000, + ProtocolTechnicalEvent0x3001 = 0x3001, + ProtocolAccountingEvent0x3002 = 0x3002, + InternalUsageMaterialConsumption0x3003 = 0x3003, + Order0x3004 = 0x3004, + CopyReceiptPrintExistingReceipt0x3010 = 0x3010, + + InitialOperationReceipt0x4001 = 0x4001, + OutOfOperationReceipt0x4002 = 0x4002, + InitSCUSwitch0x4011 = 0x4011, + FinishSCUSwitch0x4012 = 0x4012, +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/ftStatesFlags.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/ftStatesFlags.cs new file mode 100644 index 000000000..a1c1edecc --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Cases/ftStatesFlags.cs @@ -0,0 +1,18 @@ +namespace fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +public static class ftStatesFlags +{ + public const long SECURITY_MECHAMISN_DEACTIVATED = 0x0000_0000_0000_0001; + + public const long SCU_TEMPORARY_OUT_OF_SERVICE = 0x0000_0000_0000_0002; + + public const long LATESIGNINGMODE_ISACTIVE = 0x0000_0000_0000_0008; + + public const long MESSAGE_IS_PENDING = 0x0000_0000_0000_0040; + + public const long DAILY_CLOSING_IS_DUE = 0x0000_0000_0000_0100; + + public const long ERROR = 0x0000_0000_EEEE_EEEE; + + public const long FAIL = 0x0000_0000_FFFF_FFFF; +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/ChargeItem.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/ChargeItem.cs new file mode 100644 index 000000000..db2bf632c --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/ChargeItem.cs @@ -0,0 +1,116 @@ +using System; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace fiskaltrust.Api.POS.Models.ifPOS.v2; + +public class ChargeItem +{ + [JsonPropertyName("ftChargeItemId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public Guid? ftChargeItemId { get; set; } + + [JsonPropertyName("Quantity")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required decimal Quantity { get; set; } = 1m; + + [JsonPropertyName("Description")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required string Description { get; set; } + + [JsonPropertyName("Amount")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required decimal Amount { get; set; } = 0; + + [JsonPropertyName("VATRate")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required decimal VATRate { get; set; } = 0; + + [JsonPropertyName("ftChargeItemCase")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public long ftChargeItemCase { get; set; } = 0; + + [JsonPropertyName("ftChargeItemCaseData")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public object? ftChargeItemCaseData { get; set; } + + [JsonPropertyName("VATAmount")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public decimal? VATAmount { get; set; } + + [JsonPropertyName("Moment")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public DateTime? Moment { get; set; } + + [JsonPropertyName("Position")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public decimal Position { get; set; } = 0; + + [JsonPropertyName("AccountNumber")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? AccountNumber { get; set; } + + [JsonPropertyName("CostCenter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? CostCenter { get; set; } + + [JsonPropertyName("ProductGroup")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? ProductGroup { get; set; } + + [JsonPropertyName("ProductNumber")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? ProductNumber { get; set; } + + [JsonPropertyName("ProductBarcode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? ProductBarcode { get; set; } + + [JsonPropertyName("Unit")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? Unit { get; set; } + + [JsonPropertyName("UnitQuantity")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public decimal? UnitQuantity { get; set; } + + [JsonPropertyName("UnitPrice")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public decimal? UnitPrice { get; set; } + + [JsonPropertyName("Currency")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonConverter(typeof(JsonStringEnumConverter))] + [DataMember(Order = 170, EmitDefaultValue = false, IsRequired = false)] + public Currency Currency { get; set; } + + [JsonPropertyName("DecimalPrecisionMultiplier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 180, EmitDefaultValue = false, IsRequired = false)] + public int DecimalPrecisionMultiplierSerialization + { + get => DecimalPrecisionMultiplier == 1 ? 0 : DecimalPrecisionMultiplier; + set => DecimalPrecisionMultiplier = value == 0 ? 1 : value; + } + + [JsonIgnore] + public int DecimalPrecisionMultiplier { get; set; } = 1; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Currency.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Currency.cs new file mode 100644 index 000000000..5cb4395de --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/Currency.cs @@ -0,0 +1,184 @@ +namespace fiskaltrust.Api.POS.Models.ifPOS.v2; + +public enum Currency +{ + EUR, + CHF, + CZK, + HUF, + BAM, + DKK, + RON, + NOK, + PLN, + RSD, + SEK, + UAH, + USD, + AED, + AFN, + ALL, + AMD, + ANG, + AOA, + ARS, + AUD, + AWG, + AZN, + BBD, + BDT, + BGN, + BHD, + BIF, + BMD, + BND, + BOB, + BOV, + BRL, + BSD, + BTN, + BWP, + BYN, + BZD, + CAD, + CDF, + CHE, + CHW, + CLF, + CLP, + CNY, + COP, + COU, + CRC, + CUP, + CVE, + DJF, + DOP, + DZD, + EGP, + ERN, + ETB, + FJD, + FKP, + GBP, + GEL, + GHS, + GIP, + GMD, + GNF, + GTQ, + GYD, + HKD, + HNL, + HTG, + IDR, + ILS, + INR, + IQD, + IRR, + ISK, + JMD, + JOD, + JPY, + KES, + KGS, + KHR, + KMF, + KPW, + KRW, + KWD, + KYD, + KZT, + LAK, + LBP, + LKR, + LRD, + LSL, + LYD, + MAD, + MDL, + MGA, + MKD, + MMK, + MNT, + MOP, + MRU, + MUR, + MVR, + MWK, + MXN, + MXV, + MYR, + MZN, + NAD, + NGN, + NIO, + NPR, + NZD, + OMR, + PAB, + PEN, + PGK, + PHP, + PKR, + PYG, + QAR, + RUB, + RWF, + SAR, + SBD, + SCR, + SDG, + SGD, + SHP, + SLE, + SLL, + SOS, + SRD, + SSP, + STN, + SVC, + SYP, + SZL, + THB, + TJS, + TMT, + TND, + TOP, + TRY, + TTD, + TWD, + TZS, + UGX, + USN, + UYI, + UYU, + UYW, + UZS, + VED, + VES, + VND, + VUV, + WST, + XAF, + XAG, + XAU, + XBA, + XBB, + XBC, + XBD, + XCD, + XDR, + XOF, + XPD, + XPF, + XPT, + XSU, + XTS, + XUA, + XXX, + YER, + ZAR, + ZMW, + ZWL +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/PayItem.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/PayItem.cs new file mode 100644 index 000000000..4add9c364 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/PayItem.cs @@ -0,0 +1,98 @@ +using System; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace fiskaltrust.Api.POS.Models.ifPOS.v2; + +public class PayItem +{ + [JsonPropertyName("ftPayItemId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public Guid? ftPayItemId { get; set; } + + [JsonPropertyName("Quantity")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public decimal? QuantitySerialization + { + get => Quantity == 1 ? null : Quantity; + set => Quantity = value ?? 1; + } + + [JsonIgnore] + public decimal Quantity { get; set; } = 1; + + [JsonPropertyName("Description")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public string? Description { get; set; } + + [JsonPropertyName("Amount")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public decimal Amount { get; set; } + + [JsonPropertyName("ftPayItemCase")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public long ftPayItemCase { get; set; } = 0x0; + + [JsonPropertyName("ftPayItemCaseData")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public object? ftPayItemCaseData { get; set; } + + [JsonPropertyName("Moment")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public DateTime? Moment { get; set; } + + [JsonPropertyName("Position")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public decimal Position { get; set; } = 0; + + [JsonPropertyName("AccountNumber")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? AccountNumber { get; set; } + + [JsonPropertyName("CostCenter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? CostCenter { get; set; } + + [JsonPropertyName("MoneyGroup")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? MoneyGroup { get; set; } + + [JsonPropertyName("MoneyNumber")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? MoneyNumber { get; set; } + + [JsonPropertyName("MoneyBarcode")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? MoneyBarcode { get; set; } + + [JsonPropertyName("Currency")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonConverter(typeof(JsonStringEnumConverter))] + [DataMember(Order = 170, EmitDefaultValue = false, IsRequired = false)] + public Currency Currency { get; set; } + + [JsonPropertyName("DecimalPrecisionMultiplier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 180, EmitDefaultValue = false, IsRequired = false)] + public int DecimalPrecisionMultiplierSerialization + { + get => DecimalPrecisionMultiplier == 1 ? 0 : DecimalPrecisionMultiplier; + set => DecimalPrecisionMultiplier = value == 0 ? 1 : value; + } + + [JsonIgnore] + public int DecimalPrecisionMultiplier { get; set; } = 1; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/ReceiptRequest.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/ReceiptRequest.cs new file mode 100644 index 000000000..58a327f30 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/ReceiptRequest.cs @@ -0,0 +1,108 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace fiskaltrust.Api.POS.Models.ifPOS.v2; + +public class ReceiptRequest +{ + [JsonPropertyName("cbTerminalID")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 10, EmitDefaultValue = false, IsRequired = false)] + public string? cbTerminalID { get; set; } + + [JsonPropertyName("cbReceiptReference")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(Order = 20, EmitDefaultValue = true, IsRequired = true)] + public string? cbReceiptReference { get; set; } + + [JsonPropertyName("cbReceiptMoment")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(Order = 30, EmitDefaultValue = true, IsRequired = true)] + public DateTime cbReceiptMoment { get; set; } + + [JsonPropertyName("cbChargeItems")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(Order = 40, EmitDefaultValue = true, IsRequired = true)] + public List cbChargeItems { get; set; } = []; + + [JsonPropertyName("cbPayItems")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(Order = 50, EmitDefaultValue = true, IsRequired = true)] + public List cbPayItems { get; set; } = []; + + [JsonPropertyName("ftCashBoxID")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 60, EmitDefaultValue = false, IsRequired = false)] + public Guid? ftCashBoxID { get; set; } + + [JsonPropertyName("ftPosSystemId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 70, EmitDefaultValue = false, IsRequired = false)] + public Guid? ftPosSystemId { get; set; } + + [JsonPropertyName("ftReceiptCase")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(Order = 80, EmitDefaultValue = false, IsRequired = false)] + public long ftReceiptCase { get; set; } = 0; + + [JsonPropertyName("ftReceiptCaseData")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 90, EmitDefaultValue = false, IsRequired = false)] + public object? ftReceiptCaseData { get; set; } + + [JsonPropertyName("ftQueueID")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 100, EmitDefaultValue = false, IsRequired = false)] + public Guid? ftQueueID { get; set; } + + [JsonPropertyName("cbPreviousReceiptReference")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 110, EmitDefaultValue = false, IsRequired = false)] + public string? cbPreviousReceiptReference { get; set; } + + [JsonPropertyName("cbReceiptAmount")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 120, EmitDefaultValue = false, IsRequired = false)] + public decimal? cbReceiptAmount { get; set; } + + [JsonPropertyName("cbUser")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 130, EmitDefaultValue = false, IsRequired = false)] + public object? cbUser { get; set; } + + [JsonPropertyName("cbArea")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 140, EmitDefaultValue = false, IsRequired = false)] + public object? cbArea { get; set; } + + [JsonPropertyName("cbCustomer")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 150, EmitDefaultValue = false, IsRequired = false)] + public object? cbCustomer { get; set; } + + [JsonPropertyName("cbSettlement")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 160, EmitDefaultValue = false, IsRequired = false)] + public object? cbSettlement { get; set; } + + [JsonPropertyName("Currency")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonConverter(typeof(JsonStringEnumConverter))] + [DataMember(Order = 170, EmitDefaultValue = false, IsRequired = false)] + public Currency Currency { get; set; } + + [JsonPropertyName("DecimalPrecisionMultiplier")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(Order = 180, EmitDefaultValue = false, IsRequired = false)] + public int DecimalPrecisionMultiplierSerialization + { + get => DecimalPrecisionMultiplier == 1 ? 0 : DecimalPrecisionMultiplier; + set => DecimalPrecisionMultiplier = value == 0 ? 1 : value; + } + + [JsonIgnore] + public int DecimalPrecisionMultiplier { get; set; } = 1; +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/ReceiptResponse.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/ReceiptResponse.cs new file mode 100644 index 000000000..45c3ff5bf --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/ReceiptResponse.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace fiskaltrust.Api.POS.Models.ifPOS.v2; + +public class ReceiptResponse +{ + [JsonPropertyName("ftQueueID")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required Guid ftQueueID { get; set; } + + [JsonPropertyName("ftQueueItemID")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required Guid ftQueueItemID { get; set; } + + [JsonPropertyName("ftQueueRow")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required long ftQueueRow { get; set; } + + [JsonPropertyName("ftCashBoxIdentification")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required string ftCashBoxIdentification { get; set; } + + [JsonPropertyName("ftCashBoxID")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public Guid? ftCashBoxID { get; set; } + + [JsonPropertyName("cbTerminalID")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public string? cbTerminalID { get; set; } + + [JsonPropertyName("cbReceiptReference")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public string? cbReceiptReference { get; set; } + + [JsonPropertyName("ftReceiptIdentification")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required string ftReceiptIdentification { get; set; } + + [JsonPropertyName("ftReceiptMoment")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required DateTime ftReceiptMoment { get; set; } + + [JsonPropertyName("ftReceiptHeader")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public List? ftReceiptHeader { get; set; } + + [JsonPropertyName("ftChargeItems")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public List? ftChargeItems { get; set; } + + [JsonPropertyName("ftChargeLines")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public List? ftChargeLines { get; set; } + + [JsonPropertyName("ftPayItems")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public List? ftPayItems { get; set; } + + [JsonPropertyName("ftPayLines")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public List? ftPayLines { get; set; } + + [JsonPropertyName("ftSignatures")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public List ftSignatures { get; set; } = []; + + [JsonPropertyName("ftReceiptFooter")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public List? ftReceiptFooter { get; set; } + + [JsonPropertyName("ftState")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required long ftState { get; set; } + + [JsonPropertyName("ftStateData")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public object? ftStateData { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/SignatureItem.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/SignatureItem.cs new file mode 100644 index 000000000..2044bba3a --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Models/ifPOS/v2/SignatureItem.cs @@ -0,0 +1,33 @@ +using System; +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace fiskaltrust.Api.POS.Models.ifPOS.v2; + +public class SignatureItem +{ + [JsonPropertyName("ftSignatureItemId")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public Guid? ftSignatureItemId { get; set; } + + [JsonPropertyName("ftSignatureFormat")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required long ftSignatureFormat { get; set; } + + [JsonPropertyName("ftSignatureType")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required long ftSignatureType { get; set; } + + [JsonPropertyName("Caption")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [DataMember(EmitDefaultValue = false, IsRequired = false)] + public string? Caption { get; set; } + + [JsonPropertyName("Data")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required string Data { get; set; } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Queue.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Queue.cs new file mode 100644 index 000000000..f337396ff --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Queue.cs @@ -0,0 +1,57 @@ +using System.Text; +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.Synchronizer; +using Microsoft.Extensions.Logging; + +namespace fiskaltrust.Middleware.Localization.v2 +{ + public class Queue + { + private readonly ISignProcessor _signProcessor; + private readonly IJournalProcessor _journalProcessor; + private readonly EchoProcessor _echoProcessor; + + public required Guid Id { get; set; } + public required Dictionary Configuration { get; set; } + + public Queue(ISignProcessor signProcessor, IJournalProcessor journalProcessor, ILoggerFactory loggerFactory) + { + _signProcessor = new LocalQueueSynchronizationDecorator(signProcessor, loggerFactory.CreateLogger()); + _journalProcessor = journalProcessor; + _echoProcessor = new EchoProcessor(); + } + + public Func> RegisterForEcho() + { + return async (message) => + { + var request = JsonSerializer.Deserialize(message) ?? throw new ArgumentException($"Invalid message format. The body for the message {message} could not be serialized."); + var response = await _echoProcessor.ProcessAsync(request); + return JsonSerializer.Serialize(response); + }; + } + + public Func> RegisterForSign() + { + return async (message) => + { + var request = JsonSerializer.Deserialize(message) ?? throw new ArgumentException($"Invalid message format. The body for the message {message} could not be serialized."); + var response = await _signProcessor.ProcessAsync(request); + return JsonSerializer.Serialize(response); + }; + } + + public Func> RegisterForJournal() + { + return async (message) => + { + var request = JsonSerializer.Deserialize(message) ?? throw new ArgumentException($"Invalid message format. The body for the message {message} could not be serialized."); + var response = await _journalProcessor.ProcessAsync(request).ToListAsync(); + var responsePayload = response.SelectMany(x => x.Chunk).ToArray(); + return Encoding.UTF8.GetString(responsePayload); + }; + } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/ReceiptProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/ReceiptProcessor.cs new file mode 100644 index 000000000..036037fb0 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/ReceiptProcessor.cs @@ -0,0 +1,72 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.storage.V0; +using Microsoft.Extensions.Logging; + +namespace fiskaltrust.Middleware.Localization.v2; + +public class ReceiptProcessor : IReceiptProcessor +{ + private readonly ILifecycleCommandProcessor _lifecyclCommandProcessor; + private readonly IReceiptCommandProcessor _receiptCommandProcessor; + private readonly IDailyOperationsCommandProcessor _dailyOperationsCommandProcessor; + private readonly IInvoiceCommandProcessor _invoiceCommandProcessor; + private readonly IProtocolCommandProcessor _protocolCommandProcessor; + private readonly ILogger _logger; + + public ReceiptProcessor(ILogger logger, ILifecycleCommandProcessor lifecyclCommandProcessor, IReceiptCommandProcessor receiptCommandProcessor, IDailyOperationsCommandProcessor dailyOperationsCommandProcessor, IInvoiceCommandProcessor invoiceCommandProcessor, IProtocolCommandProcessor protocolCommandProcessor) + { + _lifecyclCommandProcessor = lifecyclCommandProcessor; + _receiptCommandProcessor = receiptCommandProcessor; + _dailyOperationsCommandProcessor = dailyOperationsCommandProcessor; + _invoiceCommandProcessor = invoiceCommandProcessor; + _protocolCommandProcessor = protocolCommandProcessor; + _logger = logger; + } + + public async Task<(ReceiptResponse receiptResponse, List actionJournals)> ProcessAsync(ReceiptRequest request, ReceiptResponse receiptResponse, ftQueue queue, ftQueueItem queueItem) + { + try + { + + if (request.IsDailyOperation()) + { + (var response, var actionJournals) = await _dailyOperationsCommandProcessor.ProcessReceiptAsync(new ProcessCommandRequest(queue, request, receiptResponse)).ConfigureAwait(false); + return (response, actionJournals); + } + + if (request.IsLifeCycleOperation()) + { + (var response, var actionJournals) = await _lifecyclCommandProcessor.ProcessReceiptAsync(new ProcessCommandRequest(queue, request, receiptResponse)).ConfigureAwait(false); + return (response, actionJournals); + } + + if (request.IsReceiptOperation()) + { + var (response, actionJournals) = await _receiptCommandProcessor.ProcessReceiptAsync(new ProcessCommandRequest(queue, request, receiptResponse)).ConfigureAwait(false); + return (response, actionJournals); + } + + if (request.IsProtocolOperation()) + { + var (response, actionJournals) = await _protocolCommandProcessor.ProcessReceiptAsync(new ProcessCommandRequest(queue, request, receiptResponse)).ConfigureAwait(false); + return (response, actionJournals); + } + + if (request.IsInvoiceOperation()) + { + var (response, actionJournals) = await _invoiceCommandProcessor.ProcessReceiptAsync(new ProcessCommandRequest(queue, request, receiptResponse)).ConfigureAwait(false); + return (response, actionJournals); + } + + receiptResponse.SetReceiptResponseError($"The given ftReceiptCase 0x{request.ftReceiptCase:x} is not supported. Please refer to docs.fiskaltrust.cloud for supported cases."); + return (receiptResponse, new List()); + } + catch (Exception ex) + { + _logger.LogError(ex, "Failed to process receiptcase 0x{receiptcase}", request.ftReceiptCase.ToString("X")); + receiptResponse.SetReceiptResponseError($"Failed to process receiptcase 0x{request.ftReceiptCase.ToString("X")}. with the following exception message: " + ex.Message); + return (receiptResponse, new List()); + } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/SignProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/SignProcessor.cs new file mode 100644 index 000000000..b2355d4c4 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/SignProcessor.cs @@ -0,0 +1,211 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.v2.Configuration; +using fiskaltrust.Middleware.Localization.v2.Helpers; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.Storage; +using fiskaltrust.storage.V0; +using Microsoft.Extensions.Logging; + +namespace fiskaltrust.Middleware.Localization.v2; + +public class SignProcessor : ISignProcessor +{ + private readonly ILogger _logger; + private readonly Func actionJournals)>> _processRequest; + private readonly string _cashBoxIdentification; + private readonly Guid _queueId = Guid.Empty; + private readonly Guid _cashBoxId = Guid.Empty; + private readonly bool _isSandbox; + private readonly QueueStorageProvider _queueStorageProvider; + private readonly int _receiptRequestMode = 0; + + public SignProcessor( + ILogger logger, + QueueStorageProvider queueStorageProvider, + Func actionJournals)>> processRequest, + string cashBoxIdentification, + MiddlewareConfiguration configuration) + { + _logger = logger; + _processRequest = processRequest; + _cashBoxIdentification = cashBoxIdentification; + _queueId = configuration.QueueId; + _cashBoxId = configuration.CashBoxId; + _isSandbox = configuration.IsSandbox; + _queueStorageProvider = queueStorageProvider; + _receiptRequestMode = configuration.ReceiptRequestMode; + } + + public async Task ProcessAsync(ReceiptRequest receiptRequest) + { + try + { + ArgumentNullException.ThrowIfNull(receiptRequest); + if (receiptRequest.ftCashBoxID != _cashBoxId) + { + throw new Exception("Provided CashBoxId does not match current CashBoxId"); + } + + if ((receiptRequest.ftReceiptCase & 0x0000800000000000L) > 0) + { + ReceiptResponse? receiptResponseFound = null; + try + { + var foundQueueItem = await _queueStorageProvider.GetExistingQueueItemOrNullAsync(receiptRequest).ConfigureAwait(false); + if (foundQueueItem != null) + { + var message = $"Queue {_queueId} found cbReceiptReference \"{foundQueueItem.cbReceiptReference}\""; + _logger.LogWarning(message); + await _queueStorageProvider.CreateActionJournalAsync(message, "", foundQueueItem.ftQueueItemId).ConfigureAwait(false); + receiptResponseFound = System.Text.Json.JsonSerializer.Deserialize(foundQueueItem.response); + } + } + catch (Exception x) + { + var message = $"Queue {_queueId} problem on receitrequest"; + _logger.LogError(x, message); + await _queueStorageProvider.CreateActionJournalAsync(message, "", null).ConfigureAwait(false); + } + + + if (receiptResponseFound != null) + { + return receiptResponseFound; + } + else + { + if (_receiptRequestMode == 1) + { + //try to sign, remove receiptrequest-flag + receiptRequest.ftReceiptCase -= 0x0000800000000000L; + } + else + { + return null; + } + } + } + var actionjournals = new List(); + try + { + var queueItem = await _queueStorageProvider.ReserveNextQueueItem(receiptRequest); + queueItem.ftWorkMoment = DateTime.UtcNow; + var receiptResponse = CreateReceiptResponse(receiptRequest, queueItem); + receiptResponse.ftReceiptIdentification = $"ft{await _queueStorageProvider.GetReceiptNumerator():X}#"; + List countrySpecificActionJournals; + try + { + (receiptResponse, countrySpecificActionJournals) = await ProcessAsync(receiptRequest, receiptResponse, queueItem).ConfigureAwait(false); + actionjournals.AddRange(countrySpecificActionJournals); + } + catch (Exception e) + { + receiptResponse.HasFailed(); + receiptResponse.AddSignatureItem(new SignatureItem + { + ftSignatureFormat = 0x1, + ftSignatureType = (long) (((ulong) receiptRequest.ftReceiptCase & 0xFFFF_0000_0000_0000) | 0x2000_0000_3000), + Caption = "uncaught-exeption", + Data = e.ToString() + }); + } + if (_isSandbox) + { + receiptResponse.ftSignatures.Add(SignatureFactory.CreateSandboxSignature(_queueId)); + } + + await _queueStorageProvider.FinishQueueItem(queueItem, receiptResponse); + + if ((receiptResponse.ftState & 0xFFFF_FFFF) == 0xEEEE_EEEE) + { + var errorMessage = "An error occurred during receipt processing, resulting in ftState = 0xEEEE_EEEE."; + await _queueStorageProvider.CreateActionJournalAsync(errorMessage, $"{receiptResponse.ftState:X}", queueItem.ftQueueItemId); + return receiptResponse; + } + else + { + _ = await _queueStorageProvider.InsertReceiptJournal(queueItem, receiptRequest); + } + return receiptResponse; + } + finally + { + foreach (var actionJournal in actionjournals) + { + await _queueStorageProvider.CreateActionJournalAsync(actionJournal); + } + } + } + catch (Exception ex) + { + _logger.LogError(ex, ""); + throw; + } + } + + private ReceiptResponse CreateReceiptResponse(ReceiptRequest receiptRequest, ftQueueItem queueItem) + { + return new ReceiptResponse + { + ftCashBoxID = receiptRequest.ftCashBoxID, + ftQueueID = queueItem.ftQueueId, + ftQueueItemID = queueItem.ftQueueItemId, + ftQueueRow = queueItem.ftQueueRow, + cbTerminalID = receiptRequest.cbTerminalID, + cbReceiptReference = receiptRequest.cbReceiptReference, + ftCashBoxIdentification = _cashBoxIdentification, + ftReceiptMoment = DateTime.UtcNow, + ftState = (long) ((ulong) receiptRequest.ftReceiptCase & 0xFFFF_F000_0000_0000), + ftReceiptIdentification = "", + }; + } + + public async Task<(ReceiptResponse receiptResponse, List actionJournals)> ProcessAsync(ReceiptRequest request, ReceiptResponse receiptResponse, ftQueueItem queueItem) + { + var queue = await _queueStorageProvider.GetQueueAsync(); + if (queue.IsDeactivated()) + { + return ReturnWithQueueIsDisabled(queue, receiptResponse, queueItem); + } + + if (request.IsInitialOperation() && !queue.IsNew()) + { + receiptResponse.SetReceiptResponseError("The queue is already operational. It is not allowed to send another InitOperation Receipt"); + return (receiptResponse, new List()); + } + + if (!request.IsInitialOperation() && queue.IsNew()) + { + return ReturnWithQueueIsNotActive(queue, receiptResponse, queueItem); + } + return await _processRequest(request, receiptResponse, queue, queueItem).ConfigureAwait(false); + } + + private (ReceiptResponse receiptResponse, List actionJournals) ReturnWithQueueIsNotActive(ftQueue queue, ReceiptResponse receiptResponse, ftQueueItem queueItem) + { + receiptResponse.MarkAsDisabled(); + receiptResponse.ftReceiptIdentification = $"ft{queue.ftReceiptNumerator:X}#"; + return (receiptResponse, [ + new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queueItem.ftQueueId, + ftQueueItemId = queueItem.ftQueueItemId, + Moment = DateTime.UtcNow, + Message = $"QueueId {queueItem.ftQueueId} has not been activated yet." + }]); + } + + private (ReceiptResponse receiptResponse, List actionJournals) ReturnWithQueueIsDisabled(ftQueue queue, ReceiptResponse receiptResponse, ftQueueItem queueItem) + { + receiptResponse.MarkAsDisabled(); + receiptResponse.ftReceiptIdentification = $"ft{queue.ftReceiptNumerator:X}#"; + return (receiptResponse, [ new() { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queueItem.ftQueueId, + ftQueueItemId = queueItem.ftQueueItemId, + Moment = DateTime.UtcNow, + Message = $"QueueId {queueItem.ftQueueId} has been disabled." + }]); + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Storage/IQueueStorageProvider.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Storage/IQueueStorageProvider.cs new file mode 100644 index 000000000..da3f0b692 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Storage/IQueueStorageProvider.cs @@ -0,0 +1,25 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.v2.Storage +{ + public interface IQueueStorageProvider : ILocalizedQueueStorageProvider + { + Task CreateActionJournalAsync(ftActionJournal actionJournal); + Task CreateActionJournalAsync(string message, string type, Guid? queueItemId); + Task FinishQueueItem(ftQueueItem queueItem, ReceiptResponse receiptResponse); + Task GetCurrentRow(); + Task GetExistingQueueItemOrNullAsync(ReceiptRequest data); + Task IncrementQueueRow(); + Task GetQueueAsync(); + Task GetReceiptNumerator(); + Task InsertReceiptJournal(ftQueueItem queueItem, ReceiptRequest receiptrequest); + Task ReserveNextQueueItem(ReceiptRequest receiptRequest); + } + + public interface ILocalizedQueueStorageProvider + { + Task ActivateQueueAsync(); + Task DeactivateQueueAsync(); + } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Storage/QueueStorageProvider.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Storage/QueueStorageProvider.cs new file mode 100644 index 000000000..f71821d20 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Storage/QueueStorageProvider.cs @@ -0,0 +1,188 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Contracts.Repositories; +using fiskaltrust.Middleware.Localization.v2.Helpers; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.v2.Storage; + +public class QueueStorageProvider : IQueueStorageProvider +{ + private readonly Guid _queueId; + private readonly IStorageProvider _storageProvider; + private readonly IConfigurationRepository _configurationRepository; + private readonly IMiddlewareQueueItemRepository _middlewareQueueItemRepository; + private readonly IMiddlewareReceiptJournalRepository _middlewareReceiptJournalRepository; + private readonly IMiddlewareActionJournalRepository _actionJournalRepository; + private readonly CryptoHelper _cryptoHelper; + private ftQueue? _cachedQueue; + + public QueueStorageProvider(Guid queueId, IStorageProvider storageProvider) + { + _queueId = queueId; + _storageProvider = storageProvider; + _configurationRepository = storageProvider.GetConfigurationRepository(); + _middlewareQueueItemRepository = storageProvider.GetMiddlewareQueueItemRepository(); + _middlewareReceiptJournalRepository = storageProvider.GetMiddlewareReceiptJournalRepository(); + _actionJournalRepository = storageProvider.GetMiddlewareActionJournalRepository(); + _cryptoHelper = new CryptoHelper(); + } + + public async Task ActivateQueueAsync() + { + _cachedQueue ??= await GetQueueAsync(); + var queue = _cachedQueue; + queue.StartMoment = DateTime.UtcNow; + await _configurationRepository.InsertOrUpdateQueueAsync(queue).ConfigureAwait(false); + } + + public async Task DeactivateQueueAsync() + { + _cachedQueue ??= await GetQueueAsync(); + var queue = _cachedQueue; + queue.StopMoment = DateTime.UtcNow; + await _configurationRepository.InsertOrUpdateQueueAsync(queue).ConfigureAwait(false); + } + + public async Task ReserveNextQueueItem(ReceiptRequest receiptRequest) + { + _cachedQueue ??= await GetQueueAsync(); + + var queueItem = new ftQueueItem + { + ftQueueItemId = Guid.NewGuid(), + ftQueueId = _queueId, + ftQueueMoment = DateTime.UtcNow, + ftQueueTimeout = _cachedQueue.Timeout, + cbReceiptMoment = receiptRequest.cbReceiptMoment, + cbTerminalID = receiptRequest.cbTerminalID, + cbReceiptReference = receiptRequest.cbReceiptReference, + ftQueueRow = await IncrementQueueRow(), + country = receiptRequest.GetCountry(), + version = "v2", + request = System.Text.Json.JsonSerializer.Serialize(receiptRequest), + }; + if (queueItem.ftQueueTimeout == 0) + { + queueItem.ftQueueTimeout = 15000; + } + queueItem.requestHash = _cryptoHelper.GenerateBase64Hash(queueItem.request); + await _middlewareQueueItemRepository.InsertOrUpdateAsync(queueItem).ConfigureAwait(false); + return queueItem; + } + + public async Task GetReceiptNumerator() + { + _cachedQueue ??= await GetQueueAsync(); + return _cachedQueue.ftReceiptNumerator; + } + + public async Task GetCurrentRow() + { + _cachedQueue ??= await GetQueueAsync(); + return _cachedQueue.ftCurrentRow; + } + + public async Task GetQueueAsync() + { + await Task.WhenAny(Task.Delay(TimeSpan.FromMinutes(5)), _storageProvider.Initialized); + if (!_storageProvider.Initialized.IsCompleted) + { + throw new Exception("Storage provider is not initialized yet."); + } + _cachedQueue ??= await _configurationRepository.GetQueueAsync(_queueId); + return _cachedQueue; + } + + public async Task FinishQueueItem(ftQueueItem queueItem, ReceiptResponse receiptResponse) + { + _cachedQueue ??= await GetQueueAsync(); + var queue = _cachedQueue; + queueItem.response = System.Text.Json.JsonSerializer.Serialize(receiptResponse); + queueItem.responseHash = _cryptoHelper.GenerateBase64Hash(queueItem.response); + queueItem.ftDoneMoment = DateTime.UtcNow; + queue.ftCurrentRow++; + await _middlewareQueueItemRepository.InsertOrUpdateAsync(queueItem).ConfigureAwait(false); + await _configurationRepository.InsertOrUpdateQueueAsync(queue).ConfigureAwait(false); + _cachedQueue = queue; + } + + public async Task IncrementQueueRow() + { + _cachedQueue ??= await GetQueueAsync(); + var queue = _cachedQueue; + ++queue.ftQueuedRow; + await _configurationRepository.InsertOrUpdateQueueAsync(queue).ConfigureAwait(false); + _cachedQueue = queue; + return _cachedQueue.ftQueuedRow; + } + + public async Task InsertReceiptJournal(ftQueueItem queueItem, ReceiptRequest receiptrequest) + { + _cachedQueue ??= await GetQueueAsync(); + var queue = _cachedQueue; + queue.ftReceiptNumerator++; + var receiptjournal = new ftReceiptJournal + { + ftReceiptJournalId = Guid.NewGuid(), + ftQueueId = queue.ftQueueId, + ftQueueItemId = queueItem.ftQueueItemId, + ftReceiptMoment = DateTime.UtcNow, + ftReceiptNumber = queue.ftReceiptNumerator + }; + if (receiptrequest.cbReceiptAmount.HasValue) + { + receiptjournal.ftReceiptTotal = receiptrequest.cbReceiptAmount.Value; + } + else + { + receiptjournal.ftReceiptTotal = (receiptrequest?.cbChargeItems?.Sum(ci => ci.Amount)).GetValueOrDefault(); + } + receiptjournal.ftReceiptHash = _cryptoHelper.GenerateBase64ChainHash(queue.ftReceiptHash, receiptjournal, queueItem); + await _middlewareReceiptJournalRepository.InsertAsync(receiptjournal).ConfigureAwait(false); + queue.ftReceiptHash = receiptjournal.ftReceiptHash; + queue.ftReceiptTotalizer += receiptjournal.ftReceiptTotal; + await _configurationRepository.InsertOrUpdateQueueAsync(queue).ConfigureAwait(false); + _cachedQueue = queue; + return receiptjournal; + } + + public async Task CreateActionJournalAsync(string message, string type, Guid? queueItemId) + { + _cachedQueue ??= await _configurationRepository.GetQueueAsync(_queueId); + var actionJournal = new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = _cachedQueue.ftQueueId, + ftQueueItemId = queueItemId.GetValueOrDefault(), + Message = message, + Priority = 0, + Type = type, + Moment = DateTime.UtcNow + }; + await _actionJournalRepository.InsertAsync(actionJournal).ConfigureAwait(false); + } + + public async Task CreateActionJournalAsync(ftActionJournal actionJournal) + { + _cachedQueue ??= await _configurationRepository.GetQueueAsync(_queueId); + await _actionJournalRepository.InsertAsync(actionJournal).ConfigureAwait(false); + } + + public async Task GetExistingQueueItemOrNullAsync(ReceiptRequest data) + { + var queueItems = (await _middlewareQueueItemRepository.GetByReceiptReferenceAsync(data.cbReceiptReference, data.cbTerminalID).ToListAsync().ConfigureAwait(false)).OrderByDescending(x => x.TimeStamp); + foreach (var existingQueueItem in queueItems) + { + if (!existingQueueItem.IsReceiptRequestFinished()) + { + continue; + } + if (existingQueueItem.IsContentOfQueueItemEqualWithGivenRequest(data)) + { + return existingQueueItem; + } + } + return null; + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Synchronizer/LocalQueueSynchronizationDecorator.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Synchronizer/LocalQueueSynchronizationDecorator.cs new file mode 100644 index 000000000..087cb1450 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Synchronizer/LocalQueueSynchronizationDecorator.cs @@ -0,0 +1,88 @@ +using System.Runtime.ExceptionServices; +using System.Threading.Channels; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using Microsoft.Extensions.Logging; +using ISignProcessor = fiskaltrust.Middleware.Localization.v2.Interface.ISignProcessor; + +namespace fiskaltrust.Middleware.Localization.v2.Synchronizer +{ + public sealed class LocalQueueSynchronizationDecorator : ISignProcessor, IDisposable + { + private readonly ISignProcessor _signProcessor; + private readonly ILogger _logger; + private readonly Channel<(ReceiptRequest request, ChannelWriter)> _channel; + private volatile bool _disposed = false; + + public LocalQueueSynchronizationDecorator(ISignProcessor signProcessor, ILogger logger) + { + _signProcessor = signProcessor; + _logger = logger; + _channel = Channel.CreateUnbounded<(ReceiptRequest, ChannelWriter)>(); + + _ = Task.Run(ProcessReceipts); + } + + public async Task ProcessAsync(ReceiptRequest receiptRequest) + { + _logger.LogTrace("LocalQueueSynchronizationDecorator.ProcessAsync called."); + var responseChannel = Channel.CreateBounded(1); + + if (!await _channel.Writer.WaitToWriteAsync()) + { + throw new ObjectDisposedException(nameof(ISignProcessor), "Queue was already disposed"); + } + + await _channel.Writer.WriteAsync((receiptRequest, responseChannel.Writer)); + + _logger.LogTrace("LocalQueueSynchronizationDecorator.ProcessAsync: Waiting until result is available."); + var synchronizedResult = await responseChannel.Reader.ReadAsync(); + + _logger.LogTrace("LocalQueueSynchronizationDecorator.ProcessAsync: Got receipt result."); + synchronizedResult.ExceptionDispatchInfo?.Throw(); + + return await Task.FromResult(synchronizedResult.Response).ConfigureAwait(false); + } + + private async Task ProcessReceipts() + { + while (true) + { + if (!await _channel.Reader.WaitToReadAsync()) + { + break; + } + + var (request, responseChannel) = await _channel.Reader.ReadAsync(); + + _logger.LogTrace("LocalQueueSynchronizationDecorator.ProcessReceipts: Processing a new receipt."); + var synchronizedResult = new SynchronizedResult(); + + try + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(ISignProcessor), "Queue was already disposed"); + } + + var response = await _signProcessor.ProcessAsync(request).ConfigureAwait(false); + synchronizedResult.Response = response; + } + catch (Exception ex) + { + synchronizedResult.ExceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex); + } + finally + { + await responseChannel.WriteAsync(synchronizedResult); + responseChannel.Complete(); + } + } + } + + public void Dispose() + { + _disposed = true; + _channel.Writer.Complete(); + } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/Synchronizer/SynchronizedResult.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/Synchronizer/SynchronizedResult.cs new file mode 100644 index 000000000..375aed4d3 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/Synchronizer/SynchronizedResult.cs @@ -0,0 +1,57 @@ +using System.Runtime.ExceptionServices; +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.v2.Synchronizer +{ + // https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-2-asyncautoresetevent/ + public class AsyncAutoResetEvent + { + private readonly Queue> _waits = new Queue>(); + private bool _signaled; + + public Task WaitAsync() + { + lock (_waits) + { + if (_signaled) + { + _signaled = false; + return Task.FromResult(true); + } + else + { + var tcs = new TaskCompletionSource(); + _waits.Enqueue(tcs); + return tcs.Task; + } + } + } + + public void Set() + { + TaskCompletionSource? toRelease = null; + lock (_waits) + { + if (_waits.Count > 0) + { + toRelease = _waits.Dequeue(); + } + else if (!_signaled) + { + _signaled = true; + } + } + toRelease?.SetResult(true); + } + } + + internal class SynchronizedResult + { +#if NET461 + public AsyncAutoResetEvent AutoResetEvent { get; set; } = new AsyncAutoResetEvent(); +#endif + public ReceiptResponse? Response { get; set; } + public ExceptionDispatchInfo? ExceptionDispatchInfo { get; set; } + } + +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/fiskaltrust.Middleware.Localization.v2.csproj b/queue/src/fiskaltrust.Middleware.Localization.v2/fiskaltrust.Middleware.Localization.v2.csproj new file mode 100644 index 000000000..a9003ece7 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/fiskaltrust.Middleware.Localization.v2.csproj @@ -0,0 +1,18 @@ + + + + net8 + Latest + enable + enable + + + + + + + + + + + diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IDailyOperationsCommandProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IDailyOperationsCommandProcessor.cs new file mode 100644 index 000000000..6b4b80437 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IDailyOperationsCommandProcessor.cs @@ -0,0 +1,12 @@ +namespace fiskaltrust.Middleware.Localization.v2; + +public interface IDailyOperationsCommandProcessor +{ + Task DailyClosing0x2011Async(ProcessCommandRequest request); + Task MonthlyClosing0x2012Async(ProcessCommandRequest request); + Task OneReceipt0x2001Async(ProcessCommandRequest request); + Task ProcessReceiptAsync(ProcessCommandRequest request); + Task ShiftClosing0x2010Async(ProcessCommandRequest request); + Task YearlyClosing0x2013Async(ProcessCommandRequest request); + Task ZeroReceipt0x2000Async(ProcessCommandRequest request); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IInvoiceCommandProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IInvoiceCommandProcessor.cs new file mode 100644 index 000000000..8185ec9b6 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IInvoiceCommandProcessor.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace fiskaltrust.Middleware.Localization.v2; + +public interface IInvoiceCommandProcessor +{ + Task ProcessReceiptAsync(ProcessCommandRequest request); + Task InvoiceUnknown0x1000Async(ProcessCommandRequest request); + Task InvoiceB2C0x1001Async(ProcessCommandRequest request); + Task InvoiceB2B0x1002Async(ProcessCommandRequest request); + Task InvoiceB2G0x1003Async(ProcessCommandRequest request); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/v2/ILifecycleCommandProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/ILifecycleCommandProcessor.cs new file mode 100644 index 000000000..6ed55fa6d --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/ILifecycleCommandProcessor.cs @@ -0,0 +1,12 @@ +using System.Threading.Tasks; + +namespace fiskaltrust.Middleware.Localization.v2; + +public interface ILifecycleCommandProcessor +{ + Task ProcessReceiptAsync(ProcessCommandRequest request); + Task InitialOperationReceipt0x4001Async(ProcessCommandRequest request); + Task OutOfOperationReceipt0x4002Async(ProcessCommandRequest request); + Task InitSCUSwitch0x4011Async(ProcessCommandRequest request); + Task FinishSCUSwitch0x4012Async(ProcessCommandRequest request); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IProtocolCommandProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IProtocolCommandProcessor.cs new file mode 100644 index 000000000..86afe7cca --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IProtocolCommandProcessor.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; + +namespace fiskaltrust.Middleware.Localization.v2; + +public interface IProtocolCommandProcessor +{ + Task ProcessReceiptAsync(ProcessCommandRequest request); + Task ProtocolUnspecified0x3000Async(ProcessCommandRequest request); + Task ProtocolTechnicalEvent0x3001Async(ProcessCommandRequest request); + Task ProtocolAccountingEvent0x3002Async(ProcessCommandRequest request); + Task InternalUsageMaterialConsumption0x3003Async(ProcessCommandRequest request); + Task Order0x3004Async(ProcessCommandRequest request); + Task CopyReceiptPrintExistingReceipt0x3010Async(ProcessCommandRequest request); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IReceiptCommandProcessor.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IReceiptCommandProcessor.cs new file mode 100644 index 000000000..c5263ed03 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/IReceiptCommandProcessor.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; + +namespace fiskaltrust.Middleware.Localization.v2; + +public interface IReceiptCommandProcessor +{ + Task ProcessReceiptAsync(ProcessCommandRequest request); + Task UnknownReceipt0x0000Async(ProcessCommandRequest request); + Task PointOfSaleReceipt0x0001Async(ProcessCommandRequest request); + Task PaymentTransfer0x0002Async(ProcessCommandRequest request); + Task PointOfSaleReceiptWithoutObligation0x0003Async(ProcessCommandRequest request); + Task ECommerce0x0004Async(ProcessCommandRequest request); + Task Protocol0x0005Async(ProcessCommandRequest request); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/v2/ProcessCommandRequest.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/ProcessCommandRequest.cs new file mode 100644 index 000000000..8cc09b7ec --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/ProcessCommandRequest.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.v2; + +// Maybe add the scu state string here and in the response and pull the loading and saving of it into the v2.signprocessor. +public record ProcessCommandRequest(ftQueue queue, ReceiptRequest ReceiptRequest, ReceiptResponse ReceiptResponse); diff --git a/queue/src/fiskaltrust.Middleware.Localization.v2/v2/ProcessCommandResponse.cs b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/ProcessCommandResponse.cs new file mode 100644 index 000000000..8a4bffac3 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Localization.v2/v2/ProcessCommandResponse.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.v2; + +public record ProcessCommandResponse(ReceiptResponse receiptResponse, List actionJournals); diff --git a/queue/src/fiskaltrust.Middleware.Queue/Bootstrapper/LocalizedQueueBootStrapperFactory.cs b/queue/src/fiskaltrust.Middleware.Queue/Bootstrapper/LocalizedQueueBootStrapperFactory.cs index 808039f92..1cc0ff296 100644 --- a/queue/src/fiskaltrust.Middleware.Queue/Bootstrapper/LocalizedQueueBootStrapperFactory.cs +++ b/queue/src/fiskaltrust.Middleware.Queue/Bootstrapper/LocalizedQueueBootStrapperFactory.cs @@ -5,7 +5,6 @@ using fiskaltrust.Middleware.Localization.QueueAT; using fiskaltrust.Middleware.Localization.QueueDE; using fiskaltrust.Middleware.Localization.QueueDEFAULT; -using fiskaltrust.Middleware.Localization.QueueES; using fiskaltrust.Middleware.Localization.QueueIT; using fiskaltrust.Middleware.Localization.QueueFR; using fiskaltrust.Middleware.Localization.QueueME; @@ -24,7 +23,6 @@ public static ILocalizedQueueBootstrapper GetBootstrapperForLocalizedQueue(Guid { "AT" => new QueueATBootstrapper(), "DE" => new QueueDEBootstrapper(), - "ES" => new QueueESBootstrapper(), "FR" => new QueueFRBootstrapper(), "IT" => new QueueITBootstrapper(), "ME" => new QueueMeBootstrapper(), diff --git a/queue/src/fiskaltrust.Middleware.Queue/fiskaltrust.Middleware.Queue.csproj b/queue/src/fiskaltrust.Middleware.Queue/fiskaltrust.Middleware.Queue.csproj index ea9560300..894d21277 100644 --- a/queue/src/fiskaltrust.Middleware.Queue/fiskaltrust.Middleware.Queue.csproj +++ b/queue/src/fiskaltrust.Middleware.Queue/fiskaltrust.Middleware.Queue.csproj @@ -22,7 +22,6 @@ - diff --git a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/AzureTableStorageConfiguration.cs b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/AzureTableStorageConfiguration.cs index 48f60a310..555cc8b47 100644 --- a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/AzureTableStorageConfiguration.cs +++ b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/AzureTableStorageConfiguration.cs @@ -6,12 +6,15 @@ namespace fiskaltrust.Middleware.Storage.AzureTableStorage public class AzureTableStorageConfiguration { [JsonProperty("connectionstring")] + [System.Text.Json.Serialization.JsonPropertyName("connectionstring")] public string ConnectionString { get; set; } [JsonProperty("storageconnectionstring")] + [System.Text.Json.Serialization.JsonPropertyName("storageconnectionstring")] public string StorageConnectionString { get; set; } [JsonProperty("storageaccountname")] + [System.Text.Json.Serialization.JsonPropertyName("storageaccountname")] public string StorageAccountName { get; set; } public static AzureTableStorageConfiguration FromConfigurationDictionary(Dictionary configuration) => JsonConvert.DeserializeObject(JsonConvert.SerializeObject(configuration)); diff --git a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/DatabaseMigrator.cs b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/DatabaseMigrator.cs index 204158cf5..741d1113e 100644 --- a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/DatabaseMigrator.cs +++ b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/DatabaseMigrator.cs @@ -5,7 +5,6 @@ using Azure.Storage.Blobs; using fiskaltrust.Middleware.Abstractions; using fiskaltrust.Middleware.Storage.AzureTableStorage.Migrations; -using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories; using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.Configuration; using Microsoft.Extensions.Logging; @@ -33,7 +32,8 @@ public DatabaseMigrator(ILogger logger, TableServiceCli _migrations = new IAzureTableStorageMigration[] { new Migration_000_Initial(_tableServiceClient, blobServiceClient, queueConfiguration), - new Migration_001_TableNameFix(_tableServiceClient, queueConfiguration) + new Migration_001_TableNameFix(_tableServiceClient, queueConfiguration), + new Migration_002_QueueES(_tableServiceClient, queueConfiguration) }; } diff --git a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Migrations/Migration_000_Initial.cs b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Migrations/Migration_000_Initial.cs index 56620d630..98bb10643 100644 --- a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Migrations/Migration_000_Initial.cs +++ b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Migrations/Migration_000_Initial.cs @@ -41,6 +41,7 @@ public async Task ExecuteAsync() await _tableServiceClient.CreateTableIfNotExistsAsync(GetTableName(AzureTableStorageQueueRepository.TABLE_NAME)); await _tableServiceClient.CreateTableIfNotExistsAsync(GetTableName(AzureTableStorageQueueATRepository.TABLE_NAME)); await _tableServiceClient.CreateTableIfNotExistsAsync(GetTableName(AzureTableStorageQueueDERepository.TABLE_NAME)); + await _tableServiceClient.CreateTableIfNotExistsAsync(GetTableName(AzureTableStorageQueueESRepository.TABLE_NAME)); await _tableServiceClient.CreateTableIfNotExistsAsync(GetTableName(AzureTableStorageQueueFRRepository.TABLE_NAME)); await _tableServiceClient.CreateTableIfNotExistsAsync(GetTableName(AzureTableStorageQueueITRepository.TABLE_NAME)); await _tableServiceClient.CreateTableIfNotExistsAsync(GetTableName(AzureTableStorageQueueMERepository.TABLE_NAME)); diff --git a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Migrations/Migration_002_QueueES.cs b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Migrations/Migration_002_QueueES.cs new file mode 100644 index 000000000..452fe40de --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Migrations/Migration_002_QueueES.cs @@ -0,0 +1,38 @@ +using System.Threading.Tasks; +using Azure.Data.Tables; +using Azure.Storage.Blobs; +using fiskaltrust.Middleware.Contracts.Models.Transactions; +using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories; +using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.AT; +using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.Configuration; +using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.DE; +using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.FR; +using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.IT; +using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.MasterData; +using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.ME; +using fiskaltrust.storage.V0; +using fiskaltrust.storage.V0.MasterData; + +namespace fiskaltrust.Middleware.Storage.AzureTableStorage.Migrations +{ + public class Migration_002_QueueES : IAzureTableStorageMigration + { + private readonly TableServiceClient _tableServiceClient; + private readonly QueueConfiguration _queueConfiguration; + + public Migration_002_QueueES(TableServiceClient tableServiceClient, QueueConfiguration queueConfiguration) + { + _tableServiceClient = tableServiceClient; + _queueConfiguration = queueConfiguration; + } + + public int Version => 2; + + public async Task ExecuteAsync() + { + await _tableServiceClient.CreateTableIfNotExistsAsync(GetTableName(AzureTableStorageQueueESRepository.TABLE_NAME)); + } + + private string GetTableName(string entityName) => $"x{_queueConfiguration.QueueId.ToString().Replace("-", "")}{entityName}"; + } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/AzureTableStorageConfigurationRepository.cs b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/AzureTableStorageConfigurationRepository.cs index 9d4894741..b5cc5e369 100644 --- a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/AzureTableStorageConfigurationRepository.cs +++ b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/AzureTableStorageConfigurationRepository.cs @@ -3,17 +3,17 @@ using System.Threading.Tasks; using fiskaltrust.storage.V0; using Azure.Data.Tables; -using fiskaltrust.Middleware.Storage.AzureTableStorage.TableEntities.Configuration; using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.Configuration; namespace fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories { - public class AzureTableStorageConfigurationRepository : IConfigurationRepository + public class AzureTableStorageConfigurationRepository : IConfigurationRepository, ES.IConfigurationRepository { private readonly AzureTableStorageCashBoxRepository _cashBoxRepository; private readonly AzureTableStorageQueueRepository _queueRepository; private readonly AzureTableStorageQueueATRepository _queueATRepository; private readonly AzureTableStorageQueueDERepository _queueDERepository; + private readonly AzureTableStorageQueueESRepository _queueESRepository; private readonly AzureTableStorageQueueFRRepository _queueFRRepository; private readonly AzureTableStorageQueueITRepository _queueITRepository; private readonly AzureTableStorageQueueMERepository _queueMERepository; @@ -31,6 +31,7 @@ public AzureTableStorageConfigurationRepository(QueueConfiguration queueConfig, _queueRepository = new AzureTableStorageQueueRepository(queueConfig, tableServiceClient); _queueATRepository = new AzureTableStorageQueueATRepository(queueConfig, tableServiceClient); _queueDERepository = new AzureTableStorageQueueDERepository(queueConfig, tableServiceClient); + _queueESRepository = new AzureTableStorageQueueESRepository(queueConfig, tableServiceClient); _queueFRRepository = new AzureTableStorageQueueFRRepository(queueConfig, tableServiceClient); _queueITRepository = new AzureTableStorageQueueITRepository(queueConfig, tableServiceClient); _queueMERepository = new AzureTableStorageQueueMERepository(queueConfig, tableServiceClient); @@ -87,7 +88,14 @@ public AzureTableStorageConfigurationRepository(QueueConfiguration queueConfig, public async Task InsertOrUpdateSignaturCreationUnitMEAsync(ftSignaturCreationUnitME scu) => await _signaturCreationUnitMERepository.InsertOrUpdateAsync(scu).ConfigureAwait(false); public async Task> GetSignaturCreationUnitMEListAsync() => await _signaturCreationUnitMERepository.GetAsync().ConfigureAwait(false); - public async Task GetSignaturCreationUnitMEAsync(Guid signaturCreationUnitDEId) => await _signaturCreationUnitMERepository.GetAsync(signaturCreationUnitDEId).ConfigureAwait(false); + public async Task GetSignaturCreationUnitMEAsync(Guid signaturCreationUnitMEId) => await _signaturCreationUnitMERepository.GetAsync(signaturCreationUnitMEId).ConfigureAwait(false); + + public Task InsertOrUpdateSignaturCreationUnitESAsync(ES.ftSignaturCreationUnitES scu) => throw new NotImplementedException(); + public Task InsertOrUpdateQueueESAsync(ES.ftQueueES queue) => _queueESRepository.InsertOrUpdateAsync(queue); + public Task> GetSignaturCreationUnitESListAsync() => throw new NotImplementedException(); + public Task GetSignaturCreationUnitESAsync(Guid signaturCreationUnitESId) => throw new NotImplementedException(); + public Task> GetQueueESListAsync() => _queueESRepository.GetAsync(); + public Task GetQueueESAsync(Guid queueESId) => _queueESRepository.GetAsync(queueESId); } } diff --git a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/AzureTableStorageReceiptJournalRepository.cs b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/AzureTableStorageReceiptJournalRepository.cs index e311205a4..3ad125870 100644 --- a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/AzureTableStorageReceiptJournalRepository.cs +++ b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/AzureTableStorageReceiptJournalRepository.cs @@ -84,7 +84,9 @@ public IAsyncEnumerable GetEntriesOnOrAfterTimeStampAsync(long public Task GetByQueueItemId(Guid ftQueueItemId) => throw new NotImplementedException(); public Task GetByReceiptNumber(long ftReceiptNumber) => throw new NotImplementedException(); - public Task GetWithLastTimestampAsync() => throw new NotImplementedException(); + public async Task GetWithLastTimestampAsync() + => MapToStorageEntity(await _tableClient.QueryAsync().OrderBy(x => x.TimeStamp).LastAsync()); + public async Task CountAsync() { diff --git a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/Configuration/AzureTableStorageQueueDERepository.cs b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/Configuration/AzureTableStorageQueueDERepository.cs index 405075327..253e418be 100644 --- a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/Configuration/AzureTableStorageQueueDERepository.cs +++ b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/Configuration/AzureTableStorageQueueDERepository.cs @@ -1,7 +1,6 @@ using System; using System.Threading.Tasks; using Azure.Data.Tables; -using fiskaltrust.Middleware.Storage.AzureTableStorage.Mapping; using fiskaltrust.Middleware.Storage.AzureTableStorage.TableEntities.Configuration; using fiskaltrust.storage.V0; diff --git a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/Configuration/AzureTableStorageQueueESRepository.cs b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/Configuration/AzureTableStorageQueueESRepository.cs new file mode 100644 index 000000000..02d18483b --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/Repositories/Configuration/AzureTableStorageQueueESRepository.cs @@ -0,0 +1,81 @@ +using System; +using System.Threading.Tasks; +using Azure.Data.Tables; +using fiskaltrust.Middleware.Storage.AzureTableStorage.TableEntities.Configuration; +using fiskaltrust.Middleware.Storage.ES; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories.Configuration +{ + public class AzureTableStorageQueueESRepository : BaseAzureTableStorageRepository + { + public AzureTableStorageQueueESRepository(QueueConfiguration queueConfig, TableServiceClient tableServiceClient) + : base(queueConfig, tableServiceClient, TABLE_NAME) { } + + public const string TABLE_NAME = "QueueES"; + + protected override void EntityUpdated(ftQueueES entity) => entity.TimeStamp = DateTime.UtcNow.Ticks; + + protected override Guid GetIdForEntity(ftQueueES entity) => entity.ftQueueESId; + + public async Task InsertOrUpdateAsync(ftQueueES storageEntity) + { + EntityUpdated(storageEntity); + var entity = MapToAzureEntity(storageEntity); + await _tableClient.UpsertEntityAsync(entity, TableUpdateMode.Replace); + } + + protected override AzureTableStorageFtQueueES MapToAzureEntity(ftQueueES src) + { + if (src == null) + { + return null; + } + + return new AzureTableStorageFtQueueES + { + PartitionKey = src.ftQueueESId.ToString(), + RowKey = src.ftQueueESId.ToString(), + ftQueueESId = src.ftQueueESId, + ftSignaturCreationUnitESId = src.ftSignaturCreationUnitESId, + LastHash = src.LastHash, + CashBoxIdentification = src.CashBoxIdentification, + SSCDSignQueueItemId = src.SSCDSignQueueItemId, + SSCDFailCount = src.SSCDFailCount, + SSCDFailMoment = src.SSCDFailMoment?.ToUniversalTime(), + SSCDFailQueueItemId = src.SSCDFailQueueItemId, + UsedFailedCount = src.UsedFailedCount, + UsedFailedMomentMin = src.UsedFailedMomentMin?.ToUniversalTime(), + UsedFailedMomentMax = src.UsedFailedMomentMax?.ToUniversalTime(), + UsedFailedQueueItemId = src.UsedFailedQueueItemId, + TimeStamp = src.TimeStamp, + }; + } + + protected override ftQueueES MapToStorageEntity(AzureTableStorageFtQueueES src) + { + if (src == null) + { + return null; + } + + return new ftQueueES + { + ftQueueESId = src.ftQueueESId, + ftSignaturCreationUnitESId = src.ftSignaturCreationUnitESId, + LastHash = src.LastHash, + CashBoxIdentification = src.CashBoxIdentification, + SSCDSignQueueItemId = src.SSCDSignQueueItemId, + SSCDFailCount = src.SSCDFailCount, + SSCDFailMoment = src.SSCDFailMoment, + SSCDFailQueueItemId = src.SSCDFailQueueItemId, + UsedFailedCount = src.UsedFailedCount, + UsedFailedMomentMin = src.UsedFailedMomentMin, + UsedFailedMomentMax = src.UsedFailedMomentMax, + UsedFailedQueueItemId = src.UsedFailedQueueItemId, + TimeStamp = src.TimeStamp, + }; + } + } +} + diff --git a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/TableEntities/Configuration/AzureTableStorageFtQueueES.cs b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/TableEntities/Configuration/AzureTableStorageFtQueueES.cs new file mode 100644 index 000000000..9c380ad3d --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/TableEntities/Configuration/AzureTableStorageFtQueueES.cs @@ -0,0 +1,28 @@ +using System; + +namespace fiskaltrust.Middleware.Storage.AzureTableStorage.TableEntities.Configuration +{ + public class AzureTableStorageFtQueueES : BaseTableEntity + { + public Guid ftQueueESId { get; set; } + public Guid? ftSignaturCreationUnitESId { get; set; } + public string LastHash { get; set; } + public string CashBoxIdentification { get; set; } + + public Guid? SSCDSignQueueItemId { get; set; } + + public int SSCDFailCount { get; set; } + public DateTime? SSCDFailMoment { get; set; } + public Guid? SSCDFailQueueItemId { get; set; } + + + public int UsedFailedCount { get; set; } + public DateTime? UsedFailedMomentMin { get; set; } + public DateTime? UsedFailedMomentMax { get; set; } + public Guid? UsedFailedQueueItemId { get; set; } + + public long TimeStamp { get; set; } + + public int DailyClosingNumber { get; set; } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/fiskaltrust.Middleware.Storage.AzureTableStorage.csproj b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/fiskaltrust.Middleware.Storage.AzureTableStorage.csproj index 7631713e8..4586847d1 100644 --- a/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/fiskaltrust.Middleware.Storage.AzureTableStorage.csproj +++ b/queue/src/fiskaltrust.Middleware.Storage.AzureTableStorage/fiskaltrust.Middleware.Storage.AzureTableStorage.csproj @@ -15,5 +15,7 @@ + \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Storage/ES/ftJournalES.cs b/queue/src/fiskaltrust.Middleware.Storage/ES/ftJournalES.cs new file mode 100644 index 000000000..e384cafbf --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/ES/ftJournalES.cs @@ -0,0 +1,18 @@ +using System; + +namespace fiskaltrust.Middleware.Storage.ES; + +public class ftJournalES +{ + public Guid ftJournalESId { get; set; } + + public Guid ftSignaturCreationUnitId { get; set; } + + public Guid ftQueueId { get; set; } + + public string JournalType { get; set; } + + public byte[] JournalData { get; set; } + + public long TimeStamp { get; set; } +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Storage/ES/ftQueueES.cs b/queue/src/fiskaltrust.Middleware.Storage/ES/ftQueueES.cs new file mode 100644 index 000000000..1fd6dbabc --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/ES/ftQueueES.cs @@ -0,0 +1,35 @@ +using System; + +namespace fiskaltrust.Middleware.Storage.ES +{ + public class ftQueueES + { + public Guid ftQueueESId { get; set; } + + public Guid? ftSignaturCreationUnitESId { get; set; } + + public string LastHash { get; set; } + + public string LastSignature { get; set; } + + public string CashBoxIdentification { get; set; } + + public Guid? SSCDSignQueueItemId { get; set; } + + public int SSCDFailCount { get; set; } + + public DateTime? SSCDFailMoment { get; set; } + + public Guid? SSCDFailQueueItemId { get; set; } + + public int UsedFailedCount { get; set; } + + public DateTime? UsedFailedMomentMin { get; set; } + + public DateTime? UsedFailedMomentMax { get; set; } + + public Guid? UsedFailedQueueItemId { get; set; } + + public long TimeStamp { get; set; } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Storage/ES/ftSignaturCreationUnitES.cs b/queue/src/fiskaltrust.Middleware.Storage/ES/ftSignaturCreationUnitES.cs new file mode 100644 index 000000000..dec47d06b --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/ES/ftSignaturCreationUnitES.cs @@ -0,0 +1,11 @@ +using System; + +namespace fiskaltrust.Middleware.Storage.ES +{ + public class ftSignaturCreationUnitES + { + public Guid ftSignaturCreationUnitESId { get; set; } + + public long TimeStamp { get; set; } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Storage/GR/ftQueueGR.cs b/queue/src/fiskaltrust.Middleware.Storage/GR/ftQueueGR.cs new file mode 100644 index 000000000..e54fc38c4 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/GR/ftQueueGR.cs @@ -0,0 +1,33 @@ +using System; + +namespace fiskaltrust.Middleware.Storage.GR +{ + public class ftQueueGR + { + public Guid ftQueueGRId { get; set; } + + public Guid? ftSignaturCreationUnitGRId { get; set; } + + public string LastHash { get; set; } + + public string LastSignature { get; set; } + + public string CashBoxIdentification { get; set; } + + public int SSCDFailCount { get; set; } + + public DateTime? SSCDFailMoment { get; set; } + + public Guid? SSCDFailQueueItemId { get; set; } + + public int UsedFailedCount { get; set; } + + public DateTime? UsedFailedMomentMin { get; set; } + + public DateTime? UsedFailedMomentMax { get; set; } + + public Guid? UsedFailedQueueItemId { get; set; } + + public long TimeStamp { get; set; } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Storage/GR/ftSignaturCreationUnitGR.cs b/queue/src/fiskaltrust.Middleware.Storage/GR/ftSignaturCreationUnitGR.cs new file mode 100644 index 000000000..0e53892dd --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/GR/ftSignaturCreationUnitGR.cs @@ -0,0 +1,11 @@ +using System; + +namespace fiskaltrust.Middleware.Storage.GR +{ + public class ftSignaturCreationUnitGR + { + public Guid ftSignaturCreationUnitGRId { get; set; } + + public long TimeStamp { get; set; } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Storage/PT/ftQueuePT.cs b/queue/src/fiskaltrust.Middleware.Storage/PT/ftQueuePT.cs new file mode 100644 index 000000000..3668b696b --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/PT/ftQueuePT.cs @@ -0,0 +1,42 @@ +using System; + +namespace fiskaltrust.Middleware.Storage.PT +{ + public class ftQueuePT + { + public Guid ftQueuePTId { get; set; } + + public Guid? ftSignaturCreationUnitPTId { get; set; } + + public string LastHash { get; set; } + + public string LastSignature { get; set; } + + public string CashBoxIdentification { get; set; } + + public int SSCDFailCount { get; set; } + + public DateTime? SSCDFailMoment { get; set; } + + public Guid? SSCDFailQueueItemId { get; set; } + + public int UsedFailedCount { get; set; } + + public DateTime? UsedFailedMomentMin { get; set; } + + public DateTime? UsedFailedMomentMax { get; set; } + + public Guid? UsedFailedQueueItemId { get; set; } + + public long TimeStamp { get; set; } + + /* + * The following fields should probably be moved to a different config + */ + public string TaxRegion { get; set; } + public string IssuerTIN { get; set; } + public string ATCUD { get; set; } + public string SimplifiedInvoiceSeries { get; set; } + public long SimplifiedInvoiceSeriesNumerator { get; set; } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Storage/PT/ftSignaturCreationUnitPT.cs b/queue/src/fiskaltrust.Middleware.Storage/PT/ftSignaturCreationUnitPT.cs new file mode 100644 index 000000000..0d7047df2 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/PT/ftSignaturCreationUnitPT.cs @@ -0,0 +1,20 @@ +using System; + +namespace fiskaltrust.Middleware.Storage.PT +{ + public class ftSignaturCreationUnitPTConfiguration + { + public Guid ftSignaturCreationUnitPTId { get; set; } + } + + public class ftSignaturCreationUnitPT + { + public Guid ftSignaturCreationUnitPTId { get; set; } + + public string PrivateKey { get; set; } + + public string SoftwareCertificateNumber { get; set; } + + public long TimeStamp { get; set; } + } +} diff --git a/queue/src/fiskaltrust.Middleware.Storage/Repositories/IConfigurationRepository.cs b/queue/src/fiskaltrust.Middleware.Storage/Repositories/IConfigurationRepository.cs new file mode 100644 index 000000000..fa92ef6d1 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/Repositories/IConfigurationRepository.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; +namespace fiskaltrust.Middleware.Storage.ES; + +public interface IConfigurationRepository : IReadOnlyConfigurationRepository +{ + Task InsertOrUpdateSignaturCreationUnitESAsync(ftSignaturCreationUnitES scu); + + Task InsertOrUpdateQueueESAsync(ftQueueES queue); + +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Storage/Repositories/IReadOnlyConfigurationRepository.cs b/queue/src/fiskaltrust.Middleware.Storage/Repositories/IReadOnlyConfigurationRepository.cs new file mode 100644 index 000000000..21ab0ce52 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/Repositories/IReadOnlyConfigurationRepository.cs @@ -0,0 +1,19 @@ + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace fiskaltrust.Middleware.Storage.ES; + +public interface IReadOnlyConfigurationRepository +{ + Task> GetSignaturCreationUnitESListAsync(); + + Task GetSignaturCreationUnitESAsync(Guid signaturCreationUnitESId); + + + Task> GetQueueESListAsync(); + + Task GetQueueESAsync(Guid queueESId); + +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Storage/Repositories/IReadOnlyJournalFRRepository.cs b/queue/src/fiskaltrust.Middleware.Storage/Repositories/IReadOnlyJournalFRRepository.cs new file mode 100644 index 000000000..2735ce7c9 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/Repositories/IReadOnlyJournalFRRepository.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using fiskaltrust.Middleware.Storage.ES; + +namespace fiskaltrust.Middleware.Storage.Repositories; + +public interface IReadOnlyJournalESRepository +{ + Task> GetAsync(); + + Task GetAsync(Guid id); +} \ No newline at end of file diff --git a/queue/src/fiskaltrust.Middleware.Storage/fiskaltrust.Middleware.Storage.csproj b/queue/src/fiskaltrust.Middleware.Storage/fiskaltrust.Middleware.Storage.csproj new file mode 100644 index 000000000..8859a7b41 --- /dev/null +++ b/queue/src/fiskaltrust.Middleware.Storage/fiskaltrust.Middleware.Storage.csproj @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/Certificates/.gitignore b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/Certificates/.gitignore new file mode 100644 index 000000000..16ebf9f28 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/Certificates/.gitignore @@ -0,0 +1 @@ +*.p12 \ No newline at end of file diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/FullTest.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/FullTest.cs new file mode 100644 index 000000000..0d0f78bef --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/FullTest.cs @@ -0,0 +1,170 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.storage.serialization.V0; +using fiskaltrust.storage.V0; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueueES.UnitTest +{ + public class FullTest() + { + public async Task GetConfigurationAsync(Guid cashBoxId, string accessToken) + { + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = new Uri("https://helipad-sandbox.fiskaltrust.cloud"); + httpClient.DefaultRequestHeaders.Clear(); + httpClient.DefaultRequestHeaders.Add("cashboxid", cashBoxId.ToString()); + httpClient.DefaultRequestHeaders.Add("accesstoken", accessToken); + var result = await httpClient.GetAsync("api/configuration"); + var content = await result.Content.ReadAsStringAsync(); + if (result.IsSuccessStatusCode) + { + if (string.IsNullOrEmpty(content)) + { + throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + } + + var configuration = JsonConvert.DeserializeObject(content); + configuration.TimeStamp = DateTime.UtcNow.Ticks; + return configuration; + } + else + { + throw new Exception($"{content}"); + } + } + } + + [Fact] + public async Task FullTests() + { + var cashBoxId = Guid.Parse("8e2e348b-0a37-45d6-8f22-0aaa82db44ea"); + var accessToken = "BMlOgJSEC1url4Nwd9QSc7rIXGfiEC65Afai4WZjPxbIIUIHykTnp96nryJsnsC98BYaY2jh+lZIbN06JF6LEtg="; + + var configuration = await GetConfigurationAsync(cashBoxId, accessToken); + var queue = configuration.ftQueues.First(); + + queue.Configuration["certificate"] = Convert.ToBase64String(await File.ReadAllBytesAsync("Certificates/Certificado_RPJ_A39200019_CERTIFICADO_ENTIDAD_PRUEBAS_4_Pre.p12")); + queue.Configuration["certificatePassword"] = "1234"; + var bootstrapper = new QueueESBootstrapper(queue.Id, new LoggerFactory(), queue.Configuration); + var signMethod = bootstrapper.RegisterForSign(); + var journalMethod = bootstrapper.RegisterForJournal(); + { + + var initialOperationRequest = InitialOperation(cashBoxId); + var initOperationResponse = await signMethod(System.Text.Json.JsonSerializer.Serialize(initialOperationRequest)); + } + + { + var receiptRequestWrong = ExampleCashSales(cashBoxId); + receiptRequestWrong.cbChargeItems.First().VATRate = 20; + var exampleCashSalesResponseWrongString = await signMethod(System.Text.Json.JsonSerializer.Serialize(receiptRequestWrong)); + var exampleCashSalesResponseWrong = System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponseWrongString)!; + + exampleCashSalesResponseWrong.ftState.Should().Match(x => (x & 0xFFFF_FFFF) == 0xEEEE_EEEE, $"ftState 0x{exampleCashSalesResponseWrong.ftState:X} should be == 0xEEEE_EEEE\n{exampleCashSalesResponseWrong.ftSignatures.Find(x => (x.ftSignatureType & 0xFFFF_FFFF) == 0x3000)?.Data ?? exampleCashSalesResponseWrongString}\n"); + } + + var receiptRequest = ExampleCashSales(cashBoxId); + { + var exampleCashSalesResponseString = await signMethod(System.Text.Json.JsonSerializer.Serialize(receiptRequest)); + var exampleCashSalesResponse = System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponseString)!; + var errorItem = exampleCashSalesResponse.ftSignatures.Find(x => (x.ftSignatureType & 0xFFFF_FFFF) == 0x3000); + exampleCashSalesResponse.ftState.Should().Match(x => (x & 0xFFFF_FFFF) < 0xEEEE_EEEE, $"ftState 0x{exampleCashSalesResponse.ftState:X} should be < 0xEEEE_EEEE\n{errorItem?.Data ?? exampleCashSalesResponseString}\n"); + } + + { + var receiptRequestVoid = ExampleCashSales(cashBoxId); + receiptRequestVoid.ftReceiptCase = receiptRequestVoid.ftReceiptCase | 0x0004_0000; + receiptRequestVoid.cbPreviousReceiptReference = receiptRequest.cbReceiptReference; + var exampleCashSalesVoidResponseString = await signMethod(System.Text.Json.JsonSerializer.Serialize(receiptRequestVoid)); + var exampleCashSalesVoidResponse = System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesVoidResponseString)!; + var errorItemVoid = exampleCashSalesVoidResponse.ftSignatures.Find(x => (x.ftSignatureType & 0xFFFF_FFFF) == 0x3000); + exampleCashSalesVoidResponse.ftState.Should().Match(x => (x & 0xFFFF_FFFF) < 0xEEEE_EEEE, $"ftState 0x{exampleCashSalesVoidResponse.ftState:X} should be < 0xEEEE_EEEE\n{errorItemVoid?.Data ?? exampleCashSalesVoidResponseString}\n"); + } + + receiptRequest = ExampleCashSales(cashBoxId); + { + var exampleCashSalesResponseString = await signMethod(System.Text.Json.JsonSerializer.Serialize(receiptRequest)); + var exampleCashSalesResponse = System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponseString)!; + var errorItem = exampleCashSalesResponse.ftSignatures.Find(x => (x.ftSignatureType & 0xFFFF_FFFF) == 0x3000); + exampleCashSalesResponse.ftState.Should().Match(x => (x & 0xFFFF_FFFF) < 0xEEEE_EEEE, $"ftState 0x{exampleCashSalesResponse.ftState:X} should be < 0xEEEE_EEEE\n{errorItem?.Data ?? exampleCashSalesResponseString}\n"); + } + + { + var receiptRequestVoid = ExampleCashSales(cashBoxId); + receiptRequestVoid.ftReceiptCase = receiptRequestVoid.ftReceiptCase | 0x0004_0000; + receiptRequestVoid.cbPreviousReceiptReference = receiptRequest.cbReceiptReference; + var exampleCashSalesVoidResponseString = await signMethod(System.Text.Json.JsonSerializer.Serialize(receiptRequestVoid)); + var exampleCashSalesVoidResponse = System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesVoidResponseString)!; + var errorItemVoid = exampleCashSalesVoidResponse.ftSignatures.Find(x => (x.ftSignatureType & 0xFFFF_FFFF) == 0x3000); + exampleCashSalesVoidResponse.ftState.Should().Match(x => (x & 0xFFFF_FFFF) < 0xEEEE_EEEE, $"ftState 0x{exampleCashSalesVoidResponse.ftState:X} should be < 0xEEEE_EEEE\n{errorItemVoid?.Data ?? exampleCashSalesVoidResponseString}\n"); + } + + var veriFactuString = await journalMethod(System.Text.Json.JsonSerializer.Serialize(new ifPOS.v1.JournalRequest + { + ftJournalType = 0x4752_2000_0000_0000 + })); + } + + private static ReceiptRequest InitialOperation(Guid cashBoxId) + { + return new ReceiptRequest + { + ftCashBoxID = cashBoxId, + ftReceiptCase = 0x4752_2000_0000_4001, + cbTerminalID = "1", + cbReceiptReference = Guid.NewGuid().ToString(), + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = [], + cbPayItems = [] + }; + } + + private static ReceiptRequest ExampleCashSales(Guid cashBoxId) + { + return new ReceiptRequest + { + ftCashBoxID = cashBoxId, + ftReceiptCase = 0x4752_2000_0000_0000, + cbTerminalID = "1", + cbReceiptReference = Guid.NewGuid().ToString(), + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + ftChargeItemCase = 0x4752_2000_0000_0013, + VATAmount = 1.30m, + Amount = 6.2m, + VATRate = 21m, + Quantity = 1, + Description = "ChargeItem1" + }, + new ChargeItem + { + Position = 2, + ftChargeItemCase = 0x4752_2000_0000_0013, + VATAmount = 1.30m, + Amount = 6.2m, + VATRate = 21m, + Quantity = 1, + Description = "ChargeItem2" + } + ], + cbPayItems = + [ + new PayItem + { + ftPayItemCase = 0x4752_2000_0000_0001, + Amount = 12.4m, + Description = "Cash" + } + ] + }; + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/DailyOperationsCommandProcessorESTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/DailyOperationsCommandProcessorESTests.cs new file mode 100644 index 000000000..086af5059 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/DailyOperationsCommandProcessorESTests.cs @@ -0,0 +1,77 @@ +using System.Threading.Tasks; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueES.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using FluentAssertions; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using Moq; +using fiskaltrust.Middleware.Localization.QueueES.ESSSCD; +using fiskaltrust.Middleware.Localization.v2.Storage; + +namespace fiskaltrust.Middleware.Localization.QueueES.UnitTest.QueueES.Processors +{ + public class DailyOperationsCommandProcessorESTests + { + private readonly DailyOperationsCommandProcessorES _sut = new DailyOperationsCommandProcessorES(Mock.Of(), Mock.Of()); + + [Theory] + [InlineData(ReceiptCases.ZeroReceipt0x2000)] + [InlineData(ReceiptCases.OneReceipt0x2001)] + [InlineData(ReceiptCases.ShiftClosing0x2010)] + [InlineData(ReceiptCases.DailyClosing0x2011)] + [InlineData(ReceiptCases.MonthlyClosing0x2012)] + [InlineData(ReceiptCases.YearlyClosing0x2013)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError_IfInvalidCaseIsUsed() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/InvoiceCommandProcessorESTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/InvoiceCommandProcessorESTests.cs new file mode 100644 index 000000000..edd266546 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/InvoiceCommandProcessorESTests.cs @@ -0,0 +1,73 @@ +using System.Threading.Tasks; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueES.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using FluentAssertions; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueueES.UnitTest.QueueES.Processors +{ + public class InvoiceCommandProcessorESTests + { + private readonly InvoiceCommandProcessorES _sut = new InvoiceCommandProcessorES(); + + [Theory] + [InlineData(ReceiptCases.InvoiceUnknown0x1000)] + [InlineData(ReceiptCases.InvoiceB2C0x1001)] + [InlineData(ReceiptCases.InvoiceB2B0x1002)] + [InlineData(ReceiptCases.InvoiceB2G0x1003)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await _sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/LifecycleCommandProcessorESTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/LifecycleCommandProcessorESTests.cs new file mode 100644 index 000000000..d13944947 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/LifecycleCommandProcessorESTests.cs @@ -0,0 +1,365 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueES.Models; +using fiskaltrust.Middleware.Localization.QueueES.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.Storage; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using FluentAssertions; +using FluentAssertions.Execution; +using Moq; +using Newtonsoft.Json; +using Xunit; +using AutoFixture; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using fiskaltrust.Middleware.Localization.QueueES.ESSSCD; +using fiskaltrust.Middleware.Storage.ES; +using fiskaltrust.storage.V0.MasterData; +using fiskaltrust.Middleware.Contracts.Repositories; + +namespace fiskaltrust.Middleware.Localization.QueueES.UnitTest.QueueES.Processors +{ + public class LifecycleCommandProcessorESTests + { + private readonly Fixture _fixture = new Fixture(); + private readonly LifecycleCommandProcessorES _sut = new(Mock.Of()); + + [Theory] + [InlineData(ReceiptCases.InitialOperationReceipt0x4001)] + [InlineData(ReceiptCases.OutOfOperationReceipt0x4002)] + [InlineData(ReceiptCases.InitSCUSwitch0x4011)] + [InlineData(ReceiptCases.FinishSCUSwitch0x4012)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var signaturCreationUnitES = new ftSignaturCreationUnitES + { + + }; + + var masterDataConfiguration = _fixture.Create(); + + var configMock = new Mock(); + configMock.Setup(x => x.ActivateQueueAsync()).Returns(Task.CompletedTask); + var sut = new LifecycleCommandProcessorES(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "0#0", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().NotBe(0x4752_2000_EEEE_EEEE); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + } + + [Fact(Skip = "not fully implemented")] + public async Task InitialOperationReceipt0x4001Async_ShouldReturnActionJournal_InitOperationSignature_AndSetStateInQueue() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var signaturCreationUnitES = new ftSignaturCreationUnitES + { + + }; + + var masterDataConfiguration = _fixture.Create(); + + var configMock = new Mock(); + configMock.Setup(x => x.ActivateQueueAsync()).Returns(Task.CompletedTask); + var sut = new LifecycleCommandProcessorES(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "0#0", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await sut.InitialOperationReceipt0x4001Async(request); + + using var scope = new AssertionScope(); + + queue.StartMoment.Should().BeCloseTo(DateTime.UtcNow, 1000); + + result.receiptResponse.Should().Be(receiptResponse); + result.actionJournals.Should().NotBeEmpty(); + result.receiptResponse.ftSignatures.Should().NotBeEmpty(); + + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000, because: $"ftState {result.receiptResponse.ftState.ToString("X")} is different than expected."); + + var expectedSignaturItem = new SignatureItem + { + Caption = "Initial-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}", + ftSignatureFormat = (int) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = 0x4752_2000_0001_1001 + }; + + result.receiptResponse.ftSignatures[0].Should().BeEquivalentTo(expectedSignaturItem); + + var expectedActionJournal = new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queue.ftQueueId, + ftQueueItemId = queueItem.ftQueueItemId, + Moment = DateTime.UtcNow, + Priority = -1, + Type = "4752200000004001-ActivateQueueES", + Message = $"Initial-Operation receipt. Queue-ID: {queue.ftQueueId}", + DataBase64 = null, + TimeStamp = 0 + }; + result.actionJournals[0].ftActionJournalId.Should().NotBe(Guid.Empty); + result.actionJournals[0].ftQueueId.Should().Be(expectedActionJournal.ftQueueId); + result.actionJournals[0].ftQueueItemId.Should().Be(expectedActionJournal.ftQueueItemId); + result.actionJournals[0].Moment.Should().BeCloseTo(expectedActionJournal.Moment, 1000); + result.actionJournals[0].Priority.Should().Be(expectedActionJournal.Priority); + result.actionJournals[0].Type.Should().Be(expectedActionJournal.Type); + result.actionJournals[0].Message.Should().Be(expectedActionJournal.Message); + result.actionJournals[0].DataBase64.Should().Be(expectedActionJournal.DataBase64); + result.actionJournals[0].TimeStamp.Should().Be(expectedActionJournal.TimeStamp); + + var data = JsonConvert.DeserializeObject(result.actionJournals[0].DataJson); + data.CashBoxId.Should().Be(receiptRequest.ftCashBoxID.GetValueOrDefault()); + data.IsStartReceipt.Should().Be(true); + data.Moment.Should().BeCloseTo(DateTime.UtcNow, 1000); + data.QueueId.Should().Be(queueItem.ftQueueId); + data.Version.Should().Be("V0"); + + configMock.Verify(x => x.ActivateQueueAsync(), Times.Exactly(1)); + } + + [Fact(Skip = "not fully implemented")] + public async Task OutOfOperationReceipt0x4002Async_ShouldReturnActionJournal_InitOperationSignature_AndSetStateInQueue() + { + var queue = TestHelpers.CreateQueue(); + queue.StartMoment = DateTime.UtcNow; + + var queueItem = TestHelpers.CreateQueueItem(); + + var signaturCreationUnitES = new ftSignaturCreationUnitES + { + + }; + + var masterDataConfiguration = new MasterDataConfiguration + { + + }; + + var configMock = new Mock(); + var sut = new LifecycleCommandProcessorES(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0000 | (long) ReceiptCases.OutOfOperationReceipt0x4002 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await sut.OutOfOperationReceipt0x4002Async(request); + + using var scope = new AssertionScope(); + queue.StopMoment.Should().BeCloseTo(DateTime.UtcNow, 1000); + result.receiptResponse.Should().Be(receiptResponse); + result.actionJournals.Should().NotBeEmpty(); + result.receiptResponse.ftSignatures.Should().NotBeEmpty(); + + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0001, because: $"ftState {result.receiptResponse.ftState.ToString("X")} is different than expected."); + + var expectedSignaturItem = new SignatureItem + { + ftSignatureType = 0x4752_2000_0001_1002, + ftSignatureFormat = (int) ifPOS.v1.SignaturItem.Formats.Text, + Caption = "Out-of-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}" + }; + + result.receiptResponse.ftSignatures[0].Should().BeEquivalentTo(expectedSignaturItem); + + var expectedActionJournal = new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queue.ftQueueId, + ftQueueItemId = queueItem.ftQueueItemId, + Moment = DateTime.UtcNow, + Priority = -1, + Type = "4752200000004002-DeactivateQueueES", + Message = $"Out-of-Operation receipt. Queue-ID: {queue.ftQueueId}", + DataBase64 = null, + TimeStamp = 0 + }; + result.actionJournals[0].ftActionJournalId.Should().NotBe(Guid.Empty); + result.actionJournals[0].ftQueueId.Should().Be(expectedActionJournal.ftQueueId); + result.actionJournals[0].ftQueueItemId.Should().Be(expectedActionJournal.ftQueueItemId); + result.actionJournals[0].Moment.Should().BeCloseTo(expectedActionJournal.Moment, 1000); + result.actionJournals[0].Priority.Should().Be(expectedActionJournal.Priority); + result.actionJournals[0].Type.Should().Be(expectedActionJournal.Type); + result.actionJournals[0].Message.Should().Be(expectedActionJournal.Message); + result.actionJournals[0].DataBase64.Should().Be(expectedActionJournal.DataBase64); + result.actionJournals[0].TimeStamp.Should().Be(expectedActionJournal.TimeStamp); + + var data = JsonConvert.DeserializeObject(result.actionJournals[0].DataJson); + data.CashBoxId.Should().Be(receiptRequest.ftCashBoxID.GetValueOrDefault()); + data.IsStopReceipt.Should().Be(true); + data.Moment.Should().BeCloseTo(DateTime.UtcNow, 1000); + data.QueueId.Should().Be(queueItem.ftQueueId); + data.Version.Should().Be("V0"); + + configMock.Verify(x => x.ActivateQueueAsync(), Times.Exactly(1)); + } + + [Fact] + public async Task InitSCUSwitch0x4011Async_ShouldDoNothing() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var signaturCreationUnitES = new ftSignaturCreationUnitES + { + + }; + + var masterDataConfiguration = new MasterDataConfiguration + { + + }; + + var configMock = new Mock(); + var sut = new LifecycleCommandProcessorES(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await sut.InitSCUSwitch0x4011Async(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + result.receiptResponse.ftSignatures.Should().BeEmpty(); + result.actionJournals.Should().BeEmpty(); + } + + [Fact] + public async Task FinishSCUSwitch0x4012Async_ShouldDoNothing() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var signaturCreationUnitES = new ftSignaturCreationUnitES + { + + }; + + var masterDataConfiguration = new MasterDataConfiguration + { + + }; + + var configMock = new Mock(); + var sut = new LifecycleCommandProcessorES(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await sut.FinishSCUSwitch0x4012Async(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + result.receiptResponse.ftSignatures.Should().BeEmpty(); + result.actionJournals.Should().BeEmpty(); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/ProtocolCommandProcessorESTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/ProtocolCommandProcessorESTests.cs new file mode 100644 index 000000000..b44c3807e --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/ProtocolCommandProcessorESTests.cs @@ -0,0 +1,74 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueES.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using FluentAssertions; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueueES.UnitTest.QueueES.Processors +{ + public class ProtocolCommandProcessorESTests + { + private readonly ProtocolCommandProcessorES _sut = new(); + + [Theory] + [InlineData(ReceiptCases.ProtocolUnspecified0x3000)] + [InlineData(ReceiptCases.ProtocolTechnicalEvent0x3001)] + [InlineData(ReceiptCases.ProtocolAccountingEvent0x3002)] + [InlineData(ReceiptCases.InternalUsageMaterialConsumption0x3003)] + [InlineData(ReceiptCases.Order0x3004)] + [InlineData(ReceiptCases.CopyReceiptPrintExistingReceipt0x3010)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/ReceiptCommandProcessorESTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/ReceiptCommandProcessorESTests.cs new file mode 100644 index 000000000..8db0411cf --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/ReceiptCommandProcessorESTests.cs @@ -0,0 +1,222 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueES.ESSSCD; +using fiskaltrust.Middleware.Localization.QueueES.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Storage; +using fiskaltrust.Middleware.Storage.ES; +using fiskaltrust.storage.V0; +using FluentAssertions; +using FluentAssertions.Execution; +using Moq; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using fiskaltrust.storage.V0.MasterData; +using AutoFixture; +using fiskaltrust.Middleware.Localization.v2.Storage; +using System.Text.Json; +using fiskaltrust.Middleware.Contracts.Factories; +using System.Text; +using fiskaltrust.Middleware.Localization.QueueES.Interface; +using fiskaltrust.Middleware.Contracts.Repositories; + +namespace fiskaltrust.Middleware.Localization.QueueES.UnitTest.QueueES.Processors +{ + public class ReceiptCommandProcessorESTests + { + private readonly Fixture _fixture; + + public ReceiptCommandProcessorESTests() + { + _fixture = new Fixture(); + _fixture.Customize(c => c + .With(r => r.ftReceiptIdentification, () => $"{_fixture.Create():X}#{_fixture.Create()}") + .With(r => r.ftSignatures, () => + _fixture + .CreateMany() + .Append(Factories.SignaturItemFactory.CreateESHuella(Convert.ToBase64String(_fixture.CreateMany().ToArray()))) + .Append(new SignatureItem + { + ftSignatureType = (long) SignatureTypesES.IDEmisorFactura, + ftSignatureFormat = (int) ifPOS.v1.SignaturItem.Formats.QR_Code, + Caption = "IDEmisorFactura", + Data = _fixture.Create() + }) + .ToList() + ) + ); + _fixture.Customize(c => c + .With(q => q.request, () => JsonSerializer.Serialize(_fixture.Create())) + .With(q => q.response, () => JsonSerializer.Serialize(_fixture.Create())) + ); + } + + private readonly ReceiptCommandProcessorES _sut = new ReceiptCommandProcessorES(Mock.Of(), Mock.Of(), Mock.Of()); + + [Theory] + [InlineData(ReceiptCases.PaymentTransfer0x0002)] + [InlineData(ReceiptCases.PointOfSaleReceiptWithoutObligation0x0003)] + [InlineData(ReceiptCases.ECommerce0x0004)] + [InlineData(ReceiptCases.Protocol0x0005)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4553_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await _sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4553_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4553_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4553_2000_EEEE_EEEE); + } + + [Fact] + public async Task PointOfSaleReceipt0x0001Async_Should_Return_QRCodeInSignatures() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var previousQueueItem = TestHelpers.CreateQueueItem(); + var queueES = new ftQueueES() + { + SSCDSignQueueItemId = previousQueueItem.ftQueueItemId + }; + var signaturCreationUnitES = new ftSignaturCreationUnitES + { + + }; + + var masterDataConfiguration = _fixture.Create(); + masterDataConfiguration.Outlet.VatId = "VATTEST"; + + var configMock = new Mock(); + configMock.Setup(x => x.InsertOrUpdateQueueAsync(It.IsAny())).Returns(Task.CompletedTask); + var configurationRepositoryMock = new Mock(); + configurationRepositoryMock.Setup(x => x.GetQueueESAsync(queue.ftQueueId)).ReturnsAsync(queueES); + var queueItemRepositoryMock = new Mock(); + queueItemRepositoryMock.Setup(x => x.GetAsync(previousQueueItem.ftQueueItemId)).ReturnsAsync(previousQueueItem); + + var config = new InMemorySCUConfiguration(); + var sut = new ReceiptCommandProcessorES(new InMemorySCU(signaturCreationUnitES, masterDataConfiguration, config, Mock.Of()), configurationRepositoryMock.Object, queueItemRepositoryMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x4553_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001, + cbReceiptMoment = new DateTime(2019, 12, 31), + cbReceiptReference = "TEST", + cbChargeItems = [ + new ChargeItem + { + ftChargeItemCase = 0x4553_2000_0000_0008, + Amount = 12000.00m, + VATAmount = 0m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + new ChargeItem + { + ftChargeItemCase = 0x4553_2000_0000_0001, + Amount = 15900m, + VATAmount = 900m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + new ChargeItem + { + ftChargeItemCase = 0x4553_2000_0000_0006, + Amount = 56500m, + VATAmount = 6500m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + new ChargeItem + { + ftChargeItemCase = 0x4553_2000_0000_0003, + Amount = 98400m, + VATAmount = 18400m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + ] + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4553_2000_0000_0000, + ftQueueID = queue.ftQueueId, + ftQueueItemID = queueItem.ftQueueItemId, + ftCashBoxIdentification = "cashBoxIdentification", + + ftQueueRow = 1, + ftReceiptIdentification = "0#", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await sut.PointOfSaleReceipt0x0001Async(request); + + using var scope = new AssertionScope(); + result.receiptResponse.Should().Be(receiptResponse); + result.actionJournals.Should().BeEmpty(); + result.receiptResponse.ftSignatures.Should().NotBeEmpty(); + + + result.receiptResponse.ftState.Should().Be(0x4553_2000_0000_0000, because: $"ftState {result.receiptResponse.ftState:X} is different than expected."); + var expectedSignaturItem = new SignatureItem + { + ftSignatureType = 0x4553_2000_0000_0001, + ftSignatureFormat = (int) ifPOS.v1.SignaturItem.Formats.QR_Code, + Caption = "[www.fiskaltrust.es]", + Data = "https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQR?nif=VATTEST&numserie=1%2fTEST&fecha=31-12-2019&importe=182800.00" + }; + result.receiptResponse.ftQueueID.Should().Be(receiptResponse.ftQueueID); + result.receiptResponse.ftQueueItemID.Should().Be(receiptResponse.ftQueueItemID); + result.receiptResponse.ftReceiptIdentification.Should().Be("0#1/TEST"); + result.receiptResponse.ftSignatures[0].Should().BeEquivalentTo(expectedSignaturItem); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/TestHelpers.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/TestHelpers.cs new file mode 100644 index 000000000..01db5f7d0 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/QueueES/Processors/TestHelpers.cs @@ -0,0 +1,25 @@ +using System; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.QueueES.UnitTest.QueueES.Processors +{ + public static class TestHelpers + { + public static ftQueue CreateQueue() + { + return new ftQueue + { + ftQueueId = Guid.NewGuid(), + }; + } + + public static ftQueueItem CreateQueueItem() + { + return new ftQueueItem + { + ftQueueId = Guid.NewGuid(), + ftQueueItemId = Guid.NewGuid(), + }; + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/ReceiptProcessorTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/ReceiptProcessorTests.cs new file mode 100644 index 000000000..1aa4131b3 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/ReceiptProcessorTests.cs @@ -0,0 +1,70 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueueES.UnitTest +{ + + public class ReceiptProcessorTests + { + [Fact] + public async Task ReceiptProcessor_ThrowException_ReturnErrorResponse() + { + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = 0x4752_2000_0000_0000 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var sut = new ReceiptProcessor(LoggerFactory.Create(x => { }).CreateLogger(), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict)); + var result = await sut.ProcessAsync(receiptRequest, receiptResponse, new ftQueue { }, new ftQueueItem { }); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + result.receiptResponse.ftSignatures.Should().HaveCount(1); + result.receiptResponse.ftSignatures[0].ftSignatureType.Should().Be(0x4752_2000_0000_3000); + result.receiptResponse.ftSignatures[0].Caption.Should().Be("FAILURE"); + } + + [Fact] + public async Task ReceiptProcessor_ReturnNotSupported_ReturnErrorResponse() + { + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = 0x4752_2000_0000_0000 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var sut = new ReceiptProcessor(LoggerFactory.Create(x => { }).CreateLogger(), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict)); + var result = await sut.ProcessAsync(receiptRequest, receiptResponse, new ftQueue { }, new ftQueueItem { }); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + result.receiptResponse.ftSignatures.Should().HaveCount(1); + result.receiptResponse.ftSignatures[0].ftSignatureType.Should().Be(0x4752_2000_0000_3000); + result.receiptResponse.ftSignatures[0].Caption.Should().Be("FAILURE"); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/VeriFactuTest.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/VeriFactuTest.cs new file mode 100644 index 000000000..0ad85f6b4 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/VeriFactuTest.cs @@ -0,0 +1,163 @@ +using System.Security.Cryptography.X509Certificates; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Contracts.Repositories; +using fiskaltrust.Middleware.Localization.QueueES.ESSSCD; +using fiskaltrust.Middleware.Localization.QueueES.Exports; +using fiskaltrust.Middleware.SCU.ES.Models; +using fiskaltrust.Middleware.SCU.ES.Soap; +using fiskaltrust.storage.V0; +using fiskaltrust.storage.V0.MasterData; +using FluentAssertions; +using FluentAssertions.Execution; +using Moq; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueueES.UnitTest +{ + public class VeriFactuTest() + { + [Fact] + public async Task ResetReceipts() + { + var certificate = new X509Certificate2(await File.ReadAllBytesAsync("Certificates/Certificado_RPJ_A39200019_CERTIFICADO_ENTIDAD_PRUEBAS_4_Pre.p12"), "1234"); + + var masterData = new MasterDataConfiguration() + { + Account = new AccountMasterData + { + VatId = "M0291081Q", + AccountName = "Thomas Steininger" + } + }; + var queueItemRepository = new Mock(); + + var receiptRequest = ExampleCashSales(Guid.NewGuid()); + var receiptResponse = new ReceiptResponse + { + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 0, + ftCashBoxIdentification = Guid.NewGuid().ToString(), + ftReceiptIdentification = $"0#0/{receiptRequest.cbReceiptReference}", + ftReceiptMoment = DateTime.UtcNow, + ftState = 0x4752_2000_0000_0000, + }; + + var veriFactuMapping = new VeriFactuMapping(masterData, queueItemRepository.Object, certificate); + var journalES = await veriFactuMapping.CreateRegistroFacturacionAltaAsync(receiptRequest, receiptResponse, null, null); + + var client = new Client(new Uri(new InMemorySCUConfiguration().BaseUrl), certificate); + + var envelope = new Envelope + { + Body = new RequestBody + { + RegFactuSistemaFacturacion = veriFactuMapping.CreateRegFactuSistemaFacturacion(journalES) + } + }; + envelope.Body.RegFactuSistemaFacturacion.RegistroFactura.First().Item.As().Subsanacion = Booleano.S; + envelope.Body.RegFactuSistemaFacturacion.RegistroFactura.First().Item.As().RechazoPrevio = RechazoPrevio.X; + + var requestXml = envelope.XmlSerialize(); + var response = await client.SendAsync(envelope); + + using var scope = new AssertionScope(); + response.IsOk.Should().BeTrue($"Response should not be error\n{response.ErrValue?.ToString()}\n"); + response.OkValue!.RespuestaLinea.Should().HaveCount(1); + response.OkValue!.RespuestaLinea!.First().CodigoErrorRegistro.Should().BeNullOrEmpty(); + response.OkValue!.RespuestaLinea!.First().DescripcionErrorRegistro.Should().BeNullOrEmpty(); + } + [Fact] + public async Task VeriFactuTestInit() + { + var certificate = new X509Certificate2(await File.ReadAllBytesAsync("Certificates/Certificado_RPJ_A39200019_CERTIFICADO_ENTIDAD_PRUEBAS_4_Pre.p12"), "1234"); + + var masterData = new MasterDataConfiguration() + { + Account = new AccountMasterData + { + VatId = "M0291081Q", + AccountName = "Thomas Steininger" + } + }; + var queueItemRepository = new Mock(); + + var receiptRequest = ExampleCashSales(Guid.NewGuid()); + var receiptResponse = new ReceiptResponse + { + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 0, + ftCashBoxIdentification = Guid.NewGuid().ToString(), + ftReceiptIdentification = $"0#0/{receiptRequest.cbReceiptReference}", + ftReceiptMoment = DateTime.UtcNow, + ftState = 0x4752_2000_0000_0000, + }; + + var veriFactuMapping = new VeriFactuMapping(masterData, queueItemRepository.Object, certificate); + var journalES = await veriFactuMapping.CreateRegistroFacturacionAltaAsync(receiptRequest, receiptResponse, null, null); + + var client = new Client(new Uri(new InMemorySCUConfiguration().BaseUrl), certificate); + + var envelope = new Envelope + { + Body = new RequestBody + { + RegFactuSistemaFacturacion = veriFactuMapping.CreateRegFactuSistemaFacturacion(journalES) + } + }; + var requestXml = envelope.XmlSerialize(); + var response = await client.SendAsync(envelope); + + using var scope = new AssertionScope(); + response.IsOk.Should().BeTrue($"Response should not be error\n{response.ErrValue?.ToString()}\n"); + response.OkValue!.RespuestaLinea.Should().HaveCount(1); + response.OkValue!.RespuestaLinea!.First().CodigoErrorRegistro.Should().BeNullOrEmpty(); + response.OkValue!.RespuestaLinea!.First().DescripcionErrorRegistro.Should().BeNullOrEmpty(); + } + + private static ReceiptRequest ExampleCashSales(Guid cashBoxId) + { + return new ReceiptRequest + { + ftCashBoxID = cashBoxId, + ftReceiptCase = 0x4752_2000_0000_0000, + cbTerminalID = "1", + cbReceiptReference = Guid.NewGuid().ToString(), + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + ftChargeItemCase = 0x4752_2000_0000_0013, + VATAmount = 1.30m, + Amount = 6.2m, + VATRate = 21m, + Quantity = 1, + Description = "ChargeItem1" + }, + new ChargeItem + { + Position = 2, + ftChargeItemCase = 0x4752_2000_0000_0013, + VATAmount = 1.30m, + Amount = 6.2m, + VATRate = 21m, + Quantity = 1, + Description = "ChargeItem2" + } + ], + cbPayItems = + [ + new PayItem + { + ftPayItemCase = 0x4752_2000_0000_0001, + Amount = 12.4m, + Description = "Cash" + } + ] + }; + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/fiskaltrust.Middleware.Localization.QueueES.UnitTest.csproj b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/fiskaltrust.Middleware.Localization.QueueES.UnitTest.csproj new file mode 100644 index 000000000..a37a763df --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueES.UnitTest/fiskaltrust.Middleware.Localization.QueueES.UnitTest.csproj @@ -0,0 +1,30 @@ + + + + net8 + Latest + enable + enable + + + + + + + + + + + + + + + + + PreserveNewest + + + + \ No newline at end of file diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/FullTest.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/FullTest.cs new file mode 100644 index 000000000..31fd23f03 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/FullTest.cs @@ -0,0 +1,188 @@ +using System.Net.Http.Json; +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.v2.Configuration; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueueGR.UnitTest +{ + public class FullTest() + { + public async Task GetConfigurationAsync(Guid cashBoxId, string accessToken) + { + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = new Uri("https://helipad-sandbox.fiskaltrust.cloud"); + httpClient.DefaultRequestHeaders.Clear(); + httpClient.DefaultRequestHeaders.Add("cashboxid", cashBoxId.ToString()); + httpClient.DefaultRequestHeaders.Add("accesstoken", accessToken); + var result = await httpClient.GetAsync("api/configuration"); + var content = await result.Content.ReadAsStringAsync(); + if (result.IsSuccessStatusCode) + { + if (string.IsNullOrEmpty(content)) + { + throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + } + + var configuration = Newtonsoft.Json.JsonConvert.DeserializeObject(content) ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + configuration.TimeStamp = DateTime.UtcNow.Ticks; + return configuration; + } + else + { + throw new Exception($"{content}"); + } + } + } + + public async Task<(QueueGRBootstrapper bootstrapper, Guid cashBoxId)> InitializeQueueGRBootstrapperAsync() + { + var cashBoxId = Guid.Parse("e117e4b5-88ea-4511-a134-e5408f3cfd4c"); + var accessToken = "BBNu3xCxDz9VKOTQJQATmCzj1zQRjeE25DW/F8hcqsk/Uc5hHc4m1lEgd2QDsWLpa6MRDHz+vLlQs0hCprWt9XY="; + var configuration = await GetConfigurationAsync(cashBoxId, accessToken); + var queue = configuration.ftQueues?.First() ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + var bootstrapper = new QueueGRBootstrapper(queue.Id, new LoggerFactory(), queue.Configuration ?? new Dictionary()); + return (bootstrapper, cashBoxId); + } + + [Fact] + public async Task Example_RetailSales_Tests() + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + var signMethod = bootstrapper.RegisterForSign(); + var receiptRequest = ReceiptExamples.Example_RetailSales(cashBoxId); + var exampleCashSalesResponse = await signMethod(System.Text.Json.JsonSerializer.Serialize(receiptRequest)); + var result = await SendIssueAsync(receiptRequest, System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!); + } + + [Fact] + public async Task Example_RetailSales_Error2_Tests() + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + var signMethod = bootstrapper.RegisterForSign(); + var ticks = DateTime.UtcNow.Ticks; + var receiptRequest = ReceiptExamples.Example_RetailSales(cashBoxId); + var exampleCashSalesResponse = await signMethod(System.Text.Json.JsonSerializer.Serialize(receiptRequest)); + await StoreDataAsync("A11_1_offline_2", "A11_1_offline_2", ticks, bootstrapper, receiptRequest, System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!); + } + + [Fact] + public async Task Example_RetailSales_LateSigning_Tests() + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + var signMethod = bootstrapper.RegisterForSign(); + var ticks = DateTime.UtcNow.Ticks; + var receiptRequest = ReceiptExamples.Example_RetailSales(cashBoxId); + receiptRequest.ftReceiptCase = receiptRequest.ftReceiptCase | 0x0000_0000_0001_0000; + var exampleCashSalesResponse = await signMethod(JsonSerializer.Serialize(receiptRequest)); + await StoreDataAsync("A11_1_offline_1", "A11_1_offline_1", ticks, bootstrapper, receiptRequest, System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!); + } + + [Fact] + public async Task Example_SalesInvoice_1_1_Tests() + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + var signMethod = bootstrapper.RegisterForSign(); + var ticks = DateTime.UtcNow.Ticks; + var receiptRequest = ReceiptExamples.Example_SalesInvoice_1_1(cashBoxId); + var exampleCashSalesResponse = await signMethod(JsonSerializer.Serialize(receiptRequest)); + await StoreDataAsync("A1_1", "A1_1", ticks, bootstrapper, receiptRequest, System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!); + } + + [Fact] + public async Task Example_SalesInvoice_1_1_Tests_nowithholding() + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + var signMethod = bootstrapper.RegisterForSign(); + var ticks = DateTime.UtcNow.Ticks; + var receiptRequest = ReceiptExamples.Example_SalesInvoice_1_1(cashBoxId); + var exampleCashSalesResponse = await signMethod(JsonSerializer.Serialize(receiptRequest)); + await StoreDataAsync("A1_1", "A1_1", ticks, bootstrapper, receiptRequest, System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!); + } + + [Fact] + public async Task Example_POSReceipt_Tests() + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + var signMethod = bootstrapper.RegisterForSign(); + var ticks = DateTime.UtcNow.Ticks; + var receiptRequest = ReceiptExamples.ExamplePosReceipt(cashBoxId); + var exampleCashSalesResponse = await signMethod(JsonSerializer.Serialize(receiptRequest)); + await StoreDataAsync("A1_8_4", "A1_8_4", ticks, bootstrapper, receiptRequest, System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!); + } + + [Fact] + public async Task Example_POSReceipt_Testss_A11_1_Online_100() + { + var cashBoxId = Guid.Parse("e117e4b5-88ea-4511-a134-e5408f3cfd4c"); + var accessToken = "BBNu3xCxDz9VKOTQJQATmCzj1zQRjeE25DW/F8hcqsk/Uc5hHc4m1lEgd2QDsWLpa6MRDHz+vLlQs0hCprWt9XY="; + var configuration = await GetConfigurationAsync(cashBoxId, accessToken); + var queue = configuration.ftQueues?.First() ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + var bootstrapper = new QueueGRBootstrapper(queue.Id, new LoggerFactory(), queue.Configuration ?? new Dictionary()); + var signMethod = bootstrapper.RegisterForSign(); + + var ticks = DateTime.UtcNow.Ticks; + var receiptRequest = ReceiptExamples.Example_RetailSales_100(cashBoxId); + var raw = System.Text.Json.JsonSerializer.Serialize(receiptRequest); + var exampleCashSalesResponse = await signMethod(raw); + + await StoreDataAsync("A11_1_Online_100", "A11_1_Online_100", ticks, bootstrapper, receiptRequest, System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!); + } + + [Fact] + public async Task Example_POSReceipt_Testss_A11_1_Online_100_App2App() + { + var cashBoxId = Guid.Parse("e117e4b5-88ea-4511-a134-e5408f3cfd4c"); + var accessToken = "BBNu3xCxDz9VKOTQJQATmCzj1zQRjeE25DW/F8hcqsk/Uc5hHc4m1lEgd2QDsWLpa6MRDHz+vLlQs0hCprWt9XY="; + var configuration = await GetConfigurationAsync(cashBoxId, accessToken); + var queue = configuration.ftQueues?.First() ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + var bootstrapper = new QueueGRBootstrapper(queue.Id, new LoggerFactory(), queue.Configuration ?? new Dictionary()); + var signMethod = bootstrapper.RegisterForSign(); + + var ticks = DateTime.UtcNow.Ticks; + var receiptRequest = ReceiptExamples.Example_RetailSales_100App2APp(cashBoxId); + var raw = System.Text.Json.JsonSerializer.Serialize(receiptRequest); + var exampleCashSalesResponse = await signMethod(raw); + + await StoreDataAsync("A11_1_Online_100", "A11_1_Online_100", ticks, bootstrapper, receiptRequest, System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!); + } + + + public async Task StoreDataAsync(string folder, string casename, long ticks, QueueGRBootstrapper bootstrapper, ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) + { + var result = await SendIssueAsync(receiptRequest, receiptResponse); + + var pdfdata = await new HttpClient().GetAsync(result?.DocumentURL + "?format=pdf"); + + var journalMethod = bootstrapper.RegisterForJournal(); + var xmlData = await journalMethod(System.Text.Json.JsonSerializer.Serialize(new ifPOS.v1.JournalRequest + { + ftJournalType = 0x4752_2000_0000_0001, + From = ticks + })); + Directory.CreateDirectory("C:\\temp\\viva_examples\\" + folder); + File.WriteAllBytes($"C:\\temp\\viva_examples\\{folder}\\{casename}.receipt.pdf", await pdfdata.Content.ReadAsByteArrayAsync()); + File.WriteAllText($"C:\\temp\\viva_examples\\{folder}\\{casename}_aade.xml", xmlData); + } + + private async Task SendIssueAsync(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) + { + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Post, "https://possystem-api-sandbox.fiskaltrust.eu/v2/issue"); + request.Headers.Add("x-cashbox-id", "e117e4b5-88ea-4511-a134-e5408f3cfd4c"); + request.Headers.Add("x-cashbox-accesstoken", "BBNu3xCxDz9VKOTQJQATmCzj1zQRjeE25DW/F8hcqsk/Uc5hHc4m1lEgd2QDsWLpa6MRDHz+vLlQs0hCprWt9XY="); + var data = JsonSerializer.Serialize(new + { + ReceiptRequest = receiptRequest, + ReceiptResponse = receiptResponse + }); + request.Headers.Add("x-operation-id", Guid.NewGuid().ToString()); + var content = new StringContent(data, null, "application/json"); + request.Content = content; + var response = await client.SendAsync(request); + return await response.Content.ReadFromJsonAsync(); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/IssueResponse.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/IssueResponse.cs new file mode 100644 index 000000000..153db0712 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/IssueResponse.cs @@ -0,0 +1,11 @@ +namespace fiskaltrust.Middleware.Localization.QueueGR.UnitTest +{ + public class IssueResponse + { + public string? ftQueueID { get; set; } + public string? ftQueueItemID { get; set; } + public string? DocumentURL { get; set; } + public string? DocumentContentType { get; set; } + } + +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationExamples.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationExamples.cs new file mode 100644 index 000000000..d7520db02 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationExamples.cs @@ -0,0 +1,658 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.SAFT.CLI; + +public static class AADECertificationExamples +{ + public const string CUSOMTER_VATNUMBER = "026883248"; + //public const string CUSOMTER_VATNUMBER = "997671770"; + + public static ReceiptRequest A1_1_1p2() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 0, + VATAmount = 0, + ftChargeItemCase = 0x4752_2000_0000_6017, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4154_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = "ATU68541544", + CustomerCity = "Salzburg", + CustomerZip = "5020", + CustomerStreet = "Alpenstraße 99/2.OG/02", + CustomerName = "fiskaltrust consulting gmbh" + } + }; + } + + public static ReceiptRequest A1_1_1p3() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 0, + VATAmount = 0, + ftChargeItemCase = 0x4752_2000_0000_6017, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x0000_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = "GB300325371", + CustomerCountry = "GB", + CustomerCity = "Milton Keynes, Buckinghamshire", + CustomerZip = "MK9 2AH", + CustomerStreet = "Part Second Floor West Wing, Ashton House Silbury Boulevard, United Kingdom", + CustomerName = "VIVA WALLET.COM LTD" + } + }; + } + + public static ReceiptRequest A1_1_1p5_1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 200m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0093, + Quantity = 1, + Description = "Line item 1" + }, + new ChargeItem + { + Position = 2, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + + ], + cbPayItems = + [ + new PayItem + { + Amount = 200m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_1_1p5_2() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 220m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 120, + VATRate = 24, + VATAmount = decimal.Round(120 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0093, + Quantity = 1, + Description = "Line item 1" + }, + new ChargeItem + { + Position = 2, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 220m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_2_2p2() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 0, + VATAmount = 0, + ftChargeItemCase = 0x4752_2000_0000_6027, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4154_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = "ATU68541544", + CustomerCity = "Salzburg", + CustomerZip = "5020", + CustomerStreet = "Alpenstraße 99/2.OG/02", + CustomerName = "fiskaltrust consulting gmbh" + } + }; + } + + public static ReceiptRequest A1_2_2p3() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 0, + VATAmount = 0, + ftChargeItemCase = 0x4752_2000_0000_6027, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x0000_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = "GB300325371", + CustomerCountry = "GB", + CustomerCity = "Milton Keynes, Buckinghamshire", + CustomerZip = "MK9 2AH", + CustomerStreet = "Part Second Floor West Wing, Ashton House Silbury Boulevard, United Kingdom", + CustomerName = "VIVA WALLET.COM LTD" + } + }; + } + + public static ReceiptRequest A1_3_3p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 0, + VATAmount = 0, + ftChargeItemCase = 0x4752_2000_0000_0028, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0003, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerZip = "12345", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_3_3p2() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 0, + VATAmount = 0, + ftChargeItemCase = 0x4752_2000_0000_0028, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0003, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + CustomerZip = "12345" + }, + ftReceiptCaseData = "3.2" + }; + } + + public static ReceiptRequest A1_6_6p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_3003, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerZip = "12345", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_6_6p2() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0023, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_3003, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_7_7p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0023, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_3006, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + CustomerZip = "12345" + } + }; + } + + public static ReceiptRequest A1_8_8p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 4m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 0, + VATAmount = 0, + ftChargeItemCase = 0x4752_2000_0000_0018, + Quantity = 1, + Description = "Renting something" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100, + Quantity = 1, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_3005, // Rent not defined yet + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_8_8p2() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 4m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 4, + VATRate = 0, + VATAmount = 0, + ftChargeItemCase = 0x4752_2000_0000_1168, // Nature for the Climate Resilience Tax + Quantity = 1, + Description = "Τέλος ανθεκτικότητας κλιματικής κρίσης" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 4, + Quantity = 1, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerZip = "12345", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A2_11_11p3() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 99, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 99, + VATRate = 24, + VATAmount = decimal.Round(99 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 99, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_1001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0001 + }; + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationExamplesCard.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationExamplesCard.cs new file mode 100644 index 000000000..ba50f0fca --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationExamplesCard.cs @@ -0,0 +1,667 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.SAFT.CLI; + +public static class AADECertificationExamplesCard +{ + public const string CUSOMTER_VATNUMBER = "026883248"; + //public const string CUSOMTER_VATNUMBER = "997671770"; + + public static ReceiptRequest A1_1_1p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100m, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_1_1p4() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0063, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + CustomerZip = "12345" + } + }; + } + + public static ReceiptRequest A1_1_1p6() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbPreviousReceiptReference = "400001941223252", + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100m, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_2_2p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0023, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100m, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_2_2p4() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbPreviousReceiptReference = "400001941223255", // need to replace this with lookup + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0023, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100m, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_5_5p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbPreviousReceiptReference = "400001941221523", + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100m, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1004, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_5_5p2() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_6023, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1004, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_8_8p4() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 150m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 150, + VATRate = 0, + VATAmount = 0, + ftChargeItemCase = 0x4752_2000_0000_0018, + Quantity = 1, + Description = "Something" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 150, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_3004, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_8_8p5() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = -150, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = -150, + VATRate = 0, + VATAmount = 0, + ftChargeItemCase = 0x4752_2000_0002_0018, + Quantity = 1, + Description = "Something" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = -150, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0100_3004, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A2_11_11p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100m, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0001 + }; + } + + public static ReceiptRequest A2_11_11p2() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0023, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100m, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0001 + }; + } + + public static ReceiptRequest A2_11_11p4() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100m, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0005 + }; + } + + public static ReceiptRequest A2_11_1p5() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0063, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Position = 1, + Quantity = 1, + Amount = 100m, + Description = "Κάρτα", + ftPayItemCase = 0x4752_2000_0000_0004 + }, + new PayItem + { + Description = "Φιλοδώρημα", + ftPayItemCase = 0x4752_2000_0040_0004, + Amount = 0 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4555_2000_0000_0001 + }; + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationExamplesSelfPricing.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationExamplesSelfPricing.cs new file mode 100644 index 000000000..9873a59f1 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationExamplesSelfPricing.cs @@ -0,0 +1,437 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.SAFT.CLI; + +public static class AADECertificationExamplesSelfPricing +{ + public const string CUSOMTER_VATNUMBER = "026883248"; + //public const string CUSOMTER_VATNUMBER = "997671770"; + + public static ReceiptRequest A1_1_1p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2100_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_1_1p4() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0063, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2100_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + CustomerZip = "12345" + } + }; + } + + public static ReceiptRequest A1_1_1p5_1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 200m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0093, + Quantity = 1, + Description = "Line item 1" + }, + new ChargeItem + { + Position = 2, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + + ], + cbPayItems = + [ + new PayItem + { + Amount = 200m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2100_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_1_1p5_2() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 220m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 120, + VATRate = 24, + VATAmount = decimal.Round(120 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0093, + Quantity = 1, + Description = "Line item 1" + }, + new ChargeItem + { + Position = 2, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 220m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2100_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_1_1p6() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbPreviousReceiptReference = "400001941223252", + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2100_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_2_2p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0023, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2100_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_2_2p4() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbPreviousReceiptReference = "400001941223255", // need to replace this with lookup + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0023, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2100_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_5_5p1() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbPreviousReceiptReference = "400001941221523", + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Description = "Μετρητά", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2100_0000_1004, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } + + public static ReceiptRequest A1_5_5p2() + { + return new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + Amount = 100, + VATRate = 24, + VATAmount = decimal.Round(100 / (100M + 24) * 24, 2, MidpointRounding.ToEven), + ftChargeItemCase = 0x4752_2000_0000_6023, + Quantity = 1, + Description = "Line item 1" + } + ], + cbPayItems = + [ + new PayItem + { + Amount = 100m, + Quantity = 1, + Description = "Πίστωση", + ftPayItemCase = 0x4752_2000_0000_0001 + } + ], + + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2100_0000_1004, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = CUSOMTER_VATNUMBER, + CustomerName = "Πελάτης A.E.", + CustomerStreet = "Κηφισίας 12, 12345, Αθήνα", + CustomerCity = "Αθηνών", + CustomerCountry = "GR", + } + }; + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationTests.cs new file mode 100644 index 000000000..7276704ef --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationTests.cs @@ -0,0 +1,355 @@ +using System.Net.Http.Json; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; +using System.Xml.Serialization; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE; +using fiskaltrust.Middleware.Localization.v2.Configuration; +using FluentAssertions; +using FluentAssertions.Execution; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +namespace fiskaltrust.Middleware.Localization.QueueGR.UnitTest +{ + public static class Constants + { + public const string CASHBOX_CERTIFICATION_ID = ""; + public const string CASHBOX_CERTIFICATION_ACCESSTOKEN = ""; + } + public class AADECertificationTests + { + private readonly ITestOutputHelper _output; + private readonly AADEFactory _aadeFactory; + + public async Task GetConfigurationAsync(Guid cashBoxId, string accessToken) + { + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = new Uri("https://helipad-sandbox.fiskaltrust.cloud"); + httpClient.DefaultRequestHeaders.Clear(); + httpClient.DefaultRequestHeaders.Add("cashboxid", cashBoxId.ToString()); + httpClient.DefaultRequestHeaders.Add("accesstoken", accessToken); + var result = await httpClient.GetAsync("api/configuration"); + var content = await result.Content.ReadAsStringAsync(); + if (result.IsSuccessStatusCode) + { + if (string.IsNullOrEmpty(content)) + { + throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + } + + var configuration = Newtonsoft.Json.JsonConvert.DeserializeObject(content) ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + configuration.TimeStamp = DateTime.UtcNow.Ticks; + return configuration; + } + else + { + throw new Exception($"{content}"); + } + } + } + + public async Task<(QueueGRBootstrapper bootstrapper, Guid cashBoxId)> InitializeQueueGRBootstrapperAsync() + { + var cashBoxId = Guid.Parse(Constants.CASHBOX_CERTIFICATION_ID); + var accessToken = Constants.CASHBOX_CERTIFICATION_ACCESSTOKEN; + var configuration = await GetConfigurationAsync(cashBoxId, accessToken); + var queue = configuration.ftQueues?.First() ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + var bootstrapper = new QueueGRBootstrapper(queue.Id, new LoggerFactory(), queue.Configuration ?? new Dictionary()); + return (bootstrapper, cashBoxId); + } + + public AADECertificationTests(ITestOutputHelper output) + { + _output = output; + _aadeFactory = new AADEFactory(new storage.V0.MasterData.MasterDataConfiguration + { + Account = new storage.V0.MasterData.AccountMasterData + { + VatId = "112545020" + } + }); + } + + public ResponseDoc? GetResponse(string xmlContent) + { + var xmlSerializer = new XmlSerializer(typeof(ResponseDoc)); + using var stringReader = new StringReader(xmlContent); + return xmlSerializer.Deserialize(stringReader) as ResponseDoc; + } + + private async Task SendToMayData(string xml) + { + var httpClient = new HttpClient() + { + BaseAddress = new Uri("https://mydataapidev.aade.gr/") + }; + httpClient.DefaultRequestHeaders.Add("aade-user-id", "user11111111"); + httpClient.DefaultRequestHeaders.Add("ocp-apim-subscription-key", "41291863a36d552c4d7fc8195d427dd3"); + + var response = await httpClient.PostAsync("/myDataProvider/SendInvoices", new StringContent(xml, Encoding.UTF8, "application/xml")); + var content = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + { + throw new Exception("Failed to send data to myData API: " + content); + } + + var ersult = GetResponse(content); + var marker = ""; + if (ersult != null) + { + var data = ersult.response[0]; + if (data.statusCode.ToLower() == "success") + { + for (var i = 0; i < data.ItemsElementName.Length; i++) + { + if (data.ItemsElementName[i] == ItemsChoiceType.qrUrl) + { + + } + else if (data.ItemsElementName[i] == ItemsChoiceType.invoiceMark) + { + marker = data.Items[i].ToString(); + + } + } + _output.WriteLine(content); + } + else + { + _output.WriteLine(xml); + + _output.WriteLine(content); + throw new Exception("Error" + content); + } + } + else + { + _output.WriteLine(xml); + + _output.WriteLine(content); + throw new Exception("Invalid response" + content); + } + return marker; + } + + private async Task ValidateMyData(ReceiptRequest receiptRequest, InvoiceType expectedInvoiceType, [CallerMemberName] string caller = "") + { + using var scope = new AssertionScope(); + var invoiceDoc = _aadeFactory.MapToInvoicesDoc(receiptRequest, ExampleResponse); + invoiceDoc.invoice[0].invoiceHeader.invoiceType.Should().Be(expectedInvoiceType); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification.Should().BeEmpty(); + var xml = _aadeFactory.GenerateInvoicePayload(invoiceDoc); + await SendToMayData(xml); + System.Console.WriteLine(caller); + await ExecuteMiddleware(receiptRequest, caller); + } + + private async Task ValidateMyData(ReceiptRequest receiptRequest, InvoiceType expectedInvoiceType, IncomeClassificationCategoryType expectedCategory, [CallerMemberName] string caller = "") + { + using var scope = new AssertionScope(); + var invoiceDoc = _aadeFactory.MapToInvoicesDoc(receiptRequest, ExampleResponse); + invoiceDoc.invoice[0].invoiceHeader.invoiceType.Should().Be(expectedInvoiceType); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationCategory.Should().Be(expectedCategory); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationTypeSpecified.Should().BeFalse(); + var xml = _aadeFactory.GenerateInvoicePayload(invoiceDoc); + await SendToMayData(xml); + + System.Console.WriteLine(caller); + //await ExecuteMiddleware(receiptRequest, caller); + } + + private async Task ValidateMyData(ReceiptRequest receiptRequest, InvoiceType expectedInvoiceType, IncomeClassificationCategoryType expectedCategory, IncomeClassificationValueType expectedValueType, [CallerMemberName] string caller = "") + { + using var scope = new AssertionScope(); + var invoiceDoc = _aadeFactory.MapToInvoicesDoc(receiptRequest, ExampleResponse); + invoiceDoc.invoice[0].invoiceHeader.invoiceType.Should().Be(expectedInvoiceType); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationCategory.Should().Be(expectedCategory); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationType.Should().Be(expectedValueType); + var xml = _aadeFactory.GenerateInvoicePayload(invoiceDoc); + await SendToMayData(xml); + System.Console.WriteLine(caller); + await ExecuteMiddleware(receiptRequest, caller); + } + +#pragma warning disable + private async Task ExecuteMiddleware(ReceiptRequest receiptRequest, string caller) + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + receiptRequest.ftCashBoxID = cashBoxId; + var signMethod = bootstrapper.RegisterForSign(); + var ticks = DateTime.UtcNow.Ticks; + var exampleCashSalesResponse = await signMethod(JsonSerializer.Serialize(receiptRequest)); + await StoreDataAsync(caller, caller, ticks, bootstrapper, receiptRequest, System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!); + } + + private async Task SendIssueAsync(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) + { + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Post, "https://possystem-api-sandbox.fiskaltrust.eu/v2/issue"); + request.Headers.Add("x-cashbox-id", "e117e4b5-88ea-4511-a134-e5408f3cfd4c"); + request.Headers.Add("x-cashbox-accesstoken", "BBNu3xCxDz9VKOTQJQATmCzj1zQRjeE25DW/F8hcqsk/Uc5hHc4m1lEgd2QDsWLpa6MRDHz+vLlQs0hCprWt9XY="); + var data = JsonSerializer.Serialize(new + { + ReceiptRequest = receiptRequest, + ReceiptResponse = receiptResponse + }); + request.Headers.Add("x-operation-id", Guid.NewGuid().ToString()); + var content = new StringContent(data, null, "application/json"); + request.Content = content; + var response = await client.SendAsync(request); + return await response.Content.ReadFromJsonAsync(); + } + + public async Task StoreDataAsync(string folder, string casename, long ticks, QueueGRBootstrapper bootstrapper, ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) + { + var result = await SendIssueAsync(receiptRequest, receiptResponse); + + var pdfdata = await new HttpClient().GetAsync(result?.DocumentURL + "?format=pdf"); + var pngdata = await new HttpClient().GetAsync(result?.DocumentURL + "?format=png"); + + var journalMethod = bootstrapper.RegisterForJournal(); + var xmlData = await journalMethod(System.Text.Json.JsonSerializer.Serialize(new ifPOS.v1.JournalRequest + { + ftJournalType = 0x4752_2000_0000_0001, + From = ticks + })); + Directory.CreateDirectory("C:\\temp\\viva_aade_certification_examples\\" + folder); + File.WriteAllText($"C:\\temp\\viva_aade_certification_examples\\{folder}\\{casename}.receiptrequest.json", JsonSerializer.Serialize(receiptRequest, new JsonSerializerOptions + { + WriteIndented = true + })); + File.WriteAllText($"C:\\temp\\viva_aade_certification_examples\\{folder}\\{casename}.receiptresponse.json", JsonSerializer.Serialize(receiptResponse, new JsonSerializerOptions + { + WriteIndented = true + })); + File.WriteAllBytes($"C:\\temp\\viva_aade_certification_examples\\{folder}\\{casename}.receipt.pdf", await pdfdata.Content.ReadAsByteArrayAsync()); + File.WriteAllBytes($"C:\\temp\\viva_aade_certification_examples\\{folder}\\{casename}.receipt.png", await pngdata.Content.ReadAsByteArrayAsync()); + File.WriteAllText($"C:\\temp\\viva_aade_certification_examples\\{folder}\\{casename}_aade.xml", xmlData); + } + + [Fact] + public async void JOurnal() + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + var journalMethod = bootstrapper.RegisterForJournal(); + var xmlData = await journalMethod(System.Text.Json.JsonSerializer.Serialize(new ifPOS.v1.JournalRequest + { + ftJournalType = 0x4752_2000_0000_0001, + From = 0 + })); + } + + [Fact] + public async Task AADECertificationExamples_A1_1_1p2() + { + var receiptRequest = AADECertificationExamples.A1_1_1p2(); + await ValidateMyData(receiptRequest, InvoiceType.Item12, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_005); + } + + [Fact] + public async Task AADECertificationExamples_A1_1_1p3() + { + var receiptRequest = AADECertificationExamples.A1_1_1p3(); + await ValidateMyData(receiptRequest, InvoiceType.Item13, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_006); + } + + [Fact] + public async Task AADECertificationExamples_A1_1_1p5() + { + var receiptRequest = AADECertificationExamples.A1_1_1p5_1(); + await ValidateMyData(receiptRequest, InvoiceType.Item15, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_1_1p5_2() + { + var receiptRequest = AADECertificationExamples.A1_1_1p5_2(); + await ValidateMyData(receiptRequest, InvoiceType.Item15, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_2_2p2() + { + var receiptRequest = AADECertificationExamples.A1_2_2p2(); + await ValidateMyData(receiptRequest, InvoiceType.Item22, IncomeClassificationCategoryType.category1_3, IncomeClassificationValueType.E3_561_005); + } + + [Fact] + public async Task AADECertificationExamples_A1_2_2p3() + { + var receiptRequest = AADECertificationExamples.A1_2_2p3(); + await ValidateMyData(receiptRequest, InvoiceType.Item23, IncomeClassificationCategoryType.category1_3, IncomeClassificationValueType.E3_561_006); + } + + [Fact] + public async Task AADECertificationExamples_A1_3_3p1() + { + var receiptRequest = AADECertificationExamples.A1_3_3p1(); + await ValidateMyData(receiptRequest, InvoiceType.Item31); + } + + [Fact] + public async Task AADECertificationExamples_A1_3_3p2() + { + var receiptRequest = AADECertificationExamples.A1_3_3p2(); + await ValidateMyData(receiptRequest, InvoiceType.Item32); + } + + [Fact] + public async Task AADECertificationExamples_A1_6_6p1() + { + var receiptRequest = AADECertificationExamples.A1_6_6p1(); + await ValidateMyData(receiptRequest, InvoiceType.Item61, IncomeClassificationCategoryType.category1_6, IncomeClassificationValueType.E3_595); + } + + [Fact] + public async Task AADECertificationExamples_A1_6_6p2() + { + var receiptRequest = AADECertificationExamples.A1_6_6p2(); + await ValidateMyData(receiptRequest, InvoiceType.Item62, IncomeClassificationCategoryType.category1_6, IncomeClassificationValueType.E3_595); + } + + [Fact] + public async Task AADECertificationExamples_A1_7_7p1() + { + var receiptRequest = AADECertificationExamples.A1_7_7p1(); + await ValidateMyData(receiptRequest, InvoiceType.Item71, IncomeClassificationCategoryType.category1_3, IncomeClassificationValueType.E3_561_007); + } + + [Fact] + public async Task AADECertificationExamples_A1_8_8p1() + { + var receiptRequest = AADECertificationExamples.A1_8_8p1(); + await ValidateMyData(receiptRequest, InvoiceType.Item81, IncomeClassificationCategoryType.category1_5, IncomeClassificationValueType.E3_562); + } + + [Fact] + public async Task AADECertificationExamples_A1_8_8p2() + { + var receiptRequest = AADECertificationExamples.A1_8_8p2(); + await ValidateMyData(receiptRequest, InvoiceType.Item82); + } + + [Fact] + public async Task AADECertificationExamples_A2_11_11p3() + { + var receiptRequest = AADECertificationExamples.A2_11_11p3(); + await ValidateMyData(receiptRequest, InvoiceType.Item113, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_003); + } + + public ReceiptResponse ExampleResponse => new ReceiptResponse + { + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftCashBoxIdentification = "cashBoxIdentification", + ftReceiptIdentification = "ft" + DateTime.UtcNow.Ticks.ToString("X"), + ftReceiptMoment = DateTime.UtcNow, + ftState = 0x4752_2000_0000_0000 + }; + } +} \ No newline at end of file diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationTestsCard.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationTestsCard.cs new file mode 100644 index 000000000..3a61b6d9a --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationTestsCard.cs @@ -0,0 +1,462 @@ +using System.Linq.Expressions; +using System.Net.Http.Json; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Xml.Serialization; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE; +using fiskaltrust.Middleware.Localization.v2.Configuration; +using FluentAssertions; +using FluentAssertions.Execution; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +namespace fiskaltrust.Middleware.Localization.QueueGR.UnitTest +{ + public class PayResponse + { + [JsonPropertyName("Protocol")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required string Protocol { get; set; } + + [JsonPropertyName("ftQueueID")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public Guid ftQueueId { get; set; } + + [JsonPropertyName("ftPayItems")] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] + [DataMember(EmitDefaultValue = true, IsRequired = true)] + public required List ftPayItems { get; set; } + } + + + public class AADECertificationTestsCard + { + private readonly ITestOutputHelper _output; + private readonly AADEFactory _aadeFactory; + + public async Task GetConfigurationAsync(Guid cashBoxId, string accessToken) + { + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = new Uri("https://helipad-sandbox.fiskaltrust.cloud"); + httpClient.DefaultRequestHeaders.Clear(); + httpClient.DefaultRequestHeaders.Add("cashboxid", cashBoxId.ToString()); + httpClient.DefaultRequestHeaders.Add("accesstoken", accessToken); + var result = await httpClient.GetAsync("api/configuration"); + var content = await result.Content.ReadAsStringAsync(); + if (result.IsSuccessStatusCode) + { + if (string.IsNullOrEmpty(content)) + { + throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + } + + var configuration = Newtonsoft.Json.JsonConvert.DeserializeObject(content) ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + configuration.TimeStamp = DateTime.UtcNow.Ticks; + return configuration; + } + else + { + throw new Exception($"{content}"); + } + } + } + + public async Task<(QueueGRBootstrapper bootstrapper, Guid cashBoxId)> InitializeQueueGRBootstrapperAsync() + { + var cashBoxId = Guid.Parse(Constants.CASHBOX_CERTIFICATION_ID); + var accessToken = Constants.CASHBOX_CERTIFICATION_ACCESSTOKEN; + var configuration = await GetConfigurationAsync(cashBoxId, accessToken); + var queue = configuration.ftQueues?.First() ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + var bootstrapper = new QueueGRBootstrapper(queue.Id, new LoggerFactory(), queue.Configuration ?? new Dictionary()); + return (bootstrapper, cashBoxId); + } + + public AADECertificationTestsCard(ITestOutputHelper output) + { + _output = output; + _aadeFactory = new AADEFactory(new storage.V0.MasterData.MasterDataConfiguration + { + Account = new storage.V0.MasterData.AccountMasterData + { + VatId = "112545020" + } + }); + } + + public ResponseDoc? GetResponse(string xmlContent) + { + var xmlSerializer = new XmlSerializer(typeof(ResponseDoc)); + using var stringReader = new StringReader(xmlContent); + return xmlSerializer.Deserialize(stringReader) as ResponseDoc; + } + + private async Task SendToMayData(string xml) + { + var httpClient = new HttpClient() + { + BaseAddress = new Uri("https://mydataapidev.aade.gr/") + }; + httpClient.DefaultRequestHeaders.Add("aade-user-id", "user11111111"); + httpClient.DefaultRequestHeaders.Add("ocp-apim-subscription-key", "41291863a36d552c4d7fc8195d427dd3"); + + var response = await httpClient.PostAsync("/myDataProvider/SendInvoices", new StringContent(xml, Encoding.UTF8, "application/xml")); + var content = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + { + throw new Exception("Failed to send data to myData API: " + content); + } + + var ersult = GetResponse(content); + var marker = ""; + if (ersult != null) + { + var data = ersult.response[0]; + if (data.statusCode.ToLower() == "success") + { + for (var i = 0; i < data.ItemsElementName.Length; i++) + { + if (data.ItemsElementName[i] == ItemsChoiceType.qrUrl) + { + + } + else if (data.ItemsElementName[i] == ItemsChoiceType.invoiceMark) + { + marker = data.Items[i].ToString(); + + } + } + _output.WriteLine(content); + } + else + { + _output.WriteLine(xml); + + _output.WriteLine(content); + throw new Exception("Error" + content); + } + } + else + { + _output.WriteLine(xml); + + _output.WriteLine(content); + throw new Exception("Invalid response" + content); + } + return marker; + } + + private async Task ValidateMyData(ReceiptRequest receiptRequest, InvoiceType expectedInvoiceType, IncomeClassificationCategoryType expectedCategory, [CallerMemberName] string caller = "") + { + using var scope = new AssertionScope(); + var invoiceDoc = _aadeFactory.MapToInvoicesDoc(receiptRequest, ExampleResponse); + invoiceDoc.invoice[0].invoiceHeader.invoiceType.Should().Be(expectedInvoiceType); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationCategory.Should().Be(expectedCategory); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationTypeSpecified.Should().BeFalse(); + var xml = _aadeFactory.GenerateInvoicePayload(invoiceDoc); + await SendToMayData(xml); + await ExecuteMiddleware(receiptRequest, caller); + } + + private async Task ValidateMyData(ReceiptRequest receiptRequest, InvoiceType expectedInvoiceType, IncomeClassificationCategoryType expectedCategory, IncomeClassificationValueType expectedValueType, [CallerMemberName] string caller = "") + { + var payment = await SendPayRequest(receiptRequest.cbPayItems[0]); + receiptRequest.cbPayItems[0] = payment!.ftPayItems[0]; + using var scope = new AssertionScope(); + var invoiceDoc = _aadeFactory.MapToInvoicesDoc(receiptRequest, ExampleResponse); + invoiceDoc.invoice[0].invoiceHeader.invoiceType.Should().Be(expectedInvoiceType); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationCategory.Should().Be(expectedCategory); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationType.Should().Be(expectedValueType); + var xml = _aadeFactory.GenerateInvoicePayload(invoiceDoc); + await SendToMayData(xml); + await ExecuteMiddleware(receiptRequest, caller); + } + +#pragma warning disable + private async Task ExecuteMiddleware(ReceiptRequest receiptRequest, string caller) + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + receiptRequest.ftCashBoxID = cashBoxId; + var signMethod = bootstrapper.RegisterForSign(); + var ticks = DateTime.UtcNow.Ticks; + var exampleCashSalesResponse = await signMethod(JsonSerializer.Serialize(receiptRequest)); + await StoreDataAsync(caller, caller, ticks, bootstrapper, receiptRequest, System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!); + } + + private async Task SendIssueAsync(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) + { + + receiptRequest.cbReceiptAmount = Math.Abs(receiptRequest.cbReceiptAmount ?? 0.0m); + foreach(var chargeItem in receiptRequest.cbChargeItems) + { + chargeItem.Amount = Math.Abs(chargeItem.Amount); + } + foreach(var payItem in receiptRequest.cbPayItems) + { + payItem.Amount = Math.Abs(payItem.Amount); + } + + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Post, "https://possystem-api-sandbox.fiskaltrust.eu/v2/issue"); + var cashBoxId = Guid.Parse(Constants.CASHBOX_CERTIFICATION_ID); + var accessToken = Constants.CASHBOX_CERTIFICATION_ACCESSTOKEN; + request.Headers.Add("x-cashbox-id", cashBoxId.ToString()); + request.Headers.Add("x-cashbox-accesstoken", accessToken); + var data = JsonSerializer.Serialize(new + { + ReceiptRequest = receiptRequest, + ReceiptResponse = receiptResponse + }); + request.Headers.Add("x-operation-id", Guid.NewGuid().ToString()); + var content = new StringContent(data, null, "application/json"); + request.Content = content; + var response = await client.SendAsync(request); + return await response.Content.ReadFromJsonAsync(); + } + + + private async Task SendRefundRequest(string operationId, PayItem payItem) + { + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Post, "https://possystem-api-sandbox.fiskaltrust.eu/v2/pay"); + var cashBoxId = Guid.Parse(Constants.CASHBOX_CERTIFICATION_ID); + var accessToken = Constants.CASHBOX_CERTIFICATION_ACCESSTOKEN; + request.Headers.Add("x-cashbox-id", cashBoxId.ToString()); + request.Headers.Add("x-cashbox-accesstoken", accessToken); + request.Headers.Add("x-operation-id", Guid.NewGuid().ToString()); + var content = new StringContent("{\r\n " + + "\"Action\": \"refund\"," + + "\"Protocol\": \"viva_eft_pos\"," + + "\"cbPayItem\": {" + + $"\"{nameof(PayItem.MoneyBarcode)}\": \"{operationId}\",\r\n " + + $"\"Position\": {payItem.Position},\r\n " + + $"\"Quantity\": {payItem.Quantity},\r\n " + + $"\"Description\": \"{payItem.Description}\",\r\n " + + $"\"Amount\": {Math.Abs(payItem.Amount)},\r\n " + + $"\"ftPayItemCase\": {payItem.ftPayItemCase}\r\n " + + "},\r\n \"cbTerminalId\": \"16009303\"\r\n}", null, "application/json"); + request.Content = content; + var response = await client.SendAsync(request); + if(!response.IsSuccessStatusCode) + { + throw new Exception(await response.Content.ReadAsStringAsync()); + } + return await response.Content.ReadFromJsonAsync(); + } + + private async Task<(PayResponse?, string sessionid)> SendPayRequestGetOperationId(PayItem payItem) + { + var operationId = Guid.NewGuid().ToString(); + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Post, "https://possystem-api-sandbox.fiskaltrust.eu/v2/pay"); + var cashBoxId = Guid.Parse(Constants.CASHBOX_CERTIFICATION_ID); + var accessToken = Constants.CASHBOX_CERTIFICATION_ACCESSTOKEN; + request.Headers.Add("x-cashbox-id", cashBoxId.ToString()); + request.Headers.Add("x-cashbox-accesstoken", accessToken); + request.Headers.Add("x-operation-id", operationId); + var content = new StringContent("{\r\n " + + "\"Action\": \"payment\"," + + "\"Protocol\": \"viva_eft_pos\"," + + "\"cbPayItem\": {" + + $"\"Position\": {payItem.Position},\r\n " + + $"\"Quantity\": {payItem.Quantity},\r\n " + + $"\"Description\": \"{payItem.Description}\",\r\n " + + $"\"Amount\": {Math.Abs(payItem.Amount)},\r\n " + + $"\"ftPayItemCase\": {payItem.ftPayItemCase}\r\n " + + "},\r\n \"cbTerminalId\": \"16009303\"\r\n}", null, "application/json"); + request.Content = content; + var response = await client.SendAsync(request); + return (await response.Content.ReadFromJsonAsync(), operationId); + } + + + private async Task SendPayRequest(PayItem payItem) + { + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Post, "https://possystem-api-sandbox.fiskaltrust.eu/v2/pay"); + var cashBoxId = Guid.Parse(Constants.CASHBOX_CERTIFICATION_ID); + var accessToken = Constants.CASHBOX_CERTIFICATION_ACCESSTOKEN; + request.Headers.Add("x-cashbox-id", cashBoxId.ToString()); + request.Headers.Add("x-cashbox-accesstoken", accessToken); + request.Headers.Add("x-operation-id", Guid.NewGuid().ToString()); + var content = new StringContent("{\r\n " + + "\"Action\": \"payment\"," + + "\"Protocol\": \"viva_eft_pos_instore\"," + + "\"cbPayItem\": {" + + $"\"Position\": {payItem.Position},\r\n " + + $"\"Quantity\": {payItem.Quantity},\r\n " + + $"\"Description\": \"{payItem.Description}\",\r\n " + + $"\"Amount\": {Math.Abs(payItem.Amount)},\r\n " + + $"\"ftPayItemCase\": {payItem.ftPayItemCase}\r\n " + + "},\r\n \"cbTerminalId\": \"16009303\"\r\n}", null, "application/json"); + request.Content = content; + var response = await client.SendAsync(request); + if (!response.IsSuccessStatusCode) + { + throw new Exception(await response.Content.ReadAsStringAsync()); + } + return await response.Content.ReadFromJsonAsync(); + } + + + public async Task StoreDataAsync(string folder, string casename, long ticks, QueueGRBootstrapper bootstrapper, ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) + { + var result = await SendIssueAsync(receiptRequest, receiptResponse); + var pdfdata = await new HttpClient().GetAsync(result?.DocumentURL + "?format=pdf"); + var pngdata = await new HttpClient().GetAsync(result?.DocumentURL + "?format=png"); + + var journalMethod = bootstrapper.RegisterForJournal(); + var xmlData = await journalMethod(System.Text.Json.JsonSerializer.Serialize(new ifPOS.v1.JournalRequest + { + ftJournalType = 0x4752_2000_0000_0001, + From = ticks + })); + var baseFolder = Path.Combine("C:\\temp", "viva_aade_certification_examples_card"); + var folderPath = Path.Combine(baseFolder, folder); + Directory.CreateDirectory(Path.Combine(baseFolder, folder)); + File.WriteAllText(Path.Combine(folderPath, casename + ".receiptrequest.json"), JsonSerializer.Serialize(receiptRequest, new JsonSerializerOptions + { + WriteIndented = true + })); + File.WriteAllText(Path.Combine(folderPath, casename + ".receiptresponse.json"), JsonSerializer.Serialize(receiptResponse, new JsonSerializerOptions + { + WriteIndented = true + })); + File.WriteAllBytes(Path.Combine(folderPath, casename + ".receipt.pdf"), await pdfdata.Content.ReadAsByteArrayAsync()); + File.WriteAllBytes(Path.Combine(folderPath, casename + ".receipt.png"), await pngdata.Content.ReadAsByteArrayAsync()); + File.WriteAllText(Path.Combine(folderPath, casename + "_aade.xml"), xmlData); + } + + [Fact] + public async void AADECertificationExamples_A1_1_1p1() + { + var receiptRequest = AADECertificationExamplesCard.A1_1_1p1(); + await ValidateMyData(receiptRequest, InvoiceType.Item11, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_1_1p4() + { + var receiptRequest = AADECertificationExamplesCard.A1_1_1p4(); + await ValidateMyData(receiptRequest, InvoiceType.Item14, IncomeClassificationCategoryType.category1_7, IncomeClassificationValueType.E3_881_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_1_1p6() + { + var receiptRequest = AADECertificationExamplesCard.A1_1_1p6(); + await ValidateMyData(receiptRequest, InvoiceType.Item16, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async void AADECertificationExamples_A1_2_2p1() + { + var receiptRequest = AADECertificationExamplesCard.A1_2_2p1(); + await ValidateMyData(receiptRequest, InvoiceType.Item21, IncomeClassificationCategoryType.category1_3, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_2_2p4() + { + var receiptRequest = AADECertificationExamplesCard.A1_2_2p4(); + await ValidateMyData(receiptRequest, InvoiceType.Item24, IncomeClassificationCategoryType.category1_3, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_5_5p1() + { + + //var invoiceOriginal = _aadeFactory.MapToInvoicesDoc(AADECertificationExamplesCard.A1_1_1p1(), ExampleResponse); + //var marker = await SendToMayData(_aadeFactory.GenerateInvoicePayload(invoiceOriginal)); + + var creditnote = AADECertificationExamplesCard.A1_5_5p1(); + creditnote.cbPreviousReceiptReference = "400001941996088"; + await Task.Delay(1000); + //var invoiceDoc = _aadeFactory.MapToInvoicesDoc(creditnote, ExampleResponse); + //using var assertionScope = new AssertionScope(); + //invoiceDoc.invoice[0].invoiceHeader.invoiceType.Should().Be(InvoiceType.Item51); + //invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationCategory.Should().Be(IncomeClassificationCategoryType.category1_2); + //invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationType.Should().Be(IncomeClassificationValueType.E3_561_001); + //var xml = _aadeFactory.GenerateInvoicePayload(invoiceDoc); + //await SendToMayData(xml); + var payment = await SendPayRequest(creditnote.cbPayItems[0]); + creditnote.cbPayItems[0] = payment!.ftPayItems[0]; + await ExecuteMiddleware(creditnote, "AADECertificationExamples_A1_5_5p1"); + } + + [Fact] + public async Task AADECertificationExamples_A1_5_5p2() + { + var receiptRequest = AADECertificationExamplesCard.A1_5_5p2(); + await ValidateMyData(receiptRequest, InvoiceType.Item52, IncomeClassificationCategoryType.category1_3, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_8_8p4() + { + var receiptRequest = AADECertificationExamplesCard.A1_8_8p4(); + var payment = await SendPayRequest(receiptRequest.cbPayItems[0]); + receiptRequest.cbPayItems[0] = payment!.ftPayItems[0]; + await ValidateMyData(receiptRequest, InvoiceType.Item84, IncomeClassificationCategoryType.category1_95); + } + + [Fact] + public async Task AADECertificationExamples_A1_8_8p5() + { + var receiptRequest = AADECertificationExamplesCard.A1_8_8p5(); + var payment = await SendPayRequestGetOperationId(receiptRequest.cbPayItems[0]); + + var refund = await SendRefundRequest(payment.sessionid, receiptRequest.cbPayItems[0]); + receiptRequest.cbPayItems[0] = refund!.ftPayItems[0]; + receiptRequest.cbPayItems[0].Amount = -receiptRequest.cbPayItems[0].Amount; + await ValidateMyData(receiptRequest, InvoiceType.Item85, IncomeClassificationCategoryType.category1_95); + } + + [Fact] + public async Task AADECertificationExamples_A2_11_11p1() + { + var receiptRequest = AADECertificationExamplesCard.A2_11_11p1(); + await ValidateMyData(receiptRequest, InvoiceType.Item111, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_003); + } + + [Fact] + public async Task AADECertificationExamples_A2_11_11p2() + { + var receiptRequest = AADECertificationExamplesCard.A2_11_11p2(); + await ValidateMyData(receiptRequest, InvoiceType.Item112, IncomeClassificationCategoryType.category1_3, IncomeClassificationValueType.E3_561_003); + } + + + [Fact] + public async Task AADECertificationExamples_A2_11_11p4() + { + var receiptRequest = AADECertificationExamplesCard.A2_11_11p4(); + await ValidateMyData(receiptRequest, InvoiceType.Item114, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A2_11_11p5() + { + var receiptRequest = AADECertificationExamplesCard.A2_11_1p5(); + await ValidateMyData(receiptRequest, InvoiceType.Item115, IncomeClassificationCategoryType.category1_7, IncomeClassificationValueType.E3_881_003); + } + + public ReceiptResponse ExampleResponse => new ReceiptResponse + { + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftCashBoxIdentification = "cashBoxIdentification", + ftReceiptIdentification = "ft" + DateTime.UtcNow.Ticks.ToString("X"), + ftReceiptMoment = DateTime.UtcNow, + ftState = 0x4752_2000_0000_0000 + }; + } +} \ No newline at end of file diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationTestsSelfPricing.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationTestsSelfPricing.cs new file mode 100644 index 000000000..b6de80743 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADECertificationTestsSelfPricing.cs @@ -0,0 +1,338 @@ +using System.Net.Http.Json; +using System.Runtime.CompilerServices; +using System.Text; +using System.Text.Json; +using System.Xml.Serialization; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE; +using fiskaltrust.Middleware.Localization.v2.Configuration; +using FluentAssertions; +using FluentAssertions.Execution; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +namespace fiskaltrust.Middleware.Localization.QueueGR.UnitTest +{ + + public class AADECertificationTestsSelfPricing + { + private readonly ITestOutputHelper _output; + private readonly AADEFactory _aadeFactory; + + public async Task GetConfigurationAsync(Guid cashBoxId, string accessToken) + { + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = new Uri("https://helipad-sandbox.fiskaltrust.cloud"); + httpClient.DefaultRequestHeaders.Clear(); + httpClient.DefaultRequestHeaders.Add("cashboxid", cashBoxId.ToString()); + httpClient.DefaultRequestHeaders.Add("accesstoken", accessToken); + var result = await httpClient.GetAsync("api/configuration"); + var content = await result.Content.ReadAsStringAsync(); + if (result.IsSuccessStatusCode) + { + if (string.IsNullOrEmpty(content)) + { + throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + } + + var configuration = Newtonsoft.Json.JsonConvert.DeserializeObject(content) ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + configuration.TimeStamp = DateTime.UtcNow.Ticks; + return configuration; + } + else + { + throw new Exception($"{content}"); + } + } + } + + public async Task<(QueueGRBootstrapper bootstrapper, Guid cashBoxId)> InitializeQueueGRBootstrapperAsync() + { + var cashBoxId = Guid.Parse(Constants.CASHBOX_CERTIFICATION_ID); + var accessToken = Constants.CASHBOX_CERTIFICATION_ACCESSTOKEN; + var configuration = await GetConfigurationAsync(cashBoxId, accessToken); + var queue = configuration.ftQueues?.First() ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + var bootstrapper = new QueueGRBootstrapper(queue.Id, new LoggerFactory(), queue.Configuration ?? new Dictionary()); + return (bootstrapper, cashBoxId); + } + + public AADECertificationTestsSelfPricing(ITestOutputHelper output) + { + _output = output; + _aadeFactory = new AADEFactory(new storage.V0.MasterData.MasterDataConfiguration + { + Account = new storage.V0.MasterData.AccountMasterData + { + VatId = "112545020" + } + }); + } + + public ResponseDoc? GetResponse(string xmlContent) + { + var xmlSerializer = new XmlSerializer(typeof(ResponseDoc)); + using var stringReader = new StringReader(xmlContent); + return xmlSerializer.Deserialize(stringReader) as ResponseDoc; + } + + private async Task SendToMayData(string xml) + { + var httpClient = new HttpClient() + { + BaseAddress = new Uri("https://mydataapidev.aade.gr/") + }; + httpClient.DefaultRequestHeaders.Add("aade-user-id", "user11111111"); + httpClient.DefaultRequestHeaders.Add("ocp-apim-subscription-key", "41291863a36d552c4d7fc8195d427dd3"); + + var response = await httpClient.PostAsync("/myDataProvider/SendInvoices", new StringContent(xml, Encoding.UTF8, "application/xml")); + var content = await response.Content.ReadAsStringAsync(); + if (!response.IsSuccessStatusCode) + { + throw new Exception("Failed to send data to myData API: " + content); + } + + var ersult = GetResponse(content); + var marker = ""; + if (ersult != null) + { + var data = ersult.response[0]; + if (data.statusCode.ToLower() == "success") + { + for (var i = 0; i < data.ItemsElementName.Length; i++) + { + if (data.ItemsElementName[i] == ItemsChoiceType.qrUrl) + { + + } + else if (data.ItemsElementName[i] == ItemsChoiceType.invoiceMark) + { + marker = data.Items[i].ToString(); + + } + } + _output.WriteLine(content); + } + else + { + _output.WriteLine(xml); + + _output.WriteLine(content); + throw new Exception("Error" + content); + } + } + else + { + _output.WriteLine(xml); + + _output.WriteLine(content); + throw new Exception("Invalid response" + content); + } + return marker; + } + + private async Task ValidateMyData(ReceiptRequest receiptRequest, InvoiceType expectedInvoiceType, [CallerMemberName] string caller = "") + { + using var scope = new AssertionScope(); + var invoiceDoc = _aadeFactory.MapToInvoicesDoc(receiptRequest, ExampleResponse); + invoiceDoc.invoice[0].invoiceHeader.invoiceType.Should().Be(expectedInvoiceType); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification.Should().BeEmpty(); + var xml = _aadeFactory.GenerateInvoicePayload(invoiceDoc); + await SendToMayData(xml); + System.Console.WriteLine(caller); + await ExecuteMiddleware(receiptRequest, caller); + } + + private async Task ValidateMyData(ReceiptRequest receiptRequest, InvoiceType expectedInvoiceType, IncomeClassificationCategoryType expectedCategory, [CallerMemberName] string caller = "") + { + using var scope = new AssertionScope(); + var invoiceDoc = _aadeFactory.MapToInvoicesDoc(receiptRequest, ExampleResponse); + invoiceDoc.invoice[0].invoiceHeader.invoiceType.Should().Be(expectedInvoiceType); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationCategory.Should().Be(expectedCategory); + invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationTypeSpecified.Should().BeFalse(); + var xml = _aadeFactory.GenerateInvoicePayload(invoiceDoc); + await SendToMayData(xml); + + System.Console.WriteLine(caller); + //await ExecuteMiddleware(receiptRequest, caller); + } + + private async Task ValidateMyData(ReceiptRequest receiptRequest, InvoiceType expectedInvoiceType, IncomeClassificationCategoryType expectedCategory, IncomeClassificationValueType expectedValueType, [CallerMemberName] string caller = "") + { + using var scope = new AssertionScope(); + var invoiceDoc = _aadeFactory.MapToInvoicesDoc(receiptRequest, ExampleResponse); + invoiceDoc.invoice[0].invoiceHeader.invoiceType.Should().Be(expectedInvoiceType); + //invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationCategory.Should().Be(expectedCategory); + //invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationType.Should().Be(expectedValueType); + var xml = _aadeFactory.GenerateInvoicePayload(invoiceDoc); + await SendToMayData(xml); + System.Console.WriteLine(caller); + await ExecuteMiddleware(receiptRequest, caller); + } + +#pragma warning disable + private async Task ExecuteMiddleware(ReceiptRequest receiptRequest, string caller) + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + receiptRequest.ftCashBoxID = cashBoxId; + var signMethod = bootstrapper.RegisterForSign(); + var ticks = DateTime.UtcNow.Ticks; + var exampleCashSalesResponse = await signMethod(JsonSerializer.Serialize(receiptRequest)); + var receiptResponse = System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse)!; + if((receiptResponse.ftState & 0x0000_0000_0000_FFFF) != 0x0000) + { + throw new Exception(exampleCashSalesResponse); + } + await StoreDataAsync(caller, caller, ticks, bootstrapper, receiptRequest, receiptResponse); + } + + private async Task SendIssueAsync(ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) + { + var client = new HttpClient(); + var request = new HttpRequestMessage(HttpMethod.Post, "https://possystem-api-sandbox.fiskaltrust.eu/v2/issue"); + var cashBoxId = Guid.Parse(Constants.CASHBOX_CERTIFICATION_ID); + var accessToken = Constants.CASHBOX_CERTIFICATION_ACCESSTOKEN; + request.Headers.Add("x-cashbox-id", cashBoxId.ToString()); + request.Headers.Add("x-cashbox-accesstoken", accessToken); + var data = JsonSerializer.Serialize(new + { + ReceiptRequest = receiptRequest, + ReceiptResponse = receiptResponse + }); + request.Headers.Add("x-operation-id", Guid.NewGuid().ToString()); + var content = new StringContent(data, null, "application/json"); + request.Content = content; + var response = await client.SendAsync(request); + return await response.Content.ReadFromJsonAsync(); + } + + public async Task StoreDataAsync(string folder, string casename, long ticks, QueueGRBootstrapper bootstrapper, ReceiptRequest receiptRequest, ReceiptResponse receiptResponse) + { + var result = await SendIssueAsync(receiptRequest, receiptResponse); + var pdfdata = await new HttpClient().GetAsync(result?.DocumentURL + "?format=pdf"); + var pngdata = await new HttpClient().GetAsync(result?.DocumentURL + "?format=png"); + + var journalMethod = bootstrapper.RegisterForJournal(); + var xmlData = await journalMethod(System.Text.Json.JsonSerializer.Serialize(new ifPOS.v1.JournalRequest + { + ftJournalType = 0x4752_2000_0000_0001, + From = ticks + })); + var baseFolder = Path.Combine("C:\\temp", "viva_aade_certification_examples_selfpricing"); + var folderPath = Path.Combine(baseFolder, folder); + Directory.CreateDirectory(Path.Combine(baseFolder, folder)); + File.WriteAllText(Path.Combine(folderPath, casename + ".receiptrequest.json"), JsonSerializer.Serialize(receiptRequest, new JsonSerializerOptions + { + WriteIndented = true + })); + File.WriteAllText(Path.Combine(folderPath, casename + ".receiptresponse.json"), JsonSerializer.Serialize(receiptResponse, new JsonSerializerOptions + { + WriteIndented = true + })); + File.WriteAllBytes(Path.Combine(folderPath, casename + ".receipt.pdf"), await pdfdata.Content.ReadAsByteArrayAsync()); + File.WriteAllBytes(Path.Combine(folderPath, casename + ".receipt.png"), await pngdata.Content.ReadAsByteArrayAsync()); + File.WriteAllText(Path.Combine(folderPath, casename + "_aade.xml"), xmlData); + } + + [Fact] + public async void JOurnal() + { + (var bootstrapper, var cashBoxId) = await InitializeQueueGRBootstrapperAsync(); + var journalMethod = bootstrapper.RegisterForJournal(); + var xmlData = await journalMethod(System.Text.Json.JsonSerializer.Serialize(new ifPOS.v1.JournalRequest + { + ftJournalType = 0x4752_2000_0000_0001, + From = 0 + })); + } + + [Fact] + public async void AADECertificationExamples_A1_1_1p1() + { + var receiptRequest = AADECertificationExamplesSelfPricing.A1_1_1p1(); + await ValidateMyData(receiptRequest, InvoiceType.Item11, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_1_1p4() + { + var receiptRequest = AADECertificationExamplesSelfPricing.A1_1_1p4(); + await ValidateMyData(receiptRequest, InvoiceType.Item14, IncomeClassificationCategoryType.category1_7, IncomeClassificationValueType.E3_881_003); + } + + [Fact] + public async Task AADECertificationExamples_A1_1_1p5() + { + var receiptRequest = AADECertificationExamplesSelfPricing.A1_1_1p5_1(); + await ValidateMyData(receiptRequest, InvoiceType.Item15, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_1_1p5_2() + { + var receiptRequest = AADECertificationExamplesSelfPricing.A1_1_1p5_2(); + await ValidateMyData(receiptRequest, InvoiceType.Item15, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_1_1p6() + { + var receiptRequest = AADECertificationExamplesSelfPricing.A1_1_1p6(); + await ValidateMyData(receiptRequest, InvoiceType.Item16, IncomeClassificationCategoryType.category1_2, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async void AADECertificationExamples_A1_2_2p1() + { + var receiptRequest = AADECertificationExamplesSelfPricing.A1_2_2p1(); + await ValidateMyData(receiptRequest, InvoiceType.Item21, IncomeClassificationCategoryType.category1_3, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_2_2p4() + { + var receiptRequest = AADECertificationExamplesSelfPricing.A1_2_2p4(); + await ValidateMyData(receiptRequest, InvoiceType.Item24, IncomeClassificationCategoryType.category1_3, IncomeClassificationValueType.E3_561_001); + } + + [Fact] + public async Task AADECertificationExamples_A1_5_5p1() + { + + //var invoiceOriginal = _aadeFactory.MapToInvoicesDoc(AADECertificationExamplesSelfPricing.A1_1_1p1(), ExampleResponse); + //var marker = await SendToMayData(_aadeFactory.GenerateInvoicePayload(invoiceOriginal)); + + var creditnote = AADECertificationExamplesSelfPricing.A1_5_5p1(); + creditnote.cbPreviousReceiptReference = "400001942899521"; + await Task.Delay(1000); + //var invoiceDoc = _aadeFactory.MapToInvoicesDoc(creditnote, ExampleResponse); + //using var assertionScope = new AssertionScope(); + //invoiceDoc.invoice[0].invoiceHeader.invoiceType.Should().Be(InvoiceType.Item51); + //invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationCategory.Should().Be(IncomeClassificationCategoryType.category1_2); + //invoiceDoc.invoice[0].invoiceSummary.incomeClassification[0].classificationType.Should().Be(IncomeClassificationValueType.E3_561_001); + //var xml = _aadeFactory.GenerateInvoicePayload(invoiceDoc); + //await SendToMayData(xml); + + await ExecuteMiddleware(creditnote, "AADECertificationExamples_A1_5_5p1"); + } + + [Fact] + public async Task AADECertificationExamples_A1_5_5p2() + { + var receiptRequest = AADECertificationExamplesSelfPricing.A1_5_5p2(); + await ValidateMyData(receiptRequest, InvoiceType.Item52, IncomeClassificationCategoryType.category1_3, IncomeClassificationValueType.E3_561_001); + } + + public ReceiptResponse ExampleResponse => new ReceiptResponse + { + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftCashBoxIdentification = "cashBoxIdentification", + ftReceiptIdentification = "ft" + DateTime.UtcNow.Ticks.ToString("X"), + ftReceiptMoment = DateTime.UtcNow, + ftState = 0x4752_2000_0000_0000 + }; + } +} \ No newline at end of file diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADEFactoryTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADEFactoryTests.cs new file mode 100644 index 000000000..99f80a5e8 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/AADEFactoryTests.cs @@ -0,0 +1,78 @@ +using System.Security.Cryptography; +using System.Text; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using fiskaltrust.SAFT.CLI; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueueGR.UnitTest +{ + public class AADEFactoryTests + { + [Theory] + [InlineData("802035962-2024-10-22-0-11.1-013-2866", "FBA6C7EA5A018D27C94CAFC5A521F6A3259EF0C1")] + [InlineData("800739773-2024-11-01-0-11.1-1253-111002", "B96A69F6054CACCFC9958A0B4757CF2A1A3A76AA")] + [InlineData("062062972-2024-10-08-0-2.1-0-2970", "40F3AB32183CFBF7F91F5C1A4831E71EA5769792")] + [InlineData("094036033-2024-11-07-2-1.1-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-1.2-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-1.3-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-1.4-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-1.5-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-1.6-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-2.1-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-2.2-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-2.3-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-2.4-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-3.1-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-3.2-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-5.1-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-5.2-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-6.1-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-6.2-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-7.1-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-8.1-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-8.2-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-8.3-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-8.4-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-8.5-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-11.1-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-11.2-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-11.3-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-11.4-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-11.5-0-69489", "0CEBA664CD73B1942057E34B7ECD4EE8A65CCD70")] + [InlineData("094036033-2024-11-07-2-8.2-0-62840", "908E133E5B9677D438A3BBF388BD9967670E735D")] + public void CompareHash(string data, string hash) + { + var actualHash = GetUid(data); + actualHash.Should().Be(hash); + } + + [Fact] + public void sd() + { + + for (var i = 0; i < 10000; i++) + { + var dta = GetUid($"094036033-2024-11-07-{i}-8.2-0-62840"); + if(dta == "908E133E5B9677D438A3BBF388BD9967670E735D") + { + + } + if (dta == "CA6B78319805B8B5B020A339430725B18A87DA4D") + { + + } + } + + } + + + public string GetUid(string data) => BitConverter.ToString(SHA1.HashData(Encoding.UTF8.GetBytes(data))).Replace("-", ""); + } +} + + +/** + * Case 1: + * **/ \ No newline at end of file diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/IGRSSCD/MyDataApiTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/IGRSSCD/MyDataApiTests.cs new file mode 100644 index 000000000..8e1672d1a --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/IGRSSCD/MyDataApiTests.cs @@ -0,0 +1,89 @@ +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.myDataSCU; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueueGR.UnitTest.QueueGR.IGRSSCD +{ + public class MyDataApiTests + { + [Fact] + public async Task Test() + { + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.Parse("6244b69d-15c5-4653-8e22-c72e6e954883"), + ftReceiptCase = 0x4752_2000_0000_0000, + cbTerminalID = "1", + cbReceiptReference = Guid.NewGuid().ToString(), + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + ftChargeItemCase = 0x4752_2000_0000_0013, + VATAmount = 1.2m, + Amount = 6.2m, + VATRate = 24m, + Quantity = 1, + Description = "ChargeItem1" + }, + new ChargeItem + { + Position = 2, + ftChargeItemCase = 0x4752_2000_0000_0013, + VATAmount = 1.2m, + Amount = 6.2m, + VATRate = 24m, + Quantity = 1, + Description = "ChargeItem2" + } + ], + cbPayItems = + [ + new PayItem + { + ftPayItemCase = 0x4752_2000_0000_0001, + Amount = 6.2m, + Description = "Cash" + } + ] + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "fiskaltrust1", + ftCashBoxID = receiptRequest.ftCashBoxID, + cbReceiptReference = receiptRequest.cbReceiptReference, + cbTerminalID = receiptRequest.cbTerminalID, + ftQueueID = Guid.Parse("30100f56-6009-48fb-a612-90143e48a67b"), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 5, + ftReceiptIdentification = "ft123#", + ftReceiptMoment = DateTime.UtcNow + }; + + var sut = new MyDataApiClient("user11111111", "41291863a36d552c4d7fc8195d427dd3", true); + // var payload = sut.GenerateInvoicePayload(receiptRequest, receiptResponse); + + + var result = await sut.ProcessReceiptAsync(new GRSSCD.ProcessRequest + { + ReceiptRequest = receiptRequest, + ReceiptResponse = receiptResponse + }); + + var req = JsonSerializer.Serialize(receiptRequest); + var data = JsonSerializer.Serialize(result.ReceiptResponse); + + var issueRequest = new + { + ReceiptRequest = receiptRequest, + ReceiptResponse = receiptResponse + }; + + var dd = JsonSerializer.Serialize(issueRequest); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/DailyOperationsCommandProcessorGRTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/DailyOperationsCommandProcessorGRTests.cs new file mode 100644 index 000000000..022e9236b --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/DailyOperationsCommandProcessorGRTests.cs @@ -0,0 +1,74 @@ +using System.Threading.Tasks; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using FluentAssertions; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public class DailyOperationsCommandProcessorGRTests + { + private readonly DailyOperationsCommandProcessorGR _sut = new DailyOperationsCommandProcessorGR(); + + [Theory] + [InlineData(ReceiptCases.ZeroReceipt0x2000)] + [InlineData(ReceiptCases.OneReceipt0x2001)] + [InlineData(ReceiptCases.ShiftClosing0x2010)] + [InlineData(ReceiptCases.DailyClosing0x2011)] + [InlineData(ReceiptCases.MonthlyClosing0x2012)] + [InlineData(ReceiptCases.YearlyClosing0x2013)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError_IfInvalidCaseIsUsed() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/InvoiceCommandProcessorGRTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/InvoiceCommandProcessorGRTests.cs new file mode 100644 index 000000000..f8eba43d7 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/InvoiceCommandProcessorGRTests.cs @@ -0,0 +1,74 @@ +using System.Threading.Tasks; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using FluentAssertions; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public class InvoiceCommandProcessorGRTests + { +#pragma warning disable + private readonly InvoiceCommandProcessorGR _sut = new InvoiceCommandProcessorGR(null, null, null); + + [Theory] + [InlineData(ReceiptCases.InvoiceUnknown0x1000)] + [InlineData(ReceiptCases.InvoiceB2C0x1001)] + [InlineData(ReceiptCases.InvoiceB2B0x1002)] + [InlineData(ReceiptCases.InvoiceB2G0x1003)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await _sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/LifecycleCommandProcessorGRTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/LifecycleCommandProcessorGRTests.cs new file mode 100644 index 000000000..0eea55b32 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/LifecycleCommandProcessorGRTests.cs @@ -0,0 +1,313 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.Models; +using fiskaltrust.Middleware.Localization.QueueGR.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2.Storage; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using FluentAssertions; +using FluentAssertions.Execution; +using Moq; +using Newtonsoft.Json; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public class LifecycleCommandProcessorGRTests + { + private readonly LifecycleCommandProcessorGR _sut = new(Mock.Of()); + + [Theory] + [InlineData(ReceiptCases.InitialOperationReceipt0x4001)] + [InlineData(ReceiptCases.OutOfOperationReceipt0x4002)] + [InlineData(ReceiptCases.InitSCUSwitch0x4011)] + [InlineData(ReceiptCases.FinishSCUSwitch0x4012)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().NotBe(0x4752_2000_EEEE_EEEE); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + } + + [Fact] + public async Task InitialOperationReceipt0x4001Async_ShouldReturnActionJournal_InitOperationSignature_AndSetStateInQueue() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var configMock = new Mock(); + configMock.Setup(x => x.ActivateQueueAsync()).Returns(Task.CompletedTask); + var sut = new LifecycleCommandProcessorGR(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await sut.InitialOperationReceipt0x4001Async(request); + + queue.StartMoment.Should().BeCloseTo(DateTime.UtcNow, 1000); + + using var scope = new AssertionScope(); + result.receiptResponse.Should().Be(receiptResponse); + result.actionJournals.Should().NotBeEmpty(); + result.receiptResponse.ftSignatures.Should().NotBeEmpty(); + + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000, because: $"ftState {result.receiptResponse.ftState.ToString("X")} is different than expected."); + + var expectedSignaturItem = new SignatureItem + { + Caption = "Initial-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}", + ftSignatureFormat = (int) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = 0x4752_2000_0001_1001 + }; + + result.receiptResponse.ftSignatures[0].Should().BeEquivalentTo(expectedSignaturItem); + + var expectedActionJournal = new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queue.ftQueueId, + ftQueueItemId = queueItem.ftQueueItemId, + Moment = DateTime.UtcNow, + Priority = -1, + Type = "4752200000004001-ActivateQueueGR", + Message = $"Initial-Operation receipt. Queue-ID: {queue.ftQueueId}", + DataBase64 = null, + TimeStamp = 0 + }; + result.actionJournals[0].ftActionJournalId.Should().NotBe(Guid.Empty); + result.actionJournals[0].ftQueueId.Should().Be(expectedActionJournal.ftQueueId); + result.actionJournals[0].ftQueueItemId.Should().Be(expectedActionJournal.ftQueueItemId); + result.actionJournals[0].Moment.Should().BeCloseTo(expectedActionJournal.Moment, 1000); + result.actionJournals[0].Priority.Should().Be(expectedActionJournal.Priority); + result.actionJournals[0].Type.Should().Be(expectedActionJournal.Type); + result.actionJournals[0].Message.Should().Be(expectedActionJournal.Message); + result.actionJournals[0].DataBase64.Should().Be(expectedActionJournal.DataBase64); + result.actionJournals[0].TimeStamp.Should().Be(expectedActionJournal.TimeStamp); + + var data = JsonConvert.DeserializeObject(result.actionJournals[0].DataJson); + data.CashBoxId.Should().Be(receiptRequest.ftCashBoxID.GetValueOrDefault()); + data.IsStartReceipt.Should().Be(true); + data.Moment.Should().BeCloseTo(DateTime.UtcNow, 1000); + data.QueueId.Should().Be(queueItem.ftQueueId); + data.Version.Should().Be("V0"); + + configMock.Verify(x => x.ActivateQueueAsync(), Times.Exactly(1)); + } + + [Fact] + public async Task OutOfOperationReceipt0x4002Async_ShouldReturnActionJournal_InitOperationSignature_AndSetStateInQueue() + { + var queue = TestHelpers.CreateQueue(); + queue.StartMoment = DateTime.UtcNow; + + var queueItem = TestHelpers.CreateQueueItem(); + + var configMock = new Mock(); + configMock.Setup(x => x.ActivateQueueAsync()).Returns(Task.CompletedTask); + var sut = new LifecycleCommandProcessorGR(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0000 | (long) ReceiptCases.OutOfOperationReceipt0x4002 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await sut.OutOfOperationReceipt0x4002Async(request); + + using var scope = new AssertionScope(); + queue.StopMoment.Should().BeCloseTo(DateTime.UtcNow, 1000); + result.receiptResponse.Should().Be(receiptResponse); + result.actionJournals.Should().NotBeEmpty(); + result.receiptResponse.ftSignatures.Should().NotBeEmpty(); + + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0001, because: $"ftState {result.receiptResponse.ftState.ToString("X")} is different than expected."); + + var expectedSignaturItem = new SignatureItem + { + ftSignatureType = 0x4752_2000_0001_1002, + ftSignatureFormat = (int) ifPOS.v1.SignaturItem.Formats.Text, + Caption = "Out-of-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}" + }; + + result.receiptResponse.ftSignatures[0].Should().BeEquivalentTo(expectedSignaturItem); + + var expectedActionJournal = new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queue.ftQueueId, + ftQueueItemId = queueItem.ftQueueItemId, + Moment = DateTime.UtcNow, + Priority = -1, + Type = "4752200000004002-DeactivateQueueGR", + Message = $"Out-of-Operation receipt. Queue-ID: {queue.ftQueueId}", + DataBase64 = null, + TimeStamp = 0 + }; + result.actionJournals[0].ftActionJournalId.Should().NotBe(Guid.Empty); + result.actionJournals[0].ftQueueId.Should().Be(expectedActionJournal.ftQueueId); + result.actionJournals[0].ftQueueItemId.Should().Be(expectedActionJournal.ftQueueItemId); + result.actionJournals[0].Moment.Should().BeCloseTo(expectedActionJournal.Moment, 1000); + result.actionJournals[0].Priority.Should().Be(expectedActionJournal.Priority); + result.actionJournals[0].Type.Should().Be(expectedActionJournal.Type); + result.actionJournals[0].Message.Should().Be(expectedActionJournal.Message); + result.actionJournals[0].DataBase64.Should().Be(expectedActionJournal.DataBase64); + result.actionJournals[0].TimeStamp.Should().Be(expectedActionJournal.TimeStamp); + + var data = JsonConvert.DeserializeObject(result.actionJournals[0].DataJson); + data.CashBoxId.Should().Be(receiptRequest.ftCashBoxID.GetValueOrDefault()); + data.IsStopReceipt.Should().Be(true); + data.Moment.Should().BeCloseTo(DateTime.UtcNow, 1000); + data.QueueId.Should().Be(queueItem.ftQueueId); + data.Version.Should().Be("V0"); + + configMock.Verify(x => x.ActivateQueueAsync(), Times.Exactly(1)); + } + + [Fact] + public async Task InitSCUSwitch0x4011Async_ShouldDoNothing() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var configMock = new Mock(); + configMock.Setup(x => x.ActivateQueueAsync()).Returns(Task.CompletedTask); + var sut = new LifecycleCommandProcessorGR(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await sut.InitSCUSwitch0x4011Async(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + result.receiptResponse.ftSignatures.Should().BeEmpty(); + result.actionJournals.Should().BeEmpty(); + } + + [Fact] + public async Task FinishSCUSwitch0x4012Async_ShouldDoNothing() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var configMock = new Mock(); + configMock.Setup(x => x.ActivateQueueAsync()).Returns(Task.CompletedTask); + var sut = new LifecycleCommandProcessorGR(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await sut.FinishSCUSwitch0x4012Async(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + result.receiptResponse.ftSignatures.Should().BeEmpty(); + result.actionJournals.Should().BeEmpty(); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/ProtocolCommandProcessorGRTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/ProtocolCommandProcessorGRTests.cs new file mode 100644 index 000000000..220269663 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/ProtocolCommandProcessorGRTests.cs @@ -0,0 +1,75 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using FluentAssertions; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public class ProtocolCommandProcessorGRTests + { +#pragma warning disable + private readonly ProtocolCommandProcessorGR _sut = new(null, null, null); + + [Theory] + [InlineData(ReceiptCases.ProtocolUnspecified0x3000)] + [InlineData(ReceiptCases.ProtocolTechnicalEvent0x3001)] + [InlineData(ReceiptCases.ProtocolAccountingEvent0x3002)] + [InlineData(ReceiptCases.InternalUsageMaterialConsumption0x3003)] + [InlineData(ReceiptCases.Order0x3004)] + [InlineData(ReceiptCases.CopyReceiptPrintExistingReceipt0x3010)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/ReceiptCommandProcessorPTTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/ReceiptCommandProcessorPTTests.cs new file mode 100644 index 000000000..c64f9d5a9 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/ReceiptCommandProcessorPTTests.cs @@ -0,0 +1,174 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.myDataSCU; +using fiskaltrust.Middleware.Localization.QueueGR.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Storage; +using fiskaltrust.Middleware.Storage.GR; +using fiskaltrust.storage.V0; +using FluentAssertions; +using FluentAssertions.Execution; +using Moq; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public class ReceiptCommandProcessorPTTests + { + private readonly ReceiptCommandProcessorGR _sut = new ReceiptCommandProcessorGR(Mock.Of(), new ftQueueGR(), new ftSignaturCreationUnitGR()); + + [Theory] + [InlineData(ReceiptCases.PaymentTransfer0x0002)] + [InlineData(ReceiptCases.PointOfSaleReceiptWithoutObligation0x0003)] + [InlineData(ReceiptCases.ECommerce0x0004)] + [InlineData(ReceiptCases.Protocol0x0005)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await _sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + } + + [Fact] + public async Task PointOfSaleReceipt0x0001Async_Should_Return_QRCodeInSignatures() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var queuePT = new ftQueueGR(); + var signaturCreationUnitPT = new ftSignaturCreationUnitGR + { + + }; + + var configMock = new Mock(); + configMock.Setup(x => x.InsertOrUpdateQueueAsync(It.IsAny())).Returns(Task.CompletedTask); + var sut = new ReceiptCommandProcessorGR(new MyDataApiClient("", "", false), queuePT, signaturCreationUnitPT); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001, + cbReceiptMoment = new DateTime(2019, 12, 31), + cbChargeItems = [ + new ChargeItem + { + ftChargeItemCase = 0x4752_2000_0000_0008, + Amount = 12000.00m, + VATAmount = 0m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + new ChargeItem + { + ftChargeItemCase = 0x4752_2000_0000_0001, + Amount = 15900m, + VATAmount = 900m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + new ChargeItem + { + ftChargeItemCase = 0x4752_2000_0000_0006, + Amount = 56500m, + VATAmount = 6500m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + new ChargeItem + { + ftChargeItemCase = 0x4752_2000_0000_0003, + Amount = 98400m, + VATAmount = 18400m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + ] + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftQueueID = queue.ftQueueId, + ftQueueItemID = queueItem.ftQueueItemId, + ftCashBoxIdentification = "cashBoxIdentification", + + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await sut.PointOfSaleReceipt0x0001Async(request); + + using var scope = new AssertionScope(); + result.receiptResponse.Should().Be(receiptResponse); + result.actionJournals.Should().BeEmpty(); + result.receiptResponse.ftSignatures.Should().NotBeEmpty(); + + + result.receiptResponse.ftState.Should().Be(0x4752_2000_0000_0000, because: $"ftState {result.receiptResponse.ftState.ToString("X")} is different than expected."); + var expectedSignaturItem = new SignatureItem + { + ftSignatureType = 0x4752_2000_0000_0001, + ftSignatureFormat = (int) ifPOS.v1.SignaturItem.Formats.QR_Code, + Caption = "[www.fiskaltrust.gr]", + Data = $"??????" + }; + result.receiptResponse.ftQueueID.Should().Be(receiptResponse.ftQueueID); + result.receiptResponse.ftQueueItemID.Should().Be(receiptResponse.ftQueueItemID); + result.receiptResponse.ftReceiptIdentification.Should().Be("????"); + result.receiptResponse.ftSignatures[0].Should().BeEquivalentTo(expectedSignaturItem); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/TestHelpers.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/TestHelpers.cs new file mode 100644 index 000000000..0ba8d8cec --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/QueueGR/Processors/TestHelpers.cs @@ -0,0 +1,25 @@ +using System; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public static class TestHelpers + { + public static ftQueue CreateQueue() + { + return new ftQueue + { + ftQueueId = Guid.NewGuid(), + }; + } + + public static ftQueueItem CreateQueueItem() + { + return new ftQueueItem + { + ftQueueId = Guid.NewGuid(), + ftQueueItemId = Guid.NewGuid(), + }; + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/ReceiptExamples.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/ReceiptExamples.cs new file mode 100644 index 000000000..c5a90a262 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/ReceiptExamples.cs @@ -0,0 +1,515 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE; +using fiskaltrust.Middleware.Localization.QueueGR.GRSSCD.AADE.Models; +using fiskaltrust.Middleware.Localization.QueueGR.UnitTest; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; +using fiskaltrust.SAFT.CLI; + +public static class ReceiptExamples +{ + public static ReceiptRequest InitialOperation(Guid cashBoxId) + { + return new ReceiptRequest + { + ftCashBoxID = cashBoxId, + ftReceiptCase = 0x4752_2000_0000_4001, + cbTerminalID = "1", + cbReceiptReference = Guid.NewGuid().ToString(), + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = [], + cbPayItems = [] + }; + } + + public static ReceiptRequest ExamplePosReceipt(Guid cashBoxId) + { + var payItems = new List + { + new PayItem + { + Amount = 100m, + Description = "DebitCard", + ftPayItemCase = 0x4752_2000_0000_0000 | (long) PayItemCases.DebitCardPayment, + ftPayItemCaseData = new PayItemCaseDataCloudApi + { + Provider = new PayItemCaseProviderVivaWallet + { + Action = "Sale", + Protocol = "VivaWallet", + ProtocolVersion = "1.0", + ProtocolRequest = new VivaWalletPayment + { + amount = 100 * 100, + cashRegisterId = "", + currencyCode = "EUR", + merchantReference = Guid.NewGuid().ToString(), + sessionId = "John015", + terminalId = "123456", + aadeProviderSignatureData = "4680AFE5D58088BF8C55F57A5B5DBB15936B51DE;;20241015153111;4600;9;1;10;16007793", + aadeProviderSignature = "MEUCIQCnUrakY9pemgdXIsYvbOahoBBadDa9DPaRS9ZtTTra8gIgIUp9LPaH/E+LRwTGJWeL+MZl5j5PtFcM+chiXTqeed4=" + }, + ProtocolResponse = new VivaPaymentSession + { + aadeTransactionId = "116430909552789552789" + } + } + } + } + }; + var receiptRequest = new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = 100m, + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = [new ChargeItem { + Amount = 100m, + Quantity = 1, + VATRate = 0m, + VATAmount = 0m, + Description = "Line Item 1", + Position = 1, + ftChargeItemCase = 0x4752_2000_0000_0018 + }], + cbPayItems = payItems, + ftCashBoxID = cashBoxId, + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_3004 + }; + return receiptRequest; + } + + public static ReceiptRequest Example_SalesInvoice_1_1(Guid cashBoxId) + { + var chargeItems = new List { + CreateGoodNormalVATRateItem(description: "Product 1", amount: 89.20m, quantity: 1), + CreateGoodNormalVATRateItem(description: "Product 2", amount: 23.43m, quantity: 1), + CreateServiceNormalVATRateItem_WithWithHoldingTax(description: "Service Provision 1", netAmount: 461.93m, quantity: 1), + CreateGoodDiscountedVATRateItem(description: "Merchandise Product 1", amount: 12.30m, quantity: 1), + CreateGoodDiscountedVATRateItem(description: "Merchandise Product 2", amount: 113.43m, quantity: 1), + }; + + var i = 1; + foreach (var chargeItem in chargeItems) + { + chargeItem.Position = i++; + // Set fraction + chargeItem.Amount = decimal.Round(chargeItem.Amount, 2, MidpointRounding.AwayFromZero); + chargeItem.VATAmount = decimal.Round(chargeItem.VATAmount ?? 0.0m, 2, MidpointRounding.AwayFromZero); + } + + var payItems = new List + { + new PayItem + { + Amount = 92.39m, + Description = "VAT withholding (-20%)", + ftPayItemCase = 0x4752_2000_0000_0099 + }, + new PayItem + { + Amount = chargeItems.Sum(x => x.Amount) - 92.39m, + Description = "Cash", + ftPayItemCase = 0x4752_2000_0000_0001 + } + }; + + i = 1; + foreach (var payItem in payItems) + { + payItem.Position = i++; + // Set fraction + payItem.Amount = decimal.Round(payItem.Amount, 2, MidpointRounding.AwayFromZero); + } + + var receiptRequest = new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = chargeItems.Sum(x => x.Amount), + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = chargeItems, + cbPayItems = payItems, + ftCashBoxID = cashBoxId, + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = "997671770", + + } + }; + return receiptRequest; + } + + + public static ReceiptRequest Example_SalesInvoice_1_1_nowithholding(Guid cashBoxId) + { + var chargeItems = new List { + CreateGoodNormalVATRateItem(description: "Product 1", amount: 89.20m, quantity: 1), + CreateGoodNormalVATRateItem(description: "Product 2", amount: 23.43m, quantity: 1), + CreateGoodDiscountedVATRateItem(description: "Merchandise Product 1", amount: 12.30m, quantity: 1), + CreateGoodDiscountedVATRateItem(description: "Merchandise Product 2", amount: 113.43m, quantity: 1), + }; + + var i = 1; + foreach (var chargeItem in chargeItems) + { + chargeItem.Position = i++; + // Set fraction + chargeItem.Amount = decimal.Round(chargeItem.Amount, 2, MidpointRounding.AwayFromZero); + chargeItem.VATAmount = decimal.Round(chargeItem.VATAmount ?? 0.0m, 2, MidpointRounding.AwayFromZero); + } + + var payItems = new List + { + new PayItem + { + Amount = chargeItems.Sum(x => x.Amount), + Description = "Cash", + ftPayItemCase = 0x4752_2000_0000_0001 + } + }; + + i = 1; + foreach (var payItem in payItems) + { + payItem.Position = i++; + // Set fraction + payItem.Amount = decimal.Round(payItem.Amount, 2, MidpointRounding.AwayFromZero); + } + + var receiptRequest = new ReceiptRequest + { + cbTerminalID = "1", + Currency = Currency.EUR, + cbReceiptAmount = chargeItems.Sum(x => x.Amount), + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = chargeItems, + cbPayItems = payItems, + ftCashBoxID = cashBoxId, + ftPosSystemId = Guid.NewGuid(), + ftReceiptCase = 0x4752_2000_0000_1001, + cbCustomer = new MiddlewareCustomer + { + CustomerVATId = "997671770", + + } + }; + return receiptRequest; + } + + + public static ReceiptRequest Example_RetailSales(Guid cashBoxId) + { + var chargeItems = new List + { + CreateGoodNormalVATRateItem(description: "Merchandise Product 1", amount: 1.3m, quantity: 1), + CreateGoodNormalVATRateItem(description: "Merchandise Product 2", amount: 1.0m, quantity: 1), + CreateGoodNormalVATRateItem(description: "Merchandise Product 3", amount: 1.2m, quantity: 1), + CreateGoodDiscountedVATRateItem(description: "Merchandise Product Discounted 1", amount: 0.5m, quantity: 1), + CreateGoodDiscountedVATRateItem(description: "Merchandise Product Discounted 2", amount: 0.6m, quantity: 1) + }; + var i = 1; + foreach (var chargeItem in chargeItems) + { + chargeItem.Position = i++; + // Set fraction + chargeItem.Amount = decimal.Round(chargeItem.Amount, 2, MidpointRounding.AwayFromZero); + chargeItem.VATAmount = decimal.Round(chargeItem.VATAmount ?? 0.0m, 2, MidpointRounding.AwayFromZero); + } + var payItems = new List + { + new PayItem + { + Amount = chargeItems.Sum(x => x.Amount), + Description = "Card", + ftPayItemCase = 0x4752_2000_0000_0000 | (long) PayItemCases.DebitCardPayment, + ftPayItemCaseData = new PayItemCaseDataCloudApi + { + Provider = new PayItemCaseProviderVivaWallet + { + Action = "Sale", + Protocol = "VivaWallet", + ProtocolVersion = "1.0", + ProtocolRequest = new VivaWalletPayment + { + amount = (int) chargeItems.Sum(x => x.Amount) * 100, + cashRegisterId = "", + currencyCode = "EUR", + merchantReference = Guid.NewGuid().ToString(), + sessionId = "John015", + terminalId = "123456", + aadeProviderSignatureData = "4680AFE5D58088BF8C55F57A5B5DBB15936B51DE;;20241015153111;4600;9;1;10;16007793", + aadeProviderSignature = "MEUCIQCnUrakY9pemgdXIsYvbOahoBBadDa9DPaRS9ZtTTra8gIgIUp9LPaH/E+LRwTGJWeL+MZl5j5PtFcM+chiXTqeed4=" + }, + ProtocolResponse = new VivaPaymentSession + { + aadeTransactionId = "116430909552789552789" + } + } + } + } + }; + return new ReceiptRequest + { + Currency = Currency.EUR, + cbReceiptAmount = chargeItems.Sum(x => x.Amount), + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = chargeItems, + cbPayItems = payItems, + ftCashBoxID = cashBoxId, + ftPosSystemId = Guid.NewGuid(), + cbTerminalID = "1", + ftReceiptCase = 0x4752_2000_0000_0001 // posreceipt + }; + } + + public static ReceiptRequest Example_RetailSales_100(Guid cashBoxId) + { + var chargeItems = new List + { + CreateGoodNormalVATRateItem(description: "Merchandise Product 1", amount: 100m, quantity: 1) + }; + var i = 1; + foreach (var chargeItem in chargeItems) + { + chargeItem.Position = i++; + chargeItem.Amount = decimal.Round(chargeItem.Amount, 2, MidpointRounding.AwayFromZero); + chargeItem.VATAmount = decimal.Round(chargeItem.VATAmount ?? 0.0m, 2, MidpointRounding.AwayFromZero); + } + var payItems = new List + { + new PayItem + { + Amount = chargeItems.Sum(x => x.Amount), + Description = "Card", + ftPayItemCase = 0x4752_2000_0000_0000 | (long) PayItemCases.DebitCardPayment, + ftPayItemCaseData = new PayItemCaseDataCloudApi + { + Provider = new PayItemCaseProviderVivaWallet + { + Action = "Sale", + Protocol = "VivaWallet", + ProtocolVersion = "1.0", + ProtocolRequest = new VivaWalletPayment + { + amount = (int) chargeItems.Sum(x => x.Amount) * 100, + cashRegisterId = "", + currencyCode = "EUR", + merchantReference = Guid.NewGuid().ToString(), + sessionId = "e34072ea-067c-46ca-afca-52fecbbcba7f", + terminalId = "16009303", + aadeProviderSignatureData = "fb35d169-42a3-4064-ad7c-ac92e3c0fe30;;20241105125841;10000;10000;2400;10000;16009303", + aadeProviderSignature = "MEUCIQCnUrakY9pemgdXIsYvbOahoBBadDa9DPaRS9ZtTTra8gIgIUp9LPaH/E+LRwTGJWeL+MZl5j5PtFcM+chiXTqeed4=" + }, + ProtocolResponse = new VivaPaymentSession + { + aadeTransactionId = "116431015555865555865" + } + } + } + } + }; + return new ReceiptRequest + { + Currency = Currency.EUR, + cbReceiptAmount = chargeItems.Sum(x => x.Amount), + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = chargeItems, + cbPayItems = payItems, + ftCashBoxID = cashBoxId, + ftPosSystemId = Guid.NewGuid(), + cbTerminalID = "1", + ftReceiptCase = 0x4752_2000_0000_0001 // posreceipt + }; + } + + public static ReceiptRequest Example_RetailSales_100App2APp(Guid cashBoxId) + { + var chargeItems = new List + { + CreateGoodNormalVATRateItem(description: "Merchandise Product 1", amount: 100m, quantity: 1) + }; + var i = 1; + foreach (var chargeItem in chargeItems) + { + chargeItem.Position = i++; + chargeItem.Amount = decimal.Round(chargeItem.Amount, 2, MidpointRounding.AwayFromZero); + chargeItem.VATAmount = decimal.Round(chargeItem.VATAmount ?? 0.0m, 2, MidpointRounding.AwayFromZero); + } + var payItems = new List + { + new PayItem + { + Amount = chargeItems.Sum(x => x.Amount), + Description = "Card", + ftPayItemCase = 0x4752_2000_0000_0000 | (long) PayItemCases.DebitCardPayment, + ftPayItemCaseData = new PayItemCaseDataApp2App + { + Provider = new PayItemCaseProviderVivaWalletApp2APp + { + Action = "Sale", + Protocol = "use_auto", + ProtocolVersion = "1.0", + ProtocolRequest = "vivapayclient://pay/v1?appId=eu.fiskaltrust.instore.app&action=sale&clientTransactionId=29d6e88a-432a-44bd-8390-20af43d48df0&amount=100&callback=instoreapp%3a%2f%2fresult&show_receipt=false&show_transaction_result=false&show_rating=false&aadeProviderId=999&aadeProviderSignatureData=29d6e88a-432a-44bd-8390-20af43d48df0%3b%3b20241116101605%3b100%3b100%3b2400%3b100%3b16009303&aadeProviderSignature=MEQCIBaEvb4fHFPn2omFEExMcKmPyz62mhGqX5TsB8AjDJRpAiAYWgnzvwpNzSXr98QTQJ%2bThMfvYZe48MtA5Eed0QfEow%3d%3d", + ProtocolResponse = "instoreapp://result?aid=A0000000041010&status=success&message=Transaction successful&action=sale&clientTransactionId=29d6e88a-432a-44bd-8390-20af43d48df0&amount=100&rrn=432122575537&verificationMethod=CONTACTLESS - NO CVM&cardType=Debit Mastercard&accountNumber=535686******1364&referenceNumber=575537&authorisationCode=575537&tid=16009303&orderCode=4321235753009303&transactionDate=2024-11-17T00:16:11.4043324+02:00&transactionId=ca0532f1-75e6-465f-a11e-ebb065a87a91&paymentMethod=CARD_PRESENT&shortOrderCode=4321235753&aadeTransactionId=116432122575537575537" + } + } + } + }; + return new ReceiptRequest + { + Currency = Currency.EUR, + cbReceiptAmount = chargeItems.Sum(x => x.Amount), + cbReceiptMoment = DateTime.UtcNow, + cbReceiptReference = Guid.NewGuid().ToString(), + cbChargeItems = chargeItems, + cbPayItems = payItems, + ftCashBoxID = cashBoxId, + ftPosSystemId = Guid.NewGuid(), + cbTerminalID = "1", + ftReceiptCase = 0x4752_2000_0000_0001 // posreceipt + }; + } + + public static ReceiptRequest ExampleCashSales(Guid cashBoxId) + { + return new ReceiptRequest + { + ftCashBoxID = cashBoxId, + ftReceiptCase = 0x4752_2000_0000_0000, + cbTerminalID = "1", + cbReceiptReference = Guid.NewGuid().ToString(), + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + ftChargeItemCase = 0x4752_2000_0000_0013, + VATAmount = 1.2m, + Amount = 6.2m, + VATRate = 24m, + Quantity = 1, + Description = "ChargeItem1" + }, + new ChargeItem + { + Position = 2, + ftChargeItemCase = 0x4752_2000_0000_0013, + VATAmount = 1.2m, + Amount = 6.2m, + VATRate = 24m, + Quantity = 1, + Description = "ChargeItem2" + } + ], + cbPayItems = + [ + new PayItem + { + ftPayItemCase = 0x4752_2000_0000_0001, + Amount = 12.4m, + Description = "Cash" + } + ] + }; + } + + public static ChargeItem CreateServiceNormalVATRateItem_WithWithHoldingTax(string description, decimal netAmount, decimal quantity) + { + var vatRate = 24m; + var withholdingAmount = decimal.Round(netAmount * (20m / 100m), 2, MidpointRounding.AwayFromZero); + var vatAmount = netAmount * (vatRate / 100); + var chargeItem = new ChargeItem + { + Amount = netAmount + vatAmount, + VATRate = vatRate, + VATAmount = vatAmount, + ftChargeItemCase = 0x4752_2000_0000_0023, + Quantity = quantity, + Description = description, + ftChargeItemCaseData = new WithHoldingChargeItem + { + WithHoldingPercentage = 20m, + WithHoldingAmount = withholdingAmount + } + }; + return chargeItem; + } + + public static ChargeItem CreateServiceNormalVATRateItem(string description, decimal amount, decimal quantity) + { + var vatRate = 24m; + return new ChargeItem + { + Amount = amount, + VATRate = vatRate, + VATAmount = amount / (100M + vatRate) * vatRate, + ftChargeItemCase = 0x4752_2000_0000_0023, + Quantity = quantity, + Description = description + }; + } + + public static ChargeItem CreateServiceDiscountedVATRateItem(string description, decimal amount, decimal quantity) + { + var vatRate = 13m; + return new ChargeItem + { + Amount = amount, + VATRate = vatRate, + VATAmount = amount / (100M + vatRate) * vatRate, + ftChargeItemCase = 0x4752_2000_0000_0021, + Quantity = quantity, + Description = description + }; + } + + public static PayItem CreateWithHoldingPayItem(string description, decimal amount) + { + var percent = 20m; + return new PayItem + { + Amount = amount * (percent / 100), + ftPayItemCase = 0x4752_2000_0000_099, + Quantity = 1, + Description = description + }; + } + + public static ChargeItem CreateGoodNormalVATRateItem(string description, decimal amount, decimal quantity) + { + var vatRate = 24m; + return new ChargeItem + { + Amount = amount, + VATRate = vatRate, + VATAmount = amount / (100M + vatRate) * vatRate, + ftChargeItemCase = 0x4752_2000_0000_0013, + Quantity = quantity, + Description = description + }; + } + + public static ChargeItem CreateGoodDiscountedVATRateItem(string description, decimal amount, decimal quantity) + { + var vatRate = 13m; + return new ChargeItem + { + Amount = amount, + VATRate = vatRate, + VATAmount = amount / (100M + vatRate) * vatRate, + ftChargeItemCase = 0x4752_2000_0000_0011, + Quantity = quantity, + Description = description + }; + } + +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/ReceiptProcessorTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/ReceiptProcessorTests.cs new file mode 100644 index 000000000..83dc03ae9 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/ReceiptProcessorTests.cs @@ -0,0 +1,70 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueueGR.UnitTest +{ + + public class ReceiptProcessorTests + { + [Fact] + public async Task ReceiptProcessor_ThrowException_ReturnErrorResponse() + { + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = 0x5054_2000_0000_0000 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var sut = new ReceiptProcessor(LoggerFactory.Create(x => { }).CreateLogger(), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict)); + var result = await sut.ProcessAsync(receiptRequest, receiptResponse, new ftQueue { }, new ftQueueItem { }); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + result.receiptResponse.ftSignatures.Should().HaveCount(1); + result.receiptResponse.ftSignatures[0].ftSignatureType.Should().Be(0x5054_2000_0000_3000); + result.receiptResponse.ftSignatures[0].Caption.Should().Be("FAILURE"); + } + + [Fact] + public async Task ReceiptProcessor_ReturnNotSupported_ReturnErrorResponse() + { + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = 0x4752_2000_0000_0000 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x4752_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var sut = new ReceiptProcessor(LoggerFactory.Create(x => { }).CreateLogger(), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict)); + var result = await sut.ProcessAsync(receiptRequest, receiptResponse, new ftQueue { }, new ftQueueItem { }); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x4752_2000_EEEE_EEEE); + result.receiptResponse.ftSignatures.Should().HaveCount(1); + result.receiptResponse.ftSignatures[0].ftSignatureType.Should().Be(0x5054_2000_0000_3000); + result.receiptResponse.ftSignatures[0].Caption.Should().Be("FAILURE"); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/fiskaltrust.Middleware.Localization.QueueGR.UnitTest.csproj b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/fiskaltrust.Middleware.Localization.QueueGR.UnitTest.csproj new file mode 100644 index 000000000..1d05737c4 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueueGR.UnitTest/fiskaltrust.Middleware.Localization.QueueGR.UnitTest.csproj @@ -0,0 +1,22 @@ + + + + net8 + Latest + enable + enable + + + + + + + + + + + + + + + diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/FullTest.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/FullTest.cs new file mode 100644 index 000000000..80a9fdee9 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/FullTest.cs @@ -0,0 +1,123 @@ +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT; +using fiskaltrust.Middleware.Localization.v2.Configuration; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueueGR.UnitTest +{ + public class FullTest() + { + public async Task GetConfigurationAsync(Guid cashBoxId, string accessToken) + { + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = new Uri("https://helipad-sandbox.fiskaltrust.cloud"); + httpClient.DefaultRequestHeaders.Clear(); + httpClient.DefaultRequestHeaders.Add("cashboxid", cashBoxId.ToString()); + httpClient.DefaultRequestHeaders.Add("accesstoken", accessToken); + var result = await httpClient.GetAsync("api/configuration"); + var content = await result.Content.ReadAsStringAsync(); + if (result.IsSuccessStatusCode) + { + if (string.IsNullOrEmpty(content)) + { + throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + } + + var configuration = Newtonsoft.Json.JsonConvert.DeserializeObject(content) ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + configuration.TimeStamp = DateTime.UtcNow.Ticks; + return configuration; + } + else + { + throw new Exception($"{content}"); + } + } + } + + [Fact] + public async Task FullTests() + { + var cashBoxId = Guid.Parse("3b88c673-025c-4358-ab7f-4234e4c1a068"); + var accessToken = "BPYu7kfJa64JcdbAdF9/AJNNHjxQpqRMQu0QKTwcN8tar9hoYH89fE/AztAiOo8u/Prr+h96DhMqcp1TEzlelR8="; + + var configuration = await GetConfigurationAsync(cashBoxId, accessToken); + var queue = configuration.ftQueues?.First() ?? throw new Exception($"The configuration for {cashBoxId} is empty and therefore not valid."); + + var bootstrapper = new QueuePTBootstrapper(queue.Id, new LoggerFactory(), queue.Configuration ?? new Dictionary()); + var signMethod = bootstrapper.RegisterForSign(); + + //var initialOperationRequest = InitialOperation(cashBoxId); + //var initOperationResponse = await signMethod(System.Text.Json.JsonSerializer.Serialize(initialOperationRequest)); + + var receiptRequest = ExampleCashSales(cashBoxId); + var exampleCashSalesResponse = await signMethod(System.Text.Json.JsonSerializer.Serialize(receiptRequest)); + var issueRequest = new + { + ReceiptRequest = receiptRequest, + ReceiptResponse = System.Text.Json.JsonSerializer.Deserialize(exampleCashSalesResponse) + }; + var dd = System.Text.Json.JsonSerializer.Serialize(issueRequest); + } + + private static ReceiptRequest InitialOperation(Guid cashBoxId) + { + return new ReceiptRequest + { + ftCashBoxID = cashBoxId, + ftReceiptCase = 0x5054_2000_0000_4001, + cbTerminalID = "1", + cbReceiptReference = Guid.NewGuid().ToString(), + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = [], + cbPayItems = [] + }; + } + + private static ReceiptRequest ExampleCashSales(Guid cashBoxId) + { + return new ReceiptRequest + { + ftCashBoxID = cashBoxId, + ftReceiptCase = 0x5054_2000_0000_0000, + cbTerminalID = "1", + cbReceiptReference = Guid.NewGuid().ToString(), + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + ftChargeItemCase = 0x5054_2000_0000_0013, + VATAmount = 1.2m, + Amount = 6.2m, + VATRate = 24m, + Quantity = 1, + Description = "ChargeItem1" + }, + new ChargeItem + { + Position = 2, + ftChargeItemCase = 0x5054_2000_0000_0013, + VATAmount = 1.2m, + Amount = 6.2m, + VATRate = 24m, + Quantity = 1, + Description = "ChargeItem2" + } + ], + cbPayItems = + [ + new PayItem + { + ftPayItemCase = 0x5054_2000_0000_0001, + Amount = 12.4m, + Description = "Cash" + } + ] + }; + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/PrivateKey.pem b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/PrivateKey.pem new file mode 100644 index 000000000..a8a33bbf9 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/PrivateKey.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKeOYaguRzdkJrgN +SKmbWc+pE0FyK+zjvOJ7x3iF4PT6uIuCZu0MtaDzM02E/qyLIjMUnahFdMcc+w1n +1RtWpKcCjWJE3DT7ke8sge/bFhSUMAl+yZvm7jeY/dOmMnXRkcwppv5KXxdokqDQ +eW5+rMkXybp6EWtT1ZV97iVE8oePAgMBAAECgYBIGwrFoFy/ZpcO/5B0hMkqh10k +/egPQpYndRMLN7nuUvMV/mEixCRphh9ezcv3Hszx5H5QSsuNYFhdlYBtCmVCU2co +C6Y05bZO6Q48cTdBv5R7XRcCSC57LJ6W+ROOZtrwsz66t5JX8MM9KMWcTZ8PCdbE +CANCJgIjx7NPP6uK0QJBANnp4bL6POrKS3hwX+ZKdEyaDAIcZb1JivSk81DE7Mk3 +gdoDSgmnr6R7wB2LaEYPbz2hhEJwaFzBVGMHioDv//cCQQDE11r3JNpSH6dpoFuh +zDBvCFyLtsHbUc6I5nthvZl+ofLTJyYxusumGzL5JJFOgVVONXvN0K7KkAo6CMVh +8X8pAkAoqJ7YCiC7niseubjq+xFgCY4cBrhk7QfkRbKwa03S7WxpBRwXWRZIll5u +JMi76b1TvoMy8k5GoDvijlGlZSJZAkEAplyQoAJAlln0ZvxFlYh6gszhUp+iVddd +JC0PqDrTKMh87uLOkAccqQWh3hl+yYfbbh8bxIYTxFVan1PcZnvxiQJAb33dxzfN +JG9M+wDlG8ZC/UJTnD1euK4g/HShqhL/JSHn2JNxea5o7Rr+Cd4y8+Q5Rd7SmYJE +8zaG1+W8FOvcQA== +-----END PRIVATE KEY----- diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/AuditFileTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/AuditFileTests.cs new file mode 100644 index 000000000..1f0ca4bed --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/AuditFileTests.cs @@ -0,0 +1,24 @@ +using System.Text.Json; + +using fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +using fiskaltrust.storage.V0; +using FluentAssertions; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT +{ + public class SAFTMappingTests + { + [Fact] + public void SAFTMapping_ShouldMapHeaderCorrectly() + { + //var queueItem = new ftQueueItem + //{ + // request = JsonSerializer.Serialize(ReceiptExamples.CASH_SALES_RECEIPT), + //}; + //var auditFile = SAFTMapping.CreateAuditFile([queueItem]); + //auditFile.MasterFiles.Customer.Should().HaveCount(1); + //auditFile.MasterFiles.Product.Should().HaveCount(1); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/JournalProcessorPTTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/JournalProcessorPTTests.cs new file mode 100644 index 000000000..40f3ffa90 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/JournalProcessorPTTests.cs @@ -0,0 +1,107 @@ +using System.Text; +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT.Interface; +using fiskaltrust.Middleware.Localization.QueuePT.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.storage.V0; +using Moq; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT +{ + public class JournalProcessorPTTests + { + [Fact] + public async Task JournalProcessorPT_ShouldReturnJournalResponse() + { + var storageProvider = new Mock(); + var queueItems = new List + { + new ftQueueItem + { + request = JsonSerializer.Serialize(ReceiptExamples.CASH_SALES_RECEIPT), + response = JsonSerializer.Serialize(new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "ft1234#FS 123893/2444", + ftSignatures = [ + new SignatureItem { + Data = "hash_data", + ftSignatureType = (long) SignatureTypesPT.Hash, + ftSignatureFormat = 0x001, + }, + new SignatureItem { + Data = "atcud_data", + ftSignatureType = (long) SignatureTypesPT.ATCUD, + ftSignatureFormat = 0x001, + } + ], + ftReceiptMoment = DateTime.UtcNow, + }), + }, + new ftQueueItem + { + request = JsonSerializer.Serialize(ReceiptExamples.CASH_SALES_RECEIPT), + response = JsonSerializer.Serialize(new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "ft1234#FS 123893/2444", + ftSignatures = [ + new SignatureItem { + Data = "hash_data", + ftSignatureType = (long) SignatureTypesPT.Hash, + ftSignatureFormat = 0x001, + }, + new SignatureItem { + Data = "atcud_data", + ftSignatureType = (long) SignatureTypesPT.ATCUD, + ftSignatureFormat = 0x001, + } + ], + ftReceiptMoment = DateTime.UtcNow, + }), + }, + new ftQueueItem + { + request = JsonSerializer.Serialize(ReceiptExamples.CASH_SALES_RECEIPT), + response = JsonSerializer.Serialize(new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "ft1234#FS 123893/2444", + ftReceiptMoment = DateTime.UtcNow, + ftSignatures = [ + new SignatureItem { + Data = "hash_data", + ftSignatureType = (long) SignatureTypesPT.Hash, + ftSignatureFormat = 0x001, + }, + new SignatureItem { + Data = "atcud_data", + ftSignatureType = (long) SignatureTypesPT.ATCUD, + ftSignatureFormat = 0x001, + } + ] + }), + } + }; + storageProvider.Setup(x => x.GetMiddlewareQueueItemRepository().GetAsync()).ReturnsAsync(queueItems); + var processor = new JournalProcessorPT(storageProvider.Object); + var result = processor.ProcessAsync(new ifPOS.v1.JournalRequest()); + var journalResponse = await result.ToListAsync(); + var data = Encoding.UTF8.GetString(journalResponse.SelectMany(x => x.Chunk).ToArray()); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/PTQrCodeTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/PTQrCodeTests.cs new file mode 100644 index 000000000..d582132a5 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/PTQrCodeTests.cs @@ -0,0 +1,179 @@ +using Xunit; +using FluentAssertions; +using fiskaltrust.Middleware.Localization.QueuePT.Models; +using System; + +namespace fiskaltrust.Middleware.Localization.QueuePT.Tests +{ + public class PTQrCodeTests + { + private PTQrCode _qrCode => new PTQrCode + { + IssuerTIN = "123456789", + CustomerTIN = PTQrCode.CUSTOMER_TIN_ANONYMOUS, + CustomerCountry = PTQrCode.CUSTOMER_COUNTRY_ANONYMOUS, + DocumentType = "FT", + DocumentStatus = "N", + DocumentDate = new DateTime(2024, 10, 8), + UniqueIdentificationOfTheDocument = "FT20241008001", + ATCUD = "0", + TaxCountryRegion = "PT", + TotalTaxes = 23.34234m, + GrossTotal = 100.34234m, + Hash = "HASH1234", + SoftwareCertificateNumber = "CERT12345", + OtherInformation = "Payment via IBAN;ATM Ref. 123456" + }; + + [Fact] + public void GenerateQRCode_ShouldIncludeMandatoryFields() + { + // Expected Output for mandatory fields + var expected = "A:123456789*" + + "B:999999990*" + + "C:PT*" + + "D:FT*" + + "E:N*" + + "F:20241008*" + + "G:FT20241008001*" + + "H:0*" + + "I1:PT*" + + "N:23.34*" + + "O:100.34*" + + "Q:HASH1234*" + + "R:CERT12345*" + + "S:Payment via IBAN;ATM Ref. 123456"; + + // Actual Output + var actual = _qrCode.GenerateQRCode(); + + // Assert using FluentAssertions + actual.Should().Be(expected); + } + + [Fact] + public void GenerateQRCode_ShouldIncludeVATFields_WhenProvided() + { + // Setup: Adding VAT fields + var qrCode = _qrCode; + qrCode.TaxableBasisOfVAT_StandardRate = 80.00m; + qrCode.TotalVAT_StandardRate = 20.00m; + + // Expected Output including VAT fields + var expected = "A:123456789*" + + "B:999999990*" + + "C:PT*" + + "D:FT*" + + "E:N*" + + "F:20241008*" + + "G:FT20241008001*" + + "H:0*" + + "I1:PT*" + + "I7:80.00*" + + "I8:20.00*" + + "N:23.34*" + + "O:100.34*" + + "Q:HASH1234*" + + "R:CERT12345*" + + "S:Payment via IBAN;ATM Ref. 123456"; + + // Actual Output + var actual = qrCode.GenerateQRCode(); + + // Assert using FluentAssertions + actual.Should().Be(expected); + } + + [Fact] + public void GenerateQRCode_ShouldNotIncludeOptionalFields_WhenNull() + { + var qrCode = _qrCode; + // Setup: Removing the optional "OtherInformation" field + qrCode.OtherInformation = null; + + // Expected Output without optional field + var expected = "A:123456789*" + + "B:999999990*" + + "C:PT*" + + "D:FT*" + + "E:N*" + + "F:20241008*" + + "G:FT20241008001*" + + "H:0*" + + "I1:PT*" + + "N:23.34*" + + "O:100.34*" + + "Q:HASH1234*" + + "R:CERT12345"; + + // Actual Output + var actual = qrCode.GenerateQRCode(); + + // Assert using FluentAssertions + actual.Should().Be(expected); + } + + [Fact] + public void GenerateQRCode_ShouldFormatDecimalFieldsCorrectly() + { + var qrCode = _qrCode; + // Setup: Setting fields with decimals + qrCode.TaxableBasisOfVAT_StandardRate = 1234.5m; + qrCode.TotalVAT_StandardRate = 23.456m; // Should round to 23.46 + + // Expected Output with correctly formatted decimal fields + var expected = "A:123456789*" + + "B:999999990*" + + "C:PT*" + + "D:FT*" + + "E:N*" + + "F:20241008*" + + "G:FT20241008001*" + + "H:0*" + + "I1:PT*" + + "I7:1234.50*" + + "I8:23.46*" + + "N:23.34*" + + "O:100.34*" + + "Q:HASH1234*" + + "R:CERT12345*" + + "S:Payment via IBAN;ATM Ref. 123456"; + + // Actual Output + var actual = qrCode.GenerateQRCode(); + + // Assert using FluentAssertions + actual.Should().Be(expected); + } + + [Fact] + public void GenerateQRCode_ShouldIncludeTaxCountryRegionWithZero_WhenNoVATRate() + { + var qrCode = _qrCode; + // Setup: No VAT rate indicated, TaxCountryRegion should be I1:0 + qrCode.TaxCountryRegion = "0"; + + // Expected Output with I1:0 + var expected = "A:123456789*" + + "B:999999990*" + + "C:PT*" + + "D:FT*" + + "E:N*" + + "F:20241008*" + + "G:FT20241008001*" + + "H:0*" + + "I1:0*" + + "N:23.34*" + + "O:100.34*" + + "Q:HASH1234*" + + "R:CERT12345*" + + "S:Payment via IBAN;ATM Ref. 123456"; + + // Actual Output + var actual = qrCode.GenerateQRCode(); + + // Assert using FluentAssertions + actual.Should().Be(expected); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/DailyOperationsCommandProcessorPTTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/DailyOperationsCommandProcessorPTTests.cs new file mode 100644 index 000000000..a8b352661 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/DailyOperationsCommandProcessorPTTests.cs @@ -0,0 +1,73 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using FluentAssertions; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public class DailyOperationsCommandProcessorPTTests + { + private readonly DailyOperationsCommandProcessorPT _sut = new DailyOperationsCommandProcessorPT(); + + [Theory] + [InlineData(ReceiptCases.ZeroReceipt0x2000)] + [InlineData(ReceiptCases.OneReceipt0x2001)] + [InlineData(ReceiptCases.ShiftClosing0x2010)] + [InlineData(ReceiptCases.DailyClosing0x2011)] + [InlineData(ReceiptCases.MonthlyClosing0x2012)] + [InlineData(ReceiptCases.YearlyClosing0x2013)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError_IfInvalidCaseIsUsed() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_EEEE_EEEE); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/InvoiceCommandProcessorPTTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/InvoiceCommandProcessorPTTests.cs new file mode 100644 index 000000000..0ea0f4cd1 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/InvoiceCommandProcessorPTTests.cs @@ -0,0 +1,71 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using FluentAssertions; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public class InvoiceCommandProcessorPTTests + { + private readonly InvoiceCommandProcessorPT _sut = new InvoiceCommandProcessorPT(); + + [Theory] + [InlineData(ReceiptCases.InvoiceUnknown0x1000)] + [InlineData(ReceiptCases.InvoiceB2C0x1001)] + [InlineData(ReceiptCases.InvoiceB2B0x1002)] + [InlineData(ReceiptCases.InvoiceB2G0x1003)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_EEEE_EEEE); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/LifecycleCommandProcessorPTTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/LifecycleCommandProcessorPTTests.cs new file mode 100644 index 000000000..424dd8db6 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/LifecycleCommandProcessorPTTests.cs @@ -0,0 +1,313 @@ +using System; +using System.Threading.Tasks; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT.Models; +using fiskaltrust.Middleware.Localization.QueuePT.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using FluentAssertions; +using FluentAssertions.Execution; +using Moq; +using Newtonsoft.Json; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public class LifecycleCommandProcessorPTTests + { + private readonly LifecycleCommandProcessorPT _sut = new(Mock.Of()); + + [Theory] + [InlineData(ReceiptCases.InitialOperationReceipt0x4001)] + [InlineData(ReceiptCases.OutOfOperationReceipt0x4002)] + [InlineData(ReceiptCases.InitSCUSwitch0x4011)] + [InlineData(ReceiptCases.FinishSCUSwitch0x4012)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().NotBe(0x5054_2000_EEEE_EEEE); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_EEEE_EEEE); + } + + [Fact] + public async Task InitialOperationReceipt0x4001Async_ShouldReturnActionJournal_InitOperationSignature_AndSetStateInQueue() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var configMock = new Mock(); + configMock.Setup(x => x.InsertOrUpdateQueueAsync(It.IsAny())).Returns(Task.CompletedTask); + var sut = new LifecycleCommandProcessorPT(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x5054_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await sut.InitialOperationReceipt0x4001Async(request); + + queue.StartMoment.Should().BeCloseTo(DateTime.UtcNow, 1000); + + using var scope = new AssertionScope(); + result.receiptResponse.Should().Be(receiptResponse); + result.actionJournals.Should().NotBeEmpty(); + result.receiptResponse.ftSignatures.Should().NotBeEmpty(); + + result.receiptResponse.ftState.Should().Be(0x5054_2000_0000_0000, because: $"ftState {result.receiptResponse.ftState.ToString("X")} is different than expected."); + + var expectedSignaturItem = new SignatureItem + { + Caption = "Initial-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}", + ftSignatureFormat = (int) ifPOS.v1.SignaturItem.Formats.Text, + ftSignatureType = 0x5054_2000_0001_1001 + }; + + result.receiptResponse.ftSignatures[0].Should().BeEquivalentTo(expectedSignaturItem); + + var expectedActionJournal = new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queue.ftQueueId, + ftQueueItemId = queueItem.ftQueueItemId, + Moment = DateTime.UtcNow, + Priority = -1, + Type = "5054200000004001-ActivateQueuePT", + Message = $"Initial-Operation receipt. Queue-ID: {queue.ftQueueId}", + DataBase64 = null, + TimeStamp = 0 + }; + result.actionJournals[0].ftActionJournalId.Should().NotBe(Guid.Empty); + result.actionJournals[0].ftQueueId.Should().Be(expectedActionJournal.ftQueueId); + result.actionJournals[0].ftQueueItemId.Should().Be(expectedActionJournal.ftQueueItemId); + result.actionJournals[0].Moment.Should().BeCloseTo(expectedActionJournal.Moment, 1000); + result.actionJournals[0].Priority.Should().Be(expectedActionJournal.Priority); + result.actionJournals[0].Type.Should().Be(expectedActionJournal.Type); + result.actionJournals[0].Message.Should().Be(expectedActionJournal.Message); + result.actionJournals[0].DataBase64.Should().Be(expectedActionJournal.DataBase64); + result.actionJournals[0].TimeStamp.Should().Be(expectedActionJournal.TimeStamp); + + var data = JsonConvert.DeserializeObject(result.actionJournals[0].DataJson); + data.CashBoxId.Should().Be(receiptRequest.ftCashBoxID.GetValueOrDefault()); + data.IsStartReceipt.Should().Be(true); + data.Moment.Should().BeCloseTo(DateTime.UtcNow, 1000); + data.QueueId.Should().Be(queueItem.ftQueueId); + data.Version.Should().Be("V0"); + + configMock.Verify(x => x.InsertOrUpdateQueueAsync(queue), Times.Exactly(1)); + } + + [Fact] + public async Task OutOfOperationReceipt0x4002Async_ShouldReturnActionJournal_InitOperationSignature_AndSetStateInQueue() + { + var queue = TestHelpers.CreateQueue(); + queue.StartMoment = DateTime.UtcNow; + + var queueItem = TestHelpers.CreateQueueItem(); + + var configMock = new Mock(); + configMock.Setup(x => x.InsertOrUpdateQueueAsync(It.IsAny())).Returns(Task.CompletedTask); + var sut = new LifecycleCommandProcessorPT(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x5054_2000_0000_0000 | (long) ReceiptCases.OutOfOperationReceipt0x4002 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + + var result = await sut.OutOfOperationReceipt0x4002Async(request); + + using var scope = new AssertionScope(); + queue.StopMoment.Should().BeCloseTo(DateTime.UtcNow, 1000); + result.receiptResponse.Should().Be(receiptResponse); + result.actionJournals.Should().NotBeEmpty(); + result.receiptResponse.ftSignatures.Should().NotBeEmpty(); + + result.receiptResponse.ftState.Should().Be(0x5054_2000_0000_0001, because: $"ftState {result.receiptResponse.ftState.ToString("X")} is different than expected."); + + var expectedSignaturItem = new SignatureItem + { + ftSignatureType = 0x5054_2000_0001_1002, + ftSignatureFormat = (int) ifPOS.v1.SignaturItem.Formats.Text, + Caption = "Out-of-operation receipt", + Data = $"Queue-ID: {queue.ftQueueId}" + }; + + result.receiptResponse.ftSignatures[0].Should().BeEquivalentTo(expectedSignaturItem); + + var expectedActionJournal = new ftActionJournal + { + ftActionJournalId = Guid.NewGuid(), + ftQueueId = queue.ftQueueId, + ftQueueItemId = queueItem.ftQueueItemId, + Moment = DateTime.UtcNow, + Priority = -1, + Type = "5054200000004002-DeactivateQueuePT", + Message = $"Out-of-Operation receipt. Queue-ID: {queue.ftQueueId}", + DataBase64 = null, + TimeStamp = 0 + }; + result.actionJournals[0].ftActionJournalId.Should().NotBe(Guid.Empty); + result.actionJournals[0].ftQueueId.Should().Be(expectedActionJournal.ftQueueId); + result.actionJournals[0].ftQueueItemId.Should().Be(expectedActionJournal.ftQueueItemId); + result.actionJournals[0].Moment.Should().BeCloseTo(expectedActionJournal.Moment, 1000); + result.actionJournals[0].Priority.Should().Be(expectedActionJournal.Priority); + result.actionJournals[0].Type.Should().Be(expectedActionJournal.Type); + result.actionJournals[0].Message.Should().Be(expectedActionJournal.Message); + result.actionJournals[0].DataBase64.Should().Be(expectedActionJournal.DataBase64); + result.actionJournals[0].TimeStamp.Should().Be(expectedActionJournal.TimeStamp); + + var data = JsonConvert.DeserializeObject(result.actionJournals[0].DataJson); + data.CashBoxId.Should().Be(receiptRequest.ftCashBoxID.GetValueOrDefault()); + data.IsStopReceipt.Should().Be(true); + data.Moment.Should().BeCloseTo(DateTime.UtcNow, 1000); + data.QueueId.Should().Be(queueItem.ftQueueId); + data.Version.Should().Be("V0"); + + configMock.Verify(x => x.InsertOrUpdateQueueAsync(queue), Times.Exactly(1)); + } + + [Fact] + public async Task InitSCUSwitch0x4011Async_ShouldDoNothing() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var configMock = new Mock(); + configMock.Setup(x => x.InsertOrUpdateQueueAsync(It.IsAny())).Returns(Task.CompletedTask); + var sut = new LifecycleCommandProcessorPT(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x5054_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await sut.InitSCUSwitch0x4011Async(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_0000_0000); + result.receiptResponse.ftSignatures.Should().BeEmpty(); + result.actionJournals.Should().BeEmpty(); + } + + [Fact] + public async Task FinishSCUSwitch0x4012Async_ShouldDoNothing() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + + var configMock = new Mock(); + configMock.Setup(x => x.InsertOrUpdateQueueAsync(It.IsAny())).Returns(Task.CompletedTask); + var sut = new LifecycleCommandProcessorPT(configMock.Object); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x5054_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await sut.FinishSCUSwitch0x4012Async(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_0000_0000); + result.receiptResponse.ftSignatures.Should().BeEmpty(); + result.actionJournals.Should().BeEmpty(); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/ProtocolCommandProcessorPTTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/ProtocolCommandProcessorPTTests.cs new file mode 100644 index 000000000..89d82713d --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/ProtocolCommandProcessorPTTests.cs @@ -0,0 +1,72 @@ +using System.Threading.Tasks; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT.Processors; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using FluentAssertions; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public class ProtocolCommandProcessorPTTests + { + private readonly ProtocolCommandProcessorPT _sut = new ProtocolCommandProcessorPT(); + + [Theory] + [InlineData(ReceiptCases.ProtocolUnspecified0x3000)] + [InlineData(ReceiptCases.ProtocolTechnicalEvent0x3001)] + [InlineData(ReceiptCases.ProtocolAccountingEvent0x3002)] + [InlineData(ReceiptCases.InternalUsageMaterialConsumption0x3003)] + [InlineData(ReceiptCases.Order0x3004)] + [InlineData(ReceiptCases.CopyReceiptPrintExistingReceipt0x3010)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(new ftQueue { }, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(new ftQueue { }, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_EEEE_EEEE); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/ReceiptCommandProcessorPTTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/ReceiptCommandProcessorPTTests.cs new file mode 100644 index 000000000..4103e5fc8 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/ReceiptCommandProcessorPTTests.cs @@ -0,0 +1,178 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT.Processors; +using fiskaltrust.Middleware.Localization.QueuePT.PTSSCD; +using fiskaltrust.Middleware.Localization.v2.Interface; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.Middleware.Storage; +using fiskaltrust.Middleware.Storage.PT; +using fiskaltrust.storage.V0; +using FluentAssertions; +using FluentAssertions.Execution; +using Moq; +using Xunit; +using fiskaltrust.Middleware.Localization.v2.Models.ifPOS.v2.Cases; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public class ReceiptCommandProcessorPTTests + { + private readonly ReceiptCommandProcessorPT _sut = new ReceiptCommandProcessorPT(Mock.Of(), new ftQueuePT(), new ftSignaturCreationUnitPT()); + + [Theory] + [InlineData(ReceiptCases.PaymentTransfer0x0002)] + [InlineData(ReceiptCases.PointOfSaleReceiptWithoutObligation0x0003)] + [InlineData(ReceiptCases.ECommerce0x0004)] + [InlineData(ReceiptCases.Protocol0x0005)] + public async Task ProcessReceiptAsync_ShouldReturnEmptyList(ReceiptCases receiptCase) + { + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = (int) receiptCase + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(new ftQueue { }, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_0000_0000); + } + + [Fact] + public async Task ProcessReceiptAsync_ShouldReturnError() + { + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = -1 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + var request = new ProcessCommandRequest(new ftQueue { }, receiptRequest, receiptResponse); + + var result = await _sut.ProcessReceiptAsync(request); + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_EEEE_EEEE); + } + + [Fact] + public async Task PointOfSaleReceipt0x0001Async_Should_Return_QRCodeInSignatures() + { + var queue = TestHelpers.CreateQueue(); + var queueItem = TestHelpers.CreateQueueItem(); + var queuePT = new ftQueuePT + { + IssuerTIN = "123456789", + TaxRegion = "PT", + ATCUD = "CSDF7T5H0035", + SimplifiedInvoiceSeries = "AB2019", + SimplifiedInvoiceSeriesNumerator = 34 + }; + var signaturCreationUnitPT = new ftSignaturCreationUnitPT + { + PrivateKey = File.ReadAllText("PrivateKey.pem"), + SoftwareCertificateNumber = "9999", + }; + + var configMock = new Mock(); + configMock.Setup(x => x.InsertOrUpdateQueueAsync(It.IsAny())).Returns(Task.CompletedTask); + var sut = new ReceiptCommandProcessorPT(new InMemorySCU(signaturCreationUnitPT), queuePT, signaturCreationUnitPT); + + var receiptRequest = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftReceiptCase = 0x5054_2000_0000_0000 | (long) ReceiptCases.InitialOperationReceipt0x4001, + cbReceiptMoment = new DateTime(2019, 12, 31), + cbChargeItems = [ + new ChargeItem + { + ftChargeItemCase = 0x5054_2000_0000_0008, + Amount = 12000.00m, + VATAmount = 0m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + new ChargeItem + { + ftChargeItemCase = 0x5054_2000_0000_0001, + Amount = 15900m, + VATAmount = 900m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + new ChargeItem + { + ftChargeItemCase = 0x5054_2000_0000_0006, + Amount = 56500m, + VATAmount = 6500m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + new ChargeItem + { + ftChargeItemCase = 0x5054_2000_0000_0003, + Amount = 98400m, + VATAmount = 18400m, + Description = "Description", + Quantity = 1, + VATRate = 23m + }, + ] + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftQueueID = queue.ftQueueId, + ftQueueItemID = queueItem.ftQueueItemId, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var request = new ProcessCommandRequest(queue, receiptRequest, receiptResponse); + var result = await sut.PointOfSaleReceipt0x0001Async(request); + + using var scope = new AssertionScope(); + result.receiptResponse.Should().Be(receiptResponse); + result.actionJournals.Should().BeEmpty(); + result.receiptResponse.ftSignatures.Should().NotBeEmpty(); + + + result.receiptResponse.ftState.Should().Be(0x5054_2000_0000_0000, because: $"ftState {result.receiptResponse.ftState.ToString("X")} is different than expected."); + var expectedSignaturItem = new SignatureItem + { + ftSignatureType = 0x5054_2000_0000_0001, + ftSignatureFormat = (int) ifPOS.v1.SignaturItem.Formats.QR_Code, + Caption = "[www.fiskaltrust.pt]", + Data = $"A:123456789*B:999999990*C:PT*D:FS*E:N*F:20191231*G:FS AB2019/0035*H:CSDF7T5H0035*I1:PT*I2:12000.00*I3:15000.00*I4:900.00*I5:50000.00*I6:6500.00*I7:80000.00*I8:18400.00*N:25800.00*O:182800.00*Q:jvs6*R:9999*S:ftQueueId={receiptResponse.ftQueueID};ftQueueItemId={receiptResponse.ftQueueItemID}" + }; + result.receiptResponse.ftQueueID.Should().Be(receiptResponse.ftQueueID); + result.receiptResponse.ftQueueItemID.Should().Be(receiptResponse.ftQueueItemID); + result.receiptResponse.ftReceiptIdentification.Should().Be("FS AB2019/0035"); + result.receiptResponse.ftSignatures[0].Should().BeEquivalentTo(expectedSignaturItem); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/TestHelpers.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/TestHelpers.cs new file mode 100644 index 000000000..0ba8d8cec --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/Processors/TestHelpers.cs @@ -0,0 +1,25 @@ +using System; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT.Processors +{ + public static class TestHelpers + { + public static ftQueue CreateQueue() + { + return new ftQueue + { + ftQueueId = Guid.NewGuid(), + }; + } + + public static ftQueueItem CreateQueueItem() + { + return new ftQueueItem + { + ftQueueId = Guid.NewGuid(), + ftQueueItemId = Guid.NewGuid(), + }; + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/QRCodeTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/QRCodeTests.cs new file mode 100644 index 000000000..ee1f7e001 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/QRCodeTests.cs @@ -0,0 +1,7 @@ +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT +{ + public class QRCodeTests + { + + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/ReceiptExamples.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/ReceiptExamples.cs new file mode 100644 index 000000000..5fc0e8b16 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/ReceiptExamples.cs @@ -0,0 +1,158 @@ +using System.Text.RegularExpressions; +using System; +using fiskaltrust.Api.POS.Models.ifPOS.v2; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT; + +public static class ReceiptExamples +{ + public static ReceiptRequest NUNO_BASIC_RECEIPT = new ReceiptRequest + { + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + ProductNumber = "SUPCERTIFIC", + Description = "Suporte Certifica o Software", + Quantity = 1.000000m, + Unit = "UN", + UnitPrice = 400.00m, + Moment = new DateTime(2024, 06, 27, 11, 37, 18), + Amount = 400.00m, + VATRate = 0.00m, + VATAmount = 0.00m, + ftChargeItemCase = 0x5054_2000_0000_0008 + } + ], + ftReceiptCase = 0x5054_2000_0000_0001 + }; + + public static ReceiptRequest CASH_SALES_RECEIPT = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftPosSystemId = Guid.Empty, + cbTerminalID = "00010001", + cbReceiptReference = "0001-0002", + cbUser = "user", + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + ProductGroup = "Drinks", + ProductNumber = Guid.NewGuid().ToString(), + Unit = "l", + Quantity = 1.0m, + Amount = 3.20m, + UnitPrice = 3.20m, + VATRate = 6m, + VATAmount = 3.20m - (3.20m / (1 + 0.06m)), + ftChargeItemCase = 0x5054_2000_0000_0001, + Description = "Beer", + Moment = DateTime.UtcNow + } + ], + cbPayItems = + [ + new PayItem + { + Quantity = 1, + Description = "Cash", + ftPayItemCase = 0x5054_2000_0000_0001, + Moment = DateTime.UtcNow, + Amount = 3.20m, + } + ], + ftReceiptCase = 0x5054_2000_0000_0001 + }; + + public static ReceiptRequest DEBIT_SALES_RECEIPT = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftPosSystemId = Guid.Empty, + cbTerminalID = "00010001", + cbReceiptReference = "0001-0002", + cbUser = "user", + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + ProductGroup = "Drinks", + ProductNumber = Guid.NewGuid().ToString(), + Unit = "l", + Quantity = 1.0m, + Amount = 3.20m, + UnitPrice = 3.20m, + VATRate = 6m, + VATAmount = 3.20m - (3.20m / (1 + 0.06m)), + ftChargeItemCase = 0x5054_2000_0000_0001, + Description = "Beer", + Moment = DateTime.UtcNow + } + ], + cbPayItems = + [ + new PayItem + { + Quantity = 1, + Description = "Card", + ftPayItemCase = 0x5054_2000_0000_0004, + Moment = DateTime.UtcNow, + Amount = 3.20m, + } + ], + ftReceiptCase = 0x5054_2000_0000_0001 + }; + + public static ReceiptRequest MULTIPLE_PAYITEMS_SALES_RECEIPT = new ReceiptRequest + { + ftCashBoxID = Guid.NewGuid(), + ftPosSystemId = Guid.Empty, + cbTerminalID = "00010001", + cbReceiptReference = "0001-0002", + cbUser = "user", + cbReceiptMoment = DateTime.UtcNow, + cbChargeItems = + [ + new ChargeItem + { + Position = 1, + ProductGroup = "Drinks", + ProductNumber = Guid.NewGuid().ToString(), + Unit = "l", + Quantity = 1.0m, + Amount = 3.20m, + UnitPrice = 3.20m, + VATRate = 6m, + VATAmount = 3.20m - (3.20m / (1 + 0.06m)), + ftChargeItemCase = 0x5054_2000_0000_0001, + Description = "Beer", + Moment = DateTime.UtcNow + } + ], + cbPayItems = + [ + new PayItem + { + Quantity = 1, + Description = "Cash", + ftPayItemCase = 0x5054_2000_0000_0001, + Moment = DateTime.UtcNow, + Amount = 1.00m, + }, + new PayItem + { + Quantity = 1, + Description = "Card", + ftPayItemCase = 0x5054_2000_0000_0004, + Moment = DateTime.UtcNow, + Amount = 2.20m, + } + ], + ftReceiptCase = 0x5054_2000_0000_0001 + }; +} \ No newline at end of file diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/SAFTTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/SAFTTests.cs new file mode 100644 index 000000000..c12c6b6c6 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePT/SAFTTests.cs @@ -0,0 +1,26 @@ +using System; +using System.IO; +using System.Text.Json; +using System.Xml.Serialization; +using fiskaltrust.Middleware.Localization.QueuePT.Exports.SAFTPT; +using fiskaltrust.SAFT.CLI.SAFTSchemaPT10401; +using fiskaltrust.storage.V0; + +namespace fiskaltrust.Middleware.Localization.QueuePT.UnitTest.QueuePT +{ + + public class SAFTTests + { + public void BasicTest() + { + + + } + private static AuditFile? GetAuditFileFromXML(string xml) + { + var serializer = new XmlSerializer(typeof(AuditFile)); + using var reader = new StringReader(xml); + return (AuditFile?) serializer.Deserialize(reader); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePTBootstrapperTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePTBootstrapperTests.cs new file mode 100644 index 000000000..fc31afe20 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/QueuePTBootstrapperTests.cs @@ -0,0 +1,55 @@ +using System.Text.Json; +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.QueuePT; +using fiskaltrust.storage.V0; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueueIT.UnitTest +{ + public class QueuePTBootstrapperTests + { + [Fact] + public void BootstrapSetupTests() + { + var cashBoxId = Guid.NewGuid(); + var queueId = Guid.NewGuid(); + + var bootstrapper = new QueuePTBootstrapper(queueId, new LoggerFactory(), new Dictionary + { + { "storageaccountname", "test" }, + { "init_ftQueue", Newtonsoft.Json.JsonConvert.SerializeObject(new List { new ftQueue { ftQueueId = queueId, ftCashBoxId = cashBoxId } }) } + }); + _ = bootstrapper.RegisterForSign(); + + //var signResult = await signMethod(JsonSerializer.Serialize(new ReceiptRequest + //{ + // ftCashBoxID = cashBoxId, + //})); + //var response = JsonSerializer.Deserialize(signResult); + } + + [Fact] + public async Task BootstrapSetup_WithSignMethodTests() + { + var cashBoxId = Guid.NewGuid(); + var queueId = Guid.NewGuid(); + var bootstrapper = new QueuePTBootstrapper(queueId, new LoggerFactory(), new Dictionary + { + { "storageaccountname", "test" }, + { "init_ftQueue", Newtonsoft.Json.JsonConvert.SerializeObject(new List { new ftQueue { ftQueueId = queueId, ftCashBoxId = cashBoxId } }) } + }); + + var signMethod = bootstrapper.RegisterForSign(); + + var signResult = await signMethod(JsonSerializer.Serialize(new ReceiptRequest + { + ftCashBoxID = cashBoxId, + })); + var response = JsonSerializer.Deserialize(signResult); + response.Should().NotBeNull(); + //response!.ftState.Should().Be(0x5054_2000_FFFF_FFFFF); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/ReceiptProcessorTests.cs b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/ReceiptProcessorTests.cs new file mode 100644 index 000000000..229e92c39 --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/ReceiptProcessorTests.cs @@ -0,0 +1,69 @@ +using fiskaltrust.Api.POS.Models.ifPOS.v2; +using fiskaltrust.Middleware.Localization.v2; +using fiskaltrust.storage.V0; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace fiskaltrust.Middleware.Localization.QueueIT.UnitTest +{ + public class ReceiptProcessorTests + { + [Fact] + public async Task ReceiptProcessor_ThrowException_ReturnErrorResponse() + { + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = 0x5054_2000_0000_0000 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var sut = new ReceiptProcessor(LoggerFactory.Create(x => { }).CreateLogger(), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict)); + var result = await sut.ProcessAsync(receiptRequest, receiptResponse, new ftQueue { }, new ftQueueItem { }); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_EEEE_EEEE); + result.receiptResponse.ftSignatures.Should().HaveCount(1); + result.receiptResponse.ftSignatures[0].ftSignatureType.Should().Be(0x5054_2000_0000_3000); + result.receiptResponse.ftSignatures[0].Caption.Should().Be("FAILURE"); + } + + [Fact] + public async Task ReceiptProcessor_ReturnNotSupported_ReturnErrorResponse() + { + var receiptRequest = new ReceiptRequest + { + ftReceiptCase = 0x5054_2000_0000_0000 + }; + var receiptResponse = new ReceiptResponse + { + ftState = 0x5054_2000_0000_0000, + ftCashBoxIdentification = "cashBoxIdentification", + ftQueueID = Guid.NewGuid(), + ftQueueItemID = Guid.NewGuid(), + ftQueueRow = 1, + ftReceiptIdentification = "receiptIdentification", + ftReceiptMoment = DateTime.UtcNow, + }; + + var sut = new ReceiptProcessor(LoggerFactory.Create(x => { }).CreateLogger(), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict), Mock.Of(MockBehavior.Strict)); + var result = await sut.ProcessAsync(receiptRequest, receiptResponse, new ftQueue { }, new ftQueueItem { }); + + result.receiptResponse.Should().Be(receiptResponse); + result.receiptResponse.ftState.Should().Be(0x5054_2000_EEEE_EEEE); + result.receiptResponse.ftSignatures.Should().HaveCount(1); + result.receiptResponse.ftSignatures[0].ftSignatureType.Should().Be(0x5054_2000_0000_3000); + result.receiptResponse.ftSignatures[0].Caption.Should().Be("FAILURE"); + } + } +} diff --git a/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/fiskaltrust.Middleware.Localization.QueuePT.UnitTest.csproj b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/fiskaltrust.Middleware.Localization.QueuePT.UnitTest.csproj new file mode 100644 index 000000000..7f48b4d9b --- /dev/null +++ b/queue/test/fiskaltrust.Middleware.Localization.QueuePT.UnitTest/fiskaltrust.Middleware.Localization.QueuePT.UnitTest.csproj @@ -0,0 +1,28 @@ + + + + net8 + Latest + enable + enable + + + + + + + + + + + + + + + + + PreserveNewest + + + + diff --git "a/queue/test/fiskaltrust.Middleware.Queue.AcceptanceTest/ReceiptExamples/3_RechnungZuru\314\210ckholen/request.json" "b/queue/test/fiskaltrust.Middleware.Queue.AcceptanceTest/ReceiptExamples/3_RechnungZuru\314\210ckholen/request.json" new file mode 100644 index 000000000..24197af27 --- /dev/null +++ "b/queue/test/fiskaltrust.Middleware.Queue.AcceptanceTest/ReceiptExamples/3_RechnungZuru\314\210ckholen/request.json" @@ -0,0 +1,54 @@ +{ + "ftCashBoxID": "c094f242-91d5-4343-9c54-bce85f70d0d6", + "ftPosSystemId": "b3dc6573-96d9-e611-80f7-5065f38adae1", + "cbTerminalID": "1", + "cbReceiptReference": "1", + "cbReceiptMoment": "2019-10-25T13:33:03.013Z", + "cbChargeItems": [ + { + "Quantity": 1.0, + "Description": "Bier 0,5 lt", + "Amount": -3.80000000000000000000000000, + "VATRate": 19.0000, + "ftChargeItemCase": 4919338167972134913, + "ftChargeItemCaseData": "", + "VATAmount": -0.6067226890756302521008403361, + "CostCenter": "1", + "ProductGroup": "Bier", + "ProductNumber": "101", + "ProductBarcode": "", + "Unit": "Liter", + "UnitQuantity": 1.0000 + }, + { + "Quantity": 1.0, + "Description": "Schnitzel", + "Amount": -9.20000000000000000000000000, + "VATRate": 7.0000, + "ftChargeItemCase": 4919338167972134914, + "ftChargeItemCaseData": "", + "VATAmount": -0.601869158878504672897196262, + "CostCenter": "1", + "ProductGroup": "Speisen", + "ProductNumber": "102", + "ProductBarcode": "", + "Unit": "Stk", + "UnitQuantity": 1.0000 + } + ], + "cbPayItems": [ + { + "Quantity": 1.0, + "Description": "Bar", + "Amount": -13.0000, + "ftPayItemCase": 4919338167972134913, + "ftPayItemCaseData": "", + "CostCenter": "1", + "MoneyGroup": "1", + "MoneyNumber": "" + } + ], + "ftReceiptCase": 4919338172267364353, + "cbReceiptAmount": -13.00, + "cbUser": "Chef" +} \ No newline at end of file diff --git a/queue/test/fiskaltrust.Middleware.Storage.AzureTableStorage.AcceptanceTest/AzureTableStorageQueueItemRepositoryTests.cs b/queue/test/fiskaltrust.Middleware.Storage.AzureTableStorage.AcceptanceTest/AzureTableStorageQueueItemRepositoryTests.cs index 788bfc62e..682e17fb2 100644 --- a/queue/test/fiskaltrust.Middleware.Storage.AzureTableStorage.AcceptanceTest/AzureTableStorageQueueItemRepositoryTests.cs +++ b/queue/test/fiskaltrust.Middleware.Storage.AzureTableStorage.AcceptanceTest/AzureTableStorageQueueItemRepositoryTests.cs @@ -1,12 +1,10 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using AutoFixture; using Azure.Data.Tables; using fiskaltrust.Middleware.Contracts.Repositories; using fiskaltrust.Middleware.Storage.AcceptanceTest; -using fiskaltrust.Middleware.Storage.AzureTableStorage; using fiskaltrust.Middleware.Storage.AzureTableStorage.AcceptanceTest.Fixtures; using fiskaltrust.Middleware.Storage.AzureTableStorage.Repositories; using fiskaltrust.storage.V0; diff --git a/scu-it/src/.DS_Store b/scu-it/src/.DS_Store deleted file mode 100644 index aaa60ff16..000000000 Binary files a/scu-it/src/.DS_Store and /dev/null differ diff --git a/scu-it/src/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter/Utilities/EpsonCommandFactory.cs b/scu-it/src/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter/Utilities/EpsonCommandFactory.cs index ef18d951c..978e0e18b 100644 --- a/scu-it/src/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter/Utilities/EpsonCommandFactory.cs +++ b/scu-it/src/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter/Utilities/EpsonCommandFactory.cs @@ -5,6 +5,7 @@ using fiskaltrust.Middleware.SCU.IT.Abstraction; using System.Collections.Generic; using Newtonsoft.Json; +using System.Security.Cryptography; #pragma warning disable @@ -331,7 +332,7 @@ private static void GenerateItems(List itemAndMessages, ChargeIt itemAndMessages.Add(new() { PrintRecItem = printRecItem, PrintRecMessage = printRecMessage }); } else if (i.IsSingleUseVoucher() && i.Amount < 0) - { + { var printRecItemAdjustment = new PrintRecItemAdjustment { Description = i.Description, diff --git a/scu-it/test/fiskaltrust.Middleware.SCU.IT.CustomRTServer.UnitTest/.DS_Store b/scu-it/test/fiskaltrust.Middleware.SCU.IT.CustomRTServer.UnitTest/.DS_Store deleted file mode 100644 index 6583b1f7f..000000000 Binary files a/scu-it/test/fiskaltrust.Middleware.SCU.IT.CustomRTServer.UnitTest/.DS_Store and /dev/null differ diff --git a/scu-it/test/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest/ReceiptExampleTests.cs b/scu-it/test/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest/ReceiptExampleTests.cs new file mode 100644 index 000000000..0a5a867ce --- /dev/null +++ b/scu-it/test/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest/ReceiptExampleTests.cs @@ -0,0 +1,114 @@ +using System; +using System.Linq; +using fiskaltrust.ifPOS.v1; +using fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.Utilities; +using Newtonsoft.Json; + +namespace fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest +{ + public class ReceiptExampleTests + { + [Fact] + public void Test1() + { + var receipt = $$""" +{ + "ftCashBoxID": "4038ca6d-fe63-46d0-95d6-a1fce0f98258", + "ftQueueID": "910347dc-a5fc-44bf-9ef0-2ec5fda824ca", + "ftPosSystemId": "e0c014d5-44de-4eec-886f-02dde5ec2d3a", + "cbTerminalID": "1", + "cbReceiptReference": "f64afd38-91be-44fc-8625-2fba33c22004", + "cbReceiptMoment": "2024-10-02T07:01:46.993Z", + "cbChargeItems": [ + { + "Position": 100, + "Quantity": 0.3330, + "Description": "Americano", + "Amount": 0.99900000000000000000000000, + "VATRate": 22.0000, + "ftChargeItemCase": 5283883447184523283, + "ftChargeItemCaseData": "", + "VATAmount": 0.1801475409836065573770491803, + "CostCenter": "4", + "ProductGroup": "Warme Getränke", + "ProductNumber": "1019", + "ProductBarcode": "", + "Unit": "Stk", + "Moment": "2024-10-02T07:01:36.867Z" + }, + { + "Position": 200, + "Quantity": 1.0000, + "Description": "Espresso", + "Amount": 1.60000000000000000000000000, + "VATRate": 22.0000, + "ftChargeItemCase": 5283883447184523283, + "ftChargeItemCaseData": "", + "VATAmount": 0.2885245901639344262295081967, + "CostCenter": "4", + "ProductGroup": "Warme Getränke", + "ProductNumber": "1001", + "ProductBarcode": "", + "Unit": "Stk", + "Moment": "2024-09-20T09:17:59.667Z" + }, + { + "Position": 300, + "Quantity": 0.3330, + "Description": "Glühmix", + "Amount": 1.16550000000000000000000000, + "VATRate": 22.0000, + "ftChargeItemCase": 5283883447184523283, + "ftChargeItemCaseData": "", + "VATAmount": 0.210172131147540983606557377, + "CostCenter": "4", + "ProductGroup": "Warme Getränke", + "ProductNumber": "1015", + "ProductBarcode": "", + "Unit": "Stk", + "Moment": "2024-09-17T08:52:01.367Z" + }, + { + "Position": 400, + "Quantity": 0.3330, + "Description": "Glühwein", + "Amount": 1.16550000000000000000000000, + "VATRate": 22.0000, + "ftChargeItemCase": 5283883447184523283, + "ftChargeItemCaseData": "", + "VATAmount": 0.210172131147540983606557377, + "CostCenter": "4", + "ProductGroup": "Warme Getränke", + "ProductNumber": "1016", + "ProductBarcode": "", + "Unit": "Stk", + "Moment": "2024-10-02T07:01:37.177Z" + } + ], + "cbPayItems": [ + { + "Quantity": 1.0, + "Description": "Bar", + "Amount": 4.9300, + "ftPayItemCase": 5283883447184523265, + "ftPayItemCaseData": "", + "CostCenter": "4", + "MoneyGroup": "1", + "MoneyNumber": "" + } + ], + "ftReceiptCase": 5283883447318740993, + "cbReceiptAmount": 4.93, + "cbUser": "Chef", + "cbArea": "50" +} +"""; + var receiptDataDeserialized = JsonConvert.DeserializeObject(receipt); + var content = EpsonCommandFactory.CreateInvoiceRequestContent(new EpsonRTPrinterSCUConfiguration { }, receiptDataDeserialized); + var sumChargeItems = content.ItemAndMessages.Select(x => x.PrintRecItem).Sum(x => x.Quantity * x.UnitPrice); + + var xml = SoapSerializer.Serialize(content); + Console.WriteLine(xml); + } + } +} \ No newline at end of file diff --git a/scu-it/test/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest.csproj b/scu-it/test/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest.csproj index 089ac9854..945e552e4 100644 --- a/scu-it/test/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest.csproj +++ b/scu-it/test/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest/fiskaltrust.Middleware.SCU.IT.EpsonRTPrinter.UnitTest.csproj @@ -1,7 +1,7 @@  - net6 + net8 false 11