Merge branch 'master' into yeji-5-11
This commit is contained in:
@@ -557,6 +557,24 @@ class OrderController extends BaseAdminController
|
||||
}
|
||||
}
|
||||
|
||||
public function setExempt()
|
||||
{
|
||||
$id = (int) $this->request->post('id', 0);
|
||||
$isExempt = (int) $this->request->post('is_exempt', 0);
|
||||
if ($id <= 0) {
|
||||
return $this->fail('参数错误');
|
||||
}
|
||||
if (!in_array($isExempt, [0, 1], true)) {
|
||||
return $this->fail('is_exempt 值无效');
|
||||
}
|
||||
$result = OrderLogic::setExempt($id, $isExempt);
|
||||
if (!$result) {
|
||||
return $this->fail(OrderLogic::getError());
|
||||
}
|
||||
$this->logOrderAction($id, 'set_exempt', $isExempt === 1 ? '设置豁免权' : '取消豁免权');
|
||||
return $this->success($isExempt === 1 ? '已设置豁免权' : '已取消豁免权');
|
||||
}
|
||||
|
||||
private function logOrderAction(int $orderId, string $action, string $summary = ''): void
|
||||
{
|
||||
OrderActionLogLogic::record($orderId, (int) $this->adminId, $this->adminInfo, $action, $summary);
|
||||
|
||||
@@ -117,6 +117,7 @@ class DiagnosisController extends BaseAdminController
|
||||
|
||||
$params = (new DiagnosisValidate())->goCheck('id');
|
||||
$result = DiagnosisLogic::detail($params);
|
||||
DiagnosisLogic::markAssignRead((int) ($params['id'] ?? 0), $this->adminId);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
@@ -135,7 +136,7 @@ class DiagnosisController extends BaseAdminController
|
||||
if (empty($result)) {
|
||||
return $this->fail(DiagnosisLogic::getError() ?: '诊单不存在或无权访问');
|
||||
}
|
||||
|
||||
DiagnosisLogic::markAssignRead((int) ($params['id'] ?? 0), $this->adminId);
|
||||
return $this->data($result);
|
||||
}
|
||||
|
||||
|
||||
@@ -177,14 +177,14 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
|
||||
$isCompletedTab = !$pendingWideSearch && isset($this->params['completed_appointment']) && (string) $this->params['completed_appointment'] === '1';
|
||||
if ($isCompletedTab) {
|
||||
$maxCompletedAptExpr = '(SELECT MAX(CONCAT(apt.appointment_date, \' \', IFNULL(NULLIF(TRIM(apt.appointment_time), \'\'), \'00:00:00\'))) FROM ' . $aptTbl . ' apt WHERE apt.patient_id = ' . $diagTbl . '.id AND apt.status = 3' . $minAptDateCond . ')';
|
||||
$orderRaw = 'IFNULL(' . $maxCompletedAptExpr . ", '1970-01-01 00:00:00') DESC, {$diagTbl}.id DESC";
|
||||
$orderRaw = $diagTbl . '.assign_read_at IS NULL DESC, IFNULL(' . $maxCompletedAptExpr . ", '1970-01-01 00:00:00') DESC, {$diagTbl}.id DESC";
|
||||
} else {
|
||||
$orderRaw = 'CASE IFNULL(' . $minAptStatusExpr . ', 999) WHEN 4 THEN 1 WHEN 1 THEN 2 WHEN 3 THEN 3 ELSE 4 END ASC, IFNULL(' . $minAptExpr . ", '9999-12-31 23:59:59') ASC, {$diagTbl}.id DESC";
|
||||
$orderRaw = $diagTbl . '.assign_read_at IS NULL DESC, CASE IFNULL(' . $minAptStatusExpr . ', 999) WHEN 4 THEN 1 WHEN 1 THEN 2 WHEN 3 THEN 3 ELSE 4 END ASC, IFNULL(' . $minAptExpr . ", '9999-12-31 23:59:59') ASC, {$diagTbl}.id DESC";
|
||||
}
|
||||
|
||||
$lists = $query
|
||||
->with(['DiagnosisViewRecord'])
|
||||
->field(['id', 'patient_id', 'patient_name', 'id_card', 'phone', 'gender', 'age', 'diagnosis_date', 'diagnosis_type', 'syndrome_type', 'assistant_id', 'status', 'create_time', 'update_time'])
|
||||
->field(['id', 'patient_id', 'patient_name', 'id_card', 'phone', 'gender', 'age', 'diagnosis_date', 'diagnosis_type', 'syndrome_type', 'assistant_id', 'assign_read_at', 'status', 'create_time', 'update_time'])
|
||||
->append(['gender_desc', 'status_desc', 'diagnosis_date_text'])
|
||||
->orderRaw($orderRaw)
|
||||
->limit($this->limitOffset, $this->limitLength)
|
||||
|
||||
@@ -906,6 +906,24 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
|
||||
return;
|
||||
}
|
||||
$yejiTableRowDeptIds = [];
|
||||
if (isset($this->params['yeji_table_row_dept_ids']) && trim((string) $this->params['yeji_table_row_dept_ids']) !== '') {
|
||||
foreach (explode(',', (string) $this->params['yeji_table_row_dept_ids']) as $p) {
|
||||
$v = (int) trim((string) $p);
|
||||
if ($v > 0) {
|
||||
$yejiTableRowDeptIds[] = $v;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((int) ($this->params['yeji_order_drawer'] ?? 0) === 1 && $yejiTableRowDeptIds !== []) {
|
||||
YejiStatsLogic::applyPrescriptionOrderListAlignedWinnerDeptFilter(
|
||||
$query,
|
||||
(int) $this->params['assistant_dept_id'],
|
||||
$yejiTableRowDeptIds
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
$rootDeptId = (int) $this->params['assistant_dept_id'];
|
||||
$deptIds = DeptLogic::getSelfAndDescendantIds($rootDeptId);
|
||||
$deptIds = array_values(array_filter(array_map('intval', $deptIds), static function (int $id): bool {
|
||||
@@ -918,6 +936,7 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
}
|
||||
$inList = implode(',', $deptIds);
|
||||
$adTbl = (new AdminDept())->getTable();
|
||||
<<<<<<< HEAD
|
||||
if ($yejiPanel) {
|
||||
/** 业绩看板部门侧栏:仅订单创建人人事部门 ∈ 子树(与「合计业绩」/「接诊诊单」/「复诊」聚合同口径) */
|
||||
$query->whereExists(
|
||||
@@ -936,6 +955,12 @@ class PrescriptionOrderLists extends BaseAdminDataLists implements ListsSearchIn
|
||||
);
|
||||
});
|
||||
}
|
||||
=======
|
||||
$poRef = '`' . str_replace('`', '', $poTbl) . '`';
|
||||
$query->whereRaw(
|
||||
YejiStatsLogic::sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst($poRef, $diagTbl, $adTbl, $inList)
|
||||
);
|
||||
>>>>>>> master
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1213,7 +1213,7 @@ class OrderLogic
|
||||
}
|
||||
|
||||
$rows = $q
|
||||
->field(['id', 'order_no', 'order_type', 'amount', 'status', 'create_time', 'creator_id', 'remark'])
|
||||
->field(['id', 'order_no', 'order_type', 'amount', 'status', 'create_time', 'creator_id', 'remark', 'is_exempt'])
|
||||
->order('id', 'desc')
|
||||
->select()
|
||||
->toArray();
|
||||
@@ -1400,4 +1400,16 @@ class OrderLogic
|
||||
'errors' => $errors,
|
||||
];
|
||||
}
|
||||
|
||||
public static function setExempt(int $id, int $isExempt): bool
|
||||
{
|
||||
$order = Order::find($id);
|
||||
if (!$order) {
|
||||
self::setError('订单不存在');
|
||||
return false;
|
||||
}
|
||||
$order->is_exempt = $isExempt;
|
||||
$order->save();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ declare(strict_types=1);
|
||||
namespace app\adminapi\logic\stats;
|
||||
|
||||
use app\adminapi\logic\dept\DeptLogic;
|
||||
use app\common\model\auth\AdminDept;
|
||||
use app\common\model\tcm\Diagnosis;
|
||||
use app\common\model\tcm\PrescriptionOrder;
|
||||
use app\common\service\DataScope\DataScopeService;
|
||||
use app\common\service\qywx\MediaChannelService;
|
||||
@@ -20,10 +22,17 @@ use think\facade\Db;
|
||||
* · **二中心及其组织下级在选定渠道下计 0**(与进线一致)
|
||||
* - 复诊统计 = 仅**名称含「二中心」的部门及其组织架构下级**:区间内业务单按患者 create_time 排序,第 1 笔不计,第 2 笔起分列「复诊2」「复诊3」…;口径同业绩单条件;按**订单创建人**所在部门在组织树内 rollup(含下级),与排行榜复诊一致
|
||||
* - 已完成挂号 = status=3 挂号条数(appointment_date),列「已完成挂号」;部门归属:有效医助→接诊医生→展示行映射。
|
||||
<<<<<<< HEAD
|
||||
* - 接诊诊单 = 列名对应**业务订单条数**(非取消、create_time 区间),按**订单创建人**人事部门落在该行子树即计入;多行命中时取**最深的展示部门行**。与处方列表按 assistant_dept_id 筛选不同(列表保留旧口径:诊单医助 ∪ 创建人)。
|
||||
* - {渠道}成交单 = 选定渠道时与 {渠道}业绩同源:区间内 **非取消业务订单条数**(仅剔除 fulfillment_status=4;NULL 与其它状态计入,按 create_time 归日);dict(channels) 与 tag 口径均按订单计(按订单创建人归属),非挂号条数。
|
||||
* - 合计业绩 = 区间内业务订单 amount 求和(排除已取消(4) 与软删),**部门列**与「接诊诊单」一致:**订单创建人**人事部门落在该展示行子树则计入,多行命中取最深展示行;与排行榜诊金同口径。
|
||||
* 无法归入任一展示部门的金额单列「未归属中心」行;**底栏「合计业绩」「接诊诊单」= 当前表内各行业绩列相加**(含未归属行),与弹窗摊分口径一致。
|
||||
=======
|
||||
* - 接诊诊单 = 列名对应**业务订单条数**(非取消、create_time 区间),与处方列表按 **assistant_dept_id** 筛选时一致:优先按订单 **creator_id** 人事部门;无创建人时回退诊单 **assistant_id**;多行命中时取**最深的展示部门行**,与「业绩归属医助」分组可能不同。
|
||||
* - {渠道}成交单 = 选定渠道时与 {渠道}业绩同源:区间内 **非取消业务订单条数**(仅剔除 fulfillment_status=4;NULL 与其它状态计入,按 create_time 归日);dict(channels) 与 tag 口径均按订单计(tag 时按诊单医助收窄),非挂号条数。
|
||||
* - 合计业绩 = 区间内业务订单 amount 求和(排除已取消(4) 与软删),**部门列**与「接诊诊单」一致;与处方列表按 assistant_dept_id 筛选后的金额合计一致。
|
||||
* 无法归入任一展示部门的金额单列「未归属中心」行;**全表合计行**仍为同时间/业绩条件的全表 SUM(不限部门)。
|
||||
>>>>>>> master
|
||||
* · 时间窗与 stats_order_amount_performance 一致;列表若另有患者筛选或账号数据域收窄,金额可能仍不同
|
||||
* · 选定渠道时「{渠道}业绩」列仍按比例展示;合计列全量与渠道列含未归属补差;**二中心及其组织下级展示部门不参与该渠道进线/业绩/成交单/ROI 分子**(与同模块 DeptLogic::getErCenterSubtreeDeptIdSet 一致)
|
||||
* - {渠道}业绩 = 与「处方订单列表」业绩同口径(≠4):按 **o.create_time** 落入区间,按**订单创建人**归部门;
|
||||
@@ -550,12 +559,20 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
$scopedTableNote = ($explicitDeptFilter
|
||||
<<<<<<< HEAD
|
||||
? ' 已筛选展示部门时,表格仅展示所选树中的部门行;底栏「合计业绩」「接诊诊单」与当前表内各行业绩列相加一致(含「未归属中心」行若展示)。'
|
||||
=======
|
||||
? ' 已筛选展示部门时,表格「合计业绩」「接诊诊单」各行为同一套列表部门口径(**创建人人事部门优先**,无创建人则诊单医助;∪子树、最深展示行);底栏合计业绩/成交单与处方订单列表 extend(所选部门树+数据域)一致,可能与各行相加不同。'
|
||||
>>>>>>> master
|
||||
: '')
|
||||
. ($dataScopeRestricted
|
||||
? ' 当前账号受数据范围限制时,底栏合计业绩与列表同域。'
|
||||
: '');
|
||||
<<<<<<< HEAD
|
||||
$baseNoteNoChannel = '业绩口径:除履约已取消(4)外全部计入。部门行「合计业绩」「接诊诊单」「复诊」均按**订单创建人人事部门 ∈ 该展示行子树**归属(多行命中取最深展示行),与医助排行榜诊金一致;与处方订单列表 assistant_dept_id 默认筛选(诊单医助 ∪ 创建人)不同——侧栏点击会自动收窄为创建人口径。多表「本月」区间为当月1日至服务器当日。**投放成本**按进线占比分摊,**二中心子树展示部门进线在分摊中计 0**(与部门 ErCenter 子树一致)。';
|
||||
=======
|
||||
$baseNoteNoChannel = '业绩口径:除履约已取消(4)外全部计入。部门行「合计业绩」与「接诊诊单」均为列表 assistant_dept_id 口径(**创建人优先**,否则诊单医助;人事部门∪子树,多行命中取最深展示行)。多表「本月」区间为当月1日至服务器当日。**投放成本**按进线占比分摊,**二中心子树展示部门进线在分摊中计 0**(与部门 ErCenter 子树一致)。';
|
||||
>>>>>>> master
|
||||
|
||||
return [
|
||||
'start_date' => $startDate,
|
||||
@@ -565,9 +582,15 @@ class YejiStatsLogic
|
||||
'channel_name' => $channelInfo ? (string) $channelInfo['channel_name'] : '',
|
||||
'channel_group' => $channelInfo ? (string) $channelInfo['source_group_name'] : '',
|
||||
'channel_filter_note' => $channelCode !== ''
|
||||
<<<<<<< HEAD
|
||||
? ('{渠道}业绩:按订单 create_time 归日、仅剔除履约已取消(4)(NULL 与其它状态均计入);渠道判定用"该患者是否有挂号 channels 命中 dict + status=3"(不限挂号时点)。部门归属按**订单创建人**人事部门 ∈ 子树。'
|
||||
. ' {渠道}成交单数:与{渠道}业绩同源,仅剔除已取消(4)的订单条数(非挂号条数),按订单创建人归部门。'
|
||||
. ' 「已完成挂号」列同 admin 挂号已完成列表;「接诊诊单」列为业务订单条数(非取消),同样按订单创建人归部门。**二中心及其组织下级**(与部门模块 ErCenter 子树一致)**不计入**该渠道进线、业绩与成交单。**投放成本**按各部门进线条数占展示部门进线合计的比例分摊,**二中心子树进线在分摊中计 0**(未选渠道时仍显示进线数,仅不参与均摊)。ROI = 渠道业绩(不含二中心子树)÷ 总投放成本。进线按所选标签客户精确过滤。'
|
||||
=======
|
||||
? ('{渠道}业绩:与「处方订单列表」业绩同口径,按订单 create_time 归日、仅剔除履约已取消(4)(NULL 与其它状态均计入);渠道判定用"该患者是否有挂号 channels 命中 dict + status=3"(不限挂号时点)。'
|
||||
. ' {渠道}成交单数:与{渠道}业绩同源,仅剔除已取消(4)的订单条数(非挂号条数)。'
|
||||
. ' 「已完成挂号」列同 admin 挂号已完成列表;「接诊诊单」列为业务订单条数(非取消),与列表按 assistant_dept_id 筛选时一致(**创建人优先**,否则诊单医助;∪子树)。**二中心及其组织下级**(与部门模块 ErCenter 子树一致)**不计入**该渠道进线、业绩与成交单。**投放成本**按各部门进线条数占展示部门进线合计的比例分摊,**二中心子树进线在分摊中计 0**(未选渠道时仍显示进线数,仅不参与均摊)。ROI = 渠道业绩(不含二中心子树)÷ 总投放成本。进线按所选标签客户精确过滤。'
|
||||
>>>>>>> master
|
||||
. ($tagFallback
|
||||
? ' ⚠ 当前业务尚未回填 tcm_diagnosis.external_userid 与 user_auth.unionid,已自动退化为「该标签客户的跟进医助 → 区间内接诊/业绩」口径,仅用于趋势观察。'
|
||||
: '')
|
||||
@@ -3399,6 +3422,101 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
* 与处方订单列表 buildBaseQueryForStats(仅人事筛选维度)对齐的订单查询 WHERE。
|
||||
*
|
||||
* @param int[]|null $expandedDeptIds
|
||||
* @param int[]|null $visibleAdminIds
|
||||
*/
|
||||
private static function applyOrderListStatsVisibilityToPrescriptionQuery(
|
||||
$query,
|
||||
?array $expandedDeptIds,
|
||||
?array $visibleAdminIds
|
||||
): void {
|
||||
$hasDept = $expandedDeptIds !== null && $expandedDeptIds !== [];
|
||||
$hasVis = $visibleAdminIds !== null && $visibleAdminIds !== [];
|
||||
if (!$hasDept && !$hasVis) {
|
||||
return;
|
||||
}
|
||||
$diagTable = self::tableWithPrefix('tcm_diagnosis');
|
||||
if ($hasDept) {
|
||||
$ids = array_values(array_unique(array_filter(array_map('intval', $expandedDeptIds), static function (int $x): bool {
|
||||
return $x > 0;
|
||||
})));
|
||||
if ($ids === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
$inList = implode(',', $ids);
|
||||
$diagTb = (new Diagnosis())->getTable();
|
||||
$adTb = (new AdminDept())->getTable();
|
||||
$query->whereRaw(self::sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst('`o`', $diagTb, $adTb, $inList));
|
||||
}
|
||||
if ($hasVis) {
|
||||
$ids = array_values(array_unique(array_filter(array_map('intval', $visibleAdminIds), static function (int $x): bool {
|
||||
return $x > 0;
|
||||
})));
|
||||
if ($ids === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
$inList = implode(',', $ids);
|
||||
$query->where(function ($q) use ($diagTable, $inList, $ids) {
|
||||
$q->whereIn('o.creator_id', $ids);
|
||||
$q->whereOrRaw(
|
||||
"EXISTS (SELECT 1 FROM {$diagTable} dg WHERE dg.id = o.diagnosis_id "
|
||||
. "AND dg.delete_time IS NULL AND dg.assistant_id IN ({$inList}))"
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 收窄合计时与订单列表 extend「业绩金额」一致(create_time、NOT(fulfillment<=>4)、人事筛选)。
|
||||
*
|
||||
* @param int[]|null $expandedDeptIds
|
||||
* @param int[]|null $visibleAdminIds
|
||||
*/
|
||||
private static function sumOrderListAlignedPerformanceAmount(
|
||||
int $startTs,
|
||||
int $endTs,
|
||||
?array $expandedDeptIds,
|
||||
?array $visibleAdminIds
|
||||
): float {
|
||||
$query = Db::name('tcm_prescription_order')
|
||||
->alias('o')
|
||||
->whereNull('o.delete_time')
|
||||
->where('o.create_time', 'between', [$startTs, $endTs]);
|
||||
self::applyPrescriptionOrderNotCancelledForPerformance($query);
|
||||
self::applyOrderListStatsVisibilityToPrescriptionQuery($query, $expandedDeptIds, $visibleAdminIds);
|
||||
|
||||
return round((float) $query->sum('o.amount'), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 sumOrderListAlignedPerformanceAmount 同源,计订单条数。
|
||||
*/
|
||||
private static function countOrderListAlignedPerformanceOrders(
|
||||
int $startTs,
|
||||
int $endTs,
|
||||
?array $expandedDeptIds,
|
||||
?array $visibleAdminIds
|
||||
): int {
|
||||
$query = Db::name('tcm_prescription_order')
|
||||
->alias('o')
|
||||
->whereNull('o.delete_time')
|
||||
->where('o.create_time', 'between', [$startTs, $endTs]);
|
||||
self::applyPrescriptionOrderNotCancelledForPerformance($query);
|
||||
self::applyOrderListStatsVisibilityToPrescriptionQuery($query, $expandedDeptIds, $visibleAdminIds);
|
||||
|
||||
return (int) $query->count();
|
||||
}
|
||||
|
||||
/**
|
||||
>>>>>>> master
|
||||
* 标签/医助兜底口径下的渠道向业绩全量 SUM(与 sumPerformance 的 scoped 分支同一组 WHERE,无部门归属过滤)。
|
||||
*/
|
||||
private static function sumGlobalPerformanceTagScoped(
|
||||
@@ -3824,8 +3942,13 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* 与业绩看板部门列一致:仅按订单创建人候选,将其人事部门落在展示行子树即命中;命中多行时取组织深度最大的展示行。
|
||||
* (处方订单列表默认 assistant_dept_id 仍为「诊单医助 ∪ 创建人」并集,与看板有别。)
|
||||
=======
|
||||
* 与 {@see PrescriptionOrderLists::applyDoctorAssistantFilters} 中 assistant_dept_id(未开抽屉对齐开关)一致:
|
||||
* 有创建人则仅用创建人人事部门落在该行子树即命中;无创建人则诊单医助;命中多行时取组织深度最大的展示行。
|
||||
>>>>>>> master
|
||||
*
|
||||
* @param array<int, true> $candidates admin_id => true
|
||||
* @param array<int, int[]> $adminDeptIdList
|
||||
@@ -3899,8 +4022,12 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* 看板「合计业绩 / 接诊诊单」列用的部门摊行:仅按订单创建人 o.creator_id 的人事部门映射到展示行子树(命中多行取最深),
|
||||
* 与侧栏 yeji_order_drawer=1、{@see sqlPerformanceAttributionAdminExpr} 一致。不再把诊单医助纳入归属候选。
|
||||
=======
|
||||
* 列表部门口径(有创建人则仅用创建人、否则诊单医助 ⇄ 展示行子树,最深行):聚单笔数与金额,供「接诊诊单」与「合计业绩」列共用。
|
||||
>>>>>>> master
|
||||
*
|
||||
* @return array{count: array<int, int>, amount: array<int, float>}
|
||||
*/
|
||||
@@ -3970,6 +4097,8 @@ class YejiStatsLogic
|
||||
$c = (int) ($r['creator_id'] ?? 0);
|
||||
if ($c > 0) {
|
||||
$adminIds[] = $c;
|
||||
} elseif ($a > 0) {
|
||||
$adminIds[] = $a;
|
||||
}
|
||||
}
|
||||
$adminIds = array_values(array_unique(array_filter($adminIds, static fn (int $x): bool => $x > 0)));
|
||||
@@ -3990,8 +4119,16 @@ class YejiStatsLogic
|
||||
$byAmt = [];
|
||||
foreach ($orderRows as $r) {
|
||||
$cr = (int) ($r['creator_id'] ?? 0);
|
||||
<<<<<<< HEAD
|
||||
if ($cr <= 0) {
|
||||
continue;
|
||||
=======
|
||||
$cand = [];
|
||||
if ($cr > 0) {
|
||||
$cand[$cr] = true;
|
||||
} elseif ($asst > 0) {
|
||||
$cand[$asst] = true;
|
||||
>>>>>>> master
|
||||
}
|
||||
$cand = [$cr => true];
|
||||
$deptKey = self::resolveDeepestYejiTableRowForListDeptOr(
|
||||
@@ -4014,9 +4151,15 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* 成交单数按部门:有展示行时与 {@see aggregateListAlignedOrderMetricsByDept} 同口径(仅订单创建人归属)。
|
||||
* 无展示行时按 sqlPerformanceAttributionAdminExpr 分组再 fold(同样为创建人)。
|
||||
* 选定医助 tag 时收窄为 o.creator_id ∈ 集。
|
||||
=======
|
||||
* 成交单数按部门:**处方订单列表**按 assistant_dept_id 筛选时的业务单条数(创建人优先、无创建人则诊单医助 ∪ 部门子树),
|
||||
* 与 {@see PrescriptionOrderLists::applyDoctorAssistantFilters} 在 <b>未</b> 传 yeji_drawer_match_table_performance 时一致。
|
||||
* 选定医助 tag 时收窄为(业绩归属医助 ∈ 集 **或** 诊单医助 ∈ 集 **或** 创建人 ∈ 集)。
|
||||
>>>>>>> master
|
||||
*
|
||||
* @return array<int, int>
|
||||
*/
|
||||
@@ -4804,6 +4947,7 @@ class YejiStatsLogic
|
||||
}
|
||||
|
||||
/**
|
||||
<<<<<<< HEAD
|
||||
* 提成结算统计:渠道收窄条件应用到业务订单查询(与 sumPerformance「scoped」同源)。
|
||||
*/
|
||||
private static function applyCommissionSettlementChannelFilter(Query $query, array $c): void
|
||||
@@ -4837,11 +4981,49 @@ class YejiStatsLogic
|
||||
return;
|
||||
}
|
||||
if ($tagAssistantIds !== null && $tagAssistantIds === [] && $tagFallback) {
|
||||
=======
|
||||
* 业务订单列表 WHERE 片段:有创建人则仅按创建人人事部门落在子树;无创建人则按诊单医助(与
|
||||
* {@see aggregateListAlignedOrderMetricsByDept} 及
|
||||
* {@see PrescriptionOrderLists::applyDoctorAssistantFilters} 中 assistant_dept_id 一致)。
|
||||
*
|
||||
* @param string $orderRefBackticked 主查询里业务订单表标识,须已加反引号:列表/Model 无别名时用 `` `zyt_tcm_prescription_order` ``;Db::name()->alias('o') 时用 `` `o` ``
|
||||
* @param string $inListComma 已净化的 id 列表(仅数字与逗号)
|
||||
*/
|
||||
public static function sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst(
|
||||
string $orderRefBackticked,
|
||||
string $diagTbl,
|
||||
string $adTbl,
|
||||
string $inListComma
|
||||
): string {
|
||||
$creatorInSubtree = "EXISTS (SELECT 1 FROM `{$adTbl}` adc WHERE adc.`admin_id` = {$orderRefBackticked}.`creator_id` AND adc.`dept_id` IN ({$inListComma}))";
|
||||
$assistantInSubtree = "EXISTS (SELECT 1 FROM `{$adTbl}` ad INNER JOIN `{$diagTbl}` dg ON ad.admin_id = dg.assistant_id AND dg.delete_time IS NULL "
|
||||
. "WHERE dg.`id` = {$orderRefBackticked}.`diagnosis_id` AND ad.dept_id IN ({$inListComma}))";
|
||||
|
||||
return '('
|
||||
. '((IFNULL(' . $orderRefBackticked . '.`creator_id`,0) > 0) AND (' . $creatorInSubtree . '))'
|
||||
. ' OR '
|
||||
. '((IFNULL(' . $orderRefBackticked . '.`creator_id`,0) <= 0) AND (' . $assistantInSubtree . '))'
|
||||
. ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* 业绩看板侧栏:与「接诊诊单」列 {@see aggregateListAlignedOrderMetricsByDept} 同源——在**同一表格**
|
||||
* 展示行集合内,创建人优先归属;无创建人则医助;多行命中时取组织深度最大的展示行(同深取较小 dept id)。
|
||||
* 传入当前表全部展示行 dept id。
|
||||
*
|
||||
* @param int[] $tableRowDeptIds 当前业绩表 body 行 dept_id(含点击行及兄弟行)
|
||||
*/
|
||||
public static function applyPrescriptionOrderListAlignedWinnerDeptFilter(Query $query, int $targetRowDeptId, array $tableRowDeptIds): void
|
||||
{
|
||||
$targetRowDeptId = (int) $targetRowDeptId;
|
||||
if ($targetRowDeptId <= 0) {
|
||||
>>>>>>> master
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
$att = self::sqlPerformanceAttributionAdminExpr();
|
||||
$narrowed = false;
|
||||
if ($tagDiagIds !== null) {
|
||||
@@ -5954,5 +6136,63 @@ class YejiStatsLogic
|
||||
'next_settlement_month' => $next,
|
||||
'deferred_carry_count' => count($deferredIds),
|
||||
];
|
||||
=======
|
||||
$uniq = [];
|
||||
foreach ($tableRowDeptIds as $id) {
|
||||
$id = (int) $id;
|
||||
if ($id > 0) {
|
||||
$uniq[$id] = true;
|
||||
}
|
||||
}
|
||||
$tableRowDeptIds = array_keys($uniq);
|
||||
if ($tableRowDeptIds === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$deptRows = Db::name('dept')->whereNull('delete_time')->field(['id', 'pid'])->select()->toArray();
|
||||
$deptById = [];
|
||||
foreach ($deptRows as $row) {
|
||||
$deptById[(int) $row['id']] = $row;
|
||||
}
|
||||
|
||||
$dT = self::deptDepthFromRoot($targetRowDeptId, $deptById);
|
||||
$beaters = [];
|
||||
foreach ($tableRowDeptIds as $r) {
|
||||
$r = (int) $r;
|
||||
if ($r <= 0 || $r === $targetRowDeptId) {
|
||||
continue;
|
||||
}
|
||||
$dR = self::deptDepthFromRoot($r, $deptById);
|
||||
if ($dR > $dT || ($dR === $dT && $r < $targetRowDeptId)) {
|
||||
$beaters[] = $r;
|
||||
}
|
||||
}
|
||||
|
||||
$poRef = '`' . str_replace('`', '', (new PrescriptionOrder())->getTable()) . '`';
|
||||
$diagTbl = (new Diagnosis())->getTable();
|
||||
$adTbl = (new AdminDept())->getTable();
|
||||
|
||||
$tSubtree = DeptLogic::getSelfAndDescendantIds($targetRowDeptId);
|
||||
$tSubtree = array_values(array_filter(array_map('intval', $tSubtree), static fn (int $id): bool => $id > 0));
|
||||
if ($tSubtree === []) {
|
||||
$query->whereRaw('0 = 1');
|
||||
|
||||
return;
|
||||
}
|
||||
$tIn = implode(',', $tSubtree);
|
||||
$clauses = [self::sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst($poRef, $diagTbl, $adTbl, $tIn)];
|
||||
foreach ($beaters as $b) {
|
||||
$bSubtree = DeptLogic::getSelfAndDescendantIds((int) $b);
|
||||
$bSubtree = array_values(array_filter(array_map('intval', $bSubtree), static fn (int $id): bool => $id > 0));
|
||||
if ($bSubtree === []) {
|
||||
continue;
|
||||
}
|
||||
$bIn = implode(',', $bSubtree);
|
||||
$clauses[] = 'NOT (' . self::sqlPrescriptionOrderListDeptSubtreeMatchCreatorFirst($poRef, $diagTbl, $adTbl, $bIn) . ')';
|
||||
}
|
||||
$query->whereRaw('(' . implode(' AND ', $clauses) . ')');
|
||||
>>>>>>> master
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,6 +535,19 @@ class DiagnosisLogic extends BaseLogic
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static function markAssignRead(int $diagnosisId, int $adminId): void
|
||||
{
|
||||
if ($diagnosisId <= 0 || $adminId <= 0) {
|
||||
return;
|
||||
}
|
||||
Db::name('tcm_diagnosis')
|
||||
->where('id', $diagnosisId)
|
||||
->where('assistant_id', $adminId)
|
||||
->whereNull('assign_read_at')
|
||||
->whereNull('delete_time')
|
||||
->update(['assign_read_at' => time()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @notes 指派医助(每次成功更新写入一条操作记录,批量指派为多次接口调用各记一条)。
|
||||
* **原医助**:优先诊单 `assistant_id`;若为 0 且本次为指派到某人,则从**最近一条**指派日志推断(覆盖发货释放后库为 0、业务上仍有「前任」医助的情况)。
|
||||
@@ -588,7 +601,10 @@ class DiagnosisLogic extends BaseLogic
|
||||
Db::name('tcm_diagnosis')
|
||||
->where('id', $id)
|
||||
->whereNull('delete_time')
|
||||
->update(['assistant_id' => $toAssistantId]);
|
||||
->update([
|
||||
'assistant_id' => $toAssistantId,
|
||||
'assign_read_at' => $toAssistantId > 0 ? null : 0,
|
||||
]);
|
||||
|
||||
$req = request();
|
||||
$admin = $req->adminInfo ?? [];
|
||||
|
||||
@@ -324,7 +324,16 @@ class PrescriptionOrderLogic
|
||||
if ($payOrderIds === []) {
|
||||
return '未关联支付单,关联支付单总金额须大于等于定金门槛 ¥' . $min;
|
||||
}
|
||||
|
||||
|
||||
// 豁免权:关联的支付单中有豁免单则跳过金额校验
|
||||
$hasExempt = Order::whereIn('id', $payOrderIds)
|
||||
->whereNull('delete_time')
|
||||
->where('is_exempt', 1)
|
||||
->count();
|
||||
if ($hasExempt > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查关联支付单的总金额
|
||||
$rows = Order::whereIn('id', $payOrderIds)->whereNull('delete_time')->column('amount', 'id');
|
||||
$totalAmount = 0.0;
|
||||
@@ -504,7 +513,7 @@ class PrescriptionOrderLogic
|
||||
return [];
|
||||
}
|
||||
$rows = Order::whereIn('id', $ids)->whereNull('delete_time')
|
||||
->field(['id', 'order_no', 'order_type', 'amount', 'status', 'create_time', 'creator_id', 'remark'])
|
||||
->field(['id', 'order_no', 'order_type', 'amount', 'status', 'create_time', 'creator_id', 'remark', 'is_exempt'])
|
||||
->order('id', 'asc')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
Reference in New Issue
Block a user