拆分工单验收结算仓储
This commit is contained in:
@@ -1,46 +1,3 @@
|
|||||||
import { query, withTransaction } from '../../db/client.js'
|
|
||||||
import { offsetWorkerAfterSalesDebtsWithClient } from './after-sales-repo.js'
|
|
||||||
import {
|
|
||||||
ensureWorkerWalletWithClient,
|
|
||||||
getWorkerWalletWithClient,
|
|
||||||
toJsonString,
|
|
||||||
toPositiveInteger,
|
|
||||||
WORK_ORDER_SHARE_SELECT,
|
|
||||||
resolveWorkOrderShareCancellationStatus,
|
|
||||||
} from './shared.js'
|
|
||||||
import { createWorkOrderEventWithClient } from './work-order-event-repo.js'
|
|
||||||
import {
|
|
||||||
countTimeoutEventsByWorkerIds,
|
|
||||||
countWorkerCancellationsSince,
|
|
||||||
countWorkerActiveOrders,
|
|
||||||
countWorkerActiveOrdersWithClient,
|
|
||||||
countWorkerTimeoutEvents,
|
|
||||||
getWorkOrderById,
|
|
||||||
getWorkOrderByIdWithClient,
|
|
||||||
getWorkOrderByOrderItemId,
|
|
||||||
getWorkerOrderOverview,
|
|
||||||
isWorkOrderWithinHallCapacityWithClient,
|
|
||||||
listOverdueWorkOrders,
|
|
||||||
WORK_ORDER_SELECT,
|
|
||||||
} from './work-order-query-repo.js'
|
|
||||||
import type {
|
|
||||||
CreateWorkOrderInput,
|
|
||||||
GrabWorkOrderResult,
|
|
||||||
ListInput,
|
|
||||||
ProblemWorkOrderResolutionAction,
|
|
||||||
WorkerDepositUnfreezeRow,
|
|
||||||
WorkOrderRow,
|
|
||||||
WorkOrderShareRow,
|
|
||||||
WorkOrderStatistics,
|
|
||||||
} from './types.js'
|
|
||||||
import type { PoolClient } from 'pg'
|
|
||||||
import { maybeUpgradeWorkerLevelWithClient } from './worker-ranking-repo.js'
|
|
||||||
import {
|
|
||||||
enqueueDepositUnfreezeWithClient,
|
|
||||||
getOutstandingDepositAmountWithClient,
|
|
||||||
resolveOutstandingDepositAmount,
|
|
||||||
} from './work-order-deposit-repo.js'
|
|
||||||
|
|
||||||
export * from './work-order-query-repo.js'
|
export * from './work-order-query-repo.js'
|
||||||
export { sumPendingUnfreezeByOrderIds } from './work-order-deposit-query-repo.js'
|
export { sumPendingUnfreezeByOrderIds } from './work-order-deposit-query-repo.js'
|
||||||
export { listDueDepositUnfreezes } from './work-order-deposit-query-repo.js'
|
export { listDueDepositUnfreezes } from './work-order-deposit-query-repo.js'
|
||||||
@@ -73,701 +30,8 @@ export {
|
|||||||
resolveProblemWorkOrder,
|
resolveProblemWorkOrder,
|
||||||
settleOverdueWorkOrder,
|
settleOverdueWorkOrder,
|
||||||
} from './work-order-issue-repo.js'
|
} from './work-order-issue-repo.js'
|
||||||
|
export {
|
||||||
export async function acceptWorkOrderAndSettle(input: {
|
acceptWorkOrderAndSettle,
|
||||||
workOrderId: number
|
acceptWorkOrderShareAndSettle,
|
||||||
unfreezeDays: number
|
cancelWorkOrderShare,
|
||||||
now: string
|
} from './work-order-settlement-repo.js'
|
||||||
/** 操作人类型:admin / worker / system */
|
|
||||||
actorType?: string
|
|
||||||
/** 操作人显示名 */
|
|
||||||
actorName?: string
|
|
||||||
}): Promise<{
|
|
||||||
order: WorkOrderRow | null
|
|
||||||
failureReason: 'sharing_pending_submissions' | null
|
|
||||||
}> {
|
|
||||||
return withTransaction(async (client) => {
|
|
||||||
const currentResult = await client.query<WorkOrderRow>(
|
|
||||||
`
|
|
||||||
SELECT *
|
|
||||||
FROM work_orders
|
|
||||||
WHERE id = $1
|
|
||||||
AND status = 'pending_acceptance'
|
|
||||||
FOR UPDATE
|
|
||||||
`,
|
|
||||||
[input.workOrderId],
|
|
||||||
)
|
|
||||||
const workOrder = currentResult.rows[0] || null
|
|
||||||
if (!workOrder) {
|
|
||||||
return { order: null, failureReason: null }
|
|
||||||
}
|
|
||||||
|
|
||||||
const sharesResult = await client.query<WorkOrderShareRow>(
|
|
||||||
`
|
|
||||||
SELECT *
|
|
||||||
FROM work_order_shares
|
|
||||||
WHERE work_order_id = $1
|
|
||||||
AND status != 'cancelled'
|
|
||||||
ORDER BY id ASC
|
|
||||||
FOR UPDATE
|
|
||||||
`,
|
|
||||||
[input.workOrderId],
|
|
||||||
)
|
|
||||||
const shares = sharesResult.rows
|
|
||||||
if (shares.length > 0) {
|
|
||||||
const pendingShares = shares.filter((share) => share.status === 'joined')
|
|
||||||
if (pendingShares.length > 0) {
|
|
||||||
return {
|
|
||||||
order: null,
|
|
||||||
failureReason: 'sharing_pending_submissions',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
UPDATE work_orders
|
|
||||||
SET status = 'accepted', accepted_at = $1, updated_at = $1
|
|
||||||
WHERE id = $2
|
|
||||||
`,
|
|
||||||
[input.now, input.workOrderId],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (shares.length > 0) {
|
|
||||||
// 单独验收过的份额已经完成资金结算,统一验收时只处理尚未结算的份额。
|
|
||||||
const unsettledShares = shares.filter((share) => share.status !== 'accepted')
|
|
||||||
for (const share of unsettledShares) {
|
|
||||||
const shareWorkerId = Number(share.worker_id || 0)
|
|
||||||
if (!shareWorkerId) continue
|
|
||||||
await ensureWorkerWalletWithClient(client, shareWorkerId, input.now)
|
|
||||||
const wallet = await getWorkerWalletWithClient(client, shareWorkerId)
|
|
||||||
const releaseAmount = Math.min(
|
|
||||||
Number(share.share_deposit || 0),
|
|
||||||
Number(wallet?.frozen_deposit_amount || 0),
|
|
||||||
)
|
|
||||||
const shareReward = Number(share.share_reward || 0)
|
|
||||||
const debtOffsets = await offsetWorkerAfterSalesDebtsWithClient(client, {
|
|
||||||
workerId: shareWorkerId,
|
|
||||||
amount: shareReward,
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
const debtOffsetAmount = debtOffsets.reduce((total, item) => total + item.amount, 0)
|
|
||||||
const shouldDelayUnfreeze = input.unfreezeDays > 0
|
|
||||||
const nextAvailable =
|
|
||||||
Number(wallet?.available_amount || 0) +
|
|
||||||
shareReward +
|
|
||||||
(shouldDelayUnfreeze ? 0 : releaseAmount) -
|
|
||||||
debtOffsetAmount
|
|
||||||
const nextFrozen = Math.max(0, Number(wallet?.frozen_deposit_amount || 0) - releaseAmount)
|
|
||||||
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
UPDATE worker_wallets
|
|
||||||
SET
|
|
||||||
available_amount = $1,
|
|
||||||
frozen_deposit_amount = $2,
|
|
||||||
pending_unfreeze_amount = pending_unfreeze_amount + $3,
|
|
||||||
total_settled_amount = total_settled_amount + $4,
|
|
||||||
updated_at = $5
|
|
||||||
WHERE worker_id = $6
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
shouldDelayUnfreeze ? releaseAmount : 0,
|
|
||||||
shareReward,
|
|
||||||
input.now,
|
|
||||||
shareWorkerId,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (releaseAmount > 0) {
|
|
||||||
if (shouldDelayUnfreeze) {
|
|
||||||
await enqueueDepositUnfreezeWithClient(client, {
|
|
||||||
workerId: shareWorkerId,
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
amount: releaseAmount,
|
|
||||||
unfreezeDays: input.unfreezeDays,
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'deposit_pending_unfreeze', $2, $3, $4, $5, '拼单验收押金待解冻', $6::jsonb, $7)
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
shareWorkerId,
|
|
||||||
releaseAmount,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
JSON.stringify({
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
shareId: Number(share.id),
|
|
||||||
unfreezeDays: input.unfreezeDays,
|
|
||||||
}),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'deposit_release', $2, $3, $4, $5, '拼单验收释放押金', $6::jsonb, $7)
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
shareWorkerId,
|
|
||||||
releaseAmount,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
JSON.stringify({ workOrderId: input.workOrderId, shareId: Number(share.id) }),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shareReward > 0) {
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'sharing_reward', $2, $3, $4, $5, $6, $7::jsonb, $8)
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
shareWorkerId,
|
|
||||||
shareReward,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
`拼单结算报酬 ${share.quantity} 份`,
|
|
||||||
JSON.stringify({
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
shareId: Number(share.id),
|
|
||||||
quantity: share.quantity,
|
|
||||||
}),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for (const debtOffset of debtOffsets) {
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'after_sales_debt_offset', $2, $3, $4, $5, '验收报酬自动抵扣售后欠款', $6::jsonb, $7)
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
shareWorkerId,
|
|
||||||
-debtOffset.amount,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
JSON.stringify({
|
|
||||||
caseId: debtOffset.caseId,
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
shareId: Number(share.id),
|
|
||||||
}),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
UPDATE work_order_shares
|
|
||||||
SET status = 'accepted', accepted_at = $1, updated_at = $1
|
|
||||||
WHERE id = $2
|
|
||||||
`,
|
|
||||||
[input.now, Number(share.id)],
|
|
||||||
)
|
|
||||||
await maybeUpgradeWorkerLevelWithClient(client, shareWorkerId, input.now)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const workerId = Number(workOrder.assigned_worker_id || 0)
|
|
||||||
if (workerId > 0) {
|
|
||||||
await ensureWorkerWalletWithClient(client, workerId, input.now)
|
|
||||||
const wallet = await getWorkerWalletWithClient(client, workerId)
|
|
||||||
const releaseAmount = Math.min(
|
|
||||||
await getOutstandingDepositAmountWithClient(client, workerId, input.workOrderId),
|
|
||||||
Number(wallet?.frozen_deposit_amount || 0),
|
|
||||||
)
|
|
||||||
const rewardAmount = Number(workOrder.reward_amount || 0)
|
|
||||||
const debtOffsets = await offsetWorkerAfterSalesDebtsWithClient(client, {
|
|
||||||
workerId,
|
|
||||||
amount: rewardAmount,
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
const debtOffsetAmount = debtOffsets.reduce((total, item) => total + item.amount, 0)
|
|
||||||
const shouldDelayUnfreeze = input.unfreezeDays > 0
|
|
||||||
const nextAvailable =
|
|
||||||
Number(wallet?.available_amount || 0) +
|
|
||||||
rewardAmount +
|
|
||||||
(shouldDelayUnfreeze ? 0 : releaseAmount) -
|
|
||||||
debtOffsetAmount
|
|
||||||
const nextFrozen = Math.max(0, Number(wallet?.frozen_deposit_amount || 0) - releaseAmount)
|
|
||||||
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
UPDATE worker_wallets
|
|
||||||
SET
|
|
||||||
available_amount = $1,
|
|
||||||
frozen_deposit_amount = $2,
|
|
||||||
pending_unfreeze_amount = pending_unfreeze_amount + $3,
|
|
||||||
total_settled_amount = total_settled_amount + $4,
|
|
||||||
updated_at = $5
|
|
||||||
WHERE worker_id = $6
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
shouldDelayUnfreeze ? releaseAmount : 0,
|
|
||||||
rewardAmount,
|
|
||||||
input.now,
|
|
||||||
workerId,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (releaseAmount > 0) {
|
|
||||||
if (shouldDelayUnfreeze) {
|
|
||||||
await enqueueDepositUnfreezeWithClient(client, {
|
|
||||||
workerId,
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
amount: releaseAmount,
|
|
||||||
unfreezeDays: input.unfreezeDays,
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'deposit_pending_unfreeze', $2, $3, $4, $5, '验收通过押金待解冻', $6::jsonb, $7)
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
workerId,
|
|
||||||
releaseAmount,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
JSON.stringify({
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
unfreezeDays: input.unfreezeDays,
|
|
||||||
}),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'deposit_release', $2, $3, $4, $5, '验收通过释放押金', '{}'::jsonb, $6)
|
|
||||||
`,
|
|
||||||
[workerId, releaseAmount, nextAvailable, nextFrozen, input.workOrderId, input.now],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rewardAmount > 0) {
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'reward_settlement', $2, $3, $4, $5, '验收通过结算报酬', '{}'::jsonb, $6)
|
|
||||||
`,
|
|
||||||
[workerId, rewardAmount, nextAvailable, nextFrozen, input.workOrderId, input.now],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for (const debtOffset of debtOffsets) {
|
|
||||||
await client.query(
|
|
||||||
`
|
|
||||||
INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'after_sales_debt_offset', $2, $3, $4, $5, '验收报酬自动抵扣售后欠款', $6::jsonb, $7)
|
|
||||||
`,
|
|
||||||
[
|
|
||||||
workerId,
|
|
||||||
-debtOffset.amount,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
JSON.stringify({ caseId: debtOffset.caseId, workOrderId: input.workOrderId }),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await createWorkOrderEventWithClient(client, {
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
actorType: input.actorType || 'admin',
|
|
||||||
actorId: input.actorName || '',
|
|
||||||
eventType: 'accepted',
|
|
||||||
fromStatus: 'pending_acceptance',
|
|
||||||
toStatus: 'accepted',
|
|
||||||
payloadJson: JSON.stringify({
|
|
||||||
shareCount: shares.length,
|
|
||||||
settledShareCount: shares.filter((share) => share.status !== 'accepted').length,
|
|
||||||
}),
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (workerId > 0) {
|
|
||||||
await maybeUpgradeWorkerLevelWithClient(client, workerId, input.now)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
order: await getWorkOrderByIdWithClient(client, input.workOrderId),
|
|
||||||
failureReason: null,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 单独验收一个拼单份额,只结算该参与者,不影响其他份额。 */
|
|
||||||
export async function acceptWorkOrderShareAndSettle(input: {
|
|
||||||
workOrderId: number
|
|
||||||
shareId: number
|
|
||||||
unfreezeDays: number
|
|
||||||
now: string
|
|
||||||
actorType?: string
|
|
||||||
actorName?: string
|
|
||||||
}): Promise<{
|
|
||||||
order: WorkOrderRow | null
|
|
||||||
share: WorkOrderShareRow | null
|
|
||||||
failureReason: 'work_order_not_accepting' | 'share_not_submitted' | null
|
|
||||||
}> {
|
|
||||||
return withTransaction(async (client) => {
|
|
||||||
const orderResult = await client.query<WorkOrderRow>(
|
|
||||||
`SELECT * FROM work_orders WHERE id = $1 AND status IN ('open', 'pending_acceptance') FOR UPDATE`,
|
|
||||||
[input.workOrderId],
|
|
||||||
)
|
|
||||||
const workOrder = orderResult.rows[0] || null
|
|
||||||
if (!workOrder) {
|
|
||||||
return { order: null, share: null, failureReason: 'work_order_not_accepting' }
|
|
||||||
}
|
|
||||||
|
|
||||||
const shareResult = await client.query<WorkOrderShareRow>(
|
|
||||||
`SELECT * FROM work_order_shares WHERE id = $1 AND work_order_id = $2 FOR UPDATE`,
|
|
||||||
[input.shareId, input.workOrderId],
|
|
||||||
)
|
|
||||||
const share = shareResult.rows[0] || null
|
|
||||||
if (!share || share.status !== 'submitted') {
|
|
||||||
return { order: workOrder, share, failureReason: 'share_not_submitted' }
|
|
||||||
}
|
|
||||||
|
|
||||||
const workerId = Number(share.worker_id || 0)
|
|
||||||
if (workerId > 0) {
|
|
||||||
await ensureWorkerWalletWithClient(client, workerId, input.now)
|
|
||||||
const wallet = await getWorkerWalletWithClient(client, workerId)
|
|
||||||
const releaseAmount = Math.min(
|
|
||||||
Number(share.share_deposit || 0),
|
|
||||||
Number(wallet?.frozen_deposit_amount || 0),
|
|
||||||
)
|
|
||||||
const shareReward = Number(share.share_reward || 0)
|
|
||||||
const debtOffsets = await offsetWorkerAfterSalesDebtsWithClient(client, {
|
|
||||||
workerId,
|
|
||||||
amount: shareReward,
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
const debtOffsetAmount = debtOffsets.reduce((total, item) => total + item.amount, 0)
|
|
||||||
const shouldDelayUnfreeze = input.unfreezeDays > 0
|
|
||||||
const nextAvailable =
|
|
||||||
Number(wallet?.available_amount || 0) +
|
|
||||||
shareReward +
|
|
||||||
(shouldDelayUnfreeze ? 0 : releaseAmount) -
|
|
||||||
debtOffsetAmount
|
|
||||||
const nextFrozen = Math.max(0, Number(wallet?.frozen_deposit_amount || 0) - releaseAmount)
|
|
||||||
await client.query(
|
|
||||||
`UPDATE worker_wallets
|
|
||||||
SET available_amount = $1, frozen_deposit_amount = $2,
|
|
||||||
pending_unfreeze_amount = pending_unfreeze_amount + $3,
|
|
||||||
total_settled_amount = total_settled_amount + $4, updated_at = $5
|
|
||||||
WHERE worker_id = $6`,
|
|
||||||
[
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
shouldDelayUnfreeze ? releaseAmount : 0,
|
|
||||||
shareReward,
|
|
||||||
input.now,
|
|
||||||
workerId,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
if (releaseAmount > 0 && shouldDelayUnfreeze) {
|
|
||||||
await enqueueDepositUnfreezeWithClient(client, {
|
|
||||||
workerId,
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
amount: releaseAmount,
|
|
||||||
unfreezeDays: input.unfreezeDays,
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if (releaseAmount > 0) {
|
|
||||||
await client.query(
|
|
||||||
`INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9)`,
|
|
||||||
[
|
|
||||||
workerId,
|
|
||||||
shouldDelayUnfreeze ? 'deposit_pending_unfreeze' : 'deposit_release',
|
|
||||||
releaseAmount,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
shouldDelayUnfreeze ? '拼单份额验收押金待解冻' : '拼单份额验收释放押金',
|
|
||||||
JSON.stringify({ workOrderId: input.workOrderId, shareId: input.shareId }),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (shareReward > 0) {
|
|
||||||
await client.query(
|
|
||||||
`INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'sharing_reward', $2, $3, $4, $5, $6, $7::jsonb, $8)`,
|
|
||||||
[
|
|
||||||
workerId,
|
|
||||||
shareReward,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
`拼单份额验收结算报酬 ${share.quantity} 份`,
|
|
||||||
JSON.stringify({ workOrderId: input.workOrderId, shareId: input.shareId }),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
for (const debtOffset of debtOffsets) {
|
|
||||||
await client.query(
|
|
||||||
`INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'after_sales_debt_offset', $2, $3, $4, $5, '验收报酬自动抵扣售后欠款', $6::jsonb, $7)`,
|
|
||||||
[
|
|
||||||
workerId,
|
|
||||||
-debtOffset.amount,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
JSON.stringify({
|
|
||||||
caseId: debtOffset.caseId,
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
shareId: input.shareId,
|
|
||||||
}),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.query(
|
|
||||||
`UPDATE work_order_shares SET status = 'accepted', accepted_at = $1, updated_at = $1 WHERE id = $2`,
|
|
||||||
[input.now, input.shareId],
|
|
||||||
)
|
|
||||||
if (workerId > 0) {
|
|
||||||
// 先落库份额已验收状态,再按去重后的订单数计算等级进度。
|
|
||||||
await maybeUpgradeWorkerLevelWithClient(client, workerId, input.now)
|
|
||||||
}
|
|
||||||
const progressResult = await client.query<{
|
|
||||||
joined_quantity: number
|
|
||||||
unfinished_count: number
|
|
||||||
}>(
|
|
||||||
`SELECT COALESCE(SUM(quantity), 0)::int AS joined_quantity,
|
|
||||||
COUNT(*) FILTER (WHERE status != 'accepted' AND status != 'cancelled')::int AS unfinished_count
|
|
||||||
FROM work_order_shares WHERE work_order_id = $1 AND status != 'cancelled'`,
|
|
||||||
[input.workOrderId],
|
|
||||||
)
|
|
||||||
const progress = progressResult.rows[0]
|
|
||||||
const filled =
|
|
||||||
Number(progress?.joined_quantity || 0) >= Number(workOrder.sharing_total_quantity || 1)
|
|
||||||
const allAccepted = Number(progress?.unfinished_count || 0) === 0
|
|
||||||
const nextOrderStatus = filled && allAccepted ? 'accepted' : workOrder.status
|
|
||||||
if (nextOrderStatus === 'accepted') {
|
|
||||||
await client.query(
|
|
||||||
`UPDATE work_orders SET status = 'accepted', accepted_at = $1, updated_at = $1 WHERE id = $2`,
|
|
||||||
[input.now, input.workOrderId],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
await createWorkOrderEventWithClient(client, {
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
actorType: input.actorType || 'admin',
|
|
||||||
actorId: input.actorName || '',
|
|
||||||
eventType: 'sharing_share_accepted',
|
|
||||||
fromStatus: workOrder.status,
|
|
||||||
toStatus: nextOrderStatus,
|
|
||||||
payloadJson: JSON.stringify({ shareId: input.shareId, workerId }),
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
const updatedOrder = await getWorkOrderByIdWithClient(client, input.workOrderId)
|
|
||||||
const updatedShareResult = await client.query<WorkOrderShareRow>(
|
|
||||||
`${WORK_ORDER_SHARE_SELECT} WHERE wos.id = $1 LIMIT 1`,
|
|
||||||
[input.shareId],
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
order: updatedOrder,
|
|
||||||
share: updatedShareResult.rows[0] || null,
|
|
||||||
failureReason: null,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 后台撤销单个拼单份额,释放该打手押金并把份数重新放回大厅。 */
|
|
||||||
export async function cancelWorkOrderShare(input: {
|
|
||||||
workOrderId: number
|
|
||||||
shareId: number
|
|
||||||
now: string
|
|
||||||
reason?: string
|
|
||||||
actorName?: string
|
|
||||||
}): Promise<{
|
|
||||||
order: WorkOrderRow | null
|
|
||||||
share: WorkOrderShareRow | null
|
|
||||||
releasedDepositAmount: number
|
|
||||||
failureReason:
|
|
||||||
| 'work_order_not_cancellable'
|
|
||||||
| 'share_not_found'
|
|
||||||
| 'share_status_not_cancellable'
|
|
||||||
| null
|
|
||||||
}> {
|
|
||||||
return withTransaction(async (client) => {
|
|
||||||
const orderResult = await client.query<WorkOrderRow>(
|
|
||||||
`SELECT * FROM work_orders WHERE id = $1 FOR UPDATE`,
|
|
||||||
[input.workOrderId],
|
|
||||||
)
|
|
||||||
const workOrder = orderResult.rows[0] || null
|
|
||||||
if (!workOrder || !['open', 'pending_acceptance'].includes(workOrder.status)) {
|
|
||||||
return {
|
|
||||||
order: workOrder,
|
|
||||||
share: null,
|
|
||||||
releasedDepositAmount: 0,
|
|
||||||
failureReason: 'work_order_not_cancellable',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const shareResult = await client.query<WorkOrderShareRow>(
|
|
||||||
`SELECT * FROM work_order_shares WHERE id = $1 AND work_order_id = $2 FOR UPDATE`,
|
|
||||||
[input.shareId, input.workOrderId],
|
|
||||||
)
|
|
||||||
const share = shareResult.rows[0] || null
|
|
||||||
if (!share) {
|
|
||||||
return {
|
|
||||||
order: workOrder,
|
|
||||||
share: null,
|
|
||||||
releasedDepositAmount: 0,
|
|
||||||
failureReason: 'share_not_found',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const nextOrderStatus = resolveWorkOrderShareCancellationStatus(workOrder.status, share.status)
|
|
||||||
if (!nextOrderStatus) {
|
|
||||||
return {
|
|
||||||
order: workOrder,
|
|
||||||
share,
|
|
||||||
releasedDepositAmount: 0,
|
|
||||||
failureReason: 'share_status_not_cancellable',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const workerId = Number(share.worker_id || 0)
|
|
||||||
let releasedDepositAmount = 0
|
|
||||||
if (workerId > 0) {
|
|
||||||
await ensureWorkerWalletWithClient(client, workerId, input.now)
|
|
||||||
const wallet = await getWorkerWalletWithClient(client, workerId)
|
|
||||||
releasedDepositAmount = Math.min(
|
|
||||||
Number(share.share_deposit || 0),
|
|
||||||
Number(wallet?.frozen_deposit_amount || 0),
|
|
||||||
)
|
|
||||||
if (releasedDepositAmount > 0) {
|
|
||||||
const nextAvailable = Number(wallet?.available_amount || 0) + releasedDepositAmount
|
|
||||||
const nextFrozen = Math.max(
|
|
||||||
0,
|
|
||||||
Number(wallet?.frozen_deposit_amount || 0) - releasedDepositAmount,
|
|
||||||
)
|
|
||||||
await client.query(
|
|
||||||
`UPDATE worker_wallets
|
|
||||||
SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
|
|
||||||
WHERE worker_id = $4`,
|
|
||||||
[nextAvailable, nextFrozen, input.now, workerId],
|
|
||||||
)
|
|
||||||
await client.query(
|
|
||||||
`INSERT INTO worker_wallet_ledgers (
|
|
||||||
worker_id, ledger_type, amount, balance_after, frozen_after,
|
|
||||||
related_work_order_id, note, payload_json, created_at
|
|
||||||
) VALUES ($1, 'deposit_release', $2, $3, $4, $5, '后台撤销拼单份额退还押金', $6::jsonb, $7)`,
|
|
||||||
[
|
|
||||||
workerId,
|
|
||||||
releasedDepositAmount,
|
|
||||||
nextAvailable,
|
|
||||||
nextFrozen,
|
|
||||||
input.workOrderId,
|
|
||||||
JSON.stringify({
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
shareId: input.shareId,
|
|
||||||
reason: String(input.reason || '').trim(),
|
|
||||||
}),
|
|
||||||
input.now,
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await client.query(
|
|
||||||
`UPDATE work_order_shares
|
|
||||||
SET status = 'cancelled', updated_at = $1
|
|
||||||
WHERE id = $2`,
|
|
||||||
[input.now, input.shareId],
|
|
||||||
)
|
|
||||||
await client.query(
|
|
||||||
`UPDATE work_orders
|
|
||||||
SET status = $1,
|
|
||||||
submitted_at = NULL,
|
|
||||||
published_at = COALESCE(published_at, $2),
|
|
||||||
updated_at = $2
|
|
||||||
WHERE id = $3`,
|
|
||||||
[nextOrderStatus, input.now, input.workOrderId],
|
|
||||||
)
|
|
||||||
await createWorkOrderEventWithClient(client, {
|
|
||||||
workOrderId: input.workOrderId,
|
|
||||||
actorType: 'admin',
|
|
||||||
actorId: input.actorName || '',
|
|
||||||
eventType: 'sharing_share_cancelled_by_admin',
|
|
||||||
fromStatus: workOrder.status,
|
|
||||||
toStatus: nextOrderStatus,
|
|
||||||
payloadJson: JSON.stringify({
|
|
||||||
shareId: input.shareId,
|
|
||||||
workerId,
|
|
||||||
quantity: Number(share.quantity || 0),
|
|
||||||
releasedDepositAmount,
|
|
||||||
reason: String(input.reason || '').trim(),
|
|
||||||
}),
|
|
||||||
now: input.now,
|
|
||||||
})
|
|
||||||
|
|
||||||
const updatedShareResult = await client.query<WorkOrderShareRow>(
|
|
||||||
`${WORK_ORDER_SHARE_SELECT} WHERE wos.id = $1 LIMIT 1`,
|
|
||||||
[input.shareId],
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
order: await getWorkOrderByIdWithClient(client, input.workOrderId),
|
|
||||||
share: updatedShareResult.rows[0] || null,
|
|
||||||
releasedDepositAmount,
|
|
||||||
failureReason: null,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,713 @@
|
|||||||
|
import { withTransaction } from '../../db/client.js'
|
||||||
|
import { offsetWorkerAfterSalesDebtsWithClient } from './after-sales-repo.js'
|
||||||
|
import {
|
||||||
|
ensureWorkerWalletWithClient,
|
||||||
|
getWorkerWalletWithClient,
|
||||||
|
WORK_ORDER_SHARE_SELECT,
|
||||||
|
resolveWorkOrderShareCancellationStatus,
|
||||||
|
} from './shared.js'
|
||||||
|
import { createWorkOrderEventWithClient } from './work-order-event-repo.js'
|
||||||
|
import { getWorkOrderByIdWithClient } from './work-order-query-repo.js'
|
||||||
|
import {
|
||||||
|
enqueueDepositUnfreezeWithClient,
|
||||||
|
getOutstandingDepositAmountWithClient,
|
||||||
|
} from './work-order-deposit-repo.js'
|
||||||
|
import { maybeUpgradeWorkerLevelWithClient } from './worker-ranking-repo.js'
|
||||||
|
import type { WorkOrderRow, WorkOrderShareRow } from './types.js'
|
||||||
|
|
||||||
|
export async function acceptWorkOrderAndSettle(input: {
|
||||||
|
workOrderId: number
|
||||||
|
unfreezeDays: number
|
||||||
|
now: string
|
||||||
|
/** 操作人类型:admin / worker / system */
|
||||||
|
actorType?: string
|
||||||
|
/** 操作人显示名 */
|
||||||
|
actorName?: string
|
||||||
|
}): Promise<{
|
||||||
|
order: WorkOrderRow | null
|
||||||
|
failureReason: 'sharing_pending_submissions' | null
|
||||||
|
}> {
|
||||||
|
return withTransaction(async (client) => {
|
||||||
|
const currentResult = await client.query<WorkOrderRow>(
|
||||||
|
`
|
||||||
|
SELECT *
|
||||||
|
FROM work_orders
|
||||||
|
WHERE id = $1
|
||||||
|
AND status = 'pending_acceptance'
|
||||||
|
FOR UPDATE
|
||||||
|
`,
|
||||||
|
[input.workOrderId],
|
||||||
|
)
|
||||||
|
const workOrder = currentResult.rows[0] || null
|
||||||
|
if (!workOrder) {
|
||||||
|
return { order: null, failureReason: null }
|
||||||
|
}
|
||||||
|
|
||||||
|
const sharesResult = await client.query<WorkOrderShareRow>(
|
||||||
|
`
|
||||||
|
SELECT *
|
||||||
|
FROM work_order_shares
|
||||||
|
WHERE work_order_id = $1
|
||||||
|
AND status != 'cancelled'
|
||||||
|
ORDER BY id ASC
|
||||||
|
FOR UPDATE
|
||||||
|
`,
|
||||||
|
[input.workOrderId],
|
||||||
|
)
|
||||||
|
const shares = sharesResult.rows
|
||||||
|
if (shares.length > 0) {
|
||||||
|
const pendingShares = shares.filter((share) => share.status === 'joined')
|
||||||
|
if (pendingShares.length > 0) {
|
||||||
|
return {
|
||||||
|
order: null,
|
||||||
|
failureReason: 'sharing_pending_submissions',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
UPDATE work_orders
|
||||||
|
SET status = 'accepted', accepted_at = $1, updated_at = $1
|
||||||
|
WHERE id = $2
|
||||||
|
`,
|
||||||
|
[input.now, input.workOrderId],
|
||||||
|
)
|
||||||
|
|
||||||
|
if (shares.length > 0) {
|
||||||
|
// 单独验收过的份额已经完成资金结算,统一验收时只处理尚未结算的份额。
|
||||||
|
const unsettledShares = shares.filter((share) => share.status !== 'accepted')
|
||||||
|
for (const share of unsettledShares) {
|
||||||
|
const shareWorkerId = Number(share.worker_id || 0)
|
||||||
|
if (!shareWorkerId) continue
|
||||||
|
await ensureWorkerWalletWithClient(client, shareWorkerId, input.now)
|
||||||
|
const wallet = await getWorkerWalletWithClient(client, shareWorkerId)
|
||||||
|
const releaseAmount = Math.min(
|
||||||
|
Number(share.share_deposit || 0),
|
||||||
|
Number(wallet?.frozen_deposit_amount || 0),
|
||||||
|
)
|
||||||
|
const shareReward = Number(share.share_reward || 0)
|
||||||
|
const debtOffsets = await offsetWorkerAfterSalesDebtsWithClient(client, {
|
||||||
|
workerId: shareWorkerId,
|
||||||
|
amount: shareReward,
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
const debtOffsetAmount = debtOffsets.reduce((total, item) => total + item.amount, 0)
|
||||||
|
const shouldDelayUnfreeze = input.unfreezeDays > 0
|
||||||
|
const nextAvailable =
|
||||||
|
Number(wallet?.available_amount || 0) +
|
||||||
|
shareReward +
|
||||||
|
(shouldDelayUnfreeze ? 0 : releaseAmount) -
|
||||||
|
debtOffsetAmount
|
||||||
|
const nextFrozen = Math.max(0, Number(wallet?.frozen_deposit_amount || 0) - releaseAmount)
|
||||||
|
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
UPDATE worker_wallets
|
||||||
|
SET
|
||||||
|
available_amount = $1,
|
||||||
|
frozen_deposit_amount = $2,
|
||||||
|
pending_unfreeze_amount = pending_unfreeze_amount + $3,
|
||||||
|
total_settled_amount = total_settled_amount + $4,
|
||||||
|
updated_at = $5
|
||||||
|
WHERE worker_id = $6
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
shouldDelayUnfreeze ? releaseAmount : 0,
|
||||||
|
shareReward,
|
||||||
|
input.now,
|
||||||
|
shareWorkerId,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
if (releaseAmount > 0) {
|
||||||
|
if (shouldDelayUnfreeze) {
|
||||||
|
await enqueueDepositUnfreezeWithClient(client, {
|
||||||
|
workerId: shareWorkerId,
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
amount: releaseAmount,
|
||||||
|
unfreezeDays: input.unfreezeDays,
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'deposit_pending_unfreeze', $2, $3, $4, $5, '拼单验收押金待解冻', $6::jsonb, $7)
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
shareWorkerId,
|
||||||
|
releaseAmount,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
JSON.stringify({
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
shareId: Number(share.id),
|
||||||
|
unfreezeDays: input.unfreezeDays,
|
||||||
|
}),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'deposit_release', $2, $3, $4, $5, '拼单验收释放押金', $6::jsonb, $7)
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
shareWorkerId,
|
||||||
|
releaseAmount,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
JSON.stringify({ workOrderId: input.workOrderId, shareId: Number(share.id) }),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shareReward > 0) {
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'sharing_reward', $2, $3, $4, $5, $6, $7::jsonb, $8)
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
shareWorkerId,
|
||||||
|
shareReward,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
`拼单结算报酬 ${share.quantity} 份`,
|
||||||
|
JSON.stringify({
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
shareId: Number(share.id),
|
||||||
|
quantity: share.quantity,
|
||||||
|
}),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (const debtOffset of debtOffsets) {
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'after_sales_debt_offset', $2, $3, $4, $5, '验收报酬自动抵扣售后欠款', $6::jsonb, $7)
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
shareWorkerId,
|
||||||
|
-debtOffset.amount,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
JSON.stringify({
|
||||||
|
caseId: debtOffset.caseId,
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
shareId: Number(share.id),
|
||||||
|
}),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
UPDATE work_order_shares
|
||||||
|
SET status = 'accepted', accepted_at = $1, updated_at = $1
|
||||||
|
WHERE id = $2
|
||||||
|
`,
|
||||||
|
[input.now, Number(share.id)],
|
||||||
|
)
|
||||||
|
await maybeUpgradeWorkerLevelWithClient(client, shareWorkerId, input.now)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const workerId = Number(workOrder.assigned_worker_id || 0)
|
||||||
|
if (workerId > 0) {
|
||||||
|
await ensureWorkerWalletWithClient(client, workerId, input.now)
|
||||||
|
const wallet = await getWorkerWalletWithClient(client, workerId)
|
||||||
|
const releaseAmount = Math.min(
|
||||||
|
await getOutstandingDepositAmountWithClient(client, workerId, input.workOrderId),
|
||||||
|
Number(wallet?.frozen_deposit_amount || 0),
|
||||||
|
)
|
||||||
|
const rewardAmount = Number(workOrder.reward_amount || 0)
|
||||||
|
const debtOffsets = await offsetWorkerAfterSalesDebtsWithClient(client, {
|
||||||
|
workerId,
|
||||||
|
amount: rewardAmount,
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
const debtOffsetAmount = debtOffsets.reduce((total, item) => total + item.amount, 0)
|
||||||
|
const shouldDelayUnfreeze = input.unfreezeDays > 0
|
||||||
|
const nextAvailable =
|
||||||
|
Number(wallet?.available_amount || 0) +
|
||||||
|
rewardAmount +
|
||||||
|
(shouldDelayUnfreeze ? 0 : releaseAmount) -
|
||||||
|
debtOffsetAmount
|
||||||
|
const nextFrozen = Math.max(0, Number(wallet?.frozen_deposit_amount || 0) - releaseAmount)
|
||||||
|
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
UPDATE worker_wallets
|
||||||
|
SET
|
||||||
|
available_amount = $1,
|
||||||
|
frozen_deposit_amount = $2,
|
||||||
|
pending_unfreeze_amount = pending_unfreeze_amount + $3,
|
||||||
|
total_settled_amount = total_settled_amount + $4,
|
||||||
|
updated_at = $5
|
||||||
|
WHERE worker_id = $6
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
shouldDelayUnfreeze ? releaseAmount : 0,
|
||||||
|
rewardAmount,
|
||||||
|
input.now,
|
||||||
|
workerId,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
if (releaseAmount > 0) {
|
||||||
|
if (shouldDelayUnfreeze) {
|
||||||
|
await enqueueDepositUnfreezeWithClient(client, {
|
||||||
|
workerId,
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
amount: releaseAmount,
|
||||||
|
unfreezeDays: input.unfreezeDays,
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'deposit_pending_unfreeze', $2, $3, $4, $5, '验收通过押金待解冻', $6::jsonb, $7)
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
workerId,
|
||||||
|
releaseAmount,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
JSON.stringify({
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
unfreezeDays: input.unfreezeDays,
|
||||||
|
}),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'deposit_release', $2, $3, $4, $5, '验收通过释放押金', '{}'::jsonb, $6)
|
||||||
|
`,
|
||||||
|
[workerId, releaseAmount, nextAvailable, nextFrozen, input.workOrderId, input.now],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rewardAmount > 0) {
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'reward_settlement', $2, $3, $4, $5, '验收通过结算报酬', '{}'::jsonb, $6)
|
||||||
|
`,
|
||||||
|
[workerId, rewardAmount, nextAvailable, nextFrozen, input.workOrderId, input.now],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (const debtOffset of debtOffsets) {
|
||||||
|
await client.query(
|
||||||
|
`
|
||||||
|
INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'after_sales_debt_offset', $2, $3, $4, $5, '验收报酬自动抵扣售后欠款', $6::jsonb, $7)
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
workerId,
|
||||||
|
-debtOffset.amount,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
JSON.stringify({ caseId: debtOffset.caseId, workOrderId: input.workOrderId }),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await createWorkOrderEventWithClient(client, {
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
actorType: input.actorType || 'admin',
|
||||||
|
actorId: input.actorName || '',
|
||||||
|
eventType: 'accepted',
|
||||||
|
fromStatus: 'pending_acceptance',
|
||||||
|
toStatus: 'accepted',
|
||||||
|
payloadJson: JSON.stringify({
|
||||||
|
shareCount: shares.length,
|
||||||
|
settledShareCount: shares.filter((share) => share.status !== 'accepted').length,
|
||||||
|
}),
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (workerId > 0) {
|
||||||
|
await maybeUpgradeWorkerLevelWithClient(client, workerId, input.now)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
order: await getWorkOrderByIdWithClient(client, input.workOrderId),
|
||||||
|
failureReason: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 单独验收一个拼单份额,只结算该参与者,不影响其他份额。 */
|
||||||
|
export async function acceptWorkOrderShareAndSettle(input: {
|
||||||
|
workOrderId: number
|
||||||
|
shareId: number
|
||||||
|
unfreezeDays: number
|
||||||
|
now: string
|
||||||
|
actorType?: string
|
||||||
|
actorName?: string
|
||||||
|
}): Promise<{
|
||||||
|
order: WorkOrderRow | null
|
||||||
|
share: WorkOrderShareRow | null
|
||||||
|
failureReason: 'work_order_not_accepting' | 'share_not_submitted' | null
|
||||||
|
}> {
|
||||||
|
return withTransaction(async (client) => {
|
||||||
|
const orderResult = await client.query<WorkOrderRow>(
|
||||||
|
`SELECT * FROM work_orders WHERE id = $1 AND status IN ('open', 'pending_acceptance') FOR UPDATE`,
|
||||||
|
[input.workOrderId],
|
||||||
|
)
|
||||||
|
const workOrder = orderResult.rows[0] || null
|
||||||
|
if (!workOrder) {
|
||||||
|
return { order: null, share: null, failureReason: 'work_order_not_accepting' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const shareResult = await client.query<WorkOrderShareRow>(
|
||||||
|
`SELECT * FROM work_order_shares WHERE id = $1 AND work_order_id = $2 FOR UPDATE`,
|
||||||
|
[input.shareId, input.workOrderId],
|
||||||
|
)
|
||||||
|
const share = shareResult.rows[0] || null
|
||||||
|
if (!share || share.status !== 'submitted') {
|
||||||
|
return { order: workOrder, share, failureReason: 'share_not_submitted' }
|
||||||
|
}
|
||||||
|
|
||||||
|
const workerId = Number(share.worker_id || 0)
|
||||||
|
if (workerId > 0) {
|
||||||
|
await ensureWorkerWalletWithClient(client, workerId, input.now)
|
||||||
|
const wallet = await getWorkerWalletWithClient(client, workerId)
|
||||||
|
const releaseAmount = Math.min(
|
||||||
|
Number(share.share_deposit || 0),
|
||||||
|
Number(wallet?.frozen_deposit_amount || 0),
|
||||||
|
)
|
||||||
|
const shareReward = Number(share.share_reward || 0)
|
||||||
|
const debtOffsets = await offsetWorkerAfterSalesDebtsWithClient(client, {
|
||||||
|
workerId,
|
||||||
|
amount: shareReward,
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
const debtOffsetAmount = debtOffsets.reduce((total, item) => total + item.amount, 0)
|
||||||
|
const shouldDelayUnfreeze = input.unfreezeDays > 0
|
||||||
|
const nextAvailable =
|
||||||
|
Number(wallet?.available_amount || 0) +
|
||||||
|
shareReward +
|
||||||
|
(shouldDelayUnfreeze ? 0 : releaseAmount) -
|
||||||
|
debtOffsetAmount
|
||||||
|
const nextFrozen = Math.max(0, Number(wallet?.frozen_deposit_amount || 0) - releaseAmount)
|
||||||
|
await client.query(
|
||||||
|
`UPDATE worker_wallets
|
||||||
|
SET available_amount = $1, frozen_deposit_amount = $2,
|
||||||
|
pending_unfreeze_amount = pending_unfreeze_amount + $3,
|
||||||
|
total_settled_amount = total_settled_amount + $4, updated_at = $5
|
||||||
|
WHERE worker_id = $6`,
|
||||||
|
[
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
shouldDelayUnfreeze ? releaseAmount : 0,
|
||||||
|
shareReward,
|
||||||
|
input.now,
|
||||||
|
workerId,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
if (releaseAmount > 0 && shouldDelayUnfreeze) {
|
||||||
|
await enqueueDepositUnfreezeWithClient(client, {
|
||||||
|
workerId,
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
amount: releaseAmount,
|
||||||
|
unfreezeDays: input.unfreezeDays,
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (releaseAmount > 0) {
|
||||||
|
await client.query(
|
||||||
|
`INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9)`,
|
||||||
|
[
|
||||||
|
workerId,
|
||||||
|
shouldDelayUnfreeze ? 'deposit_pending_unfreeze' : 'deposit_release',
|
||||||
|
releaseAmount,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
shouldDelayUnfreeze ? '拼单份额验收押金待解冻' : '拼单份额验收释放押金',
|
||||||
|
JSON.stringify({ workOrderId: input.workOrderId, shareId: input.shareId }),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (shareReward > 0) {
|
||||||
|
await client.query(
|
||||||
|
`INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'sharing_reward', $2, $3, $4, $5, $6, $7::jsonb, $8)`,
|
||||||
|
[
|
||||||
|
workerId,
|
||||||
|
shareReward,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
`拼单份额验收结算报酬 ${share.quantity} 份`,
|
||||||
|
JSON.stringify({ workOrderId: input.workOrderId, shareId: input.shareId }),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (const debtOffset of debtOffsets) {
|
||||||
|
await client.query(
|
||||||
|
`INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'after_sales_debt_offset', $2, $3, $4, $5, '验收报酬自动抵扣售后欠款', $6::jsonb, $7)`,
|
||||||
|
[
|
||||||
|
workerId,
|
||||||
|
-debtOffset.amount,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
JSON.stringify({
|
||||||
|
caseId: debtOffset.caseId,
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
shareId: input.shareId,
|
||||||
|
}),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.query(
|
||||||
|
`UPDATE work_order_shares SET status = 'accepted', accepted_at = $1, updated_at = $1 WHERE id = $2`,
|
||||||
|
[input.now, input.shareId],
|
||||||
|
)
|
||||||
|
if (workerId > 0) {
|
||||||
|
// 先落库份额已验收状态,再按去重后的订单数计算等级进度。
|
||||||
|
await maybeUpgradeWorkerLevelWithClient(client, workerId, input.now)
|
||||||
|
}
|
||||||
|
const progressResult = await client.query<{
|
||||||
|
joined_quantity: number
|
||||||
|
unfinished_count: number
|
||||||
|
}>(
|
||||||
|
`SELECT COALESCE(SUM(quantity), 0)::int AS joined_quantity,
|
||||||
|
COUNT(*) FILTER (WHERE status != 'accepted' AND status != 'cancelled')::int AS unfinished_count
|
||||||
|
FROM work_order_shares WHERE work_order_id = $1 AND status != 'cancelled'`,
|
||||||
|
[input.workOrderId],
|
||||||
|
)
|
||||||
|
const progress = progressResult.rows[0]
|
||||||
|
const filled =
|
||||||
|
Number(progress?.joined_quantity || 0) >= Number(workOrder.sharing_total_quantity || 1)
|
||||||
|
const allAccepted = Number(progress?.unfinished_count || 0) === 0
|
||||||
|
const nextOrderStatus = filled && allAccepted ? 'accepted' : workOrder.status
|
||||||
|
if (nextOrderStatus === 'accepted') {
|
||||||
|
await client.query(
|
||||||
|
`UPDATE work_orders SET status = 'accepted', accepted_at = $1, updated_at = $1 WHERE id = $2`,
|
||||||
|
[input.now, input.workOrderId],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
await createWorkOrderEventWithClient(client, {
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
actorType: input.actorType || 'admin',
|
||||||
|
actorId: input.actorName || '',
|
||||||
|
eventType: 'sharing_share_accepted',
|
||||||
|
fromStatus: workOrder.status,
|
||||||
|
toStatus: nextOrderStatus,
|
||||||
|
payloadJson: JSON.stringify({ shareId: input.shareId, workerId }),
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
const updatedOrder = await getWorkOrderByIdWithClient(client, input.workOrderId)
|
||||||
|
const updatedShareResult = await client.query<WorkOrderShareRow>(
|
||||||
|
`${WORK_ORDER_SHARE_SELECT} WHERE wos.id = $1 LIMIT 1`,
|
||||||
|
[input.shareId],
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
order: updatedOrder,
|
||||||
|
share: updatedShareResult.rows[0] || null,
|
||||||
|
failureReason: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 后台撤销单个拼单份额,释放该打手押金并把份数重新放回大厅。 */
|
||||||
|
export async function cancelWorkOrderShare(input: {
|
||||||
|
workOrderId: number
|
||||||
|
shareId: number
|
||||||
|
now: string
|
||||||
|
reason?: string
|
||||||
|
actorName?: string
|
||||||
|
}): Promise<{
|
||||||
|
order: WorkOrderRow | null
|
||||||
|
share: WorkOrderShareRow | null
|
||||||
|
releasedDepositAmount: number
|
||||||
|
failureReason:
|
||||||
|
| 'work_order_not_cancellable'
|
||||||
|
| 'share_not_found'
|
||||||
|
| 'share_status_not_cancellable'
|
||||||
|
| null
|
||||||
|
}> {
|
||||||
|
return withTransaction(async (client) => {
|
||||||
|
const orderResult = await client.query<WorkOrderRow>(
|
||||||
|
`SELECT * FROM work_orders WHERE id = $1 FOR UPDATE`,
|
||||||
|
[input.workOrderId],
|
||||||
|
)
|
||||||
|
const workOrder = orderResult.rows[0] || null
|
||||||
|
if (!workOrder || !['open', 'pending_acceptance'].includes(workOrder.status)) {
|
||||||
|
return {
|
||||||
|
order: workOrder,
|
||||||
|
share: null,
|
||||||
|
releasedDepositAmount: 0,
|
||||||
|
failureReason: 'work_order_not_cancellable',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const shareResult = await client.query<WorkOrderShareRow>(
|
||||||
|
`SELECT * FROM work_order_shares WHERE id = $1 AND work_order_id = $2 FOR UPDATE`,
|
||||||
|
[input.shareId, input.workOrderId],
|
||||||
|
)
|
||||||
|
const share = shareResult.rows[0] || null
|
||||||
|
if (!share) {
|
||||||
|
return {
|
||||||
|
order: workOrder,
|
||||||
|
share: null,
|
||||||
|
releasedDepositAmount: 0,
|
||||||
|
failureReason: 'share_not_found',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const nextOrderStatus = resolveWorkOrderShareCancellationStatus(workOrder.status, share.status)
|
||||||
|
if (!nextOrderStatus) {
|
||||||
|
return {
|
||||||
|
order: workOrder,
|
||||||
|
share,
|
||||||
|
releasedDepositAmount: 0,
|
||||||
|
failureReason: 'share_status_not_cancellable',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const workerId = Number(share.worker_id || 0)
|
||||||
|
let releasedDepositAmount = 0
|
||||||
|
if (workerId > 0) {
|
||||||
|
await ensureWorkerWalletWithClient(client, workerId, input.now)
|
||||||
|
const wallet = await getWorkerWalletWithClient(client, workerId)
|
||||||
|
releasedDepositAmount = Math.min(
|
||||||
|
Number(share.share_deposit || 0),
|
||||||
|
Number(wallet?.frozen_deposit_amount || 0),
|
||||||
|
)
|
||||||
|
if (releasedDepositAmount > 0) {
|
||||||
|
const nextAvailable = Number(wallet?.available_amount || 0) + releasedDepositAmount
|
||||||
|
const nextFrozen = Math.max(
|
||||||
|
0,
|
||||||
|
Number(wallet?.frozen_deposit_amount || 0) - releasedDepositAmount,
|
||||||
|
)
|
||||||
|
await client.query(
|
||||||
|
`UPDATE worker_wallets
|
||||||
|
SET available_amount = $1, frozen_deposit_amount = $2, updated_at = $3
|
||||||
|
WHERE worker_id = $4`,
|
||||||
|
[nextAvailable, nextFrozen, input.now, workerId],
|
||||||
|
)
|
||||||
|
await client.query(
|
||||||
|
`INSERT INTO worker_wallet_ledgers (
|
||||||
|
worker_id, ledger_type, amount, balance_after, frozen_after,
|
||||||
|
related_work_order_id, note, payload_json, created_at
|
||||||
|
) VALUES ($1, 'deposit_release', $2, $3, $4, $5, '后台撤销拼单份额退还押金', $6::jsonb, $7)`,
|
||||||
|
[
|
||||||
|
workerId,
|
||||||
|
releasedDepositAmount,
|
||||||
|
nextAvailable,
|
||||||
|
nextFrozen,
|
||||||
|
input.workOrderId,
|
||||||
|
JSON.stringify({
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
shareId: input.shareId,
|
||||||
|
reason: String(input.reason || '').trim(),
|
||||||
|
}),
|
||||||
|
input.now,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.query(
|
||||||
|
`UPDATE work_order_shares
|
||||||
|
SET status = 'cancelled', updated_at = $1
|
||||||
|
WHERE id = $2`,
|
||||||
|
[input.now, input.shareId],
|
||||||
|
)
|
||||||
|
await client.query(
|
||||||
|
`UPDATE work_orders
|
||||||
|
SET status = $1,
|
||||||
|
submitted_at = NULL,
|
||||||
|
published_at = COALESCE(published_at, $2),
|
||||||
|
updated_at = $2
|
||||||
|
WHERE id = $3`,
|
||||||
|
[nextOrderStatus, input.now, input.workOrderId],
|
||||||
|
)
|
||||||
|
await createWorkOrderEventWithClient(client, {
|
||||||
|
workOrderId: input.workOrderId,
|
||||||
|
actorType: 'admin',
|
||||||
|
actorId: input.actorName || '',
|
||||||
|
eventType: 'sharing_share_cancelled_by_admin',
|
||||||
|
fromStatus: workOrder.status,
|
||||||
|
toStatus: nextOrderStatus,
|
||||||
|
payloadJson: JSON.stringify({
|
||||||
|
shareId: input.shareId,
|
||||||
|
workerId,
|
||||||
|
quantity: Number(share.quantity || 0),
|
||||||
|
releasedDepositAmount,
|
||||||
|
reason: String(input.reason || '').trim(),
|
||||||
|
}),
|
||||||
|
now: input.now,
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedShareResult = await client.query<WorkOrderShareRow>(
|
||||||
|
`${WORK_ORDER_SHARE_SELECT} WHERE wos.id = $1 LIMIT 1`,
|
||||||
|
[input.shareId],
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
order: await getWorkOrderByIdWithClient(client, input.workOrderId),
|
||||||
|
share: updatedShareResult.rows[0] || null,
|
||||||
|
releasedDepositAmount,
|
||||||
|
failureReason: null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user