docs: Add order calculation implementation plan
Created ORDER_CALCULATION_PLAN.md with: - Backend endpoints documentation - Frontend implementation steps - Code examples for OrderForm.tsx - Testing checklist - Expected results Next: Implement frontend integration
This commit is contained in:
187
ORDER_CALCULATION_PLAN.md
Normal file
187
ORDER_CALCULATION_PLAN.md
Normal file
@@ -0,0 +1,187 @@
|
||||
# Order Calculation - WooCommerce Native Implementation
|
||||
|
||||
## ✅ BACKEND COMPLETE
|
||||
|
||||
### New Endpoints:
|
||||
|
||||
1. **POST `/woonoow/v1/shipping/calculate`**
|
||||
- Input: `{ items: [], shipping: {} }`
|
||||
- Output: `{ methods: [{ id, method_id, instance_id, label, cost, taxes, meta_data }] }`
|
||||
- Returns live rates from UPS, FedEx, etc.
|
||||
- Returns service-level options (UPS Ground, UPS Express)
|
||||
|
||||
2. **POST `/woonoow/v1/orders/preview`**
|
||||
- Input: `{ items: [], billing: {}, shipping: {}, shipping_method: '', coupons: [] }`
|
||||
- Output: `{ subtotal, shipping_total, total_tax, total, ... }`
|
||||
- Calculates taxes correctly
|
||||
- Applies coupons
|
||||
- Uses WooCommerce cart engine
|
||||
|
||||
---
|
||||
|
||||
## 🔄 FRONTEND TODO
|
||||
|
||||
### 1. Update OrderForm.tsx
|
||||
|
||||
#### A. Add Shipping Rate Calculation Query
|
||||
|
||||
```tsx
|
||||
// Query shipping rates when address changes
|
||||
const { data: shippingRates, refetch: refetchShipping } = useQuery({
|
||||
queryKey: ['shipping-rates', items, shippingData],
|
||||
queryFn: async () => {
|
||||
if (!hasPhysicalProduct || !shippingData.country) return null;
|
||||
return api.post('/shipping/calculate', {
|
||||
items: items.map(i => ({ product_id: i.product_id, qty: i.qty })),
|
||||
shipping: shippingData,
|
||||
});
|
||||
},
|
||||
enabled: hasPhysicalProduct && !!shippingData.country,
|
||||
});
|
||||
```
|
||||
|
||||
#### B. Add Order Preview Query
|
||||
|
||||
```tsx
|
||||
// Query order preview for totals
|
||||
const { data: orderPreview } = useQuery({
|
||||
queryKey: ['order-preview', items, bCountry, shippingData, shippingMethod, validatedCoupons],
|
||||
queryFn: async () => {
|
||||
if (items.length === 0) return null;
|
||||
return api.post('/orders/preview', {
|
||||
items: items.map(i => ({ product_id: i.product_id, qty: i.qty })),
|
||||
billing: { country: bCountry, state: bState, postcode: bPost, city: bCity },
|
||||
shipping: shipDiff ? shippingData : undefined,
|
||||
shipping_method: shippingMethod,
|
||||
coupons: validatedCoupons.map(c => c.code),
|
||||
});
|
||||
},
|
||||
enabled: items.length > 0,
|
||||
});
|
||||
```
|
||||
|
||||
#### C. Update Shipping Method Dropdown
|
||||
|
||||
**Current:**
|
||||
```tsx
|
||||
<Select value={shippingMethod} onValueChange={setShippingMethod}>
|
||||
{shippings.map(s => (
|
||||
<SelectItem value={s.id}>{s.title} - {s.cost}</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
```
|
||||
|
||||
**New:**
|
||||
```tsx
|
||||
<Select value={shippingMethod} onValueChange={setShippingMethod}>
|
||||
{shippingRates?.methods?.map(rate => (
|
||||
<SelectItem value={rate.id}>
|
||||
{rate.label} - {money(rate.cost)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
```
|
||||
|
||||
#### D. Update Order Summary Display
|
||||
|
||||
**Add tax breakdown:**
|
||||
```tsx
|
||||
<div className="space-y-2">
|
||||
<div className="flex justify-between">
|
||||
<span>Items</span>
|
||||
<span>{items.length}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span>Subtotal</span>
|
||||
<span>{money(orderPreview?.subtotal || 0)}</span>
|
||||
</div>
|
||||
{orderPreview?.shipping_total > 0 && (
|
||||
<div className="flex justify-between">
|
||||
<span>Shipping</span>
|
||||
<span>{money(orderPreview.shipping_total)}</span>
|
||||
</div>
|
||||
)}
|
||||
{orderPreview?.total_tax > 0 && (
|
||||
<div className="flex justify-between">
|
||||
<span>Tax</span>
|
||||
<span>{money(orderPreview.total_tax)}</span>
|
||||
</div>
|
||||
)}
|
||||
{orderPreview?.discount_total > 0 && (
|
||||
<div className="flex justify-between text-green-600">
|
||||
<span>Discount</span>
|
||||
<span>-{money(orderPreview.discount_total)}</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex justify-between font-bold text-lg border-t pt-2">
|
||||
<span>Total</span>
|
||||
<span>{money(orderPreview?.total || 0)}</span>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
#### E. Trigger Recalculation
|
||||
|
||||
```tsx
|
||||
// Refetch shipping when address changes
|
||||
useEffect(() => {
|
||||
if (hasPhysicalProduct && shippingData.country) {
|
||||
refetchShipping();
|
||||
}
|
||||
}, [shippingData.country, shippingData.postcode, shippingData.state]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Implementation Steps
|
||||
|
||||
1. ✅ Backend endpoints created
|
||||
2. ⏳ Add shipping rate calculation query
|
||||
3. ⏳ Add order preview query
|
||||
4. ⏳ Update shipping method dropdown to show services
|
||||
5. ⏳ Update order summary to show tax
|
||||
6. ⏳ Add loading states
|
||||
7. ⏳ Test with UPS Live Rates
|
||||
8. ⏳ Test tax calculation
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Expected Result
|
||||
|
||||
### Before:
|
||||
- Shipping: "UPS Live Rates - RM0.00"
|
||||
- Total: RM97,000 (no tax)
|
||||
|
||||
### After:
|
||||
- Shipping dropdown shows:
|
||||
- UPS Ground - RM15,000
|
||||
- UPS Express - RM25,000
|
||||
- UPS Next Day Air - RM35,000
|
||||
- Order summary shows:
|
||||
- Subtotal: RM97,000
|
||||
- Shipping: RM15,000
|
||||
- Tax (11%): RM12,320
|
||||
- **Total: RM124,320**
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Testing Checklist
|
||||
|
||||
- [ ] Select UPS Live Rates → Shows service options
|
||||
- [ ] Select UPS Ground → Updates total
|
||||
- [ ] Change address → Recalculates rates
|
||||
- [ ] Add item → Recalculates totals
|
||||
- [ ] Apply coupon → Updates discount and total
|
||||
- [ ] Tax shows 11% of subtotal + shipping
|
||||
- [ ] Digital products → No shipping, no shipping tax
|
||||
- [ ] Physical products → Shipping + tax calculated
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Important Notes
|
||||
|
||||
1. **Don't reinvent calculation** - Use WooCommerce cart engine
|
||||
2. **Clean up cart** - Always `WC()->cart->empty_cart()` after calculation
|
||||
3. **Session handling** - Use `WC()->session` for chosen shipping method
|
||||
4. **Tax context** - Set both billing and shipping addresses for accurate tax
|
||||
5. **Live rates** - May take 1-2 seconds to calculate, show loading state
|
||||
Reference in New Issue
Block a user