m0_70156489 1 vecka sedan
förälder
incheckning
6218d40d64
2 ändrade filer med 202 tillägg och 8 borttagningar
  1. 177 5
      application/index/controller/Index.php
  2. 25 3
      application/index/view/index/index.html

+ 177 - 5
application/index/controller/Index.php

@@ -1407,6 +1407,105 @@ class Index extends Frontend
         return ($sr !== '' && !preg_match('/^0000-00-00/i', $sr)) ? $sr : '';
     }
 
+    /**
+     * 从主表/明细解析订单号
+     *
+     * @param array<string, mixed>|null $po
+     * @param array<string, mixed>|null $row
+     */
+    protected function mprocResolveCcydhFromPoOrRow(?array $po, ?array $row = null): string
+    {
+        foreach ([$po, $row] as $src) {
+            if (!is_array($src)) {
+                continue;
+            }
+            $c = trim((string)($src['CCYDH'] ?? $src['ccydh'] ?? ''));
+            if ($c !== '') {
+                return $c;
+            }
+        }
+
+        return '';
+    }
+
+    /**
+     * 批量:已开标验证的订单号集合(本请求内可复用)
+     *
+     * @var array<string, bool>|null
+     */
+    protected $mprocBidOpenVerifiedSet = null;
+
+    /**
+     * @param array<int, string> $ccydhs
+     * @return array<string, bool>
+     */
+    protected function mprocLoadBidOpenVerifiedCcydhSet(array $ccydhs): array
+    {
+        $set = [];
+        $list = [];
+        foreach ($ccydhs as $c) {
+            $c = trim((string)$c);
+            if ($c !== '') {
+                $list[$c] = true;
+            }
+        }
+        if ($list === []) {
+            return $set;
+        }
+        try {
+            $rows = Db::table('purchase_order_bid_open')
+                ->where('ccydh', 'in', array_keys($list))
+                ->field('ccydh')
+                ->select();
+            if (!is_array($rows)) {
+                return $set;
+            }
+            foreach ($rows as $row) {
+                if (!is_array($row)) {
+                    continue;
+                }
+                $c = trim((string)($row['ccydh'] ?? ''));
+                if ($c !== '') {
+                    $set[$c] = true;
+                }
+            }
+        } catch (\Throwable $e) {
+            return [];
+        }
+
+        return $set;
+    }
+
+    /**
+     * 该订单是否已完成开标双重验证(有 purchase_order_bid_open 记录)
+     */
+    protected function mprocIsBidOpenVerified(string $ccydh): bool
+    {
+        $ccydh = trim($ccydh);
+        if ($ccydh === '') {
+            return false;
+        }
+        if (is_array($this->mprocBidOpenVerifiedSet)) {
+            return isset($this->mprocBidOpenVerifiedSet[$ccydh]);
+        }
+        try {
+            $row = Db::table('purchase_order_bid_open')->where('ccydh', $ccydh)->find();
+
+            return is_array($row);
+        } catch (\Throwable $e) {
+            return false;
+        }
+    }
+
+    /**
+     * @param array<string, mixed>|null $po
+     * @param array<string, mixed>|null $row
+     */
+    protected function mprocIsBidOpenVerifiedForPoOrRow(?array $po, ?array $row = null): bool
+    {
+        return $this->mprocIsBidOpenVerified($this->mprocResolveCcydhFromPoOrRow($po, $row));
+    }
+
     /**
      * 手机端:仅当主表设置了 sys_rq 且已到期时视为截止(无截止时间仍可填报)
      * 保存时务必再调一次,防止页面长时间打开、截止后仍提交
@@ -1454,6 +1553,40 @@ class Index extends Frontend
         }
     }
 
+    /**
+     * 保存前:截止时间 + 已开标均不可再改
+     *
+     * @param array<string, mixed>|null $po
+     * @param array<string, mixed>|null $row
+     */
+    protected function mprocAssertQuoteStillEditable(?array $po, string $label = '', ?array $row = null): void
+    {
+        $this->mprocAssertQuoteNotDeadlineReached($po, $label);
+        $freshPo = is_array($po) ? $po : null;
+        if (is_array($po)) {
+            $sid = (int)($po['scydgy_id'] ?? $po['SCYDGY_ID'] ?? 0);
+            if ($this->mprocIsValidScydgyRowId($sid)) {
+                try {
+                    $got = Db::table('purchase_order')->where('scydgy_id', $sid)->find();
+                    if (is_array($got)) {
+                        $freshPo = $got;
+                    }
+                } catch (\Throwable $e) {
+                }
+            }
+        }
+        // 保存场景不走列表缓存,直接查库
+        $prev = $this->mprocBidOpenVerifiedSet;
+        $this->mprocBidOpenVerifiedSet = null;
+        $opened = $this->mprocIsBidOpenVerifiedForPoOrRow($freshPo, $row);
+        $this->mprocBidOpenVerifiedSet = $prev;
+        if ($opened) {
+            throw new \InvalidArgumentException(
+                ($label !== '' ? $label : '') . '已开标验证,不可再提交'
+            );
+        }
+    }
+
     /**
      * 主单已完结后:中标 / 未中标
      *
@@ -1554,13 +1687,20 @@ class Index extends Frontend
             $g['lines'] = $lines;
             $g['line_count'] = count($lines);
             $canEdit = false;
+            $bidOpen = false;
             foreach ($lines as $ln) {
-                if (is_array($ln) && (int)($ln['mproc_can_edit'] ?? 0) === 1) {
+                if (!is_array($ln)) {
+                    continue;
+                }
+                if ((int)($ln['mproc_can_edit'] ?? 0) === 1) {
                     $canEdit = true;
-                    break;
+                }
+                if ((int)($ln['mproc_bid_open_verified'] ?? 0) === 1) {
+                    $bidOpen = true;
                 }
             }
             $g['can_edit'] = $canEdit ? 1 : 0;
+            $g['mproc_bid_open_verified'] = $bidOpen ? 1 : 0;
             $remark = '';
             $doneLabel = '';
             $pickResult = '';
@@ -1664,6 +1804,27 @@ class Index extends Frontend
             }
         }
 
+        $ccydhForBid = [];
+        foreach ($poBySid as $pr) {
+            if (!is_array($pr)) {
+                continue;
+            }
+            $c = trim((string)($pr['CCYDH'] ?? $pr['ccydh'] ?? ''));
+            if ($c !== '') {
+                $ccydhForBid[$c] = true;
+            }
+        }
+        foreach ($rows as $r0) {
+            if (!is_array($r0)) {
+                continue;
+            }
+            $c = trim((string)($r0['CCYDH'] ?? $r0['ccydh'] ?? ''));
+            if ($c !== '') {
+                $ccydhForBid[$c] = true;
+            }
+        }
+        $this->mprocBidOpenVerifiedSet = $this->mprocLoadBidOpenVerifiedCcydhSet(array_keys($ccydhForBid));
+
         foreach ($rows as &$row) {
             if (!is_array($row)) {
                 continue;
@@ -1704,6 +1865,7 @@ class Index extends Frontend
                 $row['status_name'] = trim((string)$row['status_name']);
             }
             $row['mproc_can_edit'] = $this->mprocCanEditRow($user, $row, $poRow) ? 1 : 0;
+            $row['mproc_bid_open_verified'] = $this->mprocIsBidOpenVerifiedForPoOrRow($poRow, $row) ? 1 : 0;
 
             $am = $row['amount'] ?? null;
             if ($am === null || $am === '' || (is_string($am) && trim($am) === '')) {
@@ -1933,6 +2095,7 @@ class Index extends Frontend
             $row['mproc_done_label'] = '';
         }
         $row['mproc_can_edit'] = $this->mprocCanEditRow($user, $row, $poRow) ? 1 : 0;
+        $row['mproc_bid_open_verified'] = $this->mprocIsBidOpenVerifiedForPoOrRow(is_array($poRow) ? $poRow : null, $row) ? 1 : 0;
         $am = $row['amount'] ?? null;
         $row['amount_display'] = ($am === null || $am === '' || (is_string($am) && trim($am) === '')) ? '' : (is_scalar($am) ? (string)$am : '');
         $dv = isset($row['delivery']) ? trim((string)$row['delivery']) : '';
@@ -2527,6 +2690,9 @@ class Index extends Frontend
         if ($this->mprocIsQuoteDeadlineReachedForPo($po)) {
             return false;
         }
+        if ($this->mprocIsBidOpenVerifiedForPoOrRow($po, $row)) {
+            return false;
+        }
         $uCo = trim((string)($user['company_name'] ?? ''));
         if ($uCo === '') {
             $uPhone = trim((string)($user['phone'] ?? ''));
@@ -2717,12 +2883,15 @@ class Index extends Frontend
         if (in_array($effectiveSn, ['已完成', '未通过', '已废弃'], true)) {
             throw new \InvalidArgumentException($label . '已结束,不能再修改');
         }
-        $this->mprocAssertQuoteNotDeadlineReached(is_array($po) ? $po : null, $label);
+        $this->mprocAssertQuoteStillEditable(is_array($po) ? $po : null, $label, $row);
         if (!$this->mprocCanEditRow($user, array_merge($row, ['status_name' => $effectiveSn]), is_array($po) ? $po : null)) {
             if (!empty($user['is_admin'])) {
                 throw new \InvalidArgumentException('当前账号仅可查看,不能修改单价与交货日期');
             }
-            // 截止后 canEdit=false,统一给明确文案
+            // 截止后 / 已开标 canEdit=false,统一给明确文案
+            if ($this->mprocIsBidOpenVerifiedForPoOrRow(is_array($po) ? $po : null, $row)) {
+                throw new \InvalidArgumentException($label . '已开标验证,不可再提交');
+            }
             if ($this->mprocIsQuoteDeadlineReachedForPo(is_array($po) ? $po : null)) {
                 throw new \InvalidArgumentException($label . '已超过招标截止时间,不可再提交');
             }
@@ -2917,11 +3086,14 @@ class Index extends Frontend
         if (in_array($effectiveSn, ['已完成', '未通过', '已废弃'], true)) {
             throw new \InvalidArgumentException('订单已结束,不能再修改备注');
         }
-        $this->mprocAssertQuoteNotDeadlineReached(is_array($po) ? $po : null);
+        $this->mprocAssertQuoteStillEditable(is_array($po) ? $po : null, '', $row);
         if (!$this->mprocCanEditRow($user, array_merge($row, ['status_name' => $effectiveSn]), is_array($po) ? $po : null)) {
             if (!empty($user['is_admin'])) {
                 throw new \InvalidArgumentException('当前账号仅可查看,不能修改备注');
             }
+            if ($this->mprocIsBidOpenVerifiedForPoOrRow(is_array($po) ? $po : null, $row)) {
+                throw new \InvalidArgumentException('已开标验证,不可再提交');
+            }
             if ($this->mprocIsQuoteDeadlineReachedForPo(is_array($po) ? $po : null)) {
                 throw new \InvalidArgumentException('已超过招标截止时间,不可再提交');
             }

+ 25 - 3
application/index/view/index/index.html

@@ -678,7 +678,7 @@
     {empty name="groups"}
     {notempty name="rows"}
     {volist name="rows" id="r"}
-    <div class="card order-group-card js-order-group{if $mprocFocusEid && $mprocFocusEid == $r.eid && ($mprocFocusTab == '' || $mprocFocusTab == $mprocTab)} mproc-card-highlight{/if}"{notempty name="r.mproc_remark"} data-remark="{$r.mproc_remark|htmlentities}"{/notempty} data-delivery-deadline="{$r.mproc_delivery_deadline|default=''|htmlentities}" data-bid-deadline="{$r.mproc_bid_deadline|default=''|htmlentities}">
+    <div class="card order-group-card js-order-group{if $mprocFocusEid && $mprocFocusEid == $r.eid && ($mprocFocusTab == '' || $mprocFocusTab == $mprocTab)} mproc-card-highlight{/if}"{notempty name="r.mproc_remark"} data-remark="{$r.mproc_remark|htmlentities}"{/notempty} data-delivery-deadline="{$r.mproc_delivery_deadline|default=''|htmlentities}" data-bid-deadline="{$r.mproc_bid_deadline|default=''|htmlentities}" data-bid-open="{$r.mproc_bid_open_verified|default=0}">
         <div class="order-group-head">
             <p class="title">{notempty name="r.CCYDH"}<span class="mproc-ord-no">{$r.CCYDH}</span>{/notempty}{$r.CYJMC|default=''}{eq name="mprocTab" value="draft"}<em class="mproc-pick-badge mproc-pick-unquoted">未报价</em>{/eq}{eq name="mprocTab" value="submitted"}<em class="mproc-pick-badge mproc-pick-quoted">已报价</em>{/eq}{notempty name="r.mproc_done_label"}{eq name="mprocTab" value="done"}<em class="mproc-pick-badge {eq name='r.mproc_pick_result' value='中标'}mproc-pick-win{else /}{eq name='r.mproc_pick_result' value='未中标'}mproc-pick-lose{else /}mproc-pick-expired{/eq}{/eq}">{$r.mproc_done_label|htmlentities}</em>{/eq}{/notempty}</p>
             <div class="kv-row order-group-deadline-row">
@@ -725,7 +725,7 @@
     {/notempty}
     {else /}
     {volist name="groups" id="g"}
-    <div class="card order-group-card js-order-group{volist name='g.lines' id='_fe'}{if $mprocFocusEid && $mprocFocusEid == $_fe.eid && ($mprocFocusTab == '' || $mprocFocusTab == $mprocTab)} mproc-card-highlight{/if}{/volist}"{notempty name="g.remark"} data-remark="{$g.remark|htmlentities}"{/notempty} data-delivery-deadline="{$g.mproc_delivery_deadline|default=''|htmlentities}" data-bid-deadline="{$g.mproc_bid_deadline|default=''|htmlentities}">
+    <div class="card order-group-card js-order-group{volist name='g.lines' id='_fe'}{if $mprocFocusEid && $mprocFocusEid == $_fe.eid && ($mprocFocusTab == '' || $mprocFocusTab == $mprocTab)} mproc-card-highlight{/if}{/volist}"{notempty name="g.remark"} data-remark="{$g.remark|htmlentities}"{/notempty} data-delivery-deadline="{$g.mproc_delivery_deadline|default=''|htmlentities}" data-bid-deadline="{$g.mproc_bid_deadline|default=''|htmlentities}" data-bid-open="{$g.mproc_bid_open_verified|default=0}">
         <div class="order-group-head">
             <p class="title">{notempty name="g.CCYDH"}<span class="mproc-ord-no">{$g.CCYDH}</span>{/notempty}{$g.CYJMC|default=''}{eq name="mprocTab" value="draft"}<em class="mproc-pick-badge mproc-pick-unquoted">未报价</em>{/eq}{eq name="mprocTab" value="submitted"}<em class="mproc-pick-badge mproc-pick-quoted">已报价</em>{/eq}{notempty name="g.mproc_done_label"}{eq name="mprocTab" value="done"}<em class="mproc-pick-badge {eq name='g.mproc_pick_result' value='中标'}mproc-pick-win{else /}{eq name='g.mproc_pick_result' value='未中标'}mproc-pick-lose{else /}mproc-pick-expired{/eq}{/eq}">{$g.mproc_done_label|htmlentities}</em>{/eq}{/notempty}</p>
             <div class="kv-row order-group-deadline-row">
@@ -1433,6 +1433,12 @@
                 return parseInt(r.mproc_can_edit, 10) === 1;
             });
         }
+        var bidOpen = parseInt(g.mproc_bid_open_verified, 10) === 1;
+        if (!bidOpen) {
+            bidOpen = lines.some(function (r) {
+                return parseInt(r.mproc_bid_open_verified, 10) === 1;
+            });
+        }
         var remark = pick(g, ['remark', 'Remark']);
         if (!remark && lines.length) {
             for (var ri = 0; ri < lines.length; ri++) {
@@ -1461,7 +1467,8 @@
         }).join('');
         return '<div class="card order-group-card js-order-group' + groupHl + '"' + remarkAttr
             + ' data-delivery-deadline="' + mprocEscAttr(delDl || '') + '"'
-            + ' data-bid-deadline="' + mprocEscAttr(bidDlRaw || '') + '">'
+            + ' data-bid-deadline="' + mprocEscAttr(bidDlRaw || '') + '"'
+            + ' data-bid-open="' + (bidOpen ? '1' : '0') + '">'
             + '<div class="order-group-head">'
             + '<p class="title">' + (ccydh ? ('<span class="mproc-ord-no">' + mprocEsc(ccydh) + '</span>') : '') + mprocEsc(tit) + badgeHtml + '</p>'
             + deadlineHtml
@@ -1692,6 +1699,7 @@
     var currentEditLines = [];
     var currentEditDeliveryDeadline = '';
     var currentEditBidDeadline = '';
+    var currentEditBidOpen = false;
     var mprocToastTimer = null;
 
     function mprocShowToast(msg, ms) {
@@ -1739,6 +1747,10 @@
     }
 
     function mprocAssertEditBidDeadlineOk() {
+        if (currentEditBidOpen) {
+            mprocShowToast('已开标验证,不可再提交');
+            return false;
+        }
         if (!mprocIsBidDeadlineReached(currentEditBidDeadline)) {
             return true;
         }
@@ -2277,6 +2289,14 @@
             return;
         }
         var bidRaw = groupEl ? String(groupEl.getAttribute('data-bid-deadline') || '').trim() : '';
+        var bidOpen = groupEl ? String(groupEl.getAttribute('data-bid-open') || '') === '1' : false;
+        if (bidOpen) {
+            mprocShowToast('已开标验证,不可再提交');
+            if (currentMain === 'orders') {
+                fetchOrderList(currentListTab, getSearchQ());
+            }
+            return;
+        }
         if (mprocIsBidDeadlineReached(bidRaw)) {
             mprocShowToast('已超过招标截止时间,不可再提交');
             if (currentMain === 'orders') {
@@ -2286,6 +2306,7 @@
         }
         currentEditLines = lines;
         currentEditBidDeadline = bidRaw;
+        currentEditBidOpen = false;
         var dlRaw = groupEl ? String(groupEl.getAttribute('data-delivery-deadline') || '').trim() : '';
         currentEditDeliveryDeadline = (dlRaw && /^\d{4}-\d{2}-\d{2}/.test(dlRaw)) ? dlRaw.slice(0, 10) : '';
         var first = lines[0];
@@ -2327,6 +2348,7 @@
         currentEditLines = [];
         currentEditDeliveryDeadline = '';
         currentEditBidDeadline = '';
+        currentEditBidOpen = false;
         if (editLinesBox) {
             editLinesBox.innerHTML = '';
         }