diff --git a/app/Http/Controllers/StoreController.php b/app/Http/Controllers/StoreController.php index f11dcdcf377..4248f8bd226 100644 --- a/app/Http/Controllers/StoreController.php +++ b/app/Http/Controllers/StoreController.php @@ -8,8 +8,6 @@ use App\Http\Controllers\Store\Controller as Controller; use App\Models\Store; use Auth; -use Request; -use Validator; class StoreController extends Controller { @@ -20,21 +18,16 @@ public function __construct() { $this->middleware('auth', ['only' => [ 'getInvoice', - 'postNewAddress', - 'postUpdateAddress', ]]); if (!$this->isAllowRestrictedUsers()) { $this->middleware('check-user-restricted', ['only' => [ 'getInvoice', - 'postNewAddress', - 'postUpdateAddress', ]]); } $this->middleware('verify-user', ['only' => [ 'getInvoice', - 'postUpdateAddress', ]]); parent::__construct(); @@ -58,96 +51,6 @@ public function getInvoice($id = null) abort(403); } - $sentViaAddress = Store\Address::sender(); - $forShipping = Auth::user()->isAdmin() && get_bool(Request::input('for_shipping')); - $copies = clamp(get_int(request('copies')), 1, config('store.invoice.max_copies')); - - return ext_view('store.invoice', compact('order', 'forShipping', 'copies', 'sentViaAddress')); - } - - public function missingMethod($parameters = []) - { - abort(404); - } - - public function postUpdateAddress() - { - $address_id = (int) Request::input('id'); - $address = Store\Address::find($address_id); - $order = $this->userCart(); - - if (!$address || $address->user_id !== Auth::user()->user_id) { - return error_popup('invalid address'); - } - - switch (Request::input('action')) { - default: - case 'use': - $order->address()->associate($address); - $order->save(); - - return ext_view('layout.ujs-reload', [], 'js'); - break; - case 'remove': - if ($order->address_id === $address_id) { - return error_popup('Address is being used for this order!'); - } - - if ($otherOrders = Store\Order::where('address_id', '=', $address_id)->first()) { - return error_popup('Address was used in a previous order!'); - } - - Store\Address::destroy($address_id); - - return ext_view('store.address-destroy', ['address_id' => $address_id], 'js'); - break; - } - } - - public function postNewAddress() - { - \Log::info(json_encode([ - 'tag' => 'NEW_ADDRESS', - 'user_id' => Auth::user()->user_id, - 'address' => Request::input('address'), - ])); - - $addressInput = get_params(request()->all(), 'address', [ - 'first_name', - 'last_name', - 'street', - 'city', - 'state', - 'zip', - 'country_code', - 'phone', - ]); - - $validator = Validator::make($addressInput, [ - 'first_name' => ['required'], - 'last_name' => ['required'], - 'street' => ['required', 'mixture'], - 'city' => ['required'], - 'state' => ['required'], - 'zip' => ['required', 'required'], - 'country_code' => ['required'], - 'phone' => ['required'], - ]); - - $addressInput['user_id'] = Auth::user()->user_id; - - if ($validator->fails()) { - return error_popup($validator->errors()->first()); - } - - $address = Store\Address::create($addressInput); - $address->user()->associate(Auth::user()); - $address->save(); - - $order = $this->userCart(); - $order->address()->associate($address); - $order->save(); - - return ext_view('layout.ujs-reload', [], 'js'); + return ext_view('store.invoice', compact('order')); } } diff --git a/app/Libraries/OrderCheckout.php b/app/Libraries/OrderCheckout.php index 0f3293badb8..6f93a84d0e5 100644 --- a/app/Libraries/OrderCheckout.php +++ b/app/Libraries/OrderCheckout.php @@ -174,23 +174,29 @@ public function validate() $messages[] = $item->validationErrors()->allMessages(); } + $product = $item->product; + // Checkout process level validations, should not be part of OrderItem validation. - if ($item->product === null || !$item->product->isAvailable()) { + if ($product === null || !$product->isAvailable()) { $messages[] = osu_trans('model_validation/store/product.not_available'); } - if (!$item->product->inStock($item->quantity)) { + if (!$product->inStock($item->quantity)) { $messages[] = osu_trans('model_validation/store/product.insufficient_stock'); } - if ($item->quantity > $item->product->max_quantity) { - $messages[] = osu_trans('model_validation/store/product.too_many', ['count' => $item->product->max_quantity]); + if ($item->quantity > $product->max_quantity) { + $messages[] = osu_trans('model_validation/store/product.too_many', ['count' => $product->max_quantity]); } - if ($shouldShopify && !$item->product->isShopify()) { + if ($shouldShopify && !$product->isShopify()) { $messages[] = osu_trans('model_validation/store/product.must_separate'); } + if ($product->requiresShipping() && !$product->isShopify()) { + $messages[] = osu_trans('model_validation/store/product.not_available'); + } + $customClass = $item->getCustomClassInstance(); if ($customClass !== null) { $messages[] = $customClass->validate()->allMessages(); diff --git a/app/Models/Store/Address.php b/app/Models/Store/Address.php index c2b5b3d9c3f..363104a06a2 100644 --- a/app/Models/Store/Address.php +++ b/app/Models/Store/Address.php @@ -7,7 +7,6 @@ use App\Models\Country; use App\Models\User; -use Auth; /** * @property int $address_id @@ -55,34 +54,4 @@ public function countryName() return $this->country->name; } } - - public static function sender() - { - //todo: move to database - switch (Auth::user()->user_id) { - default: - case 4916903: - return new self([ - 'first_name' => 'osu!store', - 'last_name' => '', - 'street' => 'Room 304, Build 700 Nishijin 7-7-1', - 'city' => 'Sawara', - 'state' => 'Fukuoka', - 'zip' => '814-0002', - 'country' => Country::find('JP'), - 'phone' => '+819064201305', - ]); - case 2: - return new self([ - 'first_name' => 'osu!store', - 'last_name' => '', - 'street' => 'Nishi-Ooi 4-21-3 Birdie House A', - 'city' => 'Shinagawa', - 'state' => 'Tokyo', - 'zip' => '140-0015', - 'country' => Country::find('JP'), - 'phone' => '+818013811430', - ]); - } - } } diff --git a/app/Models/Store/Order.php b/app/Models/Store/Order.php index 915ab61bffc..8662594f1f7 100644 --- a/app/Models/Store/Order.php +++ b/app/Models/Store/Order.php @@ -265,14 +265,10 @@ public function getProviderReference(): ?string return static::splitTransactionId($this->transaction_id)[1] ?? null; } - public function getSubtotal($forShipping = false) + public function getSubtotal() { $total = 0; foreach ($this->items as $i) { - if ($forShipping && !$i->product->requiresShipping()) { - continue; - } - $total += $i->subtotal(); } diff --git a/config/store.php b/config/store.php index a64bf6b7808..f23b652b58f 100644 --- a/config/store.php +++ b/config/store.php @@ -2,9 +2,6 @@ return [ 'allow_restricted_users' => get_bool(env('STORE_ALLOW_RESTRICTED_USERS')) ?? false, - 'invoice' => [ - 'max_copies' => get_int(env('STORE_INVOICE_MAX_COPIES')) ?? 10, - ], 'mail' => [ 'donation_thanks' => [ 'sender_address' => env('STORE_THANKS_SENDER_ADDRESS', 'osu@ppy.sh'), diff --git a/database/factories/Store/ProductFactory.php b/database/factories/Store/ProductFactory.php index cece4411d43..439f543d111 100644 --- a/database/factories/Store/ProductFactory.php +++ b/database/factories/Store/ProductFactory.php @@ -96,4 +96,13 @@ public function masterTshirt(): static 'weight' => 100, ]); } + + public function virtual(): static + { + return $this->state([ + 'base_shipping' => 0.00, + 'next_shipping' => 0.00, + 'weight' => null, + ]); + } } diff --git a/resources/css/bem-index.less b/resources/css/bem-index.less index 3a4014018ba..918f9d66ce1 100644 --- a/resources/css/bem-index.less +++ b/resources/css/bem-index.less @@ -6,7 +6,6 @@ @import "bem/account-edit-status"; @import "bem/account-verification-message"; @import "bem/address"; -@import "bem/address-list"; @import "bem/admin-contest"; @import "bem/admin-contest-entry"; @import "bem/admin-menu"; diff --git a/resources/css/bem/address-list.less b/resources/css/bem/address-list.less deleted file mode 100644 index 71478b859c0..00000000000 --- a/resources/css/bem/address-list.less +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. -// See the LICENCE file in the repository root for full licence text. - -.address-list { - margin: 20px 0; - display: grid; - grid-gap: 20px; - - @media @desktop { - grid-template-columns: 1fr 1fr; - } -} diff --git a/resources/css/bem/address.less b/resources/css/bem/address.less index 0ec682959af..11a71601396 100644 --- a/resources/css/bem/address.less +++ b/resources/css/bem/address.less @@ -3,47 +3,4 @@ .address { margin-bottom: 20px; - - &--card { - .default-box-shadow(); - .default-border-radius(); - background: @osu-colour-b4; - padding: 20px; - margin: 0; - } - - &--card-active { - .default-box-shadow(); - box-shadow: 0 0 0 3px @osu-colour-l4; - } - - &--card-hover { - &:hover { - .default-box-shadow(); - box-shadow: 0 0 0 3px @osu-colour-l4; - } - } - - &__button-delete { - .reset-input(); - color: @osu-colour-c1; - position: absolute; - padding: 10px; - top: 0; - right: 0; - font-size: 20px; // icon size - - &:hover { - color: @osu-colour-red-2; - } - } - - &__button-select { - position: absolute; - top: 0; - left: 0; - width: 1px; - height: 1px; - opacity: 0; - } } diff --git a/resources/css/utilities.less b/resources/css/utilities.less index e4c641be451..663efd8a524 100644 --- a/resources/css/utilities.less +++ b/resources/css/utilities.less @@ -7,12 +7,6 @@ } } -.print-page-break { - @media print { - page-break-before: always; - } -} - .u-blackout-visible { z-index: @z-index--blackout-visible !important; } diff --git a/resources/views/store/_shipping_germany_warning.blade.php b/resources/views/store/_shipping_germany_warning.blade.php deleted file mode 100644 index d6bed9f1fbe..00000000000 --- a/resources/views/store/_shipping_germany_warning.blade.php +++ /dev/null @@ -1,16 +0,0 @@ -{{-- - Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. - See the LICENCE file in the repository root for full licence text. ---}} - -
-

NOTE TO GERMAN CUSTOMERS

- -

- We have recently been notified of issues regarding deliveries within Germany, possibly due to a change in German customs regulations. Multiple cases have been reported where packages are not delivered to the addressee, but instead to a customs house. The addressee is then sent a notice to pick up the item in person and pay an import sales tax. Unfortunately international customs procedures are out of our control, but please take this into account when placing your order. -

- -

- -

-
diff --git a/resources/views/store/address-destroy.blade.php b/resources/views/store/address-destroy.blade.php deleted file mode 100644 index d7ef27522c1..00000000000 --- a/resources/views/store/address-destroy.blade.php +++ /dev/null @@ -1,8 +0,0 @@ -{{-- - Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. - See the LICENCE file in the repository root for full licence text. ---}} -(function() { - var $el = $("#address-{{ $address_id }}") - $el.slideUp(function() { $el.remove() }) -}).call(this) diff --git a/resources/views/store/checkout/show.blade.php b/resources/views/store/checkout/show.blade.php index fa7069ff36e..d797be2ca6b 100644 --- a/resources/views/store/checkout/show.blade.php +++ b/resources/views/store/checkout/show.blade.php @@ -49,56 +49,26 @@ - @if ($order->requiresShipping()) -
-

Shipping Address

- - @if (count($addresses)) -
- @foreach($addresses as $a) - @include('store.objects.address', [ - 'data' => $a, - 'selected' => (isset($order->address) && $order->address->address_id === $a->address_id), - 'modifiable' => true, - ]) - @endforeach -
- @endif - - @include('store.objects.new_address') -
- @endif - - @if(!$order->requiresShipping() || $order->shipping) - @endsection diff --git a/resources/views/store/invoice.blade.php b/resources/views/store/invoice.blade.php index f5b0b821ae3..be5aec35ed8 100644 --- a/resources/views/store/invoice.blade.php +++ b/resources/views/store/invoice.blade.php @@ -19,13 +19,7 @@ @endif - @for ($i = 0; $i < $copies; $i++) - @if ($i > 0) - - @endif - - @include('store.orders._details') - @endfor + @include('store.orders._details') @include('store.orders._status') @@ -35,19 +29,4 @@ @endforeach @endif - - @if ($copies > 1) - - @endif @endsection diff --git a/resources/views/store/objects/address.blade.php b/resources/views/store/objects/address.blade.php index 11d7e3e3859..8c7eac562ac 100644 --- a/resources/views/store/objects/address.blade.php +++ b/resources/views/store/objects/address.blade.php @@ -2,49 +2,11 @@ Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. See the LICENCE file in the repository root for full licence text. --}} -@php - $modifiable = $modifiable ?? false; - $isCard = isset($selected); - if ($isCard) { - $modifiers[] = 'card'; - - if ($selected) { - $modifiers[] = 'card-active'; - } else { - $modifiers[] = 'card-hover'; - $withButtons = true; - } - } - - $mainClasses = class_with_modifiers('address', $modifiers ?? []); - $withButtons = $withButtons ?? false; - - if ($withButtons) { - $mainClasses .= ' clickable-row'; - } -@endphp -{!! Form::open([ - 'action' => 'StoreController@postUpdateAddress', - 'class' => $mainClasses, - 'data-remote' => true, - 'id' => "address-{$data->address_id}", -]) !!} +
{{ $data->first_name }} {{ $data->last_name }}
{{ $data->street }}
-
{{ $data->city }}, {{ $data->state }}, {{ $data->zip }}
+
{{ implode(', ', array_filter([$data->city, $data->state, $data->zip])) }}
{{ $data->countryName() }}
{{ $data->phone }}
- - @if ($withButtons) - {!! Form::hidden('id', $data->address_id) !!} - - - - - @endif -{!! Form::close() !!} +
diff --git a/resources/views/store/objects/new_address.blade.php b/resources/views/store/objects/new_address.blade.php deleted file mode 100644 index ee333754ead..00000000000 --- a/resources/views/store/objects/new_address.blade.php +++ /dev/null @@ -1,62 +0,0 @@ -{{-- - Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. - See the LICENCE file in the repository root for full licence text. ---}} -@php - $showForm = count($addresses) === 0; -@endphp -{!! Form::open(['action' => 'StoreController@postNewAddress', "data-remote" => true]) !!} -
-
-
-

Adding new shipping address

-
- -
- {!! Form::label('address[first_name]', 'First Name', ["class" => "sr-only"]) !!} - {!! Form::text("address[first_name]", null, ['class' => 'form-control', "placeholder" => "First Name"]) !!} -
-
- {!! Form::label('address[last_name]', 'Last Name', ["class" => "sr-only"]) !!} - {!! Form::text("address[last_name]", null, ['class' => 'form-control', "placeholder" => "Last Name"]) !!} -
-
- {!! Form::label('address[street]', 'Street Number, Name, Building, etc', ["class" => "sr-only"]) !!} - {!! Form::text("address[street]", null, ['class' => 'form-control', "placeholder" => "Street Number, Name, Building, etc"]) !!} -
-
- {!! Form::label('address[city]', 'City', ["class" => "sr-only"]) !!} - {!! Form::text("address[city]", null, ['class' => 'form-control', "placeholder" => "City"]) !!} -
-
- {!! Form::label('address[state]', 'State', ["class" => "sr-only"]) !!} - {!! Form::text("address[state]", null, ['class' => 'form-control', "placeholder" => "State"]) !!} -
-
- {!! Form::label('address[zip]', 'Post Code', ["class" => "sr-only"]) !!} - {!! Form::text("address[zip]", null, ['class' => 'form-control', "placeholder" => "Post Code"]) !!} -
-
- {!! Form::label('address[country_code]', 'Country', ["class" => "sr-only"]) !!} - {!!Form::select('address[country_code]', countries_array_for_select(), request_country(), ['class' => 'form-control']) !!} -
-
- {!! Form::label('address[phone]', 'Phone Number', ["class" => "sr-only"]) !!} - {!! Form::text("address[phone]", null, ['class' => 'form-control', "placeholder" => "Phone Number"]) !!} -
-
-
- -
- - @if (!$showForm) - Add new shipping address - @endif -
-{!! Form::close() !!} diff --git a/resources/views/store/objects/order.blade.php b/resources/views/store/objects/order.blade.php index 264b701167a..7795f494a13 100644 --- a/resources/views/store/objects/order.blade.php +++ b/resources/views/store/objects/order.blade.php @@ -6,7 +6,6 @@ $itemErrors = $validationErrors['orderItems'] ?? []; $checkout = $checkout ?? true; - $forShipping = $forShipping ?? false; $modifiers = $modifiers ?? []; $extraClasses = presence($extraClasses ?? null); @@ -14,43 +13,41 @@
    @foreach ($order->items as $i) - @if (!$forShipping || $i->product->requiresShipping()) -
  • -
    - @php - $subtext = $i->getSubtext(); - @endphp -
    {{ $i->getDisplayName(true) }}
    - @if ($subtext !== null) -
    - {{ $subtext }} -
    - @endif - - @if (isset($itemErrors[$i->id])) -
      - @foreach ($itemErrors[$i->id] as $message) -
    • {{ $message }}
    • - @endforeach -
    - @endif - -
    - @if (isset($weight)) -
    - @if ($i->product->weight !== null) - {{ $i->product->weight }}g - @endif +
  • +
    + @php + $subtext = $i->getSubtext(); + @endphp +
    {{ $i->getDisplayName(true) }}
    + @if ($subtext !== null) +
    + {{ $subtext }}
    @endif -
    - {{ osu_trans_choice('common.count.item', $i->quantity) }} -
    -
    - {{ currency($i->subtotal()) }} + + @if (isset($itemErrors[$i->id])) +
      + @foreach ($itemErrors[$i->id] as $message) +
    • {{ $message }}
    • + @endforeach +
    + @endif + +
    + @if (isset($weight)) +
    + @if ($i->product->weight !== null) + {{ $i->product->weight }}g + @endif
    -
  • - @endif + @endif +
    + {{ osu_trans_choice('common.count.item', $i->quantity) }} +
    +
    + {{ currency($i->subtotal()) }} +
    + @endforeach @if ($checkout && $order->shipping > 0) @@ -79,7 +76,7 @@ Total
    - {{ currency($order->getSubtotal($forShipping)) }} + {{ currency($order->getSubtotal()) }}
    @endif diff --git a/resources/views/store/orders/_details.blade.php b/resources/views/store/orders/_details.blade.php index 1b744385276..5e46052fe23 100644 --- a/resources/views/store/orders/_details.blade.php +++ b/resources/views/store/orders/_details.blade.php @@ -37,7 +37,9 @@

    Sent Via:

    - @include('store.objects.address', ['data' => $sentViaAddress, 'grid' => '']) +
    + osu!store +
    @@ -46,7 +48,7 @@

    Shipping To:

    - @include('store.objects.address', ['data' => $order->address, 'grid' => '']) + @include('store.objects.address', ['data' => $order->address])
    @endif @@ -63,7 +65,6 @@ 'order' => $order, 'weight' => true, 'checkout' => false, - 'forShipping' => $forShipping, ]) @if ($order->isHideSupporterFromActivity()) @@ -77,7 +78,7 @@ $showTrackingCode = ($order->isShipped() || $order->isDelivered() || Auth::user()->isAdmin()) && $order->tracking_code; $transactionDetails = [ - 'Salesperson' => "{$sentViaAddress->first_name} {$sentViaAddress->last_name}", + 'Salesperson' => 'osu!store', 'Order #' => "#{$order->order_id}", 'Shipping Method' => $showTrackingCode ? 'EMS ('.trim($order->tracking_code).')' : 'N/A', 'Shipping Terms' => 'FOB Japan', diff --git a/routes/web.php b/routes/web.php index ee02e21cd03..cbe2f921739 100644 --- a/routes/web.php +++ b/routes/web.php @@ -325,9 +325,6 @@ Route::get('listing', 'StoreController@getListing')->name('products.index'); Route::get('invoice/{invoice}', 'StoreController@getInvoice')->name('invoice.show'); - Route::post('update-address', 'StoreController@postUpdateAddress'); - Route::post('new-address', 'StoreController@postNewAddress'); - Route::group(['namespace' => 'Store'], function () { Route::post('products/{product}/notification-request', 'NotificationRequestsController@store')->name('notification-request'); Route::delete('products/{product}/notification-request', 'NotificationRequestsController@destroy'); diff --git a/tests/Libraries/OrderCheckoutTest.php b/tests/Libraries/OrderCheckoutTest.php index e50ca04800d..f33d1ce62f3 100644 --- a/tests/Libraries/OrderCheckoutTest.php +++ b/tests/Libraries/OrderCheckoutTest.php @@ -69,10 +69,30 @@ public function testTournamentBannerWhenNotAvailable() $this->assertNotEmpty($checkout->validate()); } + public function testShippableItemRequiresShopify() + { + $product = Product::factory()->create(['stock' => 5, 'max_quantity' => 5, 'shopify_id' => null]); + $orderItem = OrderItem::factory()->create([ + 'product_id' => $product, + 'quantity' => 1, + ]); + + $order = Order::factory()->create(); + $order->items()->save($orderItem); + + $checkout = new OrderCheckout($order); + $result = $checkout->validate(); + + $this->assertSame( + [osu_trans('model_validation/store/product.not_available')], + array_get($result, "orderItems.{$orderItem->getKey()}") + ); + } + public function testShopifyItemDoesNotMix() { $product1 = Product::factory()->create(['stock' => 5, 'max_quantity' => 5, 'shopify_id' => 1]); - $product2 = Product::factory()->create(['stock' => 5, 'max_quantity' => 5, 'shopify_id' => null]); + $product2 = Product::factory()->virtual()->create(['stock' => 5, 'max_quantity' => 5, 'shopify_id' => null]); $orderItem1 = OrderItem::factory()->create([ 'product_id' => $product1, 'quantity' => 1,