liuhairui 2 settimane fa
parent
commit
91b846e3ff

+ 131 - 29
application/admin/controller/Procuremen.php

@@ -2678,6 +2678,9 @@ class Procuremen extends Backend
                 }
                 }
             }
             }
 
 
+            $this->updatePurchaseOrderDetailStatusNameByIds($sid, $selectedIds, '已完成');
+            $this->updatePurchaseOrderDetailStatusNameByIds($sid, $unselectedIds, '未通过');
+
             if ($manageTransaction) {
             if ($manageTransaction) {
                 Db::commit();
                 Db::commit();
             }
             }
@@ -2712,27 +2715,7 @@ class Procuremen extends Backend
             $poIdFinal > 0 ? $poIdFinal : null
             $poIdFinal > 0 ? $poIdFinal : null
         );
         );
 
 
-        $ccydh = '';
-        $cyjmc = '';
-        try {
-            $po = Db::table('purchase_order')->where('scydgy_id', $sid)->find();
-            if (is_array($po)) {
-                $ccydh = trim((string)($po['CCYDH'] ?? ''));
-                $cyjmc = trim((string)($po['CYJMC'] ?? ''));
-            }
-        } catch (\Throwable $e) {
-        }
-        if ($ccydh === '' || $cyjmc === '') {
-            $any = $this->purchaseOrderDetai($sid, $selectedIds[0] ?? 0);
-            if (is_array($any)) {
-                if ($ccydh === '') {
-                    $ccydh = trim((string)($any['CCYDH'] ?? ''));
-                }
-                if ($cyjmc === '') {
-                    $cyjmc = trim((string)($any['CYJMC'] ?? ''));
-                }
-            }
-        }
+        $confirmNotifyVars = $this->buildPurchaseConfirmNotifyVars($sid, $selectedIds[0] ?? 0);
 
 
         $sendSmsSafe = function ($phone, $content) {
         $sendSmsSafe = function ($phone, $content) {
             $phone = trim((string)$phone);
             $phone = trim((string)$phone);
@@ -2753,13 +2736,11 @@ class Procuremen extends Backend
             }
             }
             $cname = trim((string)($dr['company_name'] ?? ''));
             $cname = trim((string)($dr['company_name'] ?? ''));
             $ph = trim((string)($dr['phone'] ?? ''));
             $ph = trim((string)($dr['phone'] ?? ''));
-            $sms = $this->renderNotifyTemplate('confirm_ok', [
+            $sms = $this->renderNotifyTemplate('confirm_ok', array_merge($confirmNotifyVars, [
                 'company_name' => $cname,
                 'company_name' => $cname,
                 'contact_name' => $this->resolveCustomerContactName($ph, $cname),
                 'contact_name' => $this->resolveCustomerContactName($ph, $cname),
                 'phone'        => $ph,
                 'phone'        => $ph,
-                'ccydh'        => $ccydh,
-                'cyjmc'        => $cyjmc,
-            ]);
+            ]));
             $sendSmsSafe((string)($dr['phone'] ?? ''), $sms);
             $sendSmsSafe((string)($dr['phone'] ?? ''), $sms);
         }
         }
         foreach ($unselectedIds as $pid) {
         foreach ($unselectedIds as $pid) {
@@ -2769,13 +2750,11 @@ class Procuremen extends Backend
             }
             }
             $cname = trim((string)($dr['company_name'] ?? ''));
             $cname = trim((string)($dr['company_name'] ?? ''));
             $ph = trim((string)($dr['phone'] ?? ''));
             $ph = trim((string)($dr['phone'] ?? ''));
-            $sms = $this->renderNotifyTemplate('confirm_fail', [
+            $sms = $this->renderNotifyTemplate('confirm_fail', array_merge($confirmNotifyVars, [
                 'company_name' => $cname,
                 'company_name' => $cname,
                 'contact_name' => $this->resolveCustomerContactName($ph, $cname),
                 'contact_name' => $this->resolveCustomerContactName($ph, $cname),
                 'phone'        => $ph,
                 'phone'        => $ph,
-                'ccydh'        => $ccydh,
-                'cyjmc'        => $cyjmc,
-            ]);
+            ]));
             $sendSmsSafe((string)($dr['phone'] ?? ''), $sms);
             $sendSmsSafe((string)($dr['phone'] ?? ''), $sms);
         }
         }
 
 
@@ -2803,6 +2782,68 @@ class Procuremen extends Backend
         ];
         ];
     }
     }
 
 
+    /**
+     * 批量更新 purchase_order_detail.status_name(兼容 id / ID 列名)
+     *
+     * @param int[] $detailIds
+     */
+    protected function updatePurchaseOrderDetailStatusNameByIds(int $sid, array $detailIds, string $statusName): void
+    {
+        $detailIds = array_values(array_unique(array_filter(array_map('intval', $detailIds))));
+        if ($detailIds === []) {
+            return;
+        }
+        $payload = ['status_name' => $statusName];
+        try {
+            Db::table('purchase_order_detail')->where('scydgy_id', $sid)->where('id', 'in', $detailIds)->update($payload);
+        } catch (\Throwable $e) {
+            Db::table('purchase_order_detail')->where('scydgy_id', $sid)->where('ID', 'in', $detailIds)->update($payload);
+        }
+    }
+
+    /**
+     * 审批驳回后按是否已报价恢复 status_name(未提交 / 已提交)
+     */
+    protected function restorePurchaseOrderDetailStatusNamesAfterReject(int $sid): void
+    {
+        try {
+            $rows = Db::table('purchase_order_detail')->where('scydgy_id', $sid)->select();
+        } catch (\Throwable $e) {
+            return;
+        }
+        if (!is_array($rows)) {
+            return;
+        }
+        foreach ($rows as $dr) {
+            if (!is_array($dr)) {
+                continue;
+            }
+            $id = (int)($dr['id'] ?? $dr['ID'] ?? 0);
+            if ($id <= 0) {
+                continue;
+            }
+            $sn = $this->resolvePurchaseOrderDetailQuoteStatusName($dr);
+            $pk = isset($dr['id']) ? 'id' : (isset($dr['ID']) ? 'ID' : 'id');
+            try {
+                Db::table('purchase_order_detail')->where($pk, $id)->update(['status_name' => $sn]);
+            } catch (\Throwable $e) {
+            }
+        }
+    }
+
+    /**
+     * @param array<string, mixed> $detailRow
+     */
+    protected function resolvePurchaseOrderDetailQuoteStatusName(array $detailRow): string
+    {
+        $am = $detailRow['amount'] ?? null;
+        $dv = isset($detailRow['delivery']) ? trim((string)$detailRow['delivery']) : '';
+        $amountFilled = !($am === null || $am === '' || (is_string($am) && trim($am) === ''));
+        $deliveryFilled = ($dv !== '' && !preg_match('/^0000-00-00/i', $dv));
+
+        return ($amountFilled || $deliveryFilled) ? '已提交' : '未提交';
+    }
+
     /**
     /**
      * @return int[]
      * @return int[]
      */
      */
@@ -4226,6 +4267,64 @@ class Procuremen extends Backend
         return implode("\n", $lines);
         return implode("\n", $lines);
     }
     }
 
 
+    /**
+     * 采购确认短信/邮件共用变量(含订单号、印件名称、工序明细)
+     *
+     * @return array<string, string>
+     */
+    protected function buildPurchaseConfirmNotifyVars(int $sid, int $fallbackDetailId = 0): array
+    {
+        $po = null;
+        try {
+            $po = Db::table('purchase_order')->where('scydgy_id', $sid)->find();
+        } catch (\Throwable $e) {
+            $po = null;
+        }
+        $ccydh = is_array($po) ? trim((string)($po['CCYDH'] ?? '')) : '';
+        $cyjmc = is_array($po) ? trim((string)($po['CYJMC'] ?? '')) : '';
+        if (($ccydh === '' || $cyjmc === '') && $fallbackDetailId > 0) {
+            $any = $this->purchaseOrderDetai($sid, $fallbackDetailId);
+            if (is_array($any)) {
+                if ($ccydh === '') {
+                    $ccydh = trim((string)($any['CCYDH'] ?? ''));
+                }
+                if ($cyjmc === '') {
+                    $cyjmc = trim((string)($any['CYJMC'] ?? ''));
+                }
+            }
+        }
+        $mergeRows = is_array($po) && $po !== [] ? [$po] : [];
+        $processPlain = $mergeRows !== [] ? $this->buildProcessLinesPlain($mergeRows) : '';
+        $processLines = $this->buildPurchaseConfirmProcessLinesText($ccydh, $cyjmc, $processPlain);
+
+        return [
+            'ccydh'              => $ccydh,
+            'cyjmc'              => $cyjmc,
+            'cgymc'              => is_array($po) ? trim((string)($po['CGYMC'] ?? '')) : '',
+            'process_lines'      => $processLines,
+            'process_lines_html' => '',
+        ];
+    }
+
+    /**
+     * 采购确认短信中的工序/订单信息块(模版常用 {process_lines})
+     */
+    protected function buildPurchaseConfirmProcessLinesText(string $ccydh, string $cyjmc, string $processPlain): string
+    {
+        $lines = [];
+        if ($ccydh !== '') {
+            $lines[] = '订单号:' . $ccydh;
+        }
+        if ($cyjmc !== '') {
+            $lines[] = '印件名称:' . $cyjmc;
+        }
+        if ($processPlain !== '') {
+            $lines[] = $processPlain;
+        }
+
+        return implode("\n", $lines);
+    }
+
     /**
     /**
      * 各工序手机端链接(纯文本,供短信模版 {platform_links})
      * 各工序手机端链接(纯文本,供短信模版 {platform_links})
      *
      *
@@ -6020,6 +6119,7 @@ class Procuremen extends Backend
             'pick_company_name' => '',
             'pick_company_name' => '',
         ]);
         ]);
         Db::table('purchase_order_detail')->where('scydgy_id', $sid)->update(['status' => 0]);
         Db::table('purchase_order_detail')->where('scydgy_id', $sid)->update(['status' => 0]);
+        $this->restorePurchaseOrderDetailStatusNamesAfterReject($sid);
 
 
         $poId = 0;
         $poId = 0;
         try {
         try {
@@ -6911,6 +7011,8 @@ class Procuremen extends Backend
         foreach ($vars as $k => $v) {
         foreach ($vars as $k => $v) {
             $tpl = str_replace('{' . $k . '}', (string)$v, $tpl);
             $tpl = str_replace('{' . $k . '}', (string)$v, $tpl);
         }
         }
+        // 未传入的占位符置空,避免短信出现 {process_lines} 等原文
+        $tpl = preg_replace('/\{[a-zA-Z0-9_]+\}/', '', $tpl);
 
 
         return $tpl;
         return $tpl;
     }
     }

+ 1 - 1
application/admin/model/Purchasesmstemplate.php

@@ -70,7 +70,7 @@ class Purchasesmstemplate extends Model
             ['tag' => '{cgymc}', 'label' => '工序名称(单道工序)', 'example' => '骑马订', 'scenes' => '外发下发'],
             ['tag' => '{cgymc}', 'label' => '工序名称(单道工序)', 'example' => '骑马订', 'scenes' => '外发下发'],
             ['tag' => '{category}', 'label' => '业务分类', 'example' => '出版物印刷', 'scenes' => '外发下发'],
             ['tag' => '{category}', 'label' => '业务分类', 'example' => '出版物印刷', 'scenes' => '外发下发'],
             ['tag' => '{deadline}', 'label' => '截止时间', 'example' => '2026-05-18 14:30', 'scenes' => '外发下发'],
             ['tag' => '{deadline}', 'label' => '截止时间', 'example' => '2026-05-18 14:30', 'scenes' => '外发下发'],
-            ['tag' => '{process_lines}', 'label' => '订单工序明细(文本)', 'example' => "1.压折线 单位:张\n2.模切", 'scenes' => '外发下发'],
+            ['tag' => '{process_lines}', 'label' => '订单工序明细(文本)', 'example' => "订单号:YW20240629001\n印件名称:藏书票2\n1.工序名称:做刀版 单位:张 本次数量:500", 'scenes' => '全部'],
             ['tag' => '{process_lines_html}', 'label' => '订单工序明细(表格)', 'example' => '<table>…</table>', 'scenes' => '外发下发邮箱'],
             ['tag' => '{process_lines_html}', 'label' => '订单工序明细(表格)', 'example' => '<table>…</table>', 'scenes' => '外发下发邮箱'],
             ['tag' => '{platform_url}', 'label' => '平台链接', 'example' => 'https://…', 'scenes' => '外发下发邮箱'],
             ['tag' => '{platform_url}', 'label' => '平台链接', 'example' => 'https://…', 'scenes' => '外发下发邮箱'],
         ];
         ];

+ 188 - 1
application/index/controller/Index.php

@@ -894,6 +894,9 @@ class Index extends Frontend
         if ($userWhere !== []) {
         if ($userWhere !== []) {
             $query->where($userWhere);
             $query->where($userWhere);
         }
         }
+        if (trim((string)$q) === '' && $tab === 'done' && $statusNameCol !== null) {
+            $this->mprocSyncLegacyApprovedStatusNames($user, $statusNameCol);
+        }
         $this->mprocApplySearchKeywordToDetailQuery($query, $q);
         $this->mprocApplySearchKeywordToDetailQuery($query, $q);
         // 有搜索词时不在此按 status_name 分栏筛选,全局匹配;无搜索词时仍按左侧 Tab(未提交/已提交/已完成)筛选
         // 有搜索词时不在此按 status_name 分栏筛选,全局匹配;无搜索词时仍按左侧 Tab(未提交/已提交/已完成)筛选
         if (trim((string)$q) === '') {
         if (trim((string)$q) === '') {
@@ -946,6 +949,13 @@ class Index extends Frontend
             if ($this->mprocIsValidScydgyRowId($sid) && isset($poBySid[$sid])) {
             if ($this->mprocIsValidScydgyRowId($sid) && isset($poBySid[$sid])) {
                 $this->mprocMergePurchaseOrderIntoDetail($row, $poBySid[$sid]);
                 $this->mprocMergePurchaseOrderIntoDetail($row, $poBySid[$sid]);
             }
             }
+            $poRow = ($this->mprocIsValidScydgyRowId($sid) && isset($poBySid[$sid])) ? $poBySid[$sid] : null;
+            $oldSn = trim((string)($row['status_name'] ?? ''));
+            $effectiveSn = $this->mprocResolveEffectiveStatusName($row, $poRow);
+            $row['status_name'] = $effectiveSn;
+            if ($statusNameCol !== null && $effectiveSn !== $oldSn && $row['eid'] > 0) {
+                $this->mprocPersistDetailStatusName((int)$row['eid'], $statusNameCol, $effectiveSn);
+            }
             // status_name 由库表/后端维护,不在此根据 amount 覆盖
             // status_name 由库表/后端维护,不在此根据 amount 覆盖
             if (!isset($row['status_name']) || $row['status_name'] === null) {
             if (!isset($row['status_name']) || $row['status_name'] === null) {
                 $row['status_name'] = '';
                 $row['status_name'] = '';
@@ -975,6 +985,18 @@ class Index extends Frontend
         }
         }
         unset($row);
         unset($row);
 
 
+        if (trim((string)$q) === '' && $statusNameCol !== null) {
+            $tabLabelMap = [
+                'draft'     => '未提交',
+                'submitted' => '已提交',
+                'done'      => '已完成',
+            ];
+            $expectLabel = $tabLabelMap[$tab] ?? '未提交';
+            $rows = array_values(array_filter($rows, function ($r) use ($expectLabel) {
+                return is_array($r) && trim((string)($r['status_name'] ?? '')) === $expectLabel;
+            }));
+        }
+
         return [
         return [
             'rows'             => $rows ?: [],
             'rows'             => $rows ?: [],
             'done_no_status'   => (int)($statusNameCol === null),
             'done_no_status'   => (int)($statusNameCol === null),
@@ -1000,6 +1022,154 @@ class Index extends Frontend
         return is_scalar($gzl) ? trim((string)$gzl) : '';
         return is_scalar($gzl) ? trim((string)$gzl) : '';
     }
     }
 
 
+    /**
+     * 明细是否已填写单价或交货日期
+     *
+     * @param array<string, mixed> $row
+     */
+    protected function mprocDetailQuoteSubmitted(array $row): bool
+    {
+        $am = $row['amount'] ?? null;
+        $dv = isset($row['delivery']) ? trim((string)$row['delivery']) : '';
+        $amountFilled = !($am === null || $am === '' || (is_string($am) && trim($am) === ''));
+        $deliveryFilled = ($dv !== '' && !preg_match('/^0000-00-00/i', $dv));
+
+        return $amountFilled || $deliveryFilled;
+    }
+
+    /**
+     * 手机端列表 Tab 用 status_name;审批通过后主表 status=1 时按明细 status 纠偏
+     *
+     * @param array<string, mixed>      $row
+     * @param array<string, mixed>|null $po
+     */
+    protected function mprocResolveEffectiveStatusName(array $row, ?array $po): string
+    {
+        $sn = trim((string)($row['status_name'] ?? ''));
+        if (in_array($sn, ['已完成', '未通过', '已废弃'], true)) {
+            return $sn;
+        }
+        if (!is_array($po)) {
+            if ($sn !== '') {
+                return $sn;
+            }
+
+            return $this->mprocDetailQuoteSubmitted($row) ? '已提交' : '未提交';
+        }
+        $poStatus = (int)($po['status'] ?? $po['STATUS'] ?? 0);
+        if ($poStatus !== 1) {
+            if ($sn !== '') {
+                return $sn;
+            }
+
+            return $this->mprocDetailQuoteSubmitted($row) ? '已提交' : '未提交';
+        }
+        $detailStatus = (int)($row['status'] ?? $row['STATUS'] ?? 0);
+        if ($detailStatus === 1) {
+            return '已完成';
+        }
+        if ($sn === '已提交') {
+            return '未通过';
+        }
+
+        return $sn !== '' ? $sn : '未提交';
+    }
+
+    /**
+     * 将纠偏后的 status_name 写回库表(兼容历史已审批数据)
+     */
+    protected function mprocPersistDetailStatusName(int $detailId, string $statusNameCol, string $statusName): void
+    {
+        if ($detailId <= 0 || $statusNameCol === '') {
+            return;
+        }
+        $idCol = $this->mprocResolveProcuremenColumn(['id']);
+        if ($idCol === null || $idCol === '') {
+            return;
+        }
+        try {
+            Db::table('purchase_order_detail')->where($idCol, $detailId)->update([$statusNameCol => $statusName]);
+        } catch (\Throwable $e) {
+        }
+    }
+
+    /**
+     * 纠偏历史数据:主表已审批(status=1)但明细 status_name 仍为「已提交」
+     *
+     * @param array<string, mixed> $user
+     */
+    protected function mprocSyncLegacyApprovedStatusNames(array $user, string $statusNameCol): void
+    {
+        $userWhere = $this->mprocListWhereForLoginUser($user);
+        try {
+            $query = Db::table('purchase_order_detail')->where($statusNameCol, '已提交');
+            if ($userWhere !== []) {
+                $query->where($userWhere);
+            }
+            $candidates = $query->limit(200)->select();
+        } catch (\Throwable $e) {
+            return;
+        }
+        if (!is_array($candidates) || $candidates === []) {
+            return;
+        }
+        $sidList = [];
+        foreach ($candidates as $cr) {
+            if (!is_array($cr)) {
+                continue;
+            }
+            $sid = (int)($cr['scydgy_id'] ?? $cr['SCYDGY_ID'] ?? 0);
+            if ($this->mprocIsValidScydgyRowId($sid)) {
+                $sidList[$sid] = true;
+            }
+        }
+        if ($sidList === []) {
+            return;
+        }
+        $poBySid = [];
+        try {
+            $poRows = Db::table('purchase_order')
+                ->where('scydgy_id', 'in', array_keys($sidList))
+                ->where('status', 1)
+                ->select();
+            if (is_array($poRows)) {
+                foreach ($poRows as $pr) {
+                    $sidk = (int)($pr['scydgy_id'] ?? $pr['SCYDGY_ID'] ?? 0);
+                    if ($this->mprocIsValidScydgyRowId($sidk)) {
+                        $poBySid[$sidk] = $pr;
+                    }
+                }
+            }
+        } catch (\Throwable $e) {
+            return;
+        }
+        if ($poBySid === []) {
+            return;
+        }
+        $idCol = $this->mprocResolveProcuremenColumn(['id']);
+        if ($idCol === null || $idCol === '') {
+            return;
+        }
+        foreach ($candidates as $cr) {
+            if (!is_array($cr)) {
+                continue;
+            }
+            $sid = (int)($cr['scydgy_id'] ?? $cr['SCYDGY_ID'] ?? 0);
+            if (!isset($poBySid[$sid])) {
+                continue;
+            }
+            $detailId = (int)($cr[$idCol] ?? $cr['id'] ?? $cr['ID'] ?? 0);
+            if ($detailId <= 0) {
+                continue;
+            }
+            $targetSn = $this->mprocResolveEffectiveStatusName($cr, $poBySid[$sid]);
+            if ($targetSn === '已提交') {
+                continue;
+            }
+            $this->mprocPersistDetailStatusName($detailId, $statusNameCol, $targetSn);
+        }
+    }
+
     /**
     /**
      * 外发明细首页(需登录)
      * 外发明细首页(需登录)
      * GET:main_tab=orders|me,orders 时 tab=draft|submitted|done 对应 status_name:未提交|已提交|已完成;q 搜索词
      * GET:main_tab=orders|me,orders 时 tab=draft|submitted|done 对应 status_name:未提交|已提交|已完成;q 搜索词
@@ -1333,6 +1503,10 @@ class Index extends Frontend
         if (!empty($user['is_admin'])) {
         if (!empty($user['is_admin'])) {
             return false;
             return false;
         }
         }
+        $sn = trim((string)($row['status_name'] ?? ''));
+        if (in_array($sn, ['已完成', '未通过', '已废弃'], true)) {
+            return false;
+        }
         $uCo = trim((string)($user['company_name'] ?? ''));
         $uCo = trim((string)($user['company_name'] ?? ''));
         if ($uCo === '') {
         if ($uCo === '') {
             $uPhone = trim((string)($user['phone'] ?? ''));
             $uPhone = trim((string)($user['phone'] ?? ''));
@@ -1419,7 +1593,20 @@ class Index extends Frontend
         if (!$row || !is_array($row)) {
         if (!$row || !is_array($row)) {
             $this->error('记录不存在');
             $this->error('记录不存在');
         }
         }
-        if (!$this->mprocCanEditRow($user, $row)) {
+        $sid = (int)($row['scydgy_id'] ?? $row['SCYDGY_ID'] ?? 0);
+        $po = null;
+        if ($this->mprocIsValidScydgyRowId($sid)) {
+            try {
+                $po = Db::table('purchase_order')->where('scydgy_id', $sid)->find();
+            } catch (\Throwable $e) {
+                $po = null;
+            }
+        }
+        $effectiveSn = $this->mprocResolveEffectiveStatusName($row, is_array($po) ? $po : null);
+        if (in_array($effectiveSn, ['已完成', '未通过', '已废弃'], true)) {
+            $this->error('订单已结束,不能再修改单价与交货日期');
+        }
+        if (!$this->mprocCanEditRow($user, array_merge($row, ['status_name' => $effectiveSn]))) {
             if (!empty($user['is_admin'])) {
             if (!empty($user['is_admin'])) {
                 $this->error('当前账号仅可查看,不能修改单价与交货日期');
                 $this->error('当前账号仅可查看,不能修改单价与交货日期');
             }
             }