Reject reason added for PO
This commit is contained in:
parent
70deacb969
commit
7516354ce5
@ -239,7 +239,7 @@ Each sub-master supports: `GET /` (list), `GET /:id`, `POST /`, `PUT /:id`, `DEL
|
||||
| [x] | DELETE | `/purchase-orders/:id` | delete | Soft delete |
|
||||
| [x] | POST | `/purchase-orders/:id/submit` | edit | Submit for approval |
|
||||
| [x] | POST | `/purchase-orders/:id/approve` | approve | Approve PO |
|
||||
| [x] | POST | `/purchase-orders/:id/reject` | approve | Reject PO |
|
||||
| [x] | POST | `/purchase-orders/:id/reject` | approve | Reject PO (`reject_reason` required; stored on PO) |
|
||||
| [x] | POST | `/purchase-orders/:id/amend` | edit | Amend PO |
|
||||
| [x] | POST | `/purchase-orders/:id/cancel` | edit | Cancel PO |
|
||||
| [x] | GET | `/purchase-orders/:id/pdf` | view | PDF export |
|
||||
|
||||
@ -711,6 +711,7 @@ model purchase_orders {
|
||||
parent_po_id BigInt?
|
||||
terms_and_conditions String?
|
||||
remarks String?
|
||||
reject_reason String?
|
||||
is_active Boolean @default(true)
|
||||
created_by BigInt?
|
||||
updated_by BigInt?
|
||||
|
||||
4
scripts/patch-po-reject-reason.sql
Normal file
4
scripts/patch-po-reject-reason.sql
Normal file
@ -0,0 +1,4 @@
|
||||
-- Persist PO rejection reason on purchase_orders header
|
||||
|
||||
ALTER TABLE purchase_orders
|
||||
ADD COLUMN IF NOT EXISTS reject_reason TEXT;
|
||||
@ -67,9 +67,10 @@ components:
|
||||
remarks: { type: string, example: 'Approved for procurement' }
|
||||
PurchaseOrdersRejectBody:
|
||||
type: object
|
||||
required: [remarks]
|
||||
required: [reject_reason]
|
||||
properties:
|
||||
remarks: { type: string, example: 'Rates not competitive' }
|
||||
reject_reason: { type: string, example: 'Rates not competitive' }
|
||||
remarks: { type: string, example: 'Rates not competitive', description: 'Legacy alias for reject_reason' }
|
||||
PoAttachmentResponse:
|
||||
type: object
|
||||
properties:
|
||||
@ -247,6 +248,7 @@ paths:
|
||||
post:
|
||||
tags: [Purchase Orders]
|
||||
summary: Reject purchase order
|
||||
description: Requires reject_reason. Stored on purchase_orders.reject_reason and cleared on resubmit/approve.
|
||||
parameters:
|
||||
- { name: id, in: path, required: true, schema: { type: string, example: '1' } }
|
||||
requestBody:
|
||||
|
||||
@ -697,6 +697,7 @@ const submitPurchaseOrder = async (id, payload, userId, requestId) => {
|
||||
where: { id: BigInt(id) },
|
||||
data: {
|
||||
status: 'PENDING_APPROVAL',
|
||||
reject_reason: null,
|
||||
remarks: payload.remarks ?? existing.remarks,
|
||||
updated_by: userId ? BigInt(userId) : null,
|
||||
},
|
||||
@ -739,6 +740,7 @@ const approvePurchaseOrder = async (id, payload, userId, requestId) => {
|
||||
where: { id: BigInt(id) },
|
||||
data: {
|
||||
status: 'APPROVED',
|
||||
reject_reason: null,
|
||||
updated_by: userId ? BigInt(userId) : null,
|
||||
},
|
||||
include: poDetailInclude,
|
||||
@ -765,12 +767,15 @@ const rejectPurchaseOrder = async (id, payload, userId, requestId) => {
|
||||
const pendingApproval = existing.po_approvals.find((row) => row.status === 'PENDING');
|
||||
if (!pendingApproval) throw new ApiError(409, 'No pending approval step found');
|
||||
|
||||
const rejectReason = String(payload.reject_reason || payload.remarks || '').trim();
|
||||
if (!rejectReason) throw new ApiError(422, 'reject_reason is required');
|
||||
|
||||
const updated = await prisma.$transaction(async (tx) => {
|
||||
await tx.po_approvals.update({
|
||||
where: { id: pendingApproval.id },
|
||||
data: {
|
||||
status: 'REJECTED',
|
||||
remarks: payload.remarks,
|
||||
remarks: rejectReason,
|
||||
approver_user_id: userId ? BigInt(userId) : null,
|
||||
acted_at: new Date(),
|
||||
},
|
||||
@ -780,6 +785,7 @@ const rejectPurchaseOrder = async (id, payload, userId, requestId) => {
|
||||
where: { id: BigInt(id) },
|
||||
data: {
|
||||
status: 'REJECTED',
|
||||
reject_reason: rejectReason,
|
||||
updated_by: userId ? BigInt(userId) : null,
|
||||
},
|
||||
include: poDetailInclude,
|
||||
|
||||
@ -111,8 +111,13 @@ const workflowRemarksSchema = Joi.object({
|
||||
});
|
||||
|
||||
const rejectPurchaseOrderSchema = Joi.object({
|
||||
remarks: Joi.string().trim().min(1).required(),
|
||||
});
|
||||
reject_reason: Joi.string().trim().min(1).optional(),
|
||||
remarks: Joi.string().trim().min(1).optional(), // legacy alias for reject_reason
|
||||
})
|
||||
.or('reject_reason', 'remarks')
|
||||
.messages({
|
||||
'object.missing': 'reject_reason is required',
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
createPurchaseOrderSchema,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user