New customer: Present terms for e-signature during first order
Data Model
1. Contract Templates (Base Agreements)
Table: contract_templates
Master templates that define the base terms and conditions for product categories.
# Fields:
- id (primary key)
- name (string) - e.g., "Standard Dumpster Rental Agreement"
- slug (string) - e.g., "standard-dumpster"
- product_category (string) - e.g., "Dumpster - Trash", "Concrete Equipment", "Portable Toilets"
- content (text) - The actual contract text (supports Markdown/HTML)
- effective_date (date) - When this version becomes active
- expires_at (date, nullable) - When this version expires
- version (integer) - Version number for tracking
- is_active (boolean) - Current active template
- created_at, updated_at (timestamps)
# Indexes:
- product_category
- slug
- is_active
Validations:
Name must be present and unique
Product category must be present
Content must be present
Only one active template per product_category at a time
Version auto-increments on creation
2. Vendor Contract Overrides
Table: vendor_contract_templates
Vendor-specific customizations to base templates. Vendors can override terms for their products.
# Fields:
- id (primary key)
- vendor_id (foreign key → vendors)
- base_template_id (foreign key → contract_templates, nullable)
- product_category (string) - Must match a product category
- name (string) - e.g., "ABC Dumpsters Custom Terms"
- custom_terms (text) - Vendor's custom contract text
- pricing_terms (text, nullable) - Special pricing clauses
- liability_clauses (text, nullable) - Liability modifications
- effective_date (date)
- expires_at (date, nullable)
- version (integer) - Vendor's version number
- is_active (boolean)
- approved_by_admin (boolean) - Admin must approve vendor changes
- approved_at (timestamp)
- created_at, updated_at
# Indexes:
- vendor_id
- product_category
- [vendor_id, product_category, is_active] (composite)
Business Rules:
Vendor can have multiple templates for different product categories
Vendor can override base template or create entirely custom terms
Admin approval required before vendor terms go live
Version increments with each change
Old versions archived (is_active = false) but retained for audit
3. Contract Acceptances (Customer Signatures)
Table: contract_acceptances
Records of customer acceptance of terms. Tracks who accepted what, when, and where.
# Fields:
- id (primary key)
- acceptable_type (string) - Polymorphic: "Company" or "Contact"
- acceptable_id (integer) - Company or Contact ID
- vendor_id (foreign key → vendors)
- product_category (string)
- contract_template_id (foreign key → contract_templates, nullable)
- vendor_contract_template_id (foreign key → vendor_contract_templates, nullable)
- address_id (foreign key → addresses, nullable) - Optional site-specific
- accepted_version (integer) - Version number accepted
- accepted_at (timestamp)
- expires_at (timestamp, nullable) - Optional expiration
- ip_address (string) - For legal compliance
- user_agent (text) - Browser info
- signature_data (text, nullable) - E-signature data (if applicable)
- signed_by_name (string) - Name of person who signed
- signed_by_email (string) - Email of person who signed
- order_id (foreign key → orders, nullable) - Order where this was accepted
- created_at, updated_at
# Indexes:
- [acceptable_type, acceptable_id]
- vendor_id
- product_category
- [vendor_id, product_category, acceptable_type, acceptable_id] (composite)
- [address_id, vendor_id, product_category] (for site-specific)
Business Rules:
One acceptance per vendor + product_category + customer (+ optionally address)
If terms version changes, new acceptance required
Acceptance linked to specific order for audit trail
IP address and user agent stored for legal compliance
Optional expiration (e.g., annual renewal)
4. Product Categories (Reference)
Table: product_categories (or use existing product categorization)
Standardized categories for consistent term application.
# Fields:
- id (primary key)
- name (string) - e.g., "Dumpster - Trash"
- slug (string) - e.g., "dumpster-trash"
- description (text)
- requires_terms (boolean, default: true) - Some products may not need special terms
- created_at, updated_at
# Common categories:
- "Dumpster - Trash"
- "Dumpster - Recycling"
- "Concrete Equipment - Lowboy"
- "Concrete Equipment - Mixer"
- "Portable Toilets"
- "Storage Containers"
- "Mobile Offices"
Business Logic Flow
Order Creation Flow
1. Customer selects product
└─→ Product has product_category
2. Customer selects vendor
└─→ System checks for applicable terms
3. Determine applicable contract:
├─→ Check: Does vendor have custom template for this category?
│ ├─→ YES: vendor_contract_template_id = vendor's custom template
│ └─→ NO: contract_template_id = base template for category
│
└─→ Get template version number
4. Check customer acceptance:
└─→ Query: contract_acceptances
WHERE acceptable = customer
AND vendor_id = selected_vendor
AND product_category = product.category
AND (address_id = delivery_address OR address_id IS NULL) [if site-specific]
AND accepted_version = current_template_version
├─→ FOUND: Customer has accepted current terms
│ └─→ Proceed with order
│
└─→ NOT FOUND: Customer needs to accept terms
└─→ Present contract for e-signature
└─→ Create contract_acceptance record
└─→ Link to order
└─→ Proceed with order
Version Change Flow
1. Vendor updates terms (or admin updates base template)
└─→ Old template: is_active = false
└─→ New template: version += 1, is_active = true
2. Next order with this vendor + category:
└─→ Check acceptance with new version number
└─→ No match found (version mismatch)
└─→ Require re-acceptance
└─→ Show changes diff (optional)
└─→ Customer signs new version
Site-Specific Acceptance (Optional)
If site-specific mode enabled for product category:
└─→ Acceptance required per delivery address
└─→ contract_acceptance.address_id = delivery_address.id
└─→ Customer may have multiple acceptances (same vendor, different sites)
Admin:
GET /admin/contract_templates
POST /admin/contract_templates
GET /admin/contract_templates/:id/edit
PATCH /admin/contract_templates/:id
DELETE /admin/contract_templates/:id
GET /admin/vendor_contracts/pending_approval
PATCH /admin/vendor_contracts/:id/approve
PATCH /admin/vendor_contracts/:id/reject
Vendor Portal:
GET /vendor/contracts
POST /vendor/contracts
GET /vendor/contracts/:id/edit
PATCH /vendor/contracts/:id
Customer/Order:
GET /orders/:order_id/contract
POST /orders/:order_id/contract/accept
GET /contracts/history
GET /contracts/:id/pdf
Example Contract Template Variables
Templates can use placeholders for dynamic content:
## Rental Agreement
**Customer:** {{customer_name}}
**Delivery Address:** {{delivery_address}}
**Product:** {{product_name}}
**Vendor:** {{vendor_name}}
**Rental Period:** {{rental_start_date}} to {{rental_end_date}}
### Terms and Conditions
1. The customer agrees to pay ${{total_amount}} for rental of {{product_name}}.
2. Delivery will occur on {{delivery_date}} between {{delivery_time_window}}.
3. Weight limit: {{weight_limit}} tons. Overages charged at ${{overage_rate}}/ton.
...
**Signature:** _________________________
**Date:** {{signature_date}}
Success Metrics
Acceptance rate: % of customers who accept terms on first order
Rejection rate: % of customers who abandon after seeing terms
Re-acceptance compliance: % of customers who re-sign when terms change
Vendor customization adoption: % of vendors who customize terms
Time to acceptance: Average time from terms presented to signed
Legal compliance: 100% of orders have associated contract acceptance
Future Enhancements
DocuSign Integration - Professional e-signature service
/orders/:id/review_contract - Review before accepting
/orders/:id/accept_contract - Accept contract
/quote/:token/contract - View contract in quote flow
/quote/:token/sign_contract - Sign contract in quote flow
Next Steps to Complete
Critical Path (MVP)
Build Vendor Portal Views (High Priority)
Create views for vendor contract templates
List, create, edit, show pages
Approval status indicators
Submit for approval button
Build Admin Approval UI (High Priority)
Pending approval list view
Approve/reject actions with UI
Diff view (optional but helpful)
Integrate into Order Flow (High Priority)
Check for contract acceptance during checkout
Present terms if not accepted
Simple e-signature (typed name for MVP)
Link accepted contract to order
Customer Portal Views (Medium Priority)
List accepted contracts
View individual contract
Download/print option
Nice to Have
E-signature UI Enhancement
Canvas-based drawn signature
Signature preview
Better mobile UX
Email Notifications
Notify customers when terms change
Notify admins of pending vendor approvals
Send copy of signed contract
Analytics Dashboard
Acceptance rates
Pending signatures
Version adoption
Last Updated: October 1, 2025 (Implementation Review)
Status: Phase 2 Complete, Phase 3-4 Partial, Phase 5-6 Not Started
Next Review: After vendor portal views completed