# NocoDB Orders Table Schema

## Proposed Schema (Updated 2025-11-03)

### Core Identification
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `Id` | Number | Yes (auto) | NocoDB record ID |
| `CreatedAt` | DateTime | Yes (auto) | Record creation timestamp |
| `UpdatedAt` | DateTime | Yes (auto) | Record last update timestamp |
| `orderId` | String | Yes | Unique order identifier (UUID) |
| `status` | Enum | Yes | Order status: `pending`, `paid`, `fulfilled`, `cancelled` |
| `source` | String | No | Order source: `web`, `mobile-app`, `manual`, `api` |

### Payment Information
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `stripeSessionId` | String | No | Stripe Checkout Session ID |
| `stripePaymentIntent` | String | No | Stripe Payment Intent ID (for refunds) |
| `baseAmount` | String | Yes | Pre-fee amount in cents (e.g., "160174") |
| `processingFee` | String | Yes | Stripe processing fee in cents (e.g., "4805") |
| `totalAmount` | String | Yes | Total charged amount in cents (baseAmount + processingFee) |
| `currency` | String | Yes | Currency code: `USD`, `EUR`, `GBP`, `CHF` |
| `amountUSD` | String | No | Amount converted to USD for reporting |
| `paymentMethod` | String | No | Payment method type (e.g., "card", "bank_transfer") |

### Carbon Offset Details
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `co2Tons` | String | Yes | Tons of CO2 offset (e.g., "6.73") |
| `portfolioId` | String | Yes | Wren portfolio ID (e.g., "37") |
| `portfolioName` | String | No | Human-readable portfolio name |
| `wrenOrderId` | String | No | Wren API order ID (populated after fulfillment) |
| `certificateUrl` | String | No | URL to offset certificate |
| `fulfilledAt` | DateTime | No | Timestamp when order was fulfilled with Wren |

### Customer Information
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `customerName` | String | Yes | Customer display name (business or individual) |
| `customerEmail` | String | Yes | Customer email address |
| `customerPhone` | String | No | Customer phone (if phone collection enabled in Stripe) |
| `businessName` | String | No | Business name for B2B purchases |
| `stripeCustomerId` | String | No | Stripe Customer ID (for recurring customers) |
| `taxIdType` | String | No | Tax ID type (e.g., "eu_vat", "us_ein", "gb_vat") |
| `taxIdValue` | String | No | Tax identification number |
| `billingCity` | String | No | Billing address city |
| `billingCountry` | String | No | Billing address country code (e.g., "FR") |
| `billingLine1` | String | No | Billing address line 1 |
| `billingLine2` | String | No | Billing address line 2 |
| `billingPostalCode` | String | No | Billing address postal/zip code |
| `billingState` | String | No | Billing address state/region |

### Vessel Information (Optional - for yacht calculations)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `vesselName` | String | No | Name of vessel |
| `imoNumber` | String | No | IMO vessel identification number |
| `vesselType` | String | No | Type of vessel (e.g., "Motor Yacht") |
| `vesselLength` | String | No | Vessel length in meters |

### Trip Details (Optional - for trip-based calculations)
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `departurePort` | String | No | Departure port name |
| `arrivalPort` | String | No | Arrival port name |
| `distance` | String | No | Distance in nautical miles |
| `avgSpeed` | String | No | Average speed in knots |
| `duration` | String | No | Trip duration in hours |
| `enginePower` | String | No | Engine power in horsepower |

### Administrative
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `notes` | LongText | No | Internal admin notes |

---

## Changes from Original Schema

### ✅ Added Fields
**Payment & Stripe Integration:**
- `stripeSessionId` - Link to Stripe Checkout Session
- `stripePaymentIntent` - Link to Stripe Payment Intent
- `stripeCustomerId` - Reusable Stripe Customer ID
- `baseAmount` - Pre-fee amount from Stripe metadata
- `processingFee` - Stripe fee from metadata

**Billing Address:**
- `billingCity` - From Stripe address
- `billingCountry` - From Stripe address
- `billingLine1` - From Stripe address
- `billingLine2` - From Stripe address
- `billingPostalCode` - From Stripe address
- `billingState` - From Stripe address

**B2B Customer Support:**
- `businessName` - Business name for B2B purchases
- `taxIdType` - Tax ID type (eu_vat, us_ein, gb_vat, etc.)
- `taxIdValue` - Tax identification number

### ⚠️ Modified Fields
- `customerPhone` - Now optional, populated from Stripe if phone collection is enabled
- `customerName` - Now serves as display name (either business_name or individual_name)

### ❌ Removed Fields
- `customerCompany` - Not provided by Stripe, can be added manually if needed
- `paymentReference` - Redundant with `stripePaymentIntent`

### ⚠️ Made Optional
All vessel and trip fields are now optional since they only apply to specific order types (not all orders are for vessel trips).

---

## Stripe Webhook Mapping

When receiving Stripe webhook `checkout.session.completed`:

```typescript
{
  // Payment Information
  stripeSessionId: session.id,
  stripePaymentIntent: session.payment_intent,
  baseAmount: session.metadata.baseAmount, // in cents
  processingFee: session.metadata.processingFee, // in cents
  totalAmount: session.amount_total.toString(), // in cents
  currency: session.currency,
  paymentMethod: session.payment_method_types[0], // 'card', 'us_bank_account', etc.

  // Carbon Offset Details
  co2Tons: session.metadata.tons,
  portfolioId: session.metadata.portfolioId,

  // Customer Information
  customerName: session.customer_details.name, // Display name (business or individual)
  customerEmail: session.customer_details.email,
  customerPhone: session.customer_details.phone, // if phone collection enabled

  // Business Customer Fields (B2B)
  businessName: session.customer_details.business_name, // For B2B purchases
  stripeCustomerId: session.customer, // Reusable customer ID

  // Tax Collection (if enabled)
  taxIdType: session.customer_details.tax_ids?.[0]?.type, // 'eu_vat', 'us_ein', etc.
  taxIdValue: session.customer_details.tax_ids?.[0]?.value, // Tax number

  // Billing Address
  billingCity: session.customer_details.address?.city,
  billingCountry: session.customer_details.address?.country,
  billingLine1: session.customer_details.address?.line1,
  billingLine2: session.customer_details.address?.line2,
  billingPostalCode: session.customer_details.address?.postal_code,
  billingState: session.customer_details.address?.state,

  // Order Status
  status: 'paid'
}
```

### Real-World Example (Business Purchase)

From actual Stripe payload `evt_1SPPa3Pdj1mnVT5kscrqB21t`:

```typescript
{
  stripeSessionId: "cs_test_b1HSYDGs73Ail2Vumu0qC3yu96ce9X4qnozsDr5hDwRndpZOsq8H47flLc",
  stripePaymentIntent: "pi_3SPPa2Pdj1mnVT5k2qsmDiV1",
  stripeCustomerId: "cus_TM7pU6vRGh0N5N",

  baseAmount: "16023588", // $160,235.88
  processingFee: "480708", // $4,807.08
  totalAmount: "16504296", // $165,042.96
  currency: "usd",

  co2Tons: "673.26",
  portfolioId: "37",

  customerName: "LetsBe Solutions LLC", // Business name used as display name
  customerEmail: "matt@letsbe.solutions",
  customerPhone: "+33633219796",
  businessName: "LetsBe Solutions LLC",

  taxIdType: "eu_vat",
  taxIdValue: "FRAB123456789",

  billingLine1: "108 Avenue du Trois Septembre",
  billingLine2: null,
  billingCity: "Cap-d'Ail",
  billingState: null,
  billingPostalCode: "06320",
  billingCountry: "FR",

  paymentMethod: "card",
  status: "paid"
}
```

---

## Field Type Notes

**Why String for numeric fields?**
NocoDB stores all custom fields as strings by default. Numeric calculations should be done in application code by parsing these strings. This prevents precision issues with currency and decimal values.

**Date/DateTime fields:**
- `CreatedAt`, `UpdatedAt`, `fulfilledAt` use NocoDB's DateTime type
- ISO 8601 format: `2025-11-03T14:30:00.000Z`

**Enum constraints:**
- `status`: Must be one of `pending`, `paid`, `fulfilled`, `cancelled`
- `currency`: Must be one of `USD`, `EUR`, `GBP`, `CHF`
- `source`: Typically `web`, `mobile-app`, `manual`, or `api`
