Smart order entry
← Back to sales
@if (session('error'))
{{ session('error') }}
@endif {{-- Who, from where, when: shared by every way of starting an order. --}}

Smart order entry

Start from the order as it was written, a budget, or one plain sentence. You review and fix every line before anything is saved.

@if ($this->tierLabel())

Priced at {{ $this->tierLabel() }} rates

@endif
{{-- Three ways in. --}}
@foreach (['paste' => 'Paste the order', 'amount' => 'By amount', 'prompt' => 'Plain English', 'grid' => 'Grid'] as $key => $label) @endforeach
@if ($mode === 'paste')

One product and quantity per line, blank lines between groups, an optional group label like "Serums" or "Gummies". Place Order matches every line to a product.

@include('livewire.partials.validate-items-button')
Place order Parsing… @if (! empty($draftLines) && ! $showModal) @endif
@elseif ($mode === 'amount')

No product list needed. Enter roughly what the order should come to and every eligible in-stock product gets the same quantity, capped by what is on hand, so the total lands as close as possible to your amount. Pick specific products to use only those.

Narrows the whole-catalog pool below, and the product picker's own list.

@if (count($selectedProductIds))
@foreach ($selectedProductIds as $id) @php $product = $this->pickerProductMap()->get((int) $id); @endphp @continue(! $product) {{ $product->name }} @endforeach
@endif
Auto-suggest order Allocating…
@elseif ($mode === 'grid') @php $grid = $this->grid; @endphp {{-- Spreadsheet-style entry. Quantities live in the browser (deferred wire:model) and reach the server with the next request, so typing never waits on it; the totals below are worked out in the browser. Keyed by customer so the rates are fresh when the customer changes. --}}

Pick a category and type the pieces against each product: Enter or ↓ moves down, ↑ moves up. What you type stays as you switch categories, and until the sale is confirmed or you clear it.

@if (! $warehouseId)

Pick the warehouse above to see the stock.

@endif @if (! $customerId)

Pick the customer above to see their rates and the amounts.

@endif
@foreach ($grid['categories'] as $category) @endforeach
Only products in stock{{ $warehouseId ? '' : ' (in any of your warehouses until you pick one)' }} are listed.
@forelse ($grid['rows'] as $n => $row) @empty @endforelse
# Product Category Stock Rate Qty Amount
{{ $n + 1 }} {{ $row['name'] }} {{ $row['category'] ?? '—' }} {{ $row['stock'] ?? '—' }} {{ $row['rate'] !== null ? number_format($row['rate'], 2) : '—' }}
No products match these filters.
products · pcs · at list price with GST
Review order Opening…
@else

e.g. "create a sale order of available stock at shakti nagar of 2 piece each". Pick the customer above first; the warehouse can come from the instruction itself (typos are fine) or from the dropdown above.

@foreach ($this->promptTemplates() as $label => $template) @endforeach

Pick a template to start, then replace anything in [brackets] with the real warehouse, category, amount or quantity. A target amount is shared across the categories named; "1 each [category]" gives that group its own quantity. Products with no stock or no rate are always left out.

Build from prompt Thinking…
@endif
@if ($showModal) @php $priced = $this->pricedDraftLines(); $visible = $this->visibleLineIndices(); $schemes = $this->schemePreview(); $totals = $this->totals(); $unresolved = $this->unresolvedCount(); $unpriced = $this->unpricedCount(); $stockOnHand = $this->stockOnHand(); $shortIds = $this->overStockProductIds(); $overStock = $this->overStockCount(); $warehouseName = $warehouses->firstWhere('id', (int) $warehouseId)?->name ?? 'this warehouse'; $ready = count($draftLines) - $unresolved; $freeTotal = array_sum(array_column($schemes, 'free')); @endphp

Review order — {{ count($draftLines) }} line{{ count($draftLines) === 1 ? '' : 's' }}

{{-- The lines as order text (name + pieces), to paste as the next order with the same items. --}}
@if ($promptNote !== '')
{{ $promptNote }}
@endif @if ($unpriced > 0)
{{ $unpriced }} line(s) below have no selling price, so they would be sold at 0.00 and show as a loss. Set a price on the product, or pick the one that has a price, then confirm.
@endif @if ($unresolved > 0)
{{ $unresolved }} line(s) below still need a product and/or a quantity greater than zero before you can confirm.
@endif @if ($overStock > 0)
{{ $overStock }} line(s) below ask for more than {{ $warehouseName }} has in stock (marked in red). Lower the quantity, remove the line or pick another product, then confirm.
@endif
@if ($reviewSearch !== '') {{ count($visible) }} of {{ count($draftLines) }} shown @endif
{{-- Not overflow-x-auto: the per-row product picker is an absolutely-positioned dropdown and any overflow other than visible clips it (see sale-form.blade.php). --}}
@foreach ($draftLines as $i => $line) @continue(! in_array($i, $visible, true)) @php $p = $priced[$i] ?? null; $needsAttention = empty($line['product_id']) || (int) ($line['quantity'] ?? 0) <= 0; $scheme = $schemes[$i] ?? null; @endphp $needsAttention])> {{-- The line's place in the order: it keeps its number while a search hides other rows. --}} @endforeach @if (empty($visible)) @endif
# Typed as Group Matched product Qty Free Rate Amount
{{ $i + 1 }} {{ $line['raw_text'] }} {{ $line['section_label'] ?? '—' }} @if ($p && (float) $p['gross_unit_price'] <= 0) No price set @endif @if ($line['status'] === 'exact') Matched @elseif ($line['status'] === 'fuzzy') Check match{{ isset($line['candidates'][0]['score']) ? ' ('.$line['candidates'][0]['score'].'%)' : '' }} @elseif ($line['status'] === 'auto') Auto-suggested @else Select a product @endif @if (! empty($line['product_id'])) @php $onHand = $stockOnHand[(int) $line['product_id']] ?? 0; @endphp @if (in_array((int) $line['product_id'], $shortIds, true)) {{ $onHand > 0 ? 'Only '.$onHand.' in stock' : 'No stock' }} @else Stock {{ $onHand }} @endif @endif @php // Other likely matches, only those in stock. $alternatives = array_values(array_filter(array_slice($line['candidates'], 1), fn ($c) => ($stockOnHand[$c['product_id']] ?? 0) > 0)); @endphp @if (count($alternatives) > 0)
@foreach ($alternatives as $c) @endforeach
@endif
@if ($scheme && $scheme['free'] > 0) +{{ $scheme['free'] }} free @endif @if ($scheme && $scheme['next'])
{{ $scheme['next'] }}
@endif
{{ $p ? number_format($p['unit_price'], 2) : '—' }} {{ $p ? number_format($p['taxable'], 2) : '—' }}
No lines match "{{ $reviewSearch }}".
{{-- Summary stays in view beside the lines. --}}
@endif