Back to ER Diagram
Offline Negotiation

Offline Negotiation Cycle

Multi-round offline negotiation workflow with vendors including face-to-face meetings, counter-offers, revised terms, and multi-level approvals before purchase order creation.

PostgreSQL
4 Tables
Schema: procurement
Multi-Round

Negotiation Process Overview

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.

Bid Evaluation
Compare Vendors
Shortlist Vendors
Select Top Bids
Initiate Negotiation
Round 1 Begins
Round 1
Online / Offline
Vendor Counter-Offer
Revised Terms
Round 2 (if needed)
Further Negotiation
Agreement Reached
Terms Finalized
Approval of Terms
Multi-Level
PO Creation
Purchase Order

Escalation Path

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.

Database Tables

4
Total Tables
56
Total Columns
6
Foreign Keys
5
Enum Types

procurement.bid_negotiations (Enhanced)

  • id SERIAL - Primary Key
  • bid_id INTEGER - FK → bids.id
  • round_number INTEGER - Negotiation round (1, 2, 3...)
  • negotiation_mode VARCHAR - ONLINE | OFFLINE_MEETING | EMAIL | PHONE
  • negotiated_by INTEGER - FK → users.id (buyer conducting negotiation)
  • original_amount DECIMAL(18,2) - Original bid amount
  • counter_amount DECIMAL(18,2) - Vendor's counter-offer amount
  • negotiated_amount DECIMAL(18,2) - Final agreed amount for this round
  • revised_payment_terms TEXT - Updated payment terms
  • revised_delivery_terms TEXT - Updated delivery terms
  • revised_warranty_terms TEXT - Updated warranty terms
  • meeting_date TIMESTAMP - Scheduled meeting date/time
  • meeting_location VARCHAR(255) - Meeting venue or virtual link
  • meeting_notes TEXT - Detailed meeting notes / minutes
  • vendor_response TEXT - Vendor's formal response
  • vendor_response_date TIMESTAMP - When vendor responded
  • status VARCHAR - INITIATED | COUNTER_OFFERED | ACCEPTED | REJECTED | ESCALATED
  • approved_by INTEGER - FK → users.id (final approver)
  • approved_at TIMESTAMP - Approval timestamp
  • negotiated_at TIMESTAMP - Record creation timestamp

procurement.negotiation_items

  • id SERIAL - Primary Key
  • negotiation_id INTEGER - FK → bid_negotiations.id
  • rfq_item_id INTEGER - FK → rfq_items.id
  • original_unit_price DECIMAL(18,2) - Original quoted unit price
  • negotiated_unit_price DECIMAL(18,2) - Negotiated unit price
  • original_quantity INTEGER - Original quantity
  • negotiated_quantity INTEGER - Revised quantity (if changed)
  • original_delivery_days INTEGER - Original delivery timeline in days
  • negotiated_delivery_days INTEGER - Revised delivery timeline
  • remarks TEXT - Line-item specific remarks

procurement.negotiation_attachments

  • id SERIAL - Primary Key
  • negotiation_id INTEGER - FK → bid_negotiations.id
  • document_type VARCHAR - MOM | REVISED_QUOTE | VENDOR_LETTER | COUNTER_OFFER | APPROVAL_NOTE
  • file_name VARCHAR(255) - Original file name
  • file_path VARCHAR(500) - Storage path / blob URL
  • file_size BIGINT - File size in bytes
  • uploaded_by INTEGER - FK → users.id
  • uploaded_at TIMESTAMP - Upload timestamp
  • remarks TEXT - Description or notes about the document

procurement.negotiation_approvals

  • id SERIAL - Primary Key
  • negotiation_id INTEGER - FK → bid_negotiations.id
  • approval_level INTEGER - Level in approval hierarchy (1, 2, 3...)
  • approver_id INTEGER - FK → users.id
  • status VARCHAR - PENDING | APPROVED | REJECTED | RETURNED
  • remarks TEXT - Approver comments
  • approved_at TIMESTAMP - When action was taken

Negotiation Workflow

1

Initiate Negotiation

After 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());
2

Schedule Offline Meeting

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;
3

Conduct Meeting & Record Notes

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());
4

Vendor Counter-Offer

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);
5

Buyer Review & Next Round

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;
6

Reach Agreement

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;
7

Approval of Negotiated Terms

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;

Negotiation Modes

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 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 Transitions

INITIATED
Negotiation Started
COUNTER_OFFERED
Vendor Responded
ACCEPTED
Terms Agreed
Approval Flow
Multi-Level
Alternative Paths
COUNTER_OFFERED
REJECTED
Terms Not Acceptable
End / Re-negotiate
New Round or Close
COUNTER_OFFERED
ESCALATED
Higher Authority
Management Review
Director Decision

Status Descriptions

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

Negotiation Limits & Escalation

Maximum Rounds

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.

Discount Authority Levels

<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

Auto-Escalation

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.

Audit Trail

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.

Sample .NET Code

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;
    }
}

Best Practices

Document All Offline Meetings

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.

Track Line-Item Level Negotiations

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.

Set Maximum Negotiation Rounds

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.

Require Approval for Discounts Beyond Threshold

Enforce multi-level approval workflows for negotiated discounts that exceed the buyer's authority level. This ensures financial governance and prevents unauthorized concessions.

Maintain Complete Audit Trail

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.

Use Escalation for Stalled Negotiations

Leverage the auto-escalation workflow when negotiations stall beyond configurable timeframes. This ensures timely resolution by involving higher authorities and prevents procurement bottlenecks.