{"id":43,"date":"2026-08-08T18:04:53","date_gmt":"2026-08-08T18:04:53","guid":{"rendered":"https:\/\/sanaris.es\/?page_id=43"},"modified":"2026-08-08T18:20:29","modified_gmt":"2026-08-08T18:20:29","slug":"prikodu","status":"publish","type":"page","link":"https:\/\/sanaris.es\/index.php\/prikodu\/","title":{"rendered":"PriKodu"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"43\" class=\"elementor elementor-43\">\n\t\t\t\t<div class=\"elementor-element elementor-element-e4ead12 e-flex e-con-boxed e-con e-parent\" data-id=\"e4ead12\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-5c0b98f elementor-widget elementor-widget-html\" data-id=\"5c0b98f\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!-- PriKodu V12.2 \u00b7 Elementor \u00b7 OCR Espa\u00f1a\/Colombia -->\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/tesseract.js@5\/dist\/tesseract.min.js\"><\/script>\n<style>\n#prikodu-app{\n  --bg:#f5f7fb;--card:#ffffff;--ink:#162033;--muted:#667085;--line:#dce3ef;\n  --green:#10b981;--amber:#f59e0b;--red:#ef4444;--blue:#2563eb;\n  width:100%;margin:0;background:var(--bg);color:var(--ink);\n}\n#prikodu-app, #prikodu-app *{box-sizing:border-box;font-family:Inter,Arial,sans-serif}\n#prikodu-app .wrap{max-width:1280px;margin:0 auto;padding:20px}\n#prikodu-app h1{margin:0 0 6px;font-size:28px;line-height:1.2;color:var(--ink)}\n#prikodu-app .sub{color:var(--muted);margin:0 0 16px}\n#prikodu-app .grid{display:grid;gap:16px}\n#prikodu-app .g2{grid-template-columns:1.1fr .9fr}\n#prikodu-app .g3{grid-template-columns:repeat(3,1fr)}\n#prikodu-app .g4{grid-template-columns:repeat(4,1fr)}\n#prikodu-app .card{background:var(--card);border:1px solid var(--line);border-radius:16px;padding:16px;box-shadow:0 4px 18px rgba(20,30,60,.05)}\n#prikodu-app .card h2{font-size:18px;line-height:1.3;margin:0 0 12px;color:var(--ink)}\n#prikodu-app label{display:block;font-size:13px;font-weight:600;margin-bottom:6px;color:var(--ink)}\n#prikodu-app input,#prikodu-app select,#prikodu-app textarea,#prikodu-app button{width:100%;border:1px solid #cfd8e6;border-radius:10px;padding:10px 12px;font-size:14px;line-height:1.35;background:#fff;box-shadow:none}\n#prikodu-app textarea{min-height:112px;resize:vertical}\n#prikodu-app button{cursor:pointer;border:none;background:var(--blue);color:#fff;font-weight:700}\n#prikodu-app button.secondary{background:#eef4ff;color:#1849a9;border:1px solid #c7d7ff}\n#prikodu-app button.ghost{background:#fff;color:var(--blue);border:1px solid #c7d7ff}\n#prikodu-app .row{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}\n#prikodu-app .tiny{font-size:12px;color:var(--muted)}\n#prikodu-app .kpis{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}\n#prikodu-app .kpi{border:1px solid var(--line);border-radius:14px;padding:14px;background:#fafcff}\n#prikodu-app .kpi .t{font-size:12px;color:var(--muted);margin-bottom:6px}\n#prikodu-app .kpi .v{font-size:24px;font-weight:800}\n#prikodu-app .bigVerdict{display:flex;align-items:center;justify-content:center;min-height:140px;border-radius:18px;color:#fff;font-size:32px;font-weight:800;margin-bottom:16px;text-align:center;padding:20px}\n#prikodu-app .opp{background:linear-gradient(135deg,#0f9f6e,#17c78d)}\n#prikodu-app .neg{background:linear-gradient(135deg,#d68b00,#ffb52c)}\n#prikodu-app .des{background:linear-gradient(135deg,#db3d46,#ff6471)}\n#prikodu-app .na{background:linear-gradient(135deg,#64748b,#94a3b8)}\n#prikodu-app .pill{display:inline-block;padding:6px 10px;border-radius:999px;font-size:12px;font-weight:700}\n#prikodu-app .pill.green{background:#dcfce7;color:#166534}\n#prikodu-app .pill.amber{background:#fef3c7;color:#92400e}\n#prikodu-app .pill.red{background:#fee2e2;color:#991b1b}\n#prikodu-app .pill.slate{background:#e2e8f0;color:#334155}\n#prikodu-app .sectionTitle{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}\n#prikodu-app .hr{height:1px;background:var(--line);margin:16px 0}\n#prikodu-app .alert{padding:10px 12px;border-radius:10px;background:#f8fafc;border:1px solid var(--line);font-size:13px;color:#334155}\n#prikodu-app .ok{background:#ecfdf3;border-color:#bbe8d0}\n#prikodu-app .warn{background:#fffbeb;border-color:#f8df8b}\n#prikodu-app .err{background:#fef2f2;border-color:#f7c0c0}\n#prikodu-app .hidden{display:none}\n@media(max-width:980px){\n  #prikodu-app .g2,#prikodu-app .g3,#prikodu-app .g4,#prikodu-app .kpis{grid-template-columns:1fr}\n}\n<\/style>\n<div id=\"prikodu-app\">\n<div class=\"wrap\">\n  <h1>PriKodu V12 \u00b7 OCR por capturas<\/h1>\n  <p class=\"sub\">Espa\u00f1a y Colombia \u00b7 Subes capturas, PriKodu vuelca el anuncio, t\u00fa corriges si hace falta y analiza la operaci\u00f3n.<\/p>\n\n  <div class=\"grid g2\">\n    <div class=\"grid\">\n      <div class=\"card\">\n        <div class=\"sectionTitle\">\n          <h2>1) Configuraci\u00f3n b\u00e1sica<\/h2>\n          <span id=\"countryBadge\" class=\"pill slate\">Espa\u00f1a<\/span>\n        <\/div>\n        <div class=\"g4 grid\">\n          <div>\n            <label>Pa\u00eds<\/label>\n            <select id=\"country\">\n              <option value=\"ES\">Espa\u00f1a<\/option>\n              <option value=\"CO\">Colombia<\/option>\n            <\/select>\n          <\/div>\n          <div>\n            <label>Moneda<\/label>\n            <input id=\"currency\" value=\"\u20ac\" \/>\n          <\/div>\n          <div>\n            <label>Beneficio m\u00ednimo final<\/label>\n            <input id=\"minProfit\" type=\"number\" value=\"12000\" \/>\n          <\/div>\n          <div>\n            <label>ROI m\u00ednimo final (%)<\/label>\n            <input id=\"minRoi\" type=\"number\" step=\"0.1\" value=\"12\" \/>\n          <\/div>\n        <\/div>\n        <div class=\"g4 grid\" style=\"margin-top:10px\">\n          <div>\n            <label>Plazo estimado (meses)<\/label>\n            <input id=\"holdMonths\" type=\"number\" value=\"8\" \/>\n          <\/div>\n          <div>\n            <label>Financiaci\u00f3n (%)<\/label>\n            <input id=\"ltv\" type=\"number\" step=\"1\" value=\"70\" \/>\n          <\/div>\n          <div>\n            <label>Inter\u00e9s anual (%)<\/label>\n            <input id=\"rate\" type=\"number\" step=\"0.1\" value=\"3.2\" \/>\n          <\/div>\n          <div>\n            <label>Diferencia anuncio \u2192 m\u00e1ximo para \u201cNEGOCIAR\u201d (%)<\/label>\n            <input id=\"negThreshold\" type=\"number\" step=\"0.1\" value=\"12\" \/>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"card\">\n        <div class=\"sectionTitle\">\n          <h2>2) Capturas del anuncio<\/h2>\n          <span class=\"tiny\">Idealista \/ Fotocasa \/ Metrocuadrado \/ FincaRa\u00edz \/ otros<\/span>\n        <\/div>\n        <div class=\"alert warn\">Sube 1\u20133 capturas: cabecera, caracter\u00edsticas y descripci\u00f3n. La lectura del <b>link<\/b> sigue sin ser fiable; la v\u00eda correcta es por <b>captura<\/b>.<\/div>\n        <div style=\"margin-top:10px\" class=\"grid g3\">\n          <div>\n            <label>Capturas<\/label>\n            <input id=\"images\" type=\"file\" accept=\"image\/*\" multiple \/>\n          <\/div>\n          <div>\n            <label>&nbsp;<\/label>\n            <button id=\"ocrBtn\" type=\"button\">Procesar capturas<\/button>\n          <\/div>\n          <div>\n            <label>&nbsp;<\/label>\n            <button class=\"secondary\" id=\"sampleBtn\" type=\"button\">Cargar ejemplo<\/button>\n          <\/div>\n        <\/div>\n        <div id=\"ocrStatus\" class=\"alert\" style=\"margin-top:10px\">Esperando capturas\u2026<\/div>\n        <div style=\"margin-top:10px\">\n          <label>Texto OCR \/ descripci\u00f3n pegada<\/label>\n          <textarea id=\"ocrText\" placeholder=\"Aqu\u00ed aparecer\u00e1 el texto extra\u00eddo o puedes pegar manualmente el anuncio completo\"><\/textarea>\n        <\/div>\n        <div class=\"row\" style=\"margin-top:10px\">\n          <button class=\"ghost\" id=\"parseBtn\" type=\"button\">Volcar datos desde texto<\/button>\n          <button class=\"ghost\" id=\"clearBtn\" type=\"button\">Limpiar<\/button>\n        <\/div>\n      <\/div>\n\n      <div class=\"card\">\n        <div class=\"sectionTitle\"><h2>3) Datos del inmueble<\/h2><span class=\"tiny\">PriKodu rellena y t\u00fa corriges<\/span><\/div>\n        <div class=\"g4 grid\">\n          <div><label>Zona \/ barrio<\/label><input id=\"zone\" placeholder=\"Pego, Daim\u00fas, Niza, Caobos\u2026\" \/><\/div>\n          <div><label>Precio anuncio<\/label><input id=\"askPrice\" type=\"number\" \/><\/div>\n          <div><label>m\u00b2<\/label><input id=\"area\" type=\"number\" \/><\/div>\n          <div><label>Habitaciones<\/label><input id=\"beds\" type=\"number\" \/><\/div>\n        <\/div>\n        <div class=\"g4 grid\" style=\"margin-top:10px\">\n          <div><label>Ba\u00f1os<\/label><input id=\"baths\" type=\"number\" \/><\/div>\n          <div><label>Planta<\/label><input id=\"floor\" placeholder=\"1 \/ 4\u00aa \/ Bajo\" \/><\/div>\n          <div><label>Ascensor<\/label><select id=\"elevator\"><option value=\"\">No detectado<\/option><option>S\u00ed<\/option><option>No<\/option><\/select><\/div>\n          <div><label>Garaje \/ parqueadero<\/label><select id=\"garage\"><option value=\"\">No detectado<\/option><option>S\u00ed<\/option><option>No<\/option><\/select><\/div>\n        <\/div>\n        <div class=\"g4 grid\" style=\"margin-top:10px\">\n          <div><label>Terraza \/ balc\u00f3n<\/label><select id=\"terrace\"><option value=\"\">No detectado<\/option><option>S\u00ed<\/option><option>No<\/option><\/select><\/div>\n          <div><label>Estado<\/label><select id=\"condition\"><option value=\"\">No detectado<\/option><option>Ya presentable<\/option><option>Lavado ligero<\/option><option>Lavado completo<\/option><option>Para reformar \/ fuera estrategia<\/option><\/select><\/div>\n          <div><label>A\u00f1o<\/label><input id=\"year\" type=\"number\" \/><\/div>\n          <div><label>Comunidad \/ administraci\u00f3n mensual<\/label><input id=\"community\" type=\"number\" value=\"0\" \/><\/div>\n        <\/div>\n        <div style=\"margin-top:10px\">\n          <label>Observaciones extra\u00eddas \/ notas<\/label>\n          <textarea id=\"notes\" placeholder=\"Reformado, exterior, piscina, vigilancia, amueblado, etc.\"><\/textarea>\n        <\/div>\n      <\/div>\n\n      <div class=\"card\">\n        <div class=\"sectionTitle\"><h2>4) Valor de mercado y salida<\/h2><span class=\"tiny\">Aqu\u00ed est\u00e1 la clave. Sin esto no hay veredicto serio.<\/span><\/div>\n        <div class=\"g4 grid\">\n          <div><label>Valor de mercado probable<\/label><input id=\"mv\" type=\"number\" \/><\/div>\n          <div><label>Rango m\u00ednimo<\/label><input id=\"mvMin\" type=\"number\" \/><\/div>\n          <div><label>Rango m\u00e1ximo<\/label><input id=\"mvMax\" type=\"number\" \/><\/div>\n          <div><label>Salida conservadora<\/label><input id=\"exitConservative\" type=\"number\" \/><\/div>\n        <\/div>\n        <div class=\"g4 grid\" style=\"margin-top:10px\">\n          <div><label>Tasaci\u00f3n profesional (opcional)<\/label><input id=\"tasacion\" type=\"number\" value=\"0\" \/><\/div>\n          <div><label>Cierres reales microzona \u20ac\/m\u00b2 (opcional)<\/label><input id=\"realM2\" type=\"number\" value=\"0\" \/><\/div>\n          <div><label>m\u00b2 Catastro (opcional)<\/label><input id=\"catM2\" type=\"number\" value=\"0\" \/><\/div>\n          <div><label>Valor de referencia Catastro (opcional)<\/label><input id=\"catRef\" type=\"number\" value=\"0\" \/><\/div>\n        <\/div>\n        <div class=\"g4 grid\" style=\"margin-top:10px\">\n          <div><label>D\u00edas en mercado (opcional)<\/label><input id=\"daysMarket\" type=\"number\" value=\"0\" \/><\/div>\n          <div><label>Rebaja acumulada (%)<\/label><input id=\"discountPct\" type=\"number\" value=\"0\" \/><\/div>\n          <div><label>Confianza base<\/label><select id=\"confidenceBase\"><option>Alta<\/option><option selected>Media<\/option><option>Baja<\/option><\/select><\/div>\n          <div><label>Descripci\u00f3n de comparables<\/label><input id=\"compInfo\" placeholder=\"Mismo edificio \/ microzona \/ ofertas\" \/><\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"card\">\n        <div class=\"sectionTitle\"><h2>5) Costes<\/h2><span class=\"tiny\">Lavado de cara, no reforma integral<\/span><\/div>\n        <div class=\"g4 grid\">\n          <div><label>Puesta a punto<\/label>\n            <select id=\"refreshPreset\">\n              <option value=\"manual\">Manual<\/option>\n              <option value=\"presentable\">Ya presentable<\/option>\n              <option value=\"ligero\" selected>Lavado ligero<\/option>\n              <option value=\"completo\">Lavado completo<\/option>\n            <\/select>\n          <\/div>\n          <div><label>Coste puesta a punto<\/label><input id=\"refreshCost\" type=\"number\" value=\"3500\" \/><\/div>\n          <div><label>Coste compra variable (%)<\/label><input id=\"buyPct\" type=\"number\" step=\"0.1\" value=\"9\" \/><\/div>\n          <div><label>Coste compra fijo<\/label><input id=\"buyFixed\" type=\"number\" value=\"1800\" \/><\/div>\n        <\/div>\n        <div class=\"g4 grid\" style=\"margin-top:10px\">\n          <div><label>Coste venta (%)<\/label><input id=\"sellPct\" type=\"number\" step=\"0.1\" value=\"5\" \/><\/div>\n          <div><label>Coste venta fijo<\/label><input id=\"sellFixed\" type=\"number\" value=\"0\" \/><\/div>\n          <div><label>Otros costes operaci\u00f3n<\/label><input id=\"otherCosts\" type=\"number\" value=\"0\" \/><\/div>\n          <div><label>Impuesto\/retenci\u00f3n sobre venta (%)<\/label><input id=\"saleTaxPct\" type=\"number\" step=\"0.1\" value=\"0.5\" \/><\/div>\n        <\/div>\n        <div class=\"row\" style=\"margin-top:10px\">\n          <div>\n            <label><input id=\"gainTaxToggle\" type=\"checkbox\" checked style=\"width:auto;margin-right:8px\" \/> Estimar impuesto sobre ganancia<\/label>\n            <div class=\"tiny\">Espa\u00f1a: escala ahorro aprox. \u00b7 Colombia: editable v\u00eda retenci\u00f3n\/otros si prefieres no estimarlo.<\/div>\n          <\/div>\n          <div>\n            <label>&nbsp;<\/label>\n            <button id=\"calcBtn\" type=\"button\">Analizar operaci\u00f3n<\/button>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/div>\n\n    <div class=\"grid\">\n      <div class=\"card\">\n        <div id=\"verdictBox\" class=\"bigVerdict na\">SIN DATOS<\/div>\n        <div class=\"kpis\">\n          <div class=\"kpi\"><div class=\"t\">Precio m\u00e1ximo PriKodu<\/div><div id=\"kpiMaxBuy\" class=\"v\">\u2014<\/div><\/div>\n          <div class=\"kpi\"><div class=\"t\">Beneficio final estimado<\/div><div id=\"kpiProfit\" class=\"v\">\u2014<\/div><\/div>\n          <div class=\"kpi\"><div class=\"t\">Oferta inicial<\/div><div id=\"kpiOffer\" class=\"v\">\u2014<\/div><\/div>\n        <\/div>\n        <div class=\"kpis\" style=\"margin-top:12px\">\n          <div class=\"kpi\"><div class=\"t\">Salida conservadora<\/div><div id=\"kpiExit\" class=\"v\">\u2014<\/div><\/div>\n          <div class=\"kpi\"><div class=\"t\">ROI final<\/div><div id=\"kpiRoi\" class=\"v\">\u2014<\/div><\/div>\n          <div class=\"kpi\"><div class=\"t\">Capital propio<\/div><div id=\"kpiEquity\" class=\"v\">\u2014<\/div><\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"card\">\n        <h2>Resumen simple<\/h2>\n        <div id=\"summary\" class=\"alert\">PriKodu necesita: <b>precio anuncio<\/b> y <b>salida conservadora<\/b>. Las capturas sirven para rellenar el anuncio; la salida conservadora debe venir de comparables\/mercado.<\/div>\n        <div class=\"hr\"><\/div>\n        <div class=\"g3 grid\">\n          <div class=\"kpi\"><div class=\"t\">Valor mercado<\/div><div id=\"kpiMV\" class=\"v\">\u2014<\/div><\/div>\n          <div class=\"kpi\"><div class=\"t\">Rango<\/div><div id=\"kpiRange\" class=\"v\" style=\"font-size:18px\">\u2014<\/div><\/div>\n          <div class=\"kpi\"><div class=\"t\">Confianza<\/div><div id=\"kpiConf\" class=\"v\" style=\"font-size:20px\">\u2014<\/div><\/div>\n        <\/div>\n      <\/div>\n\n      <div class=\"card\">\n        <h2>Desglose econ\u00f3mico<\/h2>\n        <div id=\"breakdown\" class=\"tiny\">Sin c\u00e1lculo todav\u00eda.<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n<\/div>\n<script>\nconst $ = (id)=>document.getElementById(id);\nconst fields = ['zone','askPrice','area','beds','baths','floor','elevator','garage','terrace','condition','year','community','notes','mv','mvMin','mvMax','exitConservative'];\n\nfunction setCountryDefaults(country){\n  if(country==='ES'){\n    $('currency').value='\u20ac'; $('minProfit').value=12000; $('minRoi').value=12; $('holdMonths').value=8; $('ltv').value=70;\n    $('rate').value=3.2; $('buyPct').value=9; $('buyFixed').value=1800; $('sellPct').value=5; $('sellFixed').value=0; $('saleTaxPct').value=0.5;\n    $('refreshCost').value=3500; $('countryBadge').textContent='Espa\u00f1a';\n  } else {\n    $('currency').value='COP'; $('minProfit').value=20000000; $('minRoi').value=12; $('holdMonths').value=8; $('ltv').value=70;\n    $('rate').value=13; $('buyPct').value=2.5; $('buyFixed').value=1500000; $('sellPct').value=5; $('sellFixed').value=0; $('saleTaxPct').value=1.0;\n    $('refreshCost').value=8000000; $('countryBadge').textContent='Colombia';\n  }\n  applyRefreshPreset();\n}\n\nfunction applyRefreshPreset(){\n  const country=$('country').value, preset=$('refreshPreset').value;\n  const map = country==='ES'\n    ? {presentable:1500,ligero:3500,completo:6500}\n    : {presentable:3000000,ligero:8000000,completo:15000000};\n  if(preset==='manual') return;\n  $('refreshCost').value = map[preset] || 0;\n  if(preset==='presentable' && !$('condition').value) $('condition').value='Ya presentable';\n  if(preset==='ligero' && (!$('condition').value || $('condition').value==='Ya presentable')) $('condition').value='Lavado ligero';\n  if(preset==='completo') $('condition').value='Lavado completo';\n}\n\nfunction normalizeSpaces(s){return s.replace(\/\\s+\/g,' ').trim();}\nfunction parseNum(s){\n  if(s==null||s==='') return 0;\n  if(typeof s==='number') return s;\n  s=(''+s).replace(\/\\s\/g,'').replace(\/\u20ac\/g,'').replace(\/cop\/ig,'').replace(\/\\$\/g,'');\n  s=s.replace(\/\\.(?=\\d{3}(\\D|$))\/g,'').replace(\/,(?=\\d{3}(\\D|$))\/g,'');\n  s=s.replace(\/,\/g,'.');\n  const n=parseFloat(s.replace(\/[^\\d.-]\/g,''));\n  return isNaN(n)?0:n;\n}\nfunction fmt(n){\n  if(!isFinite(n)||n===0) return '\u2014';\n  const cur=$('currency').value;\n  if(cur==='\u20ac') return new Intl.NumberFormat('es-ES',{style:'currency',currency:'EUR',maximumFractionDigits:0}).format(n);\n  return new Intl.NumberFormat('es-CO',{style:'currency',currency:'COP',maximumFractionDigits:0}).format(n);\n}\nfunction fmtPct(n){return (!isFinite(n)?'\u2014':(n.toFixed(1)+'%'));}\n\nfunction gainTaxEstimate(country,gain){\n  if(gain<=0 || !$('gainTaxToggle').checked) return 0;\n  if(country==='ES'){\n    let tax=0, g=gain;\n    const bands=[[6000,0.19],[44000,0.21],[150000,0.23],[100000,0.27],[Infinity,0.30]];\n    for(const [limit,rate] of bands){\n      const chunk=Math.min(g,limit); if(chunk<=0) break; tax += chunk*rate; g -= chunk; if(g<=0) break;\n    }\n    return tax;\n  }\n  return 0; \/\/ Colombia: por defecto no estimo ganancia salvo retenci\u00f3n\/otros\n}\n\nfunction confidenceScore(){\n  let score = {'Alta':85,'Media':68,'Baja':50}[$('confidenceBase').value] || 60;\n  if(parseNum($('tasacion').value)>0) score += 8;\n  if(parseNum($('realM2').value)>0 && parseNum($('area').value)>0) score += 6;\n  if(parseNum($('catM2').value)>0) score += 3;\n  if(parseNum($('daysMarket').value)>0) score += 2;\n  const ask=parseNum($('askPrice').value), mv=parseNum($('mv').value);\n  const cat=parseNum($('catRef').value);\n  if(cat>0 && mv>0){ const diff=Math.abs(cat-mv)\/mv; if(diff>0.25) score-=6; }\n  if(ask>0 && mv>0){ const diff=Math.abs(ask-mv)\/mv; if(diff>0.25) score-=4; }\n  return Math.max(35,Math.min(95,score));\n}\n\nfunction classify(conf){ if(conf>=80) return 'Alta'; if(conf>=60) return 'Media'; return 'Baja'; }\n\nfunction calcAtPrice(buyPrice){\n  const country=$('country').value;\n  const exit=parseNum($('exitConservative').value);\n  const ltv=parseNum($('ltv').value)\/100;\n  const rate=parseNum($('rate').value)\/100;\n  const holdMonths=parseNum($('holdMonths').value);\n  const buyPct=parseNum($('buyPct').value)\/100;\n  const buyFixed=parseNum($('buyFixed').value);\n  const sellPct=parseNum($('sellPct').value)\/100;\n  const sellFixed=parseNum($('sellFixed').value);\n  const refresh=parseNum($('refreshCost').value);\n  const other=parseNum($('otherCosts').value);\n  const comm=parseNum($('community').value);\n  const saleTaxPct=parseNum($('saleTaxPct').value)\/100;\n\n  const loan=buyPrice*ltv;\n  const equity=buyPrice-loan;\n  const financingCost=loan*rate*(holdMonths\/12);\n  const buyCosts=buyPrice*buyPct + buyFixed;\n  const holding=comm*holdMonths + financingCost;\n  const totalInvested=buyPrice + buyCosts + refresh + other + holding;\n  const saleCosts=exit*sellPct + sellFixed + exit*saleTaxPct;\n  const profitBeforeGainTax=exit - saleCosts - totalInvested;\n  const gainTax=gainTaxEstimate(country,profitBeforeGainTax);\n  const finalProfit=profitBeforeGainTax - gainTax;\n  const netSale=exit-saleCosts-gainTax;\n  const roi = totalInvested>0 ? (finalProfit\/totalInvested)*100 : 0;\n  const ownCashNeeded = equity + buyCosts + refresh + other + (comm*holdMonths) + financingCost;\n  const cashRoi = ownCashNeeded>0 ? (finalProfit\/ownCashNeeded)*100 : 0;\n  return {country, buyPrice, exit, loan, equity, financingCost, buyCosts, refresh, other, holding, totalInvested, saleCosts, profitBeforeGainTax, gainTax, finalProfit, netSale, roi, ownCashNeeded, cashRoi};\n}\n\nfunction computeMaxBuy(){\n  const exit=parseNum($('exitConservative').value);\n  const minProfit=parseNum($('minProfit').value);\n  const minRoi=parseNum($('minRoi').value);\n  if(exit<=0) return 0;\n  let best=0;\n  for(let p=1000; p<=exit; p += Math.max(1, Math.round(exit\/500))){\n    const r=calcAtPrice(p);\n    if(r.finalProfit>=minProfit && r.roi>=minRoi) best=p;\n  }\n  return best;\n}\n\nfunction updateResult(){\n  const ask=parseNum($('askPrice').value), exit=parseNum($('exitConservative').value), mv=parseNum($('mv').value), mvMin=parseNum($('mvMin').value), mvMax=parseNum($('mvMax').value);\n  if(!ask){ showNA('Falta precio del anuncio'); return; }\n  if(!exit){ showNA('Falta salida conservadora'); return; }\n  const maxBuy=computeMaxBuy();\n  const res=calcAtPrice(ask);\n  const conf=confidenceScore();\n  const confLabel=classify(conf);\n  const negThreshold=parseNum($('negThreshold').value);\n  const gapPct = ask>0 && maxBuy>0 ? ((ask-maxBuy)\/ask)*100 : 999;\n  let verdict='DESCARTAR', cls='des';\n  if(maxBuy>=ask){ verdict='OPORTUNIDAD'; cls='opp'; }\n  else if(gapPct<=negThreshold){ verdict='NEGOCIAR'; cls='neg'; }\n  else { verdict='DESCARTAR'; cls='des'; }\n\n  $('verdictBox').className='bigVerdict '+cls;\n  $('verdictBox').textContent=verdict;\n  $('kpiMaxBuy').textContent=fmt(maxBuy);\n  $('kpiProfit').textContent=fmt(res.finalProfit);\n  const offer = maxBuy>0 ? Math.max(0, Math.round(maxBuy*0.95)) : 0;\n  $('kpiOffer').textContent=fmt(offer);\n  $('kpiExit').textContent=fmt(exit);\n  $('kpiRoi').textContent=fmtPct(res.roi);\n  $('kpiEquity').textContent=fmt(res.ownCashNeeded);\n  $('kpiMV').textContent=fmt(mv || exit);\n  $('kpiRange').textContent=(mvMin&&mvMax)? `${fmt(mvMin)} \u2013 ${fmt(mvMax)}` : '\u2014';\n  $('kpiConf').textContent=`${confLabel} \u00b7 \u00b1${Math.max(4,Math.round((100-conf)\/4))}%`;\n\n  const relation = maxBuy>0 ? `Anuncio ${fmt(ask)} \u00b7 M\u00e1ximo PriKodu ${fmt(maxBuy)} \u00b7 diferencia ${fmt(ask-maxBuy)} (${gapPct.toFixed(1)}%)` : 'Sin precio m\u00e1ximo';\n  $('summary').className = 'alert ' + (verdict==='OPORTUNIDAD'?'ok':verdict==='NEGOCIAR'?'warn':'err');\n  $('summary').innerHTML = `<b>${verdict}<\/b><br>${relation}<br>Salida conservadora: <b>${fmt(exit)}<\/b> \u00b7 Beneficio final estimado al precio anuncio: <b>${fmt(res.finalProfit)}<\/b> \u00b7 ROI final: <b>${fmtPct(res.roi)}<\/b>`;\n\n  $('breakdown').innerHTML = `\n    <b>Compra anuncio:<\/b> ${fmt(ask)}<br>\n    <b>Costes compra:<\/b> ${fmt(res.buyCosts)}<br>\n    <b>Puesta a punto:<\/b> ${fmt(res.refresh)}<br>\n    <b>Otros costes:<\/b> ${fmt(res.other)}<br>\n    <b>Coste financiaci\u00f3n + mantenimiento:<\/b> ${fmt(res.holding)}<br>\n    <b>Inversi\u00f3n total:<\/b> ${fmt(res.totalInvested)}<br>\n    <b>Costes de venta + retenci\u00f3n:<\/b> ${fmt(res.saleCosts)}<br>\n    <b>Impuesto sobre ganancia estimado:<\/b> ${fmt(res.gainTax)}<br>\n    <b>Neto recibido al vender:<\/b> ${fmt(res.netSale)}<br>\n    <b>Beneficio final:<\/b> ${fmt(res.finalProfit)}<br>\n    <b>Capital propio necesario:<\/b> ${fmt(res.ownCashNeeded)}<br>\n    <b>ROI sobre capital propio:<\/b> ${fmtPct(res.cashRoi)}\n  `;\n}\n\nfunction showNA(msg){\n  $('verdictBox').className='bigVerdict na'; $('verdictBox').textContent='SIN VEREDICTO';\n  ['kpiMaxBuy','kpiProfit','kpiOffer','kpiExit','kpiRoi','kpiEquity','kpiMV','kpiRange','kpiConf'].forEach(id=>$(id).textContent='\u2014');\n  $('summary').className='alert'; $('summary').textContent=msg;\n  $('breakdown').textContent='Sin c\u00e1lculo todav\u00eda.';\n}\n\nfunction clearForm(){\n  ['ocrText','zone','notes','floor','year'].forEach(id=>$(id).value='');\n  ['askPrice','area','beds','baths','community','mv','mvMin','mvMax','exitConservative','tasacion','realM2','catM2','catRef','daysMarket','discountPct'].forEach(id=>$(id).value='');\n  ['elevator','garage','terrace','condition'].forEach(id=>$(id).value='');\n  $('ocrStatus').className='alert'; $('ocrStatus').textContent='Esperando capturas\u2026';\n  showNA('PriKodu necesita: precio anuncio y salida conservadora.');\n}\n\nfunction setIfEmpty(id,val){ if(val!==undefined && val!==null && val!=='' && !$(id).value) $(id).value = val; }\nfunction yesNo(b){ return b ? 'S\u00ed':'No'; }\n\nfunction extractFromText(){\n  let txt = normalizeSpaces(($('ocrText').value||'').replace(\/\\u20ac\/g,'\u20ac'));\n  if(!txt){ $('ocrStatus').className='alert err'; $('ocrStatus').textContent='No hay texto para analizar.'; return; }\n  const low=txt.toLowerCase();\n\n  \/\/ Price: take the largest reasonable money amount in text.\n  const priceCandidates=[];\n  const euroRegex=\/((?:\\d{1,3}(?:[.,]\\d{3})+|\\d{4,9})(?:[.,]\\d{2})?)\\s*(\u20ac|eur|euros)\/ig;\n  const copRegex=\/((?:\\d{1,3}(?:[.,]\\d{3})+|\\d{4,10})(?:[.,]\\d{2})?)\\s*(cop|pesos|\\$)\/ig;\n  let m;\n  while((m=euroRegex.exec(txt))!==null) priceCandidates.push(parseNum(m[1]));\n  while((m=copRegex.exec(txt))!==null) priceCandidates.push(parseNum(m[1]));\n  const millones=txt.match(\/(\\d+(?:[.,]\\d+)?)\\s*millones?\/i);\n  if(millones) priceCandidates.push(parseNum(millones[1])*1000000);\n  const bestPrice = priceCandidates.length ? Math.max(...priceCandidates.filter(n=>n>1000)) : 0;\n  setIfEmpty('askPrice', bestPrice||'');\n\n  const area = txt.match(\/(\\d{1,4})\\s*m\u00b2\/i); setIfEmpty('area', area?area[1]:'');\n  const beds = txt.match(\/(\\d+)\\s*hab\/i) || txt.match(\/(\\d+)\\s*dorm\/i) || txt.match(\/(\\d+)\\s*habitaciones?\/i); setIfEmpty('beds', beds?beds[1]:'');\n  const baths = txt.match(\/(\\d+)\\s*ba\u00f1\/i) || txt.match(\/(\\d+)\\s*banos?\/i); setIfEmpty('baths', baths?baths[1]:'');\n  const year = txt.match(\/(?:construid[oa]|a\u00f1o|ano)\\s*(?:en)?\\s*(19\\d{2}|20\\d{2})\/i); setIfEmpty('year', year?year[1]:'');\n  const floor = txt.match(\/(bajo|entreplanta|\\d+\u00aa?\\s*planta|piso\\s*\\d+|\\d+\u00ba?)\/i); setIfEmpty('floor', floor?floor[1]:'');\n\n  if(low.includes('ascensor')) $('elevator').value = low.includes('sin ascensor') ? 'No' : 'S\u00ed';\n  if(\/garaje|parqueadero|parking\/.test(low)) $('garage').value = \/sin garaje|sin parqueadero\/.test(low) ? 'No' : 'S\u00ed';\n  if(\/terraza|balc[o\u00f3]n|balcon\/.test(low)) $('terrace').value = 'S\u00ed';\n\n  if(\/reformad|integral|a estrenar\/.test(low)) $('condition').value='Ya presentable';\n  else if(\/buen estado|para entrar\/.test(low)) $('condition').value='Ya presentable';\n  else if(\/actualizar|lavado|amueblado\/.test(low)) $('condition').value='Lavado ligero';\n  else if(\/para reformar|a reformar\/.test(low)) $('condition').value='Para reformar \/ fuera estrategia';\n\n  const comm = txt.match(\/(?:comunidad|administraci[o\u00f3]n)\\s*(?:de)?\\s*([\\d.,]+)\/i); setIfEmpty('community', comm?parseNum(comm[1]):'');\n\n  \/\/ rough zone extraction: first likely place pattern\n  const zonePatterns = [\/en\\s+([A-Z\u00c1\u00c9\u00cd\u00d3\u00da\u00d1][A-Za-z\u00c1\u00c9\u00cd\u00d3\u00da\u00d1\u00f1\\- ]{2,40})\/i, \/ubicad[oa]\\s+en\\s+([A-Z\u00c1\u00c9\u00cd\u00d3\u00da\u00d1][A-Za-z\u00c1\u00c9\u00cd\u00d3\u00da\u00d1\u00f1\\- ]{2,40})\/i];\n  for(const p of zonePatterns){ const z=txt.match(p); if(z){ setIfEmpty('zone', z[1].trim()); break; } }\n  $('notes').value = txt.slice(0,1000);\n\n  $('ocrStatus').className='alert ok';\n  $('ocrStatus').textContent='Datos volcados. Revisa y corrige lo que falte.';\n}\n\nasync function runOCR(){\n  const files = $('images').files;\n  if(!files || !files.length){ $('ocrStatus').className='alert err'; $('ocrStatus').textContent='Sube al menos una captura.'; return; }\n  $('ocrStatus').className='alert'; $('ocrStatus').textContent='Leyendo capturas\u2026';\n  let all='';\n  try {\n    for(let i=0;i<files.length;i++){\n      $('ocrStatus').textContent = `Procesando captura ${i+1}\/${files.length}\u2026`;\n      const { data:{ text } } = await Tesseract.recognize(files[i], 'spa+eng');\n      all += '\\n' + text;\n    }\n    $('ocrText').value = normalizeSpaces(all);\n    $('ocrStatus').className='alert ok'; $('ocrStatus').textContent='OCR completado. Ahora se volcar\u00e1n los datos.';\n    extractFromText();\n  } catch(e){\n    $('ocrStatus').className='alert err'; $('ocrStatus').textContent='Error en OCR. Puedes pegar el texto manualmente.';\n    console.error(e);\n  }\n}\n\nfunction loadSample(){\n  const country=$('country').value;\n  if(country==='ES'){\n    $('ocrText').value = 'Piso en venta en Pego 86.000 \u20ac 97 m\u00b2 4 hab. 1 ba\u00f1o 4\u00aa planta exterior sin ascensor. Reformado en 2024. Terraza comunitaria. Construido en 1981.';\n    $('mv').value=110000; $('mvMin').value=105000; $('mvMax').value=115000; $('exitConservative').value=110000; $('zone').value='Pego';\n  } else {\n    $('ocrText').value = 'Apartamento en venta en C\u00facuta Niza 155.000.000 COP 50 m\u00b2 2 habitaciones 2 ba\u00f1os piso 3 con ascensor. Parqueadero. Administraci\u00f3n 180.000.';\n    $('mv').value=160000000; $('mvMin').value=150000000; $('mvMax').value=168000000; $('exitConservative').value=160000000; $('zone').value='Niza';\n  }\n  extractFromText();\n  updateResult();\n}\n\n$('country').addEventListener('change', ()=>{setCountryDefaults($('country').value);});\n$('refreshPreset').addEventListener('change', applyRefreshPreset);\n$('images').addEventListener('change', ()=>{\n  const files = $('images').files;\n  if(files && files.length){\n    $('ocrStatus').className='alert ok';\n    $('ocrStatus').textContent = `${files.length} captura${files.length===1?'':'s'} cargada${files.length===1?'':'s'}. Pulsa \u201cProcesar capturas\u201d.`;\n  } else {\n    $('ocrStatus').className='alert';\n    $('ocrStatus').textContent='Esperando capturas\u2026';\n  }\n});\n\n$('ocrBtn').addEventListener('click', (e)=>{ e.preventDefault(); runOCR(); });\n$('parseBtn').addEventListener('click', (e)=>{ e.preventDefault(); extractFromText(); });\n$('calcBtn').addEventListener('click', (e)=>{ e.preventDefault(); updateResult(); });\n$('sampleBtn').addEventListener('click', (e)=>{ e.preventDefault(); loadSample(); });\n$('clearBtn').addEventListener('click', (e)=>{ e.preventDefault(); clearForm(); });\n['askPrice','exitConservative','mv','mvMin','mvMax','community','refreshCost','buyPct','buyFixed','sellPct','sellFixed','otherCosts','saleTaxPct','ltv','rate','holdMonths','minProfit','minRoi'].forEach(id=>$(id).addEventListener('change', updateResult));\n\nsetCountryDefaults('ES');\nshowNA('PriKodu necesita: precio anuncio y salida conservadora.');\n<\/script>\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>PriKodu V12 \u00b7 OCR por capturas Espa\u00f1a y Colombia \u00b7 Subes capturas, PriKodu vuelca el anuncio, t\u00fa corriges si hace [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"site-sidebar-layout":"no-sidebar","site-content-layout":"","ast-site-content-layout":"full-width-container","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"disabled","ast-breadcrumbs-content":"","ast-featured-img":"disabled","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"class_list":["post-43","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/sanaris.es\/index.php\/wp-json\/wp\/v2\/pages\/43","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sanaris.es\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/sanaris.es\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/sanaris.es\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/sanaris.es\/index.php\/wp-json\/wp\/v2\/comments?post=43"}],"version-history":[{"count":8,"href":"https:\/\/sanaris.es\/index.php\/wp-json\/wp\/v2\/pages\/43\/revisions"}],"predecessor-version":[{"id":52,"href":"https:\/\/sanaris.es\/index.php\/wp-json\/wp\/v2\/pages\/43\/revisions\/52"}],"wp:attachment":[{"href":"https:\/\/sanaris.es\/index.php\/wp-json\/wp\/v2\/media?parent=43"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}