seed.ts
1 import { CreateInventoryLevelInput, ExecArgs } from "@medusajs/framework/types"; 2 import { 3 ContainerRegistrationKeys, 4 Modules, 5 ProductStatus, 6 } from "@medusajs/framework/utils"; 7 import { 8 createApiKeysWorkflow, 9 createInventoryLevelsWorkflow, 10 createProductCategoriesWorkflow, 11 createProductsWorkflow, 12 createRegionsWorkflow, 13 createSalesChannelsWorkflow, 14 createShippingOptionsWorkflow, 15 createShippingProfilesWorkflow, 16 createStockLocationsWorkflow, 17 createTaxRegionsWorkflow, 18 linkSalesChannelsToApiKeyWorkflow, 19 linkSalesChannelsToStockLocationWorkflow, 20 updateStoresWorkflow, 21 } from "@medusajs/medusa/core-flows"; 22 23 export default async function seedDemoData({ container }: ExecArgs) { 24 const logger = container.resolve(ContainerRegistrationKeys.LOGGER); 25 const link = container.resolve(ContainerRegistrationKeys.LINK); 26 const query = container.resolve(ContainerRegistrationKeys.QUERY); 27 const fulfillmentModuleService = container.resolve(Modules.FULFILLMENT); 28 const salesChannelModuleService = container.resolve(Modules.SALES_CHANNEL); 29 const storeModuleService = container.resolve(Modules.STORE); 30 31 const countries = ["gb", "de", "dk", "se", "fr", "es", "it"]; 32 33 logger.info("Seeding store data..."); 34 const [store] = await storeModuleService.listStores(); 35 let defaultSalesChannel = await salesChannelModuleService.listSalesChannels({ 36 name: "Default Sales Channel", 37 }); 38 39 if (!defaultSalesChannel.length) { 40 // create the default sales channel 41 const { result: salesChannelResult } = await createSalesChannelsWorkflow( 42 container 43 ).run({ 44 input: { 45 salesChannelsData: [ 46 { 47 name: "Default Sales Channel", 48 }, 49 ], 50 }, 51 }); 52 defaultSalesChannel = salesChannelResult; 53 } 54 55 await updateStoresWorkflow(container).run({ 56 input: { 57 selector: { id: store.id }, 58 update: { 59 supported_currencies: [ 60 { 61 currency_code: "eur", 62 is_default: true, 63 }, 64 { 65 currency_code: "usd", 66 }, 67 ], 68 default_sales_channel_id: defaultSalesChannel[0].id, 69 }, 70 }, 71 }); 72 logger.info("Seeding region data..."); 73 const { result: regionResult } = await createRegionsWorkflow(container).run({ 74 input: { 75 regions: [ 76 { 77 name: "Europe", 78 currency_code: "eur", 79 countries, 80 payment_providers: ["pp_system_default"], 81 }, 82 ], 83 }, 84 }); 85 const region = regionResult[0]; 86 logger.info("Finished seeding regions."); 87 88 logger.info("Seeding tax regions..."); 89 await createTaxRegionsWorkflow(container).run({ 90 input: countries.map((country_code) => ({ 91 country_code, 92 provider_id: "tp_system" 93 })), 94 }); 95 logger.info("Finished seeding tax regions."); 96 97 logger.info("Seeding stock location data..."); 98 const { result: stockLocationResult } = await createStockLocationsWorkflow( 99 container 100 ).run({ 101 input: { 102 locations: [ 103 { 104 name: "European Warehouse", 105 address: { 106 city: "Copenhagen", 107 country_code: "DK", 108 address_1: "", 109 }, 110 }, 111 ], 112 }, 113 }); 114 const stockLocation = stockLocationResult[0]; 115 116 await link.create({ 117 [Modules.STOCK_LOCATION]: { 118 stock_location_id: stockLocation.id, 119 }, 120 [Modules.FULFILLMENT]: { 121 fulfillment_provider_id: "manual_manual", 122 }, 123 }); 124 125 logger.info("Seeding fulfillment data..."); 126 const shippingProfiles = await fulfillmentModuleService.listShippingProfiles({ 127 type: "default" 128 }) 129 let shippingProfile = shippingProfiles.length ? shippingProfiles[0] : null 130 131 if (!shippingProfile) { 132 const { result: shippingProfileResult } = 133 await createShippingProfilesWorkflow(container).run({ 134 input: { 135 data: [ 136 { 137 name: "Default Shipping Profile", 138 type: "default", 139 }, 140 ], 141 }, 142 }); 143 shippingProfile = shippingProfileResult[0]; 144 } 145 146 const fulfillmentSet = await fulfillmentModuleService.createFulfillmentSets({ 147 name: "European Warehouse delivery", 148 type: "shipping", 149 service_zones: [ 150 { 151 name: "Europe", 152 geo_zones: [ 153 { 154 country_code: "gb", 155 type: "country", 156 }, 157 { 158 country_code: "de", 159 type: "country", 160 }, 161 { 162 country_code: "dk", 163 type: "country", 164 }, 165 { 166 country_code: "se", 167 type: "country", 168 }, 169 { 170 country_code: "fr", 171 type: "country", 172 }, 173 { 174 country_code: "es", 175 type: "country", 176 }, 177 { 178 country_code: "it", 179 type: "country", 180 }, 181 ], 182 }, 183 ], 184 }); 185 186 await link.create({ 187 [Modules.STOCK_LOCATION]: { 188 stock_location_id: stockLocation.id, 189 }, 190 [Modules.FULFILLMENT]: { 191 fulfillment_set_id: fulfillmentSet.id, 192 }, 193 }); 194 195 await createShippingOptionsWorkflow(container).run({ 196 input: [ 197 { 198 name: "Standard Shipping", 199 price_type: "flat", 200 provider_id: "manual_manual", 201 service_zone_id: fulfillmentSet.service_zones[0].id, 202 shipping_profile_id: shippingProfile.id, 203 type: { 204 label: "Standard", 205 description: "Ship in 2-3 days.", 206 code: "standard", 207 }, 208 prices: [ 209 { 210 currency_code: "usd", 211 amount: 10, 212 }, 213 { 214 currency_code: "eur", 215 amount: 10, 216 }, 217 { 218 region_id: region.id, 219 amount: 10, 220 }, 221 ], 222 rules: [ 223 { 224 attribute: "enabled_in_store", 225 value: "true", 226 operator: "eq", 227 }, 228 { 229 attribute: "is_return", 230 value: "false", 231 operator: "eq", 232 }, 233 ], 234 }, 235 { 236 name: "Express Shipping", 237 price_type: "flat", 238 provider_id: "manual_manual", 239 service_zone_id: fulfillmentSet.service_zones[0].id, 240 shipping_profile_id: shippingProfile.id, 241 type: { 242 label: "Express", 243 description: "Ship in 24 hours.", 244 code: "express", 245 }, 246 prices: [ 247 { 248 currency_code: "usd", 249 amount: 10, 250 }, 251 { 252 currency_code: "eur", 253 amount: 10, 254 }, 255 { 256 region_id: region.id, 257 amount: 10, 258 }, 259 ], 260 rules: [ 261 { 262 attribute: "enabled_in_store", 263 value: "true", 264 operator: "eq", 265 }, 266 { 267 attribute: "is_return", 268 value: "false", 269 operator: "eq", 270 }, 271 ], 272 }, 273 ], 274 }); 275 logger.info("Finished seeding fulfillment data."); 276 277 await linkSalesChannelsToStockLocationWorkflow(container).run({ 278 input: { 279 id: stockLocation.id, 280 add: [defaultSalesChannel[0].id], 281 }, 282 }); 283 logger.info("Finished seeding stock location data."); 284 285 logger.info("Seeding publishable API key data..."); 286 const { result: publishableApiKeyResult } = await createApiKeysWorkflow( 287 container 288 ).run({ 289 input: { 290 api_keys: [ 291 { 292 title: "Webshop", 293 type: "publishable", 294 created_by: "", 295 }, 296 ], 297 }, 298 }); 299 const publishableApiKey = publishableApiKeyResult[0]; 300 301 await linkSalesChannelsToApiKeyWorkflow(container).run({ 302 input: { 303 id: publishableApiKey.id, 304 add: [defaultSalesChannel[0].id], 305 }, 306 }); 307 logger.info("Finished seeding publishable API key data."); 308 309 logger.info("Seeding product data..."); 310 311 const { result: categoryResult } = await createProductCategoriesWorkflow( 312 container 313 ).run({ 314 input: { 315 product_categories: [ 316 { 317 name: "Shirts", 318 is_active: true, 319 }, 320 { 321 name: "Sweatshirts", 322 is_active: true, 323 }, 324 { 325 name: "Pants", 326 is_active: true, 327 }, 328 { 329 name: "Merch", 330 is_active: true, 331 }, 332 ], 333 }, 334 }); 335 336 await createProductsWorkflow(container).run({ 337 input: { 338 products: [ 339 { 340 title: "Medusa T-Shirt", 341 category_ids: [ 342 categoryResult.find((cat) => cat.name === "Shirts")!.id, 343 ], 344 description: 345 "Reimagine the feeling of a classic T-shirt. With our cotton T-shirts, everyday essentials no longer have to be ordinary.", 346 handle: "t-shirt", 347 weight: 400, 348 status: ProductStatus.PUBLISHED, 349 shipping_profile_id: shippingProfile.id, 350 images: [ 351 { 352 url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-black-front.png", 353 }, 354 { 355 url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-black-back.png", 356 }, 357 { 358 url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-white-front.png", 359 }, 360 { 361 url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-white-back.png", 362 }, 363 ], 364 options: [ 365 { 366 title: "Size", 367 values: ["S", "M", "L", "XL"], 368 }, 369 { 370 title: "Color", 371 values: ["Black", "White"], 372 }, 373 ], 374 variants: [ 375 { 376 title: "S / Black", 377 sku: "SHIRT-S-BLACK", 378 options: { 379 Size: "S", 380 Color: "Black", 381 }, 382 prices: [ 383 { 384 amount: 10, 385 currency_code: "eur", 386 }, 387 { 388 amount: 15, 389 currency_code: "usd", 390 }, 391 ], 392 }, 393 { 394 title: "S / White", 395 sku: "SHIRT-S-WHITE", 396 options: { 397 Size: "S", 398 Color: "White", 399 }, 400 prices: [ 401 { 402 amount: 10, 403 currency_code: "eur", 404 }, 405 { 406 amount: 15, 407 currency_code: "usd", 408 }, 409 ], 410 }, 411 { 412 title: "M / Black", 413 sku: "SHIRT-M-BLACK", 414 options: { 415 Size: "M", 416 Color: "Black", 417 }, 418 prices: [ 419 { 420 amount: 10, 421 currency_code: "eur", 422 }, 423 { 424 amount: 15, 425 currency_code: "usd", 426 }, 427 ], 428 }, 429 { 430 title: "M / White", 431 sku: "SHIRT-M-WHITE", 432 options: { 433 Size: "M", 434 Color: "White", 435 }, 436 prices: [ 437 { 438 amount: 10, 439 currency_code: "eur", 440 }, 441 { 442 amount: 15, 443 currency_code: "usd", 444 }, 445 ], 446 }, 447 { 448 title: "L / Black", 449 sku: "SHIRT-L-BLACK", 450 options: { 451 Size: "L", 452 Color: "Black", 453 }, 454 prices: [ 455 { 456 amount: 10, 457 currency_code: "eur", 458 }, 459 { 460 amount: 15, 461 currency_code: "usd", 462 }, 463 ], 464 }, 465 { 466 title: "L / White", 467 sku: "SHIRT-L-WHITE", 468 options: { 469 Size: "L", 470 Color: "White", 471 }, 472 prices: [ 473 { 474 amount: 10, 475 currency_code: "eur", 476 }, 477 { 478 amount: 15, 479 currency_code: "usd", 480 }, 481 ], 482 }, 483 { 484 title: "XL / Black", 485 sku: "SHIRT-XL-BLACK", 486 options: { 487 Size: "XL", 488 Color: "Black", 489 }, 490 prices: [ 491 { 492 amount: 10, 493 currency_code: "eur", 494 }, 495 { 496 amount: 15, 497 currency_code: "usd", 498 }, 499 ], 500 }, 501 { 502 title: "XL / White", 503 sku: "SHIRT-XL-WHITE", 504 options: { 505 Size: "XL", 506 Color: "White", 507 }, 508 prices: [ 509 { 510 amount: 10, 511 currency_code: "eur", 512 }, 513 { 514 amount: 15, 515 currency_code: "usd", 516 }, 517 ], 518 }, 519 ], 520 sales_channels: [ 521 { 522 id: defaultSalesChannel[0].id, 523 }, 524 ], 525 }, 526 { 527 title: "Medusa Sweatshirt", 528 category_ids: [ 529 categoryResult.find((cat) => cat.name === "Sweatshirts")!.id, 530 ], 531 description: 532 "Reimagine the feeling of a classic sweatshirt. With our cotton sweatshirt, everyday essentials no longer have to be ordinary.", 533 handle: "sweatshirt", 534 weight: 400, 535 status: ProductStatus.PUBLISHED, 536 shipping_profile_id: shippingProfile.id, 537 images: [ 538 { 539 url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-front.png", 540 }, 541 { 542 url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-back.png", 543 }, 544 ], 545 options: [ 546 { 547 title: "Size", 548 values: ["S", "M", "L", "XL"], 549 }, 550 ], 551 variants: [ 552 { 553 title: "S", 554 sku: "SWEATSHIRT-S", 555 options: { 556 Size: "S", 557 }, 558 prices: [ 559 { 560 amount: 10, 561 currency_code: "eur", 562 }, 563 { 564 amount: 15, 565 currency_code: "usd", 566 }, 567 ], 568 }, 569 { 570 title: "M", 571 sku: "SWEATSHIRT-M", 572 options: { 573 Size: "M", 574 }, 575 prices: [ 576 { 577 amount: 10, 578 currency_code: "eur", 579 }, 580 { 581 amount: 15, 582 currency_code: "usd", 583 }, 584 ], 585 }, 586 { 587 title: "L", 588 sku: "SWEATSHIRT-L", 589 options: { 590 Size: "L", 591 }, 592 prices: [ 593 { 594 amount: 10, 595 currency_code: "eur", 596 }, 597 { 598 amount: 15, 599 currency_code: "usd", 600 }, 601 ], 602 }, 603 { 604 title: "XL", 605 sku: "SWEATSHIRT-XL", 606 options: { 607 Size: "XL", 608 }, 609 prices: [ 610 { 611 amount: 10, 612 currency_code: "eur", 613 }, 614 { 615 amount: 15, 616 currency_code: "usd", 617 }, 618 ], 619 }, 620 ], 621 sales_channels: [ 622 { 623 id: defaultSalesChannel[0].id, 624 }, 625 ], 626 }, 627 { 628 title: "Medusa Sweatpants", 629 category_ids: [ 630 categoryResult.find((cat) => cat.name === "Pants")!.id, 631 ], 632 description: 633 "Reimagine the feeling of classic sweatpants. With our cotton sweatpants, everyday essentials no longer have to be ordinary.", 634 handle: "sweatpants", 635 weight: 400, 636 status: ProductStatus.PUBLISHED, 637 shipping_profile_id: shippingProfile.id, 638 images: [ 639 { 640 url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatpants-gray-front.png", 641 }, 642 { 643 url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatpants-gray-back.png", 644 }, 645 ], 646 options: [ 647 { 648 title: "Size", 649 values: ["S", "M", "L", "XL"], 650 }, 651 ], 652 variants: [ 653 { 654 title: "S", 655 sku: "SWEATPANTS-S", 656 options: { 657 Size: "S", 658 }, 659 prices: [ 660 { 661 amount: 10, 662 currency_code: "eur", 663 }, 664 { 665 amount: 15, 666 currency_code: "usd", 667 }, 668 ], 669 }, 670 { 671 title: "M", 672 sku: "SWEATPANTS-M", 673 options: { 674 Size: "M", 675 }, 676 prices: [ 677 { 678 amount: 10, 679 currency_code: "eur", 680 }, 681 { 682 amount: 15, 683 currency_code: "usd", 684 }, 685 ], 686 }, 687 { 688 title: "L", 689 sku: "SWEATPANTS-L", 690 options: { 691 Size: "L", 692 }, 693 prices: [ 694 { 695 amount: 10, 696 currency_code: "eur", 697 }, 698 { 699 amount: 15, 700 currency_code: "usd", 701 }, 702 ], 703 }, 704 { 705 title: "XL", 706 sku: "SWEATPANTS-XL", 707 options: { 708 Size: "XL", 709 }, 710 prices: [ 711 { 712 amount: 10, 713 currency_code: "eur", 714 }, 715 { 716 amount: 15, 717 currency_code: "usd", 718 }, 719 ], 720 }, 721 ], 722 sales_channels: [ 723 { 724 id: defaultSalesChannel[0].id, 725 }, 726 ], 727 }, 728 { 729 title: "Medusa Shorts", 730 category_ids: [ 731 categoryResult.find((cat) => cat.name === "Merch")!.id, 732 ], 733 description: 734 "Reimagine the feeling of classic shorts. With our cotton shorts, everyday essentials no longer have to be ordinary.", 735 handle: "shorts", 736 weight: 400, 737 status: ProductStatus.PUBLISHED, 738 shipping_profile_id: shippingProfile.id, 739 images: [ 740 { 741 url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/shorts-vintage-front.png", 742 }, 743 { 744 url: "https://medusa-public-images.s3.eu-west-1.amazonaws.com/shorts-vintage-back.png", 745 }, 746 ], 747 options: [ 748 { 749 title: "Size", 750 values: ["S", "M", "L", "XL"], 751 }, 752 ], 753 variants: [ 754 { 755 title: "S", 756 sku: "SHORTS-S", 757 options: { 758 Size: "S", 759 }, 760 prices: [ 761 { 762 amount: 10, 763 currency_code: "eur", 764 }, 765 { 766 amount: 15, 767 currency_code: "usd", 768 }, 769 ], 770 }, 771 { 772 title: "M", 773 sku: "SHORTS-M", 774 options: { 775 Size: "M", 776 }, 777 prices: [ 778 { 779 amount: 10, 780 currency_code: "eur", 781 }, 782 { 783 amount: 15, 784 currency_code: "usd", 785 }, 786 ], 787 }, 788 { 789 title: "L", 790 sku: "SHORTS-L", 791 options: { 792 Size: "L", 793 }, 794 prices: [ 795 { 796 amount: 10, 797 currency_code: "eur", 798 }, 799 { 800 amount: 15, 801 currency_code: "usd", 802 }, 803 ], 804 }, 805 { 806 title: "XL", 807 sku: "SHORTS-XL", 808 options: { 809 Size: "XL", 810 }, 811 prices: [ 812 { 813 amount: 10, 814 currency_code: "eur", 815 }, 816 { 817 amount: 15, 818 currency_code: "usd", 819 }, 820 ], 821 }, 822 ], 823 sales_channels: [ 824 { 825 id: defaultSalesChannel[0].id, 826 }, 827 ], 828 }, 829 ], 830 }, 831 }); 832 logger.info("Finished seeding product data."); 833 834 logger.info("Seeding inventory levels."); 835 836 const { data: inventoryItems } = await query.graph({ 837 entity: "inventory_item", 838 fields: ["id"], 839 }); 840 841 const inventoryLevels: CreateInventoryLevelInput[] = []; 842 for (const inventoryItem of inventoryItems) { 843 const inventoryLevel = { 844 location_id: stockLocation.id, 845 stocked_quantity: 1000000, 846 inventory_item_id: inventoryItem.id, 847 }; 848 inventoryLevels.push(inventoryLevel); 849 } 850 851 await createInventoryLevelsWorkflow(container).run({ 852 input: { 853 inventory_levels: inventoryLevels, 854 }, 855 }); 856 857 logger.info("Finished seeding inventory levels data."); 858 }