mad-sheet
Planilha estilo Excel para lançamento em lote: teclado, paste TSV, colunas calculadas e save transacional.
Planilha de lancamento em lote estilo Excel: grade de celulas editaveis com
navegacao por teclado, paste TSV, coluna calculada, linha de totais e save
tudo-ou-nada numa transacao unica. Toda a config visual mora no Blade
(<mad-sheet> + filhos <mad-sheet-col>); o PHP so entra quando ha escopo
multi-tenant ou enriquecimento por linha.
Compilador: Mad\Sheet\MadSheetCompiler (passo 1.08 do MadBlade). Componente:
Mad\Sheet\MadSheet. Sem host proprio, o compiler instancia um
Mad\Sheet\MadSheetStandalone.
Tag
<mad-sheet model="StockCount" database="business" rows-min="10" max-rows="2000">
<mad-sheet-col field="produto_id" type="combo" model="Produto" display="nome"
label="Produto" required width="220px" />
<mad-sheet-col field="quantidade" type="number" label="Qtd" total="sum"
decimals="0" required />
<mad-sheet-col field="valor" type="money" label="Valor unit." total="sum" width="140px" />
<mad-sheet-col field="total" label="Total" compute="{quantidade} * {valor}" />
<mad-sheet-col field="data" type="date" label="Data" :default="date('Y-m-d')" width="150px" />
<mad-sheet-col field="obs" label="Observacao" placeholder="opcional" />
</mad-sheet>
Props de <mad-sheet>
| Prop | Tipo | Default | Descricao |
|---|---|---|---|
model |
string | — | Model Eloquent alvo dos INSERTs (short name ou FQCN). Obrigatorio |
database |
string | MAIN_DATABASE |
Conexao |
rows-min |
int | 8 |
Linhas em branco iniciais |
max-rows |
int | 2000 |
Guard de payload do batch |
totals |
bool | true |
Linha de totais. Desliga com totals="false" ou no-totals |
Props de <mad-sheet-col>
Uma coluna por filho self-closing; a ordem dos filhos e a ordem visual.
| Prop | Tipo | Default | Descricao |
|---|---|---|---|
field |
string | — | Coluna do model. Obrigatorio |
label |
string | valor de field |
Header |
type |
string | text |
text | number | money | date | combo (dbcombo = alias de combo) |
required |
bool | false |
Celula obrigatoria nas linhas preenchidas |
readonly |
bool | false |
Celula nao editavel (descartada no save) |
width |
string | auto | Largura fixa, ex. 140px |
total |
string | — | Totalizador da coluna: sum | count |
default |
string | — | Valor inicial das celulas novas |
placeholder |
string | — | Placeholder do input |
compute |
string | — | Coluna calculada, ex. {qtd} * {valor} — readonly e nunca persiste |
Extras por tipo
| Tipo | Props |
|---|---|
number / money |
decimals min max step prefix suffix decimal-sep thousand-sep fill-direction allow-negative |
date |
min max display-mask (default dd/mm/yyyy) database-mask (default yyyy-mm-dd) |
text |
mask (cpf, cnpj, ...) strip-mask maxlength force-case |
combo |
options="1:Ativo,2:Inativo" (vence model), model (alias legado: source), database ('' herda a do sheet), key (default id), display (coluna ou mask {nome} - {uf}; alias legado source-label), order-by order, where (DSL, ou :where="$closure"), search min-length, depends-on / depends-column (cascade por linha, forca search), no-results-message e a familia no-results-create-* / no-results-quick-register-* |
O combo aceita ainda o filho <mad-quick-form action="..."><mad-input-field .../></mad-quick-form>
para cadastro rapido dentro da celula.
Comportamento no client
- Teclado de planilha: Enter desce (cria linha nova no fim se a atual esta preenchida), setas navegam (esquerda/direita so com o caret na borda), Ctrl+D copia a celula de cima (fill-down), Ctrl+Z desfaz (stack de 50).
- Paste TSV do Excel espalha multi-celula a partir da celula focada, pulando
colunas readonly e normalizando money (
1.234,56->1234.56) e data (dd/mm/yyyy->Y-m-d). - Virtual scroll com linha de altura fixa (37px).
- Linha de totais soma/conta ao vivo no client.
- Toolbar padrao: "+10 linhas", "Desfazer", "Validar", "Salvar", com contadores de linhas preenchidas e de erros.
- Strings i18n em
mad.sheet.*(en/pt/es).
Persistencia
O Salvar envia so as linhas preenchidas como JSON
{indiceAbsoluto: {campo: valor}} para onSaveBatch, que valida tudo
(required, faixa numerica, data no formato da database-mask, maxlength,
mask com digito verificador de cpf/cnpj, membership do combo em lote e o
pareamento pai->filho do cascade) e persiste numa transacao unica
tudo-ou-nada. Qualquer erro devolve {row, field, msg}, o client destaca as
celulas e foca a primeira; nada e gravado parcialmente. O botao "Validar"
(onValidateBatch) roda a mesma validacao sem persistir.
Subclasse (hooks)
use Mad\Sheet\MadSheet;
class LancamentoSheet extends MadSheet
{
protected static string $wrapper = self::INTERNAL;
protected string $model = 'Lancamento';
protected string $database = 'business';
/** Roda por linha DENTRO da transacao. Excecao aqui reverte o batch inteiro. */
protected function beforeSaveRow(array $row, int $index): array
{
$row['unit_id'] = MadSession::unitId();
return $row;
}
/** Models ja persistidos: notificar, recalcular agregados. */
protected function afterSaveBatch(array $models): void
{
}
}
Gotchas
- Atributo de tag
mad-*nao aceita{{ }}— o compiler roda antes do Blade. Expressao PHP entra com dois-pontos::default="date('Y-m-d')". - Persistencia via
fill(): os campos das colunas precisam estar no$fillabledo model, senao o insert sai vazio. - So campos declarados em colunas entram no insert; payload extra e descartado
e
readonlysome no save. - Model e combo resolvem pelo registry — nunca uma classe crua vinda do request.
- Zeros em
money/numbernao contam como linha preenchida (o cell-component escreve0no blur sem digitacao). <mad-sheet-col>orfao fora de<mad-sheet>e removido no compile (defense in depth), nao vira componente Blade.