|
|
@@ -437,6 +437,7 @@ class Procuremen extends Backend
|
|
|
->where('a.dStamp', '<=', $endTime)
|
|
|
->order('a.dStamp', 'desc')
|
|
|
->select();
|
|
|
+
|
|
|
if (!is_array($list)) {
|
|
|
return [];
|
|
|
}
|
|
|
@@ -2230,6 +2231,7 @@ class Procuremen extends Backend
|
|
|
|
|
|
/**
|
|
|
* 分配手工新增工序行 ID(负数递减)
|
|
|
+ * 调用方须在命名锁内执行,避免并发重复
|
|
|
*/
|
|
|
protected function allocateManualScydgyId(): int
|
|
|
{
|
|
|
@@ -2244,7 +2246,8 @@ class Procuremen extends Backend
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 手工新增订单号:YW + 年月日 + 3 位当日序号(如 YW20260618001)
|
|
|
+ * 手工新增订单号:YW + 年月日 + 当日序号(如 YW20260618001)
|
|
|
+ * 调用方须在命名锁内执行,避免并发重复
|
|
|
*/
|
|
|
protected function allocateManualOrderCcydh(): string
|
|
|
{
|
|
|
@@ -2257,15 +2260,53 @@ class Procuremen extends Backend
|
|
|
if (is_array($rows)) {
|
|
|
foreach ($rows as $ccydh) {
|
|
|
$ccydh = trim((string)$ccydh);
|
|
|
- if (preg_match('/^' . preg_quote($prefix, '/') . '(\d{3})$/', $ccydh, $m)) {
|
|
|
+ if (preg_match('/^' . preg_quote($prefix, '/') . '(\d{3,})$/', $ccydh, $m)) {
|
|
|
$maxSeq = max($maxSeq, (int)$m[1]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} catch (\Throwable $e) {
|
|
|
}
|
|
|
+ $next = $maxSeq + 1;
|
|
|
+ $width = $next > 999 ? strlen((string)$next) : 3;
|
|
|
+
|
|
|
+ return $prefix . str_pad((string)$next, $width, '0', STR_PAD_LEFT);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * MySQL 命名锁(跨连接防并发撞号)
|
|
|
+ */
|
|
|
+ protected function acquireProcuremenNamedLock(string $name, int $timeoutSec = 10): bool
|
|
|
+ {
|
|
|
+ $name = trim($name);
|
|
|
+ if ($name === '') {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $nameSql = str_replace(['\\', "'"], ['\\\\', "\\'"], $name);
|
|
|
+ $rows = Db::query('SELECT GET_LOCK(\'' . $nameSql . '\', ' . max(1, $timeoutSec) . ') AS locked');
|
|
|
+ if (!is_array($rows) || $rows === []) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ $v = $rows[0]['locked'] ?? $rows[0]['LOCKED'] ?? null;
|
|
|
+
|
|
|
+ return (int)$v === 1;
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- return $prefix . str_pad((string)($maxSeq + 1), 3, '0', STR_PAD_LEFT);
|
|
|
+ protected function releaseProcuremenNamedLock(string $name): void
|
|
|
+ {
|
|
|
+ $name = trim($name);
|
|
|
+ if ($name === '') {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $nameSql = str_replace(['\\', "'"], ['\\\\', "\\'"], $name);
|
|
|
+ Db::query('SELECT RELEASE_LOCK(\'' . $nameSql . '\')');
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -2422,45 +2463,51 @@ class Procuremen extends Backend
|
|
|
{
|
|
|
$cyjmc = trim((string)($params['CYJMC'] ?? ''));
|
|
|
$cgymc = trim((string)($params['CGYMC'] ?? ''));
|
|
|
- $ccydh = trim((string)($params['CCYDH'] ?? ''));
|
|
|
- if ($ccydh === '') {
|
|
|
- $ccydh = $this->allocateManualOrderCcydh();
|
|
|
- }
|
|
|
- $now = date('Y-m-d H:i:s');
|
|
|
- $sid = $this->allocateManualScydgyId();
|
|
|
- $this->ensurePurchaseOrderNotifySalesmanColumn();
|
|
|
- $this->ensurePurchaseOrderRfqSalesmanNotifiedColumn();
|
|
|
- // 选择供应商 / 通知业务员:支持多选,落库用顿号拼接
|
|
|
- $supplierName = $this->joinRfqMultiSelectValues($params['cGzzxMc'] ?? ($params['pick_company_name'] ?? ''));
|
|
|
- $notifySalesman = $this->joinRfqMultiSelectValues($params['notify_salesman'] ?? '');
|
|
|
- $data = [
|
|
|
- 'scydgy_id' => $sid,
|
|
|
- 'CCYDH' => $ccydh,
|
|
|
- 'CYJMC' => $cyjmc,
|
|
|
- 'CGYMC' => $cgymc,
|
|
|
- 'CCLBMMC' => trim((string)($params['CCLBMMC'] ?? '')),
|
|
|
- 'CDW' => trim((string)($params['CDW'] ?? '')),
|
|
|
- 'NGZL' => trim((string)($params['NGZL'] ?? '')),
|
|
|
- 'CDF' => trim((string)($params['CDF'] ?? '')),
|
|
|
- 'cGzzxMc' => $supplierName,
|
|
|
- 'MBZ' => trim((string)($params['MBZ'] ?? '')),
|
|
|
- 'cywyxm' => trim((string)($params['cywyxm'] ?? '')),
|
|
|
- 'notify_salesman' => $notifySalesman,
|
|
|
- 'rfq_salesman_notified' => 0,
|
|
|
- 'rfq_salesman_notify_time' => null,
|
|
|
- 'This_quantity' => trim((string)($params['This_quantity'] ?? '')),
|
|
|
- 'ceilingPrice' => trim((string)($params['ceilingPrice'] ?? '')),
|
|
|
- 'wflow_status' => ProcuremenStatus::WFLOW_PENDING_ISSUE,
|
|
|
- 'status' => ProcuremenStatus::PO_IN_PROGRESS,
|
|
|
- 'createtime' => $now,
|
|
|
- 'dStamp' => $now,
|
|
|
- 'dputrecord' => $now,
|
|
|
- ];
|
|
|
- Db::table('purchase_order')->insert($data);
|
|
|
- $poId = (int)Db::getLastInsID();
|
|
|
- $this->addOrderLog($sid, 'manual_add', '手工新增协助工序', $poId > 0 ? $poId : null);
|
|
|
+ $ccydhIn = trim((string)($params['CCYDH'] ?? ''));
|
|
|
+ $lockName = 'procuremen_manual_order_alloc';
|
|
|
+ if (!$this->acquireProcuremenNamedLock($lockName, 10)) {
|
|
|
+ throw new \RuntimeException('系统繁忙,请稍后重试');
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $ccydh = $ccydhIn !== '' ? $ccydhIn : $this->allocateManualOrderCcydh();
|
|
|
+ $now = date('Y-m-d H:i:s');
|
|
|
+ $sid = $this->allocateManualScydgyId();
|
|
|
+ $this->ensurePurchaseOrderNotifySalesmanColumn();
|
|
|
+ $this->ensurePurchaseOrderRfqSalesmanNotifiedColumn();
|
|
|
+ // 选择供应商 / 通知业务员:支持多选,落库用顿号拼接
|
|
|
+ $supplierName = $this->joinRfqMultiSelectValues($params['cGzzxMc'] ?? ($params['pick_company_name'] ?? ''));
|
|
|
+ $notifySalesman = $this->joinRfqMultiSelectValues($params['notify_salesman'] ?? '');
|
|
|
+ $data = [
|
|
|
+ 'scydgy_id' => $sid,
|
|
|
+ 'CCYDH' => $ccydh,
|
|
|
+ 'CYJMC' => $cyjmc,
|
|
|
+ 'CGYMC' => $cgymc,
|
|
|
+ 'CCLBMMC' => trim((string)($params['CCLBMMC'] ?? '')),
|
|
|
+ 'CDW' => trim((string)($params['CDW'] ?? '')),
|
|
|
+ 'NGZL' => trim((string)($params['NGZL'] ?? '')),
|
|
|
+ 'CDF' => trim((string)($params['CDF'] ?? '')),
|
|
|
+ 'cGzzxMc' => $supplierName,
|
|
|
+ 'MBZ' => trim((string)($params['MBZ'] ?? '')),
|
|
|
+ 'cywyxm' => trim((string)($params['cywyxm'] ?? '')),
|
|
|
+ 'notify_salesman' => $notifySalesman,
|
|
|
+ 'rfq_salesman_notified' => 0,
|
|
|
+ 'rfq_salesman_notify_time' => null,
|
|
|
+ 'This_quantity' => trim((string)($params['This_quantity'] ?? '')),
|
|
|
+ 'ceilingPrice' => trim((string)($params['ceilingPrice'] ?? '')),
|
|
|
+ 'wflow_status' => ProcuremenStatus::WFLOW_PENDING_ISSUE,
|
|
|
+ 'status' => ProcuremenStatus::PO_IN_PROGRESS,
|
|
|
+ 'createtime' => $now,
|
|
|
+ 'dStamp' => $now,
|
|
|
+ 'dputrecord' => $now,
|
|
|
+ ];
|
|
|
+ Db::table('purchase_order')->insert($data);
|
|
|
+ $poId = (int)Db::getLastInsID();
|
|
|
+ $this->addOrderLog($sid, 'manual_add', '手工新增协助工序', $poId > 0 ? $poId : null);
|
|
|
|
|
|
- return $sid;
|
|
|
+ return $sid;
|
|
|
+ } finally {
|
|
|
+ $this->releaseProcuremenNamedLock($lockName);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
protected function formatProcuremenRowLabel(array $row): string
|
|
|
@@ -4994,7 +5041,7 @@ class Procuremen extends Backend
|
|
|
$bundle = $this->loadOrderBundleForConfirmNotify(array_column($picks, 'scydgy_id'));
|
|
|
$this->dispatchPurchaseConfirmPickNotifications($bundle, $results);
|
|
|
} catch (\Throwable $e) {
|
|
|
- Log::write('采购确认短信批量发送异常: ' . $e->getMessage(), 'error');
|
|
|
+ Log::write('采购确认通知批量发送异常: ' . $e->getMessage(), 'error');
|
|
|
}
|
|
|
|
|
|
$this->pickHiddenScydgySetCache = null;
|
|
|
@@ -5126,7 +5173,7 @@ class Procuremen extends Backend
|
|
|
'purchase_order_id' => $purchaseOrderId,
|
|
|
]]);
|
|
|
} catch (\Throwable $e) {
|
|
|
- Log::write('采购确认短信发送异常 scydgy_id=' . $sid . ' ' . $e->getMessage(), 'error');
|
|
|
+ Log::write('采购确认通知发送异常 scydgy_id=' . $sid . ' ' . $e->getMessage(), 'error');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -5193,7 +5240,7 @@ class Procuremen extends Backend
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 审批通过/未通过短信:同一供应商只发一条,process_lines 含本次全部工序
|
|
|
+ * 审批通过/未通过:短信 + 邮件(同一供应商只发一条;邮件含平台链接)
|
|
|
*
|
|
|
* @param array{ccydh?:string,pos?:array,merge_rows?:array} $bundle
|
|
|
* @param array<int, array<string, mixed>> $picks
|
|
|
@@ -5203,6 +5250,7 @@ class Procuremen extends Backend
|
|
|
if ($picks === []) {
|
|
|
return;
|
|
|
}
|
|
|
+ $this->ensureConfirmResultEmailTemplates();
|
|
|
$fallbackDetailId = 0;
|
|
|
$winners = [];
|
|
|
$losers = [];
|
|
|
@@ -5241,6 +5289,11 @@ class Procuremen extends Backend
|
|
|
return;
|
|
|
}
|
|
|
$confirmNotifyVars = $this->buildPurchaseConfirmNotifyVars(0, $fallbackDetailId, $bundle);
|
|
|
+ $poIdLog = 0;
|
|
|
+ $pos = is_array($bundle['pos'] ?? null) ? $bundle['pos'] : [];
|
|
|
+ if (isset($pos[0]) && is_array($pos[0])) {
|
|
|
+ $poIdLog = (int)($pos[0]['id'] ?? 0);
|
|
|
+ }
|
|
|
$sendSmsSafe = function ($phone, $content) {
|
|
|
$phone = trim((string)$phone);
|
|
|
if ($phone === '') {
|
|
|
@@ -5252,6 +5305,60 @@ class Procuremen extends Backend
|
|
|
Log::write('采购确认短信失败 phone=' . $phone . ' ' . $e->getMessage(), 'error');
|
|
|
}
|
|
|
};
|
|
|
+ $sendConfirmEmailSafe = function (array $dr, string $emailScene) use ($confirmNotifyVars, $poIdLog) {
|
|
|
+ $cname = trim((string)($dr['company_name'] ?? ''));
|
|
|
+ $ph = trim((string)($dr['phone'] ?? ''));
|
|
|
+ $toEmail = trim((string)($dr['email'] ?? ''));
|
|
|
+ if ($toEmail === '' || !filter_var($toEmail, FILTER_VALIDATE_EMAIL)) {
|
|
|
+ Log::write('采购确认邮件跳过:无有效邮箱 company=' . $cname, 'error');
|
|
|
+
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ $detailId = (int)($dr['id'] ?? $dr['ID'] ?? 0);
|
|
|
+ if ($detailId <= 0) {
|
|
|
+ $detailId = (int)($confirmNotifyVars['_fallback_detail_id'] ?? 0);
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ $platformUrl = $this->buildMprocMobileOrderUrl($detailId);
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ Log::write('采购确认邮件链接失败: ' . $e->getMessage(), 'error');
|
|
|
+ $platformUrl = '';
|
|
|
+ }
|
|
|
+ $vars = array_merge($confirmNotifyVars, [
|
|
|
+ 'company_name' => $cname,
|
|
|
+ 'contact_name' => $this->resolveCustomerContactName($ph, $cname),
|
|
|
+ 'phone' => $ph,
|
|
|
+ 'email' => $toEmail,
|
|
|
+ 'platform_url' => $platformUrl,
|
|
|
+ ]);
|
|
|
+ unset($vars['_fallback_detail_id']);
|
|
|
+ try {
|
|
|
+ $mailPlain = $this->renderNotifyTemplate($emailScene, $vars);
|
|
|
+ $mailBody = $this->plainTextToHtmlEmailBody($mailPlain);
|
|
|
+ // 平台链接做成可点击
|
|
|
+ if ($platformUrl !== '') {
|
|
|
+ $esc = htmlspecialchars($platformUrl, ENT_QUOTES, 'UTF-8');
|
|
|
+ $mailBody = str_replace($esc, '<a href="' . $esc . '">' . $esc . '</a>', $mailBody);
|
|
|
+ }
|
|
|
+ $mailSubject = $this->resolveProcuremenEmailSubject($emailScene);
|
|
|
+ $mailConfig = $this->loadMailerConfig();
|
|
|
+ $this->issueSendSupplierEmail([
|
|
|
+ 'company_name' => $cname,
|
|
|
+ 'email' => $toEmail,
|
|
|
+ 'mail_plain' => $mailPlain,
|
|
|
+ 'mail_body' => $mailBody,
|
|
|
+ 'notify_vars' => $vars,
|
|
|
+ 'sms_content' => '',
|
|
|
+ 'detail_links' => $platformUrl !== '' ? [['url' => $platformUrl, 'cgymc' => '']] : [],
|
|
|
+ 'email_log_scene' => 'confirm_result',
|
|
|
+ 'email_log_scydgy_id' => (int)($dr['scydgy_id'] ?? 0),
|
|
|
+ 'email_log_purchase_order_id' => $poIdLog,
|
|
|
+ ], $mailConfig, $mailSubject);
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ Log::write('采购确认邮件失败 company=' . $cname . ' email=' . $toEmail . ' ' . $e->getMessage(), 'error');
|
|
|
+ }
|
|
|
+ };
|
|
|
+ $confirmNotifyVars['_fallback_detail_id'] = $fallbackDetailId;
|
|
|
foreach ($winners as $dr) {
|
|
|
$cname = trim((string)($dr['company_name'] ?? ''));
|
|
|
$ph = trim((string)($dr['phone'] ?? ''));
|
|
|
@@ -5261,6 +5368,7 @@ class Procuremen extends Backend
|
|
|
'phone' => $ph,
|
|
|
]));
|
|
|
$sendSmsSafe($ph, $sms);
|
|
|
+ $sendConfirmEmailSafe($dr, 'confirm_ok_email');
|
|
|
}
|
|
|
foreach ($losers as $dr) {
|
|
|
$cname = trim((string)($dr['company_name'] ?? ''));
|
|
|
@@ -5271,6 +5379,7 @@ class Procuremen extends Backend
|
|
|
'phone' => $ph,
|
|
|
]));
|
|
|
$sendSmsSafe($ph, $sms);
|
|
|
+ $sendConfirmEmailSafe($dr, 'confirm_fail_email');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -7962,6 +8071,8 @@ class Procuremen extends Backend
|
|
|
'deadline' => (string)($ctx['deadline'] ?? ''),
|
|
|
'process_lines' => (string)($ctx['process_plain'] ?? ''),
|
|
|
'process_lines_html' => (string)($ctx['process_html'] ?? ''),
|
|
|
+ 'order_type' => (string)($ctx['order_type'] ?? ''),
|
|
|
+ 'score_weights' => (string)($ctx['score_weights'] ?? ''),
|
|
|
'platform_url' => $platformUrl,
|
|
|
'platform_links' => $platformLinksPlain,
|
|
|
'platform_links_html' => $platformLinksHtml,
|
|
|
@@ -8084,6 +8195,9 @@ class Procuremen extends Backend
|
|
|
|
|
|
$emailTplScene = trim($emailTplScene) !== '' ? trim($emailTplScene) : 'review_email';
|
|
|
$ctx = $this->buildIssueNotifyContext($mergeRows, $sysRqNotify);
|
|
|
+ $orderTypeCtx = $this->resolveIssueNotifyOrderTypeFromBundle($bundle);
|
|
|
+ $ctx['order_type'] = (string)($orderTypeCtx['order_type'] ?? '');
|
|
|
+ $ctx['score_weights'] = (string)($orderTypeCtx['score_weights'] ?? '');
|
|
|
$notifyBundle = $this->composeSupplierNotifyBundle($chosen, $ctx, $detailLinks, $emailTplScene);
|
|
|
$notifyBundle['email_log_scene'] = $emailLogScene;
|
|
|
$notifyBundle['email_log_scydgy_id'] = (int)array_key_first($sids);
|
|
|
@@ -8389,6 +8503,88 @@ class Procuremen extends Backend
|
|
|
]);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 下发通知:解析订单类型名称与评分比例文案
|
|
|
+ *
|
|
|
+ * @param array<string, mixed> $bundle
|
|
|
+ * @return array{order_type:string,score_weights:string}
|
|
|
+ */
|
|
|
+ protected function resolveIssueNotifyOrderTypeFromBundle(array $bundle): array
|
|
|
+ {
|
|
|
+ $orderType = '';
|
|
|
+ $scoreWeights = '';
|
|
|
+ $rule = null;
|
|
|
+ if (!empty($bundle['score_rule']) && is_array($bundle['score_rule'])) {
|
|
|
+ $rule = $bundle['score_rule'];
|
|
|
+ }
|
|
|
+ $pos = $bundle['pos'] ?? [];
|
|
|
+ $po0 = (is_array($pos) && isset($pos[0]) && is_array($pos[0])) ? $pos[0] : null;
|
|
|
+ if ($rule === null && is_array($po0)) {
|
|
|
+ $ruleId = (int)($po0['score_rule_id'] ?? 0);
|
|
|
+ if ($ruleId > 0) {
|
|
|
+ try {
|
|
|
+ $rule = ProcuremenSupplierScore::getRuleById($ruleId);
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ $rule = null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!is_array($rule) || (int)($rule['id'] ?? 0) <= 0) {
|
|
|
+ $qw = (int)($po0['score_quality_weight'] ?? 0);
|
|
|
+ $pw = (int)($po0['score_price_weight'] ?? 0);
|
|
|
+ $lw = (int)($po0['score_lead_weight'] ?? 0);
|
|
|
+ if ($qw > 0 || $pw > 0 || $lw > 0) {
|
|
|
+ $rule = [
|
|
|
+ 'name' => trim((string)($po0['score_rule_name'] ?? '')),
|
|
|
+ 'quality_weight' => $qw,
|
|
|
+ 'price_weight' => $pw,
|
|
|
+ 'lead_weight' => $lw,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (is_array($rule)) {
|
|
|
+ $orderType = trim((string)($rule['name'] ?? ''));
|
|
|
+ $scoreWeights = $this->formatScoreRuleWeightsText($rule);
|
|
|
+ }
|
|
|
+ if ($orderType === '' && is_array($po0)) {
|
|
|
+ $orderType = trim((string)($po0['score_rule_name'] ?? ''));
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'order_type' => $orderType,
|
|
|
+ 'score_weights' => $scoreWeights,
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 评分比例文案:质量30%、价格50%、交货20%
|
|
|
+ *
|
|
|
+ * @param array{quality_weight?:mixed,price_weight?:mixed,lead_weight?:mixed} $rule
|
|
|
+ */
|
|
|
+ protected function formatScoreRuleWeightsText(array $rule): string
|
|
|
+ {
|
|
|
+ $parts = [];
|
|
|
+ $qw = max(0, (float)($rule['quality_weight'] ?? 0));
|
|
|
+ $pw = max(0, (float)($rule['price_weight'] ?? 0));
|
|
|
+ $lw = max(0, (float)($rule['lead_weight'] ?? 0));
|
|
|
+ $fmt = static function ($n) {
|
|
|
+ $s = rtrim(rtrim(sprintf('%.2F', (float)$n), '0'), '.');
|
|
|
+
|
|
|
+ return $s === '' ? '0' : $s;
|
|
|
+ };
|
|
|
+ if ($qw > 0) {
|
|
|
+ $parts[] = '质量' . $fmt($qw) . '%';
|
|
|
+ }
|
|
|
+ if ($pw > 0) {
|
|
|
+ $parts[] = '价格' . $fmt($pw) . '%';
|
|
|
+ }
|
|
|
+ if ($lw > 0) {
|
|
|
+ $parts[] = '交货' . $fmt($lw) . '%';
|
|
|
+ }
|
|
|
+
|
|
|
+ return implode('、', $parts);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 协助下发通知上下文(订单号、工序明细等,供短信/邮件共用)
|
|
|
*
|
|
|
@@ -8408,6 +8604,8 @@ class Procuremen extends Backend
|
|
|
'process_plain' => $this->buildProcessLinesPlain($mergeRows),
|
|
|
'process_html' => $this->buildProcessLinesHtml($mergeRows),
|
|
|
'deadline' => $sysRqNotify,
|
|
|
+ 'order_type' => '',
|
|
|
+ 'score_weights' => '',
|
|
|
];
|
|
|
}
|
|
|
|
|
|
@@ -8626,6 +8824,74 @@ class Procuremen extends Backend
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 确保「采购确认通过/未通过」邮箱模版存在(正文同短信,末尾加平台链接)
|
|
|
+ */
|
|
|
+ protected function ensureConfirmResultEmailTemplates(): void
|
|
|
+ {
|
|
|
+ static $done = false;
|
|
|
+ if ($done) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ $done = true;
|
|
|
+ $now = date('Y-m-d H:i:s');
|
|
|
+ $seeds = [
|
|
|
+ 'confirm_ok_email' => [
|
|
|
+ 'title' => '采购确认结果:已通过',
|
|
|
+ 'content' => "【可集达】您好,{company_name}:\n\n您参与的外发加工订单采购确认结果:已通过。\n{process_lines}\n\n请前往平台查看:{platform_url}\n",
|
|
|
+ 'remark' => '审批通过-中标供应商邮箱通知;模版名称作邮件主题',
|
|
|
+ ],
|
|
|
+ 'confirm_fail_email' => [
|
|
|
+ 'title' => '采购确认结果:未通过',
|
|
|
+ 'content' => "【可集达】您好,{company_name}:\n\n您参与的外发加工订单采购确认结果:未通过。\n{process_lines}\n\n请前往平台查看:{platform_url}\n",
|
|
|
+ 'remark' => '审批通过-未中标供应商邮箱通知;模版名称作邮件主题',
|
|
|
+ ],
|
|
|
+ ];
|
|
|
+ try {
|
|
|
+ $cols = Db::query("SHOW COLUMNS FROM `purchase_sms_template`");
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ $colNames = [];
|
|
|
+ foreach ($cols as $c) {
|
|
|
+ if (!is_array($c)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $n = (string)($c['Field'] ?? $c['field'] ?? '');
|
|
|
+ if ($n !== '') {
|
|
|
+ $colNames[$n] = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ foreach ($seeds as $scene => $seed) {
|
|
|
+ try {
|
|
|
+ $row = Db::table('purchase_sms_template')->where('scene', $scene)->find();
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (is_array($row) && $row !== []) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ $data = [
|
|
|
+ 'scene' => $scene,
|
|
|
+ 'title' => $seed['title'],
|
|
|
+ 'content' => $seed['content'],
|
|
|
+ 'remark' => $seed['remark'],
|
|
|
+ 'status' => 1,
|
|
|
+ ];
|
|
|
+ if (isset($colNames['updatetime'])) {
|
|
|
+ $data['updatetime'] = $now;
|
|
|
+ }
|
|
|
+ if (isset($colNames['createtime'])) {
|
|
|
+ $data['createtime'] = $now;
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ Db::table('purchase_sms_template')->insert($data);
|
|
|
+ } catch (\Throwable $e) {
|
|
|
+ // ignore
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 确保「供应商询价通知」邮箱模版存在(查询询价-询价/发邮件;可在短信模版配置中修改)
|
|
|
*/
|
|
|
@@ -10912,6 +11178,7 @@ class Procuremen extends Backend
|
|
|
'ccydh' => $ccydhPick,
|
|
|
'pos' => $posNotify,
|
|
|
'merge_rows' => $mergeRows,
|
|
|
+ 'score_rule' => is_array($scoreRule) ? $scoreRule : null,
|
|
|
];
|
|
|
try {
|
|
|
$this->notifyDryRunPreview = [];
|
|
|
@@ -12631,28 +12898,8 @@ class Procuremen extends Backend
|
|
|
$completeTsMap = ProcuremenTime::loadOperLogTimestampMap(array_keys($sidList), ['purchase_confirm', 'mark_complete']);
|
|
|
}
|
|
|
|
|
|
- $searchKw = trim((string)$this->request->get('search', ''));
|
|
|
- if ($searchKw === '') {
|
|
|
- $searchKw = trim((string)$this->request->request('search', ''));
|
|
|
- }
|
|
|
- $filterRaw = $this->request->get('filter', '');
|
|
|
- $filterArrTmp = [];
|
|
|
- if (is_string($filterRaw) && $filterRaw !== '') {
|
|
|
- $filterArrTmp = (array)json_decode($filterRaw, true);
|
|
|
- } elseif (is_array($filterRaw)) {
|
|
|
- $filterArrTmp = $filterRaw;
|
|
|
- }
|
|
|
- $hasSearch = ($searchKw !== '');
|
|
|
- if (!$hasSearch) {
|
|
|
- foreach ($filterArrTmp as $fv) {
|
|
|
- if (trim((string)$fv) !== '') {
|
|
|
- $hasSearch = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- $dbRows = array_values(array_filter($dbRows, function ($dbRow) use ($ym, $completeTsMap, $hasSearch) {
|
|
|
+ // 导出始终按「查看月份」取当月完结单;搜索只在当月内再缩小(勿跨月,否则会与列表不一致)
|
|
|
+ $dbRows = array_values(array_filter($dbRows, function ($dbRow) use ($ym, $completeTsMap) {
|
|
|
if (!is_array($dbRow)) {
|
|
|
return false;
|
|
|
}
|
|
|
@@ -12660,9 +12907,6 @@ class Procuremen extends Backend
|
|
|
if ($done['ts'] <= 0) {
|
|
|
return false;
|
|
|
}
|
|
|
- if ($hasSearch) {
|
|
|
- return true;
|
|
|
- }
|
|
|
|
|
|
return date('Y-m', $done['ts']) === $ym;
|
|
|
}));
|
|
|
@@ -13312,15 +13556,21 @@ class Procuremen extends Backend
|
|
|
|
|
|
protected function resolveProcuremenEmailSubject(string $scene): string
|
|
|
{
|
|
|
- // 询价通知业务员 / 供应商询价:模版名称即邮件主题
|
|
|
- if ($scene === 'rfq_salesman_email' || $scene === 'rfq_supplier_email') {
|
|
|
+ // 询价 / 采购确认结果邮箱:模版名称即邮件主题
|
|
|
+ if (in_array($scene, ['rfq_salesman_email', 'rfq_supplier_email', 'confirm_ok_email', 'confirm_fail_email'], true)) {
|
|
|
$row = $this->loadNotifyTemplateRow($scene);
|
|
|
$title = is_array($row) ? trim((string)($row['title'] ?? '')) : '';
|
|
|
if ($title !== '') {
|
|
|
return $title;
|
|
|
}
|
|
|
+ $fallback = [
|
|
|
+ 'rfq_supplier_email' => '供应商询价通知',
|
|
|
+ 'rfq_salesman_email' => '协助采购询价通知',
|
|
|
+ 'confirm_ok_email' => '采购确认结果:已通过',
|
|
|
+ 'confirm_fail_email' => '采购确认结果:未通过',
|
|
|
+ ];
|
|
|
|
|
|
- return $scene === 'rfq_supplier_email' ? '供应商询价通知' : '协助采购询价通知';
|
|
|
+ return $fallback[$scene] ?? '协助采购通知';
|
|
|
}
|
|
|
$map = [
|
|
|
'review_email' => '您有新的协助加工订单',
|
|
|
@@ -13336,13 +13586,15 @@ class Procuremen extends Backend
|
|
|
protected function notifyTemplateMissingMessage(string $scene, bool $titleRequired = false): string
|
|
|
{
|
|
|
$map = [
|
|
|
- 'review_email' => '协助下发-邮箱',
|
|
|
- 'review_sms' => '协助下发-短信',
|
|
|
- 'confirm_ok' => '采购确认-通过',
|
|
|
- 'confirm_fail' => '采购确认-未通过',
|
|
|
- 'bid_open' => '开标双重验证',
|
|
|
- 'rfq_salesman_email' => '询价通知业务员-邮箱',
|
|
|
- 'rfq_supplier_email' => '供应商询价通知',
|
|
|
+ 'review_email' => '协助下发-邮箱',
|
|
|
+ 'review_sms' => '协助下发-短信',
|
|
|
+ 'confirm_ok' => '采购确认-通过',
|
|
|
+ 'confirm_fail' => '采购确认-未通过',
|
|
|
+ 'confirm_ok_email' => '采购确认通过-邮箱',
|
|
|
+ 'confirm_fail_email' => '采购确认未通过-邮箱',
|
|
|
+ 'bid_open' => '开标双重验证',
|
|
|
+ 'rfq_salesman_email' => '询价通知业务员-邮箱',
|
|
|
+ 'rfq_supplier_email' => '供应商询价通知',
|
|
|
];
|
|
|
$label = $map[$scene] ?? $scene;
|
|
|
if ($titleRequired) {
|