Multi-Tenant - 13 - Exemplo de NFSe para o Ambiente Nacional em Golang

Perfeito, vamos padronizar os status da NFS-e nos dois mundos:

Assim todo mundo (API Go, serviço Delphi, frontend) fala o MESMO dialeto.


1. Enum de status em Go

Arquivo sugerido: internal/nfse/status.go

package nfse

type NfseStatus string

const (
	NfseStatusRascunho            NfseStatus = "rascunho"
	NfseStatusPendenteTransmissao NfseStatus = "pendente_transmissao"
	NfseStatusEmTransmissao       NfseStatus = "em_transmissao"
	NfseStatusAutorizada          NfseStatus = "autorizada"
	NfseStatusRejeitada           NfseStatus = "rejeitada"
	NfseStatusCancelada           NfseStatus = "cancelada"
	NfseStatusErroInterno         NfseStatus = "erro_interno"
)

// Se é um estado "final" (não muda sozinho)
func (s NfseStatus) IsFinal() bool {
	switch s {
	case NfseStatusAutorizada,
		NfseStatusRejeitada,
		NfseStatusCancelada:
		return true
	default:
		return false
	}
}

// Se a nota pode ser enviada para transmissão
func (s NfseStatus) CanSend() bool {
	switch s {
	case NfseStatusRascunho,
		NfseStatusPendenteTransmissao,
		NfseStatusErroInterno,
		NfseStatusRejeitada:
		return true
	default:
		return false
	}
}

// Se pode tentar cancelar a NFSe
func (s NfseStatus) CanCancel() bool {
	return s == NfseStatusAutorizada
}

// Se está em algum tipo de falha/erro
func (s NfseStatus) IsError() bool {
	return s == NfseStatusRejeitada || s == NfseStatusErroInterno
}

// Normalizar string (ex: vindo do banco ou request)
func ParseNfseStatus(raw string) NfseStatus {
	switch raw {
	case string(NfseStatusRascunho):
		return NfseStatusRascunho
	case string(NfseStatusPendenteTransmissao):
		return NfseStatusPendenteTransmissao
	case string(NfseStatusEmTransmissao):
		return NfseStatusEmTransmissao
	case string(NfseStatusAutorizada):
		return NfseStatusAutorizada
	case string(NfseStatusRejeitada):
		return NfseStatusRejeitada
	case string(NfseStatusCancelada):
		return NfseStatusCancelada
	case string(NfseStatusErroInterno):
		return NfseStatusErroInterno
	default:
		// fallback seguro
		return NfseStatusRascunho
	}
}

Uso típico no serviço/handler:

st := ParseNfseStatus(nf.Situacao)
if !st.CanSend() {
    // return erro 400: não pode reenviar neste estado
}

2. Enum equivalente em Delphi

Você pode fazer como constantes string (mais simples e casa direto com o texto do banco):

Arquivo: uNFSeStatus.pas

unit uNFSeStatus;

interface

type
  TNFSeStatus = string;

const
  NFSE_STATUS_RASCUNHO             : TNFSeStatus = 'rascunho';
  NFSE_STATUS_PENDENTE_TRANSMISSAO : TNFSeStatus = 'pendente_transmissao';
  NFSE_STATUS_EM_TRANSMISSAO       : TNFSeStatus = 'em_transmissao';
  NFSE_STATUS_AUTORIZADA           : TNFSeStatus = 'autorizada';
  NFSE_STATUS_REJEITADA            : TNFSeStatus = 'rejeitada';
  NFSE_STATUS_CANCELADA            : TNFSeStatus = 'cancelada';
  NFSE_STATUS_ERRO_INTERNO         : TNFSeStatus = 'erro_interno';

function NFSeStatusIsFinal(const AStatus: TNFSeStatus): Boolean;
function NFSeStatusCanSend(const AStatus: TNFSeStatus): Boolean;
function NFSeStatusCanCancel(const AStatus: TNFSeStatus): Boolean;
function NFSeStatusIsError(const AStatus: TNFSeStatus): Boolean;

implementation

function NFSeStatusIsFinal(const AStatus: TNFSeStatus): Boolean;
begin
  Result := (AStatus = NFSE_STATUS_AUTORIZADA) or
            (AStatus = NFSE_STATUS_REJEITADA) or
            (AStatus = NFSE_STATUS_CANCELADA);
end;

function NFSeStatusCanSend(const AStatus: TNFSeStatus): Boolean;
begin
  Result :=
    (AStatus = NFSE_STATUS_RASCUNHO) or
    (AStatus = NFSE_STATUS_PENDENTE_TRANSMISSAO) or
    (AStatus = NFSE_STATUS_ERRO_INTERNO) or
    (AStatus = NFSE_STATUS_REJEITADA);
end;

function NFSeStatusCanCancel(const AStatus: TNFSeStatus): Boolean;
begin
  Result := (AStatus = NFSE_STATUS_AUTORIZADA);
end;

function NFSeStatusIsError(const AStatus: TNFSeStatus): Boolean;
begin
  Result :=
    (AStatus = NFSE_STATUS_REJEITADA) or
    (AStatus = NFSE_STATUS_ERRO_INTERNO);
end;

end.

Uso dentro do serviço Delphi:

uses uNFSeStatus;

procedure TDmNFSe.ProcessarPendentes;
begin
  // ...
  // ao pegar uma nf:
  if not NFSeStatusCanSend(QPend.FieldByName('situacao').AsString) then
  begin
    QPend.Next;
    Exit;
  end;
  // ...
end;

Na hora de atualizar:

QUpd.ParamByName('situacao').AsString := NFSE_STATUS_EM_TRANSMISSAO;