Managing Requests & Converting to Orders

Managing Requests & Converting to Orders

After posting a buyer request, you’ll receive vendor proposals. This guide covers evaluating proposals, communicating with vendors, accepting offers, and converting proposals into orders.

Request Statuses

WP Sell Services uses 5 request statuses:

StatusDescription
openActive, accepting proposals
in_reviewEvaluating proposals, may still accept new ones
hiredAccepted a proposal, order created
expiredPast expiration date, no longer active
cancelledBuyer cancelled the request

My Requests Dashboard

Your central hub for managing all buyer requests.

Accessing the Dashboard

  1. Log in to your account
  2. Navigate to Dashboard → My Requests
  3. View all requests and their status

Request Card Information

Each request displays:

  • Title – Project name
  • Status – Current stage
  • Posted Date – When published
  • Proposal Count – Number received (from getproposalcount())
  • Budget – Your specified amount or range
  • Delivery Days – Target completion timeframe
  • Expiration – When request expires
  • Category – Service category assigned

Request Metadata

Requests are WordPress custom posts (wpss_request) with meta fields:

Meta KeyTypeDescription
wpssstatusstringRequest status
wpssbudget_typestring‘fixed’ or ‘range’
wpssbudget_minfloatMinimum budget
wpssbudget_maxfloatMaximum budget (range only)
wpssdelivery_daysintExpected delivery timeframe
wpssattachmentsarrayAttachment IDs
wpssskills_requiredarrayRequired skill tags
wpssexpires_atdatetimeExpiration timestamp
wpsshiredvendoridintVendor ID (when hired)
wpssacceptedproposalidintProposal ID (when hired)

Managing Request Status

Updating Status

Only certain status transitions are allowed:

// Valid statuses
BuyerRequestService::STATUS_OPEN
BuyerRequestService::STATUS_IN_REVIEW
BuyerRequestService::STATUS_HIRED
BuyerRequestService::STATUS_EXPIRED
BuyerRequestService::STATUS_CANCELLED

Marking as Hired

When you accept a proposal:

$buyer_request_service->mark_hired(
    $request_id,
    $vendor_id,
    $proposal_id
);

This automatically:

  • Updates status to ‘hired’
  • Stores vendor ID
  • Stores accepted proposal ID

Viewing Proposals

Proposal Count

Get total proposals submitted:

$count = $buyer_request_service->get_proposal_count( $request_id );

Queries the wpss_proposals table.

Proposal Service

Proposals are managed by ProposalService, not BuyerRequestService.

To get proposals:

$proposal_service = new ProposalService();
$proposals = $proposal_service->get_by_request( $request_id );

Converting to Order

Acceptance Process

When you accept a proposal, the system creates a service order.

Method: converttoorder()

$result = $buyer_request_service->convert_to_order(
    $request_id,
    $proposal_id
);

What Happens Automatically

  1. Validation:
  2. Order Creation:
  3. Proposal Updates:
  4. Request Updates:
  5. Order Requirements:
  6. Conversation:
  7. Notification:

Return Value

Success:

[
    'success' => true,
    'message' => 'Order created successfully. Proceed to payment.',
    'order_id' => 123,
    'order_number' => 'WPSS-ABC12345',
]

With warnings:

[
    'success' => true,
    'order_id' => 123,
    'order_number' => 'WPSS-ABC12345',
    'warnings' => [
        'Order requirements could not be saved.'
    ]
]

Failure:

[
    'success' => false,
    'message' => 'Error description'
]

Request Queries

Get Open Requests

$requests = $buyer_request_service->get_open( [
    'posts_per_page' => 20,
    'paged' => 1,
    'category_id' => 0,
    'budget_min' => 0,
    'budget_max' => 0,
    'order_by' => 'date',
    'order' => 'DESC',
] );

Returns requests with:

  • Status = ‘open’
  • Not expired (expires_at > now OR no expiration)

Get User’s Requests

$requests = $buyer_request_service->get_by_buyer( $user_id, [
    'posts_per_page' => 20,
    'paged' => 1,
    'status' => '',  // Optional status filter
] );

Returns all requests where post_author = user ID.

Search Requests

$requests = $buyer_request_service->search( 'WordPress plugin', [
    'posts_per_page' => 20,
    'paged' => 1,
    'status' => 'open',
] );

Searches post title and content.

Request Expiration

Default Expiration

When creating a request without expiration:

$default_days = get_option( 'wpss_request_expiry_days', 30 );
$expires_at = current_time('mysql') + $default_days days;

Default: 30 days from posting.

Expiring Old Requests

Background job runs expireoldrequests():

$expired_count = $buyer_request_service->expire_old_requests();

Finds requests with:

  • Status = ‘open’
  • expires_at < current time

Updates status to ‘expired’.

Extending Expiration

Not implemented in base service. You must manually update:

$new_expiry = gmdate( 'Y-m-d H:i:s', strtotime( '+14 days' ) );
update_post_meta( $request_id, '_wpss_expires_at', $new_expiry );

Budget Types

Two budget types supported:

Fixed Budget

Single price amount:

[
    'budget_type' => BuyerRequestService::BUDGET_FIXED,
    'budget_min' => 500.00,
    'budget_max' => 0, // Not used
]

Range Budget

Flexible price range:

[
    'budget_type' => BuyerRequestService::BUDGET_RANGE,
    'budget_min' => 400.00,
    'budget_max' => 600.00,
]

Filtering Requests

By Category

Requests are assigned to service categories using WordPress taxonomy:

wp_set_object_terms(
    $request_id,
    [ $category_id ],
    'wpss_service_category'
);

Filter by category in queries:

$requests = $buyer_request_service->get_open( [
    'category_id' => 5,
] );

By Budget Range

Filter requests within budget range:

$requests = $buyer_request_service->get_open( [
    'budget_min' => 500,  // Requests with budget_min >= 500
    'budget_max' => 1000, // Requests with budget_max <= 1000
] );

Request Attachments

Storing Attachments

Attachments are WordPress media library attachments:

[
    'attachments' => [ 123, 456, 789 ], // Attachment IDs
]

Stored in wpssattachments meta as serialized array.

Accessing Attachments

$request = $buyer_request_service->get( $request_id );
$attachment_ids = $request->attachments; // Array of IDs

foreach ( $attachment_ids as $attachment_id ) {
    $url = wp_get_attachment_url( $attachment_id );
    $title = get_the_title( $attachment_id );
}

Skills Required

Optional skill tags for requests:

[
    'skills_required' => [
        'WordPress',
        'React',
        'PHP 8',
        'REST API',
    ]
]

Stored in wpssskills_required meta.

Vendors can filter by matching skills.

Request Object Structure

When calling get(), you receive:

{
    "id": 123,
    "title": "Build WordPress Plugin",
    "description": "Need custom plugin for...",
    "author_id": 456,
    "status": "open",
    "budget_type": "range",
    "budget_min": 500.00,
    "budget_max": 800.00,
    "delivery_days": 14,
    "attachments": [789, 790],
    "skills_required": ["WordPress", "PHP"],
    "expires_at": "2026-03-15 10:30:00",
    "created_at": "2026-02-12 10:30:00",
    "proposal_count": 7,
    "category": {
        "term_id": 5,
        "name": "Web Development",
        "slug": "web-development"
    }
}

WordPress Hooks

Action: wpssbuyerrequest_created

Fires when request is posted:

add_action( 'wpss_buyer_request_created', function( $post_id, $data ) {
    // Notify matching vendors
    // Log new request
}, 10, 2 );

Action: wpssbuyerrequest_updated

Fires when request is edited:

add_action( 'wpss_buyer_request_updated', function( $request_id, $data ) {
    // Notify vendors of changes
}, 10, 2 );

Action: wpssbuyerrequeststatuschanged

Fires on status updates:

add_action( 'wpss_buyer_request_status_changed', function( $request_id, $status, $old_status ) {
    // Track status changes
}, 10, 3 );

Action: wpssrequestconvertedtoorder

Fires when proposal accepted:

add_action( 'wpss_request_converted_to_order', function( $order_id, $request_id, $proposal_id, $request, $proposal ) {
    // Handle order creation
    // Send notifications
    // Update stats
}, 10, 5 );

Filter: wpssproposalorder_revisions

Filter revision count for converted orders:

add_filter( 'wpss_proposal_order_revisions', function( $revisions, $proposal, $request ) {
    // Default is 2
    // Increase based on order value
    if ( $proposal->proposed_price > 1000 ) {
        return 5;
    }
    return $revisions;
}, 10, 3 );

Best Practices

Request Description

Write clear, detailed descriptions:

Good:

I need a WordPress plugin that integrates with Stripe to process recurring subscriptions.

Requirements:
- Admin panel to manage subscriptions
- Customer portal for self-service
- Email notifications for renewal
- Support for annual and monthly plans
- Compatible with WordPress 6.0+

Deliverables:
- Fully functional plugin
- Source code
- Installation documentation

Poor:

Need Stripe integration ASAP.

Budget Setting

Set realistic budgets:

Research First:

  • Check similar services on marketplace
  • Ask vendors for rough estimates
  • Consider project complexity

Budget Range Benefits:

  • Attracts more proposals
  • Shows flexibility
  • Vendors can justify pricing

Proposal Evaluation

Compare proposals on:

  1. Vendor Experience – Portfolio and reviews
  2. Proposal Quality – Understanding of requirements
  3. Price – Value for money, not just cheapest
  4. Timeline – Realistic delivery estimate
  5. Communication – Responsiveness and clarity

After Acceptance

  1. Pay Promptly – Don’t delay payment
  2. Provide Requirements – Give vendor everything needed
  3. Stay Available – Answer questions quickly
  4. Be Reasonable – Don’t change scope mid-project

Troubleshooting

Request Not Visible

Check:

  • Request status is ‘open’
  • Not expired (expires_at in future)
  • Category assigned
  • Post status is ‘publish’

Can’t Accept Proposal

Verify:

  • Request is ‘open’ or ‘in_review’
  • Proposal status is ‘pending’
  • Proposal belongs to this request
  • You are the request author

Order Creation Failed

Common Issues:

  • Database insert failed (check permissions)
  • Proposal already accepted
  • Request already hired
  • Invalid vendor or service ID

Check: Return value ‘message’ field for specific error.

Related Documentation


Key Points:

  • 5 request statuses, most common flow: open → in_review → hired
  • Proposals live in separate wpss_proposals table
  • Converting proposal to order is automatic and comprehensive
  • Expiration handled by background cron job
  • All metadata stored in post meta with wpss prefix
Last updated: February 14, 2026