Skip to content

Commit

Permalink
B2B-158 (#52)
Browse files Browse the repository at this point in the history
* B2B-158
  • Loading branch information
pa-cholek authored Sep 6, 2024
1 parent dc8a7ab commit 06b566a
Show file tree
Hide file tree
Showing 27 changed files with 1,991 additions and 476 deletions.
11 changes: 8 additions & 3 deletions app/Http/Controllers/ProductController.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
use Domain\Product\Dtos\ProductCreateDto;
use Domain\Product\Dtos\ProductSearchDto;
use Domain\Product\Dtos\ProductUpdateDto;
use Domain\Product\Dtos\ProductVariantPriceDto;
use Domain\Product\Dtos\ProductVariantPriceDtoCollection;
use Heseya\Dto\DtoException;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
Expand Down Expand Up @@ -196,8 +196,13 @@ public function deleteAttachment(Product $product, MediaAttachment $attachment):
return Response::noContent();
}

public function process(Request $request, Product $product, ProductVariantPriceDto $dto): HttpResponse
public function processSingle(Request $request, Product $product): HttpResponse
{
return $this->productService->getPriceForVariant($product, $dto)->toResponse($request);
return $this->productService->getPriceForVariant($product, $request->input('schemas', []))->toResponse($request);
}

public function process(Request $request, ProductVariantPriceDtoCollection $collection): HttpResponse
{
return $this->productService->getPricesForVariants($collection)->toResponse($request);
}
}
4 changes: 4 additions & 0 deletions app/Http/Resources/OrderShortResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@

namespace App\Http\Resources;

use App\Models\Order;
use App\Traits\MetadataResource;
use Domain\Order\Resources\OrderStatusResource;
use Illuminate\Http\Request;

/**
* @property Order $resource
*/
class OrderShortResource extends Resource
{
use MetadataResource;
Expand Down
1 change: 1 addition & 0 deletions app/Models/Order.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ final class Order extends Model implements SortableContract
'language',
'payment_method_type',
'organization_id',
'vat_rate',
];

protected array $criteria = [
Expand Down
2 changes: 1 addition & 1 deletion app/Repositories/DiscountRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static function setDiscountAmounts(string $discountId, array $amounts): v
'currency' => $amount->value->getCurrency()->getCurrencyCode(),
'value' => $amount->value->getMinorAmount(),
'gross' => $amount->value->getMinorAmount(),
'is_net' => false,
'is_net' => true,
];
Cache::driver('array')->forget(self::getCacheKey($discountId, Currency::from($amount->value->getCurrency()->getCurrencyCode())));
}
Expand Down
154 changes: 79 additions & 75 deletions app/Services/DiscountService.php

Large diffs are not rendered by default.

65 changes: 27 additions & 38 deletions app/Services/OrderService.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use App\Models\Option;
use App\Models\Order;
use App\Models\OrderProduct;
use App\Models\OrderSchema;
use App\Models\Payment;
use App\Models\Product;
use App\Models\Status;
Expand Down Expand Up @@ -206,6 +207,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,
] + $dto->toArray(),
);

Expand All @@ -221,6 +223,9 @@ public function store(OrderDto $dto): Order
$product = $products->firstWhere('id', $item->getProductId());

$price = $product->mappedPriceForPriceMap($priceMap)->value;
if (!$priceMap->is_net) {
$price = $this->salesChannelService->removeVat($price, $vat_rate);
}

$orderProduct = new OrderProduct([
'product_id' => $item->getProductId(),
Expand All @@ -243,14 +248,19 @@ public function store(OrderDto $dto): Order
foreach ($item->getSchemas() as $schemaId => $value) {
/** @var Schema $schema */
$schema = $product->schemas()->findOrFail($schemaId);
$price = $schema->getPrice($value, $item->getSchemas(), $currency);

$price = $schema->getPrice($value, $item->getSchemas(), $priceMap);
if (!$priceMap->is_net) {
$price = $this->salesChannelService->removeVat($price, $vat_rate);
}

/** @var Option $option */
$option = $schema->options()->findOrFail($value);
$tempSchemaOrderProduct[$schema->name . '_' . $item->getProductId()] = [$schemaId, $value];
$value = $option->name;

$orderProduct->schemas()->create([
/** @var OrderSchema $orderSchema */
$orderSchema = $orderProduct->schemas()->create([
'name' => $schema->getTranslation('name', $language),
'value' => $value,
'price_initial' => $price,
Expand Down Expand Up @@ -279,10 +289,13 @@ public function store(OrderDto $dto): Order
// Apply discounts to order/products
$order = $this->discountService->calcOrderProductsAndTotalDiscounts($order, $dto);

$shippingPrice = $shippingMethod?->getPrice($order->cart_total) ?? Money::zero($currency->value);
$shippingPrice = $shippingPrice->plus(
$digitalShippingMethod?->getPrice($order->cart_total) ?? Money::zero($currency->value),
);
$shippingPrice = Money::zero($currency->value);
if ($shippingMethod !== null) {
$shippingPrice = $shippingPrice->plus($shippingMethod->getPrice($order->cart_total));
}
if ($digitalShippingMethod !== null) {
$shippingPrice = $shippingPrice->plus($digitalShippingMethod->getPrice($order->cart_total));
}

// Always gross
$order->shipping_price_initial = $shippingPrice;
Expand All @@ -298,30 +311,15 @@ public function store(OrderDto $dto): Order
throw new OrderException(Exceptions::ORDER_NOT_ENOUGH_ITEMS_IN_WAREHOUSE);
}

$orderProduct->base_price_initial = $this->salesChannelService->addVat(
$orderProduct->base_price_initial,
$vat_rate,
);
$orderProduct->base_price = $this->salesChannelService->addVat(
$orderProduct->base_price,
$vat_rate,
);
$orderProduct->price_initial = $this->salesChannelService->addVat(
$orderProduct->price_initial,
$vat_rate,
);
$orderProduct->price = $this->salesChannelService->addVat(
$orderProduct->price,
$vat_rate,
);
$orderProduct->base_price_initial = $this->salesChannelService->addVat($orderProduct->base_price_initial, $vat_rate);
$orderProduct->base_price = $this->salesChannelService->addVat($orderProduct->base_price, $vat_rate);
$orderProduct->price_initial = $this->salesChannelService->addVat($orderProduct->price_initial, $vat_rate);
$orderProduct->price = $this->salesChannelService->addVat($orderProduct->price, $vat_rate);

/** @var Discount $discount */
foreach ($orderProduct->discounts as $discount) {
if ($discount->order_discount?->applied !== null) {
$discount->order_discount->applied = $this->salesChannelService->addVat(
$discount->order_discount->applied,
$vat_rate,
);
$discount->order_discount->applied = $this->salesChannelService->addVat($discount->order_discount->applied, $vat_rate);
$discount->order_discount->save();
}
}
Expand All @@ -330,22 +328,13 @@ public function store(OrderDto $dto): Order
/** @var Discount $discount */
foreach ($order->discounts as $discount) {
if ($discount->order_discount?->applied !== null) {
$discount->order_discount->applied = $this->salesChannelService->addVat(
$discount->order_discount->applied,
$vat_rate,
);
$discount->order_discount->applied = $this->salesChannelService->addVat($discount->order_discount->applied, $vat_rate);
$discount->order_discount->save();
}
}

$order->cart_total_initial = $this->salesChannelService->addVat(
$order->cart_total_initial,
$vat_rate,
);
$order->cart_total = $this->salesChannelService->addVat(
$order->cart_total,
$vat_rate,
);
$order->cart_total_initial = $this->salesChannelService->addVat($order->cart_total_initial, $vat_rate);
$order->cart_total = $this->salesChannelService->addVat($order->cart_total, $vat_rate);

// shipping price magic 🙈
$order->summary = $order->shipping_price->plus($order->cart_total);
Expand Down
19 changes: 15 additions & 4 deletions app/Services/ProductService.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
use Domain\Product\Dtos\ProductCreateDto;
use Domain\Product\Dtos\ProductSearchDto;
use Domain\Product\Dtos\ProductUpdateDto;
use Domain\Product\Dtos\ProductVariantPriceDto;
use Domain\Product\Dtos\ProductVariantPriceDtoCollection;
use Domain\Product\Models\ProductBannerMedia;
use Domain\Product\Resources\ProductVariantPriceResource;
use Domain\ProductAttribute\Models\Attribute;
Expand Down Expand Up @@ -192,7 +192,17 @@ public function updateInitialPricesForSalesChannels(Product $product, Collection
$this->priceService->setCachedProductPrices($product->getKey(), [ProductPriceType::PRICE_INITIAL->value => $prices]);
}

public function getPriceForVariant(Product $product, ProductVariantPriceDto $dto, bool $calculateForCurrentUser = false): ProductVariantPriceResource
public function getPricesForVariants(ProductVariantPriceDtoCollection $collection, bool $calculateForCurrentUser = false): DataCollection
{
$items = [];
foreach ($collection->items() as $dto) {
$items[] = $this->getPriceForVariant(Product::query()->findOrFail($dto->product_id), is_array($dto->schemas) ? $dto->schemas : [], $calculateForCurrentUser);
}

return ProductVariantPriceResource::collection($items);
}

public function getPriceForVariant(Product $product, array $schemas, bool $calculateForCurrentUser = false): ProductVariantPriceResource
{
$salesChannel = $this->salesChannelService->getCurrentRequestSalesChannel();
$priceMap = $salesChannel->priceMap;
Expand All @@ -203,14 +213,15 @@ public function getPriceForVariant(Product $product, ProductVariantPriceDto $dto

$price_initial = $product->mappedPriceForPriceMap($priceMap);

if (is_array($dto->schemas) && !empty($dto->schemas)) {
if (!empty($schemas)) {
$sales = $this->discountService->getAllAplicableSalesForProduct($product, $this->discountService->getSalesWithBlockList(), $calculateForCurrentUser);
$price = $this->discountService->calcAllDiscountsOnProductVariant($product, $sales, $salesChannel, $dto->schemas);
$price = $this->discountService->calcAllDiscountsOnProductVariant($product, $sales, $salesChannel, $schemas);
} else {
$price = ProductCachedPriceDto::from($price_initial, $salesChannel);
}

return ProductVariantPriceResource::from([
'product_id' => $product->id,
'price_initial' => ProductCachedPriceDto::from($price_initial, $salesChannel),
'price' => $price,
]);
Expand Down
37 changes: 37 additions & 0 deletions database/migrations/2024_09_04_000001_add_vat_rate_to_order.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

use App\Models\Order;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
public function up(): void
{
Schema::table('orders', function (Blueprint $table) {
$table->string('vat_rate', 9)->default('0');
});

Order::query()->with('salesChannel')->chunkById(100, function (Collection $orders) {
/** @var Order $order */
foreach ($orders as $order) {
$order->update(['vat_rate' => $order->salesChannel?->vat_rate ?? '0']);
}
});

Schema::table('prices', function (Blueprint $table) {
$table->boolean('is_net')->default(true)->change();
});
}

public function down(): void
{
Schema::table('orders', function (Blueprint $table) {
$table->dropColumn('vat_rate');
});
}
};
5 changes: 4 additions & 1 deletion routes/product.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
Route::delete('id:{product:id}/attachments/id:{attachment:id}', [ProductController::class, 'deleteAttachment'])
->middleware('can:products.edit');

Route::post('id:{product:id}/process', [ProductController::class, 'process'])
Route::post('id:{product:id}/process', [ProductController::class, 'processSingle'])
->middleware('can:products.show', 'can:cart.verify');

Route::post('/process', [ProductController::class, 'process'])
->middleware('can:products.show', 'can:cart.verify');
});
67 changes: 67 additions & 0 deletions src/Domain/Order/Dtos/OrderPriceDto.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace Domain\Order\Dtos;

use Brick\Math\BigDecimal;
use Brick\Math\RoundingMode;
use Brick\Money\Money;
use Domain\Currency\Currency;
use Domain\SalesChannel\Models\SalesChannel;
use Domain\SalesChannel\SalesChannelService;
use InvalidArgumentException;
use Spatie\LaravelData\Attributes\WithCast;
use Spatie\LaravelData\Attributes\WithTransformer;
use Spatie\LaravelData\Casts\EnumCast;
use Spatie\LaravelData\Data;
use Support\LaravelData\Casts\MoneyCast;
use Support\LaravelData\Transformers\MoneyToAmountTransformer;

final class OrderPriceDto extends Data
{
public function __construct(
#[WithTransformer(MoneyToAmountTransformer::class)]
#[WithCast(MoneyCast::class)]
public Money $net,
#[WithTransformer(MoneyToAmountTransformer::class)]
#[WithCast(MoneyCast::class)]
public Money $gross,
#[WithCast(EnumCast::class, Currency::class)]
public Currency $currency,
public BigDecimal $vat_rate,
) {
$this->vat_rate = $this->vat_rate->toScale(2, RoundingMode::HALF_UP);
}

public static function fromMoneyAndSalesChannel(Money $price, SalesChannel $salesChannel, bool $is_net = true): self
{
if ($salesChannel->priceMap === null) {
throw new InvalidArgumentException();
}

return self::fromMoneyAndVatRate($price, app(SalesChannelService::class)->getVatRate($salesChannel), $is_net);
}

public static function fromMoneyAndVatRate(Money $price, BigDecimal|float|string $vat_rate, bool $is_net = true): self
{
if (!$vat_rate instanceof BigDecimal) {
$vat_rate = BigDecimal::of($vat_rate)->multipliedBy(0.01)->abs();
}

if ($is_net) {
$net = $price;
$gross = app(SalesChannelService::class)->addVat($price, $vat_rate);
} else {
$net = app(SalesChannelService::class)->removeVat($price, $vat_rate);
$gross = $price;
}

return self::from([
'net' => $net,
'gross' => $gross,
'currency' => $price->getCurrency()->getCurrencyCode(),
'vat_rate' => $vat_rate,
]);
}
}
13 changes: 3 additions & 10 deletions src/Domain/Order/Resources/CartItemResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,18 @@

namespace Domain\Order\Resources;

use Brick\Money\Money;
use Domain\Currency\Currency;
use Domain\Order\Dtos\OrderPriceDto;
use Spatie\LaravelData\Attributes\WithCast;
use Spatie\LaravelData\Attributes\WithTransformer;
use Spatie\LaravelData\Casts\EnumCast;
use Support\Dtos\DataWithGlobalMetadata;
use Support\LaravelData\Casts\MoneyCast;
use Support\LaravelData\Transformers\MoneyToAmountTransformer;

final class CartItemResource extends DataWithGlobalMetadata
{
public function __construct(
public string $cartitem_id,
#[WithTransformer(MoneyToAmountTransformer::class)]
#[WithCast(MoneyCast::class)]
public Money $price,
#[WithTransformer(MoneyToAmountTransformer::class)]
#[WithCast(MoneyCast::class)]
public Money $price_discounted,
public OrderPriceDto $price,
public OrderPriceDto $price_discounted,
#[WithCast(EnumCast::class, Currency::class)]
public Currency $currency,
public float $quantity,
Expand Down
Loading

0 comments on commit 06b566a

Please sign in to comment.