|
|
@@ -626,6 +626,26 @@ class Index extends Frontend
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 供应商整单备注(purchase_order_detail.remark)
|
|
|
+ *
|
|
|
+ * @param array<string, mixed> $row
|
|
|
+ */
|
|
|
+ protected function mprocResolveDetailRemark(array $row): string
|
|
|
+ {
|
|
|
+ $col = $this->mprocResolveProcuremenColumn(['remark', 'memo', 'bz', 'beizhu']);
|
|
|
+ if ($col === null) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ foreach ($row as $k => $v) {
|
|
|
+ if (strcasecmp((string)$k, $col) === 0) {
|
|
|
+ return trim((string)$v);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 列表:非管理员按 company_name 与登录时解析的单位名一致;管理员不加条件
|
|
|
*
|
|
|
@@ -1273,6 +1293,186 @@ class Index extends Frontend
|
|
|
if ($ceilRaw !== '') {
|
|
|
$row['ceilingPrice'] = $ceilRaw;
|
|
|
}
|
|
|
+ $sysRq = '';
|
|
|
+ foreach (['sys_rq', 'SYS_RQ'] as $sk) {
|
|
|
+ if (array_key_exists($sk, $pl) && $pl[$sk] !== null && $pl[$sk] !== '') {
|
|
|
+ $sysRq = trim((string)$pl[$sk]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (array_key_exists($sk, $poRow) && $poRow[$sk] !== null && $poRow[$sk] !== '') {
|
|
|
+ $sysRq = trim((string)$poRow[$sk]);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ($sysRq !== '' && !preg_match('/^0000-00-00/i', $sysRq)) {
|
|
|
+ $row['sys_rq'] = $sysRq;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 主表报价截止时间(未设置则视为未截止)
|
|
|
+ *
|
|
|
+ * @param array<string, mixed>|null $po
|
|
|
+ */
|
|
|
+ protected function mprocResolveSysRqFromPo(?array $po): string
|
|
|
+ {
|
|
|
+ if (!is_array($po)) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ $sr = trim((string)($po['sys_rq'] ?? $po['SYS_RQ'] ?? ''));
|
|
|
+
|
|
|
+ return ($sr !== '' && !preg_match('/^0000-00-00/i', $sr)) ? $sr : '';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手机端:仅当主表设置了 sys_rq 且已到期时视为截止(无截止时间仍可填报)
|
|
|
+ *
|
|
|
+ * @param array<string, mixed>|null $po
|
|
|
+ */
|
|
|
+ protected function mprocIsQuoteDeadlineReachedForPo(?array $po): bool
|
|
|
+ {
|
|
|
+ $raw = $this->mprocResolveSysRqFromPo($po);
|
|
|
+ if ($raw === '') {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $ts = strtotime(str_replace('T', ' ', $raw));
|
|
|
+ if ($ts === false || $ts <= 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return time() >= $ts;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 主单已完结后:中标 / 未中标
|
|
|
+ *
|
|
|
+ * @param array<string, mixed> $row
|
|
|
+ * @param array<string, mixed>|null $po
|
|
|
+ */
|
|
|
+ protected function mprocResolvePickResultText(array $row, ?array $po): string
|
|
|
+ {
|
|
|
+ if (!is_array($po) || !ProcuremenStatus::isPoCompleted($po['status'] ?? $po['STATUS'] ?? '')) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ $detailStatus = $row['status'] ?? $row['STATUS'] ?? '';
|
|
|
+
|
|
|
+ return ProcuremenStatus::isPodPicked($detailStatus) ? '中标' : '未中标';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 手机端左侧 Tab:draft|submitted|done
|
|
|
+ *
|
|
|
+ * @param array<string, mixed> $row
|
|
|
+ * @param array<string, mixed>|null $po
|
|
|
+ */
|
|
|
+ protected function mprocResolveListTabForRow(array $row, ?array $po, string $effectiveSn): string
|
|
|
+ {
|
|
|
+ if (in_array($effectiveSn, ['已完成', '未通过', '已废弃'], true)) {
|
|
|
+ return 'done';
|
|
|
+ }
|
|
|
+ if ($this->mprocIsQuoteDeadlineReachedForPo($po)) {
|
|
|
+ return 'done';
|
|
|
+ }
|
|
|
+ if ($effectiveSn === '已提交') {
|
|
|
+ return 'submitted';
|
|
|
+ }
|
|
|
+
|
|
|
+ return 'draft';
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 同一订单号 + 供应商合并为一张卡片
|
|
|
+ *
|
|
|
+ * @param array<int, array<string, mixed>> $rows
|
|
|
+ * @return array<int, array<string, mixed>>
|
|
|
+ */
|
|
|
+ protected function mprocGroupRowsByOrder(array $rows): array
|
|
|
+ {
|
|
|
+ $groups = [];
|
|
|
+ $order = [];
|
|
|
+ foreach ($rows as $row) {
|
|
|
+ if (!is_array($row)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $ccydh = trim((string)($row['CCYDH'] ?? ''));
|
|
|
+ $cname = trim((string)($row['company_name'] ?? ''));
|
|
|
+ $key = ($ccydh !== '' ? $ccydh : ('eid_' . (int)($row['eid'] ?? 0))) . '|' . $cname;
|
|
|
+ if (!isset($groups[$key])) {
|
|
|
+ $groups[$key] = [
|
|
|
+ 'group_key' => $key,
|
|
|
+ 'CCYDH' => $ccydh,
|
|
|
+ 'CYJMC' => trim((string)($row['CYJMC'] ?? '')),
|
|
|
+ 'company_name' => $cname,
|
|
|
+ 'lines' => [],
|
|
|
+ ];
|
|
|
+ $order[] = $key;
|
|
|
+ }
|
|
|
+ $groups[$key]['lines'][] = $row;
|
|
|
+ }
|
|
|
+ $out = [];
|
|
|
+ foreach ($order as $key) {
|
|
|
+ $g = $groups[$key];
|
|
|
+ $lines = is_array($g['lines'] ?? null) ? $g['lines'] : [];
|
|
|
+ usort($lines, function ($a, $b) {
|
|
|
+ $sa = (int)($a['scydgy_id'] ?? 0);
|
|
|
+ $sb = (int)($b['scydgy_id'] ?? 0);
|
|
|
+ if ($sa !== $sb) {
|
|
|
+ return $sa <=> $sb;
|
|
|
+ }
|
|
|
+
|
|
|
+ return ((int)($a['eid'] ?? 0)) <=> ((int)($b['eid'] ?? 0));
|
|
|
+ });
|
|
|
+ $g['lines'] = $lines;
|
|
|
+ $g['line_count'] = count($lines);
|
|
|
+ $canEdit = false;
|
|
|
+ foreach ($lines as $ln) {
|
|
|
+ if (is_array($ln) && (int)($ln['mproc_can_edit'] ?? 0) === 1) {
|
|
|
+ $canEdit = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $g['can_edit'] = $canEdit ? 1 : 0;
|
|
|
+ $remark = '';
|
|
|
+ $doneLabel = '';
|
|
|
+ $pickResult = '';
|
|
|
+ $hasWin = false;
|
|
|
+ $hasLose = false;
|
|
|
+ $hasExpired = false;
|
|
|
+ foreach ($lines as $ln) {
|
|
|
+ if (!is_array($ln)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $rm = trim((string)($ln['mproc_remark'] ?? ''));
|
|
|
+ if ($rm !== '' && $remark === '') {
|
|
|
+ $remark = $rm;
|
|
|
+ }
|
|
|
+ $pr = trim((string)($ln['mproc_pick_result'] ?? ''));
|
|
|
+ $dl = trim((string)($ln['mproc_done_label'] ?? ''));
|
|
|
+ if ($pr === '中标' || $dl === '中标') {
|
|
|
+ $hasWin = true;
|
|
|
+ } elseif ($pr === '未中标' || $dl === '未中标') {
|
|
|
+ $hasLose = true;
|
|
|
+ } elseif ($dl === '已截止') {
|
|
|
+ $hasExpired = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ($hasWin) {
|
|
|
+ $doneLabel = '中标';
|
|
|
+ $pickResult = '中标';
|
|
|
+ } elseif ($hasLose) {
|
|
|
+ $doneLabel = '未中标';
|
|
|
+ $pickResult = '未中标';
|
|
|
+ } elseif ($hasExpired) {
|
|
|
+ $doneLabel = '已截止';
|
|
|
+ $pickResult = '';
|
|
|
+ }
|
|
|
+ $g['remark'] = $remark;
|
|
|
+ $g['mproc_done_label'] = $doneLabel;
|
|
|
+ $g['mproc_pick_result'] = $pickResult;
|
|
|
+ $out[] = $g;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $out;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -1296,10 +1496,7 @@ class Index extends Frontend
|
|
|
$this->mprocSyncLegacyApprovedStatusNames($user, $statusNameCol);
|
|
|
}
|
|
|
$this->mprocApplySearchKeywordToDetailQuery($query, $q);
|
|
|
- // 有搜索词时不在此按 status_name 分栏筛选,全局匹配;无搜索词时仍按左侧 Tab(未提交/已提交/已完成)筛选
|
|
|
- if (trim((string)$q) === '') {
|
|
|
- $this->mprocApplyListTabConditions($query, $tab, $statusNameCol);
|
|
|
- }
|
|
|
+ // Tab 筛选在 PHP 层按截止时间、审批结果综合判断(含逾期进「已完成」、未中标等)
|
|
|
|
|
|
try {
|
|
|
$rows = $query->limit(500)->select();
|
|
|
@@ -1354,13 +1551,24 @@ class Index extends Frontend
|
|
|
if ($statusNameCol !== null && $effectiveSn !== $oldSn && $row['eid'] > 0) {
|
|
|
$this->mprocPersistDetailStatusName((int)$row['eid'], $statusNameCol, $effectiveSn);
|
|
|
}
|
|
|
+ $listTab = $this->mprocResolveListTabForRow($row, $poRow, $effectiveSn);
|
|
|
+ $row['mproc_list_tab'] = $listTab;
|
|
|
+ $row['mproc_deadline_reached'] = $this->mprocIsQuoteDeadlineReachedForPo($poRow) ? 1 : 0;
|
|
|
+ $row['mproc_pick_result'] = $this->mprocResolvePickResultText($row, $poRow);
|
|
|
+ if ($row['mproc_pick_result'] === '' && $listTab === 'done' && $row['mproc_deadline_reached']) {
|
|
|
+ $row['mproc_done_label'] = '已截止';
|
|
|
+ } elseif ($row['mproc_pick_result'] !== '') {
|
|
|
+ $row['mproc_done_label'] = $row['mproc_pick_result'];
|
|
|
+ } else {
|
|
|
+ $row['mproc_done_label'] = '';
|
|
|
+ }
|
|
|
// status_name 由库表/后端维护,不在此根据 amount 覆盖
|
|
|
if (!isset($row['status_name']) || $row['status_name'] === null) {
|
|
|
$row['status_name'] = '';
|
|
|
} else {
|
|
|
$row['status_name'] = trim((string)$row['status_name']);
|
|
|
}
|
|
|
- $row['mproc_can_edit'] = $this->mprocCanEditRow($user, $row) ? 1 : 0;
|
|
|
+ $row['mproc_can_edit'] = $this->mprocCanEditRow($user, $row, $poRow) ? 1 : 0;
|
|
|
|
|
|
$am = $row['amount'] ?? null;
|
|
|
if ($am === null || $am === '' || (is_string($am) && trim($am) === '')) {
|
|
|
@@ -1380,23 +1588,21 @@ class Index extends Frontend
|
|
|
$row['delivery_missing'] = ($dv === '' || preg_match('/^0000-00-00/i', $dv)) ? 1 : 0;
|
|
|
$row['mproc_fill_hint'] = '';
|
|
|
$row['mproc_this_quantity_display'] = $this->mprocResolveDisplayThisQuantity($row);
|
|
|
+ $row['mproc_remark'] = $this->mprocResolveDetailRemark($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;
|
|
|
+ if (trim((string)$q) === '') {
|
|
|
+ $rows = array_values(array_filter($rows, function ($r) use ($tab) {
|
|
|
+ return is_array($r) && trim((string)($r['mproc_list_tab'] ?? '')) === $tab;
|
|
|
}));
|
|
|
}
|
|
|
|
|
|
+ $groups = $this->mprocGroupRowsByOrder($rows);
|
|
|
+
|
|
|
return [
|
|
|
'rows' => $rows ?: [],
|
|
|
+ 'groups' => $groups ?: [],
|
|
|
'done_no_status' => (int)($statusNameCol === null),
|
|
|
];
|
|
|
}
|
|
|
@@ -1406,7 +1612,7 @@ class Index extends Frontend
|
|
|
*/
|
|
|
protected function mprocStatusNameToListTab(string $statusName): string
|
|
|
{
|
|
|
- $map = ['未提交' => 'draft', '已提交' => 'submitted', '已完成' => 'done'];
|
|
|
+ $map = ['未提交' => 'draft', '已提交' => 'submitted', '已完成' => 'done', '未通过' => 'done', '已废弃' => 'done'];
|
|
|
$sn = trim($statusName);
|
|
|
|
|
|
return isset($map[$sn]) ? $map[$sn] : '';
|
|
|
@@ -1447,7 +1653,9 @@ class Index extends Frontend
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- return $this->mprocStatusNameToListTab(
|
|
|
+ return $this->mprocResolveListTabForRow(
|
|
|
+ $dr,
|
|
|
+ is_array($po) ? $po : null,
|
|
|
$this->mprocResolveEffectiveStatusName($dr, is_array($po) ? $po : null)
|
|
|
);
|
|
|
}
|
|
|
@@ -1486,6 +1694,7 @@ class Index extends Frontend
|
|
|
array_splice($rows, $foundIdx, 1);
|
|
|
array_unshift($rows, $hit);
|
|
|
$bundle['rows'] = $rows;
|
|
|
+ $bundle['groups'] = $this->mprocGroupRowsByOrder($rows);
|
|
|
|
|
|
return $bundle;
|
|
|
}
|
|
|
@@ -1525,13 +1734,24 @@ class Index extends Frontend
|
|
|
}
|
|
|
$effectiveSn = $this->mprocResolveEffectiveStatusName($row, is_array($poRow) ? $poRow : null);
|
|
|
if (trim((string)$q) === '') {
|
|
|
- $rowTab = $this->mprocStatusNameToListTab($effectiveSn);
|
|
|
+ $rowTab = $this->mprocResolveListTabForRow($row, is_array($poRow) ? $poRow : null, $effectiveSn);
|
|
|
if ($rowTab !== '' && $rowTab !== $tab) {
|
|
|
return $bundle;
|
|
|
}
|
|
|
}
|
|
|
$row['status_name'] = $effectiveSn;
|
|
|
- $row['mproc_can_edit'] = $this->mprocCanEditRow($user, $row) ? 1 : 0;
|
|
|
+ $listTab = $this->mprocResolveListTabForRow($row, is_array($poRow) ? $poRow : null, $effectiveSn);
|
|
|
+ $row['mproc_list_tab'] = $listTab;
|
|
|
+ $row['mproc_deadline_reached'] = $this->mprocIsQuoteDeadlineReachedForPo($poRow) ? 1 : 0;
|
|
|
+ $row['mproc_pick_result'] = $this->mprocResolvePickResultText($row, $poRow);
|
|
|
+ if ($row['mproc_pick_result'] === '' && $listTab === 'done' && $row['mproc_deadline_reached']) {
|
|
|
+ $row['mproc_done_label'] = '已截止';
|
|
|
+ } elseif ($row['mproc_pick_result'] !== '') {
|
|
|
+ $row['mproc_done_label'] = $row['mproc_pick_result'];
|
|
|
+ } else {
|
|
|
+ $row['mproc_done_label'] = '';
|
|
|
+ }
|
|
|
+ $row['mproc_can_edit'] = $this->mprocCanEditRow($user, $row, $poRow) ? 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']) : '';
|
|
|
@@ -1548,6 +1768,7 @@ class Index extends Frontend
|
|
|
$row['mproc_this_quantity_display'] = $this->mprocResolveDisplayThisQuantity($row);
|
|
|
array_unshift($rows, $row);
|
|
|
$bundle['rows'] = $rows;
|
|
|
+ $bundle['groups'] = $this->mprocGroupRowsByOrder($rows);
|
|
|
|
|
|
return $bundle;
|
|
|
}
|
|
|
@@ -1806,6 +2027,7 @@ class Index extends Frontend
|
|
|
$bundle = $this->mprocEnsureFocusRowInList($bundle, $mprocFocusEid, $user, $statusNameCol, $tab, $q);
|
|
|
}
|
|
|
$this->view->assign('rows', $bundle['rows']);
|
|
|
+ $this->view->assign('groups', $bundle['groups'] ?? $this->mprocGroupRowsByOrder($bundle['rows']));
|
|
|
|
|
|
return $this->view->fetch();
|
|
|
}
|
|
|
@@ -2090,7 +2312,7 @@ class Index extends Frontend
|
|
|
* 是否允许当前登录用户修改该条 purchase_order_detail 的金额、交期
|
|
|
* 仅普通用户(customer)可改;管理员(admin)仅可查看
|
|
|
*/
|
|
|
- protected function mprocCanEditRow(array $user, array $row)
|
|
|
+ protected function mprocCanEditRow(array $user, array $row, ?array $po = null)
|
|
|
{
|
|
|
if (!empty($user['is_admin'])) {
|
|
|
return false;
|
|
|
@@ -2099,6 +2321,9 @@ class Index extends Frontend
|
|
|
if (in_array($sn, ['已完成', '未通过', '已废弃'], true)) {
|
|
|
return false;
|
|
|
}
|
|
|
+ if ($this->mprocIsQuoteDeadlineReachedForPo($po)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
$uCo = trim((string)($user['company_name'] ?? ''));
|
|
|
if ($uCo === '') {
|
|
|
$uPhone = trim((string)($user['phone'] ?? ''));
|
|
|
@@ -2158,20 +2383,18 @@ class Index extends Frontend
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 保存单条协助明细的金额、交期(POST:id、amount、delivery)
|
|
|
+ * 保存单条协助明细的金额、交期(内部)
|
|
|
+ *
|
|
|
+ * @param array<string, mixed> $user
|
|
|
+ * @param int $id
|
|
|
+ * @param string $amountRaw
|
|
|
+ * @param string $deliveryRaw
|
|
|
+ * @return string 工序名(用于批量错误提示)
|
|
|
*/
|
|
|
- public function mprocSave()
|
|
|
+ protected function mprocSaveDetailQuote(array $user, int $id, string $amountRaw, string $deliveryRaw): string
|
|
|
{
|
|
|
- if (!$this->request->isPost()) {
|
|
|
- $this->error('请使用 POST');
|
|
|
- }
|
|
|
- $user = $this->mprocGetUser();
|
|
|
- if (!$user) {
|
|
|
- $this->error('请先登录', url('index/index/login'));
|
|
|
- }
|
|
|
- $id = (int)$this->request->post('id', 0);
|
|
|
if ($id <= 0) {
|
|
|
- $this->error('参数错误');
|
|
|
+ throw new \InvalidArgumentException('参数错误');
|
|
|
}
|
|
|
$row = null;
|
|
|
try {
|
|
|
@@ -2183,8 +2406,9 @@ class Index extends Frontend
|
|
|
$row = null;
|
|
|
}
|
|
|
if (!$row || !is_array($row)) {
|
|
|
- $this->error('记录不存在');
|
|
|
+ throw new \InvalidArgumentException('记录不存在');
|
|
|
}
|
|
|
+ $gymc = trim((string)($row['CGYMC'] ?? $row['cgymc'] ?? ''));
|
|
|
$sid = (int)($row['scydgy_id'] ?? $row['SCYDGY_ID'] ?? 0);
|
|
|
$po = null;
|
|
|
if ($this->mprocIsValidScydgyRowId($sid)) {
|
|
|
@@ -2193,38 +2417,48 @@ class Index extends Frontend
|
|
|
} catch (\Throwable $e) {
|
|
|
$po = null;
|
|
|
}
|
|
|
+ if (is_array($po)) {
|
|
|
+ $this->mprocMergePurchaseOrderIntoDetail($row, $po);
|
|
|
+ if ($gymc === '') {
|
|
|
+ $gymc = trim((string)($row['CGYMC'] ?? ''));
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ $label = $gymc !== '' ? ('工序「' . $gymc . '」') : ('记录#' . $id);
|
|
|
$effectiveSn = $this->mprocResolveEffectiveStatusName($row, is_array($po) ? $po : null);
|
|
|
if (in_array($effectiveSn, ['已完成', '未通过', '已废弃'], true)) {
|
|
|
- $this->error('订单已结束,不能再修改单价与交货日期');
|
|
|
+ throw new \InvalidArgumentException($label . '已结束,不能再修改');
|
|
|
}
|
|
|
- if (!$this->mprocCanEditRow($user, array_merge($row, ['status_name' => $effectiveSn]))) {
|
|
|
+ if ($this->mprocIsQuoteDeadlineReachedForPo(is_array($po) ? $po : null)) {
|
|
|
+ throw new \InvalidArgumentException($label . '报价已截止,不能再修改');
|
|
|
+ }
|
|
|
+ if (!$this->mprocCanEditRow($user, array_merge($row, ['status_name' => $effectiveSn]), is_array($po) ? $po : null)) {
|
|
|
if (!empty($user['is_admin'])) {
|
|
|
- $this->error('当前账号仅可查看,不能修改单价与交货日期');
|
|
|
+ throw new \InvalidArgumentException('当前账号仅可查看,不能修改单价与交货日期');
|
|
|
}
|
|
|
- $this->error('无权修改该记录');
|
|
|
+ throw new \InvalidArgumentException($label . '无权修改');
|
|
|
}
|
|
|
|
|
|
- $amountRaw = trim((string)$this->request->post('amount', ''));
|
|
|
- $deliveryRaw = trim((string)$this->request->post('delivery', ''));
|
|
|
-
|
|
|
+ $amountRaw = trim($amountRaw);
|
|
|
+ $deliveryRaw = trim($deliveryRaw);
|
|
|
$data = [];
|
|
|
if ($amountRaw === '') {
|
|
|
$data['amount'] = null;
|
|
|
} else {
|
|
|
if (!preg_match('/^-?\d+(\.\d{1,5})?$/', $amountRaw)) {
|
|
|
- $this->error('单价格式不正确,最多五位小数');
|
|
|
+ throw new \InvalidArgumentException($label . '单价格式不正确,最多五位小数');
|
|
|
}
|
|
|
$ceilingLimit = $this->mprocResolveCeilingPriceForDetailRow($row);
|
|
|
if ($ceilingLimit !== null && (float)$amountRaw > $ceilingLimit) {
|
|
|
- $this->error('单价不能超过最高限价 ' . $this->mprocFormatCeilingPriceDisplay($ceilingLimit));
|
|
|
+ throw new \InvalidArgumentException(
|
|
|
+ $label . '单价不能超过最高限价 ' . $this->mprocFormatCeilingPriceDisplay($ceilingLimit)
|
|
|
+ );
|
|
|
}
|
|
|
$data['amount'] = $amountRaw;
|
|
|
}
|
|
|
if ($deliveryRaw === '') {
|
|
|
$data['delivery'] = null;
|
|
|
} elseif (preg_match('/^\d{4}-\d{2}-\d{2}$/', $deliveryRaw)) {
|
|
|
- // 仅选年月日:存 DATETIME,禁止写成 00:00:00——原记录有非零点时间则沿用,否则用当前服务器时分秒
|
|
|
$existingDel = isset($row['delivery']) ? trim((string)$row['delivery']) : '';
|
|
|
$timePart = date('H:i:s');
|
|
|
if ($existingDel !== '') {
|
|
|
@@ -2241,7 +2475,7 @@ class Index extends Frontend
|
|
|
$deliveryRaw = str_replace('T', ' ', $deliveryRaw);
|
|
|
$ts = strtotime($deliveryRaw);
|
|
|
if ($ts === false) {
|
|
|
- $this->error('交期时间格式不正确');
|
|
|
+ throw new \InvalidArgumentException($label . '交期时间格式不正确');
|
|
|
}
|
|
|
$data['delivery'] = date('Y-m-d H:i:s', $ts);
|
|
|
}
|
|
|
@@ -2254,7 +2488,6 @@ class Index extends Frontend
|
|
|
$data[$upCol] = date('Y-m-d H:i:s');
|
|
|
}
|
|
|
|
|
|
- // 同步 status_name(与列表 Tab 一致):金额或交期任一有有效数据 → 已提交,否则未提交;已是「已完成」不覆盖
|
|
|
$statusNameCol = $this->mprocResolveProcuremenColumn(['status_name', 'status_txt', 'status_text']);
|
|
|
if ($statusNameCol !== null) {
|
|
|
$curSn = '';
|
|
|
@@ -2282,12 +2515,210 @@ class Index extends Frontend
|
|
|
if (stripos($msg, 'Unknown column') !== false) {
|
|
|
$msg = '请确认数据表 purchase_order_detail 已包含 amount、delivery 字段';
|
|
|
}
|
|
|
- $this->error('保存失败:' . $msg);
|
|
|
+ throw new \RuntimeException('保存失败:' . $msg);
|
|
|
}
|
|
|
if ($aff === false) {
|
|
|
- $this->error('保存失败');
|
|
|
+ throw new \RuntimeException($label . '保存失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ return $gymc;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存同订单号+供应商下的整单备注(写入该组全部明细行)
|
|
|
+ *
|
|
|
+ * @param array<string, mixed> $user
|
|
|
+ * @param int[] $detailIds 本次保存涉及的明细 ID
|
|
|
+ * @param string $remarkRaw
|
|
|
+ */
|
|
|
+ protected function mprocSaveOrderGroupRemark(array $user, array $detailIds, string $remarkRaw): void
|
|
|
+ {
|
|
|
+ $remarkCol = $this->mprocResolveProcuremenColumn(['remark', 'memo', 'bz', 'beizhu']);
|
|
|
+ if ($remarkCol === null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ $detailIds = array_values(array_unique(array_filter(array_map('intval', $detailIds))));
|
|
|
+ if ($detailIds === []) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ $anchorId = $detailIds[0];
|
|
|
+ $row = null;
|
|
|
+ try {
|
|
|
+ $row = Db::table('purchase_order_detail')->where('id', $anchorId)->find();
|
|
|
+ if (!$row) {
|
|
|
+ $row = Db::table('purchase_order_detail')->where('ID', $anchorId)->find();
|
|
|
+ }
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ $row = null;
|
|
|
+ }
|
|
|
+ if (!$row || !is_array($row)) {
|
|
|
+ throw new \InvalidArgumentException('记录不存在');
|
|
|
+ }
|
|
|
+ $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;
|
|
|
+ }
|
|
|
+ if (is_array($po)) {
|
|
|
+ $this->mprocMergePurchaseOrderIntoDetail($row, $po);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $effectiveSn = $this->mprocResolveEffectiveStatusName($row, is_array($po) ? $po : null);
|
|
|
+ if (in_array($effectiveSn, ['已完成', '未通过', '已废弃'], true)) {
|
|
|
+ throw new \InvalidArgumentException('订单已结束,不能再修改备注');
|
|
|
+ }
|
|
|
+ if ($this->mprocIsQuoteDeadlineReachedForPo(is_array($po) ? $po : null)) {
|
|
|
+ throw new \InvalidArgumentException('报价已截止,不能再修改备注');
|
|
|
+ }
|
|
|
+ if (!$this->mprocCanEditRow($user, array_merge($row, ['status_name' => $effectiveSn]), is_array($po) ? $po : null)) {
|
|
|
+ if (!empty($user['is_admin'])) {
|
|
|
+ throw new \InvalidArgumentException('当前账号仅可查看,不能修改备注');
|
|
|
+ }
|
|
|
+ throw new \InvalidArgumentException('无权修改备注');
|
|
|
+ }
|
|
|
+
|
|
|
+ $ccydhCol = $this->mprocResolveProcuremenColumn(['ccydh']);
|
|
|
+ $companyCol = $this->mprocResolveProcuremenColumn(['company_name']);
|
|
|
+ if ($ccydhCol === null || $companyCol === null) {
|
|
|
+ return;
|
|
|
}
|
|
|
- $this->success('已保存');
|
|
|
+ $ccydh = '';
|
|
|
+ $company = '';
|
|
|
+ foreach ($row as $k => $v) {
|
|
|
+ if (strcasecmp((string)$k, $ccydhCol) === 0) {
|
|
|
+ $ccydh = trim((string)$v);
|
|
|
+ } elseif (strcasecmp((string)$k, $companyCol) === 0) {
|
|
|
+ $company = trim((string)$v);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ($ccydh === '' || $company === '') {
|
|
|
+ $pkField = isset($row['id']) ? 'id' : (isset($row['ID']) ? 'ID' : 'id');
|
|
|
+ $data = [$remarkCol => ($remarkRaw === '' ? null : mb_substr($remarkRaw, 0, 500, 'UTF-8'))];
|
|
|
+ $upCol = $this->mprocResolveProcuremenColumn(['updatetime']);
|
|
|
+ if ($upCol !== null) {
|
|
|
+ $data[$upCol] = date('Y-m-d H:i:s');
|
|
|
+ }
|
|
|
+ Db::table('purchase_order_detail')->where($pkField, (int)($row[$pkField] ?? $anchorId))->update($data);
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ $remarkVal = $remarkRaw === '' ? null : mb_substr($remarkRaw, 0, 500, 'UTF-8');
|
|
|
+ $data = [$remarkCol => $remarkVal];
|
|
|
+ $upCol = $this->mprocResolveProcuremenColumn(['updatetime']);
|
|
|
+ if ($upCol !== null) {
|
|
|
+ $data[$upCol] = date('Y-m-d H:i:s');
|
|
|
+ }
|
|
|
+ $query = Db::table('purchase_order_detail')
|
|
|
+ ->where($ccydhCol, $ccydh)
|
|
|
+ ->where($companyCol, $company);
|
|
|
+ $userWhere = $this->mprocListWhereForLoginUser($user);
|
|
|
+ if ($userWhere !== []) {
|
|
|
+ $query->where($userWhere);
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $query->update($data);
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ throw new \RuntimeException('备注保存失败:' . $e->getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存协助明细金额、交期
|
|
|
+ * 单条:POST id、amount、delivery
|
|
|
+ * 批量:POST items=[{id,amount,delivery},...] JSON
|
|
|
+ */
|
|
|
+ public function mprocSave()
|
|
|
+ {
|
|
|
+ if (!$this->request->isPost()) {
|
|
|
+ $this->error('请使用 POST');
|
|
|
+ }
|
|
|
+ $user = $this->mprocGetUser();
|
|
|
+ if (!$user) {
|
|
|
+ $this->error('请先登录', url('index/index/login'));
|
|
|
+ }
|
|
|
+
|
|
|
+ // Frontend 默认 filter 含 htmlspecialchars,会把 JSON 的双引号变成 " 导致解析失败
|
|
|
+ $itemsRaw = $this->request->post('items', '', null);
|
|
|
+ if ($itemsRaw === '' || $itemsRaw === null) {
|
|
|
+ $itemsRaw = isset($_POST['items']) ? $_POST['items'] : '';
|
|
|
+ }
|
|
|
+ if (is_string($itemsRaw) && $itemsRaw !== '' && strpos($itemsRaw, '"') !== false) {
|
|
|
+ $itemsRaw = htmlspecialchars_decode($itemsRaw, ENT_QUOTES);
|
|
|
+ }
|
|
|
+ $items = [];
|
|
|
+ if (is_string($itemsRaw) && trim($itemsRaw) !== '') {
|
|
|
+ $decoded = json_decode($itemsRaw, true);
|
|
|
+ if (!is_array($decoded)) {
|
|
|
+ $decoded = json_decode(htmlspecialchars_decode($itemsRaw, ENT_QUOTES), true);
|
|
|
+ }
|
|
|
+ if (is_array($decoded)) {
|
|
|
+ $items = $decoded;
|
|
|
+ }
|
|
|
+ } elseif (is_array($itemsRaw)) {
|
|
|
+ $items = $itemsRaw;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($items === []) {
|
|
|
+ $id = (int)$this->request->post('id', 0, null);
|
|
|
+ if ($id <= 0) {
|
|
|
+ $this->error('保存失败,请关闭弹窗后重试');
|
|
|
+ }
|
|
|
+ $items = [[
|
|
|
+ 'id' => $id,
|
|
|
+ 'amount' => (string)$this->request->post('amount', '', null),
|
|
|
+ 'delivery' => (string)$this->request->post('delivery', '', null),
|
|
|
+ ]];
|
|
|
+ }
|
|
|
+
|
|
|
+ $saved = 0;
|
|
|
+ $savedIds = [];
|
|
|
+ $remarkRaw = $this->request->post('remark', '', null);
|
|
|
+ if ($remarkRaw === '' || $remarkRaw === null) {
|
|
|
+ $remarkRaw = isset($_POST['remark']) ? $_POST['remark'] : '';
|
|
|
+ }
|
|
|
+ $remarkRaw = trim(htmlspecialchars_decode((string)$remarkRaw, ENT_QUOTES));
|
|
|
+ Db::startTrans();
|
|
|
+ try {
|
|
|
+ foreach ($items as $it) {
|
|
|
+ if (!is_array($it)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $id = (int)($it['id'] ?? $it['eid'] ?? 0);
|
|
|
+ if ($id <= 0) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $delivery = (string)($it['delivery'] ?? '');
|
|
|
+ // 兼容部分手机浏览器把日期显示/提交成 2026/07/24
|
|
|
+ if (preg_match('/^(\d{4})[\/.\-](\d{1,2})[\/.\-](\d{1,2})$/', trim($delivery), $dm)) {
|
|
|
+ $delivery = sprintf('%04d-%02d-%02d', (int)$dm[1], (int)$dm[2], (int)$dm[3]);
|
|
|
+ }
|
|
|
+ $this->mprocSaveDetailQuote(
|
|
|
+ $user,
|
|
|
+ $id,
|
|
|
+ (string)($it['amount'] ?? ''),
|
|
|
+ $delivery
|
|
|
+ );
|
|
|
+ $saved++;
|
|
|
+ $savedIds[] = $id;
|
|
|
+ }
|
|
|
+ if ($saved < 1) {
|
|
|
+ throw new \InvalidArgumentException('没有可保存的工序,请刷新后重试');
|
|
|
+ }
|
|
|
+ $this->mprocSaveOrderGroupRemark($user, $savedIds, $remarkRaw);
|
|
|
+ Db::commit();
|
|
|
+ } catch (\InvalidArgumentException $e) {
|
|
|
+ Db::rollback();
|
|
|
+ $this->error($e->getMessage());
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ Db::rollback();
|
|
|
+ $this->error($e->getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ $this->success($saved > 1 ? ('已保存 ' . $saved . ' 道工序') : '已保存');
|
|
|
}
|
|
|
|
|
|
/**
|