Files
nick-doc/02 - Data Models/RequestTemplate.md

6.0 KiB

title, tags, aliases
title tags aliases
RequestTemplate
data-model
mongoose
Template
Request Template
IRequestTemplate

RequestTemplate

A reusable template authored by a seller. When a buyer visits the template's shareableLink, the front-end pre-fills a new PurchaseRequest with the template's category, urgency, specs, delivery info, and an optional default seller proposal. The schema mirrors PurchaseRequest for fast cloning, plus template-specific bookkeeping (isActive, usageCount, maxUsage, expiresAt).

[!note] Source backend/src/models/RequestTemplate.ts:65 — schema definition backend/src/models/RequestTemplate.ts:295 — model export

Schema

Field Type Required Default Validation Index Description
sellerId ObjectId → User yes yes (compound) Template author.
title String yes trim, maxlength 200 Headline.
description String yes trim, maxlength 2000 Description.
categoryId ObjectId → Category yes yes (compound) Category.
productType String no physical_product enum: physical_product / digital_product / service / consultation yes (compound) Fulfilment type.
productLink String no URL regex Reference URL.
size String no trim, maxlength 100 Size.
color String no trim, maxlength 100 Color.
brand String no trim, maxlength 100 Brand.
quantity Number no 1 min 1 Default unit count.
budget.min Number no min 0 Lower bound.
budget.max Number no min 0 Upper bound.
budget.currency String no USDT enum: USD / EUR / IRR / USDT / USDC Currency. Shared with PurchaseRequest so a template can be converted without enum drift.
urgency String no medium enum: low / medium / high / urgent Urgency.
tags[] String[] no trim Tags.
specifications[].key String yes trim Spec key.
specifications[].value String yes trim Spec value.
specifications[].label String no trim Human label.
deliveryInfo.deliveryType String no physical enum: physical / online Delivery channel.
deliveryInfo.notes String no Notes.
deliveryInfo.email String no email regex Digital delivery email.
serviceInfo.duration Number no min 0.5 Hours.
serviceInfo.sessionType String no enum: online / in_person / hybrid Session type.
serviceInfo.location String no trim, maxlength 200 Location.
serviceInfo.requirements[] String[] no trim Pre-requisites.
proposal.title String no trim, maxlength 200 Default offer title.
proposal.price Number no min 0.01 Default offer price.
proposal.deliveryTime Number no min 1, max 365 Default ETA in days.
proposal.description String no trim, maxlength 1000 Default offer description.
attachments[] String[] no File URLs.
images[] String[] no trim Image URLs.
metadata.source String no manual enum: manual / template / api Origin.
metadata.templateId String no trim Originating template id.
metadata.version String no trim Schema version.
isActive Boolean no true yes (single + compound) Active flag.
shareableLink String yes trim unique (+ compound) Public link slug.
usageCount Number no 0 min 0 Number of times used.
maxUsage Number no null min 1 Optional cap.
expiresAt Date no null yes Optional expiry.
createdAt Date auto yes (desc) Mongoose timestamp.
updatedAt Date auto Mongoose timestamp.

Virtuals

None defined.

Indexes

Defined at backend/src/models/RequestTemplate.ts:283-293:

  • { categoryId: 1 }
  • { productType: 1 }
  • { isActive: 1 }
  • { createdAt: -1 }
  • { expiresAt: 1 }
  • { sellerId: 1, isActive: 1 }
  • { shareableLink: 1, isActive: 1 }
  • { productType: 1, isActive: 1 }
  • { categoryId: 1, productType: 1 }

shareableLink and sellerId already get indexes from unique: true / field-level conventions (see source comment at line 282).

Pre/Post Hooks

None declared.

Instance Methods

None defined.

Static Methods

None defined.

Relationships

  • References: User (sellerId), Category (categoryId).
  • Referenced by: PurchaseRequest (metadata.templateId as string), Review (subjectId when subjectType === 'template').

State Transitions

stateDiagram-v2
    [*] --> active : created
    active --> inactive : seller toggles off
    inactive --> active : seller toggles on
    active --> expired : expiresAt passed
    active --> capped : usageCount == maxUsage
    expired --> [*]
    capped --> [*]

[!note] Soft state Only isActive is persisted directly. expired and capped are derived at query time using expiresAt and usageCount.

Common Queries

// Seller's active templates
RequestTemplate.find({ sellerId, isActive: true }).sort({ createdAt: -1 });

// Public template by slug
RequestTemplate.findOne({ shareableLink: slug, isActive: true });

// Bump usage atomically
RequestTemplate.findOneAndUpdate(
  { _id, isActive: true, $or: [{ maxUsage: null }, { $expr: { $lt: ['$usageCount', '$maxUsage'] } }] },
  { $inc: { usageCount: 1 } },
  { new: true }
);

// Cleanup expired
RequestTemplate.find({ expiresAt: { $lt: new Date() }, isActive: true });

Related: PurchaseRequest, User, Category, Review.