完善采购审批流程、企业微信通知和采购专用章

This commit is contained in:
reddaniel110
2026-09-16 11:45:21 +08:00
parent 4876d8805d
commit bf952196fa
58 changed files with 3451 additions and 1909 deletions
+54 -72
View File
@@ -8,15 +8,20 @@
{% if editable %}<a class="btn primary" href="{{ url_for('order_edit', oid=o.id) }}">编辑</a>{% endif %}
<a class="btn" href="{{ url_for('order_pdf', oid=o.id) }}" target="_blank">导出 / 预览 PDF</a>
<a class="btn" href="{{ url_for('order_pdf', oid=o.id) }}?dl=1" download>下载 PDF</a>
{% if user.role in ['maker','admin'] %}
<form method="post" action="{{ url_for('order_copy', oid=o.id) }}" style="display:inline;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="row_version" value="{{ o.row_version }}">
<input type="hidden" name="request_key" value="{{ request_key() }}">
<button class="btn">复制为新订单</button>
</form>
</form>{% endif %}
<span class="grow"></span>
<a class="btn" href="{{ url_for('orders') }}">返回列表</a>
</div>
<div class="card">
<h2>单据信息</h2>
<p class="mobile-only muted">单据信息和物料明细可左右滑动查看。</p>
<table class="kv-table" style="width:100%;">
<tr>
<td class="k">供应商</td><td>{{ o.supplier_name }}</td>
@@ -60,7 +65,7 @@
<td>{{ it.material_spec }}</td>
<td class="center">{{ it.unit }}</td>
<td class="num">{{ it.qty }}</td>
<td class="num">{{ '%.2f'|format(it.price) }}</td>
<td class="num">{{ it.price|price }}</td>
<td class="num">{{ '%.2f'|format(it.amount) }}</td>
<td>{{ it.delivery_date }}</td>
<td>{{ it.remark }}</td>
@@ -77,89 +82,38 @@
</div>
<div class="card">
<h2>审核流程</h2>
<table class="grid">
<h2>审批流程</h2>
<table class="grid approval-flow">
<tr><th>环节</th><th>操作人</th><th>时间</th></tr>
<tr><td>制单</td><td>{{ o.created_by_name }}</td><td>{{ o.created_at.strftime('%Y-%m-%d %H:%M') if o.created_at else '-' }}</td></tr>
<tr><td>主管审核</td><td>{{ o.reviewed_by or '—' }}</td><td>{{ o.reviewed_at.strftime('%Y-%m-%d %H:%M') if o.reviewed_at else '—' }}</td></tr>
<tr><td>经理批准</td><td>{{ o.approved_by or '—' }}</td><td>{{ o.approved_at.strftime('%Y-%m-%d %H:%M') if o.approved_at else '—' }}</td></tr>
{% if o.reviewed_by %}<tr><td>旧流程审核(历史)</td><td>{{ o.reviewed_by or '—' }}</td><td>{{ o.reviewed_at.strftime('%Y-%m-%d %H:%M') if o.reviewed_at else '—' }}</td></tr>{% endif %}
<tr><td>审批</td><td>{{ o.approved_by or '—' }}</td><td>{{ o.approved_at.strftime('%Y-%m-%d %H:%M') if o.approved_at else '—' }}</td></tr>
</table>
{% if user.role in ['reviewer','admin'] and o.status == 'pending_review' %}
<div style="margin-top:12px;">
<div id="stampBox" style="display:none; margin-bottom:10px;">
<div class="seal-wrap">
<div class="seal-tip">已加盖主管审核章</div>
<div class="seal-circle">
<span class="co">{{ o.our_company or COMPANY_NAME }}</span>
<span class="txt">审核专用</span>
<span class="who">{{ user.name }}<br><span id="stampTime"></span></span>
</div>
</div>
</div>
<form method="post" action="{{ url_for('order_review', oid=o.id) }}" class="toolbar"
onsubmit="return checkStamp()">
<input type="hidden" name="stamped" id="stampedInput" value="0">
<input type="text" name="comment" placeholder="审核意见(选填)" style="width:280px;">
<button type="button" class="btn" id="stampBtn" onclick="applyStamp()">加盖审核章</button>
<button type="submit" class="btn ok" id="confirmBtn" disabled
title="请先加盖审核章">确认审核通过 → 提交经理批准</button>
</form>
<div class="muted" style="margin-top:6px;">流程:先点「加盖审核章」,确认无误后再点「确认审核通过」。</div>
</div>
<script>
function applyStamp(){
document.getElementById('stampedInput').value = '1';
document.getElementById('stampBox').style.display = 'block';
const d = new Date(), p = n => String(n).padStart(2, '0');
document.getElementById('stampTime').textContent =
d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate()) +
' ' + p(d.getHours()) + ':' + p(d.getMinutes());
const c = document.getElementById('confirmBtn');
c.disabled = false;
const s = document.getElementById('stampBtn');
s.disabled = true;
s.textContent = '已加章';
}
function checkStamp(){
if (document.getElementById('stampedInput').value !== '1'){
alert('请先点击「加盖审核章」,再确认审核。');
return false;
}
return confirm('确认审核通过并提交经理批准?');
}
</script>
{% endif %}
{% if user.role in ['approver','admin'] and o.status == 'pending_approval' %}
{% if may_approve %}
<div style="margin-top:12px;">
<div id="apprStampBox" style="display:none; margin-bottom:10px;">
<div class="seal-wrap">
<div class="seal-tip">已加盖经理批准章</div>
<div class="seal-circle">
<span class="co">{{ o.our_company or COMPANY_NAME }}</span>
<span class="txt">批准专用</span>
<span class="who">{{ user.name }}<br><span id="apprStampTime"></span></span>
</div>
<div class="seal-tip">采购专用章预览(尚未提交)</div>
{% include 'seal_preview.html' %}
</div>
</div>
<form method="post" action="{{ url_for('order_approve', oid=o.id) }}" class="toolbar"
onsubmit="return checkStampA()">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="row_version" value="{{ o.row_version }}">
<input type="hidden" name="stamped" id="apprStampedInput" value="0">
<input type="text" name="comment" placeholder="批准意见(选填)" style="width:280px;">
<button type="button" class="btn" id="apprStampBtn" onclick="applyStampA()">加盖批准章</button>
<input type="text" name="comment" placeholder="审批意见(选填)" style="width:280px;">
<button type="button" class="btn" id="apprStampBtn" onclick="applyStampA()">加盖采购专用章</button>
<button type="submit" class="btn ok" id="apprConfirmBtn" disabled
title="请先加盖批准章">确认批准通过</button>
title="请先加盖采购专用章">确认审批通过</button>
</form>
<div class="muted" style="margin-top:6px;">流程:先点「加盖批准章」,确认无误后再点「确认批准通过」。</div>
<div class="muted" style="margin-top:6px;">流程:先点「加盖采购专用章」,确认无误后再点「确认审批通过」。</div>
</div>
<script>
function applyStampA(){
document.getElementById('apprStampedInput').value = '1';
document.getElementById('apprStampBox').style.display = 'block';
const d = new Date(), p = n => String(n).padStart(2, '0');
document.getElementById('apprStampTime').textContent =
d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate()) +
' ' + p(d.getHours()) + ':' + p(d.getMinutes());
document.getElementById('apprConfirmBtn').disabled = false;
const s = document.getElementById('apprStampBtn');
s.disabled = true;
@@ -167,33 +121,61 @@
}
function checkStampA(){
if (document.getElementById('apprStampedInput').value !== '1'){
alert('请先点击「加盖批准章」,再确认批准。');
alert('请先点击「加盖采购专用章」,再确认审批。');
return false;
}
return confirm('确认批准通过?批准后即可导出正式 PDF。');
return confirm('确认审批通过?审批通过后即可导出正式 PDF。');
}
</script>
{% endif %}
{% if user.role in ['reviewer','approver','admin'] and o.status in ['pending_review','pending_approval'] %}
{% if may_reject %}
<form method="post" action="{{ url_for('order_reject', oid=o.id) }}" class="toolbar" style="margin-top:8px;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="row_version" value="{{ o.row_version }}">
<input type="text" name="comment" placeholder="驳回原因(必填)" style="width:280px;" required>
<button class="btn danger">驳回给制单人</button>
</form>
{% endif %}
{% if editable and o.status in ['draft','rejected'] %}
<form method="post" action="{{ url_for('order_submit', oid=o.id) }}" class="toolbar" style="margin-top:8px;">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="row_version" value="{{ o.row_version }}">
<input type="text" name="comment" placeholder="提交说明(选填)" style="width:280px;">
<button class="btn primary">提交审核</button>
<button class="btn primary">提交审批</button>
</form>
{% endif %}
{% if o.status == 'rejected' and o.reject_reason %}
<div class="flash error">驳回原因:{{ o.reject_reason }}</div>
{% endif %}
<h3>审核记录</h3>
{% if o.created_by == user.id and o.status == 'pending_approval' %}
<form method="post" action="{{ url_for('order_withdraw',oid=o.id) }}" class="toolbar">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="row_version" value="{{ o.row_version }}">
<input name="comment" placeholder="撤回原因(必填)" required><button class="btn">撤回修改</button>
</form>
{% endif %}
{% if o.status == 'approved' and user.role in ['maker','admin'] and (o.created_by == user.id or user.role == 'admin') %}
<form method="post" action="{{ url_for('order_revise',oid=o.id) }}" class="toolbar" onsubmit="return confirm('原审批将停止使用,修改后须重新审批。确认发起变更?')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="row_version" value="{{ o.row_version }}">
<input name="comment" placeholder="变更原因(必填)" required><button class="btn">发起变更重审</button>
</form>
{% endif %}
{% if editable or (o.status == 'approved' and user.role in ['admin','approver']) %}
<form method="post" action="{{ url_for('order_void',oid=o.id) }}" class="toolbar" onsubmit="return confirm('确认作废此订单?已发出的文件也应通知接收人停止使用。')">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
<input type="hidden" name="row_version" value="{{ o.row_version }}">
<input name="comment" placeholder="作废原因(必填)" required><button class="btn danger">作废订单</button>
</form>
{% endif %}
<h3>提交版本</h3>
{% for version in o.versions %}<p><a href="{{ url_for('order_version',oid=o.id,revision=version.revision) }}">第 {{ version.revision }} 次提交 · {{ version.created_at.strftime('%Y-%m-%d %H:%M') }}</a>{{ ' · 已归档正式文件' if version.pdf_data else '' }}</p>
{% else %}<p class="muted">尚无提交快照;升级前的审批记录可在下方查看。</p>{% endfor %}
<h3>审批记录</h3>
<ul class="timeline">
{% for lg in o.logs %}
<li>{{ lg.created_at.strftime('%Y-%m-%d %H:%M') }} · {{ lg.user_name }} · {{ lg.action }}
<li>{{ lg.created_at.strftime('%Y-%m-%d %H:%M') }} · {{ lg.user_name }} · {{ lg.action }}(第 {{ lg.revision }} 次提交)
{% if lg.comment %}({{ lg.comment }}){% endif %}</li>
{% else %}
<li class="muted">暂无记录</li>