// Generado desde el documento OpenAPI publico de NovaFE. No lo edites a mano: regeneralo con `bun run models`. using System.Text.Json; using System.Text.Json.Serialization; namespace NovaFE.Client.Models; public sealed record DgiiMessage { [JsonPropertyName("code")] public required int Code { get; init; } [JsonPropertyName("value")] public required string Value { get; init; } } /// Una entrada de `/{amb}/consultatrackids/api/trackids/consulta`. Puede haber más de una para el mismo e-NCF si se remitió varias veces. public sealed record DgiiTrackIdEntry { [JsonPropertyName("trackId")] public required string TrackId { get; init; } [JsonPropertyName("estado")] public required string Estado { get; init; } [JsonPropertyName("fechaRecepcion")] public string? FechaRecepcion { get; init; } } /// Texto libre para la Representación Impresa. public sealed record EcfAdditionalInfoPayload { [JsonPropertyName("issuer")] public string? Issuer { get; init; } [JsonPropertyName("buyer")] public string? Buyer { get; init; } } /// Desglose de impuestos adicionales por código de la Tabla I (001–039). Todos los montos los trae el cliente ya calculados. `iscEspecifico` (monto fijo por volumen: alcoholes 006-018, cigarrillos 019-022) integra la base imponible del ITBIS de esa línea; `iscAdvalorem` y `otros` (Propina, CDT, ISC de servicios, Primera Placa…) van "por encima", sin tocar la base. public sealed record EcfAdditionalTaxPayload { [JsonPropertyName("code")] public required string Code { get; init; } [JsonPropertyName("rate")] public decimal? Rate { get; init; } [JsonPropertyName("iscEspecifico")] public decimal? IscEspecifico { get; init; } [JsonPropertyName("iscAdvalorem")] public decimal? IscAdvalorem { get; init; } [JsonPropertyName("otros")] public decimal? Otros { get; init; } } /// Un renglón del historial de envíos de un comprobante: cómo terminó un ciclo contra la DGII. Lo que emite `GET /api/v1/ecf/{id}/attempts`. public sealed record EcfAttemptDto { /// 1, 2, 3… dentro del comprobante. [JsonPropertyName("attempt")] public required int Attempt { get; init; } /// En qué estado dejó el ciclo al comprobante: `accepted`, `accepted_conditional`, `rejected`, `review` o `failed`. [JsonPropertyName("outcome")] public required string Outcome { get; init; } /// `TrackId` de la DGII de este ciclo; ausente si no llegó a haber uno. [JsonPropertyName("trackId")] public string? TrackId { get; init; } /// Código de estado de la DGII (1 aceptado, 2 rechazado, 4 aceptado condicional); ausente si no hubo veredicto. [JsonPropertyName("statusCode")] public int? StatusCode { get; init; } /// El `estado` textual de la DGII. [JsonPropertyName("statusText")] public string? StatusText { get; init; } /// `secuenciaUtilizada` de la DGII: `false` = el e-NCF no se consumió; `true` o ausente = consumido. [JsonPropertyName("sequenceUsed")] public bool? SequenceUsed { get; init; } /// Observaciones o motivo de rechazo de la DGII; o el motivo del fallo si no hubo veredicto. [JsonPropertyName("messages")] public required IReadOnlyList Messages { get; init; } /// Qué se decidió con un rechazo: `auto:<código>` (la DGII falló de forma transitoria y NovaFE reenvía el mismo XML solo, p. ej. `auto:4`) o `exhausted` (era reintentable pero se agotó el ladder y el rechazo quedó firme). Ausente en el resto de resultados. [JsonPropertyName("retryDecision")] public string? RetryDecision { get; init; } /// SHA-256 (hex) del XML enviado en este ciclo. [JsonPropertyName("xmlSha256")] public required string XmlSha256 { get; init; } /// Cuándo se registró el resultado. [JsonPropertyName("recordedAt")] public required string RecordedAt { get; init; } } /// Bloque `<Comprador>`. `Rnc` y `ForeignId` son excluyentes. public sealed record EcfBuyerPayload { [JsonPropertyName("name")] public string? Name { get; init; } [JsonPropertyName("rnc")] public string? Rnc { get; init; } [JsonPropertyName("foreignId")] public string? ForeignId { get; init; } [JsonPropertyName("email")] public string? Email { get; init; } [JsonPropertyName("contact")] public string? Contact { get; init; } [JsonPropertyName("address")] public string? Address { get; init; } [JsonPropertyName("municipality")] public string? Municipality { get; init; } [JsonPropertyName("province")] public string? Province { get; init; } [JsonPropertyName("additionalInfo")] public string? AdditionalInfo { get; init; } } /// Totales que calculó el cliente (en DOP). Se comparan con el cálculo de NovaFE (tolerancia RF-06.6); el valor de NovaFE es el que va al XML. Todo opcional. public sealed record EcfDeclaredTotalsPayload { [JsonPropertyName("montoGravadoTotal")] public decimal? MontoGravadoTotal { get; init; } [JsonPropertyName("montoExento")] public decimal? MontoExento { get; init; } [JsonPropertyName("totalItbis")] public decimal? TotalItbis { get; init; } [JsonPropertyName("montoImpuestoAdicional")] public decimal? MontoImpuestoAdicional { get; init; } [JsonPropertyName("montoTotal")] public decimal? MontoTotal { get; init; } } /// El intercambio con la DGII de un comprobante: lo que ella respondió más los instantes en que NovaFE registró cada paso. public sealed record EcfDgiiExchange { /// Identificador que la DGII asigna al recibir el comprobante. [JsonPropertyName("trackId")] public string? TrackId { get; init; } /// El `estado` textual de la DGII ("Aceptado", "Rechazado", "En Proceso"…); en el idioma en que lo manda ella. [JsonPropertyName("status")] public string? Status { get; init; } /// Código de estado de la DGII: 1 aceptado · 2 rechazado · 3 en proceso · 4 aceptado condicional. [JsonPropertyName("statusCode")] public int? StatusCode { get; init; } /// `secuenciaUtilizada` de la DGII: `false` = el e-NCF no se consumió (firma/XML inválidos); `true` o ausente = consumido. [JsonPropertyName("sequenceUsed")] public bool? SequenceUsed { get; init; } /// Observaciones o motivo de rechazo que devolvió la DGII. [JsonPropertyName("messages")] public required IReadOnlyList Messages { get; init; } /// Instante en que la DGII confirmó la recepción (hay string? EcfDgiiExchange.TrackId). [JsonPropertyName("submittedAt")] public string? SubmittedAt { get; init; } /// La `fechaRecepcion` que informó la DGII; ausente si no la dio (p. ej. RFCE). [JsonPropertyName("receivedAt")] public string? ReceivedAt { get; init; } /// Instante en que la DGII dio un resultado definitivo. [JsonPropertyName("processedAt")] public string? ProcessedAt { get; init; } } /// Vista de un comprobante emitido (respuesta de `POST /ecf` y `GET /ecf/{id}`). Trae la identidad fiscal, el resumen comercial (comprador, monto total — igual que EcfSummaryDto) y el resultado del intercambio con la DGII (EcfDgiiExchange? EcfDto.Dgii). El desglose por línea y el resto del detalle comercial siguen viviendo solo en el XML firmado (`GET /ecf/{id}/xml`). public sealed record EcfDto { [JsonPropertyName("id")] public required string Id { get; init; } [JsonPropertyName("status")] public required string Status { get; init; } [JsonPropertyName("encf")] public required string Encf { get; init; } [JsonPropertyName("type")] public required int Type { get; init; } [JsonPropertyName("environment")] public required string Environment { get; init; } [JsonPropertyName("sequenceExpiresOn")] public string? SequenceExpiresOn { get; init; } [JsonPropertyName("issueDate")] public required string IssueDate { get; init; } [JsonPropertyName("issuedAt")] public required string IssuedAt { get; init; } [JsonPropertyName("signedAt")] public required string SignedAt { get; init; } [JsonPropertyName("securityCode")] public required string SecurityCode { get; init; } [JsonPropertyName("qrUrl")] public required string QrUrl { get; init; } [JsonPropertyName("submitsRfce")] public required bool SubmitsRfce { get; init; } [JsonPropertyName("internalNumber")] public string? InternalNumber { get; init; } [JsonPropertyName("toleranceWarning")] public string? ToleranceWarning { get; init; } [JsonPropertyName("signedDuringContingency")] public required bool SignedDuringContingency { get; init; } [JsonPropertyName("montoTotal")] public required decimal MontoTotal { get; init; } [JsonPropertyName("buyerRnc")] public string? BuyerRnc { get; init; } [JsonPropertyName("buyerName")] public string? BuyerName { get; init; } [JsonPropertyName("documentHash")] public required string DocumentHash { get; init; } /// Lo que pasó con la DGII: `trackId`, estado (texto y código), mensajes, `secuenciaUtilizada` y los instantes de envío, recepción y resolución. `null` mientras el comprobante no se haya enviado. [JsonPropertyName("dgii")] public EcfDgiiExchange? Dgii { get; init; } /// El reintento automático en curso o ya hecho: cuántas veces NovaFE reenvió el mismo comprobante tras un rechazo transitorio de la DGII, el tope, y cuándo es el próximo intento. `null` mientras no haya habido ningún reintento. [JsonPropertyName("retry")] public EcfRetry? Retry { get; init; } /// Enlaces a los recursos relacionados del comprobante. [JsonPropertyName("links")] public EcfLinks? Links { get; init; } } /// Datos de exportación (solo tipo 46). public sealed record EcfExportPayload { [JsonPropertyName("loadingPortName")] public string? LoadingPortName { get; init; } [JsonPropertyName("deliveryTerms")] public string? DeliveryTerms { get; init; } [JsonPropertyName("totalFob")] public decimal? TotalFob { get; init; } [JsonPropertyName("insurance")] public decimal? Insurance { get; init; } [JsonPropertyName("freight")] public decimal? Freight { get; init; } [JsonPropertyName("otherCharges")] public decimal? OtherCharges { get; init; } [JsonPropertyName("totalCif")] public decimal? TotalCif { get; init; } [JsonPropertyName("customsRegime")] public string? CustomsRegime { get; init; } [JsonPropertyName("departurePortName")] public string? DeparturePortName { get; init; } [JsonPropertyName("unloadingPortName")] public string? UnloadingPortName { get; init; } } /// Facturación en divisa (`<OtraMoneda>`). El cliente trae los montos ya convertidos. public sealed record EcfForeignCurrencyPayload { [JsonPropertyName("currency")] public required string Currency { get; init; } [JsonPropertyName("exchangeRate")] public required decimal ExchangeRate { get; init; } [JsonPropertyName("totals")] public required EcfForeignCurrencyTotalsPayload Totals { get; init; } } public sealed record EcfForeignCurrencyTotalsPayload { [JsonPropertyName("montoGravadoTotal")] public decimal? MontoGravadoTotal { get; init; } [JsonPropertyName("montoGravadoI1")] public decimal? MontoGravadoI1 { get; init; } [JsonPropertyName("montoGravadoI2")] public decimal? MontoGravadoI2 { get; init; } [JsonPropertyName("montoGravadoI3")] public decimal? MontoGravadoI3 { get; init; } [JsonPropertyName("montoExento")] public decimal? MontoExento { get; init; } [JsonPropertyName("totalItbis")] public decimal? TotalItbis { get; init; } [JsonPropertyName("totalItbis1")] public decimal? TotalItbis1 { get; init; } [JsonPropertyName("totalItbis2")] public decimal? TotalItbis2 { get; init; } [JsonPropertyName("totalItbis3")] public decimal? TotalItbis3 { get; init; } [JsonPropertyName("montoTotal")] public decimal? MontoTotal { get; init; } } /// Un descuento o recargo global (Sección D). public sealed record EcfGlobalAdjustmentPayload { [JsonPropertyName("line")] public required int Line { get; init; } [JsonPropertyName("kind")] public string? Kind { get; init; } [JsonPropertyName("affectsItbisRate")] public int? AffectsItbisRate { get; init; } [JsonPropertyName("amount")] public decimal? Amount { get; init; } [JsonPropertyName("norm1007")] public bool? Norm1007 { get; init; } [JsonPropertyName("description")] public string? Description { get; init; } [JsonPropertyName("percentage")] public decimal? Percentage { get; init; } } public sealed record EcfItemCodePayload { [JsonPropertyName("type")] public required string Type { get; init; } [JsonPropertyName("value")] public required string Value { get; init; } } /// Campos opcionales del `<Item>` — passthrough. La derivación del ISC desde `alcoholDegrees`/`referenceQuantity` es un slice posterior; hoy el cliente trae el monto final del ISC en `additionalTaxes` (el ISC específico ahí sí integra la base del ITBIS — ver EcfAdditionalTaxPayload). public sealed record EcfLineDetailsPayload { [JsonPropertyName("referenceQuantity")] public decimal? ReferenceQuantity { get; init; } [JsonPropertyName("referenceUnit")] public string? ReferenceUnit { get; init; } [JsonPropertyName("subquantities")] public IReadOnlyList? Subquantities { get; init; } [JsonPropertyName("alcoholDegrees")] public decimal? AlcoholDegrees { get; init; } [JsonPropertyName("referenceUnitPrice")] public decimal? ReferenceUnitPrice { get; init; } [JsonPropertyName("manufactureDate")] public string? ManufactureDate { get; init; } [JsonPropertyName("expiryDate")] public string? ExpiryDate { get; init; } [JsonPropertyName("mining")] public EcfMiningPayload? Mining { get; init; } } /// `<OtraMonedaDetalle>` — precio y montos de la línea en divisa (passthrough). public sealed record EcfLineForeignCurrencyPayload { [JsonPropertyName("unitPrice")] public decimal? UnitPrice { get; init; } [JsonPropertyName("discount")] public decimal? Discount { get; init; } [JsonPropertyName("surcharge")] public decimal? Surcharge { get; init; } [JsonPropertyName("lineAmount")] public decimal? LineAmount { get; init; } } /// Una línea de `<DetallesItems>`. public sealed record EcfLinePayload { [JsonPropertyName("name")] public string? Name { get; init; } [JsonPropertyName("kind")] public string? Kind { get; init; } [JsonPropertyName("quantity")] public decimal? Quantity { get; init; } [JsonPropertyName("unitPrice")] public decimal? UnitPrice { get; init; } [JsonPropertyName("itbisRate")] public int? ItbisRate { get; init; } [JsonPropertyName("unitOfMeasure")] public string? UnitOfMeasure { get; init; } [JsonPropertyName("description")] public string? Description { get; init; } [JsonPropertyName("discount")] public decimal? Discount { get; init; } [JsonPropertyName("surcharge")] public decimal? Surcharge { get; init; } [JsonPropertyName("priceIncludesTax")] public bool? PriceIncludesTax { get; init; } [JsonPropertyName("declaredAmount")] public decimal? DeclaredAmount { get; init; } [JsonPropertyName("codes")] public IReadOnlyList? Codes { get; init; } [JsonPropertyName("retention")] public EcfLineRetentionPayload? Retention { get; init; } [JsonPropertyName("additionalTaxes")] public IReadOnlyList? AdditionalTaxes { get; init; } [JsonPropertyName("foreignCurrency")] public EcfLineForeignCurrencyPayload? ForeignCurrency { get; init; } [JsonPropertyName("details")] public EcfLineDetailsPayload? Details { get; init; } } /// Área `<Retencion>` de la línea (tipos 41 y 47). Los montos los calcula el cliente. public sealed record EcfLineRetentionPayload { [JsonPropertyName("agent")] public string? Agent { get; init; } [JsonPropertyName("itbisWithheld")] public decimal? ItbisWithheld { get; init; } [JsonPropertyName("isrWithheld")] public decimal? IsrWithheld { get; init; } } /// Enlaces (relativos) a los recursos del comprobante. public sealed record EcfLinks { /// El comprobante y su estado. [JsonPropertyName("self")] public required string Self { get; init; } /// El XML firmado (`<ECF>`). [JsonPropertyName("xml")] public required string Xml { get; init; } /// El resumen firmado (`<RFCE>`); solo cuando `submitsRfce`. [JsonPropertyName("rfceXml")] public string? RfceXml { get; init; } /// La Representación Impresa en PDF. [JsonPropertyName("representation")] public required string Representation { get; init; } } /// `<Mineria>` — datos de liquidación minera (solo tipos 32/33/34/46). public sealed record EcfMiningPayload { [JsonPropertyName("netWeightKilogram")] public decimal? NetWeightKilogram { get; init; } [JsonPropertyName("netWeightMining")] public decimal? NetWeightMining { get; init; } [JsonPropertyName("affiliationType")] public int? AffiliationType { get; init; } [JsonPropertyName("settlement")] public int? Settlement { get; init; } } /// Página de la RI (`<Paginacion>`). public sealed record EcfPagePayload { [JsonPropertyName("number")] public int? Number { get; init; } [JsonPropertyName("lineFrom")] public int? LineFrom { get; init; } [JsonPropertyName("lineTo")] public int? LineTo { get; init; } [JsonPropertyName("montoGravadoTotal")] public decimal? MontoGravadoTotal { get; init; } [JsonPropertyName("montoGravadoI1")] public decimal? MontoGravadoI1 { get; init; } [JsonPropertyName("montoGravadoI2")] public decimal? MontoGravadoI2 { get; init; } [JsonPropertyName("montoGravadoI3")] public decimal? MontoGravadoI3 { get; init; } [JsonPropertyName("montoExento")] public decimal? MontoExento { get; init; } [JsonPropertyName("totalItbis")] public decimal? TotalItbis { get; init; } [JsonPropertyName("itbis1")] public decimal? Itbis1 { get; init; } [JsonPropertyName("itbis2")] public decimal? Itbis2 { get; init; } [JsonPropertyName("itbis3")] public decimal? Itbis3 { get; init; } [JsonPropertyName("montoImpuestoAdicional")] public decimal? MontoImpuestoAdicional { get; init; } [JsonPropertyName("iscEspecifico")] public decimal? IscEspecifico { get; init; } [JsonPropertyName("otrosImpuestos")] public decimal? OtrosImpuestos { get; init; } [JsonPropertyName("amount")] public decimal? Amount { get; init; } [JsonPropertyName("nonInvoiceableAmount")] public decimal? NonInvoiceableAmount { get; init; } } /// Una forma de pago. `Type` y `Amount` son nullable para distinguir "no vino" de un valor: el validador exige ambos y la API nunca rellena uno por su cuenta. public sealed record EcfPaymentMethodPayload { [JsonPropertyName("type")] public string? Type { get; init; } [JsonPropertyName("amount")] public decimal? Amount { get; init; } } /// Bloque de pago del encabezado. public sealed record EcfPaymentPayload { [JsonPropertyName("condition")] public string? Condition { get; init; } [JsonPropertyName("dueDate")] public string? DueDate { get; init; } [JsonPropertyName("methods")] public IReadOnlyList? Methods { get; init; } } /// Sección `<InformacionReferencia>` — Notas de Crédito/Débito y reemplazos. public sealed record EcfReferencePayload { [JsonPropertyName("modifiedNcf")] public required string ModifiedNcf { get; init; } [JsonPropertyName("modifiedNcfDate")] public required string ModifiedNcfDate { get; init; } [JsonPropertyName("modificationCode")] public string? ModificationCode { get; init; } [JsonPropertyName("otherIssuerRnc")] public string? OtherIssuerRnc { get; init; } } /// Reintento automático de un comprobante tras un rechazo transitorio de la DGII. public sealed record EcfRetry { /// Reenvíos automáticos que NovaFE ya hizo de este comprobante. [JsonPropertyName("count")] public required int Count { get; init; } /// Tope de reenvíos automáticos. Al agotarse, el rechazo queda firme. [JsonPropertyName("max")] public required int Max { get; init; } /// Cuándo es el próximo intento; `null` si no hay uno pendiente. [JsonPropertyName("nextAttemptAt")] public string? NextAttemptAt { get; init; } } /// Datos de embarque (`<InformacionesAdicionales>`). Passthrough. public sealed record EcfShippingPayload { [JsonPropertyName("shipmentDate")] public string? ShipmentDate { get; init; } [JsonPropertyName("shipmentNumber")] public string? ShipmentNumber { get; init; } [JsonPropertyName("containerNumber")] public string? ContainerNumber { get; init; } [JsonPropertyName("referenceNumber")] public string? ReferenceNumber { get; init; } [JsonPropertyName("grossWeight")] public decimal? GrossWeight { get; init; } [JsonPropertyName("netWeight")] public decimal? NetWeight { get; init; } [JsonPropertyName("grossWeightUnit")] public string? GrossWeightUnit { get; init; } [JsonPropertyName("netWeightUnit")] public string? NetWeightUnit { get; init; } [JsonPropertyName("packageCount")] public decimal? PackageCount { get; init; } [JsonPropertyName("packageUnit")] public string? PackageUnit { get; init; } [JsonPropertyName("volume")] public decimal? Volume { get; init; } [JsonPropertyName("volumeUnit")] public string? VolumeUnit { get; init; } [JsonPropertyName("export")] public EcfExportPayload? Export { get; init; } } public sealed record EcfSubquantityPayload { [JsonPropertyName("quantity")] public required decimal Quantity { get; init; } [JsonPropertyName("unitCode")] public required string UnitCode { get; init; } } /// Subtotal informativo para la RI (Sección C). No afecta la base imponible. public sealed record EcfSubtotalPayload { [JsonPropertyName("number")] public int? Number { get; init; } [JsonPropertyName("description")] public string? Description { get; init; } [JsonPropertyName("order")] public int? Order { get; init; } [JsonPropertyName("montoGravadoTotal")] public decimal? MontoGravadoTotal { get; init; } [JsonPropertyName("montoGravadoI1")] public decimal? MontoGravadoI1 { get; init; } [JsonPropertyName("montoGravadoI2")] public decimal? MontoGravadoI2 { get; init; } [JsonPropertyName("montoGravadoI3")] public decimal? MontoGravadoI3 { get; init; } [JsonPropertyName("totalItbis")] public decimal? TotalItbis { get; init; } [JsonPropertyName("itbis1")] public decimal? Itbis1 { get; init; } [JsonPropertyName("itbis2")] public decimal? Itbis2 { get; init; } [JsonPropertyName("itbis3")] public decimal? Itbis3 { get; init; } [JsonPropertyName("montoImpuestoAdicional")] public decimal? MontoImpuestoAdicional { get; init; } [JsonPropertyName("montoExento")] public decimal? MontoExento { get; init; } [JsonPropertyName("amount")] public decimal? Amount { get; init; } [JsonPropertyName("lines")] public int? Lines { get; init; } } /// Fila del listado de comprobantes emitidos. public sealed record EcfSummaryDto { [JsonPropertyName("id")] public required string Id { get; init; } [JsonPropertyName("status")] public required string Status { get; init; } [JsonPropertyName("encf")] public required string Encf { get; init; } [JsonPropertyName("type")] public required int Type { get; init; } [JsonPropertyName("environment")] public required string Environment { get; init; } [JsonPropertyName("issueDate")] public required string IssueDate { get; init; } [JsonPropertyName("montoTotal")] public required decimal MontoTotal { get; init; } [JsonPropertyName("buyerRnc")] public string? BuyerRnc { get; init; } [JsonPropertyName("buyerName")] public string? BuyerName { get; init; } [JsonPropertyName("createdAt")] public required string CreatedAt { get; init; } /// Reintento automático (ver EcfRetry? EcfDto.Retry); `null` si no hubo ninguno. [JsonPropertyName("retry")] public EcfRetry? Retry { get; init; } } /// Datos de transporte (`<Transporte>`). Passthrough. public sealed record EcfTransportPayload { [JsonPropertyName("driver")] public string? Driver { get; init; } [JsonPropertyName("transportDocument")] public string? TransportDocument { get; init; } [JsonPropertyName("vehicleId")] public string? VehicleId { get; init; } [JsonPropertyName("plate")] public string? Plate { get; init; } [JsonPropertyName("route")] public string? Route { get; init; } [JsonPropertyName("zone")] public string? Zone { get; init; } [JsonPropertyName("deliveryNote")] public string? DeliveryNote { get; init; } [JsonPropertyName("via")] public string? Via { get; init; } [JsonPropertyName("originCountry")] public string? OriginCountry { get; init; } [JsonPropertyName("destinationAddress")] public string? DestinationAddress { get; init; } [JsonPropertyName("destinationCountry")] public string? DestinationCountry { get; init; } [JsonPropertyName("carrierRnc")] public string? CarrierRnc { get; init; } [JsonPropertyName("carrierName")] public string? CarrierName { get; init; } [JsonPropertyName("voyageNumber")] public string? VoyageNumber { get; init; } } /// Payload de emisión de un e-CF (`POST /api/v1/ecf`). Un solo objeto discriminado por int IssueEcfCommand.Type; el servidor asigna la secuencia, arma el bloque Emisor desde el perfil del tenant, calcula los totales, firma y persiste. public sealed record IssueEcfCommand { /// Código DGII del tipo de e-CF: 31, 32, 33, 34, 41, 43, 44, 45, 46, 47. [JsonPropertyName("type")] public required int Type { get; init; } /// e-NCF de 13 caracteres. Solo con `sequences.mode = external` (y obligatorio ahí); con `managed` se rechaza, NovaFE asigna el suyo. [JsonPropertyName("encf")] public string? Encf { get; init; } /// `<FechaVencimientoSecuencia>` del e-NCF. Con `sequences.mode = external` es obligatorio en los tipos que llevan vencimiento (todos menos 32 y 34) y no aplica en esos dos; en `managed` se rechaza. [JsonPropertyName("sequenceExpiresOn")] public string? SequenceExpiresOn { get; init; } /// Fecha de emisión (calendario dominicano). Default: hoy. [JsonPropertyName("issueDate")] public string? IssueDate { get; init; } /// `<TipoIngresos>` — "01"…"06". Obligatorio en 31/32/33/34/44/45/46. [JsonPropertyName("incomeType")] public string? IncomeType { get; init; } /// `true` si los precios de las líneas ya traen el ITBIS incluido. [JsonPropertyName("pricesIncludeTax")] public bool? PricesIncludeTax { get; init; } /// `<IndicadorEnvioDiferido>` — solo contribuyentes autorizados. [JsonPropertyName("deferredDelivery")] public bool? DeferredDelivery { get; init; } /// `<MontoNoFacturable>` — reembolsos, propina voluntaria. Puede ser negativo. [JsonPropertyName("nonInvoiceableAmount")] public decimal? NonInvoiceableAmount { get; init; } /// `<NumeroFacturaInterna>` — clave de dedup de negocio. [JsonPropertyName("internalNumber")] public string? InternalNumber { get; init; } /// `<CodigoVendedor>`. [JsonPropertyName("sellerCode")] public string? SellerCode { get; init; } [JsonPropertyName("additionalInfo")] public EcfAdditionalInfoPayload? AdditionalInfo { get; init; } [JsonPropertyName("buyer")] public EcfBuyerPayload? Buyer { get; init; } [JsonPropertyName("payment")] public EcfPaymentPayload? Payment { get; init; } [JsonPropertyName("lines")] public required IReadOnlyList Lines { get; init; } [JsonPropertyName("reference")] public EcfReferencePayload? Reference { get; init; } [JsonPropertyName("globalAdjustments")] public IReadOnlyList? GlobalAdjustments { get; init; } [JsonPropertyName("foreignCurrency")] public EcfForeignCurrencyPayload? ForeignCurrency { get; init; } [JsonPropertyName("shipping")] public EcfShippingPayload? Shipping { get; init; } [JsonPropertyName("transport")] public EcfTransportPayload? Transport { get; init; } [JsonPropertyName("subtotals")] public IReadOnlyList? Subtotals { get; init; } [JsonPropertyName("pagination")] public IReadOnlyList? Pagination { get; init; } /// Totales declarados por el cliente (chequeo de tolerancia; nunca bloquea). [JsonPropertyName("declaredTotals")] public EcfDeclaredTotalsPayload? DeclaredTotals { get; init; } } public sealed record PagedResultOfEcfSummaryDto { [JsonPropertyName("items")] public required IReadOnlyList Items { get; init; } [JsonPropertyName("totalCount")] public required int TotalCount { get; init; } [JsonPropertyName("page")] public required int Page { get; init; } [JsonPropertyName("pageSize")] public required int PageSize { get; init; } [JsonPropertyName("totalPages")] public int? TotalPages { get; init; } [JsonPropertyName("hasNextPage")] public bool? HasNextPage { get; init; } [JsonPropertyName("hasPreviousPage")] public bool? HasPreviousPage { get; init; } } public sealed record RepresentationLayout { } /// Un error o aviso de `POST /api/v1/ecf/validate`. public sealed record ValidateEcfIssueDto { /// Campo del payload al que se refiere (p. ej. `Lines[0].Quantity`); `null` si es de todo el comprobante. [JsonPropertyName("field")] public string? Field { get; init; } /// Código estable, apto para lógica del cliente. [JsonPropertyName("code")] public required string Code { get; init; } /// Descripción lista para mostrar. [JsonPropertyName("message")] public required string Message { get; init; } } /// Vista previa de `POST /api/v1/ecf/validate` para un payload válido: pasó la matriz de validación por tipo (Módulo 2) y el cálculo de totales (Módulo 6) sin asignar una secuencia real, firmar ni persistir nada. public sealed record ValidateEcfPreviewDto { [JsonPropertyName("type")] public required int Type { get; init; } [JsonPropertyName("typeName")] public required string TypeName { get; init; } /// Placeholder (`E{tipo}0000000000`) — nunca un e-NCF real, no se asignó ninguna secuencia para construir este documento. [JsonPropertyName("sampleEncf")] public required string SampleEncf { get; init; } [JsonPropertyName("issueDate")] public required string IssueDate { get; init; } /// Estimado (31-dic del año siguiente a DateOnly ValidateEcfPreviewDto.IssueDate) cuando el tipo lo lleva; no viene de una secuencia real. `null` en los tipos que no lo llevan (32, 34). [JsonPropertyName("sequenceExpiresOnEstimate")] public string? SequenceExpiresOnEstimate { get; init; } [JsonPropertyName("montoGravadoTotal")] public required decimal MontoGravadoTotal { get; init; } [JsonPropertyName("montoExento")] public required decimal MontoExento { get; init; } [JsonPropertyName("totalItbis")] public required decimal TotalItbis { get; init; } [JsonPropertyName("montoImpuestoAdicional")] public required decimal MontoImpuestoAdicional { get; init; } [JsonPropertyName("montoTotal")] public required decimal MontoTotal { get; init; } /// Los totales quedarían fuera de la tolerancia de cuadratura (RF-06.6) — la DGII probablemente aceptaría el comprobante de forma condicional si se emite así. [JsonPropertyName("expectConditionalAcceptance")] public required bool ExpectConditionalAcceptance { get; init; } } /// Resultado de `POST /api/v1/ecf/validate`. Siempre llega con `200` cuando el cuerpo se pudo leer y el contribuyente puede validar: bool ValidateEcfResultDto.Valid dice si el comprobante pasaría la validación de forma y la matriz estructural y fiscal por tipo (Módulo 2 + 6) que corre `POST /ecf`. No asigna una secuencia real, no firma y no persiste nada. Lo que impide validar (sin autenticar, perfil del emisor sin configurar) sigue siendo un error HTTP. public sealed record ValidateEcfResultDto { /// `true` si el comprobante se podría emitir tal cual en cuanto a su contenido. [JsonPropertyName("valid")] public required bool Valid { get; init; } /// Resumen en una frase, listo para mostrar. [JsonPropertyName("message")] public required string Message { get; init; } /// Todos los problemas encontrados, no solo el primero. Vacío si bool ValidateEcfResultDto.Valid. [JsonPropertyName("errors")] public required IReadOnlyList Errors { get; init; } /// Avisos que no impiden emitir (p. ej. aceptación condicional probable). [JsonPropertyName("warnings")] public required IReadOnlyList Warnings { get; init; } /// La vista previa de lo que se emitiría; `null` si el comprobante no es válido. [JsonPropertyName("preview")] public ValidateEcfPreviewDto? Preview { get; init; } }