6.0 KiB
title, tags, aliases
| title | tags | aliases | |||||
|---|---|---|---|---|---|---|---|
| RequestTemplate |
|
|
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 definitionbackend/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.templateIdas string), Review (subjectIdwhensubjectType === '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
isActiveis persisted directly.expiredandcappedare derived at query time usingexpiresAtandusageCount.
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.