import { ref } from 'vue'; import { useNuxtApp } from '#app'; import dayjs from 'dayjs'; // Interface cho các tham số từ UI interface TransactionParams { product: any; customer: any; policy: any; phaseInfo: any; priceAfterDiscount: number; discountValue: number; detailedDiscounts: any[]; paymentPlans: any[]; currentDate: string; reservationDueDate: string; reservationAmount: number; depositReceived: number; people?: number | null; earlyDiscountAmount?: number; gifts?: Array<{ id: number }>; // Thêm trường gifts } export function useAdvancedWorkflow() { const { $insertapi, $snackbar, $store } = useNuxtApp(); const isLoading = ref(false); const error = ref(null); /** * Helper: Tìm kết quả của một step cụ thể trong mảng 'result' của backend trả về */ const findStepResult = ( steps: any[], stepCode: string, filter?: string | ((res: any) => boolean) ) => { if (!Array.isArray(steps)) return null; // 1. Tìm đúng step đang thực thi const step = steps.find(s => (s.step === stepCode || s.step?.startsWith(stepCode)) && s.executed); if (!step || !Array.isArray(step.results) || step.results.length === 0) return null; // 2. Nếu filter là một hàm (Callback) if (typeof filter === 'function') { const matched = step.results.find(filter); return matched ? matched.result : null; } // 3. Nếu filter là một chuỗi (Action Name) if (typeof filter === 'string') { const matched = step.results.find(r => r.action === filter); return matched ? matched.result : null; } // 4. Mặc định: Trả về result của phần tử đầu tiên return step.results[0]?.result; }; /** * Chuyển đổi danh sách chiết khấu sang format API */ const mapDiscountsList = (detailedDiscounts: any[]) => { if (!detailedDiscounts) return []; return detailedDiscounts .filter(d => d.id) .map(d => ({ discount: d.id, value: d.customValue, type: d.customType, })); }; /** * Tạo lịch thanh toán cọc (Installments) */ const createInstallments = (depositReceived: number, reservationAmount: number) => { const installments = []; if (depositReceived > 0) { installments.push({ amount: depositReceived, due_days: 0, description: "Thanh toán tiền đặt cọc đã nhận", detail: { note: "Thanh toán tiền đặt cọc" } }); } const remaining = reservationAmount - depositReceived; if (remaining > 0) { installments.push({ amount: remaining, due_days: 2, description: "Phần cọc còn lại", detail: { note: "Thanh toán phần cọc còn lại" } }); } return installments; }; const createWorkflowTransaction = async (params: TransactionParams) => { isLoading.value = true; error.value = null; try { const userId = $store?.login?.id; if (!userId) { throw new Error('Không tìm thấy thông tin người dùng. Vui lòng đăng nhập lại.'); } // 1. Cấu hình Body theo đúng workflow const workflowPayload = { workflow_code: "FULL_CONTRACT_CREATION", trigger: "create", data: { phase_code: params.phaseInfo.code, current_date: params.currentDate, due_date: params.reservationDueDate, customer_id: params.customer.id, product_id: params.product.id, policy_id: params.policy.id, user_id: userId, sale_price: params.product.origin_price, origin_price: params.product.origin_price, deposit_amount: params.reservationAmount, discount_amount: 0, amount_received: 0, payment_plan: params.paymentPlans, installments: createInstallments(params.depositReceived, params.reservationAmount), people: params.people || null, early_discount_amount: Math.trunc(params.earlyDiscountAmount || 0), gifts: params.gifts || [] // Thêm danh sách quà tặng } }; // 2. Gọi API Workflow const response = await $insertapi('workflow', workflowPayload, undefined, false); // Kiểm tra thành công if (response === 'error' || !response?.success) { throw new Error(response?.message || 'Thực thi Workflow thất bại.'); } // 3. Bóc tách dữ liệu từ mảng result trả về const steps = response.result; // Lấy Transaction từ step "create_transaction" với action "API_CALL" const txnData = findStepResult(steps, 'create_transaction', 'API_CALL'); console.log('Transaction Data:', txnData); $snackbar('Giao dịch đã được khởi tạo thành công!', 'Thành công', 'Success'); // 4. Return transaction data return { transaction: txnData ? { id: txnData.id, code: txnData.code } : null, }; } catch (e: any) { error.value = e; console.error('Workflow Error:', e); $snackbar(e.message || 'Có lỗi xảy ra khi thực thi workflow.', 'Lỗi', 'Error'); return null; } finally { isLoading.value = false; } }; /** * DUYỆT CÔNG NỢ (KẾ TOÁN) - SỬ DỤNG WORKFLOW */ const confirmPaymentSchedule = async (paymentId: number, payload?: object): Promise => { isLoading.value = true; error.value = null; try { const userId = $store?.login?.id; if (!userId) { throw new Error('Không tìm thấy thông tin người dùng. Vui lòng đăng nhập lại.'); } const workflowPayload = { workflow_id: 9, workflow_code: "APPROVE_PAYMENT", trigger: "create", data: { payment_id: paymentId, user_id: userId, ...payload } }; const response = await $insertapi('workflow', workflowPayload, undefined, false); if (response === 'error' || !response?.success) { throw new Error(response?.message || 'Không thể xác nhận công nợ qua workflow.'); } $snackbar('Công nợ đã được xác nhận thành công!', 'Thành công', 'Success'); return true; } catch (e: any) { error.value = e; console.error('Workflow Error (confirmPaymentSchedule):', e); $snackbar(e.message || 'Có lỗi xảy ra khi xác nhận công nợ.', 'Lỗi', 'Error'); return false; } finally { isLoading.value = false; } }; const rollbackPayment = async (paymentId: number, entryCode: string, entryAmount: number) => { isLoading.value = true; error.value = null; try { const userId = $store?.login?.id; if (!userId) { throw new Error('Không tìm thấy thông tin người dùng. Vui lòng đăng nhập lại.'); } const workflowPayload = { workflow_code: "ROLLBACK_PAYMENT", trigger: "create", data: { payment_id: paymentId, entry_code: entryCode, amount: entryAmount, } }; const response = await $insertapi('workflow', workflowPayload, undefined, false); if (response === 'error' || !response?.success) { throw new Error(response?.message || 'Không thể hủy bút toán qua workflow.'); } $snackbar('Công nợ đã được xác nhận thành công!', 'Thành công', 'Success'); return true; } catch (e: any) { error.value = e; console.error('Workflow Error (confirmPaymentSchedule):', e); $snackbar(e.message || 'Có lỗi xảy ra khi xác nhận công nợ.', 'Lỗi', 'Error'); return false; } finally { isLoading.value = false; } } /** * DUYỆT CHI TIẾT GIAO DỊCH (QUẢN LÝ) - SỬ DỤNG WORKFLOW */ const approveTransactionDetail = async (detailId: number, statusCode: string): Promise => { isLoading.value = true; error.value = null; try { const userId = $store?.login?.id; if (!userId) { throw new Error('Không tìm thấy thông tin người dùng. Vui lòng đăng nhập lại.'); } const workflowPayload = { workflow_code: "APPROVE_TRANSACTION_DETAIL", trigger: "create", data: { detail_id: detailId, status_code: statusCode, user_id: userId, } }; const response = await $insertapi('workflow', workflowPayload, undefined, false); if (response === 'error' || !response?.success) { throw new Error(response?.message || 'Không thể duyệt chi tiết giao dịch qua workflow.'); } $snackbar('Chi tiết giao dịch đã được duyệt thành công!', 'Thành công', 'Success'); return response; } catch (e: any) { error.value = e; console.error('Workflow Error (approveTransactionDetail):', e); $snackbar(e.message || 'Có lỗi xảy ra khi duyệt chi tiết giao dịch.', 'Lỗi', 'Error'); return null; } finally { isLoading.value = false; } }; /** * CHUYỂN GIAI ĐOẠN GIAO DỊCH - SỬ DỤNG WORKFLOW */ const advanceTransactionPhase = async (detailId: number): Promise => { isLoading.value = true; error.value = null; try { const userId = $store?.login?.id; if (!userId) { throw new Error('Không tìm thấy thông tin người dùng. Vui lòng đăng nhập lại.'); } const workflowPayload = { workflow_code: "ADVANCE_TRANSACTION_PHASE", trigger: "create", data: { detail_id: detailId, user_id: userId, } }; const response = await $insertapi('workflow', workflowPayload, undefined, false); if (response === 'error' || !response?.success) { throw new Error(response?.message || 'Không thể chuyển giai đoạn giao dịch qua workflow.'); } const steps = response.result; // 1. Tìm contract thông qua step bắt đầu bằng 'adv_from_phase_' với action là 'GENERATE_DOCUMENT' const contract = findStepResult( steps, 'adv_lookup_transaction', 'LOOKUP_DATA' ); console.log('contract', contract) $snackbar('Chuyển giai đoạn giao dịch thành công!', contract, 'Success'); return { ...response, contract: contract }; } catch (e: any) { error.value = e; console.error('Workflow Error (advanceTransactionPhase):', e); $snackbar(e.message || 'Có lỗi xảy ra khi chuyển giai đoạn.', 'Lỗi', 'Error'); return null; } finally { isLoading.value = false; } }; /** * CHUYỂN ĐỔI KHÁCH HÀNG - SỬ DỤNG WORKFLOW */ const updateTransactionCustomer = async ( transactionId: number, newCustomerId: number, contractDate?: string ): Promise => { isLoading.value = true; error.value = null; try { const userId = $store?.login?.id; if (!userId) { throw new Error('Không tìm thấy thông tin người dùng. Vui lòng đăng nhập lại.'); } const workflowPayload = { workflow_code: "UPDATE_TRANS_AND_CO_OP", trigger: "create", data: { transaction: transactionId, new_cus: newCustomerId, user_id: userId, date: contractDate || dayjs().format('YYYY-MM-DD'), } }; const response = await $insertapi('workflow', workflowPayload, undefined, false); if (response === 'error' || !response?.success) { throw new Error(response?.message || 'Không thể chuyển đổi khách hàng qua workflow.'); } const steps = response.result; // Find the contract from any executed step starting with 'GEN_' const contract = findStepResult( steps, 'GEN_', 'API_CALL' ); $snackbar('Chuyển đổi khách hàng thành công!', 'Thành công', 'Success'); return { ...response, contract: contract }; } catch (e: any) { error.value = e; console.error('Workflow Error (updateTransactionCustomer):', e); $snackbar(e.message || 'Có lỗi xảy ra khi chuyển đổi khách hàng.', 'Lỗi', 'Error'); return null; } finally { isLoading.value = false; } }; return { isLoading, error, createWorkflowTransaction, confirmPaymentSchedule, approveTransactionDetail, advanceTransactionPhase, updateTransactionCustomer, rollbackPayment }; }