Multi-round offline negotiation workflow with vendors including face-to-face meetings, counter-offers, revised terms, and multi-level approvals before purchase order creation.
After bid evaluation, buyers may negotiate with shortlisted vendors to improve pricing, delivery terms, payment terms, warranty, and other commercial conditions. Negotiation can be conducted online (via vendor portal), or offline through face-to-face meetings, phone calls, or email correspondence. Each negotiation round is tracked with full audit trail, supporting multi-round cycles until terms are mutually agreed upon or escalated to higher authority.
If agreement cannot be reached within the configured maximum rounds, the negotiation is escalated to a higher authority (Manager, Head of Procurement, or Director) for resolution or vendor disqualification.
SERIAL - Primary KeyINTEGER - FK → bids.idINTEGER - Negotiation round (1, 2, 3...)VARCHAR - ONLINE | OFFLINE_MEETING | EMAIL | PHONEINTEGER - FK → users.id (buyer conducting negotiation)DECIMAL(18,2) - Original bid amountDECIMAL(18,2) - Vendor's counter-offer amountDECIMAL(18,2) - Final agreed amount for this roundTEXT - Updated payment termsTEXT - Updated delivery termsTEXT - Updated warranty termsTIMESTAMP - Scheduled meeting date/timeVARCHAR(255) - Meeting venue or virtual linkTEXT - Detailed meeting notes / minutesTEXT - Vendor's formal responseTIMESTAMP - When vendor respondedVARCHAR - INITIATED | COUNTER_OFFERED | ACCEPTED | REJECTED | ESCALATEDINTEGER - FK → users.id (final approver)TIMESTAMP - Approval timestampTIMESTAMP - Record creation timestampSERIAL - Primary KeyINTEGER - FK → bid_negotiations.idINTEGER - FK → rfq_items.idDECIMAL(18,2) - Original quoted unit priceDECIMAL(18,2) - Negotiated unit priceINTEGER - Original quantityINTEGER - Revised quantity (if changed)INTEGER - Original delivery timeline in daysINTEGER - Revised delivery timelineTEXT - Line-item specific remarksSERIAL - Primary KeyINTEGER - FK → bid_negotiations.idVARCHAR - MOM | REVISED_QUOTE | VENDOR_LETTER | COUNTER_OFFER | APPROVAL_NOTEVARCHAR(255) - Original file nameVARCHAR(500) - Storage path / blob URLBIGINT - File size in bytesINTEGER - FK → users.idTIMESTAMP - Upload timestampTEXT - Description or notes about the documentSERIAL - Primary KeyINTEGER - FK → bid_negotiations.idINTEGER - Level in approval hierarchy (1, 2, 3...)INTEGER - FK → users.idVARCHAR - PENDING | APPROVED | REJECTED | RETURNEDTEXT - Approver commentsTIMESTAMP - When action was takenAfter bid comparison, the buyer selects one or more shortlisted vendor(s) for negotiation. The system creates a negotiation record for Round 1 with the original bid amount captured.
-- Insert negotiation round 1 INSERT INTO procurement.bid_negotiations (bid_id, round_number, negotiation_mode, negotiated_by, original_amount, status, negotiated_at) VALUES (@bid_id, 1, 'OFFLINE_MEETING', @buyer_id, @original_bid_amount, 'INITIATED', NOW());
Set the meeting date, time, and location. The system sends a notification to the vendor with meeting details including agenda and expected discussion points.
-- Update meeting schedule UPDATE procurement.bid_negotiations SET meeting_date = @meeting_date, meeting_location = @meeting_location WHERE id = @negotiation_id;
During or after the meeting, record detailed meeting notes capturing all discussion points, vendor commitments, and agreed action items. Upload the Minutes of Meeting (MOM) document as an attachment.
-- Record meeting notes UPDATE procurement.bid_negotiations SET meeting_notes = @meeting_notes WHERE id = @negotiation_id; -- Upload MOM attachment INSERT INTO procurement.negotiation_attachments (negotiation_id, document_type, file_name, file_path, file_size, uploaded_by, uploaded_at) VALUES (@negotiation_id, 'MOM', @file_name, @file_path, @file_size, @user_id, NOW());
The vendor submits a counter-offer with revised amount and updated terms. Status transitions to COUNTER_OFFERED. Line-item level negotiations are tracked via the negotiation_items table for granular audit.
-- Update negotiation with counter-offer UPDATE procurement.bid_negotiations SET counter_amount = @counter_amount, revised_payment_terms = @payment_terms, revised_delivery_terms = @delivery_terms, revised_warranty_terms = @warranty_terms, vendor_response = 'Counter-offer submitted', vendor_response_date = NOW(), status = 'COUNTER_OFFERED' WHERE id = @negotiation_id; -- Insert line-item level negotiations INSERT INTO procurement.negotiation_items (negotiation_id, rfq_item_id, original_unit_price, negotiated_unit_price, original_quantity, negotiated_quantity, original_delivery_days, negotiated_delivery_days, remarks) VALUES (@negotiation_id, @rfq_item_id, @orig_price, @neg_price, @orig_qty, @neg_qty, @orig_days, @neg_days, @remarks);
The buyer reviews the counter-offer and decides to either accept the terms or initiate another negotiation round. If a new round is needed, round_number increments and a new record is created referencing the same bid.
-- Start Round 2 if counter-offer not satisfactory INSERT INTO procurement.bid_negotiations (bid_id, round_number, negotiation_mode, negotiated_by, original_amount, status, negotiated_at) SELECT bid_id, round_number + 1, @new_mode, @buyer_id, counter_amount, 'INITIATED', NOW() FROM procurement.bid_negotiations WHERE id = @previous_negotiation_id;
When both parties agree on the final negotiated amount and terms, the status is updated to ACCEPTED. All line items are finalized with the agreed unit prices, quantities, and delivery timelines.
-- Finalize negotiation agreement UPDATE procurement.bid_negotiations SET negotiated_amount = @final_amount, status = 'ACCEPTED' WHERE id = @negotiation_id;
The accepted negotiation is submitted for multi-level approval via the negotiation_approvals table. Each approval level is processed sequentially. Once all levels approve, the negotiation proceeds to Purchase Order creation.
-- Create approval chain INSERT INTO procurement.negotiation_approvals (negotiation_id, approval_level, approver_id, status) VALUES (@negotiation_id, 1, @level1_approver, 'PENDING'), (@negotiation_id, 2, @level2_approver, 'PENDING'), (@negotiation_id, 3, @level3_approver, 'PENDING'); -- Approve at a specific level UPDATE procurement.negotiation_approvals SET status = 'APPROVED', remarks = @approver_remarks, approved_at = NOW() WHERE negotiation_id = @negotiation_id AND approval_level = @current_level;
| Mode | Description | Use Case | Documents |
|---|---|---|---|
| ONLINE | Via vendor portal with real-time messaging | Minor price adjustments, quick clarifications | System-generated logs, chat transcripts |
| OFFLINE_MEETING | Face-to-face meeting at buyer or vendor premises | High-value items, complex specifications, strategic vendors | MOM, revised quotes, presentation decks |
| Email correspondence with formal communication trail | Term clarifications, formal counter-offers, documentation exchange | Email trail, attached documents, signed letters | |
| PHONE | Phone or video call discussion | Urgent negotiations, quick consensus building, time-sensitive deals | Call notes, follow-up confirmation emails |
| Status | Description | Next Actions |
|---|---|---|
| INITIATED | Negotiation round has been created and vendor has been notified | Schedule meeting, send terms to vendor |
| COUNTER_OFFERED | Vendor has submitted a counter-offer with revised pricing or terms | Accept, reject, escalate, or start new round |
| ACCEPTED | Both parties have agreed on the negotiated terms and amount | Submit for approval, proceed to PO creation |
| REJECTED | Negotiation terms rejected; vendor may be disqualified or re-negotiated | Start new round, select alternate vendor, close |
| ESCALATED | Negotiation escalated to higher authority due to stalemate or policy limits | Management review and decision |
Maximum number of negotiation rounds is configurable per organization (default: 3 rounds). After reaching the maximum, the negotiation must be either accepted at current terms, escalated, or the vendor is disqualified.
<5% discount: Buyer can approve directly | 5-10% discount: Procurement Manager approval required | 10-15% discount: Head of Procurement approval required | >15% discount: Director / VP approval mandatory
If no vendor response is received within a configurable number of days (default: 5 business days), the system automatically escalates the negotiation to the next authority level and sends reminder notifications to both buyer and vendor.
Complete audit trail is maintained for all negotiation rounds including timestamps, user actions, amount changes, document uploads, and approval decisions. All data is immutable once recorded and supports regulatory compliance requirements.
public class NegotiationService { private readonly IBidRepository _bidRepository; private readonly INegotiationRepository _negotiationRepository; private readonly INegotiationItemRepository _negotiationItemRepository; private readonly IApprovalRepository _approvalRepository; private readonly INotificationService _notificationService; /// <summary> /// Initiates a new negotiation round for a given bid. /// Automatically increments round number from last round. /// </summary> public async Task<NegotiationRound> InitiateNegotiation(int bidId, string mode, int negotiatedBy) { var bid = await _bidRepository.GetById(bidId); var lastRound = await _negotiationRepository.GetLastRound(bidId); var round = new BidNegotiation { BidId = bidId, RoundNumber = (lastRound?.RoundNumber ?? 0) + 1, NegotiationMode = mode, NegotiatedBy = negotiatedBy, OriginalAmount = bid.TotalAmount, Status = NegotiationStatus.Initiated, NegotiatedAt = DateTime.UtcNow }; await _negotiationRepository.Add(round); await _notificationService.NotifyVendor(bid.VendorId, "Negotiation initiated"); return round; } /// <summary> /// Records a vendor's counter-offer with revised amounts and line items. /// </summary> public async Task RecordCounterOffer(int negotiationId, decimal counterAmount, string revisedPaymentTerms, string revisedDeliveryTerms, List<NegotiationItemDto> items) { var negotiation = await _negotiationRepository.GetById(negotiationId); negotiation.CounterAmount = counterAmount; negotiation.RevisedPaymentTerms = revisedPaymentTerms; negotiation.RevisedDeliveryTerms = revisedDeliveryTerms; negotiation.VendorResponse = "Counter-offer submitted"; negotiation.VendorResponseDate = DateTime.UtcNow; negotiation.Status = NegotiationStatus.CounterOffered; foreach (var item in items) { await _negotiationItemRepository.Add(new NegotiationItem { NegotiationId = negotiationId, RfqItemId = item.RfqItemId, OriginalUnitPrice = item.OriginalUnitPrice, NegotiatedUnitPrice = item.NegotiatedUnitPrice, NegotiatedQuantity = item.NegotiatedQuantity, NegotiatedDeliveryDays = item.NegotiatedDeliveryDays }); } await _negotiationRepository.Update(negotiation); } /// <summary> /// Submits an accepted negotiation for multi-level approval. /// Only accepted negotiations can proceed to approval workflow. /// </summary> public async Task<bool> SubmitForApproval(int negotiationId, List<int> approverIds) { var negotiation = await _negotiationRepository.GetById(negotiationId); if (negotiation.Status != NegotiationStatus.Accepted) throw new InvalidOperationException("Only accepted negotiations can be submitted for approval"); for (int level = 1; level <= approverIds.Count; level++) { await _approvalRepository.Add(new NegotiationApproval { NegotiationId = negotiationId, ApprovalLevel = level, ApproverId = approverIds[level - 1], Status = level == 1 ? ApprovalStatus.Pending : ApprovalStatus.Pending }); } await _notificationService.NotifyApprover(approverIds[0], "Negotiation approval pending"); return true; } }
Always upload Minutes of Meeting (MOM) attachments for every offline negotiation session. This provides a verifiable record of discussions, commitments, and agreed action items for audit and dispute resolution.
Record negotiations at the individual line-item level using the negotiation_items table. This enables granular audit compliance and helps identify which specific items had price or term changes during negotiation.
Configure a maximum number of negotiation rounds (recommended: 3) to prevent indefinite negotiation cycles that delay procurement timelines. Enforce escalation or closure after the limit is reached.
Enforce multi-level approval workflows for negotiated discounts that exceed the buyer's authority level. This ensures financial governance and prevents unauthorized concessions.
Ensure all negotiation actions, status changes, document uploads, and approvals are timestamped and attributed to specific users. This supports regulatory compliance, internal audits, and dispute resolution.
Leverage the auto-escalation workflow when negotiations stall beyond configurable timeframes. This ensures timely resolution by involving higher authorities and prevents procurement bottlenecks.