Componentes (Admin)

mad-pdv

Frente de caixa (PDV): busca/bipagem, carrinho configurável, pagamento composto, venda a prazo e cupom.

Frente de caixa (PDV/POS) completa: busca de produto por bipagem ou nome, carrinho com colunas configuraveis, desconto por item e por total, pagamento composto com troco, venda a prazo gerando contas a receber, vendas em espera e impressao de cupom. Tudo declarado no Blade com <mad-pdv> e sub-tags.

Compilador: Mad\Pdv\MadPdvCompiler (passo 1.11 do MadBlade). Componente: Mad\Pdv\MadPdvComponent (abstrato, wrapper INTERNAL). Sem host proprio, o compiler instancia um Mad\Pdv\MadPdvStandalone. VOs de apoio: Mad\Pdv\PdvPayment, Mad\Pdv\PdvColumn, Mad\Pdv\PdvInstallmentPlan.

Exemplo minimo

<mad-pdv model="Produto" database="business"
         name-field="descricao" price-field="preco" barcode-field="ean"
         sale-model="Venda" item-model="VendaItem" payment-model="VendaPagamento"
         title="Caixa 01">
    <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_credito" label="Credito" sacado="adquirente"
                     gera-titulo prazo-primeira-dias="30" hotkey="3" />
</mad-pdv>

Obrigatorio: model, name-field, price-field, pelo menos um entre code-field e barcode-field, e os tres *-model de persistencia. Os demais campos de gravacao ja vem com o esquema canonico venda / venda_item / venda_pagamento.

Produto (busca e bipagem)

Prop Tipo Default Descricao
model string — Model do produto. Obrigatorio
database string MAIN_DATABASE Conexao
name-field string — Campo da descricao. Obrigatorio
price-field string — Campo do preco. Obrigatorio
code-field string '' Codigo interno (um dos dois e obrigatorio)
barcode-field string '' Codigo de barras (um dos dois e obrigatorio)
stock-field string '' Campo de estoque
unit-field string '' Unidade de medida
image-field string '' Imagem do produto
active-field string '' Campo de ativo
active-value string 1 Valor considerado ativo
order-by string '' Ordenacao da busca
where string '' Filtro DSL, ou :where="$closure"
search-min-length int 2 Minimo de caracteres para buscar
search-limit int 10 Itens no dropdown de busca
product-picker bool false Catalogo visual de produtos (para quem nao tem leitor)
product-picker-page-size int 24 Itens por pagina do catalogo

Persistencia

Todos os campos abaixo tem default; so os *-model sao obrigatorios.

Prop Default Prop Default
sale-model — item-model —
sale-total-field total item-sale-field venda_id
sale-subtotal-field subtotal item-product-field produto_id
sale-discount-field desconto item-qty-field quantidade
sale-datetime-field data_hora item-price-field preco_unitario
sale-customer-field cliente_id item-discount-field desconto
sale-operator-field operador_id item-total-field total
sale-status-field status payment-model —
sale-status-done concluida payment-sale-field venda_id
sale-document-field documento payment-method-field forma
sale-change-field troco payment-amount-field valor
sale-uuid-field client_uuid payment-tendered-field valor_recebido

sale-database, item-database e payment-database permitem gravar cada tabela numa conexao diferente da do produto.

Cliente

Prop Tipo Default Descricao
customer-model string '' Model do cliente (vazio = sem cliente)
customer-key string '' Chave (default: PK do model)
customer-display string {nome} Mask de exibicao
customer-order-by string '' Ordenacao (aceita direcao, ex. nome desc)
customer-where string '' Filtro DSL, ou :customer-where="$closure"
customer-required bool false Exige cliente para finalizar
customer-default-id int — Cliente pre-selecionado (consumidor final)

Comportamento

Prop Tipo Default Descricao
discount-mode string both none | item | total | both
max-discount-percent float sem limite Teto de desconto
allow-price-override bool false Permite editar o preco unitario
allow-fraction bool false Quantidade fracionada
stock-mode string — off | warn | block
stock-decrement bool — Baixa estoque ao finalizar
ask-document bool false Pergunta CPF/CNPJ na nota
hold-sales bool true Vendas em espera
hold-limit int 10 Maximo de vendas em espera
print-mode string browser off | browser
receipt-width int 80 Largura do cupom em mm: 58 ou 80
receipt-header string '' Cabecalho do cupom
receipt-footer string '' Rodape do cupom
auto-print bool false Imprime ao finalizar
on-finalized string '' Metodo do host chamado apos a venda

Visual

Prop Tipo Default Descricao
title string '' Titulo da tela
fullscreen-toggle bool true Botao de tela cheia
currency-symbol string R$ Simbolo da moeda
locale string '' Locale de formatacao (vazio = o da app)
density string normal normal | compact
show-images bool — Miniatura do produto no carrinho

<mad-pdv-payment>

Repetivel; a ordem no documento e a ordem dos botoes. Sem nenhuma declarada, o runtime usa o conjunto canonico dinheiro / debito / credito / pix.

Prop Tipo Default Descricao
method string — Identificador da forma. Obrigatorio
label string '' Rotulo do botao
icon string por metodo conhecido Icone lucide
allow-change bool false Aceita valor recebido maior e calcula troco
hotkey string '' Tecla de atalho
gera-titulo bool false Gera parcelas em contas a receber
sacado string nenhum Quem deve: nenhum | cliente | adquirente
entra-no-caixa bool false Conta na conferencia da gaveta
exige-cliente bool false Bloqueia finalizar sem cliente (crediario)
max-parcelas int 1 Limite de parcelas (cap 120)
prazo-primeira-dias int 0 Dias da venda ate o 1o vencimento
intervalo-dias int 30 Intervalo entre parcelas
ajuste-residuo string ultima Onde vai o centavo restante: ultima | primeira
taxa-percentual float 0 Taxa percentual da forma
taxa-fixa float 0 Taxa fixa por transacao
adquirente string '' Rotulo/id do sacado quando sacado="adquirente"

Com gera-titulo, configure tambem o destino dos titulos na tag raiz: receivable-model (vazio = feature desligada), mais os campos receivable-sale-field (venda_id), receivable-customer-field (cliente_id), receivable-sacado-field (sacado_tipo), receivable-doc-field (documento), receivable-number-field (parcela), receivable-count-field (total_parcelas), receivable-amount-field (valor), receivable-due-field (vencimento), receivable-method-field (forma), receivable-status-field (status) e receivable-status-open (aberto).

<mad-pdv-column>

Colunas extras do carrinho. Chave ausente (nenhuma declarada) = conjunto canonico.

Prop Tipo Default Descricao
field string '' Campo do produto exibido
label string '' Header
width string '' Largura
align string left left | center | right
slot string before-total after-product | before-total
transform string '' Transformer server-side da celula
transform-target string display display (so a celula) | stored (a saida tambem e gravada)
affects string '' Com stored: price faz a saida virar o preco unitario efetivo
mode string display display | input
input-type string text text | number | date | combo
options string '' Opcoes do combo
item-field string '' Campo do item de venda onde o valor digitado e gravado
required bool false Obrigatoria no modo input
maxlength int 120 Limite do input
default string '' Valor inicial
merge-ignore bool false Impede fundir linhas do mesmo produto com valores diferentes
receipt bool false Sai tambem no cupom

<mad-pdv-action>

Botao extra na toolbar, repetivel.

Prop Tipo Default Descricao
label string — Rotulo. Obrigatorio
method string — Metodo wire do host. Obrigatorio
icon string '' Icone lucide
hotkey string '' Tecla de atalho
confirm string '' Mensagem de confirmacao antes de executar

Subclasse (hooks)

use Mad\Pdv\MadPdvComponent;

class CaixaLoja extends MadPdvComponent
{
    protected string $model      = 'Produto';
    protected string $saleModel  = 'Venda';

    protected function beforeSaveSale(object $venda, array $ctx): void
    {
        $venda->unit_id = MadSession::unitId();
    }

    protected function afterSale(object $venda, array $itens, array $pagamentos): void
    {
    }
}

Gotchas

  • @if em volta de sub-tag do PDV nao funciona. O compilador roda antes do Blade, sobre o texto cru, e extrai <mad-pdv-payment> / <mad-pdv-column> por regex: a condicional some no render mas a sub-tag ja entrou na config. O sintoma e confuso — a forma nao aparece na tela e ainda assim dispara a validacao dela. Condicione pelos atributos (:gera-titulo="$cond").
  • Atributo mad-* nao aceita {{ }}; expressao PHP entra com dois-pontos.
  • <mad-pdv-receipt> e <mad-pdv-hotkey> sao reservadas de v2: geram warning no log e sao ignoradas, sem quebrar o render.
  • As colunas do carrinho viajam para o cliente com chave opaca (c0, c1, ...); o nome do campo nunca sai do servidor. A gravacao itera a lista do servidor, entao chave inventada no payload nunca e gravada.
  • O transformer de coluna roda sempre no servidor e e contido (captura o erro e loga uma vez por coluna) — um typo nao fecha o caixa.
  • O rateio de parcelas e servidor (PdvInstallmentPlan); o client so tem um espelho para o preview. Invariante: a soma das parcelas e sempre igual ao valor, com o residuo de centavos na ultima (ou na primeira, via ajuste-residuo).