445 lines
15 KiB
Vue
445 lines
15 KiB
Vue
<template>
|
|
<div>
|
|
<div class="content p-0">
|
|
<div class="columns">
|
|
<!-- Left Column: Transaction & Customer Info -->
|
|
<div class="column is-half">
|
|
<div class="mb-5">
|
|
<p class="title is-5 mb-3">Thông tin Giao dịch</p>
|
|
<p><strong>Loại giao dịch:</strong> {{ phaseInfo.name || "-" }}</p>
|
|
<p>
|
|
<strong>Chính sách tài chính:</strong>
|
|
{{ selectedPolicy.name || "-" }}
|
|
</p>
|
|
<p><strong>Giá gốc:</strong> {{ $numtoString(originPrice) }}</p>
|
|
<p>
|
|
<strong>Số tiền đặt cọc:</strong>
|
|
<span class="has-text-primary has-text-weight-bold">{{ $numtoString(depositAmount) }}</span>
|
|
</p>
|
|
<p>
|
|
<strong>Ngày ký hợp đồng:</strong>
|
|
<span class="has-text-primary has-text-weight-bold">{{ formatDate(initialContractDate) }}</span>
|
|
</p>
|
|
<p>
|
|
<strong>Ngày hết hạn GD:</strong>
|
|
<span class="has-text-primary has-text-weight-bold">{{ formatDate(editableDueDate) }}</span>
|
|
</p>
|
|
</div>
|
|
<hr />
|
|
<div>
|
|
<p class="title is-5 mb-3">Thông tin Khách hàng</p>
|
|
<div class="columns">
|
|
<!-- Original Customer Column -->
|
|
<div class="column">
|
|
<p>
|
|
<strong>{{ isIndividual ? "Khách hàng" : "Tổ chức" }}</strong>
|
|
</p>
|
|
<p><strong>Mã KH:</strong> {{ selectedCustomer.code || "-" }}</p>
|
|
<p>
|
|
<strong>{{ isIndividual ? "Họ và tên:" : "Tên tổ chức:" }}</strong>
|
|
{{ selectedCustomer.fullname || "-" }}
|
|
</p>
|
|
<p>
|
|
<strong>Số điện thoại:</strong>
|
|
{{ selectedCustomer.phone || "-" }}
|
|
</p>
|
|
<p>
|
|
<strong>{{ isIndividual ? "CCCD:" : "GPKD/Mã số thuế:" }}</strong>
|
|
{{ selectedCustomer.legal_code || "-" }}
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Co-owner Column -->
|
|
<div
|
|
class="column"
|
|
v-if="coOwner && isIndividual"
|
|
>
|
|
<p class="is-flex is-justify-content-space-between">
|
|
<strong>Đồng sở hữu</strong>
|
|
<button
|
|
type="button"
|
|
class="delete is-small"
|
|
@click.stop="clearCoOwner()"
|
|
></button>
|
|
</p>
|
|
<p><strong>Mã:</strong> {{ coOwner.people__code || "-" }}</p>
|
|
<p>
|
|
<strong>Họ và tên:</strong>
|
|
{{ coOwner.people__fullname || "-" }}
|
|
</p>
|
|
<p>
|
|
<strong>Số điện thoại:</strong>
|
|
{{ coOwner.people__phone || "-" }}
|
|
</p>
|
|
<p><strong>CCCD:</strong> {{ coOwner.people__legal_code || "-" }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Co-owner SearchBox -->
|
|
<div
|
|
v-if="isIndividual && relatedPeople.length > 0"
|
|
class="mt-2"
|
|
>
|
|
<label class="label">Đồng sở hữu</label>
|
|
<SearchBox
|
|
:key="coOwner ? coOwner.id : 'empty'"
|
|
v-bind="{
|
|
vdata: relatedPeople,
|
|
field: 'people__fullname',
|
|
column: ['people__code', 'people__fullname'],
|
|
first: true,
|
|
optionid: coOwner ? coOwner.id : null,
|
|
}"
|
|
@option="selectCoOwnerByPerson"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-else-if="isIndividual && relatedPeople.length === 0"
|
|
class="mt-2"
|
|
>
|
|
<p class="has-text-grey-light is-size-7">Không có người liên quan nào được thêm</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Right Column: Product Info -->
|
|
<div
|
|
class="column is-half"
|
|
style="border-left: 1px solid #dbdbdb; padding-left: 2rem"
|
|
>
|
|
<div class="mb-5">
|
|
<p class="title is-5 mb-3">Thông tin Sản phẩm</p>
|
|
<div class="columns is-multiline is-mobile">
|
|
<div class="column is-half">
|
|
<strong>Mã thương mại:</strong>
|
|
{{ productData.trade_code || "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>Số tờ thửa:</strong>
|
|
{{ productData.land_lot_code || "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>Mã quy hoạch:</strong>
|
|
{{ productData.zone_code || "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>Phân khu:</strong>
|
|
{{ productData.zone_type__name || "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>Loại sản phẩm:</strong>
|
|
{{ productData.type__name || "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>Hướng cửa:</strong>
|
|
{{ productData.direction__name || "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>Kích thước lô:</strong>
|
|
{{ productData.land_lot_size || "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>Diện tích đất:</strong>
|
|
{{ productData.lot_area ? $numtoString(productData.lot_area) + " m²" : "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>DT xây dựng:</strong>
|
|
{{ productData.building_area ? $numtoString(productData.building_area) + " m²" : "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>DT sàn:</strong>
|
|
{{ productData.total_built_area ? $numtoString(productData.total_built_area) + " m²" : "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>Tầng cao:</strong>
|
|
{{ productData.number_of_floors || "-" }}
|
|
</div>
|
|
<div class="column is-half">
|
|
<strong>Trạng thái:</strong>
|
|
{{ productData.status__name || "-" }}
|
|
</div>
|
|
<div class="column is-half"><strong>Dự án:</strong> {{ productData.project__name || "-" }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="">
|
|
<div class="columns is-vcentered">
|
|
<div class="column">
|
|
<div class="field">
|
|
<label class="label">Số tiền thanh toán</label>
|
|
<div class="control">
|
|
<InputNumber
|
|
:record="paymentRecord"
|
|
attr="paymentAmount"
|
|
placeholder="Nhập số tiền"
|
|
:disabled="isPaymentInputDisabled"
|
|
@number="updatePaymentAmount"
|
|
:class="{ 'is-danger': !isPaymentAmountValid }"
|
|
></InputNumber>
|
|
</div>
|
|
<p
|
|
v-if="!isPaymentAmountValid"
|
|
class="help is-danger"
|
|
>
|
|
{{ paymentAmountError }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column">
|
|
<div class="field">
|
|
<label class="label">Ngày ký hợp đồng</label>
|
|
<p class="control has-text-weight-bold pt-2">
|
|
{{ formatDate(initialContractDate) }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<div class="column">
|
|
<div class="field">
|
|
<label class="label">Ngày hết hạn giao dịch</label>
|
|
<Datepicker
|
|
:record="dateRecord"
|
|
attr="dueDate"
|
|
@date="updateDueDate"
|
|
position="is-top-right"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="column">
|
|
<div class="field">
|
|
<label class="label">Mã xác nhận</label>
|
|
<div class="control">
|
|
<div class="field has-addons">
|
|
<div class="control is-expanded">
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
placeholder="Nhập mã"
|
|
v-model="userInputCaptcha"
|
|
/>
|
|
</div>
|
|
<div class="control">
|
|
<a
|
|
class="button is-static has-text-weight-bold has-background-grey-lighter"
|
|
style="font-family: "Courier New", monospace; letter-spacing: 2px"
|
|
>
|
|
{{ captchaCode }}
|
|
</a>
|
|
</div>
|
|
<div class="control">
|
|
<button
|
|
class="button"
|
|
@click="generateCaptcha"
|
|
:title="'Tạo mã mới'"
|
|
>
|
|
<span class="icon">
|
|
<SvgIcon
|
|
v-bind="{
|
|
name: 'refresh.svg',
|
|
type: 'primary',
|
|
size: 23,
|
|
}"
|
|
></SvgIcon>
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="is-flex is-justify-content-flex-end mt-4">
|
|
<button
|
|
class="button has-text-white is-success mr-2"
|
|
:disabled="!isConfirmed"
|
|
@click="handleConfirm"
|
|
>
|
|
<span>Tạo giao dịch</span>
|
|
</button>
|
|
<button
|
|
class="button"
|
|
@click="$emit('close')"
|
|
>
|
|
Hủy
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { useStore } from "@/stores/index";
|
|
import dayjs from "dayjs";
|
|
import Datepicker from "~/components/datepicker/Datepicker.vue";
|
|
import InputNumber from "~/components/common/InputNumber.vue";
|
|
import SearchBox from "~/components/SearchBox";
|
|
|
|
export default {
|
|
components: { Datepicker, InputNumber, SearchBox },
|
|
props: {
|
|
productData: { type: Object, required: true },
|
|
phaseInfo: { type: Object, required: true },
|
|
selectedPolicy: { type: Object, required: true },
|
|
originPrice: { type: Number, required: true },
|
|
discountValueDisplay: { type: Number, required: true },
|
|
selectedCustomer: { type: Object, required: true },
|
|
initialContractDate: { type: String, required: true },
|
|
},
|
|
setup() {
|
|
const store = useStore();
|
|
return { store };
|
|
},
|
|
emits: ["close", "modalevent"],
|
|
data() {
|
|
const initialDueDate = dayjs(this.initialContractDate).add(0, "day").format("YYYY-MM-DD");
|
|
|
|
return {
|
|
editableDueDate: initialDueDate,
|
|
dateRecord: { dueDate: initialDueDate },
|
|
captchaCode: "",
|
|
userInputCaptcha: "",
|
|
paymentRecord: { paymentAmount: null },
|
|
coOwner: null,
|
|
relatedPeople: [],
|
|
peopleAddon: {
|
|
component: "people/People",
|
|
width: "65%",
|
|
height: "600px",
|
|
title: "Người liên quan",
|
|
},
|
|
};
|
|
},
|
|
watch: {
|
|
depositAmount: {
|
|
immediate: true,
|
|
handler(newVal) {
|
|
if (this.phaseInfo.id === "p0" && newVal === 0) {
|
|
this.paymentRecord.paymentAmount = null;
|
|
} else {
|
|
this.paymentRecord.paymentAmount = newVal;
|
|
}
|
|
},
|
|
},
|
|
selectedCustomer: {
|
|
immediate: true,
|
|
async handler(newVal) {
|
|
if (newVal && newVal.id) {
|
|
await this.fetchRelatedPeople();
|
|
} else {
|
|
this.relatedPeople = [];
|
|
this.coOwner = null;
|
|
}
|
|
},
|
|
},
|
|
},
|
|
computed: {
|
|
isIndividual() {
|
|
return this.selectedCustomer?.type === 1;
|
|
},
|
|
paymentAmount() {
|
|
return this.paymentRecord.paymentAmount;
|
|
},
|
|
isPaymentInputDisabled() {
|
|
return this.phaseInfo.id !== "p0";
|
|
},
|
|
isPaymentAmountValid() {
|
|
return this.paymentAmount > 0;
|
|
},
|
|
paymentAmountError() {
|
|
if (this.isPaymentAmountValid) return "";
|
|
return "Số tiền thanh toán phải lớn hơn 0.";
|
|
},
|
|
isConfirmed() {
|
|
return (
|
|
this.userInputCaptcha.toLowerCase() === this.captchaCode.toLowerCase() &&
|
|
this.userInputCaptcha !== "" &&
|
|
this.isPaymentAmountValid
|
|
);
|
|
},
|
|
depositAmount() {
|
|
return this.selectedPolicy?.deposit || 0;
|
|
},
|
|
},
|
|
created() {
|
|
this.generateCaptcha();
|
|
},
|
|
methods: {
|
|
async fetchRelatedPeople() {
|
|
try {
|
|
const { $getdata } = useNuxtApp();
|
|
const apiName = this.isIndividual ? "customerpeople" : "legalrep";
|
|
const filterKey = this.isIndividual ? "customer" : "organization";
|
|
|
|
let customerId = this.selectedCustomer.id;
|
|
|
|
// Nếu là organization, cần lấy organization ID
|
|
if (!this.isIndividual) {
|
|
const org = await $getdata("organization", { customer: customerId }, undefined, true);
|
|
if (org) {
|
|
customerId = org.id;
|
|
} else {
|
|
this.relatedPeople = [];
|
|
this.coOwner = null;
|
|
return;
|
|
}
|
|
}
|
|
|
|
const people = await $getdata(apiName, { [filterKey]: customerId });
|
|
this.relatedPeople = Array.isArray(people) ? people : people ? [people] : [];
|
|
|
|
// Auto-select người đầu tiên nếu có
|
|
if (this.relatedPeople.length > 0) {
|
|
this.coOwner = this.relatedPeople[0];
|
|
} else {
|
|
this.coOwner = null;
|
|
}
|
|
} catch (error) {
|
|
console.error("Error fetching related people:", error);
|
|
this.relatedPeople = [];
|
|
this.coOwner = null;
|
|
}
|
|
},
|
|
clearCoOwner() {
|
|
this.coOwner = null;
|
|
},
|
|
selectCoOwnerByPerson(selectedPerson) {
|
|
// selectedPerson là một record từ customerpeople
|
|
this.coOwner = selectedPerson || null;
|
|
},
|
|
formatDate(date) {
|
|
return date ? dayjs(date).format("DD/MM/YYYY") : "-";
|
|
},
|
|
updateDueDate(newDate) {
|
|
this.editableDueDate = newDate;
|
|
this.dateRecord.dueDate = newDate;
|
|
},
|
|
updatePaymentAmount(value) {
|
|
this.paymentRecord.paymentAmount = value;
|
|
},
|
|
generateCaptcha() {
|
|
this.captchaCode = Math.random().toString(36).substring(2, 7).toUpperCase();
|
|
this.userInputCaptcha = "";
|
|
},
|
|
handleConfirm() {
|
|
if (this.isConfirmed) {
|
|
this.$emit("modalevent", {
|
|
name: "confirm",
|
|
data: {
|
|
currentDate: this.initialContractDate,
|
|
dueDate: this.editableDueDate,
|
|
paymentAmount: this.depositAmount,
|
|
depositReceived: this.paymentAmount,
|
|
people: this.isIndividual && this.coOwner ? this.coOwner.people : null,
|
|
},
|
|
});
|
|
}
|
|
},
|
|
},
|
|
};
|
|
</script>
|