Jump to content
marcolopes

AT - questões legais

Recommended Posts

americob

 

Em 26/08/2020 às 23:12, marcolopes disse:

DÚVIDA no código L do CÓDIGO QR

De acordo com a tabela SAFT (TaxType) temos

NÃO SUJEITO é relativo ao caso "NS" da tabela TaxType...

E o NÃO TRIBUTÁVEL?

Devemos aqui incluir os casos  "OUT" da tabela TaxType?

 

Já tinha levantado essa questão há algum tempo:

Em 17/08/2020 às 19:58, americob disse:

Estava agora a passar os olhos pelas especificações técnicas:

Neste caso do código L, será que vai ser só para preencher quando uma fatura sujeita é a IS e por essa via não sujeita/não tributada em IVA, ou será que querem que se meta lá tudo o que tiver sido faturado com o código "M99" Não sujeito/Não tributado?

É que ele já pedem o I2, J2, e K2, todos semelhantes para aplicação por regiões (PT, PT-AC e PT-MA):

Eu vejo a maioria dos campos como "opcionais", mas, pelo menos alguns, só me parecem ser opcionais se outros tiverem informação.

Parece-me que estava na hora de a AT começar a divulgar umas FAQ's porque os 3 exemplos que dão no fim são muito fraquinhos.

 

Tenho pensado no assunto e, tendo em conta que o meu software só faz faturas em IVA, não faz em IS, até que saiam instruções específicas sobre como fazer, acho que os valores sem IVA só deverão ir no código "L" se o motivo TaxExemptionCode for o "M99".

Ou seja, se todos os TaxExemptionCode's de uma determinada fatura forem "M99", o L será igual à soma de I2 + J2 + K2. Por oposição, se nenhum dos TaxExemptionCode's da fatura for "M99", o L ficará a zero. Havendo diversos TaxExemptionCode's na fatura, terei que calcular a soma das linhas com "M99" para colocar em L.

Aguardemos pelas necessárias FAQ's.

  • Vote 1

Share this post


Link to post
Share on other sites
marcolopes
20 minutes ago, americob said:

Já tinha levantado essa questão há algum tempo:

Tenho pensado no assunto e, tendo em conta que o meu software só faz faturas em IVA, não faz em IS, até que saiam instruções específicas sobre como fazer, acho que os valores sem IVA só deverão ir no código "L" se o motivo TaxExemptionCode for o "M99".

Ou seja, se todos os TaxExemptionCode's de uma determinada fatura forem "M99", o L será igual à soma de I2 + J2 + K2. Por oposição, se nenhum dos TaxExemptionCode's da fatura for "M99", o L ficará a zero. Havendo diversos TaxExemptionCode's na fatura, terei que calcular a soma das linhas com "M99" para colocar em L.

Aguardemos pelas necessárias FAQ's.

EXACTAMENTE a minha abordagem! (excepto que eu não faço verificação do MOTIVO de ISENÇÃO, porque eu apenas aceito o MOTIVO M99 num TaxCode "NS" (portanto, para ser usado um M99, terá de ser criada uma "taxa de IVA" com o respectivo TaxCode = NÃO SUJEITO.

	/** KEYS ordenadas por ordem ASCENDENTE */
	private class TaxCountryRegionMap extends TreeMap<String, TaxCodeMap> {

		/** Nao sujeito / nao tributavel em IVA */
		public BigDecimal L=BigDecimal.ZERO;

		public TaxCountryRegionMap(Entidadesdocumentos documento) {
			for (Entidadesdocumentosivas iva: documento.getTabelaiva()){
				//separa por TaxCountryRegion
				String key=getTaxCountryRegion(iva.getCodigoiva());
				if (!containsKey(key)) put(key, new TaxCodeMap());
				//acumula valores
				switch(iva.getCodigoiva().getTipotaxa()){
				case Codigosiva.tipotaxa_normal:
				case Codigosiva.tipotaxa_reduzida:
				case Codigosiva.tipotaxa_intermedia:
				case Codigosiva.tipotaxa_isenta: get(key).add(iva); break;
				case Codigosiva.tipotaxa_outras:
				case Codigosiva.tipotaxa_naosujeito: L.add(iva.getTotal()); break;
				}
			}

		}

	}

Mas a complexidade do código para uma coisa aparentemente tão simples é assombrosa... tenho uma estrutura dentro desta que trata de separar cada TaxCountryRegion por TaxCode (acumulando os valores)

Será que a AT pensou bem naquilo que quer controlar?

Edited by marcolopes
  • Vote 1

The simplest explanation is usually the correct one

JAVA Utilities: https://github.com/marcolopes/dma

Share this post


Link to post
Share on other sites
paulofvoliveira
On 8/20/2020 at 11:46 AM, brunotoira said:

 


uses DelphiZXingQRCode;

Procedure GravarDocumento ou ImprimirDocumento;
var CodigoQR:String;
...
...
...
begin
  
Ao gravar documento ou antes de imprimir:
  
//obter código unico
//ainda estou a aguardar informação adicional sobre o pedido do ATCUD
CodigoQR:=copy(AnsiReverseString(IntToStr(IBDOCS.fieldbyname('ID').Asinteger*4)),1,4);
CodigoQR:=CodigoQR+IBDOCS.fieldbyname('SERIE').AsString;
CodigoQR:=CodigoQR+copy(IntToStr(NumeroDocumento*2),5,9);
    
//criar os campos para gerar o QRCode.
//A NIF do emitente *
//B NIF do adquirente *
//C País do adquirente *
//D Tipo de documento *
//E Estado do documento *
//F Data do documento *
//G Identificação única do documento *
//H ATCUD *
//I1 Espaço fiscal *
//I2 Base tributável isenta de IVA *
//I3 Base tributável de IVA à taxa reduzida *
//I4 Total de IVA à taxa reduzida *
//I5 Base tributável de IVA à taxa intermédia *
//I6 Total de IVA à taxa intermédia *
//I7 Base tributável de IVA à taxa normal *
//I8 Total de IVA à taxa normal *
//J1 Espaço fiscal *
//J2 Base tributável isenta *
//J3 Base tributável deIVA à taxa reduzida *
//J4 Total de IVA à taxa reduzida *
//J5 Base tributável de IVA à taxa intermédia *
//..
//...
//...
//....
//.....
//etc....
//N Total de impostos *
//O Total do documento com impostos *
//Q 4 carateres do Hash *
//R Nº do certificado *

   StringCodigo:='A:NIFEMISSOR*';
   StringCodigo:=StringCodigo+'B:'+IBDocs.FieldByName('NRCONTRIBUINTE').AsString+'*';
   StringCodigo:=StringCodigo+'C:PT*';
   if TIPODOCUMENTO='FACTURA SIMPLIFICADA' then
     StringCodigo:=StringCodigo+'D:FS*';
   if TIPODOCUMENTO='NOTA DE CREDITO' then
     StringCodigo:=StringCodigo+'D:NC*';
   if TIPODOCUMENTO='RECIBO' then
     StringCodigo:=StringCodigo+'D:RE*';
    //ETC...

   StringCodigo:=StringCodigo+'E:N*';
   StringCodigo:=StringCodigo+'F:'+FORMATDATETIME('YYYYMMDD',IBDocs.FieldByName('DATADOC').AsDateTime)+'*';
   StringCodigo:=StringCodigo+'G:'+IBDOCS.fieldbyname('INICIAISDOC').AsString + ' ' + IBDOCS.FIELDBYNAME('SERIE').AsString + '/' + IBDOCS.fieldbyname('NDOC').asstring+'*';
   StringCodigo:=StringCodigo+'H:'+IBDocs.FieldByName('CODLOJA').AsString +'-'+IBDocs.FieldByName('NDOC').AsString+'*';
   StringCodigo:=StringCodigo+'I1:PT*';

   ibquery1.close;
   ibquery1.SQL.clear;
   ibquery1.SQL.add(
     'select ROUND(TOTAISIVA1,2), ROUND(TOTAISIVA2,2), ROUND(TOTAISIVA3,2), ROUND(TOTAISIVA4,2) FROM DOCS WHERE tipodoc=:TIPODOC AND ndoc=:NDOC');
   ibquery1.parambyname('TIPODOC').AsString := TIPODOCUMENTO;
   ibquery1.parambyname('NDOC').AsInteger := NumeroDocumento;
   ibquery1.open;

   while NOT ibquery1.eof do
    begin
      if ibquery1.FieldByName('IVA').AsInteger=6 then
        begin
          StringCodigo:=StringCodigo+'I3:'+FloatToStr(ibquery1.FieldByName('TOTALSEMIVA').AsFloat)+'*';
          StringCodigo:=StringCodigo+'I4:'+FloatToStr(ibquery1.FieldByName('TOTALIVA').AsFloat)+'*';
        end;
      if ibquery1.FieldByName('IVA').AsInteger=13 then
        begin
          StringCodigo:=StringCodigo+'I5:'+FloatToStr(ibquery1.FieldByName('TOTALSEMIVA').AsFloat)+'*';
          StringCodigo:=StringCodigo+'I6:'+FloatToStr(ibquery1.FieldByName('TOTALIVA').AsFloat)+'*';
        end;
      if ibquery1.FieldByName('IVA').AsInteger=23 then
        begin
          StringCodigo:=StringCodigo+'I7:'+FloatToStr(ibquery1.FieldByName('TOTALSEMIVA').AsFloat)+'*';
          StringCodigo:=StringCodigo+'I8:'+FloatToStr(ibquery1.FieldByName('TOTALIVA').AsFloat)+'*';
        end;
      ibquery1.Next;
    end;

   StringCodigo:=StringCodigo+'N:'+FloatToStr(RoundNExtend(IBDOCS.FieldByName('TOTALIVA').AsFloat,2))+'*';
   StringCodigo:=StringCodigo+'O:'+FloatToStr(RoundNExtend(IBDOCS.FieldByName('TOTALGERAL').AsFloat,2))+'*';
   StringCodigo:=StringCodigo+'Q:'+copy(IBDocs.FieldByName('PRINTHASH').AsString,1,4)+'*'; //CODIGO HASH
   StringCodigo:=StringCodigo+'R:'+CodigoCertificado+'*'; // VOSSO NUMERO DE CERTIFICADO
   StringCodigo:=StringCodigo+'S:NU;'+FloatToStr(RoundNExtend(IBDOCS.FieldByName('TOTALGERAL').AsFloat,2));

   StringCodigo:=StringReplace(StringCodigo,',','.',[rfReplaceAll]);

   UpdateQRCode(StringCodigo,CodigoQR);
        
   //Isto vai-vos criar o BMP com o QRCode.
   //Depois só têm que grava-lo numa BD, e no CrystalReports, adicionar o campo dessa BD.
   +/- ISTO UPDATE DOCS SET IMAGEMQRCODE=FILESTREAM(C:\PASTATEMPORARIA\QRCODE\XXXXXXXXX.BMP
  
          
       
        
        
Procedure TDataModule1.UpdateQRCode(Texto,Codigo:String);
var
  QRCode: TDelphiZXingQRCode; //Gratuito é só ir buscar á net.
  Row, Column: Integer;
  Scale: Double;
  QRCodeBitmap: TBitmap;
  ImageQR:TImage;
begin

  try
    ImageQR:=TImage.Create(self);
    QRCodeBitmap := TBitmap.Create;
    QRCode := TDelphiZXingQRCode.Create;

    QRCode.Data := Texto;
    QRCode.Encoding := TQRCodeEncoding(0);
    QRCode.QuietZone := 1;
    QRCodeBitmap.SetSize(QRCode.Rows, QRCode.Columns);
    for Row := 0 to QRCode.Rows - 1 do
    begin
      for Column := 0 to QRCode.Columns - 1 do
      begin
        if (QRCode.IsBlack[Row, Column]) then
        begin
          QRCodeBitmap.Canvas.Pixels[Column, Row] := clBlack;
        end else
        begin
          QRCodeBitmap.Canvas.Pixels[Column, Row] := clWhite;
        end;
      end;
    end;

    ImageQR.Canvas.Brush.Color := clWhite;
    ImageQR.Canvas.FillRect(Rect(0, 0, ImageQR.Width, ImageQR.Height));
    if ((QRCodeBitmap.Width > 0) and (QRCodeBitmap.Height > 0)) then
    begin
      if (ImageQR.Width < ImageQR.Height) then
      begin
        Scale := ImageQR.Width / QRCodeBitmap.Width;
      end else
      begin
        Scale := ImageQR.Height / QRCodeBitmap.Height;
      end;
      ImageQR.Canvas.StretchDraw(Rect(0, 0, Trunc(Scale * QRCodeBitmap.Width), Trunc(Scale * QRCodeBitmap.Height)), QRCodeBitmap);
    end;
  ImageQR.Picture.SaveToFile('C:\PASTATEMPORARIA\QRCode\'+Codigo+'.bmp');
  QRCode.Free;
  QRCodeBitmap.Free;

  except

  end;

end;

Delphi

Se precisarem para outras linguagens de programação, mandem msg.

Boas,

Precisava para C# :) mas precisava mais de saber quais são os campos do documento a inserir no QRCode, ou onde posso encontrar informação sobre isso.

Obg.

Share this post


Link to post
Share on other sites
marcolopes
On 8/20/2020 at 11:46 AM, brunotoira said:

 


uses DelphiZXingQRCode;

Procedure GravarDocumento ou ImprimirDocumento;
var CodigoQR:String;
...
...
...
begin
  
Ao gravar documento ou antes de imprimir:
  
//obter código unico
//ainda estou a aguardar informação adicional sobre o pedido do ATCUD
CodigoQR:=copy(AnsiReverseString(IntToStr(IBDOCS.fieldbyname('ID').Asinteger*4)),1,4);
CodigoQR:=CodigoQR+IBDOCS.fieldbyname('SERIE').AsString;
CodigoQR:=CodigoQR+copy(IntToStr(NumeroDocumento*2),5,9);
    
//criar os campos para gerar o QRCode.
//A NIF do emitente *
//B NIF do adquirente *
//C País do adquirente *
//D Tipo de documento *
//E Estado do documento *
//F Data do documento *
//G Identificação única do documento *
//H ATCUD *
//I1 Espaço fiscal *
//I2 Base tributável isenta de IVA *
//I3 Base tributável de IVA à taxa reduzida *
//I4 Total de IVA à taxa reduzida *
//I5 Base tributável de IVA à taxa intermédia *
//I6 Total de IVA à taxa intermédia *
//I7 Base tributável de IVA à taxa normal *
//I8 Total de IVA à taxa normal *
//J1 Espaço fiscal *
//J2 Base tributável isenta *
//J3 Base tributável deIVA à taxa reduzida *
//J4 Total de IVA à taxa reduzida *
//J5 Base tributável de IVA à taxa intermédia *
//..
//...
//...
//....
//.....
//etc....
//N Total de impostos *
//O Total do documento com impostos *
//Q 4 carateres do Hash *
//R Nº do certificado *

   StringCodigo:='A:NIFEMISSOR*';
   StringCodigo:=StringCodigo+'B:'+IBDocs.FieldByName('NRCONTRIBUINTE').AsString+'*';
   StringCodigo:=StringCodigo+'C:PT*';
   if TIPODOCUMENTO='FACTURA SIMPLIFICADA' then
     StringCodigo:=StringCodigo+'D:FS*';
   if TIPODOCUMENTO='NOTA DE CREDITO' then
     StringCodigo:=StringCodigo+'D:NC*';
   if TIPODOCUMENTO='RECIBO' then
     StringCodigo:=StringCodigo+'D:RE*';
    //ETC...

   StringCodigo:=StringCodigo+'E:N*';
   StringCodigo:=StringCodigo+'F:'+FORMATDATETIME('YYYYMMDD',IBDocs.FieldByName('DATADOC').AsDateTime)+'*';
   StringCodigo:=StringCodigo+'G:'+IBDOCS.fieldbyname('INICIAISDOC').AsString + ' ' + IBDOCS.FIELDBYNAME('SERIE').AsString + '/' + IBDOCS.fieldbyname('NDOC').asstring+'*';
   StringCodigo:=StringCodigo+'H:'+IBDocs.FieldByName('CODLOJA').AsString +'-'+IBDocs.FieldByName('NDOC').AsString+'*';
   StringCodigo:=StringCodigo+'I1:PT*';

   ibquery1.close;
   ibquery1.SQL.clear;
   ibquery1.SQL.add(
     'select ROUND(TOTAISIVA1,2), ROUND(TOTAISIVA2,2), ROUND(TOTAISIVA3,2), ROUND(TOTAISIVA4,2) FROM DOCS WHERE tipodoc=:TIPODOC AND ndoc=:NDOC');
   ibquery1.parambyname('TIPODOC').AsString := TIPODOCUMENTO;
   ibquery1.parambyname('NDOC').AsInteger := NumeroDocumento;
   ibquery1.open;

   while NOT ibquery1.eof do
    begin
      if ibquery1.FieldByName('IVA').AsInteger=6 then
        begin
          StringCodigo:=StringCodigo+'I3:'+FloatToStr(ibquery1.FieldByName('TOTALSEMIVA').AsFloat)+'*';
          StringCodigo:=StringCodigo+'I4:'+FloatToStr(ibquery1.FieldByName('TOTALIVA').AsFloat)+'*';
        end;
      if ibquery1.FieldByName('IVA').AsInteger=13 then
        begin
          StringCodigo:=StringCodigo+'I5:'+FloatToStr(ibquery1.FieldByName('TOTALSEMIVA').AsFloat)+'*';
          StringCodigo:=StringCodigo+'I6:'+FloatToStr(ibquery1.FieldByName('TOTALIVA').AsFloat)+'*';
        end;
      if ibquery1.FieldByName('IVA').AsInteger=23 then
        begin
          StringCodigo:=StringCodigo+'I7:'+FloatToStr(ibquery1.FieldByName('TOTALSEMIVA').AsFloat)+'*';
          StringCodigo:=StringCodigo+'I8:'+FloatToStr(ibquery1.FieldByName('TOTALIVA').AsFloat)+'*';
        end;
      ibquery1.Next;
    end;

   StringCodigo:=StringCodigo+'N:'+FloatToStr(RoundNExtend(IBDOCS.FieldByName('TOTALIVA').AsFloat,2))+'*';
   StringCodigo:=StringCodigo+'O:'+FloatToStr(RoundNExtend(IBDOCS.FieldByName('TOTALGERAL').AsFloat,2))+'*';
   StringCodigo:=StringCodigo+'Q:'+copy(IBDocs.FieldByName('PRINTHASH').AsString,1,4)+'*'; //CODIGO HASH
   StringCodigo:=StringCodigo+'R:'+CodigoCertificado+'*'; // VOSSO NUMERO DE CERTIFICADO
   StringCodigo:=StringCodigo+'S:NU;'+FloatToStr(RoundNExtend(IBDOCS.FieldByName('TOTALGERAL').AsFloat,2));

   StringCodigo:=StringReplace(StringCodigo,',','.',[rfReplaceAll]);

   UpdateQRCode(StringCodigo,CodigoQR);
        
   //Isto vai-vos criar o BMP com o QRCode.
   //Depois só têm que grava-lo numa BD, e no CrystalReports, adicionar o campo dessa BD.
   +/- ISTO UPDATE DOCS SET IMAGEMQRCODE=FILESTREAM(C:\PASTATEMPORARIA\QRCODE\XXXXXXXXX.BMP
  
          
       
        
        
Procedure TDataModule1.UpdateQRCode(Texto,Codigo:String);
var
  QRCode: TDelphiZXingQRCode; //Gratuito é só ir buscar á net.
  Row, Column: Integer;
  Scale: Double;
  QRCodeBitmap: TBitmap;
  ImageQR:TImage;
begin

  try
    ImageQR:=TImage.Create(self);
    QRCodeBitmap := TBitmap.Create;
    QRCode := TDelphiZXingQRCode.Create;

    QRCode.Data := Texto;
    QRCode.Encoding := TQRCodeEncoding(0);
    QRCode.QuietZone := 1;
    QRCodeBitmap.SetSize(QRCode.Rows, QRCode.Columns);
    for Row := 0 to QRCode.Rows - 1 do
    begin
      for Column := 0 to QRCode.Columns - 1 do
      begin
        if (QRCode.IsBlack[Row, Column]) then
        begin
          QRCodeBitmap.Canvas.Pixels[Column, Row] := clBlack;
        end else
        begin
          QRCodeBitmap.Canvas.Pixels[Column, Row] := clWhite;
        end;
      end;
    end;

    ImageQR.Canvas.Brush.Color := clWhite;
    ImageQR.Canvas.FillRect(Rect(0, 0, ImageQR.Width, ImageQR.Height));
    if ((QRCodeBitmap.Width > 0) and (QRCodeBitmap.Height > 0)) then
    begin
      if (ImageQR.Width < ImageQR.Height) then
      begin
        Scale := ImageQR.Width / QRCodeBitmap.Width;
      end else
      begin
        Scale := ImageQR.Height / QRCodeBitmap.Height;
      end;
      ImageQR.Canvas.StretchDraw(Rect(0, 0, Trunc(Scale * QRCodeBitmap.Width), Trunc(Scale * QRCodeBitmap.Height)), QRCodeBitmap);
    end;
  ImageQR.Picture.SaveToFile('C:\PASTATEMPORARIA\QRCode\'+Codigo+'.bmp');
  QRCode.Free;
  QRCodeBitmap.Free;

  except

  end;

end;

Delphi

Se precisarem para outras linguagens de programação, mandem msg.

Este código tem diversas inconsistências / limitações, que nem sei por onde começar...

1) o ATCUD não é "obtido"... é construído através da concatenação do CÓDIGO de VALIDAÇÃO da série e o NUMERO documento (o que é "obtido" é o CÓDIGO de validação da série, mas isso é outra história)

2) TAXAS de IVA fixas? e quando mudarem?

3) Separação por ESPAÇOS FISCAIS?

4) Onde está o CAMPO L?

5) Gravação das imagens dos QRCode "NUMA" base de dados? E antes disso gravado localmente para um ficheiro como nome fixo? (espero que não existam processamentos de reports de forma simultânea)

Edited by marcolopes

The simplest explanation is usually the correct one

JAVA Utilities: https://github.com/marcolopes/dma

Share this post


Link to post
Share on other sites
paulofvoliveira

Alguém me pode informar onde é que posso encontrar a especificação do QrCode, no portal das finanças?

Quais os campos a integrar?

Share this post


Link to post
Share on other sites
paulofvoliveira

Até agora tenho usado a dll zen.barcode.core.dll para gerar QrCodes ultima versão de 2014, nunca tive problemas, agora que tenho que gerar QrCode para os documentos, encontrei um problema, não consigo gerar QrCodes com mais de 180 caracteres, alguém conhece uma alternativa a esta dll que eu possa usar em c#?

Edited by paulofvoliveira

Share this post


Link to post
Share on other sites
paulofvoliveira
12 minutes ago, paulofvoliveira said:

Até agora tenho usado a dll zen.barcode.core.dll para gerar QrCodes ultima versão de 2014, nunca tive problemas, agora que tenho que gerar QrCode para os documentos, encontrei um problema, não consigo gerar QrCodes com mais de 180 caracteres, alguém conhece uma alternativa a esta dll que eu possa usar em c#?

Esqueçam, é sexta-feira :)

A versão estava errada.

Share this post


Link to post
Share on other sites
bioshock

Bem eu vinha aqui questionar sobre uma coisa e já perdi a vontade 😅. Só hoje com as vossas mensagens é que fiquei a par do ATCUD e do QRCode.

Fui ler várias páginas para trás e acho que há malta confundida relativamente à obtenção do código ATCUD. Não é para fazer o pedido apenas uma vez por cada série, mas sim múltiplas vezes sempre que a série seja atribuída a um Tipo de documento (FT, FS, FR). 

https://dre.pt/application/conteudo/140210523 diz o seguinte:

Quote

Para a obtenção do código de validação das séries documentais (...) devem comunicar (...) por meio de processamento utilizado:

  1. O identificador da série do documento
  2. O tipo de documento
  3. O início da numeração sequencial a utilizar na série
  4. A data prevista de início da utilização da série para a qual é solicitado o código de validação

Isto significa então que podemos ter o seguinte exemplo:

  • Série A - atribuída a FT, FR, NC e RC
  • Série B - atribuída apenas a FR

Que por sua vez significa que terá de ser feito o pedido 5 vezes (A -> FT, FR, NC e RC) + (B -> FR).

Ou compreendi mal? 🤔

Edited by bioshock

Share this post


Link to post
Share on other sites
radikal
57 minutos atrás, bioshock disse:

Bem eu vinha aqui questionar sobre uma coisa e já perdi a vontade 😅. Só hoje com as vossas mensagens é que fiquei a par do ATCUD e do QRCode.

Fui ler várias páginas para trás e acho que há malta confundida relativamente à obtenção do código ATCUD. Não é para fazer o pedido apenas uma vez por cada série, mas sim múltiplas vezes sempre que a série seja atribuída a um Tipo de documento (FT, FS, FR). 

https://dre.pt/application/conteudo/140210523 diz o seguinte:

Isto significa então que podemos ter o seguinte exemplo:

  • Série A - atribuída a FT, FR, NC e RC
  • Série B - atribuída apenas a FR

Que por sua vez significa que terá de ser feito o pedido 5 vezes (A -> FT, FR, NC e RC) + (B -> FR).

Ou compreendi mal? 🤔

Compreendeste bem. O problema que se está a colocar aqui é se fizeres o exemplo:

  • Série A - atribuída ao documento com sigla FT1 que é do tipo de documento Fatura
  • Série A - atribuída ao documento com sigla FT2 que também é do tipo de documento Fatura

Não é o número de pedidos efetuado, sobre o qual estás correto e não existe nenhuma dúvida sobre isso.

Mas sim o facto de haver dúvidas na identificação ÚNICA da série aquando da comunicação, porque na portaria diz:

Citação

...sujeitos passivos devem comunicar, o identificador da série do documento e o tipo de documento.

Alguns utilizadores entenderam que o identificador da série é "A" e o tipo de documento é "FT". O que causa problemas, porque vão existir duas comunicações de série iguais com 'AFT'.
Outros utilizadores entenderam que o identificador da série é a conjugação da série e da sigla do documento, 'AFT1' e o tipo de documento 'FT'. O que funcionaria sem qualquer problema.
 

Mas a questão é que a Portaria não é clara...

Share this post


Link to post
Share on other sites
Gonçalo Silva

Para quem precisar de imprimir código QR em impressora genérica, têm aqui os comandos ESC POS da Epson (pode não funcionar em todas).

O único problema é que não deixa escolher a versão.

https://reference.epson-biz.com/modules/ref_escpos/index.php?content_id=140 (vejam também no tab da esquerda as funções 167,169,180,181,182)

Exemplo:

sCodigo_QR is string="A:123456789*B:999999990*C:PT*D:FS*E:N*F:20190812*G:FS CDVF/12345*H:CDF7T5HD-12345*I1:PT*I7:0.65*I8:0.15*N:0.15*O:0.80*Q:YhGV*R:9999*S:NU;0.80"

iConfigure("Generic / Text Only") // indicar impressora

nAvanco is int=0
nPrimeiroAvanco is int=0
nSegundoAvanco is int=0
IF nAvanco>255 THEN
	nResto is int
	nResto=modulo(nAvanco,256)
	IF nResto>0 THEN
		nPrimeiroAvanco=nResto
		nAvanco=nAvanco-nResto
	END
	nSegundoAvanco=nAvanco/256
ELSE
	nPrimeiroAvanco=nAvanco
END
IF nPrimeiroAvanco>0 OR nSegundoAvanco>0 THEN
	iEscape(EvaluateExpression("charact(27)+charact(36)+charact("+nPrimeiroAvanco+")+charact("+nSegundoAvanco+")"))
END


nTamanho is int=6
nPrimeiroValor is int=0
nSegundoValor is int=0
nTamanhoCodigo is int=(Length(sCodigo_QR)+3)
IF nTamanhoCodigo>255 THEN
	nResto is int
	nResto=modulo(nTamanhoCodigo,256)
	IF nResto>0 THEN
		nPrimeiroValor=nResto
		nTamanhoCodigo=nTamanhoCodigo-nResto
	END
	nSegundoValor=nTamanhoCodigo/256
ELSE
	nPrimeiroValor=nTamanhoCodigo
END
iEscape(EvaluateExpression("charact(29)+charact(40)+charact(107)+charact(4)+charact(0)+charact(49)+charact(65)+charact(50)+charact(0)"))  // Function 165
iEscape(EvaluateExpression("charact(29)+charact(40)+charact(107)+charact(3)+charact(0)+charact(49)+charact(67)+charact("+nTamanho+")")) // Function 167
iEscape(EvaluateExpression("charact(29)+charact(40)+charact(107)+charact(3)+charact(0)+charact(49)+charact(69)+charact(49)")) // Function 169
iEscape(EvaluateExpression("charact(29)+charact(40)+charact(107)+charact("+nPrimeiroValor+")+charact("+nSegundoValor+")+charact(49)+charact(80)+charact(48)")); // Function 180
iEscape(sCodigo_QR)
iEscape(EvaluateExpression("charact(29)+charact(40)+charact(107)+charact(3)+charact(0)+charact(49)+charact(81)+charact(48)")) //Function 181
iEscape(EvaluateExpression("charact(29)+charact(40)+charact(107)+charact(3)+charact(0)+charact(49)+charact(82)+charact(48)")) //Function 182

iEndPrinting()

 

Edited by Gonçalo Silva
  • Vote 2

Share this post


Link to post
Share on other sites
bioshock

Para a malta do PHP, segue um exemplo com recurso a https://github.com/chillerlan/php-qrcode.

Creio que não me escapou nada (🤔), mas saliento:

  1. Não é preenchido o campo "L" (Não sujeito / não tributável em IVA)
  2. Não é preenchido o campo "M" (Imposto do Selo)
  3. Não é preenchido o campo "S" (Outras informações)
  4. Creio que em termos de settings do QRCode parece estar tudo OK e em conformidade com os specs
  5. O QRCode é gerado com as dimensões mínimas (30x30), convêm imprimirem e ajustarem.

A library gera a imagem em formato Base64, sempre que o documento é visualizado é gerado o QRCode (e não é gravado em lado nenhum). Não notei qualquer diferença (delay).

use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
....
$options = new QROptions(
[
	'outputType'        => QRCode::OUTPUT_IMAGE_PNG,
	'eccLevel'          => QRCode::ECC_M,
	'dataModeOverride'  => QRCode::DATA_BYTE,
	'versionMin'        => 9,
]);

$values =
[
	'A' => '',
	'B' => '',
	'C' => '',
	'D' => '',
	'E' => '',
	'F' => '',
	'G' => '',
	'H' => '',
	'I1' => 'PT',
	'N' => '',
	'O' => '',
	'P' => '',
	'Q' => '',
	'R' => ''
];

$vatValues = '';

foreach ($vats as $vat)
{
	if ($vat['saft_region'] == 'PT')
	{
		if ($vat['type'] == 'Isenta')
			$vatsValues .= 'I2:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
		else if ($vat['type'] == 'Reduzida')
		{
			$vatsValues .= 'I3:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'I4:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Intermédia')
		{
			$vatsValues .= 'I5:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'I6:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Normal')
		{
			$vatsValues .= 'I7:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'I8:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
	}
	else if ($vat['saft_region'] == 'PT-AC')
	{
		$vatsValues .= 'J1:PT-AC*';

		if ($vat['type'] == 'Isenta')
			$vatsValues .= 'J2:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
		else if ($vat['type'] == 'Reduzida')
		{
			$vatsValues .= 'J3:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'J4:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Intermédia')
		{
			$vatsValues .= 'J5:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'J6:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Normal')
		{
			$vatsValues .= 'J7:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'J8:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
	}
	else if ($vat['saft_region'] == 'PT-MA')
	{
		$vatsValues .= 'K1:PT-MA*';

		if ($vat['type'] == 'Isenta')
			$vatsValues .= 'K2:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
		else if ($vat['type'] == 'Reduzida')
		{
			$vatsValues .= 'K3:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'K4:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Intermédia')
		{
			$vatsValues .= 'K5:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'K6:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Normal')
		{
			$vatsValues .= 'K7:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'K8:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
	}
}

$qrValue = sprintf
('
	A:%s*
	B:%s*
	C:%s*
	D:%s*
	E:%s*
	F:%s*
	G:%s*
	H:%s*
	I1:%s*
	%s
	N:%s*
	O:%s*
	P:%s*
	Q:%s*
	R:%s
',
	$values['A'],
	$values['B'],
	$values['C'],
	$values['D'],
	$values['E'],
	$values['F'],
	$values['G'],
	$values['H'],
	$values['I1'],
	$vatsValues,
	$values['N'],
	$values['O'],
	$values['P'],
	$values['Q'],
	$values['R'],
);

// Removes new lines & whitespaces
$qrValue = trim(preg_replace('/\s\s+/', '', $qrValue));

<img src="' . (new QRCode($options))->render($qrValue) . '" width="30" height="30"/>

 

  • Vote 1

Share this post


Link to post
Share on other sites
davdew05

Boa tarde a todos,

Estou aqui a andar as voltas com o tamanho do QrCode. Neste momento estou a usar a lib QRCoder. Está a gerar-me um código QR que consigo ler sem problema.

O problema reside no tamanho que código gerado cerca de 2,54 cm e estou a tentar que ele tenha os 3cm pedido nas especificações. só que depois de  fazer o resize do ficheiro resultante (bmp) no c#, já não consigo ler o código... Alguém tem alguma ideia de como fazer o resize em c# sem que o código perca legibilidade ?

Será que existe outra libraria para gerar correctamente o QrCode na dimensão correcta?

Obrigado.

Edited by davdew05

Share this post


Link to post
Share on other sites
marcolopes
3 hours ago, davdew05 said:

Boa tarde a todos,

Estou aqui a andar as voltas com o tamanho do QrCode. Neste momento estou a usar a lib QRCoder. Está a gerar-me um código QR que consigo ler sem problema.

O problema reside no tamanho que código gerado cerca de 2,54 cm e estou a tentar que ele tenha os 3cm pedido nas especificações. só que depois de  fazer o resize do ficheiro resultante (bmp) no c#, já não consigo ler o código... Alguém tem alguma ideia de como fazer o resize em c# sem que o código perca legibilidade ?

Será que existe outra libraria para gerar correctamente o QrCode na dimensão correcta?

Obrigado.

 

Efectuar o UPSCALE da IMAGEM gerada é uma PÉSSIMA ideia e não deve ser feito sob qualquer pretexto. A imagem deve ser gerada COM AS ESPECIFICAÇÕES da AT, através de uma LIB que assim o permita (nem todas têm controle total de parametrização!!!). Nesse caso, a imagem terá sempre os "3cm" aproximados (e NUNCA MENOS) que a AT exige.

A AT não indica MÍNIMO de "3cm" ao acaso! Indica 3cm porque é a o tamanho mínimo de um QRCODE gerado com Versão: v=9 (valor mínimo);

Portanto... primeiro encontre uma LIB que faça as coisas como deve ser. Na pior das hipóteses depois redimensiona imagens MAIORES do que 3cm para os 3cm (o QRcode pode ser maior do que 3CM, quando criado através das SPECS da AT, nos casos mais complexos - ver exemplos no documento técnico)


The simplest explanation is usually the correct one

JAVA Utilities: https://github.com/marcolopes/dma

Share this post


Link to post
Share on other sites
Jose Guerreiro
Em 11/09/2020 às 16:13, davdew05 disse:

Boa tarde a todos,

Estou aqui a andar as voltas com o tamanho do QrCode. Neste momento estou a usar a lib QRCoder. Está a gerar-me um código QR que consigo ler sem problema.

O problema reside no tamanho que código gerado cerca de 2,54 cm e estou a tentar que ele tenha os 3cm pedido nas especificações. só que depois de  fazer o resize do ficheiro resultante (bmp) no c#, já não consigo ler o código... Alguém tem alguma ideia de como fazer o resize em c# sem que o código perca legibilidade ?

Será que existe outra libraria para gerar correctamente o QrCode na dimensão correcta?

Obrigado.

Ola

Estou a usar essa biblioteca e nao tive problemas, apenas faço um check no final e verifico se a versão é superior ou igual a 9.

Se for, deixo estar mas se for inferior crio novamente o qrcode obrigando a que seja criada na versao 9 com parametro requestedVersion:9,

Share this post


Link to post
Share on other sites
marcolopes
19 horas atrás, Jose Guerreiro disse:

Ola

Estou a usar essa biblioteca e nao tive problemas, apenas faço um check no final e verifico se a versão é superior ou igual a 9.

Se for, deixo estar mas se for inferior crio novamente o qrcode obrigando a que seja criada na versao 9 com parametro requestedVersion:9,

As regras ditam que a versão TEM de ser a 9 (possa ou não ser codificado em versão anterior). Ou seja, independentemente do QRCODE poder ser codificado numa versão inferior, a AT quer que seja codificado na versão 9 no mínimo, que tal como já tinha dito antes, vai gerar um QRCODE com um mínimo de 3cm.


The simplest explanation is usually the correct one

JAVA Utilities: https://github.com/marcolopes/dma

Share this post


Link to post
Share on other sites
João Januário
11 horas atrás, marcolopes disse:

As regras ditam que a versão TEM de ser a 9 (possa ou não ser codificado em versão anterior). Ou seja, independentemente do QRCODE poder ser codificado numa versão inferior, a AT quer que seja codificado na versão 9 no mínimo, que tal como já tinha dito antes, vai gerar um QRCODE com um mínimo de 3cm.

Boas,

A versão, normalmente, é definida em função do tamanho da mensagem. Para garantir a versão 9 no mínimo forço que a mensagem tenho no mínimo 180 caracteres através do campo S.

Por exemplo, coloco o nosso nome comercial da empresa, o id do cliente, etc.

  • Vote 1

Share this post


Link to post
Share on other sites
marcolopes
1 hour ago, João Januário said:

Boas,

A versão, *****************normalmente***************, é definida em função do tamanho da mensagem. Para garantir a versão 9 no mínimo forço que a mensagem tenho no mínimo 180 caracteres através do campo S.

Por exemplo, coloco o nosso nome comercial da empresa, o id do cliente, etc.

Não é isso que AT pede... o que descreves é um WORKAROUND (que pode resultar num QRCODE muito maior sem necessidade, apenas para cumprir o requisito VERSION 9)

Uma "boa" LIB QRCODE deverá permitir a parametrização do MinVersion, independentemente do tamanho da mensagem...


The simplest explanation is usually the correct one

JAVA Utilities: https://github.com/marcolopes/dma

Share this post


Link to post
Share on other sites
jmta_92
Em 09/09/2020 às 03:07, bioshock disse:

Para a malta do PHP, segue um exemplo com recurso a https://github.com/chillerlan/php-qrcode.

Creio que não me escapou nada (🤔), mas saliento:

  1. Não é preenchido o campo "L" (Não sujeito / não tributável em IVA)
  2. Não é preenchido o campo "M" (Imposto do Selo)
  3. Não é preenchido o campo "S" (Outras informações)
  4. Creio que em termos de settings do QRCode parece estar tudo OK e em conformidade com os specs
  5. O QRCode é gerado com as dimensões mínimas (30x30), convêm imprimirem e ajustarem.

A library gera a imagem em formato Base64, sempre que o documento é visualizado é gerado o QRCode (e não é gravado em lado nenhum). Não notei qualquer diferença (delay).


use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
....
$options = new QROptions(
[
	'outputType'        => QRCode::OUTPUT_IMAGE_PNG,
	'eccLevel'          => QRCode::ECC_M,
	'dataModeOverride'  => QRCode::DATA_BYTE,
	'versionMin'        => 9,
]);

$values =
[
	'A' => '',
	'B' => '',
	'C' => '',
	'D' => '',
	'E' => '',
	'F' => '',
	'G' => '',
	'H' => '',
	'I1' => 'PT',
	'N' => '',
	'O' => '',
	'P' => '',
	'Q' => '',
	'R' => ''
];

$vatValues = '';

foreach ($vats as $vat)
{
	if ($vat['saft_region'] == 'PT')
	{
		if ($vat['type'] == 'Isenta')
			$vatsValues .= 'I2:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
		else if ($vat['type'] == 'Reduzida')
		{
			$vatsValues .= 'I3:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'I4:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Intermédia')
		{
			$vatsValues .= 'I5:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'I6:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Normal')
		{
			$vatsValues .= 'I7:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'I8:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
	}
	else if ($vat['saft_region'] == 'PT-AC')
	{
		$vatsValues .= 'J1:PT-AC*';

		if ($vat['type'] == 'Isenta')
			$vatsValues .= 'J2:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
		else if ($vat['type'] == 'Reduzida')
		{
			$vatsValues .= 'J3:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'J4:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Intermédia')
		{
			$vatsValues .= 'J5:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'J6:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Normal')
		{
			$vatsValues .= 'J7:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'J8:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
	}
	else if ($vat['saft_region'] == 'PT-MA')
	{
		$vatsValues .= 'K1:PT-MA*';

		if ($vat['type'] == 'Isenta')
			$vatsValues .= 'K2:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
		else if ($vat['type'] == 'Reduzida')
		{
			$vatsValues .= 'K3:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'K4:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Intermédia')
		{
			$vatsValues .= 'K5:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'K6:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
		else if ($vat['type'] == 'Normal')
		{
			$vatsValues .= 'K7:' . number_format($vat['total_base_vat'], 2, '.', '') . '*';
			$vatsValues .= 'K8:' . number_format($vat['total_vat'], 2, '.', '') . '*';
		}
	}
}

$qrValue = sprintf
('
	A:%s*
	B:%s*
	C:%s*
	D:%s*
	E:%s*
	F:%s*
	G:%s*
	H:%s*
	I1:%s*
	%s
	N:%s*
	O:%s*
	P:%s*
	Q:%s*
	R:%s
',
	$values['A'],
	$values['B'],
	$values['C'],
	$values['D'],
	$values['E'],
	$values['F'],
	$values['G'],
	$values['H'],
	$values['I1'],
	$vatsValues,
	$values['N'],
	$values['O'],
	$values['P'],
	$values['Q'],
	$values['R'],
);

// Removes new lines & whitespaces
$qrValue = trim(preg_replace('/\s\s+/', '', $qrValue));

<img src="' . (new QRCode($options))->render($qrValue) . '" width="30" height="30"/>

 

O teu tamanho de imagem está errado, é 30 mm que eles querem, que corresponde mais ou menos a 120px.

Eu tambem estou a utilizar isso mas com estas definicoes

 

'version'    => QRCode::VERSION_AUTO,
        'outputType' => QRCode::OUTPUT_IMAGE_PNG,
        'eccLevel'   => QRCode::ECC_M,
      'dataModeOverride'   => QRCode::DATA_BYTE,
      "versionMin" => 9,
      "quietzoneSize" => 2,
      "scale" => 2,
      "addQuietzone"=>true

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.