feat: Implement A4 invoice layout and hide Label for virtual orders
Invoice: - Enhanced A4-ready layout with proper structure - Store header with invoice number - Billing/shipping address sections - Styled items table with alternating rows - Totals summary with conditional display - Thank you footer Label: - Label button now hidden for virtual-only orders - Uses existing isVirtualOnly detection Print CSS: - Added @page A4 size directive - Print-color-adjust for background colors - 20mm padding for proper margins Documentation: - Updated subscription module plan (comprehensive) - Updated affiliate module plan (comprehensive) - Created shipping label standardization plan
This commit is contained in:
241
.agent/plans/affiliate-module.md
Normal file
241
.agent/plans/affiliate-module.md
Normal file
@@ -0,0 +1,241 @@
|
||||
# Affiliate Module Plan
|
||||
|
||||
## Overview
|
||||
Referral tracking with hybrid customer/affiliate roles, integrated as a core plugin module.
|
||||
|
||||
---
|
||||
|
||||
## Module Architecture
|
||||
|
||||
### Core Features
|
||||
- **Affiliate registration**: Customers can become affiliates
|
||||
- **Approval workflow**: Manual or auto-approval of affiliates
|
||||
- **Unique referral links/codes**: Each affiliate gets unique tracking
|
||||
- **Commission tracking**: Track referrals and calculate earnings
|
||||
- **Tiered commission rates**: Different rates per product/category/affiliate level
|
||||
- **Payout management**: Track and process affiliate payouts
|
||||
- **Affiliate dashboard**: Self-service stats and link generator
|
||||
|
||||
### Hybrid Roles
|
||||
- A customer can also be an affiliate
|
||||
- No separate user type; affiliate data linked to existing user
|
||||
- Affiliates can still make purchases (self-referral rules configurable)
|
||||
|
||||
---
|
||||
|
||||
## Database Schema
|
||||
|
||||
### Table: `woonoow_affiliates`
|
||||
```sql
|
||||
CREATE TABLE woonoow_affiliates (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL UNIQUE,
|
||||
status ENUM('pending', 'active', 'rejected', 'suspended') DEFAULT 'pending',
|
||||
referral_code VARCHAR(50) NOT NULL UNIQUE,
|
||||
commission_rate DECIMAL(5,2) DEFAULT NULL, -- Override global rate
|
||||
tier_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
payment_email VARCHAR(255) DEFAULT NULL,
|
||||
payment_method VARCHAR(50) DEFAULT NULL,
|
||||
total_earnings DECIMAL(15,2) DEFAULT 0,
|
||||
total_unpaid DECIMAL(15,2) DEFAULT 0,
|
||||
total_paid DECIMAL(15,2) DEFAULT 0,
|
||||
referral_count INT UNSIGNED DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_referral_code (referral_code)
|
||||
);
|
||||
```
|
||||
|
||||
### Table: `woonoow_referrals`
|
||||
```sql
|
||||
CREATE TABLE woonoow_referrals (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
affiliate_id BIGINT UNSIGNED NOT NULL,
|
||||
order_id BIGINT UNSIGNED NOT NULL,
|
||||
customer_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
subtotal DECIMAL(15,2) NOT NULL,
|
||||
commission_rate DECIMAL(5,2) NOT NULL,
|
||||
commission_amount DECIMAL(15,2) NOT NULL,
|
||||
status ENUM('pending', 'approved', 'rejected', 'paid') DEFAULT 'pending',
|
||||
referral_type ENUM('link', 'code', 'coupon') DEFAULT 'link',
|
||||
ip_address VARCHAR(45) DEFAULT NULL,
|
||||
user_agent TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
approved_at DATETIME DEFAULT NULL,
|
||||
paid_at DATETIME DEFAULT NULL,
|
||||
INDEX idx_affiliate (affiliate_id),
|
||||
INDEX idx_order (order_id),
|
||||
INDEX idx_status (status)
|
||||
);
|
||||
```
|
||||
|
||||
### Table: `woonoow_payouts`
|
||||
```sql
|
||||
CREATE TABLE woonoow_payouts (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
affiliate_id BIGINT UNSIGNED NOT NULL,
|
||||
amount DECIMAL(15,2) NOT NULL,
|
||||
method VARCHAR(50) DEFAULT NULL,
|
||||
reference VARCHAR(255) DEFAULT NULL, -- Bank ref, PayPal transaction, etc.
|
||||
status ENUM('pending', 'processing', 'completed', 'failed') DEFAULT 'pending',
|
||||
notes TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
completed_at DATETIME DEFAULT NULL,
|
||||
INDEX idx_affiliate (affiliate_id),
|
||||
INDEX idx_status (status)
|
||||
);
|
||||
```
|
||||
|
||||
### Table: `woonoow_affiliate_tiers` (Optional)
|
||||
```sql
|
||||
CREATE TABLE woonoow_affiliate_tiers (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
commission_rate DECIMAL(5,2) NOT NULL,
|
||||
min_referrals INT UNSIGNED DEFAULT 0, -- Auto-promote at X referrals
|
||||
min_earnings DECIMAL(15,2) DEFAULT 0, -- Auto-promote at X earnings
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backend Structure
|
||||
|
||||
```
|
||||
includes/Modules/Affiliate/
|
||||
├── AffiliateModule.php # Bootstrap, hooks, tracking
|
||||
├── AffiliateManager.php # Core logic
|
||||
├── ReferralTracker.php # Track referral cookies/links
|
||||
└── AffiliateSettings.php # Settings schema
|
||||
|
||||
includes/Api/AffiliatesController.php # REST endpoints
|
||||
```
|
||||
|
||||
### AffiliateManager Methods
|
||||
```php
|
||||
- register($user_id) # Register user as affiliate
|
||||
- approve($affiliate_id) # Approve pending affiliate
|
||||
- reject($affiliate_id, $reason) # Reject application
|
||||
- suspend($affiliate_id) # Suspend affiliate
|
||||
- track_referral($order, $affiliate) # Create referral record
|
||||
- calculate_commission($order, $affiliate) # Calculate earnings
|
||||
- approve_referral($referral_id) # Approve pending referral
|
||||
- create_payout($affiliate_id, $amount) # Create payout request
|
||||
- process_payout($payout_id) # Mark payout complete
|
||||
- get_affiliate_stats($affiliate_id) # Dashboard stats
|
||||
```
|
||||
|
||||
### Referral Tracking
|
||||
1. Affiliate shares link: `yourstore.com/?ref=CODE`
|
||||
2. ReferralTracker sets cookie: `wnw_ref=CODE` (30 days default)
|
||||
3. On checkout, check cookie and link to affiliate
|
||||
4. On order completion, create referral record
|
||||
|
||||
---
|
||||
|
||||
## REST API Endpoints
|
||||
|
||||
### Admin Endpoints
|
||||
```
|
||||
GET /affiliates # List affiliates
|
||||
GET /affiliates/{id} # Affiliate details
|
||||
PUT /affiliates/{id} # Update affiliate
|
||||
POST /affiliates/{id}/approve # Approve affiliate
|
||||
POST /affiliates/{id}/reject # Reject affiliate
|
||||
GET /affiliates/referrals # All referrals
|
||||
GET /affiliates/payouts # All payouts
|
||||
POST /affiliates/payouts/{id}/complete # Complete payout
|
||||
```
|
||||
|
||||
### Customer/Affiliate Endpoints
|
||||
```
|
||||
GET /my-affiliate # Check if affiliate
|
||||
POST /my-affiliate/register # Register as affiliate
|
||||
GET /my-affiliate/stats # Dashboard stats
|
||||
GET /my-affiliate/referrals # My referrals
|
||||
GET /my-affiliate/payouts # My payouts
|
||||
POST /my-affiliate/payout-request # Request payout
|
||||
GET /my-affiliate/links # Generate referral links
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Admin SPA
|
||||
|
||||
### Affiliates List (`/marketing/affiliates`)
|
||||
- Table: Name, Email, Status, Referrals, Earnings, Actions
|
||||
- Filters: Status, Date range
|
||||
- Actions: Approve, Reject, View
|
||||
|
||||
### Affiliate Detail (`/marketing/affiliates/:id`)
|
||||
- Affiliate info card
|
||||
- Stats summary
|
||||
- Referrals list
|
||||
- Payouts history
|
||||
- Action buttons
|
||||
|
||||
### Referrals List (`/marketing/affiliates/referrals`)
|
||||
- All referrals across affiliates
|
||||
- Filters: Status, Affiliate, Date
|
||||
|
||||
### Payouts (`/marketing/affiliates/payouts`)
|
||||
- Payout requests
|
||||
- Process payouts
|
||||
- Payment history
|
||||
|
||||
---
|
||||
|
||||
## Customer SPA
|
||||
|
||||
### Become an Affiliate (`/my-account/affiliate`)
|
||||
- Registration form (if not affiliate)
|
||||
- Dashboard (if affiliate)
|
||||
|
||||
### Affiliate Dashboard
|
||||
- Stats: Total Referrals, Pending, Approved, Earnings
|
||||
- Referral link generator
|
||||
- Recent referrals
|
||||
- Payout request button
|
||||
|
||||
### My Referrals
|
||||
- List of referrals with status
|
||||
- Commission amount
|
||||
|
||||
### My Payouts
|
||||
- Payout history
|
||||
- Pending amount
|
||||
- Request payout form
|
||||
|
||||
---
|
||||
|
||||
## Settings Schema
|
||||
|
||||
```php
|
||||
return [
|
||||
'enabled' => true,
|
||||
'registration_type' => 'open', // open, approval, invite
|
||||
'auto_approve' => false,
|
||||
'default_commission_rate' => 10, // 10%
|
||||
'commission_type' => 'percentage', // percentage, flat
|
||||
'cookie_duration' => 30, // days
|
||||
'min_payout_amount' => 50,
|
||||
'payout_methods' => ['bank_transfer', 'paypal'],
|
||||
'allow_self_referral' => false,
|
||||
'referral_approval' => 'auto', // auto, manual
|
||||
'approval_delay_days' => 14, // Wait X days before auto-approve
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Priority
|
||||
1. Database tables and AffiliateManager
|
||||
2. ReferralTracker (cookie-based tracking)
|
||||
3. Order hook to create referrals
|
||||
4. Admin SPA affiliates management
|
||||
5. Customer SPA affiliate dashboard
|
||||
6. Payout management
|
||||
7. Tier system (optional)
|
||||
84
.agent/plans/shipping-label.md
Normal file
84
.agent/plans/shipping-label.md
Normal file
@@ -0,0 +1,84 @@
|
||||
# Shipping Label Plan
|
||||
|
||||
## Overview
|
||||
Standardized waybill data structure for shipping label generation.
|
||||
|
||||
## Problem
|
||||
- Different shipping carrier addons (JNE, JNT, SiCepat, etc.) store data differently
|
||||
- No standard structure for label generation
|
||||
- Label button needs waybill data to function
|
||||
|
||||
## Proposed Solution
|
||||
|
||||
### 1. Standardized Meta Key
|
||||
Order meta: `_shipping_waybill`
|
||||
|
||||
### 2. Data Structure
|
||||
```json
|
||||
{
|
||||
"tracking_number": "JNE123456789",
|
||||
"carrier": "jne",
|
||||
"carrier_name": "JNE Express",
|
||||
"service": "REG",
|
||||
"estimated_days": 3,
|
||||
"sender": {
|
||||
"name": "Store Name",
|
||||
"address": "Full address line 1",
|
||||
"city": "Jakarta",
|
||||
"postcode": "12345",
|
||||
"phone": "08123456789"
|
||||
},
|
||||
"recipient": {
|
||||
"name": "Customer Name",
|
||||
"address": "Full address line 1",
|
||||
"city": "Bandung",
|
||||
"postcode": "40123",
|
||||
"phone": "08987654321"
|
||||
},
|
||||
"package": {
|
||||
"weight": "1.5",
|
||||
"weight_unit": "kg",
|
||||
"dimensions": "20x15x10",
|
||||
"dimensions_unit": "cm"
|
||||
},
|
||||
"label_url": null,
|
||||
"barcode": "JNE123456789",
|
||||
"barcode_type": "128",
|
||||
"created_at": "2026-01-05T12:00:00+07:00"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Addon Integration Contract
|
||||
|
||||
Shipping addons MUST:
|
||||
1. Call `update_post_meta($order_id, '_shipping_waybill', $waybill_data)`
|
||||
2. Use the standard structure above
|
||||
3. Set `label_url` if carrier provides downloadable PDF
|
||||
4. Set `barcode` for local label generation
|
||||
|
||||
### 4. Label Button Behavior
|
||||
1. Check if `_shipping_waybill` meta exists on order
|
||||
2. If `label_url` → open carrier's PDF
|
||||
3. Otherwise → generate printable label from meta data
|
||||
|
||||
### 5. UI Behavior
|
||||
- Label button hidden if order is virtual-only
|
||||
- Label button shows "Generate Label" if no waybill yet
|
||||
- Label button shows "Print Label" if waybill exists
|
||||
|
||||
## API Endpoint (Future)
|
||||
```
|
||||
POST /woonoow/v1/orders/{id}/generate-waybill
|
||||
- Calls shipping carrier API
|
||||
- Stores waybill in standardized format
|
||||
- Returns waybill data
|
||||
|
||||
GET /woonoow/v1/orders/{id}/waybill
|
||||
- Returns current waybill data
|
||||
```
|
||||
|
||||
## Implementation Priority
|
||||
1. Define standard structure (this document)
|
||||
2. Implement Label UI conditional logic
|
||||
3. Create waybill API endpoint
|
||||
4. Document for addon developers
|
||||
191
.agent/plans/subscription-module.md
Normal file
191
.agent/plans/subscription-module.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Subscription Module Plan
|
||||
|
||||
## Overview
|
||||
Recurring product subscriptions with flexible billing, integrated as a core plugin module (like Newsletter/Wishlist/Licensing).
|
||||
|
||||
---
|
||||
|
||||
## Module Architecture
|
||||
|
||||
### Core Features
|
||||
- **Recurring billing**: Weekly, monthly, yearly, custom intervals
|
||||
- **Free trials**: X days free before billing starts
|
||||
- **Sign-up fees**: One-time fee on first subscription
|
||||
- **Automatic renewals**: Process payment on renewal date
|
||||
- **Manual renewal**: Allow customers to renew manually
|
||||
- **Proration**: Calculate prorated amounts on plan changes
|
||||
- **Pause/Resume**: Allow customers to pause subscriptions
|
||||
|
||||
### Product Integration
|
||||
- Checkbox under "Additional Options": **Enable subscription for this product**
|
||||
- When enabled, show subscription settings:
|
||||
- Billing period (weekly/monthly/yearly/custom)
|
||||
- Billing interval (every X periods)
|
||||
- Free trial days
|
||||
- Sign-up fee
|
||||
- Subscription length (0 = unlimited)
|
||||
- Variable products: Variation-level subscription settings (different durations/prices per variation)
|
||||
|
||||
### Integration with Licensing
|
||||
- Licenses can be bound to subscriptions
|
||||
- When subscription is active → license is valid
|
||||
- When subscription expires/cancelled → license is revoked
|
||||
- Auto-renewal keeps license active
|
||||
|
||||
---
|
||||
|
||||
## Database Schema
|
||||
|
||||
### Table: `woonoow_subscriptions`
|
||||
```sql
|
||||
CREATE TABLE woonoow_subscriptions (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT UNSIGNED NOT NULL,
|
||||
order_id BIGINT UNSIGNED NOT NULL,
|
||||
product_id BIGINT UNSIGNED NOT NULL,
|
||||
variation_id BIGINT UNSIGNED DEFAULT NULL,
|
||||
status ENUM('pending', 'active', 'on-hold', 'cancelled', 'expired', 'pending-cancel') DEFAULT 'pending',
|
||||
billing_period ENUM('day', 'week', 'month', 'year') NOT NULL,
|
||||
billing_interval INT UNSIGNED DEFAULT 1,
|
||||
start_date DATETIME NOT NULL,
|
||||
trial_end_date DATETIME DEFAULT NULL,
|
||||
next_payment_date DATETIME DEFAULT NULL,
|
||||
end_date DATETIME DEFAULT NULL,
|
||||
last_payment_date DATETIME DEFAULT NULL,
|
||||
payment_method VARCHAR(100) DEFAULT NULL,
|
||||
payment_meta LONGTEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_user_id (user_id),
|
||||
INDEX idx_order_id (order_id),
|
||||
INDEX idx_status (status),
|
||||
INDEX idx_next_payment (next_payment_date)
|
||||
);
|
||||
```
|
||||
|
||||
### Table: `woonoow_subscription_orders`
|
||||
Links subscription to renewal orders:
|
||||
```sql
|
||||
CREATE TABLE woonoow_subscription_orders (
|
||||
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
|
||||
subscription_id BIGINT UNSIGNED NOT NULL,
|
||||
order_id BIGINT UNSIGNED NOT NULL,
|
||||
order_type ENUM('parent', 'renewal', 'switch', 'resubscribe') DEFAULT 'renewal',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_subscription (subscription_id),
|
||||
INDEX idx_order (order_id)
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backend Structure
|
||||
|
||||
```
|
||||
includes/Modules/Subscription/
|
||||
├── SubscriptionModule.php # Bootstrap, hooks, product meta
|
||||
├── SubscriptionManager.php # Core logic
|
||||
├── SubscriptionScheduler.php # Cron jobs for renewals
|
||||
└── SubscriptionSettings.php # Settings schema
|
||||
|
||||
includes/Api/SubscriptionsController.php # REST endpoints
|
||||
```
|
||||
|
||||
### SubscriptionManager Methods
|
||||
```php
|
||||
- create($order, $product, $user) # Create subscription from order
|
||||
- renew($subscription_id) # Process renewal
|
||||
- cancel($subscription_id, $reason) # Cancel subscription
|
||||
- pause($subscription_id) # Pause subscription
|
||||
- resume($subscription_id) # Resume paused subscription
|
||||
- switch($subscription_id, $new_product) # Switch plan
|
||||
- get_next_payment_date($subscription) # Calculate next date
|
||||
- process_renewal_payment($subscription) # Charge payment
|
||||
```
|
||||
|
||||
### Cron Jobs
|
||||
- `woonoow_process_subscription_renewals`: Run daily, process due renewals
|
||||
- `woonoow_check_expired_subscriptions`: Mark expired subscriptions
|
||||
|
||||
---
|
||||
|
||||
## REST API Endpoints
|
||||
|
||||
### Admin Endpoints
|
||||
```
|
||||
GET /subscriptions # List all subscriptions
|
||||
GET /subscriptions/{id} # Get subscription details
|
||||
PUT /subscriptions/{id} # Update subscription
|
||||
POST /subscriptions/{id}/cancel # Cancel subscription
|
||||
POST /subscriptions/{id}/renew # Force renewal
|
||||
```
|
||||
|
||||
### Customer Endpoints
|
||||
```
|
||||
GET /my-subscriptions # Customer's subscriptions
|
||||
GET /my-subscriptions/{id} # Subscription detail
|
||||
POST /my-subscriptions/{id}/cancel # Request cancellation
|
||||
POST /my-subscriptions/{id}/pause # Pause subscription
|
||||
POST /my-subscriptions/{id}/resume # Resume subscription
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Admin SPA
|
||||
|
||||
### Subscriptions List (`/subscriptions`)
|
||||
- Table: ID, Customer, Product, Status, Next Payment, Actions
|
||||
- Filters: Status, Product, Date range
|
||||
- Actions: View, Cancel, Renew
|
||||
|
||||
### Subscription Detail (`/subscriptions/:id`)
|
||||
- Subscription info card
|
||||
- Related orders list
|
||||
- Payment history
|
||||
- Action buttons: Cancel, Pause, Renew
|
||||
|
||||
---
|
||||
|
||||
## Customer SPA
|
||||
|
||||
### My Subscriptions (`/my-account/subscriptions`)
|
||||
- List of active/past subscriptions
|
||||
- Status badges
|
||||
- Next payment info
|
||||
- Actions: Cancel, Pause, View
|
||||
|
||||
### Subscription Detail
|
||||
- Product info
|
||||
- Billing schedule
|
||||
- Payment history
|
||||
- Management actions
|
||||
|
||||
---
|
||||
|
||||
## Settings Schema
|
||||
|
||||
```php
|
||||
return [
|
||||
'default_status' => 'active',
|
||||
'button_text_subscribe' => 'Subscribe Now',
|
||||
'button_text_renew' => 'Renew Subscription',
|
||||
'allow_customer_cancel' => true,
|
||||
'allow_customer_pause' => true,
|
||||
'max_pause_count' => 3,
|
||||
'renewal_retry_days' => [1, 3, 5], // Retry failed payments
|
||||
'expire_after_failed_attempts' => 3,
|
||||
'send_renewal_reminder' => true,
|
||||
'reminder_days_before' => 3,
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Priority
|
||||
1. Database tables and SubscriptionManager
|
||||
2. Product meta fields for subscription settings
|
||||
3. Order hook to create subscription
|
||||
4. Renewal cron job
|
||||
5. Admin SPA list/detail pages
|
||||
6. Customer SPA pages
|
||||
7. Integration with Licensing module
|
||||
Reference in New Issue
Block a user