Módulos visuais
mad-pdv
mad-pdv-action
mad-pdv-payment
Compacto (zero PHP, catálogo visual)
mad-pdv com product-picker (sem leitor de código de barras) e só dinheiro/Pix.
| # | Product | Qty | Price | Disc. | Total | |||
|---|---|---|---|---|---|---|---|---|
|
|
|
|
||||||
resources/views/tutor/examples/modulos-pdv/compacto.blade.php
{{-- Zero PHP (MadPdvStandalone) — catálogo visual em vez de bipagem, e só os
dois métodos de pagamento mais usados. Grava no MESMO livro de vendas do
demo grande (tutor_pdv_vendas/itens/pagamentos): dois caixas, um catálogo. --}}
<mad-pdv model="TutorDemoPdvProduto" database="business"
name-field="nome" price-field="preco" barcode-field="ean" code-field="codigo"
stock-field="estoque" unit-field="unidade" active-field="ativo" active-value="1"
sale-model="TutorDemoPdvVenda" item-model="TutorDemoPdvVendaItem"
payment-model="TutorDemoPdvVendaPagamento"
title="Caixa 02 — Compacto" product-picker :product-picker-page-size="12">
<mad-pdv-payment method="dinheiro" label="Dinheiro" allow-change entra-no-caixa hotkey="1" />
<mad-pdv-payment method="pix" label="Pix" entra-no-caixa hotkey="2" />
</mad-pdv>
Completo (bipagem, fração, ação custom)
Quatro formas de pagamento + mad-pdv-action chamando TutorModulosPdv::onOpenDrawer.
| # | Product | Qty | Price | Disc. | Total | |||
|---|---|---|---|---|---|---|---|---|
|
|
|
|
||||||
resources/views/tutor/examples/modulos-pdv/completo.blade.php
@php
// Partial de <x-tutor-demo> = escopo Blade ISOLADO — `$that` não
// atravessa esse aninhamento sozinho. Reabre aqui porque a ação custom da
// toolbar (mad-pdv-action) chama TutorModulosPdv::onOpenDrawer, que só
// existe no HOST de verdade.
$that = \Mad\Component\MadRenderContext::getComponent();
@endphp
<mad-pdv model="TutorDemoPdvProduto" database="business"
name-field="nome" price-field="preco" barcode-field="ean" code-field="codigo"
stock-field="estoque" unit-field="unidade" active-field="ativo" active-value="1"
sale-model="TutorDemoPdvVenda" item-model="TutorDemoPdvVendaItem"
payment-model="TutorDemoPdvVendaPagamento"
stock-mode="warn" allow-fraction
title="Caixa 01 — Completo"
receipt-header="MAD MERCADO LTDA CNPJ 00.000.000/0001-00">
<mad-pdv-payment method="dinheiro" label="Dinheiro" allow-change entra-no-caixa hotkey="1" />
<mad-pdv-payment method="pix" label="Pix" entra-no-caixa hotkey="2" />
<mad-pdv-payment method="cartao_debito" label="Débito" hotkey="3" />
<mad-pdv-payment method="cartao_credito" label="Crédito" hotkey="4" />
<mad-pdv-action label="Abrir gaveta" method="onOpenDrawer" icon="archive"
confirm="Abrir a gaveta?" />
</mad-pdv>
app/control/Tutor/TutorModulosPdv.php
/** Ação custom da toolbar (<mad-pdv-action method="onOpenDrawer">). */
public function onOpenDrawer(array $data = []): MadResponse
{
$itens = count($data['cart']['items'] ?? []);
return (new MadResponse())->toast("Gaveta aberta (carrinho com {$itens} itens).", 'info');
}
Código-fonte completo da página
app/control/Tutor/TutorModulosPdv.php
<?php
namespace App\Control\Tutor;
use App\Models\TutorDemo\TutorDemoPdvProduto;
use Illuminate\Support\Facades\Schema;
use Mad\Http\MadResponse;
use Mad\Pdv\MadPdvComponent;
/**
* TutorModulosPdv — página "PDV" do MAD Lab (catálogo modulos_pdv). Cobre:
* mad-pdv, mad-pdv-action, mad-pdv-payment.
*
* Ao contrário de org-chart/sheet/reconcile, aqui a config INTEIRA viaja nos
* atributos da própria tag (molde PdvDevView — que também não declara
* `$model`/`$nameField`/etc. como propriedade da classe). O demo pequeno
* (compacto.blade.php) NÃO reabre `$that` — cai num MadPdvStandalone novo por
* request (zero PHP: sem `onOpenDrawer`, só os pagamentos canônicos). O
* grande (completo.blade.php) reabre `$that` porque só o HOST de verdade
* (esta classe) tem o método `onOpenDrawer` que a ação custom da toolbar
* chama. Os dois gravam no MESMO livro de vendas (tutor_pdv_vendas/itens/
* pagamentos) — dois caixas, um catálogo, um livro só (cenário real de loja).
*/
class TutorModulosPdv extends MadPdvComponent
{
protected static string $wrapper = self::INTERNAL;
public function mount(array $params = []): void
{
$this->ensureFixture();
}
/** Ação custom da toolbar (<mad-pdv-action method="onOpenDrawer">). */
public function onOpenDrawer(array $data = []): MadResponse
{
$itens = count($data['cart']['items'] ?? []);
return (new MadResponse())->toast("Gaveta aberta (carrinho com {$itens} itens).", 'info');
}
// ── Fixture ──────────────────────────────────────────────────────────
private function ensureFixture(): void
{
$schema = Schema::connection('business');
if (!$schema->hasTable('tutor_pdv_produtos')) {
$schema->create('tutor_pdv_produtos', function ($t) {
$t->increments('id');
$t->string('nome');
$t->decimal('preco', 12, 2)->default(0);
$t->string('ean', 20)->nullable();
$t->string('codigo', 20)->nullable();
$t->decimal('estoque', 12, 3)->default(0);
$t->string('unidade', 5)->default('UN');
$t->string('ativo', 1)->default('1');
});
}
if (!$schema->hasTable('tutor_pdv_vendas')) {
$schema->create('tutor_pdv_vendas', function ($t) {
$t->increments('id');
$t->dateTime('data_hora')->nullable();
$t->unsignedInteger('operador_id')->nullable();
$t->string('status', 20)->nullable();
$t->decimal('subtotal', 12, 2)->default(0);
$t->decimal('desconto', 12, 2)->default(0);
$t->decimal('total', 12, 2)->default(0);
$t->string('documento', 20)->nullable();
$t->decimal('troco', 12, 2)->default(0);
$t->char('client_uuid', 36)->nullable()->unique();
});
}
if (!$schema->hasTable('tutor_pdv_venda_itens')) {
$schema->create('tutor_pdv_venda_itens', function ($t) {
$t->increments('id');
$t->unsignedInteger('venda_id');
$t->unsignedInteger('produto_id');
$t->decimal('quantidade', 12, 3)->default(0);
$t->decimal('preco_unitario', 12, 2)->default(0);
$t->decimal('desconto', 12, 2)->default(0);
$t->decimal('total', 12, 2)->default(0);
});
}
if (!$schema->hasTable('tutor_pdv_venda_pagamentos')) {
$schema->create('tutor_pdv_venda_pagamentos', function ($t) {
$t->increments('id');
$t->unsignedInteger('venda_id');
$t->string('forma', 30);
$t->decimal('valor', 12, 2)->default(0);
$t->decimal('valor_recebido', 12, 2)->nullable();
});
}
if (TutorDemoPdvProduto::query()->count() > 0) {
return;
}
TutorDemoPdvProduto::query()->insert([
['id' => 1, 'nome' => 'Refrigerante Lata 350ml', 'preco' => 5.50, 'ean' => '7892000000001', 'codigo' => 'T001', 'estoque' => 80, 'unidade' => 'UN', 'ativo' => '1'],
['id' => 2, 'nome' => 'Pão Francês (unidade)', 'preco' => 0.80, 'ean' => '7892000000002', 'codigo' => 'T002', 'estoque' => 200, 'unidade' => 'UN', 'ativo' => '1'],
['id' => 3, 'nome' => 'Sabonete', 'preco' => 3.20, 'ean' => '7892000000003', 'codigo' => 'T003', 'estoque' => 40, 'unidade' => 'UN', 'ativo' => '1'],
['id' => 4, 'nome' => 'Café em pó 500g', 'preco' => 18.90, 'ean' => '7892000000004', 'codigo' => 'T004', 'estoque' => 25, 'unidade' => 'UN', 'ativo' => '1'],
['id' => 5, 'nome' => 'Carne moída kg', 'preco' => 34.90, 'ean' => '7892000000005', 'codigo' => 'T005', 'estoque' => 12.5, 'unidade' => 'KG', 'ativo' => '1'],
['id' => 6, 'nome' => 'Produto Descontinuado', 'preco' => 1.00, 'ean' => '7892000000006', 'codigo' => 'T006', 'estoque' => 0, 'unidade' => 'UN', 'ativo' => '0'],
]);
}
protected function view(): string|array
{
return ['tutor.modulos.pdv', []];
}
}
resources/views/tutor/modulos/pdv.blade.php
<x-tutor-page page="modulos_pdv">
<x-tutor-demo demo="tutor.examples.modulos-pdv.compacto"
title="Compacto (zero PHP, catálogo visual)" desc="mad-pdv com product-picker (sem leitor de código de barras) e só dinheiro/Pix." />
<x-tutor-demo demo="tutor.examples.modulos-pdv.completo"
title="Completo (bipagem, fração, ação custom)" desc="Quatro formas de pagamento + mad-pdv-action chamando TutorModulosPdv::onOpenDrawer."
php="TutorModulosPdv::onOpenDrawer" />
</x-tutor-page>