MigrateAsteriaOrdersCommandTest.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. <?php
  2. namespace Webkul\BagistoApi\Tests\Unit\Migration;
  3. use Illuminate\Support\Facades\Cache;
  4. use Illuminate\Support\Facades\DB;
  5. use Illuminate\Support\Facades\Schema;
  6. use Webkul\BagistoApi\Tests\BagistoApiTestCase;
  7. use Webkul\Core\Models\Channel;
  8. use Webkul\Customer\Models\Customer;
  9. use Webkul\Product\Models\Product;
  10. use Webkul\Sales\Models\Order;
  11. use Webkul\Sales\Models\OrderAddress;
  12. use Webkul\Sales\Models\OrderItem;
  13. use Webkul\Sales\Models\OrderPayment;
  14. class MigrateAsteriaOrdersCommandTest extends BagistoApiTestCase
  15. {
  16. private string $sqlitePath;
  17. private string $connection = 'asteria_test';
  18. public function setUp(): void
  19. {
  20. parent::setUp();
  21. if (! Schema::hasColumn('orders', 'migrated_from_asteria_id')) {
  22. $this->markTestSkipped('Run php artisan migrate to add Asteria order columns.');
  23. }
  24. $this->seedRequiredData();
  25. Cache::forget('migrate_asteria_orders_last_id');
  26. $this->sqlitePath = sys_get_temp_dir().'/asteria_order_cmd_'.uniqid('', true).'.sqlite';
  27. touch($this->sqlitePath);
  28. config()->set('database.connections.'.$this->connection, [
  29. 'driver' => 'sqlite',
  30. 'database' => $this->sqlitePath,
  31. 'prefix' => '',
  32. 'foreign_key_constraints' => false,
  33. ]);
  34. DB::purge($this->connection);
  35. MagentoSchema::create($this->connection);
  36. }
  37. public function tearDown(): void
  38. {
  39. DB::purge($this->connection);
  40. if (isset($this->sqlitePath) && is_file($this->sqlitePath)) {
  41. @unlink($this->sqlitePath);
  42. }
  43. parent::tearDown();
  44. }
  45. public function test_it_migrates_an_order_with_items_addresses_and_payment(): void
  46. {
  47. $customer = $this->createCustomer([
  48. 'email' => 'jane@example.com',
  49. 'first_name' => 'Jane',
  50. 'last_name' => 'Doe',
  51. 'migrated_from_asteria_id' => 10,
  52. ]);
  53. $product = $this->createSimpleProduct('WIG-001');
  54. $this->seedCompleteMagentoOrder();
  55. $this->artisan('orders:migrate-asteria', [
  56. '--connection' => $this->connection,
  57. '--reset-progress' => true,
  58. ])->assertSuccessful();
  59. $order = Order::query()->where('increment_id', '100000010')->first();
  60. $this->assertNotNull($order);
  61. $this->assertSame(10, (int) $order->migrated_from_asteria_id);
  62. $this->assertSame(Order::STATUS_COMPLETED, $order->status);
  63. $this->assertSame((int) $customer->id, (int) $order->customer_id);
  64. $this->assertSame(0, (int) $order->is_guest);
  65. $this->assertSame('jane@example.com', $order->customer_email);
  66. $this->assertEquals(110, (float) $order->grand_total);
  67. $this->assertEquals(100, (float) $order->sub_total);
  68. $this->assertEquals(10, (float) $order->shipping_amount);
  69. $this->assertSame('2021-06-01 12:00:00', $order->created_at?->format('Y-m-d H:i:s'));
  70. $item = OrderItem::query()->where('order_id', $order->id)->first();
  71. $this->assertNotNull($item);
  72. $this->assertSame('WIG-001', $item->sku);
  73. $this->assertSame('Lace Wig', $item->name);
  74. $this->assertSame((int) $product->id, (int) $item->product_id);
  75. $this->assertSame('simple', $item->type);
  76. $this->assertEquals(100, (float) $item->price);
  77. $this->assertSame(101, (int) ($item->additional['asteria_item_id'] ?? 0));
  78. $billing = OrderAddress::query()
  79. ->where('order_id', $order->id)
  80. ->where('address_type', OrderAddress::ADDRESS_TYPE_BILLING)
  81. ->first();
  82. $this->assertNotNull($billing);
  83. $this->assertSame('123 Main St, Apt 4', $billing->address);
  84. $this->assertSame('Austin', $billing->city);
  85. $this->assertSame('US', $billing->country);
  86. $shipping = OrderAddress::query()
  87. ->where('order_id', $order->id)
  88. ->where('address_type', OrderAddress::ADDRESS_TYPE_SHIPPING)
  89. ->first();
  90. $this->assertNotNull($shipping);
  91. $this->assertSame('9 Oak Rd', $shipping->address);
  92. $payment = OrderPayment::query()->where('order_id', $order->id)->first();
  93. $this->assertNotNull($payment);
  94. $this->assertSame('paypal_standard', $payment->method);
  95. $this->assertSame('paypal_express', $payment->additional['magento_method'] ?? null);
  96. $this->assertSame('ABC123', $payment->additional['last_trans_id'] ?? null);
  97. }
  98. public function test_dry_run_does_not_write_orders(): void
  99. {
  100. MagentoSchema::seedOrder($this->connection, [
  101. 'entity_id' => 10,
  102. 'increment_id' => '100000010',
  103. 'customer_email' => 'dry-run@example.com',
  104. ]);
  105. $before = Order::query()->count();
  106. $this->artisan('orders:migrate-asteria', [
  107. '--connection' => $this->connection,
  108. '--dry-run' => true,
  109. '--reset-progress' => true,
  110. ])->assertSuccessful();
  111. $this->assertSame($before, Order::query()->count());
  112. $this->assertNull(Order::query()->where('increment_id', '100000010')->first());
  113. }
  114. public function test_it_is_idempotent_on_a_second_run(): void
  115. {
  116. MagentoSchema::seedOrder($this->connection, [
  117. 'entity_id' => 80,
  118. 'increment_id' => '100000080',
  119. 'customer_email' => 'once@example.com',
  120. 'items' => [
  121. ['item_id' => 801, 'sku' => 'ONCE-1', 'name' => 'Once'],
  122. ],
  123. 'addresses' => [
  124. ['entity_id' => 802, 'address_type' => 'billing'],
  125. ],
  126. 'payment' => [
  127. 'entity_id' => 803,
  128. 'method' => 'checkmo',
  129. ],
  130. ]);
  131. $options = [
  132. '--connection' => $this->connection,
  133. '--reset-progress' => true,
  134. ];
  135. $this->artisan('orders:migrate-asteria', $options)->assertSuccessful();
  136. $this->artisan('orders:migrate-asteria', $options)->assertSuccessful();
  137. $this->assertSame(1, Order::query()->where('increment_id', '100000080')->count());
  138. $order = Order::query()->where('increment_id', '100000080')->first();
  139. $this->assertSame(1, OrderItem::query()->where('order_id', $order->id)->count());
  140. $this->assertSame(1, OrderAddress::query()->where('order_id', $order->id)->count());
  141. $this->assertSame(1, OrderPayment::query()->where('order_id', $order->id)->count());
  142. $this->assertSame('moneytransfer', $order->payment->method);
  143. }
  144. public function test_it_links_a_customer_by_asteria_id(): void
  145. {
  146. $customer = $this->createCustomer([
  147. 'email' => 'linked@example.com',
  148. 'migrated_from_asteria_id' => 44,
  149. ]);
  150. MagentoSchema::seedOrder($this->connection, [
  151. 'entity_id' => 90,
  152. 'increment_id' => '100000090',
  153. 'customer_id' => 44,
  154. 'customer_email' => 'other-address@example.com',
  155. 'items' => [
  156. ['item_id' => 901, 'sku' => 'LINK-1'],
  157. ],
  158. 'payment' => ['entity_id' => 903, 'method' => 'paypal_standard'],
  159. ]);
  160. $this->artisan('orders:migrate-asteria', [
  161. '--connection' => $this->connection,
  162. '--reset-progress' => true,
  163. ])->assertSuccessful();
  164. $order = Order::query()->where('increment_id', '100000090')->first();
  165. $this->assertNotNull($order);
  166. $this->assertSame((int) $customer->id, (int) $order->customer_id);
  167. $this->assertSame(0, (int) $order->is_guest);
  168. }
  169. public function test_it_creates_a_guest_order_when_the_customer_is_missing(): void
  170. {
  171. MagentoSchema::seedOrder($this->connection, [
  172. 'entity_id' => 91,
  173. 'increment_id' => '100000091',
  174. 'customer_id' => 9999,
  175. 'customer_email' => 'nobody@example.com',
  176. 'customer_firstname' => 'Guest',
  177. 'customer_lastname' => 'Buyer',
  178. 'customer_is_guest' => 1,
  179. 'items' => [
  180. ['item_id' => 911, 'sku' => 'GUEST-1'],
  181. ],
  182. 'payment' => ['entity_id' => 913, 'method' => 'cashondelivery'],
  183. ]);
  184. $this->artisan('orders:migrate-asteria', [
  185. '--connection' => $this->connection,
  186. '--reset-progress' => true,
  187. ])->assertSuccessful();
  188. $order = Order::query()->where('increment_id', '100000091')->first();
  189. $this->assertNotNull($order);
  190. $this->assertNull($order->customer_id);
  191. $this->assertSame(1, (int) $order->is_guest);
  192. $this->assertSame('nobody@example.com', $order->customer_email);
  193. $this->assertSame('Guest', $order->customer_first_name);
  194. $this->assertSame('cashondelivery', $order->payment->method);
  195. }
  196. public function test_it_imports_unmatched_sku_items_without_a_product_id(): void
  197. {
  198. MagentoSchema::seedOrder($this->connection, [
  199. 'entity_id' => 92,
  200. 'increment_id' => '100000092',
  201. 'items' => [
  202. [
  203. 'item_id' => 921,
  204. 'sku' => 'MISSING-SKU',
  205. 'name' => 'Retired Product',
  206. 'product_type' => 'simple',
  207. ],
  208. ],
  209. 'payment' => ['entity_id' => 923, 'method' => 'checkmo'],
  210. ]);
  211. $this->artisan('orders:migrate-asteria', [
  212. '--connection' => $this->connection,
  213. '--reset-progress' => true,
  214. ])->assertSuccessful();
  215. $order = Order::query()->where('increment_id', '100000092')->first();
  216. $this->assertNotNull($order);
  217. $item = OrderItem::query()->where('order_id', $order->id)->first();
  218. $this->assertSame('MISSING-SKU', $item->sku);
  219. $this->assertSame('Retired Product', $item->name);
  220. $this->assertNull($item->product_id);
  221. $this->assertNull($item->product_type);
  222. $this->assertSame('simple', $item->type);
  223. }
  224. public function test_it_skips_when_increment_id_already_exists(): void
  225. {
  226. $channel = Channel::query()->first();
  227. Order::factory()->create([
  228. 'increment_id' => '100000099',
  229. 'channel_id' => $channel?->id,
  230. 'channel_type' => Channel::class,
  231. 'customer_id' => null,
  232. 'is_guest' => 1,
  233. 'customer_email'=> 'native@example.com',
  234. ]);
  235. MagentoSchema::seedOrder($this->connection, [
  236. 'entity_id' => 99,
  237. 'increment_id' => '100000099',
  238. 'customer_email' => 'asteria@example.com',
  239. 'items' => [
  240. ['item_id' => 991, 'sku' => 'SKIP-1'],
  241. ],
  242. 'payment' => ['entity_id' => 993, 'method' => 'checkmo'],
  243. ]);
  244. $this->artisan('orders:migrate-asteria', [
  245. '--connection' => $this->connection,
  246. '--reset-progress' => true,
  247. ])->assertSuccessful();
  248. $this->assertSame(1, Order::query()->where('increment_id', '100000099')->count());
  249. $existing = Order::query()->where('increment_id', '100000099')->first();
  250. $this->assertNull($existing->migrated_from_asteria_id);
  251. $this->assertSame('native@example.com', $existing->customer_email);
  252. }
  253. private function seedCompleteMagentoOrder(): void
  254. {
  255. MagentoSchema::seedOrder($this->connection, [
  256. 'entity_id' => 10,
  257. 'increment_id' => '100000010',
  258. 'customer_id' => 10,
  259. 'customer_email' => 'jane@example.com',
  260. 'customer_firstname' => 'Jane',
  261. 'customer_lastname' => 'Doe',
  262. 'status' => 'complete',
  263. 'state' => 'complete',
  264. 'grand_total' => 110,
  265. 'subtotal' => 100,
  266. 'shipping_amount' => 10,
  267. 'created_at' => '2021-06-01 12:00:00',
  268. 'items' => [
  269. [
  270. 'item_id' => 101,
  271. 'sku' => 'WIG-001',
  272. 'name' => 'Lace Wig',
  273. 'qty_ordered'=> 1,
  274. 'price' => 100,
  275. 'row_total' => 100,
  276. ],
  277. ],
  278. 'addresses' => [
  279. [
  280. 'entity_id' => 201,
  281. 'address_type' => 'billing',
  282. 'firstname' => 'Jane',
  283. 'lastname' => 'Doe',
  284. 'street' => "123 Main St\nApt 4",
  285. 'city' => 'Austin',
  286. 'country_id' => 'US',
  287. ],
  288. [
  289. 'entity_id' => 202,
  290. 'address_type' => 'shipping',
  291. 'firstname' => 'Jane',
  292. 'lastname' => 'Doe',
  293. 'street' => '9 Oak Rd',
  294. 'city' => 'Dallas',
  295. 'country_id' => 'US',
  296. ],
  297. ],
  298. 'payment' => [
  299. 'entity_id' => 301,
  300. 'method' => 'paypal_express',
  301. 'last_trans_id' => 'ABC123',
  302. ],
  303. ]);
  304. }
  305. private function createSimpleProduct(string $sku): Product
  306. {
  307. $attributeFamilyId = (int) (DB::table('attribute_families')->value('id') ?? 1);
  308. return Product::factory()->create([
  309. 'sku' => $sku,
  310. 'type' => 'simple',
  311. 'attribute_family_id' => $attributeFamilyId,
  312. ]);
  313. }
  314. }