feat: agregar funcionalidad para gestionar usuarios y folios de resguardo en la asignación de activos fijos

This commit is contained in:
Juan Felipe Zapata Moreno 2026-03-24 17:26:33 -06:00
parent 1a5e70890c
commit 8205d4203b
5 changed files with 72 additions and 3 deletions

View File

@ -1,11 +1,14 @@
<script setup lang="ts">
import Card from 'primevue/card';
import InputText from 'primevue/inputtext';
import Select from 'primevue/select';
import Textarea from 'primevue/textarea';
import type { FixedAssetAssignmentFormData } from '../../types/fixedAssetAssignment';
import type { UserOption } from '../../services/fixedAssetsService';
interface Props {
form: FixedAssetAssignmentFormData;
users: UserOption[];
}
defineProps<Props>();
@ -29,6 +32,26 @@ defineProps<Props>();
class="w-full"
/>
</div>
<div class="space-y-2">
<label class="text-sm font-semibold text-surface-800 dark:text-surface-100">Folio de Resguardo (Opcional)</label>
<InputText
v-model="form.receiptFolio"
class="w-full"
placeholder="Ej. GOLSRMVR-0170"
/>
</div>
<div class="space-y-2 md:col-span-2">
<label class="text-sm font-semibold text-surface-800 dark:text-surface-100">Autoriza (Opcional)</label>
<Select
v-model="form.authorizedById"
:options="users"
optionLabel="full_name"
optionValue="id"
placeholder="Seleccione quien autoriza..."
class="w-full"
showClear
/>
</div>
<div class="space-y-2 md:col-span-2">
<label class="text-sm font-semibold text-surface-800 dark:text-surface-100">Notas o Comentarios (Opcional)</label>
<Textarea

View File

@ -12,7 +12,7 @@ import type {
AssignmentEmployeeOption,
FixedAssetAssignmentFormData
} from '../../types/fixedAssetAssignment';
import { fixedAssetsService } from '../../services/fixedAssetsService';
import { fixedAssetsService, type UserOption } from '../../services/fixedAssetsService';
import { employeesService } from '@/modules/rh/components/employees/employees.services';
const router = useRouter();
@ -24,20 +24,24 @@ const employeeSearch = ref('');
const assets = ref<AssignmentAssetOption[]>([]);
const employees = ref<AssignmentEmployeeOption[]>([]);
const users = ref<UserOption[]>([]);
const form = ref<FixedAssetAssignmentFormData>({
assetId: null,
employeeId: null,
authorizedById: null,
assignedAt: new Date().toISOString().slice(0, 10),
receiptFolio: '',
notes: ''
});
onMounted(async () => {
loadingData.value = true;
try {
const [assetsRes, employeesRes] = await Promise.all([
const [assetsRes, employeesRes, usersRes] = await Promise.all([
fixedAssetsService.getAssets({ paginate: false, status: 1 }),
employeesService.getEmployees({ paginate: false }),
fixedAssetsService.getUsers(),
]);
const allAssets = (assetsRes as any).data?.data ?? [];
@ -59,6 +63,8 @@ onMounted(async () => {
role: e.job_position?.name ?? '—',
department: e.department?.name ?? '—',
}));
users.value = usersRes;
} catch {
toast.add({
severity: 'error',
@ -89,6 +95,8 @@ const save = async () => {
await fixedAssetsService.assignAsset(form.value.assetId, {
employee_id: form.value.employeeId,
assigned_at: form.value.assignedAt,
receipt_folio: form.value.receiptFolio || undefined,
authorized_by: form.value.authorizedById ?? undefined,
notes: form.value.notes || undefined,
});
@ -139,7 +147,7 @@ const save = async () => {
@update:selected-employee-id="form.employeeId = $event"
/>
<AssignmentDetailsCard :form="form" />
<AssignmentDetailsCard :form="form" :users="users" />
<div class="flex flex-wrap items-center justify-end gap-3">
<Button label="Cancelar" text severity="secondary" @click="cancel" />

View File

@ -77,6 +77,11 @@ const goToOffboarding = (assignment: AssetAssignment) => {
router.push(`/fixed-assets/assignments/${assignment.asset_id}/${assignment.id}/offboarding`);
};
const downloadResguardo = (assignment: AssetAssignment) => {
const url = fixedAssetsService.getResguardoUrl(assignment.asset_id, assignment.id);
window.open(url, '_blank');
};
onMounted(loadAssignments);
</script>
@ -175,6 +180,15 @@ onMounted(loadAssignments);
</td>
<td class="px-4 py-3 text-right">
<div class="flex items-center justify-end gap-1">
<Button
icon="pi pi-file-pdf"
text
rounded
size="small"
severity="secondary"
v-tooltip.top="'Descargar resguardo'"
@click="downloadResguardo(assignment)"
/>
<Button
v-if="assignment.status.id === 1"
icon="pi pi-times-circle"

View File

@ -115,10 +115,22 @@ export interface AssetAssignmentsPaginatedResponse {
};
}
export interface UserOption {
id: number;
full_name: string;
email: string;
}
export interface UsersResponse {
status: string;
data: { data: UserOption[] };
}
interface AssignAssetData {
employee_id: number;
assigned_at?: string;
receipt_folio?: string;
authorized_by?: number;
notes?: string;
}
@ -184,6 +196,16 @@ class FixedAssetsService {
const response = await api.put<AssetAssignmentResponse>(`/api/assets/${assetId}/assignments/${assignmentId}/return`, data);
return response.data;
}
async getUsers(): Promise<UserOption[]> {
const response = await api.get<UsersResponse>('/api/admin/users', { params: { paginate: false } });
return response.data.data.data;
}
getResguardoUrl(assetId: number, assignmentId: number): string {
const base = import.meta.env.VITE_API_URL || '';
return `${base}/api/asset-assignments-public/${assetId}/assignments/${assignmentId}/resguardo`;
}
}
export const fixedAssetsService = new FixedAssetsService();

View File

@ -17,6 +17,8 @@ export interface AssignmentEmployeeOption {
export interface FixedAssetAssignmentFormData {
assetId: number | null;
employeeId: number | null;
authorizedById: number | null;
assignedAt: string;
receiptFolio: string;
notes: string;
}