This commit is contained in:
Viet An
2026-06-23 14:06:29 +07:00
parent e9fcfbe610
commit 059f232283
10 changed files with 441 additions and 118 deletions

View File

@@ -3,9 +3,10 @@ export default function useCustomer({ activeCart, getCarts }) {
const addresses = ref([]);
async function getAddresses() {
addresses.value = await $getdata("Customer_Address", {
filter: { customer: activeCart.value.customer },
});
addresses.value =
(await $getdata("Customer_Address", {
filter: { customer: activeCart.value.customer },
})) || [];
}
const isChangingCus = ref(false);

View File

@@ -0,0 +1,236 @@
export default function usePlaceOrder({ activeCart, activeCartItems, orderInfo, getCarts, onSuccess }) {
const { $dayjs, $id, $getdata, $insertapi, $patchapi, $deleteapi, $snackbar } = useNuxtApp();
const isPending = ref(false);
const paidPaymentStatus = ref();
const invoice = ref();
const showVietQRModal = ref();
const subtotal = computed(() => {
return activeCartItems.value?.reduce((prev, curr) => prev + curr.imei__variant__price, 0);
});
const shipping_address = computed(() => {
if (!orderInfo.value.address) return "";
return `${orderInfo.value.address.address_detail}, ${orderInfo.value.address.ward}, ${orderInfo.value.address.district}, ${orderInfo.value.address.city}`;
});
/**
* Create a record in `invoice` as `pending`
*/
async function createOrder() {
try {
isPending.value = true;
const invoicePayload = {
customer: activeCart.value.customer,
customer_name: activeCart.value.customer__fullname,
customer_phone: activeCart.value.customer__phone,
customer_email: activeCart.value.customer__email,
shipping_address: shipping_address.value,
product_amount: activeCartItems.value.length,
shipping_fee: 0,
total_amount: subtotal.value,
discount_amount: 0,
final_amount: subtotal.value,
order_type: "pos",
status: "pending",
payment_status: 1,
staff: 1,
ordered_at: new Date(),
paid_at: null,
delivery_method: orderInfo.value.deliveryMethod.id,
};
const invoice = await $insertapi("Invoice", {
data: invoicePayload,
notify: false,
});
return invoice;
} catch (error) {
console.error(error);
$snackbar("Tạo đơn hàng không thành công", "Error");
} finally {
isPending.value = false;
}
}
/**
* Mark sold IMEIs, create invoice details, delivery info, payment records, reset cart.
* Runs when order payment is confirmed.
* @param invoiceId id of created invoice
*/
async function finalizeOrder(invoiceId) {
if (!invoiceId) {
console.error("invoiceId is required");
return;
}
const imeisSold = await markImeisSold();
await createInvoiceDetails(invoiceId, imeisSold);
await createPaymentRecord(invoiceId);
if (orderInfo.value.deliveryMethod.code === "HOME_DELIVERY") {
await createDeliveryInfo(invoiceId);
}
// update invoice.payment_status from PENDING to PAID
const patchedInvoice = await $patchapi("Invoice", { id: invoiceId, payment_status: paidPaymentStatus.value.id });
$snackbar("Khởi tạo đơn hàng thành công", "Success");
resetCart();
onSuccess();
}
async function markImeisSold() {
const imeisSoldPayload = activeCartItems.value.map((cartItem) => ({
imei: cartItem.imei__imei,
variant: cartItem.imei__variant,
sold_date: $dayjs().format("YYYY-MM-DD"),
}));
const imeisSold = await $insertapi("IMEI_Sold", {
data: imeisSoldPayload,
notify: false,
});
return imeisSold;
}
async function createInvoiceDetails(invoiceId, imeisSold) {
const invoiceDetailPayload = activeCartItems.value.map((cartItem) => {
const imeiSold = imeisSold.find((imeiSold) => imeiSold.imei === cartItem.imei__imei);
return {
invoice: invoiceId,
variant: cartItem.imei__variant,
imei_sold: imeiSold.id,
price: cartItem.imei__variant__price,
};
});
const invoiceDetails = await $insertapi("Invoice_Detail", {
data: invoiceDetailPayload,
notify: false,
});
return invoiceDetails;
}
async function createDeliveryInfo(invoiceId) {
const deliveryInfoPayload = {
invoice: invoiceId,
receiver_name: orderInfo.value.receiver_name,
receiver_phone: orderInfo.value.receiver_phone,
city: orderInfo.value.address.city,
district: orderInfo.value.address.district,
ward: orderInfo.value.address.ward,
address: orderInfo.value.address.address_detail,
shipping_fee: 0,
carrier: orderInfo.value.deliveryMethod.id,
tracking_code: $id(),
status: "pending",
staff: 1,
};
const deliveryInfo = await $insertapi("Delivery_Info", {
data: deliveryInfoPayload,
notify: false,
});
return deliveryInfo;
}
async function createPaymentRecord(invoiceId) {
const paymentRecord = await $insertapi("Payment_Record", {
data: {
invoice: invoiceId,
payment_method: orderInfo.value.paymentMethod.id,
transfer_amount: orderInfo.value.paymentMethod.code === "CASH" ? undefined : subtotal.value,
cash_amount: orderInfo.value.paymentMethod.code === "CASH" ? subtotal.value : undefined,
},
notify: false,
});
return paymentRecord;
}
async function resetCart() {
await Promise.all([
$deleteapi(
"Cart_Item",
activeCartItems.value.map((c) => c.id),
),
$patchapi("Cart", {
id: activeCartItems.value[0].cart,
customer: null,
}),
]);
getCarts();
}
async function placeOrder() {
invoice.value = await createOrder();
if (orderInfo.value.paymentMethod.code === "CASH") {
finalizeOrder(invoice.value.id);
} else if (orderInfo.value.paymentMethod.code === "VIETQR") {
showVietQRModal.value = {
component: "pos/VietQR",
title: "Thanh toán qua VietQR",
width: "700px",
vbind: { invoiceId: invoice.value.id, finalizeOrder }, // verify thủ công?
};
} else if (orderInfo.value.paymentMethod.code === "VNPAY") {
try {
const { paymentUrl } = await $fetch("/api/vnpay/create-payment", {
method: "POST",
body: {
amount: invoice.value.total_amount,
invoiceCode: invoice.value.code,
},
});
window.open(paymentUrl, "vnpaypopup", "popup=true,width=900,height=700");
listenToVnpayEvents();
} catch (error) {
console.error(error);
}
}
}
function vnpayListener(e) {
console.log("main window received e.data from popup", e.data);
const { type, message } = e.data;
if (type === "vnpay-success") {
finalizeOrder(invoice.value.id);
}
if (type === "vnpay-error") {
$snackbar(`Thanh toán VNPAY thất bại: ${message}`, "Error");
}
}
function listenToVnpayEvents() {
window.removeEventListener("message", vnpayListener);
window.addEventListener("message", vnpayListener);
}
function testReturnPage() {
listenToVnpayEvents();
const vnp_ResponseCode = "01";
window.open(
`http://localhost:3000/vnpay-return?vnp_Amount=1000000&vnp_BankCode=NCB&vnp_BankTranNo=VNP15593296&vnp_CardType=ATM&vnp_OrderInfo=Thanh+to%C3%A1n+%C4%91%C6%A1n+h%C3%A0ng+HD000001&vnp_PayDate=20260622142046&vnp_ResponseCode=${vnp_ResponseCode}&vnp_TmnCode=SCG4UUS6&vnp_TransactionNo=15593296&vnp_TransactionStatus=00&vnp_TxnRef=HD000001-2026-06-22T07:20:14.735Z&vnp_SecureHash=9726fe2db0da9741fc14fc42ced098cabf204f21ab12f633e8ac1cb7c9567a3b7a379d790f29a1a8b60fa8edb24acb0faca83feca0f7757865b0bf853b55ea01`,
"return",
"popup=true,width=800,height=600",
);
}
onMounted(async () => {
paidPaymentStatus.value = await $getdata("Payment_Status", {
filter: { code: "PAID" },
first: true,
});
});
return { isPending, subtotal, shipping_address, placeOrder, testReturnPage, showVietQRModal };
}