Skip to content

feat(invoice): expande o DTO ServiceInvoice (campos tipados, Borrower, raw) — v3.3.0#26

Merged
andrenfe merged 1 commit into
masterfrom
feat/expand-service-invoice-dto
Jul 10, 2026
Merged

feat(invoice): expande o DTO ServiceInvoice (campos tipados, Borrower, raw) — v3.3.0#26
andrenfe merged 1 commit into
masterfrom
feat/expand-service-invoice-dto

Conversation

@andrenfe

Copy link
Copy Markdown
Member

Objetivo

Tornar a NFS-e usável pela API tipada do SDK PHP. Hoje o ServiceInvoice cobre ~13 de ~39 campos do fio e o raw (escape-hatch prometido) nunca era populadonumber, checkCode, tributos e borrower ficavam inacessíveis. É a mitigação do lado do SDK enquanto o nfe/docs não componentiza a resposta de sucesso (o spec a declara inline, sem schema nomeado, então o codegen não gera o modelo).

OpenSpec change: expand-service-invoice-dto.

O que muda

  • AbstractResource::hydrate() popula raw de qualquer DTO que o declare, com o payload decodificado completo → todo campo do fio fica acessível via $dto->raw['campo'], mesmo sem tipo. Unifica o padrão que WebhooksResource/lookups já faziam à mão (consistência SDK-wide; todos os params raw são ?array). Também hidrata DTOs aninhados (Nfe\...) recursivamente.
  • Campos tipados de alto valor no ServiceInvoice: number, checkCode, description, cityServiceCode, baseTaxAmount, issRate, issTaxAmount, amountNet (anexados ao fim do construtor — aditivo).
  • DTO aninhado Borrower (ServiceInvoice::$borrower); federalTaxNumber é int|string|null (tolerante a CPF/CNPJ — desvio deliberado vs. o integer do spec, pinado pelo teste de alinhamento). provider (pesado) fica via raw.
  • Teste de alinhamento YAML↔DTO (ServiceInvoiceSpecAlignmentTest), ancorado pelo path /serviceinvoices/{id} (o operationId ServiceInvoices_idGet colide com /external/{id}). Direção DTO ⊆ spec; pina totalAmount como phantom ausente.
  • totalAmount @deprecated — a API nunca o retorna (sempre null; confirmado ao vivo). Sample e cookbook atualizados.

Decisão de design (gradiente de valor)

Não se tipa os 39 campos — só os de alto valor; o resto vive no raw. Mantém o DTO enxuto e o alinhamento barato. Detalhes e trade-offs no design.md da change.

Qualidade

  • Testes novos: raw populado (retrieve + list por item), campos tipados, Borrower (int e string federalTaxNumber), provider via raw, alinhamento (4 cenários).
  • PHPStan e php-cs-fixer limpos; generate:check em sync; testes da change verdes em PHP 8.2 e 8.4.
  • ⚠️ Nota: o teste pré-existente CurlTransportFailurePhaseTest (do feat(http): retry seguro de POST + idempotência por externalId (release v3.2.0) #23) é frágil a ambientes com resolver catch-all (.invalid → errno 60 em vez de 6). Passa no CI limpo; não é tocado por este PR. Follow-up sugerido para robustecê-lo.

SemVer

Minor (v3.3.0). Tudo aditivo; totalAmount deprecado (não removido). Corte no PR (padrão do repo). 🏷️ Tag após o merge.

Ponte (não é o conserto de raiz)

Quando o nfe/docs componentizar a resposta de sucesso (issue rascunhada) + re-sync, o modelo pode migrar para o tipo gerado — o teste de alinhamento protege essa transição. Mesma abordagem se aplica a Node/Ruby (playbooks separados).

…, raw) — release v3.3.0

Mitigação do lado do SDK para o DTO de NFS-e incompleto, enquanto o upstream
(nfe/docs) não componentiza a resposta de sucesso.

- AbstractResource::hydrate() agora popula `raw` de qualquer DTO que o declare
  (payload decodificado completo) — o escape-hatch de forward-compat vira
  consistente em todo o SDK (antes só Webhooks/lookups o faziam à mão). Também
  hidrata DTOs aninhados (Nfe\...) recursivamente. Nenhum campo do fio se perde.
- ServiceInvoice ganha campos tipados de alto valor: number, checkCode,
  description, cityServiceCode, baseTaxAmount, issRate, issTaxAmount, amountNet
  (anexados ao fim do construtor — aditivo).
- Novo DTO aninhado Borrower (federalTaxNumber int|string|null, tolerante a
  CPF/CNPJ); provider (pesado) permanece via raw.
- Teste de alinhamento YAML↔DTO (ServiceInvoiceSpecAlignmentTest) ancorado por
  PATH /serviceinvoices/{id} — o operationId ServiceInvoices_idGet colide com
  /external/{id}.
- totalAmount marcado @deprecated (a API nunca o retorna; sempre null).
- Sample e docs/recursos/service-invoices.md atualizados.

SemVer: minor. Tudo aditivo; totalAmount deprecado (não removido). É uma PONTE:
quando o nfe/docs componentizar a resposta + re-sync, o modelo migra para o tipo
gerado, protegido pelo teste de alinhamento.

Ref: OpenSpec change expand-service-invoice-dto.
@andrenfe andrenfe self-assigned this Jul 10, 2026
@andrenfe andrenfe merged commit 1ad7fba into master Jul 10, 2026
9 of 10 checks passed
@andrenfe andrenfe deleted the feat/expand-service-invoice-dto branch July 10, 2026 01:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant