diff --git a/app/Services/OrderService.php b/app/Services/OrderService.php index 84b0fcb0..21db58b6 100644 --- a/app/Services/OrderService.php +++ b/app/Services/OrderService.php @@ -116,6 +116,7 @@ public function store(OrderDto $dto): Order $currency = $priceMap->currency; $vat_rate = $this->salesChannelService->getVatRate($salesChannel); + $vat_rate_as_percent = $vat_rate->toFloat() * 100; DB::beginTransaction(); @@ -209,7 +210,7 @@ public function store(OrderDto $dto): Order 'shipping_place' => $shippingPlace, 'shipping_type' => $shippingMethod->shipping_type ?? $digitalShippingMethod->shipping_type ?? null, 'payment_method_type' => $paymentMethod->type, - 'vat_rate' => $vat_rate, + 'vat_rate' => $vat_rate_as_percent, ] + $dto->toArray(), ); @@ -235,7 +236,7 @@ public function store(OrderDto $dto): Order 'base_price_initial' => $price, 'base_price' => $price, 'name' => $product->getTranslation('name', $language), - 'vat_rate' => $vat_rate->multipliedBy(100)->toFloat(), + 'vat_rate' => $vat_rate_as_percent, 'shipping_digital' => $product->shipping_digital, ]); diff --git a/tests/Feature/PriceMap/NetAndGrossTest.php b/tests/Feature/PriceMap/NetAndGrossTest.php new file mode 100644 index 00000000..c1c22021 --- /dev/null +++ b/tests/Feature/PriceMap/NetAndGrossTest.php @@ -0,0 +1,447 @@ +productService = App::make(ProductService::class); + $this->schemaCrudService = App::make(SchemaCrudService::class); + $this->salesChannelRepository = App::make(SalesChannelRepository::class); + + $this->currency = Currency::DEFAULT; + + $this->priceMap = PriceMap::query()->findOrFail($this->currency->getDefaultPriceMapId()); + + $this->salesChannel = $this->salesChannelRepository->getDefault(); + $this->salesChannel->price_map_id = $this->priceMap->getKey(); + $this->salesChannel->vat_rate = 10; + $this->salesChannel->save(); + + $this->email = $this->faker->freeEmail; + + $this->shippingMethod = ShippingMethod::factory()->create([ + 'public' => true, + 'shipping_type' => ShippingType::ADDRESS, + ]); + + $range = PriceRange::query()->create([ + 'start' => Money::zero($this->currency->value), + 'value' => Money::of(10, $this->currency->value), + ]); + $this->shippingMethod->priceRanges()->saveMany([$range]); + + $this->product = $this->productService->create( + FakeDto::productCreateDto([ + 'public' => true, + 'prices_base' => [PriceDto::from(Money::of(100.0, $this->currency->value))], + ]) + ); + + $this->schema = $this->schemaCrudService->store( + FakeDto::schemaDto([ + 'hidden' => false, + 'required' => false, + 'options' => [ + [ + 'name' => 'Default', + 'prices' => [PriceDto::from(Money::of(0, $this->currency->value))], + ], + [ + 'name' => 'Priced', + 'prices' => [PriceDto::from(Money::of(100, $this->currency->value))], + ], + ], + 'product_id' => $this->product->getKey(), + ]) + ); + + $this->option = $this->schema->options->where('name', 'Priced')->first(); + + $this->address = Address::factory()->make(); + + $this->paymentMethod = PaymentMethod::factory()->create([ + 'type' => PaymentMethodType::PREPAID, + ]); + } + + public function testCartForNetPriceMap(): void + { + $this->user->givePermissionTo('cart.verify'); + + $this->priceMap->is_net = true; + $this->priceMap->save(); + + $discount1 = Discount::factory()->create([ + 'description' => 'Test', + 'code' => null, + 'percentage' => '10', + 'target_type' => DiscountTargetType::PRODUCTS, + 'target_is_allow_list' => true, + ]); + $discount1->products()->attach($this->product->getKey()); + + $discount2 = Discount::factory()->create([ + 'description' => 'Test', + 'code' => null, + 'percentage' => '10', + 'target_type' => DiscountTargetType::ORDER_VALUE, + 'target_is_allow_list' => true, + ]); + $discount2->products()->attach($this->product->getKey()); + + $this->productService->updateMinPrices($this->product, collect([$this->salesChannel])); + + $response = $this->actingAs($this->user)->postJson('/cart/process', [ + 'currency' => $this->currency, + 'sales_channel_id' => SalesChannel::query()->value('id'), + 'shipping_method_id' => $this->shippingMethod->getKey(), + 'items' => [ + [ + 'cartitem_id' => '1', + 'product_id' => $this->product->getKey(), + 'quantity' => 1, + 'schemas' => [ + $this->schema->getKey() => $this->option->getKey(), + ], + ], + ], + ]); + + $response + ->assertValid() + ->assertOk(); + + $response->assertJsonFragment([ + 'cart_total_initial' => [ + 'net' => '200.00', + 'gross' => '220.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'cart_total' => [ + 'net' => '162.00', + 'gross' => '178.20', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'shipping_price_initial' => [ + 'net' => '9.09', + 'gross' => '10.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'shipping_price' => [ + 'net' => '9.09', + 'gross' => '10.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'summary' => [ + 'net' => '171.09', + 'gross' => '188.20', // 178.20 + 10 or alternatively 230 - 22 - 19.8, where 22 is 10% discount of product price, and 19.8 is 10% discount of remaining order value + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + ]); + } + + public function testCartForGrossPriceMap(): void + { + $this->user->givePermissionTo('cart.verify'); + + $this->priceMap->is_net = false; + $this->priceMap->save(); + + $discount1 = Discount::factory()->create([ + 'description' => 'Test', + 'code' => null, + 'percentage' => '10', + 'target_type' => DiscountTargetType::PRODUCTS, + 'target_is_allow_list' => true, + ]); + $discount1->products()->attach($this->product->getKey()); + + $discount2 = Discount::factory()->create([ + 'description' => 'Test', + 'code' => null, + 'percentage' => '10', + 'target_type' => DiscountTargetType::ORDER_VALUE, + 'target_is_allow_list' => true, + ]); + $discount2->products()->attach($this->product->getKey()); + + $this->productService->updateMinPrices($this->product, collect([$this->salesChannel])); + + $response = $this->actingAs($this->user)->postJson('/cart/process', [ + 'currency' => $this->currency, + 'sales_channel_id' => SalesChannel::query()->value('id'), + 'shipping_method_id' => $this->shippingMethod->getKey(), + 'items' => [ + [ + 'cartitem_id' => '1', + 'product_id' => $this->product->getKey(), + 'quantity' => 1, + 'schemas' => [ + $this->schema->getKey() => $this->option->getKey(), + ], + ], + ], + ]); + + $response + ->assertValid() + ->assertOk(); + + $response->assertJsonFragment([ + 'cart_total_initial' => [ + 'net' => '181.82', + 'gross' => '200.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'cart_total' => [ + 'net' => '147.27', + 'gross' => '162.00', // 200 - 20 - 18 + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'shipping_price_initial' => [ + 'net' => '9.09', + 'gross' => '10.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'shipping_price' => [ + 'net' => '9.09', + 'gross' => '10.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'summary' => [ + 'net' => '156.36', + 'gross' => '172.00', // 162 + 10 or alternatively 200 - 20 - 18, where 20 is 10% discount of product price, and 18 is 10% discount of remaining order value + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + ]); + } + + public function testOrderForNetPriceMap(): void + { + $this->user->givePermissionTo('cart.verify'); + $this->user->givePermissionTo('orders.add'); + + $this->priceMap->is_net = true; + $this->priceMap->save(); + + $discount1 = Discount::factory()->create([ + 'description' => 'Test', + 'code' => null, + 'percentage' => '10', + 'target_type' => DiscountTargetType::PRODUCTS, + 'target_is_allow_list' => true, + ]); + $discount1->products()->attach($this->product->getKey()); + + $discount2 = Discount::factory()->create([ + 'description' => 'Test', + 'code' => null, + 'percentage' => '10', + 'target_type' => DiscountTargetType::ORDER_VALUE, + 'target_is_allow_list' => true, + ]); + $discount2->products()->attach($this->product->getKey()); + + $this->productService->updateMinPrices($this->product, collect([$this->salesChannel])); + + $response = $this->actingAs($this->user)->json('POST', '/orders', [ + 'sales_channel_id' => $this->salesChannel->getKey(), + 'currency' => $this->currency, + 'email' => $this->email, + 'shipping_method_id' => $this->shippingMethod->getKey(), + 'shipping_place' => $this->address->toArray(), + 'billing_address' => $this->address->toArray(), + 'items' => [ + [ + 'product_id' => $this->product->getKey(), + 'quantity' => 1, + 'schemas' => [ + $this->schema->getKey() => $this->option->getKey(), + ] + ], + ], + 'payment_method_id' => $this->paymentMethod->getKey(), + ]); + + $response + ->assertValid() + ->assertCreated(); + + $response->assertJsonFragment([ + 'cart_total_initial' => [ + 'net' => '200.00', + 'gross' => '220.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'cart_total' => [ + 'net' => '162.00', + 'gross' => '178.20', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'shipping_price_initial' => [ + 'net' => '9.09', + 'gross' => '10.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'shipping_price' => [ + 'net' => '9.09', + 'gross' => '10.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'summary' => [ + 'net' => '171.09', + 'gross' => '188.20', // 178.20 + 10 or alternatively 230 - 22 - 19.8, where 22 is 10% discount of product price, and 19.8 is 10% discount of remaining order value + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + ]); + } + + public function testOrderForGrossPriceMap(): void + { + $this->user->givePermissionTo('cart.verify'); + $this->user->givePermissionTo('orders.add'); + + $this->priceMap->is_net = false; + $this->priceMap->save(); + + $discount1 = Discount::factory()->create([ + 'description' => 'Test', + 'code' => null, + 'percentage' => '10', + 'target_type' => DiscountTargetType::PRODUCTS, + 'target_is_allow_list' => true, + ]); + $discount1->products()->attach($this->product->getKey()); + + $discount2 = Discount::factory()->create([ + 'description' => 'Test', + 'code' => null, + 'percentage' => '10', + 'target_type' => DiscountTargetType::ORDER_VALUE, + 'target_is_allow_list' => true, + ]); + $discount2->products()->attach($this->product->getKey()); + + $this->productService->updateMinPrices($this->product, collect([$this->salesChannel])); + + $response = $this->actingAs($this->user)->json('POST', '/orders', [ + 'sales_channel_id' => $this->salesChannel->getKey(), + 'currency' => $this->currency, + 'email' => $this->email, + 'shipping_method_id' => $this->shippingMethod->getKey(), + 'shipping_place' => $this->address->toArray(), + 'billing_address' => $this->address->toArray(), + 'items' => [ + [ + 'product_id' => $this->product->getKey(), + 'quantity' => 1, + 'schemas' => [ + $this->schema->getKey() => $this->option->getKey(), + ] + ], + ], + 'payment_method_id' => $this->paymentMethod->getKey(), + ]); + + $response + ->assertValid() + ->assertCreated(); + + $response->assertJsonFragment([ + 'cart_total_initial' => [ + 'net' => '181.82', + 'gross' => '200.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'cart_total' => [ + 'net' => '147.27', + 'gross' => '162.00', // 200 - 20 - 18 + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'shipping_price_initial' => [ + 'net' => '9.09', + 'gross' => '10.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'shipping_price' => [ + 'net' => '9.09', + 'gross' => '10.00', + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + 'summary' => [ + 'net' => '156.36', + 'gross' => '172.00', // 162 + 10 or alternatively 200 - 20 - 18, where 20 is 10% discount of product price, and 18 is 10% discount of remaining order value + 'vat_rate' => '0.10', + 'currency' => 'PLN', + ], + ]); + } +}