SMILE商品インポート Product/Variant割当補足仕様(2026-03-03)
背景
- SMILE 側の商品データには、Vendure の
Product に相当する親概念がない。
- そのため、複数の
商品コード を1つの Product に集約する運用根拠を持てない。
決定事項
- SMILE 商品CSV取り込み時は、
商品コード が異なる行を 別 Product として扱う。
- 上記により、
1 Product = 1 Variant を import 時の必須構造として採る。
- ただしこれは 環境に依らず SMILE import 時に守る必須ルール であり、seed
済み商品や storefront へ公開する curated 商品の正規形そのものを定義するものではない。
- 公開形では、単一 variant 商品なら option group なしを許容し、複数
Variant を持つ Product に整理する場合は option groups を伴うものとする。
- import 後にユーザーが regroup / 整理する前提とする。
- 初回取り込み後、Vendure 側で複数 SKU を同一 Product に手動 regroup
する運用を許容する。
初期商品コード(初期商品コード)/親商品コード
は、Product 集約キーとして使用しない。
誤解防止メモ(2026-04-13追記)
1 Product = 1 Variant は、環境に依らず SMILE importer が守る必須ルールであり、商品モデリングの推奨形ではない。
- このルールを採る理由は、SMILE 側に Vendure の Product 親概念がなく、サイズ・色・容量などの option group を機械的に束ねる確実な根拠もないため。
- したがって、取り込み後にユーザーが SKU を regroup して、単一 variant 商品のまま保つか、あるいは
1 Product + 複数 Variant + option groups へ整理することは、想定内の通常運用である。
- seed data / curated data / storefront 公開データを設計するときは、SMILE import で採る初期形をそのまま正として扱ってはいけない。
- 「SMILE import では単独 SKU Product が多い」こと自体は仕様どおりであり、バグ判断は import 必須ルールの形を公開形へ持ち込んでいないか を基準に行う。
割当ルール
- Product 識別:
商品コード
- Variant 識別(upsertキー):
商品コード
- したがって、商品コードが異なる限り Product は統合しない。
- 再インポート時に既存 Variant が見つかった場合は、その Variant を更新する。
- 再インポート時に既存 Variant が見つからない場合は、新しい Product と Variant を作成する。
- 再インポート時の親 Product 更新は、旧 canonical の
smile-<product-code> または
現行 canonical の <product-code> 形式の slug を持つ単独 SKU Product に限定する。
- 手動 regroup 済み Product には再インポートで上書きを行わない。
SMILE行データの保持先
- SKU ごとに異なる
規格 は ProductVariant.customFields.specification
に保存する。
categoryCode
が SMILE の部門コード相当で必要な場合は、共有 Product 属性ではなく smileRaw
に保存するだけに留め、プログラムから参照しない。
- SKU ごとに異なる SMILE 元行データは
ProductVariant.customFields.smileRaw
に保存する。
- これにより、手動 regroup 後も SKU 単位の属性を失わない。
- Product / Variant の責務分離と regroup 禁止条件は
docs/specifications/2026-03-smile-product-field-responsibility.md
を正本とする。
影響範囲上の注意
- 本補足で Variant 側へ寄せる対象は、
規格 と SMILE 元行データのような
SKU 差分が明確で storefront の共有導線に使わない項目に限定する。
brandCode は販売表現上の共有属性として Product 正本に固定する。
categoryCode
は SMILE の部門コード相当の参考値として扱い、Product の共有属性にはしない。
directShippingEligible は SKU ごとの配送制約(isDirectShippingOnly と同階層)なので Variant 正本とし、Product 側には持たない。
- 購入数量制限(
minimumQuantity / purchaseUnit / maxPerOrder)は PurchaseLimitRule を正本とし、Product / ProductVariant custom field 側には持たない。
consent* は 2026-03 の consent 横断改修で
Variant 優先 / Product フォールバック
へ切り替え、SKU 単位の同意要件を Variant に保持できるようにする。詳細は
docs/specifications/2026-03-consent-scope-product-variant.md を正本とする。
- 上記の Product 側項目は、手動 regroup する SKU 同士で同値であることを前提に運用する。
- 特に
brandCode は Product の共有属性なので、異なる brandCode
を持つ SKU を同一 Product に regroup してはいけない。
directShippingEligible は Variant 正本なので、SKU 間で値が混在しても同一 Product に regroup してよい。
CSV解釈とバリデーション表示
- SMILE 商品CSVの読み込みでは、各行ごとにタブ区切り / カンマ区切りを判定して列を解釈する。
- したがって、同一ファイル内にタブ区切り行とカンマ区切り行が混在していても、
商品コード・商品名・標準売上単価
の読取りを継続する。
- Vendure Dashboard のプレビューと実取り込みは同じ解釈ルールを用いる。
- バリデーション結果と取り込みエラー詳細は、先頭20件のみではなく全件を表示対象とする。運用上は行番号を基準に修正対象を特定する。
インポート巻き戻し
- SMILE 商品インポートは、取り込み成功時に巻き戻し用スナップショットを履歴へ保存する。
- 巻き戻しは履歴1件単位で実行し、SKU 単位の部分指定は行わない。
- 既存 SKU 更新の巻き戻しでは、
ProductVariant の sku / price / name /
customFields を取り込み前スナップショットへ戻す。
- 新規 SKU 作成の巻き戻しでは、同時に新しい
Product も作っていた場合は
Product ごと softDelete、既存 Product 配下へ SKU だけ追加した場合は
ProductVariant を softDelete する。
- 安全性のため、次の条件では巻き戻しを拒否する。
- より新しい PRODUCT インポート履歴が存在する
- 対象
Product / ProductVariant の updatedAt
が当該インポート完了後になっている
- 既に巻き戻し済み、または巻き戻しメタデータのない古い履歴である
- Vendure Dashboard の履歴一覧では、実行時ガードと同じく同種別の最新インポート履歴だけを
巻き戻し可能として表示し、それ以前の履歴は
rollbackUnavailableReason 付きで無効表示する。
- regroup 後に対象 SKU / Product が更新されているケースは
updatedAt
ガードで拒否する。つまり、regroup 済み SKU を安全に戻せるのは、当該インポート以降に追加更新が入っていない場合だけとする。
適用範囲
- 本補足は、SMILE 商品CSVから Vendure へ取り込む実装・運用に適用する。
- 既存の追加要件補足(
docs/specifications/ 配下)と同等の正本として扱う。