Laravel - Как убедиться, что цена заказа не меньше цены товара в поле ввода - PullRequest
0 голосов
/ 14 февраля 2020

Я использую Laravel -5,8 для веб-приложения, которое включает поле ввода Dynami c. Проект основан на Заказе клиента.

Модель

class Product extends Model
{
  protected $table = 'products';
  protected $fillable = [
          'name',
          'rate',
          'vat',
      ];
}

class Order extends Model
{
  protected $table = 'orders';
  protected $fillable = [
          'date',
          'customer_id',
          'workOrder_id',
          'details',
          'product_id',
          'rate',
          'vat',
          'quantity',
          'remaining',
      'amount',
      ];

  public function customer(){
    return $this->belongsTo(Customer::class, 'customer_id');
  }

  public function product(){
      return $this->belongsTo(Product::class, 'product_id');
  }
}

product_id в продуктах является внешним ключом для заказов

Контроллер

public function create()
{
    $order = null;
    $products = Product::all('id','name');
    $customers = Customer::all('id','name');
    return view('pages.order.create', compact('order','products','customers'));
}

public function store(Request $request)
{
    $request->validate([
        'date'      => 'required',
        'customer_id'  => ['required', Rule::notIn(['','0'])],
        'workOrder_id'     => 'required|unique:orders',
    ]);

    for ($i=0; $i < count($request->product_id) ; $i++) { 

        $order =  new Order;

        $order->date = $request->date;
        $order->customer_id = $request->customer_id;
        $order->workOrder_id = $request->workOrder_id;
        $order->details = $request->details;
        $order->product_id = $request->product_id[$i];
        $order->rate = $request->rate[$i];
        $order->vat = $request->vat[$i];
        $order->quantity = $request->quantity[$i];
        $order->remaining = $request->quantity[$i];
        $order->amount = $request->quantity[$i] * $request->rate[$i];

        $order->save();
    }

    return redirect()
                ->route('order.index')
                ->with('success', 'Added Successfully');
}

В В заказе у клиента может быть несколько продуктов

sales_order

просмотр

<section class="content">
    <div class="container-fluid">
        <div class="card card-warning">
            <div class="card-body">
                <form method="POST" action="{{ route('order.store') }}">
                    @csrf
                    {{-- Date --}}
                    <div class="form-group">
                        <label for="date">
                            Date
                        </label>

                        <input type="text" class="form-control{{ $errors->has('date') ? ' is-invalid' : '' }}" name="date" id="date" value="{{ old('date', optional($order)->date) }}">

                        @if ($errors->has('date'))
                        <span class="invalid-feedback">
                            <strong>{{ $errors->first('date') }}</strong>
                        </span>
                        @endif
                    </div>

                    {{-- customer --}}
                    <div class="form-group">
                        <label for="customer_id">Customer</label>
                        <select name="customer_id" class="form-control{{ $errors->has('customer_id') ? ' is-invalid' : '' }}" id="customer_id">
                            <option value="">Select</option>
                            @foreach($customers as $customer)
                            <option value="{{ $customer->id }}" {{old('customer_id', optional($order)->customer_id) == $customer->id ? 'selected':''}}>{{ $customer->name }}</option>
                            @endforeach
                        </select>
                        @if ($errors->has('customer_id'))
                        <span class="invalid-feedback">
                            <strong>{{ $errors->first('customer_id') }}</strong>
                        </span>
                        @endif
                    </div>

                    <div class="row">
                        <div class="col-md-1">
                            <button id="add_more" class="btn btn-info mt-4"><i class="fa fa-plus" title="Add More Product"></i></button>
                        </div>
                        <div class="col-md-11">
                            <div id="more_product">
                                <div class="row">
                                    <div class="col-md-3">
                                        {{-- Product --}}
                                        <div class="form-group">
                                            <label for="product_id">Select Product</label>
                                            <select name="product_id[]" class="form-control" id="product_id" required="required">
                                                <option value="">Select Product</option>
                                                @forelse($products as $product)
                                                <option value="{{ $product->id }}" >
                                                    {{ $product->name }}
                                                </option>
                                                @empty
                                                <option value="">No Product Found</option>
                                                @endforelse
                                            </select>
                                        </div>
                                    </div>
                                    <div class="col-md-3">
                                        {{-- Rate --}}
                                        <div class="form-group">
                                            <label for="rate">
                                                Rate
                                            </label>
                                            <input type="number" min="0" step="any" class="form-control" name="rate[]" id="rate" required="required">
                                        </div>
                                    </div>

                                    <div class="col-md-3">
                                        {{-- Quantity --}}
                                        <div class="form-group">
                                            <label for="vat">
                                                Vat
                                            </label>

                                            <input type="number" min="0" step="any" class="form-control" name="vat[]" id="vat" required="required">
                                        </div>
                                    </div>
                                    <div class="col-md-3">
                                        {{-- Quantity --}}
                                        <div class="form-group">
                                            <label for="quantity">
                                                Quantity
                                            </label>

                                            <input type="number" min="0" class="form-control" name="quantity[]" id="quantity" required="required">
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>

                    {{-- workOrder_id --}}
                    <div class="form-group">
                        <label for="workOrder_id">Work Order No</label>
                        <input type="text" class="form-control{{ $errors->has('workOrder_id') ? ' is-invalid' : '' }}" name="workOrder_id" id="workOrder_id" value="{{ old('workOrder_id', optional($order)->workOrder_id) }}">

                        @if ($errors->has('workOrder_id'))
                        <span class="invalid-feedback">
                            <strong>{{ $errors->first('workOrder_id') }}</strong>
                        </span>
                        @endif
                    </div>

                    {{-- Payment Type --}}

            {{-- <div class="form-group">
                <label for="payment">Payment</label>
                <select name="payment" class="form-control{{ $errors->has('payment') ? ' is-invalid' : '' }}" id="payment">
                    <option value="">Select Payment</option>
                    <option value="1">Cash</option>
                    <option value="2">Due</option>
                </select>
                @if ($errors->has('payment'))
                <span class="invalid-feedback">
                    <strong>{{ $errors->first('payment') }}</strong>
                </span>
                @endif
            </div> --}}


            {{-- Details --}}
            <div class="form-group">
                <label for="details">
                    Remarks
                </label>

                <textarea name="details" class="form-control {{ $errors->has('details') ? ' is-invalid' : '' }}" id="details" cols="30" rows="5">{{ old('details', optional($order)->details) }}</textarea>

                @if( $errors->has('details'))
                <span class="invalid-feedback">
                    <strong>{{ $errors->first('details') }}</strong>
                </span>
                @endif
            </div>

            {{-- Save --}}
            <div class="form-group row mb-0">
                <div class="col-md-12">
                    <button type="submit" class="btn btn-primary">
                        {{ __('Save') }}
                    </button>
                </div>
            </div>
        </form>
    </div>
</div>
</div>
</section>

Javascript

<script>
{{-- jquery datepicker --}}
$( function() {
    $( "#date" ).datepicker({
        dateFormat: 'yy-mm-dd',
        changeMonth: true,
        changeYear: true,
    });
});


$('#customer_id').select2({
    placeholder: 'Select Customer',

    ajax: {
        url: '{!!URL::route('customer-autocomplete-search')!!}',
        dataType: 'json',
        delay: 250,
        processResults: function (data) {
            return {
                results: data
            };
        },
        cache: true
    },
    theme: "bootstrap"
});

$('#product_id').select2({
    placeholder: 'Select Product',

    ajax: {
        url: '{!!URL::route('product-autocomplete-search')!!}',
        dataType: 'json',
        delay: 250,
        processResults: function (data) {
            return {
                results: data
            };
        },
        cache: true
    },
    theme: "bootstrap"
});

$(document).ready(function() {
    var max_fields      = 150;
    var wrapper         = $("#more_product");
    var add_button      = $("#add_more");

    var x = 1;
    $(add_button).click(function(e){
        e.preventDefault();
        if(x < max_fields){
            x++;
            $(wrapper).append('<div class="row"><div class="col-md-3">{{-- Product --}}<div class="form-group"><label for="product_id">Select Product</label><select name="product_id[]" class="form-control product_id" id="product_id" required="required"><option value="">Select Product</option>@forelse($products as $product)<option value="{{ $product->id }}" >{{ $product->name }}</option>@empty<option value="">No Product Found</option>@endforelse </select></div></div><div class="col-md-3">{{-- Rate --}}<div class="form-group"><label for="rate">Rate</label><input type="number" min="0" step="any" class="form-control" name="rate[]" id="rate" required="required"></div></div><div class="col-md-2">{{-- vat --}}<div class="form-group"><label for="vat">Vat</label><input type="number" min="0" step="any" class="form-control" name="vat[]" id="vat" required="required"></div></div><div class="col-md-3">{{-- Quantity --}}<div class="form-group"><label for="quantity">Quantity</label><input type="number" min="0" class="form-control" name="quantity[]" id="quantity" required="required"></div></div><div class="col-sm-1"><a href="#" class="remove_field"><button style="margin-top: 30px;" class="btn btn-info"><i class="fa fa-minus" title="Remove Item"></i></button></a></div></div>');

            $('.product_id').select2({
                placeholder: 'Select Product',

                ajax: {
                    url: '{!!URL::route('product-autocomplete-search')!!}',
                    dataType: 'json',
                    delay: 250,
                    processResults: function (data) {
                        return {
                            results: data
                        };
                    },
                    cache: true
                },
                theme: "bootstrap"
            });
        }
    });

    $(wrapper).on("click",".remove_field", function(e){
        e.preventDefault(); 
        $(this).parent().parent('div').remove(); 
        x--;
    })
});
</script>

Я занимаюсь этим уже несколько дней. Чего я хочу добиться, так это чтобы пользователь вводил ставку по заказу для определенного продукта, и система должна сравнивать ее со ставкой по продуктам.

Если ставка по заказу меньше, чем в продукте, система должна отобразить пользователю сообщение об ошибке, объясняющее проблему.

Как мне этого добиться?

Спасибо

1 Ответ

0 голосов
/ 15 февраля 2020

Хорошо, вот что я придумала. Как объяснялось ранее, это обеспечивает только проверку на стороне сервера, поскольку, как я вижу, у вас нет какой-либо клиентской части.

Первое, что я сделал, - преобразовал проверку в контроллере в запрос, введенный в * 1003. * метод. Я назвал это StoreOrderRequest

App \ Http \ Requests \ StoreOrderRequest

<?php

namespace App\Http\Requests;

use App\Order;
use App\Product;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;

/**
 * Class StoreOrderRequest
 *
 * @package App\Http\Requests
 *
 * @property string $date
 * @property int $customer_id
 * @property int $workOrder_id
 * @property array $product_id
 * @property array $rate
 * @property array $vat
 * @property array $quantity
 */
class StoreOrderRequest extends FormRequest
{
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules(): array
    {
        return [
            'date' => 'required',
            'customer_id' => [
                'required',
                'exists:customers,id',
            ],
            'workOrder_id' => [
                'required',
                'unique:orders',
            ],
            'details' => 'present',
            'product_id' => 'array',
            'rate' => 'array',
            'vat' => 'array',
            'quantity' => 'array',
        ];
    }

    /**
     * Prepare the data for validation.
     *
     * @return void
     */
    protected function prepareForValidation(): void
    {
        $this->redirect = route('order');

        $this->validator = $this->getValidatorInstance();

        $this->validator->after(function (Validator $validator) {

            $products = Product::whereIn('id', $this->product_id)->get();

            foreach ($this->product_id as $key => $id) {

                $rate = $products->firstWhere('id', $id)->rate;

                if ($rate < $this->rate[$key]) {
                    continue;
                }

                $validator->errors()->add(
                    'rate.'.$key,
                    __(
                        'validation.min.numeric',
                        [
                            'attribute' => 'rate',
                            'min' => $rate
                        ]
                    )
                );
            }

        });
    }

    /**
     * Create orders.
     *
     * @return void
     */
    protected function save(): void
    {
        $orders = collect($this->product_id)
            ->map(function (int $product_id, int $index) {
                return array_merge(
                    $this->only([
                        'date',
                        'customer_id',
                        'workOrder_id',
                        'details',
                    ]),
                    [
                        'product_id' => $this->product_id,
                        'rate' => $rate = $this->rate[$index],
                        'vat' => $this->vat[$index],
                        'quantity' => $qty = $this->quantity[$index],
                        'remaining' => $qty,
                        'amount' => $qty * $rate,
                    ]
                );
            });

        Order::insert($orders->toArray());
    }
}

Вы увидите, что я использовал метод prepareForValidation , что позволило нам зарегистрировать after проверочные хуки. Они проверяют каждую норму продукта и, если она меньше соответствующей записи Product, добавят ошибку для своего индекса в пакет ошибок.

(Обратите внимание, что я переопределил свойство redirect по умолчанию для работать с моим тестом - вам, вероятно, не понадобится эта строка, если вы просто возвращаетесь на предыдущую страницу).

Я также создал метод save, который сохраняет все эти заказы.

Благодаря всему этому мы теперь можем значительно ускорить ваш OrderController::store метод:

OrderController

/**
 * Store new record.
 *
 * @param  \App\Http\Requests\StoreOrderRequest $request
 * @return \Illuminate\Http\RedirectResponse
 */
public function store(StoreOrderRequest $request): RedirectResponse
{
    $request->save();

    return redirect()
        ->route('order')
        ->with('success', 'Added Successfully');
}

И вот соответствующий тест для выполнения Убедитесь, что все вышеперечисленное работает должным образом:

Тесты \ Feature \ ProductOrderTest

<?php

namespace Tests\Feature;

use App\Order;
use App\Product;
use App\Customer;
use Tests\TestCase;

use Illuminate\Foundation\Testing\RefreshDatabase;

class ProductOrderTest extends TestCase
{
    use RefreshDatabase;

    /**
     * @test
     */
    public function validation_fails_with_product_rate_lower_than_original_product_price()
    {
        $customer = factory(Customer::class)->create();

        $product_1 = factory(Product::class)->create(['rate' => 20.50]);
        $product_2 = factory(Product::class)->create(['rate' => 95.67]);

        $this->assertCount(0, Order::all());


        $response = $this->post(route('order.store'), [
            'date' => now(),
            'customer_id' => $customer->id,
            'workOrder_id' => 1,
            'details' => 'Order details',
            'product_id' => [
                0 => $product_1->id,
                1 => $product_2->id,
            ],
            'rate' => [
                0 => 20.49,
                1 => 95.66,
            ],
            'vat' => [
                0 => round(20.49 * 0.20),
                1 => round(95.66 * 0.20),
            ],
            'quantity' => [
                0 => 1,
                1 => 1,
            ],
        ]);


        $response->assertRedirect(route('order'));

        $response->assertSessionMissing('success');

        $response->assertSessionHasErrors([
            'rate.0' => __('validation.min.numeric', [
                'attribute' => 'rate',
                'min' => 20.50
            ]),
            'rate.1' => __('validation.min.numeric', [
                'attribute' => 'rate',
                'min' => 95.67
            ]),
        ]);

        $this->assertCount(0, Order::all());
    }

    /**
     * @test
     */
    public function saves_order_with_valid_request()
    {
        $customer = factory(Customer::class)->create();

        $product_1 = factory(Product::class)->create(['rate' => 20.50]);
        $product_2 = factory(Product::class)->create(['rate' => 95.67]);

        $this->assertCount(0, Order::all());


        $response = $this->post(route('order.store'), [
            'date' => $date = now(),
            'customer_id' => $customer->id,
            'workOrder_id' => 1,
            'details' => 'Order details',
            'product_id' => [
                0 => $product_1->id,
                1 => $product_2->id,
            ],
            'rate' => [
                0 => 20.50,
                1 => 95.68,
            ],
            'vat' => [
                0 => round(20.50 * 0.20),
                1 => round(95.68 * 0.20),
            ],
            'quantity' => [
                0 => 1,
                1 => 1,
            ],
        ]);

        $response->assertRedirect(route('order'));

        $response->assertSessionHas('success', 'Added Successfully');

        $response->assertSessionDoesntHaveErrors();

        $this->assertCount(2, Order::all());

        $this->assertDatabaseHas('orders', [
            'date' => $date = now(),
            'customer_id' => $customer->id,
            'workOrder_id' => 1,
            'details' => 'Order details',
            'product_id' => $product_1->id,
            'rate' => 20.50,
            'vat' => round(20.50 * 0.20),
            'quantity' => 1,
            'remaining' => 1,
            'amount' => 20.50,
        ]);

        $this->assertDatabaseHas('orders', [
            'date' => $date = now(),
            'customer_id' => $customer->id,
            'workOrder_id' => 1,
            'details' => 'Order details',
            'product_id' => $product_2->id,
            'rate' => 95.68,
            'vat' => round(95.68 * 0.20),
            'quantity' => 1,
            'remaining' => 1,
            'amount' => 95.68,
        ]);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...