|
@@ -6,6 +6,7 @@ import {
|
|
|
CreateProductVariantInput,
|
|
CreateProductVariantInput,
|
|
|
} from '@vendure/common/lib/generated-types';
|
|
} from '@vendure/common/lib/generated-types';
|
|
|
import { ID } from '@vendure/common/lib/shared-types';
|
|
import { ID } from '@vendure/common/lib/shared-types';
|
|
|
|
|
+import { unique } from '@vendure/common/lib/unique';
|
|
|
|
|
|
|
|
import { RequestContext } from '../../../api/common/request-context';
|
|
import { RequestContext } from '../../../api/common/request-context';
|
|
|
import { TransactionalConnection } from '../../../connection/transactional-connection';
|
|
import { TransactionalConnection } from '../../../connection/transactional-connection';
|
|
@@ -22,6 +23,7 @@ import { ProductAsset } from '../../../entity/product/product-asset.entity';
|
|
|
import { ProductTranslation } from '../../../entity/product/product-translation.entity';
|
|
import { ProductTranslation } from '../../../entity/product/product-translation.entity';
|
|
|
import { Product } from '../../../entity/product/product.entity';
|
|
import { Product } from '../../../entity/product/product.entity';
|
|
|
import { TranslatableSaver } from '../../../service/helpers/translatable-saver/translatable-saver';
|
|
import { TranslatableSaver } from '../../../service/helpers/translatable-saver/translatable-saver';
|
|
|
|
|
+import { RequestContextService } from '../../../service/index';
|
|
|
import { ChannelService } from '../../../service/services/channel.service';
|
|
import { ChannelService } from '../../../service/services/channel.service';
|
|
|
import { StockMovementService } from '../../../service/services/stock-movement.service';
|
|
import { StockMovementService } from '../../../service/services/stock-movement.service';
|
|
|
|
|
|
|
@@ -38,6 +40,7 @@ import { StockMovementService } from '../../../service/services/stock-movement.s
|
|
|
@Injectable()
|
|
@Injectable()
|
|
|
export class FastImporterService {
|
|
export class FastImporterService {
|
|
|
private defaultChannel: Channel;
|
|
private defaultChannel: Channel;
|
|
|
|
|
+ private importCtx: RequestContext;
|
|
|
|
|
|
|
|
/** @internal */
|
|
/** @internal */
|
|
|
constructor(
|
|
constructor(
|
|
@@ -45,20 +48,36 @@ export class FastImporterService {
|
|
|
private channelService: ChannelService,
|
|
private channelService: ChannelService,
|
|
|
private stockMovementService: StockMovementService,
|
|
private stockMovementService: StockMovementService,
|
|
|
private translatableSaver: TranslatableSaver,
|
|
private translatableSaver: TranslatableSaver,
|
|
|
|
|
+ private requestContextService: RequestContextService,
|
|
|
) {}
|
|
) {}
|
|
|
|
|
|
|
|
- async initialize() {
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * @description
|
|
|
|
|
+ * This should be called prior to any of the import methods, as it establishes the
|
|
|
|
|
+ * default Channel as well as the context in which the new entities will be created.
|
|
|
|
|
+ *
|
|
|
|
|
+ * Passing a `channel` argument means that Products and ProductVariants will be assigned
|
|
|
|
|
+ * to that Channel.
|
|
|
|
|
+ */
|
|
|
|
|
+ async initialize(channel?: Channel) {
|
|
|
|
|
+ this.importCtx = channel
|
|
|
|
|
+ ? await this.requestContextService.create({
|
|
|
|
|
+ apiType: 'admin',
|
|
|
|
|
+ channelOrToken: channel,
|
|
|
|
|
+ })
|
|
|
|
|
+ : RequestContext.empty();
|
|
|
this.defaultChannel = await this.channelService.getDefaultChannel();
|
|
this.defaultChannel = await this.channelService.getDefaultChannel();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async createProduct(input: CreateProductInput): Promise<ID> {
|
|
async createProduct(input: CreateProductInput): Promise<ID> {
|
|
|
|
|
+ this.ensureInitialized();
|
|
|
const product = await this.translatableSaver.create({
|
|
const product = await this.translatableSaver.create({
|
|
|
- ctx: RequestContext.empty(),
|
|
|
|
|
|
|
+ ctx: this.importCtx,
|
|
|
input,
|
|
input,
|
|
|
entityType: Product,
|
|
entityType: Product,
|
|
|
translationType: ProductTranslation,
|
|
translationType: ProductTranslation,
|
|
|
beforeSave: async p => {
|
|
beforeSave: async p => {
|
|
|
- p.channels = [this.defaultChannel];
|
|
|
|
|
|
|
+ p.channels = unique([this.defaultChannel, this.importCtx.channel], 'id');
|
|
|
if (input.facetValueIds) {
|
|
if (input.facetValueIds) {
|
|
|
p.facetValues = input.facetValueIds.map(id => ({ id } as any));
|
|
p.facetValues = input.facetValueIds.map(id => ({ id } as any));
|
|
|
}
|
|
}
|
|
@@ -82,8 +101,9 @@ export class FastImporterService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async createProductOptionGroup(input: CreateProductOptionGroupInput): Promise<ID> {
|
|
async createProductOptionGroup(input: CreateProductOptionGroupInput): Promise<ID> {
|
|
|
|
|
+ this.ensureInitialized();
|
|
|
const group = await this.translatableSaver.create({
|
|
const group = await this.translatableSaver.create({
|
|
|
- ctx: RequestContext.empty(),
|
|
|
|
|
|
|
+ ctx: this.importCtx,
|
|
|
input,
|
|
input,
|
|
|
entityType: ProductOptionGroup,
|
|
entityType: ProductOptionGroup,
|
|
|
translationType: ProductOptionGroupTranslation,
|
|
translationType: ProductOptionGroupTranslation,
|
|
@@ -92,8 +112,9 @@ export class FastImporterService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async createProductOption(input: CreateProductOptionInput): Promise<ID> {
|
|
async createProductOption(input: CreateProductOptionInput): Promise<ID> {
|
|
|
|
|
+ this.ensureInitialized();
|
|
|
const option = await this.translatableSaver.create({
|
|
const option = await this.translatableSaver.create({
|
|
|
- ctx: RequestContext.empty(),
|
|
|
|
|
|
|
+ ctx: this.importCtx,
|
|
|
input,
|
|
input,
|
|
|
entityType: ProductOption,
|
|
entityType: ProductOption,
|
|
|
translationType: ProductOptionTranslation,
|
|
translationType: ProductOptionTranslation,
|
|
@@ -103,6 +124,7 @@ export class FastImporterService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async addOptionGroupToProduct(productId: ID, optionGroupId: ID) {
|
|
async addOptionGroupToProduct(productId: ID, optionGroupId: ID) {
|
|
|
|
|
+ this.ensureInitialized();
|
|
|
await this.connection
|
|
await this.connection
|
|
|
.getRepository(Product)
|
|
.getRepository(Product)
|
|
|
.createQueryBuilder()
|
|
.createQueryBuilder()
|
|
@@ -112,6 +134,7 @@ export class FastImporterService {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
async createProductVariant(input: CreateProductVariantInput): Promise<ID> {
|
|
async createProductVariant(input: CreateProductVariantInput): Promise<ID> {
|
|
|
|
|
+ this.ensureInitialized();
|
|
|
if (!input.optionIds) {
|
|
if (!input.optionIds) {
|
|
|
input.optionIds = [];
|
|
input.optionIds = [];
|
|
|
}
|
|
}
|
|
@@ -125,12 +148,12 @@ export class FastImporterService {
|
|
|
delete inputWithoutPrice.price;
|
|
delete inputWithoutPrice.price;
|
|
|
|
|
|
|
|
const createdVariant = await this.translatableSaver.create({
|
|
const createdVariant = await this.translatableSaver.create({
|
|
|
- ctx: RequestContext.empty(),
|
|
|
|
|
|
|
+ ctx: this.importCtx,
|
|
|
input: inputWithoutPrice,
|
|
input: inputWithoutPrice,
|
|
|
entityType: ProductVariant,
|
|
entityType: ProductVariant,
|
|
|
translationType: ProductVariantTranslation,
|
|
translationType: ProductVariantTranslation,
|
|
|
beforeSave: async variant => {
|
|
beforeSave: async variant => {
|
|
|
- variant.channels = [this.defaultChannel];
|
|
|
|
|
|
|
+ variant.channels = unique([this.defaultChannel, this.importCtx.channel], 'id');
|
|
|
const { optionIds } = input;
|
|
const { optionIds } = input;
|
|
|
if (optionIds && optionIds.length) {
|
|
if (optionIds && optionIds.length) {
|
|
|
variant.options = optionIds.map(id => ({ id } as any));
|
|
variant.options = optionIds.map(id => ({ id } as any));
|
|
@@ -158,18 +181,30 @@ export class FastImporterService {
|
|
|
}
|
|
}
|
|
|
if (input.stockOnHand != null && input.stockOnHand !== 0) {
|
|
if (input.stockOnHand != null && input.stockOnHand !== 0) {
|
|
|
await this.stockMovementService.adjustProductVariantStock(
|
|
await this.stockMovementService.adjustProductVariantStock(
|
|
|
- RequestContext.empty(),
|
|
|
|
|
|
|
+ this.importCtx,
|
|
|
createdVariant.id,
|
|
createdVariant.id,
|
|
|
0,
|
|
0,
|
|
|
input.stockOnHand,
|
|
input.stockOnHand,
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
- const variantPrice = new ProductVariantPrice({
|
|
|
|
|
- price: input.price,
|
|
|
|
|
- channelId: this.defaultChannel.id,
|
|
|
|
|
- });
|
|
|
|
|
- variantPrice.variant = createdVariant;
|
|
|
|
|
- await this.connection.getRepository(ProductVariantPrice).save(variantPrice, { reload: false });
|
|
|
|
|
|
|
+ const assignedChannelIds = unique([this.defaultChannel, this.importCtx.channel], 'id').map(c => c.id);
|
|
|
|
|
+ for (const channelId of assignedChannelIds) {
|
|
|
|
|
+ const variantPrice = new ProductVariantPrice({
|
|
|
|
|
+ price: input.price,
|
|
|
|
|
+ channelId,
|
|
|
|
|
+ });
|
|
|
|
|
+ variantPrice.variant = createdVariant;
|
|
|
|
|
+ await this.connection.getRepository(ProductVariantPrice).save(variantPrice, { reload: false });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return createdVariant.id;
|
|
return createdVariant.id;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ private ensureInitialized() {
|
|
|
|
|
+ if (!this.defaultChannel || !this.importCtx) {
|
|
|
|
|
+ throw new Error(
|
|
|
|
|
+ `The FastImporterService must be initialized with a call to 'initialize()' before importing data`,
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|