import { formatDateYMD } from '@/utils/appointmentHelper' export function formatKdrq(date) { const d = date instanceof Date ? date : new Date(date) if (Number.isNaN(d.getTime())) return formatKdrq(new Date()) const pad = n => String(n).padStart(2, '0') return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}` } export function buildBillingKdrqRange(dateRange) { if (!dateRange || dateRange.length !== 2) return '' return `${dateRange[0]},${dateRange[1]}` } /** 品项总价合计 */ export function calcItemsTotal(form) { return (form.items || []).reduce((s, it) => s + (parseFloat(it.totalPrice) || 0), 0) } /** 储扣明细合计 */ export function calcDeductTotal(form) { if (form.isDeduct !== '是') return 0 return (form.deductItems || []).reduce((s, d) => { if (!d || !d.deductId) return s return s + (parseFloat(d.price) || 0) * (Number(d.quantity) || 0) }, 0) } /** 实付业绩 = 品项总价 - 储扣(与小程序 lx.vue 一致) */ export function calcSfyj(form) { const val = calcItemsTotal(form) - calcDeductTotal(form) return Math.max(0, val) } /** 欠款 = 整单业绩 - 实付 - 储扣合计(与小程序一致) */ export function calcQk(form) { const zdyj = Number(form.totalPerformance) || 0 const sfyj = calcSfyj(form) const deduct = calcDeductTotal(form) const val = zdyj - sfyj - deduct return Math.max(0, val) } function extractJsjNames(form) { const names = [] ;(form.items || []).forEach(it => { ;(it.workers || []).forEach(w => { const n = (w.jsjName || '').trim() if (n && !names.includes(n)) names.push(n) }) }) return names.length ? names.join(',') : '无' } function mapPxmxItem(it, form) { const qty = Number(it.quantity) || 1 const totalPrice = parseFloat(it.totalPrice) || 0 const pxjg = qty > 0 ? totalPrice / qty : totalPrice const lqKdJksyjList = (it.workers || []) .filter(w => w && w.workerId) .map(w => { const name = w.workerName || '' return { jks: name, jksxm: name, jkszh: w.workerId, jksyj: String(w.amount != null && w.amount !== '' ? w.amount : 0), jsj_id: w.jsjId || '' } }) const lqKdKjbsyjList = (it.kjbWorkers || []) .filter(k => k && k.workerId) .map(k => ({ kjbls: k.workerId, kjblsxm: k.workerName || '', kjblszh: k.workerId, kjblsyj: String(k.amount != null && k.amount !== '' ? k.amount : 0) })) return { px: String(it.projectId), pxmc: it.label || '', pxjg, actualPrice: totalPrice, projectNumber: qty, sourceType: it.type || '购买', remark: it.remark || '', memberId: form.memberId, qt2: it.qt2 || '', lqKdJksyjList, lqKdKjbsyjList } } /** * 门店 PC 开单表单 → LqKdKdjlbCrInput(对齐小程序 pagesA/lx/lx.vue) */ export function buildBillingSubmitBody(form, { storeId, member }) { const zdyj = Number(form.totalPerformance) || 0 const sfyj = calcSfyj(form) const qk = calcQk(form) const deductTotal = calcDeductTotal(form) const lqKdPxmxList = (form.items || []) .filter(it => it && it.projectId) .map(it => mapPxmxItem(it, form)) const lqKdKdjlbDeductList = form.isDeduct === '是' ? (form.deductItems || []) .filter(d => d && d.deductId) .map(d => { const qty = Number(d.quantity) || 1 const unit = parseFloat(d.price) || 0 return { DeductType: d.deductType || '品项', DeductId: d.deductId, Amount: unit * qty, UnitPrice: unit, ProjectNumber: qty, ItemName: d.label || '', ItemId: d.itemId || '' } }) : [] const isCoop = form.paymentMethod === '合作' const paidSfyj = sfyj return { activityId: form.activityId || '', djmd: storeId, jsj: extractJsjNames(form), kdrq: formatKdrq(form.billingDate), gjlx: (member && member.type) || form.memberType || '', kdhy: form.memberId, kdhyc: (member && member.label) || form.memberName || '', kdhysjh: (member && member.phone) || form.memberPhone || '', zdyj, sfyj, qk, ckfs: form.isDeduct === '是' ? (form.deductMode || '储扣') : '否', ckmx: form.isDeduct === '是' ? String(deductTotal) : '', fkfs: form.paymentMethod || '', hgjg: isCoop ? (form.cooperateOrg || '') : '', fkyy: !isCoop ? (form.settleOrg || '') : '', sfskdd: paidSfyj > 0 ? (form.isFirstOrder || '否') : '否', jj: form.intro || '', bz: form.remark || '', scwj: form.scwj || [], hyqz: form.hyqz || [], f_FileUrl: form.f_FileUrl ? JSON.stringify(form.f_FileUrl) : '', appointmentId: form.appointmentId || '', lqKdPxmxList, lqKdKdjlbDeductList } } /** * 开单校验(对齐小程序 validatePxList + 提交前校验) */ export function validateBillingForm(form) { if (!form.memberId) return '请选择开单会员' if (!form.billingDate) return '请选择开单日期' if (form.totalPerformance === '' || form.totalPerformance == null) return '请输入整单业绩' const items = (form.items || []).filter(x => x && x.projectId) if (!items.length) return '请至少添加 1 个品项' let pxSum = 0 for (let i = 0; i < items.length; i++) { const px = items[i] const idx = i + 1 if (!px.projectId || !px.label) return `第 ${idx} 个品项请选择品项` const pxAmount = parseFloat(px.totalPrice) || 0 pxSum += pxAmount const workers = (px.workers || []).filter(w => w && w.workerId) if (!workers.length) return `第 ${idx} 个品项请至少添加一个健康师业绩` for (let j = 0; j < workers.length; j++) { const w = workers[j] if (w.amount === '' || w.amount == null) return `第 ${idx} 个品项的第 ${j + 1} 个健康师业绩必须填写` const yj = parseFloat(w.amount) if (Number.isNaN(yj) || yj < 0) return `第 ${idx} 个品项的第 ${j + 1} 个健康师业绩必须为有效数字` } if (px.qt2 === '科美') { const kjbs = (px.kjbWorkers || []).filter(k => k && k.workerId) if (!kjbs.length) return `第 ${idx} 个品项是科美品项,必须至少选择一个科技部老师` for (let k = 0; k < kjbs.length; k++) { const kb = kjbs[k] if (kb.amount === '' || kb.amount == null) return `第 ${idx} 个品项的第 ${k + 1} 个科技部老师业绩必须填写` const yj = parseFloat(kb.amount) if (Number.isNaN(yj) || yj < 0) return `第 ${idx} 个品项的第 ${k + 1} 个科技部老师业绩必须为有效数字` } } } const zdyj = Number(form.totalPerformance) || 0 const pxSumRounded = Math.round(pxSum * 100) / 100 const zdyjRounded = Math.round(zdyj * 100) / 100 if (zdyjRounded < pxSumRounded) { return `整单业绩(${zdyj})要大于等于品项之和的总价(${pxSumRounded.toFixed(2)})` } const ds = form.isDeduct === '是' ? (form.deductItems || []).filter(d => d && d.deductId) : [] if (form.isDeduct === '是') { if (!ds.length) return '请添加储扣明细' for (let i = 0; i < ds.length; i++) { const d = ds[i] if (!d.deductId) return `第 ${i + 1} 个储扣明细请选择品项` const qty = parseInt(d.quantity, 10) || 0 if (qty <= 0) return `第 ${i + 1} 个储扣明细数量必须大于 0` if (d.maxQty != null && qty > d.maxQty) { return `第 ${i + 1} 个储扣明细数量不能超过剩余次数(${d.maxQty})` } } const ids = ds.map(d => d.deductId) if (new Set(ids).size !== ids.length) return '储扣品项不能重复添加' } const sfyj = calcSfyj(form) let jksAmount = 0 let kjbAmount = 0 let hasKjbKeMei = false items.forEach(px => { ;(px.workers || []).forEach(w => { jksAmount += parseFloat(w.amount) || 0 }) if (px.qt2 === '科美' && px.type === '购买') { hasKjbKeMei = true ;(px.kjbWorkers || []).forEach(k => { kjbAmount += parseFloat(k.amount) || 0 }) } }) if (form.isDeduct === '是' && ds.length) { if (Math.abs(jksAmount - sfyj) > 0.01) { return `总健康师业绩(${jksAmount.toFixed(2)})必须等于实付业绩(${sfyj.toFixed(2)})` } if (hasKjbKeMei && Math.abs(kjbAmount - sfyj) > 0.01) { return `总科技部业绩(${kjbAmount.toFixed(2)})必须等于实付业绩(${sfyj.toFixed(2)})` } } else { for (let i = 0; i < items.length; i++) { const px = items[i] const pxAmount = parseFloat(px.totalPrice) || 0 let itemJks = 0 ;(px.workers || []).forEach(w => { itemJks += parseFloat(w.amount) || 0 }) if (Math.abs(pxAmount - itemJks) > 0.01) { return `第 ${i + 1} 个品项总价(${pxAmount.toFixed(2)})必须等于该品项健康师业绩之和(${itemJks.toFixed(2)})` } if (px.qt2 === '科美') { let itemKjb = 0 ;(px.kjbWorkers || []).forEach(k => { itemKjb += parseFloat(k.amount) || 0 }) if (Math.abs(pxAmount - itemKjb) > 0.01) { return `第 ${i + 1} 个科美品项总价(${pxAmount.toFixed(2)})必须等于该品项科技部业绩之和(${itemKjb.toFixed(2)})` } } } } if (!form.paymentMethod) return '请选择付款方式' return '' } /** 开单日期字符串(yyyy-MM-dd)供金三角接口 */ export function billingDateYmd(form) { if (!form.billingDate) return formatDateYMD(new Date()) const d = form.billingDate instanceof Date ? form.billingDate : new Date(form.billingDate) if (Number.isNaN(d.getTime())) return formatDateYMD(new Date()) return formatDateYMD(d) }