diff --git a/src/views/oa/erp/contractInfo/edit.vue b/src/views/oa/erp/contractInfo/edit.vue
index 59a44f6..2331b7b 100644
--- a/src/views/oa/erp/contractInfo/edit.vue
+++ b/src/views/oa/erp/contractInfo/edit.vue
@@ -1240,6 +1240,7 @@ watch(
form.value.frameworkContractId = undefined;
form.value.frameworkContractCode = undefined;
form.value.frameworkContractName = undefined;
+ } else {
form.value.frameworkValidPeriod = undefined;
}
}
diff --git a/src/views/oa/erp/finInvoiceInfo/edit.vue b/src/views/oa/erp/finInvoiceInfo/edit.vue
index f4eb6af..641f4cc 100644
--- a/src/views/oa/erp/finInvoiceInfo/edit.vue
+++ b/src/views/oa/erp/finInvoiceInfo/edit.vue
@@ -159,28 +159,34 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
开票明细
@@ -212,17 +218,22 @@
-
+
-
+
-
+
@@ -232,6 +243,7 @@
:min="0"
:precision="2"
controls-position="right"
+ :disabled="isFormDisabled"
@change="calculateItemAmount($index)"
style="width: 120px"
/>
@@ -247,6 +259,7 @@
clearable
placeholder="税率"
style="width: 80px"
+ :disabled="isFormDisabled"
@change="calculateItemAmount($index)"
>
@@ -266,6 +279,7 @@
:min="0"
:precision="2"
controls-position="right"
+ :disabled="isFormDisabled"
@change="calculateItemAmountByTotal($index)"
style="width: 120px"
/>
@@ -278,6 +292,7 @@
:min="0"
:precision="2"
controls-position="right"
+ :disabled="isFormDisabled"
@change="calculateItemAmountByNoTax($index)"
style="width: 120px"
/>
@@ -301,7 +316,7 @@
-
+
@@ -356,7 +371,7 @@ import { ContractPaymentMethodVO } from '@/api/oa/erp/contractPaymentMethod/type
import { UserVO } from '@/api/system/user/types';
import ContractSelectDialog from '@/views/oa/components/ContractSelectDialog.vue';
import FileUpload from '@/components/FileUpload/index.vue';
-import { checkPermi } from '@/utils/permission';
+import { checkPermi, checkRole } from '@/utils/permission';
import { getContractInfo } from '@/api/oa/erp/contractInfo';
import { listProjectInfoByContractId } from '@/api/oa/erp/projectInfo';
@@ -397,6 +412,25 @@ const CONTRACT_FLAG = {
};
const hasInvoiceAttachPer = checkPermi(['oa/erp:finInvoiceInfo:invoiceAttach']);
+
+/** 仅允许下载:查看页;或修改页但流程审批中(与主表单锁定一致)。其余按菜单权限 / invoice 审批角色 */
+const invoiceAttachReadonly = computed(() => {
+ if (routeParams.value.type === 'view') {
+ return true;
+ }
+ if (routeParams.value.type === 'update') {
+ return true;
+ }
+ const canInvoiceRoleAttach =
+ routeParams.value.type === 'approval' &&
+ form.value.flowStatus === FLOW_STATUS.WAITING &&
+ checkRole(['invoice']);
+ if (hasInvoiceAttachPer || canInvoiceRoleAttach) {
+ return false;
+ }
+ return true;
+});
+
const isFormDisabled = computed(() => {
return routeParams.value.type === 'view' || routeParams.value.type === 'approval' || form.value.flowStatus === FLOW_STATUS.WAITING;
});
@@ -488,7 +522,7 @@ watch(
// 使用 ?? 将 null 和 undefined 都统一为 'empty' 字符串
// 如果 ossId 从无到有,或者从有到无,或者值改变 (只在初始化完成后,并且值确实发生变化时才执行)
- if (isInitialized.value && form.value.invoiceId) {
+ if (isInitialized.value && form.value.invoiceId && !invoiceAttachReadonly.value) {
const normalizedOld = oldVal ?? 'empty';
const normalizedNew = newVal ?? 'empty';
if (normalizedOld !== normalizedNew) {
@@ -529,8 +563,19 @@ const handleUserChange = async (newUserId) => {
};
const getContractPaymentMethods = async (contractId) => {
- const res = await getContractPaymentMethodList(contractId);
- contractPaymentMethodVoList.value = res.data;
+ if (!contractId) {
+ contractPaymentMethodVoList.value = [];
+ return;
+ }
+ const [pmRes, contractRes] = await Promise.all([
+ getContractPaymentMethodList(contractId),
+ getContractInfo(contractId)
+ ]);
+ contractPaymentMethodVoList.value = pmRes.data ?? [];
+ const c = contractRes?.data;
+ if (c?.contractCode) {
+ form.value.contractCode = c.contractCode;
+ }
};
const paymentDescription = computed(() => {
@@ -559,7 +604,8 @@ const filterDisabledDept = (deptList: DeptTreeVO[]) => {
// 计算合计金额 - 自动更新 issueAmount 和 issuancePercentage
const totalInvoiceAmount = computed(() => {
- const total = Number(form.value.erpFinInvoiceDetailList.reduce((sum, item) => sum + (item.totalPrice || 0), 0).toFixed(2));
+ const detailList = form.value.erpFinInvoiceDetailList || [];
+ const total = Number(detailList.reduce((sum, item) => sum + (item.totalPrice || 0), 0).toFixed(2));
// 更新开票金额
form.value.issueAmount = total;
@@ -648,8 +694,27 @@ onMounted(async () => {
if (id && (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval')) {
const res = await getFinInvoiceInfo(id);
Object.assign(form.value, res.data);
- form.value.erpFinInvoiceDetailList = res.data.erpFinInvoiceDetailVoList || [];
- form.value.erpFinInvoiceDetailList.forEach((_, index) => calculateItemAmountByTotal(index));
+ const invoiceVo = res.data;
+ const detailFromApi =
+ invoiceVo.erpFinInvoiceDetailVoList ?? (invoiceVo as FinInvoiceInfoForm).erpFinInvoiceDetailList;
+ form.value.erpFinInvoiceDetailList = normalizeInvoiceDetailListFromApi(
+ Array.isArray(detailFromApi) ? detailFromApi : []
+ );
+ // 仅补算后端未落库的不含税金额/税额,避免整表重算把「无数量但有单价」等数据清空(与台账展示一致)
+ form.value.erpFinInvoiceDetailList.forEach((item) => {
+ if (
+ item.totalPrice != null &&
+ item.totalPriceNoTax == null &&
+ item.taxRate != null
+ ) {
+ calculateTaxAmounts(item);
+ }
+ });
+ if (form.value.contractId) {
+ await getContractPaymentMethods(form.value.contractId);
+ } else {
+ contractPaymentMethodVoList.value = [];
+ }
} else {
const res = await getBaseInfo();
Object.assign(form.value, res.data);
@@ -786,6 +851,25 @@ const handleSelectionChange = (selection: FinInvoiceDetailVO[]) => {
selectedItems.value = selection;
};
+/** 接口 BigDecimal 等可能为数字或字符串,统一为表单可用的 number,便于 el-select / el-input-number 正确回显 */
+const toFormNumber = (val: unknown): number | undefined => {
+ if (val === undefined || val === null || val === '') return undefined;
+ const n = Number(val);
+ return Number.isFinite(n) ? n : undefined;
+};
+
+const normalizeInvoiceDetailListFromApi = (list: FinInvoiceDetailVO[]): FinInvoiceDetailVO[] => {
+ return list.map((row) => ({
+ ...row,
+ quantity: toFormNumber(row.quantity),
+ unitPrice: toFormNumber(row.unitPrice),
+ totalPrice: toFormNumber(row.totalPrice),
+ totalPriceNoTax: toFormNumber(row.totalPriceNoTax),
+ taxRate: toFormNumber(row.taxRate),
+ taxPrice: toFormNumber(row.taxPrice)
+ })) as FinInvoiceDetailVO[];
+};
+
const formatAmount = (value?: number) => {
return value !== undefined && value !== null ? Number(value).toFixed(2) : '0.00';
};
@@ -808,8 +892,10 @@ const calculateTaxAmounts = (item: FinInvoiceDetailVO) => {
// 通过数量和金额(含税)回算单价
const calculateItemAmount = (index: number) => {
const item = form.value.erpFinInvoiceDetailList[index];
- if (item.quantity && item.totalPrice !== undefined && item.totalPrice !== null) {
- item.unitPrice = roundAmount(item.totalPrice / item.quantity);
+ const qty = toFormNumber(item.quantity);
+ const total = toFormNumber(item.totalPrice);
+ if (qty !== undefined && qty > 0 && total !== undefined) {
+ item.unitPrice = roundAmount(total / qty);
} else {
item.unitPrice = undefined;
}
@@ -831,8 +917,9 @@ const calculateItemAmountByNoTax = (index: number) => {
}
const taxRate = Number(item.taxRate || 0);
item.totalPrice = roundAmount(noTaxAmount * (1 + taxRate / 100)) || 0;
- if (item.quantity && item.quantity > 0) {
- item.unitPrice = roundAmount((item.totalPrice || 0) / item.quantity);
+ const qty = toFormNumber(item.quantity);
+ if (qty !== undefined && qty > 0) {
+ item.unitPrice = roundAmount((item.totalPrice || 0) / qty);
} else {
item.unitPrice = undefined;
}
@@ -903,8 +990,20 @@ const submitCallback = async () => {
router.go(-1);
};
-// 审批
+// 审批(本页回调:invoice 角色在审批中打开弹窗前校验发票附件必填)
const approvalVerifyOpen = async () => {
+ const needInvoiceAttach =
+ routeParams.value.type === 'approval' &&
+ form.value.flowStatus === FLOW_STATUS.WAITING &&
+ checkRole(['invoice']);
+ if (needInvoiceAttach) {
+ const raw = form.value.ossId;
+ const hasFile = raw !== undefined && raw !== null && String(raw).trim() !== '';
+ if (!hasFile) {
+ ElMessage.warning('请先上传发票附件后再审批');
+ return;
+ }
+ }
await submitVerifyRef.value.openDialog(routeParams.value.taskId);
};
diff --git a/src/views/oa/erp/orderLedger/index.vue b/src/views/oa/erp/orderLedger/index.vue
index bbab85c..7c90de7 100644
--- a/src/views/oa/erp/orderLedger/index.vue
+++ b/src/views/oa/erp/orderLedger/index.vue
@@ -484,6 +484,7 @@ import { ContractOrderPurchaseMaterialVO } from '@/api/oa/erp/contractOrder/type
import { getContractInfo } from '@/api/oa/erp/contractInfo';
import { ContractInfoVO } from '@/api/oa/erp/contractInfo/types';
import { ProjectInfoVO } from '@/api/oa/erp/projectInfo/types';
+import { listProjectInfoByContractId } from '@/api/oa/erp/projectInfo';
import { listProjectPurchase } from '@/api/oa/erp/projectPurchase';
import { ProjectPurchaseVO } from '@/api/oa/erp/projectPurchase/types';
import { listFinInvoiceInfo } from '@/api/oa/erp/finInvoiceInfo';
@@ -499,6 +500,9 @@ import { useUserStore } from '@/store/modules/user';
/** 回款台账可见角色标识 */
const PAYMENT_LEDGER_ROLE_KEY = 'CVP';
+/** 项目类别:9 = 合同订单(非实施项目),采购数据挂在实施项目的 project_id 上 */
+const PROJECT_CATEGORY_CONTRACT_ORDER = '9';
+
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const userStore = useUserStore();
const route = useRoute();
@@ -769,24 +773,58 @@ const loadOrderAndContract = async () => {
}
};
+/**
+ * 采购/采购物料清单按 erp_project_purchase.project_id(实施项目)查询。
+ * 台账路由参数为合同订单行时,需解析到关联的实施项目 ID,不能直接用路由上的合同订单 project_id。
+ */
+const resolvePurchaseBizProjectId = async (): Promise => {
+ const o = orderInfo.value;
+ if (!o?.projectId) return undefined;
+ if (o.projectCategory !== PROJECT_CATEGORY_CONTRACT_ORDER) {
+ return o.projectId;
+ }
+ if (o.parentProjectId != null && String(o.parentProjectId) !== '') {
+ return o.parentProjectId;
+ }
+ const cid = o.contractId;
+ if (!cid) return undefined;
+ try {
+ const res = await listProjectInfoByContractId(cid);
+ const list = res.data ?? [];
+ const impl = list.find((p) => p.projectCategory !== PROJECT_CATEGORY_CONTRACT_ORDER);
+ if (impl?.projectId != null && String(impl.projectId) !== '') {
+ return impl.projectId;
+ }
+ } catch {
+ // 忽略:无实施项目时采购为空
+ }
+ return undefined;
+};
+
/** 加载采购信息 */
const loadPurchaseList = async () => {
- const contractId = orderInfo.value?.contractId ?? contractInfo.value?.contractId;
- if (!contractId && !projectId.value) {
+ if (!orderInfo.value) {
purchaseList.value = [];
purchaseTotal.value = 0;
purchaseMaterialList.value = [];
return;
}
- // 优先按合同ID查询采购
- purchaseQuery.relationId = contractId ? contractId : undefined;
+ const bizProjectId = await resolvePurchaseBizProjectId();
+ if (bizProjectId == null || bizProjectId === '') {
+ purchaseList.value = [];
+ purchaseTotal.value = 0;
+ purchaseMaterialList.value = [];
+ return;
+ }
+ purchaseQuery.projectId = bizProjectId;
+ purchaseQuery.relationId = undefined;
loadingPurchase.value = true;
try {
const [purchaseRes, materialRes] = await Promise.all([
listProjectPurchase({
...purchaseQuery
}),
- getContractOrderPurchaseMaterialList(projectId.value as string | number)
+ getContractOrderPurchaseMaterialList(bizProjectId)
]);
purchaseList.value = purchaseRes.rows || [];
purchaseTotal.value = purchaseRes.total || 0;
diff --git a/src/views/wms/wmsShippingBill/index.vue b/src/views/wms/wmsShippingBill/index.vue
index d558c6c..7cf7529 100644
--- a/src/views/wms/wmsShippingBill/index.vue
+++ b/src/views/wms/wmsShippingBill/index.vue
@@ -117,29 +117,24 @@
-
-
-
-
-
-
+
-
+
-
-
+
{{ proxy?.parseTime(scope.row.arrivalConfirmTime, '{y}-{m}-{d} {h}:{i}:{s}') || '-' }}
-
-
+
+
暂无附件
-
+
{{ proxy?.parseTime(scope.row.planArrivalTime, '{y}-{m}-{d}') }}
-
+
{{ proxy?.parseTime(scope.row.shippingTime, '{y}-{m}-{d}') }}
-
+
{{ proxy?.parseTime(scope.row.createTime, '{y}-{m}-{d}') }}
@@ -364,22 +359,21 @@ const dialog = reactive({
// 列显隐信息
const columns = ref([
{ key: 0, label: '发货单号', visible: true },
- { key: 1, label: 'SAP订单号', visible: true },
+ { key: 1, label: 'SAP订单号', visible: false },
{ key: 2, label: '发货类型', visible: true },
{ key: 3, label: '发货方式', visible: true },
{ key: 4, label: '项目名称', visible: false },
{ key: 5, label: '客户名称', visible: true },
{ key: 6, label: '供应商', visible: true },
{ key: 7, label: '发货单状态', visible: true },
- { key: 8, label: '发货状态', visible: true },
- { key: 9, label: '需到货确认', visible: true },
- { key: 10, label: '到货标识', visible: true },
- { key: 11, label: '到货确认时间', visible: true },
- { key: 12, label: '到货确认人', visible: false },
- { key: 13, label: '收货单附件', visible: true },
- { key: 14, label: '计划到货时间', visible: true },
- { key: 15, label: '实际发货时间', visible: true },
- { key: 16, label: '创建时间', visible: false }
+ { key: 8, label: '需到货确认', visible: true },
+ { key: 9, label: '到货标识', visible: true },
+ { key: 10, label: '到货确认时间', visible: true },
+ { key: 11, label: '到货确认人', visible: false },
+ { key: 12, label: '收货单附件', visible: true },
+ { key: 13, label: '计划到货时间', visible: true },
+ { key: 14, label: '实际发货时间', visible: true },
+ { key: 15, label: '创建时间', visible: false }
]);
const initFormData: WmsShippingBillForm = {