|
|
@@ -48,7 +48,6 @@ class ProcuremenDashboard
|
|
|
$todayEnd = date('Y-m-d H:i:s');
|
|
|
$monthStart = date('Y-m-01 00:00:00');
|
|
|
$monthEnd = date('Y-m-t 23:59:59');
|
|
|
- $pending = self::countCurrentPending();
|
|
|
|
|
|
return [
|
|
|
'supplier' => [
|
|
|
@@ -57,14 +56,14 @@ class ProcuremenDashboard
|
|
|
'link_text' => '',
|
|
|
],
|
|
|
'confirm' => [
|
|
|
- 'today' => (int)$pending['confirm'],
|
|
|
- 'month' => self::countOperLogDistinctOrdersInRange(['issue_submit'], $monthStart, $monthEnd),
|
|
|
+ 'today' => self::countOperLogDistinctOrdersInRange(['issue_submit'], $todayStart, $todayEnd),
|
|
|
+ 'month' => self::countPendingStageOrdersInMonth('confirm', $monthStart, $monthEnd),
|
|
|
'link' => 'procuremen/audit',
|
|
|
'link_text' => '供应商确认',
|
|
|
],
|
|
|
'approval' => [
|
|
|
- 'today' => (int)$pending['approval'],
|
|
|
- 'month' => self::countOperLogDistinctOrdersInRange(['audit_select'], $monthStart, $monthEnd),
|
|
|
+ 'today' => self::countOperLogDistinctOrdersInRange(['audit_select'], $todayStart, $todayEnd),
|
|
|
+ 'month' => self::countPendingStageOrdersInMonth('approval', $monthStart, $monthEnd),
|
|
|
'link' => 'procuremen/confirm',
|
|
|
'link_text' => '供应商审批',
|
|
|
],
|
|
|
@@ -410,33 +409,12 @@ class ProcuremenDashboard
|
|
|
$startTs,
|
|
|
$endTs
|
|
|
);
|
|
|
- $confirmBuckets = self::emptyDayBuckets($dayKeys);
|
|
|
-
|
|
|
- try {
|
|
|
- $expr = self::pickTimeSqlExpr();
|
|
|
- $query = Db::table('purchase_order')->field('CCYDH,pick_time,createtime,dputrecord,dStamp,wflow_status,status');
|
|
|
- self::applyNotDeletedWhere($query);
|
|
|
- $query->whereRaw("({$expr}) >= ? AND ({$expr}) <= ?", [$rangeStart, $rangeEnd]);
|
|
|
- $rows = $query->select();
|
|
|
- } catch (\Throwable $e) {
|
|
|
- $rows = [];
|
|
|
- }
|
|
|
- if (is_array($rows)) {
|
|
|
- foreach ($rows as $row) {
|
|
|
- if (!is_array($row)) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- $day = self::resolveRowDayKey($row, $dayKeys);
|
|
|
- if ($day === null) {
|
|
|
- continue;
|
|
|
- }
|
|
|
- $orderKey = self::resolveOrderKey($row);
|
|
|
- if (self::isPendingConfirmRow($row)) {
|
|
|
- $confirmBuckets[$day][$orderKey] = true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
+ $confirmBuckets = self::bucketOperLogOrdersByDay(
|
|
|
+ self::loadOperLogsInRange(['issue_submit'], $startTs, $endTs),
|
|
|
+ $dayKeys,
|
|
|
+ $startTs,
|
|
|
+ $endTs
|
|
|
+ );
|
|
|
$approvalBuckets = self::bucketOperLogOrdersByDay(
|
|
|
self::loadOperLogsInRange(['audit_select'], $startTs, $endTs),
|
|
|
$dayKeys,
|
|
|
@@ -643,19 +621,142 @@ class ProcuremenDashboard
|
|
|
return count($keys);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 本月待处理:与「确认供应商 / 采购确认」列表一致(当前队列 + 下发月份 + 订单号去重)
|
|
|
+ */
|
|
|
+ protected static function countPendingStageOrdersInMonth(string $stage, string $monthStart, string $monthEnd): int
|
|
|
+ {
|
|
|
+ $rows = self::loadPendingStageRowsWithTime($stage);
|
|
|
+ if ($rows === []) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ $matched = [];
|
|
|
+ foreach ($rows as $row) {
|
|
|
+ if (!is_array($row)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $normalized = self::normalizeRowForListMonth($row);
|
|
|
+ if (!self::rowMatchesMonthRange($normalized, $monthStart, $monthEnd, 'pick_time')) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $matched[] = $normalized;
|
|
|
+ }
|
|
|
+
|
|
|
+ return self::countDistinctOrders($matched);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @return array<int, array<string, mixed>>
|
|
|
+ */
|
|
|
+ protected static function loadPendingStageRowsWithTime(string $stage): array
|
|
|
+ {
|
|
|
+ try {
|
|
|
+ $fields = 'id,CCYDH,scydgy_id,pick_time,createtime,dputrecord,dStamp,wflow_status,status';
|
|
|
+ $query = Db::table('purchase_order')->field($fields);
|
|
|
+ self::applyNotDeletedWhere($query);
|
|
|
+ if ($stage === 'confirm') {
|
|
|
+ $query->whereIn('wflow_status', ProcuremenStatus::wflowPendingConfirmValues());
|
|
|
+ } else {
|
|
|
+ $vals = ProcuremenStatus::wflowPendingApprovalValues();
|
|
|
+ $quoted = [];
|
|
|
+ foreach ($vals as $v) {
|
|
|
+ $quoted[] = "'" . str_replace("'", "''", (string)$v) . "'";
|
|
|
+ }
|
|
|
+ if ($quoted === []) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+ $query->whereRaw('TRIM(CAST(wflow_status AS CHAR)) IN (' . implode(',', $quoted) . ')');
|
|
|
+ $query->whereRaw('NOT (' . ProcuremenStatus::sqlPoCompleted('status') . ')');
|
|
|
+ }
|
|
|
+ $rows = $query->select();
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ return is_array($rows) ? $rows : [];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 列表月份筛选前补齐时间字段(与 buildListRowFromPurchaseOrderDbRow 回退一致)
|
|
|
+ *
|
|
|
+ * @param array<string, mixed> $row
|
|
|
+ * @return array<string, mixed>
|
|
|
+ */
|
|
|
+ protected static function normalizeRowForListMonth(array $row): array
|
|
|
+ {
|
|
|
+ $r = $row;
|
|
|
+ $r['createtime'] = ProcuremenTime::formatDisplayDateTime($row['createtime'] ?? null);
|
|
|
+
|
|
|
+ $dpOut = trim((string)($r['dputrecord'] ?? ''));
|
|
|
+ if ($dpOut === '' || stripos($dpOut, '0000-00-00') === 0) {
|
|
|
+ $fallback = trim((string)($r['dStamp'] ?? ''));
|
|
|
+ if ($fallback !== '' && stripos($fallback, '0000-00-00') !== 0) {
|
|
|
+ $r['dputrecord'] = $fallback;
|
|
|
+ } elseif ($r['createtime'] !== '') {
|
|
|
+ $r['dputrecord'] = $r['createtime'];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $pickOut = trim((string)($r['pick_time'] ?? ''));
|
|
|
+ if ($pickOut === '' || stripos($pickOut, '0000-00-00') === 0) {
|
|
|
+ foreach (['createtime', 'dputrecord', 'dStamp'] as $pickFallbackKey) {
|
|
|
+ $pickFallback = trim((string)($r[$pickFallbackKey] ?? ''));
|
|
|
+ if ($pickFallback !== '' && stripos($pickFallback, '0000-00-00') !== 0) {
|
|
|
+ $r['pick_time'] = $pickFallback;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $r;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param array<string, mixed> $row
|
|
|
+ */
|
|
|
+ protected static function resolveRowListTime(array $row, string $primary = 'pick_time'): string
|
|
|
+ {
|
|
|
+ $keys = array_values(array_unique([$primary, 'pick_time', 'createtime', 'dputrecord', 'dStamp']));
|
|
|
+ foreach ($keys as $k) {
|
|
|
+ $t = trim((string)($row[$k] ?? ''));
|
|
|
+ if ($t !== '' && stripos($t, '0000-00-00') !== 0) {
|
|
|
+ return $t;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * @param array<string, mixed> $row
|
|
|
+ */
|
|
|
+ protected static function rowMatchesMonthRange(array $row, string $monthStart, string $monthEnd, string $primary = 'pick_time'): bool
|
|
|
+ {
|
|
|
+ $listTime = self::resolveRowListTime($row, $primary);
|
|
|
+ if ($listTime === '' || stripos($listTime, '0000-00-00') === 0
|
|
|
+ || !preg_match('/^([12]\d{3})-(\d{1,2})-(\d{1,2})/', $listTime, $m)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $mo = (int)$m[2];
|
|
|
+ if ($mo < 1 || $mo > 12) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $ymRow = $m[1] . '-' . str_pad((string)$mo, 2, '0', STR_PAD_LEFT);
|
|
|
+ $rowMonthStart = $ymRow . '-01 00:00:00';
|
|
|
+ $rowMonthEnd = date('Y-m-t 23:59:59', strtotime($rowMonthStart));
|
|
|
+
|
|
|
+ return !(strcmp($rowMonthEnd, $monthStart) < 0 || strcmp($rowMonthStart, $monthEnd) > 0);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* @return array{issued:int,confirm:int,approval:int,completed:int}
|
|
|
*/
|
|
|
protected static function countStageMetrics(string $start, string $end): array
|
|
|
{
|
|
|
- $pending = self::countCurrentPending();
|
|
|
-
|
|
|
return [
|
|
|
- // 已下发:本时段内通知下发,且当前已离开「待确认」队列(不与待确认重复)
|
|
|
'issued' => self::countIssuedInRange($start, $end),
|
|
|
- // 待确认 / 待审批:与列表一致,统计当前积压(不按 pick_time 截断)
|
|
|
- 'confirm' => $pending['confirm'],
|
|
|
- 'approval' => $pending['approval'],
|
|
|
+ 'confirm' => self::countOperLogDistinctOrdersInRange(['issue_submit'], $start, $end),
|
|
|
+ 'approval' => self::countOperLogDistinctOrdersInRange(['audit_select'], $start, $end),
|
|
|
'completed' => self::countCompletedInRange($start, $end),
|
|
|
];
|
|
|
}
|