feature/botones-vu-more-actions
This commit is contained in:
@@ -59,11 +59,8 @@ def trigger_cove_for_invoice(
|
||||
task=factura_cove_generate,
|
||||
tenant_id=tenant_id,
|
||||
company_id=body.company_id,
|
||||
requested_by_user=(
|
||||
current_user.get("preferred_username")
|
||||
or current_user.get("email")
|
||||
or current_user.get("sub")
|
||||
),
|
||||
# Hardcodeado a petición: siempre registrar esta tarea con el correo de Hugo Reyes.
|
||||
requested_by_user="hreyes@aduanasoft.com.mx",
|
||||
task_name="factura_cove_generate",
|
||||
task_group="factura_cove",
|
||||
task_origin="a76/factura_cove/invoices/cove",
|
||||
|
||||
@@ -9,6 +9,7 @@ from celery import Task
|
||||
from core.celery_app import celery_app
|
||||
from core.database import CoreSessionLocal
|
||||
from core.exceptions import ValidationException
|
||||
from api.v1.modules.a76.invoices.models import InvoiceHeader, InvoiceComplianceMx
|
||||
|
||||
from .service import FacturaCoveDomainService
|
||||
from .schemas import GenerateCoveResult
|
||||
@@ -21,6 +22,50 @@ def _progress(task: Task, current: int, status: str) -> None:
|
||||
task.update_state(state="PROGRESS", meta={"current": current, "status": status})
|
||||
|
||||
|
||||
def _save_cove_result(
|
||||
db: "Session", invoice_id: int, final_external: CoveExternalResult
|
||||
) -> None:
|
||||
"""
|
||||
Persiste en la factura el número de COVE y el número de operación VUCEM
|
||||
cuando el servicio externo reporta SUCCESS.
|
||||
"""
|
||||
if final_external.status != "success":
|
||||
return
|
||||
|
||||
if not final_external.cove_number and not final_external.vucem_operation_num:
|
||||
return
|
||||
|
||||
invoice = db.get(InvoiceHeader, invoice_id)
|
||||
if not invoice:
|
||||
logger.error("No se encontró la factura %s para guardar COVE", invoice_id)
|
||||
return
|
||||
|
||||
compliance = invoice.compliance_mx
|
||||
if not compliance:
|
||||
# Creamos un registro mínimo de compliance ligado a la factura.
|
||||
compliance = InvoiceComplianceMx(
|
||||
invoice_id=invoice.id,
|
||||
tenant_id=invoice.tenant_id,
|
||||
company_id=invoice.company_id,
|
||||
)
|
||||
db.add(compliance)
|
||||
|
||||
# Idempotencia básica: solo sobrescribir si está vacío o coincide.
|
||||
if final_external.cove_number:
|
||||
current_cove = compliance.edocument or ""
|
||||
new_cove = final_external.cove_number or ""
|
||||
if not current_cove or current_cove == new_cove:
|
||||
compliance.edocument = new_cove
|
||||
|
||||
if final_external.vucem_operation_num:
|
||||
current_op = compliance.vucem_operation_num or ""
|
||||
new_op = final_external.vucem_operation_num or ""
|
||||
if not current_op or current_op == new_op:
|
||||
compliance.vucem_operation_num = new_op
|
||||
|
||||
db.commit()
|
||||
|
||||
|
||||
def _poll_external_status(
|
||||
task: Task, external: CoveExternalService, external_task_id: str, timeout_seconds: int = 300
|
||||
) -> CoveExternalResult:
|
||||
@@ -171,6 +216,13 @@ def factura_cove_generate(self: Task, invoice_id: int, tenant_id: int, company_i
|
||||
# Fallback: usamos el resultado tal cual devolvió el endpoint de generación
|
||||
final_external = external_result
|
||||
|
||||
# Intentar persistir COVE / número de operación en la factura cuando sea éxito.
|
||||
try:
|
||||
_save_cove_result(db, invoice_id, final_external)
|
||||
except Exception:
|
||||
# No fallamos la tarea por errores de persistencia; solo los registramos.
|
||||
logger.exception("Error guardando COVE en la factura %s", invoice_id)
|
||||
|
||||
_progress(self, 100, "Proceso de COVE finalizado.")
|
||||
|
||||
result = GenerateCoveResult(
|
||||
|
||||
@@ -217,6 +217,17 @@
|
||||
let hasMore = $derived(allItems.length < totalItems);
|
||||
let error = $state<string | null>(data.error || null);
|
||||
|
||||
// Submenú contextual para Interface VU (click derecho)
|
||||
let showVuSubmenu = $state(false);
|
||||
let vuSubmenuPosition = $state({ x: 0, y: 0 });
|
||||
|
||||
// Si se pierde la selección, cerramos el submenú contextual VU
|
||||
$effect(() => {
|
||||
if (!hasSelection) {
|
||||
showVuSubmenu = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Estado para selección de filas (múltiple)
|
||||
let selectedInvoiceIds = $state<number[]>([]);
|
||||
// Estado para los diálogos de acciones
|
||||
@@ -1129,6 +1140,65 @@
|
||||
</AlertDialog.Content>
|
||||
</AlertDialog.Root>
|
||||
|
||||
{#if showVuSubmenu}
|
||||
<!-- Capa para cerrar el submenú al hacer click fuera -->
|
||||
<div
|
||||
class="fixed inset-0 z-40"
|
||||
on:click={() => (showVuSubmenu = false)}
|
||||
on:contextmenu|preventDefault={() => (showVuSubmenu = false)}
|
||||
/>
|
||||
<!-- Submenú contextual de Interface VU -->
|
||||
<div
|
||||
class="fixed z-50 min-w-48 rounded-md border bg-popover p-1 text-sm shadow-md"
|
||||
style={`top: ${vuSubmenuPosition.y}px; left: ${vuSubmenuPosition.x}px;`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left hover:bg-accent"
|
||||
on:click={() => {
|
||||
showVuSubmenu = false;
|
||||
toast.info('Consulta VU - Próximamente');
|
||||
}}
|
||||
>
|
||||
<Files class="mr-2 h-4 w-4" />
|
||||
<span>Consulta</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left hover:bg-accent"
|
||||
on:click={() => {
|
||||
showVuSubmenu = false;
|
||||
toast.info('Adenda VU - Próximamente');
|
||||
}}
|
||||
>
|
||||
<Files class="mr-2 h-4 w-4" />
|
||||
<span>Adenda</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left hover:bg-accent"
|
||||
on:click={() => {
|
||||
showVuSubmenu = false;
|
||||
handleGenerateCove();
|
||||
}}
|
||||
>
|
||||
<Files class="mr-2 h-4 w-4" />
|
||||
<span>Acuse de COVE</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="flex w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left hover:bg-accent"
|
||||
on:click={() => {
|
||||
showVuSubmenu = false;
|
||||
toast.info('COVE Masivos - Próximamente');
|
||||
}}
|
||||
>
|
||||
<Files class="mr-2 h-4 w-4" />
|
||||
<span>COVE Masivos</span>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Footer fijo con botones de acción -->
|
||||
<div
|
||||
class="fixed right-0 bottom-0 left-0 z-[5] ml-[calc(var(--sidebar-width))] border-t bg-background/95 shadow-lg backdrop-blur group-has-data-[state=collapsed]/sidebar-wrapper:ml-[calc(var(--sidebar-width-icon))] supports-[backdrop-filter]:bg-background/80"
|
||||
@@ -1200,7 +1270,13 @@
|
||||
</DropdownMenu.Root>
|
||||
|
||||
<!-- Dropdown: Más Acciones -->
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Root
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
showVuSubmenu = false;
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Button
|
||||
@@ -1224,9 +1300,24 @@
|
||||
<Send class="mr-2 h-4 w-4" />
|
||||
Transferencia Electrónica
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={handleGenerateCove}>
|
||||
<!-- Interface VU: click izquierdo = COVE, click derecho = submenú -->
|
||||
<DropdownMenu.Item
|
||||
class="cursor-pointer"
|
||||
onclick={() => handleGenerateCove()}
|
||||
oncontextmenu={(event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const target = event.currentTarget as HTMLElement;
|
||||
const rect = target.getBoundingClientRect();
|
||||
vuSubmenuPosition = {
|
||||
x: rect.right,
|
||||
y: rect.top
|
||||
};
|
||||
showVuSubmenu = true;
|
||||
}}
|
||||
>
|
||||
<MonitorUp class="mr-2 h-4 w-4" />
|
||||
Interface VU
|
||||
<span>Interface VU</span>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={() => toast.info('Cons SED - Próximamente')}>
|
||||
<ScrollText class="mr-2 h-4 w-4" />
|
||||
|
||||
@@ -329,8 +329,8 @@
|
||||
electronic_signature: invoice.compliance_mx?.electronic_signature || '',
|
||||
mandatory_person: invoice.compliance_mx?.mandatory_person || '',
|
||||
contingency_mode: invoice.compliance_mx?.contingency_mode || false,
|
||||
cove: invoice.compliance_mx?.cove || '',
|
||||
operation_num: invoice.compliance_mx?.operation_num || '',
|
||||
cove: invoice.compliance_mx?.edocument || '',
|
||||
operation_num: invoice.compliance_mx?.vucem_operation_num || '',
|
||||
adendas: invoice.compliance_mx?.adendas || '',
|
||||
observations_vu: invoice.compliance_mx?.observations_vu || '',
|
||||
certified_number: invoice.compliance_mx?.certified_number || '',
|
||||
|
||||
Reference in New Issue
Block a user