|
|
@@ -4759,7 +4759,8 @@ class Procuremen extends Backend
|
|
|
];
|
|
|
$qtySnap = trim((string)($data['This_quantity'] ?? ''));
|
|
|
$priceSnap = trim((string)($data['ceilingPrice'] ?? ''));
|
|
|
- if (!$exists && $qtySnap === '' && $priceSnap === '') {
|
|
|
+ $mbzSnap = trim((string)($data['MBZ'] ?? ''));
|
|
|
+ if (!$exists && $qtySnap === '' && $priceSnap === '' && $mbzSnap === '') {
|
|
|
$this->success('操作成功');
|
|
|
|
|
|
return;
|
|
|
@@ -4783,7 +4784,7 @@ class Procuremen extends Backend
|
|
|
|
|
|
/**
|
|
|
* 未发列表
|
|
|
- * 「完结」或「仅保存本次数量/最高限价」:有则改、无则插
|
|
|
+ * 「完结」或「仅保存本次数量/最高限价/备注」:有则改、无则插
|
|
|
* POST finish=1(默认):并置 status=1;finish=0:更新时不改 status;新增不写 status。
|
|
|
*/
|
|
|
public function completeDirectly()
|
|
|
@@ -4845,7 +4846,8 @@ class Procuremen extends Backend
|
|
|
|
|
|
$qtyCd = trim((string)($data['This_quantity'] ?? ''));
|
|
|
$priceCd = trim((string)($data['ceilingPrice'] ?? ''));
|
|
|
- if (!$asComplete && !$exists && $qtyCd === '' && $priceCd === '') {
|
|
|
+ $mbzCd = trim((string)($data['MBZ'] ?? ''));
|
|
|
+ if (!$asComplete && !$exists && $qtyCd === '' && $priceCd === '' && $mbzCd === '') {
|
|
|
$this->success('操作成功');
|
|
|
|
|
|
return;
|
|
|
@@ -4894,13 +4896,15 @@ class Procuremen extends Backend
|
|
|
}
|
|
|
} else {
|
|
|
$gymc = trim((string)($data['CGYMC'] ?? $row['CGYMC'] ?? ''));
|
|
|
+ $mbz = isset($data['MBZ']) ? trim((string)$data['MBZ']) : '';
|
|
|
$qtyPart = '本次数量「' . ($q !== '' ? $q : '') . '」';
|
|
|
$pricePart = '最高限价「' . ($p !== '' ? $p : '') . '」';
|
|
|
- $detail = ($gymc !== '' ? ($gymc . $qtyPart) : $qtyPart) . ',' . $pricePart;
|
|
|
+ $mbzPart = '备注「' . ($mbz !== '' ? $mbz : '') . '」';
|
|
|
+ $detail = ($gymc !== '' ? ($gymc . $qtyPart) : $qtyPart) . ',' . $pricePart . ',' . $mbzPart;
|
|
|
$this->addOrderLog(
|
|
|
$ids,
|
|
|
'save_qty_price',
|
|
|
- '保存本次数量、最高限价:' . $detail,
|
|
|
+ '保存本次数量、最高限价、备注:' . $detail,
|
|
|
$poIdLog
|
|
|
);
|
|
|
}
|
|
|
@@ -4909,7 +4913,7 @@ class Procuremen extends Backend
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 未发列表:从 purchase_order 合并已填的本次数量、最高限价(若表中有对应列)
|
|
|
+ * 未发列表:从 purchase_order 合并已填的本次数量、最高限价、备注(若表中有对应列)
|
|
|
*
|
|
|
* @param array<int, array> $rows 引用传递当前页行
|
|
|
*/
|
|
|
@@ -4936,7 +4940,7 @@ class Procuremen extends Backend
|
|
|
try {
|
|
|
$list = Db::table('purchase_order')
|
|
|
->where('scydgy_id', 'in', $idList)
|
|
|
- ->field('scydgy_id,This_quantity,ceilingPrice')
|
|
|
+ ->field('scydgy_id,This_quantity,ceilingPrice,MBZ')
|
|
|
->select();
|
|
|
} catch (\Throwable $e) {
|
|
|
return;
|
|
|
@@ -4960,7 +4964,7 @@ class Procuremen extends Backend
|
|
|
continue;
|
|
|
}
|
|
|
$db = $byId[$sid];
|
|
|
- // 有 purchase_order 记录时同步数量/限价(含清空后的空串,避免刷新后又冒出来)
|
|
|
+ // 有 purchase_order 记录时同步数量/限价/备注(含清空后的空串,避免刷新后又冒出来)
|
|
|
if (array_key_exists('This_quantity', $db)) {
|
|
|
$rw['This_quantity'] = $db['This_quantity'] === null ? '' : $db['This_quantity'];
|
|
|
}
|
|
|
@@ -4969,6 +4973,9 @@ class Procuremen extends Backend
|
|
|
} elseif (array_key_exists('ceiling_price', $db)) {
|
|
|
$rw['ceilingPrice'] = $db['ceiling_price'] === null ? '' : $db['ceiling_price'];
|
|
|
}
|
|
|
+ if (array_key_exists('MBZ', $db)) {
|
|
|
+ $rw['MBZ'] = $db['MBZ'] === null ? '' : $db['MBZ'];
|
|
|
+ }
|
|
|
}
|
|
|
unset($rw);
|
|
|
}
|
|
|
@@ -5540,6 +5547,7 @@ class Procuremen extends Backend
|
|
|
|
|
|
/**
|
|
|
* 获取当前登录用户信息 [id, 展示名]
|
|
|
+ * admin_name 一律记「名称」(昵称),无昵称再回退账号
|
|
|
*/
|
|
|
protected function GetUseName(): array
|
|
|
{
|
|
|
@@ -5551,8 +5559,24 @@ class Procuremen extends Backend
|
|
|
if (is_array($u)) {
|
|
|
$id = (int)($u['id'] ?? 0);
|
|
|
$name = trim((string)($u['nickname'] ?? ''));
|
|
|
+ // 会话昵称缺失或等于登录账号时,再查库取正式昵称
|
|
|
+ $sessionUser = trim((string)($u['username'] ?? ''));
|
|
|
+ if ($id > 0 && ($name === '' || ($sessionUser !== '' && strcasecmp($name, $sessionUser) === 0))) {
|
|
|
+ try {
|
|
|
+ $row = Db::name('admin')->where('id', $id)->field('nickname,username')->find();
|
|
|
+ if (is_array($row)) {
|
|
|
+ $nick = trim((string)($row['nickname'] ?? ''));
|
|
|
+ if ($nick !== '') {
|
|
|
+ $name = $nick;
|
|
|
+ } elseif ($name === '') {
|
|
|
+ $name = trim((string)($row['username'] ?? ''));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ }
|
|
|
+ }
|
|
|
if ($name === '') {
|
|
|
- $name = trim((string)($u['username'] ?? ''));
|
|
|
+ $name = $sessionUser;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -6392,6 +6416,7 @@ class Procuremen extends Backend
|
|
|
return [
|
|
|
'ok' => $main !== [] || $details !== [],
|
|
|
'ccydh' => $ccydh,
|
|
|
+ 'steps' => $bundle['steps'],
|
|
|
];
|
|
|
}
|
|
|
|
|
|
@@ -6409,6 +6434,20 @@ class Procuremen extends Backend
|
|
|
$this->error(__('Invalid parameters'));
|
|
|
}
|
|
|
$this->assertManualOrderDetailsViewable((int)$ids);
|
|
|
+
|
|
|
+ // 已完结:打开详情时按最新进度/操作记录重存存证 PDF(含质量/交货评分)
|
|
|
+ $sid = (int)$ids;
|
|
|
+ if ($this->isValidScydgyRowId($sid)) {
|
|
|
+ try {
|
|
|
+ $poCheck = Db::table('purchase_order')->where('scydgy_id', $sid)->find();
|
|
|
+ if (is_array($poCheck) && ProcuremenStatus::isPoCompleted($poCheck['status'] ?? '')) {
|
|
|
+ $this->refreshCompletedOrderArchivePdf($sid, true);
|
|
|
+ }
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ Log::write('详情页刷新完结存证PDF失败 scydgy_id=' . $sid . ' ' . $e->getMessage(), 'notice');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
$this->prepareProcuremenDetailsView($ids);
|
|
|
|
|
|
/* 弹层内不套 default 布局,避免出现「控制台 / Control panel」整块标题区 */
|
|
|
@@ -6423,6 +6462,74 @@ class Procuremen extends Backend
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 已完结订单重生成存证 PDF(与详情页一致,含质量/交货评分与「已完结」节点)
|
|
|
+ *
|
|
|
+ * @param bool $requireCompleted true 时仅已完结才重生成;false 强制按当前详情重存
|
|
|
+ * @return string 本地 web 路径或 OSS URL,失败为空串
|
|
|
+ */
|
|
|
+ protected function refreshCompletedOrderArchivePdf(int $scydgyId, bool $requireCompleted = true): string
|
|
|
+ {
|
|
|
+ if (!$this->isValidScydgyRowId($scydgyId)) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $po = Db::table('purchase_order')->where('scydgy_id', $scydgyId)->find();
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ $po = null;
|
|
|
+ }
|
|
|
+ if (!is_array($po) || $po === []) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ if ($requireCompleted && !ProcuremenStatus::isPoCompleted($po['status'] ?? '')) {
|
|
|
+ return trim((string)($po['pdf_url'] ?? ''));
|
|
|
+ }
|
|
|
+ $poId = (int)($po['id'] ?? 0);
|
|
|
+
|
|
|
+ return $this->savePurchaseConfirmDetailPdf($scydgyId, $poId);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * CLI / 手机端评分完结后调用:无后台登录态重生成存证 PDF
|
|
|
+ */
|
|
|
+ public static function regenerateArchivePdfWithoutAuth(int $scydgyId, bool $requireCompleted = true): string
|
|
|
+ {
|
|
|
+ if ((int)$scydgyId === 0) {
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $ctl = new class extends Procuremen {
|
|
|
+ public function _initialize()
|
|
|
+ {
|
|
|
+ $this->request = \think\Request::instance();
|
|
|
+ $tpl = \think\Config::get('template');
|
|
|
+ if (!is_array($tpl)) {
|
|
|
+ $tpl = [];
|
|
|
+ }
|
|
|
+ // CLI / 跨模块调用时无 admin 视图目录
|
|
|
+ $tpl['view_path'] = APP_PATH . 'admin' . DIRECTORY_SEPARATOR . 'view' . DIRECTORY_SEPARATOR;
|
|
|
+ $this->view = new \think\View($tpl, \think\Config::get('view_replace_str'));
|
|
|
+ $this->model = new \app\admin\model\Procuremen();
|
|
|
+ try {
|
|
|
+ ProcuremenSchema::ensureAll();
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function runRegen(int $sid, bool $requireCompleted): string
|
|
|
+ {
|
|
|
+ return $this->refreshCompletedOrderArchivePdf($sid, $requireCompleted);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ return $ctl->runRegen($scydgyId, $requireCompleted);
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ Log::write('regenerateArchivePdfWithoutAuth 失败 scydgy_id=' . $scydgyId . ' ' . $e->getMessage(), 'error');
|
|
|
+
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 解析合并审核工序行(订单号须一致,且均未协助)
|
|
|
*
|
|
|
@@ -13409,9 +13516,10 @@ class Procuremen extends Backend
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 采购确认成功后:用与「详情」弹窗相同的模板片段渲染 HTML,再存为 PDF(改 details_fragment 后 PDF 同步变化)。
|
|
|
+ * 采购确认成功后 / 已完结刷新:用与「详情」弹窗相同的模板片段渲染 HTML,再存为 PDF(改 details_fragment 后 PDF 同步变化)。
|
|
|
* 优先上传至阿里云 OSS(application/config.php 的 oss 节点);失败或未配置时回退到 public 下与 objectKey 相同目录结构。
|
|
|
* 成功后将相对路径写入 purchase_order.pdf_url(形如 /xinhua/年/月/日/scydgy_id/订单号_scydgy_id.pdf)。
|
|
|
+ * 说明:审批通过时会先存一版;质量+交货评分使订单「已完结」后,详情页打开或评分保存完成时会再重存,以含最终步骤与操作记录。
|
|
|
*
|
|
|
* @return string OSS 返回 https 完整 URL;本地回退为以 / 开头的 Web 路径;失败返回空串
|
|
|
*/
|
|
|
@@ -13431,10 +13539,56 @@ class Procuremen extends Backend
|
|
|
$objectKey = $paths['objectKey'];
|
|
|
$webPath = $paths['webPath'];
|
|
|
|
|
|
- $meta = sprintf('工序行ID %s | 主表订单ID %d | PDF生成时间 %s', $ids, (int)$purchaseOrderId, date('Y-m-d H:i:s'));
|
|
|
+ $cyjmc = '';
|
|
|
+ try {
|
|
|
+ $poMeta = Db::table('purchase_order')->where('scydgy_id', $scydgyId)->field('CYJMC')->find();
|
|
|
+ if (is_array($poMeta)) {
|
|
|
+ $cyjmc = trim((string)($poMeta['CYJMC'] ?? ''));
|
|
|
+ }
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ $cyjmc = '';
|
|
|
+ }
|
|
|
+ $genTime = date('Y-m-d H:i:s');
|
|
|
+ $metaIds = '工序行ID ' . $ids . ' | 主表订单ID ' . (int)$purchaseOrderId;
|
|
|
+ $metaLeft = $metaIds . ($cyjmc !== '' ? ' | ' . $cyjmc : '');
|
|
|
+ // PDF 进度:去掉「未发」;节点用纯色圆点(不用图片,避免 mPDF 破图)
|
|
|
+ $pdfSteps = [];
|
|
|
+ $viewSteps = $prep['steps'] ?? [];
|
|
|
+ if (is_array($viewSteps)) {
|
|
|
+ foreach ($viewSteps as $st) {
|
|
|
+ if (!is_array($st)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (trim((string)($st['title'] ?? '')) === '未发') {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $pdfSteps[] = $st;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ $pdfStepW = count($pdfSteps) > 0 ? (round(100 / count($pdfSteps), 2) . '%') : '16.66%';
|
|
|
+ $nPdf = count($pdfSteps);
|
|
|
+ foreach ($pdfSteps as $idx => &$pst) {
|
|
|
+ if ($idx === 0) {
|
|
|
+ $pst['pdf_left_bg'] = '';
|
|
|
+ } else {
|
|
|
+ $pst['pdf_left_bg'] = !empty($pdfSteps[$idx - 1]['done']) ? '#1890ff' : '#e0e0e0';
|
|
|
+ }
|
|
|
+ if ($idx >= $nPdf - 1) {
|
|
|
+ $pst['pdf_right_bg'] = '';
|
|
|
+ } else {
|
|
|
+ $pst['pdf_right_bg'] = !empty($pst['done']) ? '#1890ff' : '#e0e0e0';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ unset($pst);
|
|
|
$this->view->assign([
|
|
|
- 'pdf_export' => 1,
|
|
|
- 'pdfMetaLine' => $meta,
|
|
|
+ 'pdf_export' => 1,
|
|
|
+ 'pdfMetaLine' => $metaLeft,
|
|
|
+ 'pdfMetaLeft' => $metaLeft,
|
|
|
+ 'pdfMetaIds' => $metaIds,
|
|
|
+ 'pdfMetaTitle' => $cyjmc,
|
|
|
+ 'pdfMetaTime' => 'PDF生成时间 ' . $genTime,
|
|
|
+ 'pdfSteps' => $pdfSteps,
|
|
|
+ 'pdfStepWidth' => $pdfStepW,
|
|
|
]);
|
|
|
// 关闭后台 layout,避免 default 布局里的「控制台」面包屑等被打进 PDF
|
|
|
$restoreLayout = !empty($this->layout) ? ('layout/' . $this->layout) : false;
|
|
|
@@ -13446,14 +13600,22 @@ class Procuremen extends Backend
|
|
|
$this->view->engine->layout($restoreLayout);
|
|
|
}
|
|
|
Log::write('采购确认PDF模板渲染失败: ' . $e->getMessage(), 'error');
|
|
|
- $this->view->assign(['pdf_export' => '', 'pdfMetaLine' => '']);
|
|
|
+ $this->view->assign([
|
|
|
+ 'pdf_export' => '', 'pdfMetaLine' => '', 'pdfMetaLeft' => '',
|
|
|
+ 'pdfMetaIds' => '', 'pdfMetaTitle' => '', 'pdfMetaTime' => '',
|
|
|
+ 'pdfSteps' => [], 'pdfStepWidth' => '',
|
|
|
+ ]);
|
|
|
|
|
|
return '';
|
|
|
}
|
|
|
if ($restoreLayout) {
|
|
|
$this->view->engine->layout($restoreLayout);
|
|
|
}
|
|
|
- $this->view->assign(['pdf_export' => '', 'pdfMetaLine' => '']);
|
|
|
+ $this->view->assign([
|
|
|
+ 'pdf_export' => '', 'pdfMetaLine' => '', 'pdfMetaLeft' => '',
|
|
|
+ 'pdfMetaIds' => '', 'pdfMetaTitle' => '', 'pdfMetaTime' => '',
|
|
|
+ 'pdfSteps' => [], 'pdfStepWidth' => '',
|
|
|
+ ]);
|
|
|
|
|
|
$tempDir = ROOT_PATH . 'runtime' . DIRECTORY_SEPARATOR . 'mpdf_tmp';
|
|
|
if (!is_dir($tempDir)) {
|