feat: Implement complete APU (Análisis de Precios Unitarios) module
High priority features: - APU CRUD with materials, labor, and equipment breakdown - Labor catalog with FSR (Factor de Salario Real) calculation - Equipment catalog with hourly cost calculation - Link APU to budget line items (partidas) - Explosion de insumos (consolidated materials list) Additional features: - Duplicate APU functionality - Excel export for explosion de insumos - Search and filters in APU list - Price validation alerts for outdated prices - PDF report export for APU New components: - APUForm, APUList, APUDetail - ManoObraForm, EquipoForm - ConfiguracionAPUForm - VincularAPUDialog - PartidasManager - ExplosionInsumos - APUPDF New UI components: - Alert component - Tooltip component Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
104
src/app/api/apu/[id]/duplicate/route.ts
Normal file
104
src/app/api/apu/[id]/duplicate/route.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { auth } from "@/lib/auth";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
|
||||
export async function POST(
|
||||
request: Request,
|
||||
{ params }: { params: Promise<{ id: string }> }
|
||||
) {
|
||||
try {
|
||||
const session = await auth();
|
||||
if (!session?.user?.empresaId) {
|
||||
return NextResponse.json({ error: "No autorizado" }, { status: 401 });
|
||||
}
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
// Get the original APU with all insumos
|
||||
const original = await prisma.analisisPrecioUnitario.findFirst({
|
||||
where: {
|
||||
id,
|
||||
empresaId: session.user.empresaId,
|
||||
},
|
||||
include: {
|
||||
insumos: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!original) {
|
||||
return NextResponse.json({ error: "APU no encontrado" }, { status: 404 });
|
||||
}
|
||||
|
||||
// Generate a unique code for the copy
|
||||
let newCodigo = `${original.codigo}-COPIA`;
|
||||
let counter = 1;
|
||||
|
||||
while (true) {
|
||||
const existing = await prisma.analisisPrecioUnitario.findFirst({
|
||||
where: {
|
||||
codigo: newCodigo,
|
||||
empresaId: session.user.empresaId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!existing) break;
|
||||
|
||||
counter++;
|
||||
newCodigo = `${original.codigo}-COPIA${counter}`;
|
||||
}
|
||||
|
||||
// Create the duplicate APU with all its insumos
|
||||
const duplicate = await prisma.analisisPrecioUnitario.create({
|
||||
data: {
|
||||
codigo: newCodigo,
|
||||
descripcion: `${original.descripcion} (Copia)`,
|
||||
unidad: original.unidad,
|
||||
rendimientoDiario: original.rendimientoDiario,
|
||||
costoMateriales: original.costoMateriales,
|
||||
costoManoObra: original.costoManoObra,
|
||||
costoEquipo: original.costoEquipo,
|
||||
costoHerramienta: original.costoHerramienta,
|
||||
costoDirecto: original.costoDirecto,
|
||||
porcentajeIndirectos: original.porcentajeIndirectos,
|
||||
costoIndirectos: original.costoIndirectos,
|
||||
porcentajeUtilidad: original.porcentajeUtilidad,
|
||||
costoUtilidad: original.costoUtilidad,
|
||||
precioUnitario: original.precioUnitario,
|
||||
empresaId: session.user.empresaId,
|
||||
insumos: {
|
||||
create: original.insumos.map((insumo) => ({
|
||||
tipo: insumo.tipo,
|
||||
descripcion: insumo.descripcion,
|
||||
unidad: insumo.unidad,
|
||||
cantidad: insumo.cantidad,
|
||||
desperdicio: insumo.desperdicio,
|
||||
cantidadConDesperdicio: insumo.cantidadConDesperdicio,
|
||||
rendimiento: insumo.rendimiento,
|
||||
precioUnitario: insumo.precioUnitario,
|
||||
importe: insumo.importe,
|
||||
materialId: insumo.materialId,
|
||||
categoriaManoObraId: insumo.categoriaManoObraId,
|
||||
equipoId: insumo.equipoId,
|
||||
})),
|
||||
},
|
||||
},
|
||||
include: {
|
||||
insumos: {
|
||||
include: {
|
||||
material: true,
|
||||
categoriaManoObra: true,
|
||||
equipo: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return NextResponse.json(duplicate);
|
||||
} catch (error) {
|
||||
console.error("Error duplicating APU:", error);
|
||||
return NextResponse.json(
|
||||
{ error: "Error al duplicar el APU" },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user