useEnvioSRI.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. import { useState } from "react"
  2. import { toast } from "sonner"
  3. import type { EstadoEnvio, RespuestaVerificacionSRI } from "@/types/envio-sri"
  4. import { extractInfoFromXml } from "@/lib/xml-utils"
  5. export function useEnvioSRI() {
  6. const [xmlFile, setXmlFile] = useState<File | null>(null)
  7. const [p12File, setP12File] = useState<File | null>(null)
  8. const [password, setPassword] = useState("")
  9. const [signedXml, setSignedXml] = useState<string | null>(null)
  10. const [accessKey, setAccessKey] = useState<string>("")
  11. const [ambiente, setAmbiente] = useState<'1' | '2'>('1') // 1=Pruebas, 2=Producción
  12. const [isLoading, setIsLoading] = useState(false)
  13. const [estadoEnvio, setEstadoEnvio] = useState<EstadoEnvio>("idle")
  14. const [respuestaAutorizacion, setRespuestaAutorizacion] = useState<RespuestaVerificacionSRI | null>(null)
  15. // Paso 1: Firmar el XML (opcional si ya viene firmado)
  16. const handleSign = async () => {
  17. if (!xmlFile || !p12File || !password) {
  18. toast.error("Por favor completa todos los campos para firmar")
  19. return false
  20. }
  21. setIsLoading(true)
  22. setSignedXml(null)
  23. try {
  24. const formData = new FormData()
  25. formData.append("xml", xmlFile)
  26. formData.append("p12", p12File)
  27. formData.append("password", password)
  28. const response = await fetch("/api/sign-invoice", {
  29. method: "POST",
  30. body: formData,
  31. })
  32. const data = await response.json()
  33. if (!response.ok) {
  34. throw new Error(data.details || data.error || "Error al firmar")
  35. }
  36. setSignedXml(data.signedXml)
  37. toast.success("XML firmado exitosamente")
  38. return true
  39. } catch (error) {
  40. console.error(error)
  41. toast.error(error instanceof Error ? error.message : "Error al firmar el XML")
  42. return false
  43. } finally {
  44. setIsLoading(false)
  45. }
  46. }
  47. // Paso 2: Enviar al SRI
  48. const handleEnviarSRI = async () => {
  49. if (!signedXml) {
  50. toast.error("Primero debes firmar el XML o cargar uno firmado")
  51. return false
  52. }
  53. if (!accessKey.trim()) {
  54. toast.error("Debes ingresar la clave de acceso del documento")
  55. return false
  56. }
  57. setIsLoading(true)
  58. setEstadoEnvio("enviando")
  59. try {
  60. const response = await fetch("/api/send-to-sri", {
  61. method: "POST",
  62. headers: {
  63. "Content-Type": "application/json",
  64. },
  65. body: JSON.stringify({ signedXml }),
  66. })
  67. const data = await response.json()
  68. if (!response.ok) {
  69. throw new Error(data.details || data.error || "Error al enviar")
  70. }
  71. // Actualizar el estado según la respuesta del SRI
  72. setEstadoEnvio(data.estado)
  73. // Si hay mensajes (errores/advertencias), guardarlos en respuestaAutorizacion
  74. if (data.mensajes && data.mensajes.length > 0) {
  75. setRespuestaAutorizacion({
  76. success: true,
  77. estado: data.estado,
  78. mensajes: data.mensajes,
  79. numeroAutorizacion: data.claveAcceso, // Usamos la clave de acceso como referencia
  80. })
  81. }
  82. if (data.estado === "devuelto") {
  83. toast.error("Documento devuelto por el SRI. Revisa los mensajes de error.")
  84. } else if (data.estado === "verificando") {
  85. toast.success("Documento enviado al SRI, verificando estado...")
  86. // Automáticamente verificar el estado
  87. await handleVerificarEstado()
  88. }
  89. return true
  90. } catch (error) {
  91. console.error(error)
  92. toast.error(error instanceof Error ? error.message : "Error al enviar al SRI")
  93. setEstadoEnvio("error")
  94. return false
  95. } finally {
  96. setIsLoading(false)
  97. }
  98. }
  99. // Paso 3: Verificar estado
  100. const handleVerificarEstado = async () => {
  101. if (!accessKey.trim()) {
  102. toast.error("Debes ingresar la clave de acceso")
  103. return false
  104. }
  105. setIsLoading(true)
  106. setEstadoEnvio("verificando")
  107. try {
  108. const response = await fetch("/api/poll-sri", {
  109. method: "POST",
  110. headers: {
  111. "Content-Type": "application/json",
  112. },
  113. body: JSON.stringify({ accessKey }),
  114. })
  115. const data: RespuestaVerificacionSRI = await response.json()
  116. if (!response.ok) {
  117. throw new Error(data.details || data.error || "Error al verificar")
  118. }
  119. setRespuestaAutorizacion(data)
  120. setEstadoEnvio(data.estado)
  121. if (data.estado === "autorizado") {
  122. toast.success("¡Documento autorizado por el SRI!")
  123. } else if (data.estado === "devuelto") {
  124. toast.warning("Documento devuelto por el SRI")
  125. } else if (data.estado === "no_autorizado") {
  126. toast.error("Documento no autorizado por el SRI")
  127. }
  128. return true
  129. } catch (error) {
  130. console.error(error)
  131. toast.error(error instanceof Error ? error.message : "Error al verificar estado")
  132. setEstadoEnvio("error")
  133. return false
  134. } finally {
  135. setIsLoading(false)
  136. }
  137. }
  138. // Cargar un XML firmado directamente
  139. const handleLoadSignedXml = async (file: File) => {
  140. try {
  141. const content = await file.text()
  142. // Extraer información del XML automáticamente
  143. const info = extractInfoFromXml(content)
  144. if (!info.claveAcceso) {
  145. toast.error("No se pudo encontrar la clave de acceso en el XML")
  146. return
  147. }
  148. // Validar que la clave de acceso tenga 49 dígitos
  149. if (info.claveAcceso.length !== 49) {
  150. toast.error("La clave de acceso debe tener 49 dígitos")
  151. return
  152. }
  153. setSignedXml(content)
  154. setXmlFile(file)
  155. setAccessKey(info.claveAcceso)
  156. // Establecer el ambiente si se encuentra en el XML
  157. if (info.ambiente) {
  158. setAmbiente(info.ambiente)
  159. }
  160. toast.success(`XML cargado: ${file.name}`)
  161. toast.info(`Clave de acceso extraída: ${info.claveAcceso.substring(0, 10)}...`)
  162. } catch (error) {
  163. toast.error("Error al leer el archivo XML")
  164. }
  165. }
  166. // Descargar XML firmado
  167. const handleDownloadSigned = () => {
  168. if (!signedXml) return
  169. const blob = new Blob([signedXml], { type: "application/xml" })
  170. const url = URL.createObjectURL(blob)
  171. const a = document.createElement("a")
  172. a.href = url
  173. a.download = `factura_firmada_${new Date().getTime()}.xml`
  174. document.body.appendChild(a)
  175. a.click()
  176. document.body.removeChild(a)
  177. URL.revokeObjectURL(url)
  178. toast.success("XML firmado descargado")
  179. }
  180. // Descargar comprobante autorizado
  181. const handleDownloadAutorizado = () => {
  182. if (!respuestaAutorizacion?.comprobante) return
  183. const blob = new Blob([respuestaAutorizacion.comprobante], { type: "application/xml" })
  184. const url = URL.createObjectURL(blob)
  185. const a = document.createElement("a")
  186. a.href = url
  187. a.download = `factura_autorizada_${accessKey}.xml`
  188. document.body.appendChild(a)
  189. a.click()
  190. document.body.removeChild(a)
  191. URL.revokeObjectURL(url)
  192. toast.success("Comprobante autorizado descargado")
  193. }
  194. // Reset
  195. const handleReset = () => {
  196. setXmlFile(null)
  197. setP12File(null)
  198. setPassword("")
  199. setSignedXml(null)
  200. setAccessKey("")
  201. setEstadoEnvio("idle")
  202. setRespuestaAutorizacion(null)
  203. }
  204. return {
  205. // State
  206. xmlFile,
  207. p12File,
  208. password,
  209. signedXml,
  210. accessKey,
  211. ambiente,
  212. isLoading,
  213. estadoEnvio,
  214. respuestaAutorizacion,
  215. // Setters
  216. setXmlFile,
  217. setP12File,
  218. setPassword,
  219. setAccessKey,
  220. // Actions
  221. handleSign,
  222. handleEnviarSRI,
  223. handleVerificarEstado,
  224. handleLoadSignedXml,
  225. handleDownloadSigned,
  226. handleDownloadAutorizado,
  227. handleReset,
  228. }
  229. }