修复问题单退回后拼单份额无法验收
This commit is contained in:
@@ -56,7 +56,8 @@ export function resolveWorkOrderShareCancellationStatus(
|
|||||||
workOrderStatus: string,
|
workOrderStatus: string,
|
||||||
shareStatus: string,
|
shareStatus: string,
|
||||||
): 'open' | null {
|
): 'open' | null {
|
||||||
if (!['open', 'pending_acceptance'].includes(workOrderStatus)) return null
|
// in_progress 用于解围历史问题单处置产生的"份额已提交、母单回到进行中"的卡死存量单
|
||||||
|
if (!['open', 'pending_acceptance', 'in_progress'].includes(workOrderStatus)) return null
|
||||||
if (!['joined', 'submitted'].includes(shareStatus)) return null
|
if (!['joined', 'submitted'].includes(shareStatus)) return null
|
||||||
return 'open'
|
return 'open'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ import { getOutstandingDepositAmountWithClient } from './work-order-deposit-repo
|
|||||||
import { RESET_GIFT_FLOW_SQL } from './work-order-gift-repo.js'
|
import { RESET_GIFT_FLOW_SQL } from './work-order-gift-repo.js'
|
||||||
import type { ProblemWorkOrderResolutionAction, WorkOrderRow } from './types.js'
|
import type { ProblemWorkOrderResolutionAction, WorkOrderRow } from './types.js'
|
||||||
|
|
||||||
|
/** 问题单退回打手时,按标记前状态恢复;异常历史数据回退单人单进行中。 */
|
||||||
|
export function resolveProblemReturnToWorkerStatus(
|
||||||
|
fromStatus: string,
|
||||||
|
): 'open' | 'in_progress' | 'pending_acceptance' {
|
||||||
|
if (['open', 'in_progress', 'pending_acceptance'].includes(fromStatus)) {
|
||||||
|
return fromStatus as 'open' | 'in_progress' | 'pending_acceptance'
|
||||||
|
}
|
||||||
|
return 'in_progress'
|
||||||
|
}
|
||||||
|
|
||||||
export async function resolveProblemWorkOrder(input: {
|
export async function resolveProblemWorkOrder(input: {
|
||||||
workOrderId: number
|
workOrderId: number
|
||||||
action: ProblemWorkOrderResolutionAction
|
action: ProblemWorkOrderResolutionAction
|
||||||
@@ -27,6 +37,26 @@ export async function resolveProblemWorkOrder(input: {
|
|||||||
const workOrder = currentResult.rows[0] || null
|
const workOrder = currentResult.rows[0] || null
|
||||||
if (!workOrder) return null
|
if (!workOrder) return null
|
||||||
|
|
||||||
|
// return_to_worker 需恢复到标记问题单前的真实状态:拼单份额已全部提交时
|
||||||
|
// 标记前是 pending_acceptance,若一律回到 in_progress 会陷入"份额已提交、
|
||||||
|
// 母单却不可验收也不可重新提交"的卡死状态,因此按最近一次 marked_problem
|
||||||
|
// 事件的 from_status 恢复。
|
||||||
|
let restoreStatus = ''
|
||||||
|
if (input.action === 'return_to_worker') {
|
||||||
|
const eventResult = await client.query<{ from_status: string }>(
|
||||||
|
`
|
||||||
|
SELECT from_status
|
||||||
|
FROM work_order_events
|
||||||
|
WHERE work_order_id = $1 AND event_type = 'marked_problem'
|
||||||
|
ORDER BY id DESC
|
||||||
|
LIMIT 1
|
||||||
|
`,
|
||||||
|
[input.workOrderId],
|
||||||
|
)
|
||||||
|
const fromStatus = String(eventResult.rows[0]?.from_status || '').trim()
|
||||||
|
restoreStatus = resolveProblemReturnToWorkerStatus(fromStatus)
|
||||||
|
}
|
||||||
|
|
||||||
const shouldReleaseDeposit = ['reopen', 'cancel_release'].includes(input.action)
|
const shouldReleaseDeposit = ['reopen', 'cancel_release'].includes(input.action)
|
||||||
const shouldDeductDeposit = input.action === 'cancel_deduct'
|
const shouldDeductDeposit = input.action === 'cancel_deduct'
|
||||||
const workerId = Number(workOrder.assigned_worker_id || 0)
|
const workerId = Number(workOrder.assigned_worker_id || 0)
|
||||||
@@ -83,12 +113,16 @@ export async function resolveProblemWorkOrder(input: {
|
|||||||
|
|
||||||
const targetStatus =
|
const targetStatus =
|
||||||
input.action === 'return_to_worker'
|
input.action === 'return_to_worker'
|
||||||
? 'in_progress'
|
? restoreStatus || 'in_progress'
|
||||||
: input.action === 'reopen'
|
: input.action === 'reopen'
|
||||||
? 'open'
|
? 'open'
|
||||||
: 'cancelled'
|
: 'cancelled'
|
||||||
const shouldClearAssignment = input.action === 'reopen'
|
const shouldClearAssignment = input.action === 'reopen'
|
||||||
const shouldClearSubmission = ['return_to_worker', 'reopen'].includes(input.action)
|
// 仅在真正退回打手重做(回到进行中)时清验收提交;份额已提交待验收的拼单
|
||||||
|
// 需保留母单待验收状态,否则份额验收链路断裂。
|
||||||
|
const shouldClearSubmission =
|
||||||
|
input.action === 'reopen' ||
|
||||||
|
(input.action === 'return_to_worker' && targetStatus === 'in_progress')
|
||||||
|
|
||||||
await client.query(
|
await client.query(
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -392,8 +392,10 @@ export async function acceptWorkOrderShareAndSettle(input: {
|
|||||||
failureReason: 'work_order_not_accepting' | 'share_not_submitted' | null
|
failureReason: 'work_order_not_accepting' | 'share_not_submitted' | null
|
||||||
}> {
|
}> {
|
||||||
return withTransaction(async (client) => {
|
return withTransaction(async (client) => {
|
||||||
|
// in_progress 仅用于解围历史问题单处置产生的"份额已提交、母单却回到进行中"
|
||||||
|
// 的卡死存量单;正常拼单流程母单只会是 open / pending_acceptance。
|
||||||
const orderResult = await client.query<WorkOrderRow>(
|
const orderResult = await client.query<WorkOrderRow>(
|
||||||
`SELECT * FROM work_orders WHERE id = $1 AND status IN ('open', 'pending_acceptance') FOR UPDATE`,
|
`SELECT * FROM work_orders WHERE id = $1 AND status IN ('open', 'pending_acceptance', 'in_progress') FOR UPDATE`,
|
||||||
[input.workOrderId],
|
[input.workOrderId],
|
||||||
)
|
)
|
||||||
const workOrder = orderResult.rows[0] || null
|
const workOrder = orderResult.rows[0] || null
|
||||||
@@ -592,7 +594,7 @@ export async function cancelWorkOrderShare(input: {
|
|||||||
[input.workOrderId],
|
[input.workOrderId],
|
||||||
)
|
)
|
||||||
const workOrder = orderResult.rows[0] || null
|
const workOrder = orderResult.rows[0] || null
|
||||||
if (!workOrder || !['open', 'pending_acceptance'].includes(workOrder.status)) {
|
if (!workOrder || !['open', 'pending_acceptance', 'in_progress'].includes(workOrder.status)) {
|
||||||
return {
|
return {
|
||||||
order: workOrder,
|
order: workOrder,
|
||||||
share: null,
|
share: null,
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ export async function submitWorkOrderShareAcceptance(input: {
|
|||||||
now: string
|
now: string
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
share: WorkOrderShareRow | null
|
share: WorkOrderShareRow | null
|
||||||
failureReason: 'share_not_found' | 'share_status_invalid' | null
|
failureReason: 'share_not_found' | 'share_status_invalid' | 'work_order_status_invalid' | null
|
||||||
}> {
|
}> {
|
||||||
return withTransaction(async (client) => {
|
return withTransaction(async (client) => {
|
||||||
const orderResult = await client.query<{
|
const orderResult = await client.query<{
|
||||||
@@ -309,9 +309,12 @@ export async function submitWorkOrderShareAcceptance(input: {
|
|||||||
[input.workOrderId],
|
[input.workOrderId],
|
||||||
)
|
)
|
||||||
const workOrder = orderResult.rows[0] || null
|
const workOrder = orderResult.rows[0] || null
|
||||||
if (!workOrder || !['open', 'pending_acceptance'].includes(workOrder.status)) {
|
if (!workOrder) {
|
||||||
return { share: null, failureReason: 'share_not_found' }
|
return { share: null, failureReason: 'share_not_found' }
|
||||||
}
|
}
|
||||||
|
if (!['open', 'pending_acceptance'].includes(workOrder.status)) {
|
||||||
|
return { share: null, failureReason: 'work_order_status_invalid' }
|
||||||
|
}
|
||||||
|
|
||||||
const lockResult = await client.query<{ id: number }>(
|
const lockResult = await client.query<{ id: number }>(
|
||||||
`
|
`
|
||||||
|
|||||||
@@ -435,6 +435,12 @@ async function submitWorkerSharingAcceptance(
|
|||||||
errorCode: 'work_order_sharing_share_submitted',
|
errorCode: 'work_order_sharing_share_submitted',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (result.failureReason === 'work_order_status_invalid') {
|
||||||
|
throw createHttpError('当前拼单状态不能提交验收资料,请联系管理员', {
|
||||||
|
statusCode: 409,
|
||||||
|
errorCode: 'work_order_sharing_submit_status_invalid',
|
||||||
|
})
|
||||||
|
}
|
||||||
if (!result.share) {
|
if (!result.share) {
|
||||||
throw createHttpError('未找到拼单份额', {
|
throw createHttpError('未找到拼单份额', {
|
||||||
statusCode: 404,
|
statusCode: 404,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
resolveWorkOrderShareJoinQuantity,
|
resolveWorkOrderShareJoinQuantity,
|
||||||
resolveOpenSharingSyncReward,
|
resolveOpenSharingSyncReward,
|
||||||
} from '../../repositories/worker-platform/index.js'
|
} from '../../repositories/worker-platform/index.js'
|
||||||
|
import { resolveProblemReturnToWorkerStatus } from '../../repositories/worker-platform/work-order-issue-repo.js'
|
||||||
import type { OrderItemRow, OrderRow } from '../../types/repository/rows.js'
|
import type { OrderItemRow, OrderRow } from '../../types/repository/rows.js'
|
||||||
import {
|
import {
|
||||||
assertWithdrawChannelAllowedForAmount,
|
assertWithdrawChannelAllowedForAmount,
|
||||||
@@ -1045,6 +1046,13 @@ test('拼单份额撤单只允许未验收份额并让母工单回到大厅状
|
|||||||
assert.equal(resolveWorkOrderShareCancellationStatus('open', 'cancelled'), null)
|
assert.equal(resolveWorkOrderShareCancellationStatus('open', 'cancelled'), null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('问题单退回打手按标记前状态恢复', () => {
|
||||||
|
assert.equal(resolveProblemReturnToWorkerStatus('pending_acceptance'), 'pending_acceptance')
|
||||||
|
assert.equal(resolveProblemReturnToWorkerStatus('open'), 'open')
|
||||||
|
assert.equal(resolveProblemReturnToWorkerStatus('in_progress'), 'in_progress')
|
||||||
|
assert.equal(resolveProblemReturnToWorkerStatus(''), 'in_progress')
|
||||||
|
})
|
||||||
|
|
||||||
test('后台更新资料时普通接单可以修改接单价格', () => {
|
test('后台更新资料时普通接单可以修改接单价格', () => {
|
||||||
assert.equal(
|
assert.equal(
|
||||||
resolveAdminMaterialRewardAmount({
|
resolveAdminMaterialRewardAmount({
|
||||||
|
|||||||
Reference in New Issue
Block a user