panels-origin/web-panel/components/padron/LoanDialog.vue

119 lines
4.3 KiB
Vue

<template>
<Dialog
:visible="visible"
header="Registrar préstamo"
modal
:style="{ width: '28rem' }"
@update:visible="emit('update:visible', $event)"
>
<p v-if="personName" class="muted mb-3">{{ personName }}</p>
<FormField label="Monto a entregar" required>
<InputNumber v-model="loanAmount" mode="currency" currency="MXN" locale="es-MX" class="w-full" />
</FormField>
<div class="loan-switch">
<ToggleSwitch v-model="loanCommissionOn" />
<span>Comisión {{ loanCommissionPct }}%</span>
</div>
<FormField v-if="loanCommissionOn" label="Porcentaje" class="mt-2">
<InputNumber v-model="loanCommissionPct" suffix=" %" :min="0" :max="100" :max-fraction-digits="2" class="w-full" />
</FormField>
<Message v-if="loanIsSmall" severity="secondary" :closable="false" class="mt-3">
Préstamo chico (tope {{ money(loanSmallMax) }}): 1 exhibición el sábado de la semana siguiente.
Recibe {{ money(loanAmount) }}; se descuenta {{ money(loanAmount + (loanCommissionOn ? loanAmount * loanCommissionPct / 100 : 0)) }}.
</Message>
<template v-else>
<FormField label="Plan" class="mt-3">
<Select v-model="loanPlan" :options="planOpts" option-label="label" option-value="value" class="w-full" />
</FormField>
<FormField v-if="loanPlan === 'installments'" label="Cuotas" class="mt-2">
<InputNumber v-model="loanInstallments" :min="2" :max="24" class="w-full" />
</FormField>
</template>
<template #footer>
<Button label="Cancelar" text @click="emit('update:visible', false)" />
<Button label="Registrar" icon="pi pi-check" :disabled="loanAmount <= 0 || saving || !workerId" :loading="saving" @click="save" />
</template>
</Dialog>
</template>
<script setup lang="ts">
import FormField from "~/components/padron/FormField.vue";
const props = defineProps<{
visible: boolean;
workerId?: number;
personName?: string;
}>();
const emit = defineEmits<{ "update:visible": [boolean]; saved: [number] }>();
const { api, download } = useApi();
const toast = useToast();
const loanAmount = ref(0);
const loanCommissionOn = ref(true);
const loanCommissionPct = ref(10);
const loanSmallMax = ref(500);
const loanPlan = ref<"single" | "installments">("single");
const loanInstallments = ref(4);
const saving = ref(false);
const planOpts = [
{ label: "1 exhibición", value: "single" },
{ label: "Varias cuotas", value: "installments" },
];
const loanIsSmall = computed(() => loanAmount.value > 0 && loanAmount.value <= loanSmallMax.value);
function money(value: number) {
return value.toLocaleString("es-MX", { style: "currency", currency: "MXN", maximumFractionDigits: 0 });
}
async function loadSettings() {
const payrollSettings = await api<{
settings: { loan_commission_enabled: number; loan_commission_pct: number; loan_small_max: number };
}>("/v1/payroll/settings");
loanCommissionOn.value = Boolean(payrollSettings.settings.loan_commission_enabled);
loanCommissionPct.value = Number(payrollSettings.settings.loan_commission_pct);
loanSmallMax.value = Number(payrollSettings.settings.loan_small_max);
}
async function save() {
if (!props.workerId || loanAmount.value <= 0) return;
saving.value = true;
try {
const created = await api<{ id: number }>("/v1/loans", {
method: "POST",
json: {
worker_id: props.workerId,
delivered: loanAmount.value,
commission_enabled: loanCommissionOn.value,
commission_pct: loanCommissionPct.value,
plan: loanIsSmall.value ? "single" : loanPlan.value,
installments_n: loanIsSmall.value ? 1 : loanInstallments.value,
},
});
loanAmount.value = 0;
toast.add({ severity: "success", summary: "Préstamo registrado", life: 1800 });
emit("update:visible", false);
emit("saved", created.id);
if (created.id) download(`/v1/loans/${created.id}/recibo`, `prestamo-aceptacion-${created.id}.pdf`);
} finally {
saving.value = false;
}
}
watch(() => props.visible, async (open) => {
if (!open) return;
loanAmount.value = 0;
loanPlan.value = "single";
loanInstallments.value = 4;
await loadSettings();
});
</script>
<style scoped>
.loan-switch {
display: flex;
align-items: center;
gap: 8px;
margin-top: 12px;
font-size: 0.9rem;
}
</style>