diff --git a/Changelog.md b/Changelog.md index aa25129..d0657b9 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,10 @@ # Customer Module Changelog +## Unreleased +##### 2024-XX-YY + +- Added CustomerPurchases + ## 3.0.0 ##### 2024-04-25 diff --git a/src/Contracts/CustomerPurchase.php b/src/Contracts/CustomerPurchase.php new file mode 100644 index 0000000..ff938bd --- /dev/null +++ b/src/Contracts/CustomerPurchase.php @@ -0,0 +1,15 @@ +morphMany(AddressProxy::modelClass(), 'model'); } + public function purchases(): HasMany + { + return $this->hasMany(CustomerPurchaseProxy::modelClass()); + } + protected function getNameAttribute() { return $this->getName(); diff --git a/src/Models/CustomerPurchase.php b/src/Models/CustomerPurchase.php new file mode 100644 index 0000000..838d211 --- /dev/null +++ b/src/Models/CustomerPurchase.php @@ -0,0 +1,42 @@ + 'date', + ]; + + public function purchasable(): MorphTo + { + return $this->morphTo(); + } + + public function customer(): BelongsTo + { + return $this->belongsTo(CustomerProxy::modelClass()); + } +} diff --git a/src/Models/CustomerPurchaseProxy.php b/src/Models/CustomerPurchaseProxy.php new file mode 100644 index 0000000..9c62404 --- /dev/null +++ b/src/Models/CustomerPurchaseProxy.php @@ -0,0 +1,11 @@ +id(); + $table->foreignId('customer_id')->constrained()->onDelete('cascade'); + $table->date('purchase_date')->nullable(); + $table->decimal('purchase_value')->nullable(); + $table->char('currency', 3)->nullable(); + $table->morphs('purchasable'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('customer_purchases'); + } +}; diff --git a/tests/CustomerPurchaseTest.php b/tests/CustomerPurchaseTest.php new file mode 100644 index 0000000..8a0eec8 --- /dev/null +++ b/tests/CustomerPurchaseTest.php @@ -0,0 +1,185 @@ + '70H-0WJB-2OD6' + ]); + + $customer = CustomerProxy::create([])->fresh(); + + $customerPurchase = CustomerPurchaseProxy::create([ + 'customer_id' => $customer->id, + 'purchasable_id' => $order->id, + 'purchasable_type' => Order::class, + ])->fresh(); + + $this->assertTrue($order->is($customerPurchase->purchasable)); + $this->assertTrue($customer->is($customerPurchase->customer)); + } + + /** + * @test + */ + public function customer_purchase_can_be_created_with_full_data(): void + { + $order = Order::create([ + 'number' => '70H-0WJB-2OD6' + ]); + + $customer = CustomerProxy::create([])->fresh(); + + $customerPurchase = CustomerPurchaseProxy::create([ + 'customer_id' => $customer->id, + 'purchase_date' => '2024-11-21', + 'purchase_value' => 89.62, + 'currency' => 'EUR', + 'purchasable_id' => $order->id, + 'purchasable_type' => Order::class, + ])->fresh(); + + $this->assertTrue($order->is($customerPurchase->purchasable)); + $this->assertTrue($customer->is($customerPurchase->customer)); + $this->assertEquals(89.62, $customerPurchase->purchase_value); + $this->assertEquals('EUR', $customerPurchase->currency); + $this->assertEquals('2024-11-21', $customerPurchase->purchase_date->toDateString()); + } + + /** + * @test + */ + public function customer_purchase_can_be_updated_with_full_data(): void + { + $order = Order::create([ + 'number' => '70H-0WJB-2OD6' + ]); + + $order2 = Order::create([ + 'number' => '50M3-0RD3R-NUMB3R' + ]); + + $customer = CustomerProxy::create([])->fresh(); + + $customer2 = CustomerProxy::create([])->fresh(); + + $customerPurchase = CustomerPurchaseProxy::create([ + 'customer_id' => $customer->id, + 'purchase_date' => '2024-11-21', + 'purchase_value' => 89.62, + 'currency' => 'EUR', + 'purchasable_id' => $order->id, + 'purchasable_type' => Order::class, + ])->fresh(); + + $customerPurchase->update([ + 'customer_id' => $customer2->id, + 'purchase_date' => '2001-01-06', + 'purchase_value' => 2001.06, + 'currency' => 'HUF', + 'purchasable_id' => $order2->id, + 'purchasable_type' => Order::class, + ]); + + $updatedCustomerPurchase = $customerPurchase->fresh(); + + $this->assertFalse($order->is($updatedCustomerPurchase->purchasable)); + $this->assertTrue($order2->is($updatedCustomerPurchase->purchasable)); + + $this->assertFalse($customer->is($updatedCustomerPurchase->customer)); + $this->assertTrue($customer2->is($updatedCustomerPurchase->customer)); + + $this->assertEquals(2001.06, $updatedCustomerPurchase->purchase_value); + $this->assertEquals('HUF', $updatedCustomerPurchase->currency); + $this->assertEquals('2001-01-06', $updatedCustomerPurchase->purchase_date->toDateString()); + } + + /** + * @test + */ + public function customer_purchase_can_be_deleted(): void + { + $order = Order::create([ + 'number' => '50M3-0RD3R-NUMB3R' + ]); + + $customer = CustomerProxy::create([])->fresh(); + + $customerPurchase = CustomerPurchaseProxy::create([ + 'customer_id' => $customer->id, + 'purchase_date' => '2024-11-21', + 'purchase_value' => 89.62, + 'currency' => 'EUR', + 'purchasable_id' => $order->id, + 'purchasable_type' => Order::class, + ])->fresh(); + + $customerPurchaseId = $customerPurchase->id; + + $customerPurchase->delete(); + + $this->assertNull(CustomerPurchaseProxy::find($customerPurchaseId)); + } + + /** + * @test + */ + public function purchases_of_a_customer_can_be_retrieved(): void + { + $order = Order::create([ + 'number' => '50M3-0RD3R-NUMB3R' + ]); + + $order2 = Order::create([ + 'number' => 'WH47-H4V3-Y0U-B0U6H7' + ]); + + $customer = CustomerProxy::create([])->fresh(); + + $customerPurchase1 = CustomerPurchaseProxy::create([ + 'customer_id' => $customer->id, + 'purchase_date' => '2024-11-21', + 'purchase_value' => 89.62, + 'currency' => 'EUR', + 'purchasable_id' => $order->id, + 'purchasable_type' => Order::class, + ])->fresh(); + + $customerPurchase2 = CustomerPurchaseProxy::create([ + 'customer_id' => $customer->id, + 'purchase_date' => '2023-12-09', + 'purchase_value' => 10000000.94, + 'currency' => 'HUF', + 'purchasable_id' => $order2->id, + 'purchasable_type' => Order::class, + ])->fresh(); + + $this->assertEquals(2, $customer->purchases->count()); + + $this->assertTrue($customer->purchases->every(function ($purchase) { + return $purchase instanceof CustomerPurchase; + })); + + $this->assertTrue($customer->purchases->map->purchasable->every(function ($order) { + return $order instanceof Order; + })); + + $this->assertTrue($customer->purchases->pluck('purchase_value')->contains(89.62)); + $this->assertTrue($customer->purchases->pluck('purchase_value')->contains(10000000.94)); + + $this->assertTrue($customer->purchases->map->purchasable->pluck('number')->contains('50M3-0RD3R-NUMB3R')); + $this->assertTrue($customer->purchases->map->purchasable->pluck('number')->contains('WH47-H4V3-Y0U-B0U6H7')); + } +} diff --git a/tests/Dummies/Order.php b/tests/Dummies/Order.php new file mode 100644 index 0000000..4a01824 --- /dev/null +++ b/tests/Dummies/Order.php @@ -0,0 +1,12 @@ +artisan('migrate:reset'); $this->loadLaravelMigrations(); $this->artisan('migrate', ['--force' => true]); + + $app['db']->connection()->getSchemaBuilder()->create('orders', function (Blueprint $table) { + $table->increments('id'); + $table->string('number', 32); + $table->timestamps(); + }); } /**