211 lines
5.6 KiB
Vue
211 lines
5.6 KiB
Vue
<script setup>
|
|
import { ref, computed, onMounted } from 'vue';
|
|
import { useRouter } from 'vue-router';
|
|
import { api, useForm } from '@Services/Api';
|
|
import { apiTo, viewTo } from './Module';
|
|
|
|
import GoogleIcon from '@Shared/GoogleIcon.vue';
|
|
import PageHeader from '@Holos/PageHeader.vue';
|
|
import IconButton from '@Holos/Button/Icon.vue';
|
|
import Input from '@Holos/Form/Input.vue';
|
|
import Selectable from '@Holos/Form/Selectable.vue';
|
|
import PrimaryButton from '@Holos/Button/Primary.vue';
|
|
|
|
/** Definiciones */
|
|
const router = useRouter();
|
|
|
|
/** Propiedades */
|
|
const form = useForm({
|
|
petty_cash_id: '',
|
|
budget_items: [
|
|
{
|
|
id: 1,
|
|
concept: '',
|
|
amount: ''
|
|
}
|
|
]
|
|
});
|
|
|
|
const pettyCashes = ref([]);
|
|
let nextId = 2;
|
|
|
|
// Computed para calcular el total
|
|
const totalBudget = computed(() => {
|
|
return form.budget_items.reduce((total, item) => {
|
|
return total + (parseFloat(item.amount) || 0);
|
|
}, 0);
|
|
});
|
|
|
|
// Función para agregar nueva fila
|
|
const addBudgetItem = () => {
|
|
form.budget_items.push({
|
|
id: nextId++,
|
|
concept: '',
|
|
amount: 0
|
|
});
|
|
};
|
|
|
|
// Función para eliminar fila
|
|
const removeBudgetItem = (id) => {
|
|
if (form.budget_items.length > 1) {
|
|
form.budget_items = form.budget_items.filter(item => item.id !== id);
|
|
}
|
|
};
|
|
|
|
// Función para guardar presupuesto
|
|
function submit() {
|
|
// Filtrar elementos vacíos
|
|
const validItems = form.budget_items.filter(item =>
|
|
item.concept.trim() !== '' && item.amount > 0
|
|
);
|
|
|
|
if (validItems.length === 0) {
|
|
Notify.error('Por favor, agrega al menos un elemento al presupuesto');
|
|
return;
|
|
}
|
|
|
|
form.transform(data => ({
|
|
...data,
|
|
petty_cash_id: form.petty_cash_id?.id,
|
|
budget_items: validItems
|
|
})).post(apiTo('store-budget', { pettyCash: form.petty_cash_id }), {
|
|
onSuccess: () => {
|
|
Notify.success('Presupuesto guardado correctamente');
|
|
router.push(viewTo({ name: 'index' }));
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
/** Ciclos */
|
|
onMounted(() => {
|
|
api.catalog({
|
|
'pettyCash:all': null
|
|
}, {
|
|
onSuccess: (r) => pettyCashes.value = r['pettyCash:all'] ?? []
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<PageHeader
|
|
title="Asignación de Presupuesto"
|
|
>
|
|
<RouterLink :to="viewTo({ name: 'index' })">
|
|
<IconButton
|
|
class="text-white"
|
|
icon="arrow_back"
|
|
:title="$t('return')"
|
|
filled
|
|
/>
|
|
</RouterLink>
|
|
</PageHeader>
|
|
|
|
<div class="w-full pb-2">
|
|
<p class="text-justify text-sm">Gestiona y asigna el presupuesto para una caja chica</p>
|
|
</div>
|
|
|
|
<!-- Card principal -->
|
|
<section class="mt-6 bg-white rounded-lg shadow-sm p-6 dark:bg-primary-d dark:border-primary/20 dark:text-primary-dt">
|
|
|
|
<!-- Formulario de presupuesto -->
|
|
<form @submit.prevent="submit" class="space-y-6">
|
|
|
|
<!-- Campos principales -->
|
|
<div class="grid grid-cols-1 gap-6">
|
|
<Selectable
|
|
v-model="form.petty_cash_id"
|
|
id="petty_cash_id"
|
|
title="Caja Chica"
|
|
:onError="form.errors.petty_cash_id"
|
|
:options="pettyCashes"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
<!-- Filas de presupuesto -->
|
|
<div class="space-y-4">
|
|
<h3 class="text-lg font-medium text-gray-900 dark:text-primary-dt">Elementos del Presupuesto</h3>
|
|
<div
|
|
v-for="(item, index) in form.budget_items"
|
|
:key="item.id"
|
|
class="grid grid-cols-1 md:grid-cols-12 gap-4 items-end"
|
|
>
|
|
|
|
<!-- Campo Concepto -->
|
|
<div class="md:col-span-5">
|
|
<Input
|
|
v-model="item.concept"
|
|
:id="`concept_${item.id}`"
|
|
title="Concepto"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Campo Monto -->
|
|
<div class="md:col-span-4">
|
|
<Input
|
|
v-model.number="item.amount"
|
|
:id="`amount_${item.id}`"
|
|
title="Monto"
|
|
type="number"
|
|
min="0"
|
|
step="0.01"
|
|
/>
|
|
</div>
|
|
|
|
<!-- Botón de eliminar -->
|
|
<div class="md:col-span-3 flex justify-end">
|
|
<IconButton
|
|
v-if="form.budget_items.length > 1"
|
|
@click="removeBudgetItem(item.id)"
|
|
icon="delete"
|
|
class="text-red-500 hover:text-red-700"
|
|
:title="$t('delete')"
|
|
/>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Resumen del presupuesto -->
|
|
<div class="mt-8 p-4 bg-gray-50 rounded-lg dark:bg-primary/5">
|
|
<div class="flex items-center justify-between">
|
|
<div class="flex items-center gap-2">
|
|
<GoogleIcon class="text-gray-600 dark:text-primary-dt text-xl" name="account_balance_wallet" />
|
|
<span class="text-lg font-medium text-gray-800 dark:text-primary-dt">Total del Presupuesto:</span>
|
|
</div>
|
|
<div class="text-2xl font-bold text-[#2563eb] dark:text-primary-dt">
|
|
${{ totalBudget.toFixed(2) }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Botones de acción -->
|
|
<div class="flex items-center justify-between pt-6 border-t border-gray-100 dark:border-primary/20">
|
|
|
|
<!-- Botón Agregar más -->
|
|
<IconButton
|
|
@click="addBudgetItem"
|
|
icon="add"
|
|
class="bg-green-600 hover:bg-green-700 text-white"
|
|
:title="$t('add')"
|
|
>
|
|
+ Agregar más
|
|
</IconButton>
|
|
|
|
<!-- Botón Guardar Presupuesto -->
|
|
<PrimaryButton
|
|
type="submit"
|
|
:processing="form.processing"
|
|
>
|
|
<GoogleIcon class="text-white text-xl mr-2" name="save" />
|
|
Guardar Presupuesto
|
|
</PrimaryButton>
|
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
</section>
|
|
</template>
|