更新
This commit is contained in:
@@ -102,6 +102,18 @@
|
||||
@keyup.enter="resetPage"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="添加时间">
|
||||
<el-date-picker
|
||||
v-model="addTimeRange"
|
||||
type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
clearable
|
||||
class="!w-[280px]"
|
||||
@change="onAddTimeRangeChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="标签">
|
||||
<el-select
|
||||
v-model="queryParams.tag_ids"
|
||||
@@ -557,12 +569,34 @@ const syncSettings = reactive({
|
||||
interval: 3600
|
||||
})
|
||||
|
||||
const queryParams = reactive<{ name: string; follow_user: string; tag_ids: string[] }>({
|
||||
const queryParams = reactive<{
|
||||
name: string
|
||||
follow_user: string
|
||||
tag_ids: string[]
|
||||
add_time_start: string
|
||||
add_time_end: string
|
||||
}>({
|
||||
name: '',
|
||||
follow_user: '',
|
||||
tag_ids: []
|
||||
tag_ids: [],
|
||||
add_time_start: '',
|
||||
add_time_end: ''
|
||||
})
|
||||
|
||||
/** 与列表「添加时间」列口径一致(库内 external_first_add_time / create_time) */
|
||||
const addTimeRange = ref<[string, string] | null>(null)
|
||||
|
||||
function onAddTimeRangeChange(val: [string, string] | null) {
|
||||
if (val && val.length === 2 && val[0] && val[1]) {
|
||||
queryParams.add_time_start = val[0]
|
||||
queryParams.add_time_end = val[1]
|
||||
} else {
|
||||
queryParams.add_time_start = ''
|
||||
queryParams.add_time_end = ''
|
||||
}
|
||||
resetPage()
|
||||
}
|
||||
|
||||
// ── 标签维度(筛选下拉 + 抽屉面板共用同一份数据) ──────────────────────────
|
||||
interface TagItem {
|
||||
tag_id: string
|
||||
@@ -793,6 +827,9 @@ function handleReset() {
|
||||
queryParams.name = ''
|
||||
queryParams.follow_user = ''
|
||||
queryParams.tag_ids = []
|
||||
queryParams.add_time_start = ''
|
||||
queryParams.add_time_end = ''
|
||||
addTimeRange.value = null
|
||||
resetParams()
|
||||
}
|
||||
|
||||
|
||||
@@ -370,7 +370,7 @@
|
||||
<el-tooltip placement="top" effect="dark" :show-after="200">
|
||||
<template #content>
|
||||
<div style="max-width: 320px; line-height: 1.7; font-size: 12px">
|
||||
仅<b>名称含「二中心」</b>的部门及其组织下级:候选订单为有诊单医助的非取消业务单,按患者 <b>create_time</b> 排序,<b>第 1 笔不计</b>;第 2 笔起计入合计,分列复诊2、复诊三…。<b>归属</b>按订单创建人的人事部门,与<b>合计业绩</b>列同口径。非二中心子树部门为 0。
|
||||
仅<b>名称含「二中心」</b>的部门及其组织下级:候选订单为有诊单医助、且 <b>fulfillment_status</b> 非 4/9/10(<b>NULL 计入</b>)的业务单,按患者 <b>create_time</b> 排序,<b>第 1 笔不计</b>;第 2 笔起计入合计,分列复诊2、复诊三…。<b>归属</b>按订单创建人的人事部门,与<b>合计业绩</b>列同口径。非二中心子树部门为 0。
|
||||
</div>
|
||||
</template>
|
||||
<el-icon class="col-info"><InfoFilled /></el-icon>
|
||||
@@ -395,7 +395,7 @@
|
||||
<el-tooltip placement="top" effect="dark" :show-after="200">
|
||||
<template #content>
|
||||
<div style="max-width: 300px; line-height: 1.7; font-size: 12px">
|
||||
按订单<b>创建时间</b>统计(剔除履约已取消),金额与<b>接诊诊单</b>列一致:仅按<b>订单创建人</b>的人事部门落在本展示行(含下级)计入,多行命中时取最深的展示部门。与<b>医助排行榜诊金</b>同口径。<br />
|
||||
按订单<b>创建时间</b>统计(剔除履约已取消/拒收/退款 4·9·10),金额与<b>接诊诊单</b>列一致:仅按<b>订单创建人</b>的人事部门落在本展示行(含下级)计入,多行命中时取最深的展示部门。与<b>医助排行榜诊金</b>同口径。<br />
|
||||
<b>不受渠道筛选影响</b>。与侧栏按部门打开的业务订单列表对齐。<br />
|
||||
点击金额可查看对应业务订单明细(侧栏默认「合计业绩」全量列表)。
|
||||
</div>
|
||||
@@ -409,7 +409,7 @@
|
||||
<template #content>
|
||||
<div style="max-width: 340px; line-height: 1.7; font-size: 12px">
|
||||
「{{ tb.channel_name }}」渠道业绩(与卡片标题渠道一致)。与「处方订单列表」业绩<b>同口径</b>:按订单
|
||||
<b>create_time</b> 归日,剔除履约已取消(4)。<br />
|
||||
<b>create_time</b> 归日,剔除履约状态 4(已取消)/9(拒收)/10(退款)。<br />
|
||||
渠道判定用 EXISTS:该患者有任一挂号 <b>channels 命中字典 + status=3</b>(不限挂号时点)。<br />
|
||||
点击金额可查看该部门本渠道订单明细(侧栏「渠道筛选」列表)。
|
||||
</div>
|
||||
@@ -446,9 +446,9 @@
|
||||
<template #content>
|
||||
<div style="max-width: 320px; line-height: 1.7; font-size: 12px">
|
||||
<<<<<<< HEAD
|
||||
<b>业务订单条数</b>(非取消):订单 <b>create_time</b> 落入区间、<b>fulfillment_status ≠ 4</b>;按<b>订单创建人</b>的人事部门落在该部门子树即计入(表格多行命中时取最深的展示部门)。与<b>合计业绩 / 医助排行榜接诊诊单</b>同口径。选定渠道时本列仍为全量。
|
||||
<b>业务订单条数</b>(计业绩):订单 <b>create_time</b> 落入区间、<b>fulfillment_status ∉ {4,9,10}</b>(<b>NULL 计入</b>);按<b>订单创建人</b>的人事部门落在该部门子树即计入(表格多行命中时取最深的展示部门)。与<b>合计业绩 / 医助排行榜接诊诊单</b>同口径。选定渠道时本列仍为全量。
|
||||
=======
|
||||
<b>业务订单条数</b>(非取消):订单 <b>create_time</b> 落入区间、<b>fulfillment_status ≠ 4</b>;与列表筛选
|
||||
<b>业务订单条数</b>(计业绩):订单 <b>create_time</b> 落入区间、<b>fulfillment_status ∉ {4,9,10}</b>;与列表筛选
|
||||
<b>assistant_dept_id</b> 时一致——<b>创建人</b>人事部门优先,无创建人则诊单 <b>医助</b>;落在该部门子树即计入(表格多行命中时取最深的展示部门)。与侧栏勾选「与表格业绩对齐」时的集合可能略有差异。选定渠道时本列仍为全量。
|
||||
>>>>>>> master
|
||||
</div>
|
||||
@@ -461,7 +461,7 @@
|
||||
<el-tooltip placement="top" effect="dark" :show-after="200">
|
||||
<template #content>
|
||||
<div style="max-width: 320px; line-height: 1.7; font-size: 12px">
|
||||
「{{ tb.channel_name }}」渠道成交单数;与渠道业绩同源:区间内 <b>非取消业务订单</b> 条数,按订单
|
||||
「{{ tb.channel_name }}」渠道成交单数;与渠道业绩同源:区间内 <b>计业绩的业务订单</b> 条数(剔除 4/9/10),按订单
|
||||
<b>create_time</b> 归日;渠道判定同业绩列。
|
||||
</div>
|
||||
</template>
|
||||
@@ -974,7 +974,7 @@
|
||||
>
|
||||
<span class="yeji-order-drawer__stat-label">
|
||||
业务订单金额合计
|
||||
<span class="yeji-order-drawer__stat-note">(不含履约已取消,与上方业绩口径一致)</span>
|
||||
<span class="yeji-order-drawer__stat-note">(不含履约 4·9·10,与上方业绩口径一致)</span>
|
||||
</span>
|
||||
<span class="yeji-order-drawer__stat-value yeji-order-drawer__stat-value--money">
|
||||
¥{{ formatOrderDrawerMoney(orderDrawerTotalAmount) }}
|
||||
@@ -2129,7 +2129,7 @@ type OrderDrawerFilter =
|
||||
}
|
||||
|
||||
const orderDrawerFilter = ref<OrderDrawerFilter | null>(null)
|
||||
/** 列表接口 extend,含 stats_order_amount_performance(与当前筛选、不含已取消口径一致) */
|
||||
/** 列表接口 extend,含 stats_order_amount_performance(与当前筛选一致:剔除履约 4/9/10) */
|
||||
const orderDrawerExtend = ref<Record<string, any> | null>(null)
|
||||
|
||||
const orderDrawerTotalAmount = computed(() => {
|
||||
@@ -2641,7 +2641,7 @@ function buildOrderDrawerListParams(): Record<string, any> | null {
|
||||
const params: Record<string, any> = {
|
||||
start_time: `${f.start} 00:00:00`,
|
||||
end_time: `${f.end} 23:59:59`,
|
||||
/** 与业绩列口径一致:不列出履约已取消(4) */
|
||||
/** 与业绩列口径一致:不列出履约 4(已取消)/9(拒收)/10(退款) */
|
||||
exclude_fulfillment_cancelled: 1,
|
||||
/**
|
||||
* 业绩看板入口标志:后端 PrescriptionOrderLists::applyDoctorAssistantFilters 看到此标志后,
|
||||
@@ -2760,7 +2760,7 @@ function openOrderDrawerByDept(tb: YejiTable, row: YejiRow, opts?: { listTab?: '
|
||||
yejiTableRowDeptIds: tb.rows.map(r => r.dept_id).filter((id): id is number => typeof id === 'number' && id > 0),
|
||||
}
|
||||
orderDrawerTitle.value = `业务订单 · ${row.dept_name}`
|
||||
orderDrawerHint.value = `创建时间:${tb.start_date} ~ ${tb.end_date} · 与「合计业绩 / 接诊诊单」同口径:订单创建人人事部门 ∈ 本部门子树 · 不含履约已取消`
|
||||
orderDrawerHint.value = `创建时间:${tb.start_date} ~ ${tb.end_date} · 与「合计业绩 / 接诊诊单」同口径:订单创建人人事部门 ∈ 本部门子树 · 不含履约 4/9/10`
|
||||
orderDrawerPage.value = 1
|
||||
orderDrawerVisible.value = true
|
||||
void fetchOrderDrawerPage()
|
||||
@@ -2784,7 +2784,7 @@ function openOrderDrawerByYejiTableScope(
|
||||
yejiTableRowDeptIds: tb.rows.map(r => r.dept_id).filter((id): id is number => typeof id === 'number' && id > 0),
|
||||
}
|
||||
orderDrawerTitle.value = '业务订单 · 当前展示合计'
|
||||
orderDrawerHint.value = `创建时间:${tb.start_date} ~ ${tb.end_date} · 已选展示部门 1 个根节点(含其下全部展示行)· 不含履约已取消`
|
||||
orderDrawerHint.value = `创建时间:${tb.start_date} ~ ${tb.end_date} · 已选展示部门 1 个根节点(含其下全部展示行)· 不含履约 4/9/10`
|
||||
orderDrawerPage.value = 1
|
||||
orderDrawerVisible.value = true
|
||||
void fetchOrderDrawerPage()
|
||||
@@ -2841,7 +2841,7 @@ function openOrderDrawerByAssistant(row: LeaderboardPack['leaderboards'][0]['row
|
||||
end,
|
||||
}
|
||||
orderDrawerTitle.value = `业务订单 · ${row.name}`
|
||||
orderDrawerHint.value = `创建时间:${start} ~ ${end} · 创建人=${row.name}(与排行榜诊金同口径)· 不含履约已取消`
|
||||
orderDrawerHint.value = `创建时间:${start} ~ ${end} · 创建人=${row.name}(与排行榜诊金同口径)· 不含履约 4/9/10`
|
||||
orderDrawerPage.value = 1
|
||||
orderDrawerVisible.value = true
|
||||
void fetchOrderDrawerPage()
|
||||
@@ -2865,7 +2865,7 @@ function openOrderDrawerByAssistantErCenterRevisit(
|
||||
}
|
||||
const slotLbl = revisitSlot === 0 ? '复诊合计' : yejiRevisitSlotColumnTitle(revisitSlot)
|
||||
orderDrawerTitle.value = `业务订单 · ${assistant.name} · ${slotLbl}`
|
||||
orderDrawerHint.value = `创建时间:${start} ~ ${end} · 创建人=${assistant.name}(与部门表 / 排行榜复诊同口径)· 仅二中心复诊口径 · ${slotLbl} · 不含履约已取消`
|
||||
orderDrawerHint.value = `创建时间:${start} ~ ${end} · 创建人=${assistant.name}(与部门表 / 排行榜复诊同口径)· 仅二中心复诊口径 · ${slotLbl} · 不含履约 4/9/10`
|
||||
orderDrawerPage.value = 1
|
||||
orderDrawerVisible.value = true
|
||||
void fetchOrderDrawerPage()
|
||||
|
||||
@@ -797,6 +797,32 @@ function buildTcmDiagnosisListRequestPayload(req: Record<string, unknown>): Reco
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 待分配「订单月份」:未选月份时,未进入 Tab 前默认当月(与 handlePendingAssignTabClick 一致);
|
||||
* 在 Tab 内清空月份表示不限月份。
|
||||
*/
|
||||
function resolvePendingAssignOrderMonthForRequest(): string {
|
||||
const trimmed = String(formData.pending_assign_order_month ?? '').trim()
|
||||
if (trimmed !== '') return trimmed
|
||||
if (formData.pending_assign === '1') return ''
|
||||
return dayjs().format('YYYY-MM')
|
||||
}
|
||||
|
||||
/** 待分配角标 count 请求:与列表同条件,且去掉其它顶部 Tab 残留(如默认「当天挂号」) */
|
||||
function buildPendingAssignCountPayload(): Record<string, unknown> {
|
||||
return buildTcmDiagnosisListRequestPayload({
|
||||
...formData,
|
||||
page_no: 1,
|
||||
page_size: 1,
|
||||
pending_assign: 1,
|
||||
appointment_date: '',
|
||||
has_appointment: '',
|
||||
pending_booking: '',
|
||||
completed_appointment: '',
|
||||
pending_assign_order_month: resolvePendingAssignOrderMonthForRequest()
|
||||
} as Record<string, unknown>) as Record<string, unknown>
|
||||
}
|
||||
|
||||
const fetchTcmDiagnosisListsForPaging = (req: Record<string, unknown>) =>
|
||||
tcmDiagnosisLists(buildTcmDiagnosisListRequestPayload(req) as any)
|
||||
|
||||
@@ -925,16 +951,7 @@ const fetchDateCounts = async () => {
|
||||
tcmDiagnosisLists({ page_no: 1, page_size: 1 }),
|
||||
tcmDiagnosisLists({ has_appointment: 0, page_no: 1, page_size: 1 }),
|
||||
tcmDiagnosisLists({ completed_appointment: 1, page_no: 1, page_size: 1 }),
|
||||
tcmDiagnosisLists(
|
||||
buildTcmDiagnosisListRequestPayload({
|
||||
pending_assign: 1,
|
||||
page_no: 1,
|
||||
page_size: 1,
|
||||
pending_assign_order_month: formData.pending_assign_order_month,
|
||||
pending_assign_keyword: formData.pending_assign_keyword,
|
||||
keyword: formData.keyword
|
||||
}) as any
|
||||
)
|
||||
tcmDiagnosisLists(buildPendingAssignCountPayload() as any)
|
||||
])
|
||||
dateCounts.value = {
|
||||
[yesterdayStr.value]: yesterday?.count ?? 0,
|
||||
|
||||
@@ -77,6 +77,23 @@ class CustomerLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
}
|
||||
}
|
||||
|
||||
// 添加时间:与 lists 排序口径一致(external_first_add_time 优先,0 则 create_time)
|
||||
$addStart = trim((string) ($this->params['add_time_start'] ?? ''));
|
||||
$addEnd = trim((string) ($this->params['add_time_end'] ?? ''));
|
||||
$effExpr = 'COALESCE(NULLIF(external_first_add_time, 0), create_time)';
|
||||
if ($addStart !== '') {
|
||||
$t = strtotime($addStart . ' 00:00:00');
|
||||
if ($t !== false) {
|
||||
$query->whereRaw($effExpr . ' > 0 AND ' . $effExpr . ' >= ?', [$t]);
|
||||
}
|
||||
}
|
||||
if ($addEnd !== '') {
|
||||
$t = strtotime($addEnd . ' 23:59:59');
|
||||
if ($t !== false) {
|
||||
$query->whereRaw($effExpr . ' > 0 AND ' . $effExpr . ' <= ?', [$t]);
|
||||
}
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$this->applyCreatorOrOwnPrescriptionVisibility($query);
|
||||
$this->applyDataScopeForPrescriptionOrder($query);
|
||||
}
|
||||
// 业绩看板侧栏等:不展示履约已取消(4),与 stats 业绩统计口径一致
|
||||
// 业绩看板侧栏等:不展示履约 4/9/10,与 stats 业绩口径一致
|
||||
if ((int) ($this->params['exclude_fulfillment_cancelled'] ?? 0) === 1) {
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($query, '');
|
||||
}
|
||||
@@ -378,7 +378,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$this->applyDataScopeForPrescriptionOrder($query);
|
||||
}
|
||||
}
|
||||
// 与看板「合计业绩」一致:NULL 安全剔除履约已取消(4),勿用 `<>` 以免误丢 NULL 状态行
|
||||
// 与看板「合计业绩」一致:NULL 安全剔除履约 4/9/10
|
||||
if ((int) ($this->params['exclude_fulfillment_cancelled'] ?? 0) === 1) {
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($query, '');
|
||||
}
|
||||
@@ -570,13 +570,13 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
'stats_order_amount' => $s['order_amount'],
|
||||
'stats_order_amount_not_cancelled' => $s['order_amount_not_cancelled'],
|
||||
'stats_order_amount_cancelled' => $s['order_amount_cancelled'],
|
||||
/** 业绩:业务订单金额,剔除履约已取消(fulfillment_status=4),其余状态全部计入 */
|
||||
'stats_order_amount_performance' => $s['order_amount_not_cancelled'],
|
||||
/** 业绩:业务订单金额,剔除履约已取消/拒收/退款(4/9/10),NULL 与其它状态计入 */
|
||||
'stats_order_amount_performance' => $s['order_amount_performance'] ?? $s['order_amount_not_cancelled'],
|
||||
'stats_linked_pay_amount' => $s['linked_pay_amount'],
|
||||
'stats_linked_pay_amount_not_cancelled' => $s['linked_pay_amount_not_cancelled'],
|
||||
'stats_linked_pay_amount_cancelled' => $s['linked_pay_amount_cancelled'],
|
||||
/** 业绩-关联实付:剔除履约已取消订单的关联支付金额 */
|
||||
'stats_linked_pay_amount_performance' => $s['linked_pay_amount_not_cancelled'],
|
||||
/** 业绩-关联实付:剔除履约 4/9/10 订单的关联支付金额 */
|
||||
'stats_linked_pay_amount_performance' => $s['linked_pay_amount_performance'] ?? $s['linked_pay_amount_not_cancelled'],
|
||||
'stats_time_start' => $s['label_start'],
|
||||
'stats_time_end' => $s['label_end'],
|
||||
/** 统计口径:all=支付审核白名单全量;assistant=医助仅诊单协助业绩;restricted=与列表同域 */
|
||||
@@ -693,7 +693,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$t1 = $tmp;
|
||||
}
|
||||
$q = $this->buildBaseQueryForStats(false)->whereBetween('create_time', [$t0, $t1]);
|
||||
// 全量「合计业绩」与看板列同口径:始终剔除履约已取消(4),不依赖 exclude 参数
|
||||
// 全量「合计业绩」与看板列同口径:始终剔除履约 4/9/10,不依赖 exclude 参数
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($q, '');
|
||||
$amount = round((float) (clone $q)->sum('amount'), 2);
|
||||
$count = (int) (clone $q)->count();
|
||||
@@ -715,7 +715,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($pendingRxQuery, '');
|
||||
|
||||
return [
|
||||
// 处方审核待处理(不含履约已取消)
|
||||
// 处方审核待处理(不含履约 4/9/10)
|
||||
'pending_rx' => (int) $pendingRxQuery->where('prescription_audit_status', 0)->count(),
|
||||
// 支付审核待处理(前提:处方已通过)
|
||||
'pending_pay' => (int) (clone $query)->where('prescription_audit_status', 1)->where('payment_slip_audit_status', 0)->count(),
|
||||
@@ -761,8 +761,8 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
}
|
||||
|
||||
$q = $this->buildBaseQueryForStats()->whereBetween('create_time', [$t0, $t1]);
|
||||
$orderTotal = round((float) (clone $q)->sum('amount'), 2);
|
||||
$orderCancelled = round((float) (clone $q)->where('fulfillment_status', 4)->sum('amount'), 2);
|
||||
$orderTotal = round((float) (clone $q)->sum('amount'), 2);
|
||||
$orderCancelled = round((float) (clone $q)->where('fulfillment_status', 4)->sum('amount'), 2);
|
||||
$orderNotCancelled = round($orderTotal - $orderCancelled, 2);
|
||||
|
||||
$idsAll = (clone $q)->column('id');
|
||||
@@ -770,7 +770,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
return $id > 0;
|
||||
}));
|
||||
if ($idsAll === []) {
|
||||
return $this->statsExtendPayload(
|
||||
$empty = $this->statsExtendPayload(
|
||||
$orderTotal,
|
||||
$orderNotCancelled,
|
||||
$orderCancelled,
|
||||
@@ -780,6 +780,12 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$l0,
|
||||
$l1
|
||||
);
|
||||
$qPerfEmpty = clone $q;
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($qPerfEmpty, '');
|
||||
$empty['order_amount_performance'] = round((float) $qPerfEmpty->sum('amount'), 2);
|
||||
$empty['linked_pay_amount_performance'] = 0.0;
|
||||
|
||||
return $empty;
|
||||
}
|
||||
$idsCancel = (clone $q)->where('fulfillment_status', 4)->column('id');
|
||||
$idsCancel = array_values(array_filter(array_map('intval', $idsCancel), static function (int $id): bool {
|
||||
@@ -791,7 +797,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$linkedNotCancelled = $this->sumLinkedPayForPrescriptionOrderIds($idsNotCancelled);
|
||||
$linkedCancelled = $this->sumLinkedPayForPrescriptionOrderIds($idsCancel);
|
||||
|
||||
return $this->statsExtendPayload(
|
||||
$base = $this->statsExtendPayload(
|
||||
$orderTotal,
|
||||
$orderNotCancelled,
|
||||
$orderCancelled,
|
||||
@@ -801,6 +807,21 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
$l0,
|
||||
$l1
|
||||
);
|
||||
|
||||
$qPerf = clone $q;
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($qPerf, '');
|
||||
$base['order_amount_performance'] = round((float) $qPerf->sum('amount'), 2);
|
||||
|
||||
$idsExcludedPerf = (clone $q)
|
||||
->whereIn('fulfillment_status', YejiStatsLogic::PRESCRIPTION_ORDER_FULFILLMENT_EXCLUDED_FROM_PERFORMANCE)
|
||||
->column('id');
|
||||
$idsExcludedPerf = array_values(array_filter(array_map('intval', $idsExcludedPerf), static function (int $id): bool {
|
||||
return $id > 0;
|
||||
}));
|
||||
$idsPerf = array_values(array_diff($idsAll, $idsExcludedPerf));
|
||||
$base['linked_pay_amount_performance'] = $this->sumLinkedPayForPrescriptionOrderIds($idsPerf);
|
||||
|
||||
return $base;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -808,7 +829,11 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
*/
|
||||
private function statsZeroPayload(): array
|
||||
{
|
||||
return $this->statsExtendPayload(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, '', '');
|
||||
$z = $this->statsExtendPayload(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, '', '');
|
||||
$z['order_amount_performance'] = 0.0;
|
||||
$z['linked_pay_amount_performance'] = 0.0;
|
||||
|
||||
return $z;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
namespace app\adminapi\logic\dept;
|
||||
|
||||
use app\adminapi\logic\stats\YejiStatsLogic;
|
||||
use app\common\logic\BaseLogic;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\model\auth\AdminDept;
|
||||
@@ -212,8 +213,8 @@ class DeptLogic extends BaseLogic
|
||||
->join('tcm_diagnosis dg', 'dg.id = o.diagnosis_id AND dg.delete_time IS NULL', 'INNER')
|
||||
->whereNull('o.delete_time')
|
||||
->where('o.diagnosis_id', '>', 0)
|
||||
->where('dg.assistant_id', '>', 0)
|
||||
->whereRaw('NOT (o.fulfillment_status <=> ?)', [4]);
|
||||
->where('dg.assistant_id', '>', 0);
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($q, 'o');
|
||||
if ($orderStartTs !== null && $orderEndTs !== null) {
|
||||
$q->where('o.create_time', 'between', [$orderStartTs, $orderEndTs]);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace app\adminapi\logic\stats;
|
||||
|
||||
use app\adminapi\logic\dept\DeptLogic;
|
||||
use app\adminapi\logic\stats\YejiStatsLogic;
|
||||
use app\adminapi\logic\tcm\DiagnosisLogic;
|
||||
use app\common\model\finance\AccountCost;
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
@@ -1041,8 +1042,9 @@ class ConversionLogic
|
||||
->alias('po')
|
||||
->leftJoin('tcm_diagnosis dg', 'dg.id = po.diagnosis_id')
|
||||
->whereNull('po.delete_time')
|
||||
->where('po.create_time', 'between', [$startTimestamp, $endTimestamp])
|
||||
->where('po.fulfillment_status', '<>', 4)
|
||||
->where('po.create_time', 'between', [$startTimestamp, $endTimestamp]);
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($businessQuery, 'po');
|
||||
$businessQuery
|
||||
->fieldRaw("{$businessSourceExpr} AS source_admin_id, SUM(po.amount) AS total_amount")
|
||||
->group($businessSourceExpr);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace app\adminapi\logic\stats;
|
||||
|
||||
use app\adminapi\logic\stats\YejiStatsLogic;
|
||||
use app\common\model\auth\Admin;
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
use think\facade\Db;
|
||||
@@ -14,7 +15,7 @@ use think\facade\Db;
|
||||
* 医生范围:<b>admin_role.role_id = 1</b> 且管理员 <b>未软删</b>(delete_time 为空);再按账号「数据范围」收窄。
|
||||
*
|
||||
* - 系统/手动开方:tcm_prescription.prescription_date ∈ [start,end];渠道/标签与业绩渠道列同源 EXISTS / 诊单标签
|
||||
* - 成交:订单 create_time、排除履约(4),按处方 creator_id;渠道/标签同 sumPerformance 口径
|
||||
* - 成交:订单 create_time、排除履约 4/9/10,按处方 creator_id;渠道/标签同 sumPerformance 口径
|
||||
* - 挂号:appointment_date ∈ [start,end];选渠道时挂号 channels 命中字典值;标签渠道时 patient_id ∈ 标签诊单集
|
||||
*/
|
||||
class DoctorDailyStatsLogic
|
||||
@@ -300,9 +301,9 @@ class DoctorDailyStatsLogic
|
||||
->alias('o')
|
||||
->join('tcm_prescription rx', 'rx.id = o.prescription_id AND rx.delete_time IS NULL', 'INNER')
|
||||
->whereNull('o.delete_time')
|
||||
->whereBetween('o.create_time', [$startTs, $endTs])
|
||||
->whereRaw('NOT (o.fulfillment_status <=> 4)')
|
||||
->whereIn('rx.creator_id', $doctorIds)
|
||||
->whereBetween('o.create_time', [$startTs, $endTs]);
|
||||
YejiStatsLogic::applyPrescriptionOrderNotCancelledForPerformanceQuery($q, 'o');
|
||||
$q->whereIn('rx.creator_id', $doctorIds)
|
||||
->where('o.diagnosis_id', '>', 0);
|
||||
|
||||
$normCh = self::normalizeAppointmentChannelInts($appointmentChannelValues);
|
||||
|
||||
@@ -22,15 +22,15 @@ use think\facade\Db;
|
||||
* · **二中心及其组织下级在选定渠道下计 0**(与进线一致)
|
||||
* - 复诊统计 = 仅**名称含「二中心」的部门及其组织架构下级**:区间内业务单按患者 create_time 排序,第 1 笔不计,第 2 笔起分列「复诊2」「复诊3」…;口径同业绩单条件;按**订单创建人**所在部门在组织树内 rollup(含下级),与排行榜复诊一致
|
||||
* - 已完成挂号 = status=3 挂号条数(appointment_date),列「已完成挂号」;部门归属:有效医助→接诊医生→展示行映射。
|
||||
* - 接诊诊单 = 列名对应**业务订单条数**(非取消、create_time 区间),按**订单创建人**人事部门落在该行子树即计入;多行命中时取**最深的展示部门行**。与处方列表按 assistant_dept_id 筛选不同(列表保留旧口径:诊单医助 ∪ 创建人)。
|
||||
* - {渠道}成交单 = 选定渠道时与 {渠道}业绩同源:区间内 **非取消业务订单条数**(仅剔除 fulfillment_status=4;NULL 与其它状态计入,按 create_time 归日);dict(channels) 与 tag 口径均按订单计(按订单创建人归属),非挂号条数。
|
||||
* - 合计业绩 = 区间内业务订单 amount 求和(排除已取消(4) 与软删),**部门列**与「接诊诊单」一致:**订单创建人**人事部门落在该展示行子树则计入,多行命中取最深展示行;与排行榜诊金同口径。
|
||||
* - 接诊诊单 = 列名对应**业务订单条数**(剔除履约 4/9/10、create_time 区间),按**订单创建人**人事部门落在该行子树即计入;多行命中时取**最深的展示部门行**。与处方列表按 assistant_dept_id 筛选不同(列表保留旧口径:诊单医助 ∪ 创建人)。
|
||||
* - {渠道}成交单 = 选定渠道时与 {渠道}业绩同源:区间内 **计入业绩的业务订单条数**(剔除 fulfillment_status ∈ {4,9,10};NULL 与其它状态计入,按 create_time 归日);dict(channels) 与 tag 口径均按订单计(按订单创建人归属),非挂号条数。
|
||||
* - 合计业绩 = 区间内业务订单 amount 求和(排除已取消/拒收/退款(4,9,10) 与软删),**部门列**与「接诊诊单」一致:**订单创建人**人事部门落在该展示行子树则计入,多行命中取最深展示行;与排行榜诊金同口径。
|
||||
* 无法归入任一展示部门的金额单列「未归属中心」行;**底栏「合计业绩」「接诊诊单」= 当前表内各行业绩列相加**(含未归属行),与弹窗摊分口径一致。
|
||||
* · 时间窗与 stats_order_amount_performance 一致;列表若另有患者筛选或账号数据域收窄,金额可能仍不同
|
||||
* · 选定渠道时「{渠道}业绩」列仍按比例展示;合计列全量与渠道列含未归属补差;**二中心及其组织下级展示部门不参与该渠道进线/业绩/成交单/ROI 分子**(与同模块 DeptLogic::getErCenterSubtreeDeptIdSet 一致)
|
||||
* - {渠道}业绩 = 与「处方订单列表」业绩同口径(≠4):按 **o.create_time** 落入区间,按**订单创建人**归部门;
|
||||
* - {渠道}业绩 = 与「处方订单列表」业绩同口径(剔除 4/9/10):按 **o.create_time** 落入区间,按**订单创建人**归部门;
|
||||
* 渠道判定仅用 EXISTS:"患者有任一已完成挂号 (status=3) 且 channels 命中字典",**不限挂号时点**;无字典映射时回退 tag/creator 口径
|
||||
* - 成交单数 = 区间内非取消业务订单 **条数**;按**订单创建人**映射到「中心」;无法归属计入「未归属中心」;合计=全表 COUNT。
|
||||
* - 成交单数 = 区间内计入业绩的业务订单 **条数**(剔除 4/9/10);按**订单创建人**映射到「中心」;无法归属计入「未归属中心」;合计=全表 COUNT。
|
||||
* - 投放成本 = zyt_account_cost 在日期区间内 **amount** 合计(选定渠道时 `media_channel_code = channel_code` 过滤)。
|
||||
* · **各部门行投放成本** = 总投放成本 × (该部门进线 ÷ 各展示部门进线合计)。相当于先算 **单进线成本** = 总成本 ÷ 总进线,再乘部门进线。
|
||||
* · **二中心及其组织下级**不参与均摊投放成本(进线分摊分母对其计 0,与选定渠道下进线清零规则独立);其余部门按进线占比分摊。合计行成本 = 账务汇总总额。
|
||||
@@ -53,6 +53,9 @@ use think\facade\Db;
|
||||
*/
|
||||
class YejiStatsLogic
|
||||
{
|
||||
/** 不计入业绩的履约状态:已取消(4)、拒收(9)、退款(10);NULL 与其它值仍计业绩 */
|
||||
public const PRESCRIPTION_ORDER_FULFILLMENT_EXCLUDED_FROM_PERFORMANCE = [4, 9, 10];
|
||||
|
||||
/**
|
||||
* overview / assistantLeaderboards 共用的筛选与时间上下文。
|
||||
*
|
||||
@@ -557,7 +560,7 @@ class YejiStatsLogic
|
||||
. ($dataScopeRestricted
|
||||
? ' 当前账号受数据范围限制时,底栏合计业绩与列表同域。'
|
||||
: '');
|
||||
$baseNoteNoChannel = '业绩口径:除履约已取消(4)外全部计入。部门行「合计业绩」「接诊诊单」「复诊」均按**订单创建人人事部门 ∈ 该展示行子树**归属(多行命中取最深展示行),与医助排行榜诊金一致;与处方订单列表 assistant_dept_id 默认筛选(诊单医助 ∪ 创建人)不同——侧栏点击会自动收窄为创建人口径。多表「本月」区间为当月1日至服务器当日。**投放成本**按进线占比分摊,**二中心子树展示部门进线在分摊中计 0**(与部门 ErCenter 子树一致)。';
|
||||
$baseNoteNoChannel = '业绩口径:剔除履约已取消(4)、拒收(9)、退款(10),其余(含 NULL)计入。部门行「合计业绩」「接诊诊单」「复诊」均按**订单创建人人事部门 ∈ 该展示行子树**归属(多行命中取最深展示行),与医助排行榜诊金一致;与处方订单列表 assistant_dept_id 默认筛选(诊单医助 ∪ 创建人)不同——侧栏点击会自动收窄为创建人口径。多表「本月」区间为当月1日至服务器当日。**投放成本**按进线占比分摊,**二中心子树展示部门进线在分摊中计 0**(与部门 ErCenter 子树一致)。';
|
||||
|
||||
return [
|
||||
'start_date' => $startDate,
|
||||
@@ -567,9 +570,9 @@ class YejiStatsLogic
|
||||
'channel_name' => $channelInfo ? (string) $channelInfo['channel_name'] : '',
|
||||
'channel_group' => $channelInfo ? (string) $channelInfo['source_group_name'] : '',
|
||||
'channel_filter_note' => $channelCode !== ''
|
||||
? ('{渠道}业绩:按订单 create_time 归日、仅剔除履约已取消(4)(NULL 与其它状态均计入);渠道判定用"该患者是否有挂号 channels 命中 dict + status=3"(不限挂号时点)。部门归属按**订单创建人**人事部门 ∈ 子树。'
|
||||
. ' {渠道}成交单数:与{渠道}业绩同源,仅剔除已取消(4)的订单条数(非挂号条数),按订单创建人归部门。'
|
||||
. ' 「已完成挂号」列同 admin 挂号已完成列表;「接诊诊单」列为业务订单条数(非取消),同样按订单创建人归部门。**二中心及其组织下级**(与部门模块 ErCenter 子树一致)**不计入**该渠道进线、业绩与成交单。**投放成本**按各部门进线条数占展示部门进线合计的比例分摊,**二中心子树进线在分摊中计 0**(未选渠道时仍显示进线数,仅不参与均摊)。ROI = 渠道业绩(不含二中心子树)÷ 总投放成本。进线按所选标签客户精确过滤。'
|
||||
? ('{渠道}业绩:按订单 create_time 归日、剔除履约 4/9/10(NULL 与其它状态均计入);渠道判定用"该患者是否有挂号 channels 命中 dict + status=3"(不限挂号时点)。部门归属按**订单创建人**人事部门 ∈ 子树。'
|
||||
. ' {渠道}成交单数:与{渠道}业绩同源,剔除 4/9/10 的订单条数(非挂号条数),按订单创建人归部门。'
|
||||
. ' 「已完成挂号」列同 admin 挂号已完成列表;「接诊诊单」列为业务订单条数(同业绩剔除口径),同样按订单创建人归部门。**二中心及其组织下级**(与部门模块 ErCenter 子树一致)**不计入**该渠道进线、业绩与成交单。**投放成本**按各部门进线条数占展示部门进线合计的比例分摊,**二中心子树进线在分摊中计 0**(未选渠道时仍显示进线数,仅不参与均摊)。ROI = 渠道业绩(不含二中心子树)÷ 总投放成本。进线按所选标签客户精确过滤。'
|
||||
. ($tagFallback
|
||||
? ' ⚠ 当前业务尚未回填 tcm_diagnosis.external_userid 与 user_auth.unionid,已自动退化为「该标签客户的跟进医助 → 区间内接诊/业绩」口径,仅用于趋势观察。'
|
||||
: '')
|
||||
@@ -1799,7 +1802,7 @@ class YejiStatsLogic
|
||||
/**
|
||||
* 接诊诊单聚合:返回两组按部门归属的接诊计数。
|
||||
* - all: 区间内 status=3 挂号条数按部门归属(与列表计数一致),不含「仅 role_id=2」等额外限制。
|
||||
* - scoped: 选定渠道时「{渠道}成交单」= **非取消业务订单条数**(与 {渠道}业绩同源):dict 下 EXISTS 与侧栏一致(channels ∪ channel_source);有展示行时按列表部门摊行;否则走归因折叠。
|
||||
* - scoped: 选定渠道时「{渠道}成交单」= **计入业绩的业务订单条数**(与 {渠道}业绩同源,剔除 4/9/10):dict 下 EXISTS 与侧栏一致(channels ∪ channel_source);有展示行时按列表部门摊行;否则走归因折叠。
|
||||
* 未选渠道时 scoped == all(不再额外查一次)。
|
||||
*
|
||||
* @param int[] $appointmentChannelValues dict_data value 整型列表(通常 1=自媒体1)
|
||||
@@ -2996,7 +2999,7 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 sumPerformanceByAdmin 对应:按订单创建人统计成交单数(非取消业务订单条数),全量/渠道分支一致。
|
||||
* 与 sumPerformanceByAdmin 对应:按订单创建人统计成交单数(剔除履约 4/9/10),全量/渠道分支一致。
|
||||
*
|
||||
* @param int[] $appointmentChannelValues
|
||||
*
|
||||
@@ -3296,18 +3299,19 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 业绩/成交单统计:仅排除履约已取消 fulfillment_status = 4,其余(含 NULL 与任意其它状态)全部计入。
|
||||
* 与看板「合计业绩」、处方列表业绩 extend 口径一致;勿用 `<> 4`,以免误排除 NULL。
|
||||
* 业绩/成交单统计:排除履约「已取消/拒收/退款」(fulfillment_status 4、9、10),NULL 与其它状态计入。
|
||||
* 与看板「合计业绩」、处方列表业绩 extend 口径一致。
|
||||
*
|
||||
* @param \think\db\BaseQuery|\think\Model $query Db::name / Model::where 等查询实例
|
||||
* @param string $tableAlias 主表别名(如 Db 侧 `alias('o')` 传 `o`);无别名时传空字符串
|
||||
*/
|
||||
public static function applyPrescriptionOrderNotCancelledForPerformanceQuery($query, string $tableAlias = ''): void
|
||||
{
|
||||
$inList = implode(',', array_map(static fn (int $v): string => (string) $v, self::PRESCRIPTION_ORDER_FULFILLMENT_EXCLUDED_FROM_PERFORMANCE));
|
||||
if ($tableAlias !== '') {
|
||||
$query->whereRaw("NOT ({$tableAlias}.fulfillment_status <=> ?)", [4]);
|
||||
$query->whereRaw("({$tableAlias}.fulfillment_status IS NULL OR {$tableAlias}.fulfillment_status NOT IN ({$inList}))");
|
||||
} else {
|
||||
$query->whereRaw('NOT (fulfillment_status <=> ?)', [4]);
|
||||
$query->whereRaw("(fulfillment_status IS NULL OR fulfillment_status NOT IN ({$inList}))");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3609,7 +3613,7 @@ class YejiStatsLogic
|
||||
/**
|
||||
* 业绩聚合:返回两组按部门归属的业绩金额。
|
||||
* 口径与 /tcm.prescriptionOrder/lists 的 stats_*_performance 完全一致:
|
||||
* 业绩 = SUM(amount) WHERE NOT (fulfillment_status <=> 4) AND delete_time IS NULL
|
||||
* 业绩 = SUM(amount) WHERE fulfillment_status 为 NULL 或 NOT IN (4,9,10),且 delete_time IS NULL
|
||||
*
|
||||
* - all: 部门全量业绩(不限渠道)。用于「合计业绩」列展示。
|
||||
* - scoped: 「{渠道}业绩」:患者渠道 EXISTS 与侧栏/抽屉一致(channels ∪ channel_source);有展示行时部门列与「合计业绩」同为列表部门摊行。
|
||||
@@ -3725,7 +3729,7 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 渠道业绩:与「处方订单列表」业绩完全同口径 —— 按 o.create_time 归日、仅剔除已取消(4)(含 NULL);
|
||||
* 渠道业绩:与「处方订单列表」业绩完全同口径 —— 按 o.create_time 归日、剔除履约 4/9/10(NULL 与其它状态计入);
|
||||
* EXISTS 仅用于"该患者属于此渠道"的判定(有任一挂号 channels 命中 + status=3,**不限挂号时点**);
|
||||
* 部门归属按业绩归属表达式(订单创建人 o.creator_id)。
|
||||
*/
|
||||
@@ -3812,7 +3816,7 @@ class YejiStatsLogic
|
||||
/**
|
||||
* 单次业绩聚合:与 aggregatePerformanceAmountsByAdmin / aggregateDealOrderCount 同源 ——
|
||||
* INNER 诊单,按业绩归属医助 SUM(amount) 后映射到展示「中心」。
|
||||
* WHERE: delete_time IS NULL、NOT(fulfillment <=> 4)、create_time 区间、diagnosis_id>0、归属医助>0;
|
||||
* WHERE: delete_time IS NULL、fulfillment ∈ (NULL ∪ 除 4/9/10 外)、create_time 区间、diagnosis_id>0、归属医助>0;
|
||||
* 可选 tag:diagnosis_id IN、归属医助 IN。
|
||||
*
|
||||
* @return array<int, float>
|
||||
@@ -3859,7 +3863,7 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
* 全量成交单数:与 sumGlobalPerformanceAmount 同一 WHERE/COUNT(非取消订单条数)。
|
||||
* 全量成交单数:与 sumGlobalPerformanceAmount 同一 WHERE/COUNT(剔除 4/9/10)。
|
||||
*/
|
||||
private static function countGlobalDealOrders(int $startTs, int $endTs): int
|
||||
{
|
||||
@@ -4516,7 +4520,7 @@ class YejiStatsLogic
|
||||
'end_date' => (string) $c['endDate'],
|
||||
'channel_code' => (string) $c['channelCode'],
|
||||
'rows' => $rows,
|
||||
'note' => '与看板「未归属中心」补差一致:按订单 create_time、剔除履约已取消(4);**订单创建人**人事部门无法映射到任一展示「中心」计入各行;其余(无诊单/创建人为空)汇总在首行。账号数据权限下仅列出可见医助。',
|
||||
'note' => '与看板「未归属中心」补差一致:按订单 create_time、剔除履约 4/9/10;**订单创建人**人事部门无法映射到任一展示「中心」计入各行;其余(无诊单/创建人为空)汇总在首行。账号数据权限下仅列出可见医助。',
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ class ExpressAutoUpdate extends Command
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
$result = ExpressTrackingService::autoUpdateBatch(500);
|
||||
$recon = ExpressTrackingService::reconcileAssistantReleaseForShippedPrescriptionOrders(500);
|
||||
$result = ExpressTrackingService::autoUpdateBatch(1000);
|
||||
$recon = ExpressTrackingService::reconcileAssistantReleaseForShippedPrescriptionOrders(1000);
|
||||
|
||||
$duration = round(microtime(true) - $startTime, 2);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user