コンテンツにスキップ

配送計算プラグイン (Shipping Calculator Plugin)

概要

Ritsubiのブランド別配送料金と直送モード(+10%サーチャージ)を計算するVendureカスタムプラグイン。複雑な配送料金体系とゾーン別料金設定に対応します。

互換性レンジ

  • Vendure: 3.5.x(apps/vendure-server は ^3.5.1 を使用)
  • Node.js: >=22.11.0(apps/vendure-server/package.json の engines に準拠)

パッケージ情報

  • パッケージ名: @ritsubi/shipping-calculator-plugin
  • パス: /packages/plugins/shipping-calculator
  • エントリーポイント: src/index.ts

実装機能

1. ブランド別配送料金計算

商品ブランド(エクスビアンス、メソセウティカル、美容機器消耗品)ごとに異なる配送料金を適用。

ブランド別配送料金例

ブランド 基本配送料 無料配送閾値
エクスビアンス 800円 10,000円以上
メソセウティカル 1,200円 15,000円以上
美容機器消耗品 1,500円 20,000円以上

2. 直送モード追加料金(10%サーチャージ)

顧客が「ユーザー直送モード」を選択した場合、注文小計の10%を配送料に追加。

直送モード計算式

基本配送料 = ブランド別配送料金
直送サーチャージ = 注文小計 × 0.10
最終配送料 = 基本配送料 + 直送サーチャージ

例: 注文小計50,000円の場合

基本配送料: 800円
直送サーチャージ: 50,000円 × 0.10 = 5,000円
最終配送料: 800円 + 5,000円 = 5,800円

3. 配送ゾーン別料金設定

日本国内の配送エリアをゾーン分けし、エリアごとに異なる配送料金を設定。

配送ゾーン例

ゾーン 対象地域 追加料金
通常エリア 本州・四国・九州 +0円
遠隔地 北海道 +500円
離島 沖縄・離島 +1,000円

4. 無料配送閾値管理

購入金額が一定額を超えた場合、配送料を無料にする制御。

  • ブランドごとに異なる閾値を設定可能
  • キャンペーン期間中の一時的な閾値変更に対応

5. 特別取り扱い料金

冷蔵・冷凍配送、大型商品など、特別な取り扱いが必要な商品に追加料金を適用。

技術仕様

プラグイン設定オプション

// プラグインは現在オプションなしで動作
export class RitsubiShippingCalculatorPlugin {
  static init(options?: {}) {
    this.options = options ?? {};
    return RitsubiShippingCalculatorPlugin;
  }
}

プラグイン初期化

import { RitsubiShippingCalculatorPlugin } from '@ritsubi/shipping-calculator-plugin';

export const config: VendureConfig = {
  plugins: [RitsubiShippingCalculatorPlugin.init()],
  shippingOptions: {
    shippingCalculators: [
      // RitsubiShippingCalculatorは自動登録される
    ],
  },
};

エンティティ

ShippingModeEntity

配送モード(通常配送 or 直送モード)を管理するエンティティ。

@Entity()
export class ShippingModeEntity extends VendureEntity {
  @Column()
  orderId: string;

  @Column('enum', { enum: ['STANDARD', 'DIRECT_SHIPPING'] })
  mode: ShippingMode;

  @Column('decimal', { precision: 10, scale: 2 })
  surchargeAmount: number; // 直送サーチャージ金額

  @Column('decimal', { precision: 5, scale: 2 })
  surchargeRate: number; // サーチャージ率(通常0.10 = 10%)

  @Column()
  appliedAt: Date;
}

BrandShippingRuleEntity

ブランドごとの配送ルールを定義するエンティティ。

@Entity()
export class BrandShippingRuleEntity extends VendureEntity {
  @Column()
  brandCode: string;

  @Column()
  brandName: string;

  @Column('decimal', { precision: 10, scale: 2 })
  baseShippingFee: number; // 基本配送料

  @Column('decimal', { precision: 10, scale: 2 })
  freeShippingThreshold: number; // 無料配送閾値

  @Column('jsonb')
  zoneSpecificFees: Record<string, number>; // ゾーン別追加料金

  @Column()
  effectiveFrom: Date;

  @Column({ nullable: true })
  effectiveTo: Date;
}

サービス

RitsubiShippingCalculatorService

配送料金計算のコアロジックを担当するサービス。

主要メソッド:

  • calculate(ctx: RequestContext, order: Order): Promise<PriceCalculationResult>
  • 注文全体の配送料を計算
  • calculateBrandShipping(brandCode: string, orderTotal: number, zone: string): Promise<number>
  • ブランド別配送料を計算
  • calculateDirectShippingSurcharge(orderSubtotal: number, rate: number): Promise<number>
  • 直送サーチャージを計算
  • isFreeShippingEligible(brandCode: string, orderTotal: number): Promise<boolean>
  • 無料配送対象かどうかを判定

ShippingModeService

配送モードの管理を行うサービス。

主要メソッド:

  • setShippingMode(orderId: ID, mode: ShippingMode): Promise<void>
  • 注文の配送モードを設定
  • getShippingMode(orderId: ID): Promise<ShippingMode>
  • 注文の配送モードを取得
  • calculateSurcharge(orderId: ID): Promise<number>
  • 直送サーチャージ金額を計算

Vendure ShippingCalculator 統合

RitsubiShippingCalculator

Vendureの ShippingCalculator インターフェースを実装。

const RitsubiShippingCalculator = new ShippingCalculator({
  code: 'ritsubi-shipping-calculator',
  description: [
    {
      languageCode: LanguageCode.ja,
      value: 'ブランド別配送料金と直送サーチャージを計算',
    },
  ],
  args: {},
  calculate: (ctx, order, _args, _method) => {
    return calculatorService.calculate(ctx, order);
  },
});

対応する要件

要件定義書との対応

  • 直送モード追加料金: 注文小計の10%サーチャージ計算
  • ブランド別配送料金: エクスビアンス、メソセウティカル、美容機器消耗品の配送料金体系
  • 配送ゾーン管理: 日本国内のエリア別料金設定

使用例

フロントエンド(Next.js)での使用

// 配送料をプレビュー
const { data } = await apolloClient.query({
  query: PREVIEW_SHIPPING_COST,
  variables: {
    orderId: currentOrder.id,
    shippingMode: 'DIRECT_SHIPPING', // or 'STANDARD'
    zone: 'HOKKAIDO',
  },
});

console.log(`基本配送料: ${data.baseShippingFee}円`);
console.log(`直送サーチャージ: ${data.surcharge}円`);
console.log(`最終配送料: ${data.totalShippingCost}円`);

配送モードの切り替え

// 直送モードに切り替え
await apolloClient.mutate({
  mutation: SET_SHIPPING_MODE,
  variables: {
    orderId: currentOrder.id,
    mode: 'DIRECT_SHIPPING',
  },
});

// 配送料を再計算
await apolloClient.mutate({
  mutation: RECALCULATE_SHIPPING,
  variables: { orderId: currentOrder.id },
});

管理画面での使用

// ブランド別配送ルール設定
await brandShippingRuleService.createRule({
  brandCode: 'EXUVIANCE',
  brandName: 'エクスビアンス',
  baseShippingFee: 800,
  freeShippingThreshold: 10000,
  zoneSpecificFees: {
    HOKKAIDO: 500,
    OKINAWA: 1000,
  },
});

// 配送料金のバッチ再計算
await shippingCalculatorService.recalculateAllPendingOrders();

計算フロー

配送料計算の詳細フロー

1. 注文内容を取得
   ├─ 商品のブランドを識別
   ├─ 注文小計を取得
   └─ 配送先ゾーンを取得

2. ブランド別基本配送料を計算
   ├─ ブランドコードからルールを取得
   ├─ 配送ゾーン追加料金を適用
   └─ 無料配送閾値をチェック

3. 直送モードの場合
   ├─ 注文小計 × 10% を計算
   └─ サーチャージを追加

4. 特別取り扱い料金を加算
   └─ 冷蔵・大型商品の追加料金

5. 最終配送料を返す

データモデル

カスタムフィールド

  • Order.shippingMode - 配送モード(STANDARD or DIRECT_SHIPPING)
  • Order.directShippingSurcharge - 直送サーチャージ金額
  • Order.shippingZone - 配送ゾーン
  • Product.brandCode - 商品ブランドコード
  • Product.requiresSpecialHandling - 特別取り扱い要否

セキュリティ考慮事項

  1. 配送料金の検証: フロントエンドからの配送料は信頼せず、バックエンドで再計算
  2. モード変更権限: 配送モード変更は顧客本人または管理者のみ
  3. 料金履歴の保存: すべての配送料計算結果を記録

パフォーマンス最適化

  • 配送ルールキャッシング: ブランド別配送ルールをRedisにキャッシュ
  • バッチ計算: 大量の配送料再計算はバックグラウンドで処理
  • ゾーン情報の前処理: 郵便番号からゾーンへの変換を事前にキャッシュ

トラブルシューティング

よくある問題

問題: 配送料が異常に高い

  • 原因: 直送サーチャージが二重に適用されている
  • 解決: 配送モードの設定を確認

問題: 無料配送が適用されない

  • 原因: ブランド別配送ルールの閾値設定ミス
  • 解決: BrandShippingRuleEntityfreeShippingThreshold を確認

関連ドキュメント

今後の拡張予定

  • 配送日時指定機能
  • 配送業者別料金設定
  • リアルタイム配送料見積もりAPI連携
  • 梱包サイズ別配送料計算