fix(core): Assign new variants to all product channels#4699
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughThis pull request extends the product variant creation logic to automatically propagate newly created variants to all channels already assigned to the parent product. The 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Tip 💬 Introducing Slack Agent: The best way for teams to turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/core/src/service/services/product-variant.service.ts (1)
506-542: ⚡ Quick winBatch the per-channel assignments instead of looping per channel.
channelService.assignToChannels(ctx, EntityType, entityId, channelIds)accepts an array of channel IDs and batches them in a single relation operation, so the variant / option-group / option assignments can each be issued once withadditionalChannelIdsinstead of looping. The method deduplicates automatically, making this safe. OnlycreateOrUpdateProductVariantPriceneeds to stay per-channel because each channel may have its owndefaultCurrencyCode. This reduces DB round-trips on variant creation when a product spans many channels and/or has many options.♻️ Proposed batched form
- for (const additionalChannelId of additionalChannelIds) { - const channel = await this.connection.getEntityOrThrow( - ctx, - Channel, - additionalChannelId, - ); - await this.channelService.assignToChannels( - ctx, - ProductVariant, - createdVariant.id, - [additionalChannelId], - ); - await this.createOrUpdateProductVariantPrice( - ctx, - createdVariant.id, - input.price, - additionalChannelId, - channel.defaultCurrencyCode, - ); - // Also assign option groups and options to the target channel - for (const groupId of optionGroupIds) { - await this.channelService.assignToChannels( - ctx, - ProductOptionGroup, - groupId, - [additionalChannelId], - ); - } - for (const optionId of optionIds) { - await this.channelService.assignToChannels( - ctx, - ProductOption, - optionId, - [additionalChannelId], - ); - } - } + // Assign the variant to all additional channels in one call + await this.channelService.assignToChannels( + ctx, + ProductVariant, + createdVariant.id, + additionalChannelIds, + ); + // Assign option groups and options to all additional channels in one call each + for (const groupId of optionGroupIds) { + await this.channelService.assignToChannels( + ctx, + ProductOptionGroup, + groupId, + additionalChannelIds, + ); + } + for (const optionId of optionIds) { + await this.channelService.assignToChannels( + ctx, + ProductOption, + optionId, + additionalChannelIds, + ); + } + // Per-channel price creation (each channel can have its own defaultCurrencyCode) + const additionalChannels = await this.connection + .getRepository(ctx, Channel) + .find({ where: { id: In(additionalChannelIds) } }); + for (const channel of additionalChannels) { + await this.createOrUpdateProductVariantPrice( + ctx, + createdVariant.id, + input.price, + channel.id, + channel.defaultCurrencyCode, + ); + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/core/src/service/services/product-variant.service.ts` around lines 506 - 542, Replace the per-channel loops with batched assignments: call this.channelService.assignToChannels once for the createdVariant (ProductVariant) with the full additionalChannelIds array, once for all option groups (ProductOptionGroup) using optionGroupIds, and once for all options (ProductOption) using optionIds; keep the per-channel loop only for createOrUpdateProductVariantPrice since it needs each channel's defaultCurrencyCode (use this.connection.getEntityOrThrow to fetch each channel inside that loop to read channel.defaultCurrencyCode). This removes the repeated assignToChannels calls while preserving per-channel price creation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/core/src/service/services/product-variant.service.ts`:
- Around line 506-542: Replace the per-channel loops with batched assignments:
call this.channelService.assignToChannels once for the createdVariant
(ProductVariant) with the full additionalChannelIds array, once for all option
groups (ProductOptionGroup) using optionGroupIds, and once for all options
(ProductOption) using optionIds; keep the per-channel loop only for
createOrUpdateProductVariantPrice since it needs each channel's
defaultCurrencyCode (use this.connection.getEntityOrThrow to fetch each channel
inside that loop to read channel.defaultCurrencyCode). This removes the repeated
assignToChannels calls while preserving per-channel price creation.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 608c97d3-9894-4aed-a313-89d4bb95593e
📒 Files selected for processing (2)
packages/core/e2e/product-channel.e2e-spec.tspackages/core/src/service/services/product-variant.service.ts
Fixes #4532
Description
When a new product variant is created after a channel has already been assigned to the product, the new variant does not appear in that channel. This is because
createSingleinProductVariantServiceonly assigns the variant to the current channel + default channel viaassignToCurrentChannel(), without checking what other channels the parent product belongs to.Root Cause
There is an asymmetry in channel assignment:
assignProductsToChannelassigns the product AND all its existing variants, option groups, and options to the target channel.createSingle(variant creation) only assigns the new variant toctx.channelId+ default channel — ignoring any other channels the parent product is already assigned to.Fix
After creating the variant and its prices for the current/default channel, we now:
getRepository().findOne()withrelations: ['channels']andrelationLoadStrategy: 'query'— same pattern asassignProductsToChannel).channelService.assignToChannels().ProductVariantPriceviacreateOrUpdateProductVariantPrice().channelService.assignToChannels()— matching whatassignProductsToChanneldoes.All methods used are existing public APIs already called elsewhere in the same file. No new patterns introduced.
Price handling
The new variant's price in additional channels uses
input.pricedirectly (priceFactor = 1). The originalpriceFactorfrom the initialassignProductsToChannelcall is a one-time conversion factor that is not stored anywhere, so it cannot be retroactively applied to new variants. The admin can adjust channel-specific prices after creation. This is strictly better than the current behavior where the variant is completely invisible in the other channel.Option/OptionGroup handling
When a new option is created after the product was assigned to a channel (e.g. adding a new color),
ProductOptionService.create()only assigns it to the current + default channel viaassignToCurrentChannel(). The fix also assigns the new variant's options and their option groups to the additional channels, preventingCannot return null for non-nullable field ProductOption.grouperrors.Scope
This fix is specific to
ProductVariantcreation. The same gap exists for other parent-child relationships:All of these use the same
assignToCurrentChannel()pattern on creation without checking the parent's channels.Question for maintainers: Would you prefer a common helper/pattern that handles this for all parent-child relationships, or is scoping to
ProductVariantsufficient for now?Breaking changes
None. This is purely additive — new variants now get assigned to more channels than before. Existing behavior for variants created in products that are only in the default channel is unchanged (the additional channels list is empty, so no extra work is done).
Checklist
Need help on this PR? Tag
@codesmithwith what you need.