using System.Text.Json; using System.Text.Json.Serialization; namespace API.WebHost.NotasFiscais; // --------------------------- // Fluent Builder para NFCom // --------------------------- public sealed class NfcomBuilder { private readonly Pedido _root = new() { ambiente = 1, // sandbox infNFCom = new() { versao = "1.00", id = "NFCom000001ABCDEF12345600000000000000000000000000", ide = new() { cUF = 41, tpAmb = 2, mod = 62, serie = 1, nNF = 1, cNF = "1234567", cDV = 0, dhEmi = DateTimeOffset.Now, tpEmis = 1, nSiteAutoriz = 0, cMunFG = "4106902", finNFCom = 0, tpFat = 1, verProc = "VoipMundo.SDK/1.0", indPrePago = 1, indCessaoMeiosRede = 1, indNotaEntrada = 1 }, emit = new(), dest = new(), assinante = new(), det = new List(), total = new() { icmsTot = new(), vRetTribTot = new() }, gRespTec = new() }, referencia = "REF-DEFAULT" }; // --------- Facades de ambiente --------- public NfcomBuilder UseSandbox() { _root.ambiente = 1; _root.infNFCom!.ide!.tpAmb = 2; return this; } public NfcomBuilder UseProducao() { _root.ambiente = 2; _root.infNFCom!.ide!.tpAmb = 1; return this; } // --------- Identificação / IDE --------- public NfcomBuilder WithIde( int cUF, string cMunFG, int serie, int nNF, string cNF7digits, int tpEmis = 1, int nSiteAutoriz = 0, int finNFCom = 0, int tpFat = 1, string verProc = "VoipMundo.SDK/1.0") { var ide = _root.infNFCom!.ide!; ide.cUF = cUF; ide.cMunFG = cMunFG; ide.serie = serie; ide.nNF = nNF; ide.cNF = cNF7digits; ide.tpEmis = tpEmis; ide.nSiteAutoriz = nSiteAutoriz; ide.finNFCom = finNFCom; ide.tpFat = tpFat; ide.verProc = verProc; return this; } public NfcomBuilder WithDhEmi(DateTimeOffset dhEmi) { _root.infNFCom!.ide!.dhEmi = dhEmi; return this; } public NfcomBuilder WithId(string id) { _root.infNFCom!.id = id; return this; } public NfcomBuilder WithReferencia(string referencia) { _root.referencia = referencia; return this; } // --------- Emitente --------- public NfcomBuilder WithEmitente( string cnpj, string ie, int crt, string xNome, string xFant, string xLgr, string nro, string xBairro, string cMun, string xMun, string cep, string uf, string fone, string email) { _root.infNFCom!.emit = new() { cnpj = cnpj, ie = ie, crt = crt, xNome = xNome, xFant = xFant, enderEmit = new() { xLgr = xLgr, nro = nro, xBairro = xBairro, cMun = cMun, xMun = xMun, cep = cep, uf = uf, fone = fone, email = email } }; return this; } // --------- Destinatário --------- public NfcomBuilder WithDestinatarioPF( string nome, string cpf, int indIEDest, string xLgr, string nro, string xBairro, string cMun, string xMun, string cep, string uf, string cPais, string xPais, string fone, string email) { _root.infNFCom!.dest = new() { xNome = nome, cpf = cpf, indIEDest = indIEDest, enderDest = new() { xLgr = xLgr, nro = nro, xBairro = xBairro, cMun = cMun, xMun = xMun, cep = cep, uf = uf, fone = fone, email = email } }; return this; } public NfcomBuilder WithDestinatarioPJ( string nome, string cnpj, int indIEDest, string ie, string xLgr, string nro, string xBairro, string cMun, string xMun, string cep, string uf, string cPais, string xPais, string fone, string email) { _root.infNFCom!.dest = new() { xNome = nome, cnpj = cnpj, indIEDest = indIEDest, ie = ie, enderDest = new() { xLgr = xLgr, nro = nro, xBairro = xBairro, cMun = cMun, xMun = xMun, cep = cep, uf = uf, fone = fone, email = email } }; return this; } // --------- Assinante --------- public NfcomBuilder WithAssinante( string iCodAssinante, int tpAssinante, int tpServUtil, string nContrato, DateOnly dIni, DateOnly dFim, string nroTermPrinc, int cUFPrinc, string? nroTermAdic = null, int? cUFAdic = null) { _root.infNFCom!.assinante = new() { iCodAssinante = iCodAssinante, tpAssinante = tpAssinante, tpServUtil = tpServUtil, nContrato = nContrato, dContratoIni = dIni, dContratoFim = dFim, nroTermPrinc = nroTermPrinc, cUFPrinc = cUFPrinc, // Ordem importa no schema: nroTermAdic -> cUFAdic nroTermAdic = nroTermAdic, cUFAdic = cUFAdic }; return this; } // --------- Itens --------- public NfcomBuilder AddItemServico( int nItem, string cProd, string xProd, string cClass7digits, string cfop, int uMed, decimal qFaturada, decimal vItem, decimal vDesc = 0m, decimal vOutro = 0m, bool desoneradoIcms = true) { var det = new Det { nItem = nItem, prod = new Prod { cProd = cProd, xProd = xProd, cClass = cClass7digits, cfop = cfop, uMed = uMed, qFaturada = qFaturada, vItem = vItem, vDesc = vDesc, vOutro = vOutro, vProd = vItem - vDesc + vOutro }, imposto = new Imposto() }; // Impostos default sandbox (zerados) if (desoneradoIcms) { det.imposto!.icmS40 = new Icms40 { cst = "40", vICMSDeson = 0m, cBenef = "EX-TESTE" }; } det.imposto!.pis = new Pis { cst = "49", vBC = 0m, pPIS = 0m, vPIS = 0m }; det.imposto!.cofins = new Cofins { cst = "49", vBC = 0m, pCOFINS = 0m, vCOFINS = 0m }; det.imposto!.fust = new Fust { vBC = 0m, pFUST = 0m, vFUST = 0m }; det.imposto!.funttel = new Funttel { vBC = 0m, pFUNTTEL = 0m, vFUNTTEL = 0m }; _root.infNFCom!.det!.Add(det); return this; } // --------- Resp. Técnica --------- public NfcomBuilder WithResponsavelTecnico(string cnpj, string xContato, string email, string fone) { _root.infNFCom!.gRespTec = new() { cnpj = cnpj, xContato = xContato, email = email, fone = fone }; return this; } // --------- Totais (auto) --------- public NfcomBuilder AutoTotals() { var itens = _root.infNFCom!.det!; decimal vProd = itens.Sum(i => i.prod!.vProd); _root.infNFCom.total = new() { vProd = vProd, icmsTot = new() { vBC = 0m, vICMS = 0m, vICMSDeson = 0m, vFCP = 0m }, vPIS = 0m, vCOFINS = 0m, vFUNTTEL = 0m, vFUST = 0m, vDesc = 0m, vOutro = 0m, vNF = vProd, vRetTribTot = new() { vRetPIS = 0m, vRetCofins = 0m, vRetCSLL = 0m, vBCIRRF = 0m, vIRRF = 0m } }; return this; } // --------- Helpers prontos --------- public NfcomBuilder SandboxPRCuritibaTemplate() { UseSandbox(); WithIde(cUF: 41, cMunFG: "4106902", serie: 1, nNF: 1, cNF7digits: "1234567", tpEmis: 1, nSiteAutoriz: 0, finNFCom: 0, tpFat: 1); WithResponsavelTecnico("27865757000102", "VoipMundo - Suporte", "suporte@exemplo.com.br", "41911112222"); return this; } // --------- Build --------- public object BuildObject(bool autoTotals = true) { if (autoTotals) AutoTotals(); Validate(); return _root; } /// /// Converte para o tipo do SDK: NuvemFiscal.Sdk.Model.NfcomPedidoEmissao /// (sem depender do assembly em tempo de compilação do seu domínio). /// public T BuildAs(bool autoTotals = true) { if (autoTotals) AutoTotals(); Validate(); var json = JsonSerializer.Serialize(_root, JsonOptions); return JsonSerializer.Deserialize(json, JsonOptions)!; } public string BuildJson(bool autoTotals = true) { if (autoTotals) AutoTotals(); Validate(); return JsonSerializer.Serialize(_root, JsonOptionsIndented); } // --------- Validação rápida de formato/obrigatórios --------- private void Validate() { var ide = _root.infNFCom!.ide!; if (string.IsNullOrWhiteSpace(_root.infNFCom.id)) throw new InvalidOperationException("infNFCom.id é obrigatório."); if (ide.nSiteAutoriz is null) throw new InvalidOperationException("ide.nSiteAutoriz é obrigatório."); if (string.IsNullOrWhiteSpace(ide.cNF) || ide.cNF!.Length != 7 || !ide.cNF.All(char.IsDigit)) throw new InvalidOperationException("ide.cNF deve ter exatamente 7 dígitos."); if (_root.infNFCom.assinante is null || string.IsNullOrWhiteSpace(_root.infNFCom.assinante!.nroTermPrinc) || _root.infNFCom.assinante!.cUFPrinc is null) throw new InvalidOperationException("assinante.nroTermPrinc e assinante.cUFPrinc são obrigatórios e na ordem correta."); if (_root.infNFCom.det is null || _root.infNFCom.det.Count == 0) throw new InvalidOperationException("Ao menos 1 det (item) é obrigatório."); } private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, Converters = { new DateOnlyConverter(), new DateTimeOffsetConverter() } }; private static readonly JsonSerializerOptions JsonOptionsIndented = new(JsonOptions) { WriteIndented = true }; // --------------------------- // DTOs mínimos (espelho do schema) // --------------------------- private sealed class Pedido { public int ambiente { get; set; } public InfNFCom infNFCom { get; set; } = default!; public string referencia { get; set; } = default!; } private sealed class InfNFCom { public string versao { get; set; } = default!; public string id { get; set; } = default!; public Ide ide { get; set; } = new(); public Emit emit { get; set; } = new(); public Dest dest { get; set; } = new(); public Assinante assinante { get; set; } = new(); public List det { get; set; } = new(); public Total total { get; set; } = new(); public RespTec gRespTec { get; set; } = new(); public string? infAdic { get; set; } // (pode modelar completo se quiser) } private sealed class RespTec { public string cnpj { get; set; } = default!; public string xContato { get; set; } = default!; public string email { get; set; } = default!; public string fone { get; set; } = default!; // Campos opcionais (se for usar CSRT) public int? idCSRT { get; set; } public string? csrt { get; set; } public string? hashCSRT { get; set; } } private sealed class Ide { public int cUF { get; set; } public int tpAmb { get; set; } public int mod { get; set; } public int serie { get; set; } public int nNF { get; set; } public string cNF { get; set; } = default!; public int cDV { get; set; } public DateTimeOffset dhEmi { get; set; } public int tpEmis { get; set; } public int? nSiteAutoriz { get; set; } public string cMunFG { get; set; } = default!; public int finNFCom { get; set; } public int tpFat { get; set; } public string verProc { get; set; } = default!; public int indPrePago { get; set; } public int indCessaoMeiosRede { get; set; } public int indNotaEntrada { get; set; } public DateTimeOffset? dhCont { get; set; } public string? xJust { get; set; } } private sealed class Emit { public string cnpj { get; set; } = default!; public string ie { get; set; } = default!; public int crt { get; set; } public string xNome { get; set; } = default!; public string xFant { get; set; } = default!; public Ender enderEmit { get; set; } = new(); } private sealed class Dest { public string xNome { get; set; } = default!; public string? cnpj { get; set; } public string? cpf { get; set; } public int indIEDest { get; set; } public string? ie { get; set; } public Ender enderDest { get; set; } = new(); } private sealed class Assinante { public string iCodAssinante { get; set; } = default!; public int tpAssinante { get; set; } public int tpServUtil { get; set; } public string nContrato { get; set; } = default!; public DateOnly dContratoIni { get; set; } public DateOnly dContratoFim { get; set; } public string nroTermPrinc { get; set; } = default!; public int? cUFPrinc { get; set; } public string? nroTermAdic { get; set; } public int? cUFAdic { get; set; } } private sealed class Ender { public string xLgr { get; set; } = default!; public string nro { get; set; } = default!; public string xBairro { get; set; } = default!; public string cMun { get; set; } = default!; public string xMun { get; set; } = default!; public string cep { get; set; } = default!; public string uf { get; set; } = default!; public string fone { get; set; } = default!; public string email { get; set; } = default!; } private sealed class Det { public int nItem { get; set; } public Prod? prod { get; set; } public Imposto? imposto { get; set; } public string? infAdProd { get; set; } } private sealed class Prod { public string cProd { get; set; } = default!; public string xProd { get; set; } = default!; public string cClass { get; set; } = default!; public string cfop { get; set; } = default!; public int uMed { get; set; } public decimal qFaturada { get; set; } public decimal vItem { get; set; } public decimal vDesc { get; set; } public decimal vOutro { get; set; } public decimal vProd { get; set; } } private sealed class Imposto { public Icms40? icmS40 { get; set; } public Pis? pis { get; set; } public Cofins? cofins { get; set; } public Fust? fust { get; set; } public Funttel? funttel { get; set; } } private sealed class Icms40 { public string cst { get; set; } = "40"; public decimal vICMSDeson { get; set; } public string? cBenef { get; set; } } private sealed class Pis { public string cst { get; set; } = "49"; public decimal vBC { get; set; } public decimal pPIS { get; set; } public decimal vPIS { get; set; } } private sealed class Cofins { public string cst { get; set; } = "49"; public decimal vBC { get; set; } public decimal pCOFINS { get; set; } public decimal vCOFINS { get; set; } } private sealed class Fust { public decimal vBC { get; set; } public decimal pFUST { get; set; } public decimal vFUST { get; set; } } private sealed class Funttel { public decimal vBC { get; set; } public decimal pFUNTTEL { get; set; } public decimal vFUNTTEL { get; set; } } private sealed class Total { public decimal vProd { get; set; } public IcmsTot icmsTot { get; set; } = new(); public decimal vCOFINS { get; set; } public decimal vPIS { get; set; } public decimal vFUNTTEL { get; set; } public decimal vFUST { get; set; } public decimal vDesc { get; set; } public decimal vOutro { get; set; } public decimal vNF { get; set; } public VRetTribTot vRetTribTot { get; set; } = new(); } private sealed class IcmsTot { public decimal vBC { get; set; } public decimal vICMS { get; set; } public decimal vICMSDeson { get; set; } public decimal vFCP { get; set; } } private sealed class VRetTribTot { public decimal vRetPIS { get; set; } public decimal vRetCofins { get; set; } public decimal vRetCSLL { get; set; } public decimal vBCIRRF { get; set; } public decimal vIRRF { get; set; } } // Converters auxiliares private sealed class DateOnlyConverter : JsonConverter { public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => DateOnly.Parse(reader.GetString()!); public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString("yyyy-MM-dd")); } private sealed class DateTimeOffsetConverter : JsonConverter { public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => DateTimeOffset.Parse(reader.GetString()!); public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString("yyyy-MM-ddTHH:mm:sszzz")); } }