Files
hrm/app/components/transaction/AllocateItem.vue
2026-04-06 15:53:14 +07:00

203 lines
6.2 KiB
Vue

<script setup>
import AllocateForm from '@/components/transaction/AllocateForm.vue';
const props = defineProps({
paymentSchedule: Object,
i: Number,
});
const emit = defineEmits(['refresh'])
const {
$insertapi,
$snackbar,
$remove,
} = useNuxtApp();
const allocations = ref([{}]);
const showConfirmModal = ref();
const isSubmitting = ref(false);
const resetKey = ref(0);
function addAllocation() {
allocations.value.push({});
}
async function removeAllocation(index) {
$remove(allocations.value, index);
if (allocations.value.length === 0) {
allocations.value = [{}];
}
}
function setAllo({ key, value, i }) {
allocations.value[i][key] = value;
}
function openConfirmModal() {
showConfirmModal.value = {
component: 'dialog/Confirm',
title: 'Xác nhận',
width: '600px',
height: '150px',
vbind: { content: `Xác nhận phân bổ tay cho lịch thanh toán ${props.paymentSchedule.code}?` },
onConfirm: update
}
}
async function update() {
const allocationsToSend = allocations.value
.map(({ ref, date, type, amount }) => ({ ref, date, type, amount }));
const payload = {
product: props.paymentSchedule.txn_detail__transaction__product,
schedule_id: props.paymentSchedule.id,
allocation_list: allocationsToSend,
};
isSubmitting.value = true;
const res = await $insertapi('manualallocate', payload);
if (res.success) {
emit('refresh');
allocations.value = [{}];
resetKey.value++;
$snackbar(res.message);
} else {
$snackbar(res.message || "Đã có lỗi khi phân bổ tay");
}
isSubmitting.value = false;
}
const totalByRef = computed(() => {
const map = {};
for (const allo of allocations.value) {
if (allo.ref) {
map[allo.ref] = (map[allo.ref] || 0) + (allo.amount || 0);
}
}
return map;
});
const formValid = computed(() => {
if (allocations.value.some(allo => {
if (Object.keys(allo).length === 0) return true;
if (allo.amount === undefined || allo.type === undefined) return true;
if (allo.ref && totalByRef.value[allo.ref] > allo.allocation_remain) return true;
})) return false;
return true;
});
</script>
<template>
<div class="fs-15 is-flex is-gap-2">
<div class="is-flex is-flex-direction-column is-align-items-center is-gap-1">
<p class="fsb-17 is-flex is-justify-content-center is-align-items-center" style="
border: 3px solid rgb(52, 92, 103);
border-radius: 9999px;
width: 2.5rem;
height: 2.5rem;
">
{{ i + 1 }}
</p>
<div style="
border: 3px solid #dddddd;
border-radius: 9999px;
flex-grow: 1;
width: min-content;
">
</div>
</div>
<div class="is-flex-grow-1">
<div class="columns">
<div class="column">
<p><span class="has-text-weight-semibold">{{ paymentSchedule.code }}</span> - {{ paymentSchedule.type__name }}</p>
</div>
<div class="column">
<span class="has-text-weight-semibold">Từ ngày: </span>
<FormatDate :date="paymentSchedule.from_date" />
</div>
<div class="column">
<span class="has-text-weight-semibold">Đến ngày: </span>
<FormatDate :date="paymentSchedule.to_date" />
</div>
<div class="column">
<span class="has-text-weight-semibold">Ngày tính lãi: </span>
<FormatDate :date="paymentSchedule.batch_date" />
</div>
<div class="column">
<span class="has-text-weight-semibold">Quá hạn: </span>
<span>{{ paymentSchedule.ovd_days }} ngày</span>
</div>
</div>
<div class="columns">
<div class="column">
<p class="has-text-weight-semibold">Số tiền theo (Gốc)</p>
<FormatNumber :value="paymentSchedule.amount" />
</div>
<div class="column">
<p class="has-text-weight-semibold">Đã thu theo </p>
<FormatNumber :value="paymentSchedule.paid_amount" />
</div>
<div class="column">
<p class="has-text-weight-semibold">Số tiền còn theo </p>
<FormatNumber :value="paymentSchedule.amount_remain" />
</div>
<div class="column">
<p class="has-text-weight-semibold">Lãi phạt</p>
<FormatNumber :value="paymentSchedule.penalty_amount" color="red" />
</div>
<div class="column">
<p class="has-text-weight-semibold">Lãi còn lại</p>
<FormatNumber :value="paymentSchedule.penalty_remain" color="red" />
</div>
<div class="column">
<p class="has-text-weight-semibold">Tổng tiền còn lại</p>
<FormatNumber :value="paymentSchedule.remain_amount" />
</div>
</div>
<div>
<AllocateForm
v-for="(allo, i) in allocations"
:key="`${resetKey}-${i}`"
v-bind="{
allo,
i,
length: allocations.length,
productId: paymentSchedule.txn_detail__transaction__product,
amount_remain: paymentSchedule.amount_remain,
totalByRef,
onSetAllo: setAllo,
onRemoveAllocation: removeAllocation,
}"
/>
<button class="button is-pulled-right is-info is-light mb-4" @click="addAllocation">
<span class="icon">
<svg
xmlns="http://www.w3.org/2000/svg"
height="12"
width="12"
viewBox="0 0 448 512"
>
<path
d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 144L48 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l144 0 0 144c0 17.7 14.3 32 32 32s32-14.3 32-32l0-144 144 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-144 0 0-144z"
/>
</svg>
</span>
</button>
<button
@click="openConfirmModal"
:class="`button is-secondary ${isSubmitting ? 'is-loading' : ''}`"
:disabled="isSubmitting || !formValid"
>
<span>Phân bổ</span>
</button>
<Modal
v-bind="showConfirmModal"
v-if="showConfirmModal"
@close="showConfirmModal = undefined"
/>
<!-- <pre class="fs-12">{{ JSON.stringify(allocations, null, 2) }}</pre> -->
</div>
</div>
</div>
</template>