204 lines
12 KiB
HTML
204 lines
12 KiB
HTML
{% extends 'base.html' %}
|
|
|
|
{% block content %}
|
|
<div class="form-container" style="max-width: 800px; margin: 0 auto;">
|
|
<div class="data-card" style="padding: 2.5rem; background: white; border-radius: 8px; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.05);">
|
|
|
|
<!-- Banner Requerimiento 6: Plan de mitigación de errores para registro de proveedores -->
|
|
<div id="draft-alert-banner" style="display:none; margin-bottom:1.5rem; background:#eff6ff; border:1px solid #bfdbfe; border-left:4px solid #2563eb; padding:1rem; border-radius:6px;">
|
|
<div style="display:flex; justify-content:space-between; align-items:center;">
|
|
<div>
|
|
<strong style="color:#1e40af; font-size:0.9rem;">Borrador Detectado en Almacenamiento Local (Autosave Mitigación)</strong>
|
|
<p style="margin:0.25rem 0 0 0; color:#1e3a8a; font-size:0.8rem;">Se detectaron datos no guardados de una sesión anterior. ¿Desea restaurarlos?</p>
|
|
</div>
|
|
<div style="display:flex; gap:0.5rem;">
|
|
<button type="button" onclick="restoreVendorDraft()" class="btn-primary" style="padding:0.3rem 0.8rem; font-size:0.8rem; background:#2563eb;">Restaurar</button>
|
|
<button type="button" onclick="discardVendorDraft()" class="btn-secondary" style="padding:0.3rem 0.8rem; font-size:0.8rem;">Descartar</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<h2 style="color: var(--efintel-petroleum); margin-bottom: 2rem; border-bottom: 2px solid var(--efintel-light); padding-bottom: 1rem; display: flex; align-items: center; justify-content:space-between;">
|
|
<span style="display:flex; align-items:center; gap:0.5rem;">
|
|
<i data-lucide="{% if form.instance.pk %}edit-3{% else %}plus-circle{% endif %}"></i>
|
|
{% if form.instance.pk %}Editar Proveedor{% else %}Nuevo Proveedor (Registro Seguro){% endif %}
|
|
</span>
|
|
<span class="badge badge-emerald" style="font-size:0.75rem;"><i data-lucide="shield-check" style="width:12px;"></i> Autosave / Anti-Pérdida Activo</span>
|
|
</h2>
|
|
|
|
<form method="post" id="vendor-form">
|
|
{% csrf_token %}
|
|
|
|
<h3 style="margin-bottom: 1rem; color: var(--efintel-dark); font-size: 1rem;">Información General</h3>
|
|
|
|
<div style="display: grid; grid-template-columns: 1fr 2fr; gap: 1.5rem; margin-bottom: 1.5rem;">
|
|
<div class="form-group">
|
|
<label for="{{ form.rut.id_for_label }}" style="font-weight: 600; font-size: 0.85rem; color: var(--efintel-dark);">RUT Proveedor</label>
|
|
<div style="display:flex; gap:0.3rem;">
|
|
{{ form.rut }}
|
|
<button type="button" onclick="consultarSIIVendor()" class="btn-outline" style="padding:0.4rem 0.7rem; font-size:0.75rem; white-space:nowrap; background:#f0f9ff; border-color:#7dd3fc; color:#0369a1;" title="Validar y autocompletar con la API del SII">
|
|
<i data-lucide="search" style="width:12px;"></i> Consultar SII
|
|
</button>
|
|
</div>
|
|
<span id="sii-status-text" style="font-size:0.7rem; color:var(--efintel-gray); margin-top:0.25rem; display:block;"></span>
|
|
{% if form.rut.errors %}<div style="color: var(--efintel-danger); font-size: 0.75rem; margin-top: 4px;">{{ form.rut.errors }}</div>{% endif %}
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="{{ form.name.id_for_label }}" style="font-weight: 600; font-size: 0.85rem; color: var(--efintel-dark);">Razón Social / Nombre</label>
|
|
{{ form.name }}
|
|
{% if form.name.errors %}<div style="color: var(--efintel-danger); font-size: 0.75rem; margin-top: 4px;">{{ form.name.errors }}</div>{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group" style="margin-bottom: 2rem;">
|
|
<label for="{{ form.business_line.id_for_label }}" style="font-weight: 600; font-size: 0.85rem; color: var(--efintel-dark);">Giro Comercial</label>
|
|
{{ form.business_line }}
|
|
{% if form.business_line.errors %}<div style="color: var(--efintel-danger); font-size: 0.75rem; margin-top: 4px;">{{ form.business_line.errors }}</div>{% endif %}
|
|
</div>
|
|
|
|
<h3 style="margin-top: 2.5rem; margin-bottom: 1rem; color: var(--efintel-dark); font-size: 1rem; border-top: 1px solid var(--efintel-light); padding-top: 1.5rem;">Datos Bancarios y Facturación</h3>
|
|
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom: 1.5rem;">
|
|
<div class="form-group">
|
|
<label for="{{ form.bank.id_for_label }}" style="font-weight: 600; font-size: 0.85rem; color: var(--efintel-dark);">Banco</label>
|
|
{{ form.bank }}
|
|
{% if form.bank.errors %}<div style="color: var(--efintel-danger); font-size: 0.75rem; margin-top: 4px;">{{ form.bank.errors }}</div>{% endif %}
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="{{ form.account_type.id_for_label }}" style="font-weight: 600; font-size: 0.85rem; color: var(--efintel-dark);">Tipo de Cuenta</label>
|
|
{{ form.account_type }}
|
|
{% if form.account_type.errors %}<div style="color: var(--efintel-danger); font-size: 0.75rem; margin-top: 4px;">{{ form.account_type.errors }}</div>{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-bottom: 1.5rem;">
|
|
<div class="form-group">
|
|
<label for="{{ form.account_number.id_for_label }}" style="font-weight: 600; font-size: 0.85rem; color: var(--efintel-dark);">Número de Cuenta</label>
|
|
{{ form.account_number }}
|
|
{% if form.account_number.errors %}<div style="color: var(--efintel-danger); font-size: 0.75rem; margin-top: 4px;">{{ form.account_number.errors }}</div>{% endif %}
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="{{ form.email.id_for_label }}" style="font-weight: 600; font-size: 0.85rem; color: var(--efintel-dark);">Correo de Pago/Facturación</label>
|
|
{{ form.email }}
|
|
{% if form.email.errors %}<div style="color: var(--efintel-danger); font-size: 0.75rem; margin-top: 4px;">{{ form.email.errors }}</div>{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group" style="margin-bottom: 2rem; width: 50%;">
|
|
<label for="{{ form.payment_terms.id_for_label }}" style="font-weight: 600; font-size: 0.85rem; color: var(--efintel-dark);">Plazo de Pago (Días)</label>
|
|
{{ form.payment_terms }}
|
|
{% if form.payment_terms.errors %}<div style="color: var(--efintel-danger); font-size: 0.75rem; margin-top: 4px;">{{ form.payment_terms.errors }}</div>{% endif %}
|
|
</div>
|
|
|
|
<div style="display: flex; gap: 1rem; padding-top: 1.5rem; border-top: 1px solid var(--efintel-light);">
|
|
<button type="submit" class="btn-primary" style="padding: 0.6rem 2rem; font-size: 0.9rem; background:var(--efintel-blue);">Guardar Proveedor</button>
|
|
<a href="{% url 'gestion_panel' %}" class="btn-secondary" style="padding: 0.6rem 2rem; font-size: 0.9rem; text-decoration: none;">Cancelar</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.querySelectorAll('input, select, textarea').forEach(el => {
|
|
el.classList.add('form-control');
|
|
});
|
|
|
|
// Requerimiento 6: Autosave en localStorage para mitigación de errores de formulario
|
|
const DRAFT_KEY = 'vendor_form_draft_{{ form.instance.pk|default:"new" }}';
|
|
const vendorForm = document.getElementById('vendor-form');
|
|
const draftBanner = document.getElementById('draft-alert-banner');
|
|
|
|
function saveDraft() {
|
|
const formData = {};
|
|
const elements = vendorForm.querySelectorAll('input, select, textarea');
|
|
elements.forEach(el => {
|
|
if (el.name && el.name !== 'csrfmiddlewaretoken') {
|
|
formData[el.name] = el.value;
|
|
}
|
|
});
|
|
localStorage.setItem(DRAFT_KEY, JSON.stringify(formData));
|
|
}
|
|
|
|
function checkForDraft() {
|
|
const saved = localStorage.getItem(DRAFT_KEY);
|
|
if (saved) {
|
|
try {
|
|
const data = JSON.parse(saved);
|
|
// Verify if saved data has actual input content
|
|
const hasValues = Object.values(data).some(v => v && v.trim() !== '');
|
|
if (hasValues && draftBanner) {
|
|
draftBanner.style.display = 'block';
|
|
}
|
|
} catch(e){}
|
|
}
|
|
}
|
|
|
|
function restoreVendorDraft() {
|
|
const saved = localStorage.getItem(DRAFT_KEY);
|
|
if (saved) {
|
|
const data = JSON.parse(saved);
|
|
Object.keys(data).forEach(key => {
|
|
const el = vendorForm.querySelector(`[name="${key}"]`);
|
|
if (el) el.value = data[key];
|
|
});
|
|
if (draftBanner) draftBanner.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
function discardVendorDraft() {
|
|
localStorage.removeItem(DRAFT_KEY);
|
|
if (draftBanner) draftBanner.style.display = 'none';
|
|
}
|
|
|
|
vendorForm.querySelectorAll('input, select, textarea').forEach(el => {
|
|
el.addEventListener('input', saveDraft);
|
|
el.addEventListener('change', saveDraft);
|
|
});
|
|
|
|
vendorForm.addEventListener('submit', function() {
|
|
localStorage.removeItem(DRAFT_KEY);
|
|
});
|
|
|
|
// Check draft on load
|
|
checkForDraft();
|
|
|
|
// Requerimiento 4: Consulta API SII en vivo para autocompletar RUT y Razón Social
|
|
function consultarSIIVendor() {
|
|
const rutInput = document.getElementById('{{ form.rut.id_for_label }}');
|
|
const statusText = document.getElementById('sii-status-text');
|
|
if (!rutInput || !rutInput.value.trim()) {
|
|
alert('Por favor ingrese un RUT para consultar en el SII.');
|
|
return;
|
|
}
|
|
|
|
statusText.textContent = 'Consultando API SII...';
|
|
statusText.style.color = '#0284c7';
|
|
|
|
fetch(`/api/sii/taxpayer-info/?rut=${encodeURIComponent(rutInput.value.trim())}`)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.valid) {
|
|
rutInput.value = data.rut;
|
|
const nameInput = document.getElementById('{{ form.name.id_for_label }}');
|
|
const girosInput = document.getElementById('{{ form.business_line.id_for_label }}');
|
|
if (nameInput && (!nameInput.value || data.source === 'SII_API')) nameInput.value = data.name;
|
|
if (girosInput && !girosInput.value) girosInput.value = data.business_line;
|
|
statusText.textContent = `✓ RUT Validador SII OK (${data.source})`;
|
|
statusText.style.color = '#16a34a';
|
|
saveDraft();
|
|
} else {
|
|
statusText.textContent = `✗ ${data.error || 'RUT inválido'}`;
|
|
statusText.style.color = '#dc2626';
|
|
}
|
|
})
|
|
.catch(err => {
|
|
statusText.textContent = 'Error al consultar API SII.';
|
|
statusText.style.color = '#dc2626';
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|