segunda-feira, 10 de janeiro de 2011

Conhecendo os Fundamentos do ICMS ST (Substituição Tributária)


Olá Amigos! Resolvi escrever sobre um assunto complicado para os programadores, principalmente os iniciantes! Lembro-me que quando comecei, logo de cara tinha que fazer um cálculo de Substituição Tributária em um software de emissão de notas fiscais
Os problemas apareceram quando resolvi perguntar ao contador do cliente. Aí sim descobri que quase ninguém domina esse assunto! Felizmente isso são águas passadas. Hoje resolvi escrever esse artigo para ajudar aqueles que estão passando por algo parecido.

Vou utilizar algumas nomenclaturas comuns para quem já está trabalhando com NFe (Nota Fiscal Eletrônica).

Base de cálculo (BC)

A Lei Complementar nº 87/96 em seu artigo 8º, ao tratar do regime de sujeição passiva por substituição, determina que a base de cálculo será o valor correspondente ao preço de venda a consumidor acrescido do valor do frete, IPI e demais despesas debitadas ao estabelecimento destinatário, bem como a parcela resultante da aplicação (sobre esse total) do percentual de valor agregado (margem de lucro). Esse percentual é estabelecido em cada caso de acordo com as peculiaridades de cada mercadoria.

BC = (Valor mercadoria + frete + IPI + outras despesas) x Margem de lucro

Margem de valor agregado

A margem de valor agregado será determinada com base em preços usualmente praticados no mercado, obtidos por levantamento, ainda que por amostragem ou através de informações e outros elementos fornecidos por entidades representativas dos setores, adotando-se a média ponderada dos preços coletados. A mercadoria submetida ao regime de substituição tributária em operação interestadual terá a margem de valor agregado estabelecida em Convênio ou Protocolo.

Esse campo o escritório de contabilidade deverá fornecer ao cliente. 

Forma para realizar o cálculo

Devemos pensar em dois pontos básicos: trabalhar com o ICMS da OPERAÇÃO PRÓPRIA e o ICMS DAS OPERAÇÕES SUBSEQUENTES. Assim Temos:

O ICMS da operação própria - realizará uma seqüência de operações matemáticas para chegar a um valor que será utilizado no próximo item.
O ICMS das operações subsequentes - realizará os cálculos necessários para chegar ao valor do ICMS – ST.

Vou exemplificar. Pense em uma operação realizada por um fabricante de brinquedos estabelecido no estado de São Paulo, vendendo à um cliente do mesmo estado. A venda é de R$ 1.000,00 e com IPI calculado a uma alíquota de 5%, teremos:

Nosso 1º passo será realizar o cálculo do ICMS de Operação Própria:
ICMS da operação própria = R$ 1.000,00 x 18% (SP para SP) = R$ 180,00*
* Devemos guardar esse resultado em uma variável para uso futuro

2º Passo, calcular a Base de Cálculo ST:
Base cálculo da ST = R$ 1.000,00 + R$ 50,00 (5% de IPI) + 52% (margem de valor agregado) = R$ 1.596,00

3º Passo, encontrar o valor do ICMS ST
R$ 1.596,00 x 18% (SP para SP) = R$ 287,28

O valor do imposto substituição será a diferença entre o calculado no 1º passo e o do 3º passo. Você se lembra daquela variável guardada no 1º passo? Pois bem, agora usaremos ela para realizar o cálculo da diferença.

R$ 287,28 – R$ 180,00 = R$ 107,28

Pronto, o valor do ICMS – ST é R$ 107,28

sexta-feira, 7 de janeiro de 2011

Conexão com Balanças - Entendo os Códigos Emitidos pela Balança


Como nesse artigo o objetivo é explicar como funcionam os códigos emitidos pela balanças eletrônicas, vou postar uma dúvida que eu peguei em um fórum de automação comercial que participo.

Pergunta:
"Gostaria de entender os Códigos Emitidos pelas Balanças Computadoras (Platina, Prix, etc)
Tipo todas elas emitem o Código com inicio 2 então no meu sistema já sei se o recebido tiver 2 no inicio isso indicado que é algo na balança. até ai tudo bem...
Porem não consegui entender os outros dígitos que estão junto...
Se não estou enganado o ultimo seria digito verificador outros seriam o Código do Produto e outros seriam o total do produto.
o Meu problema esta ai abstrair o restante...
Também sei que tem tamanhos de códigos diferentes alguns com 4 dígitos outros com 6 dígitos mas não consegui ligar muito as coisas não"
Resposta:
Pode haver algumas variações na etiqueta impressa. Essas variações podem ser:
A - 2 C C C C 0 T T T T T T DV
B - 2 C C C C 0 0 P P P P P DV
C - 2 C C C C C T T T T T T DV
D - 2 C C C C C 0 P P P P P DV
E - 2 C C C C C C P P P P P DV
Onde:
2 - Digito 2 sempre
C - Código do Produto
0 - Espaço, não utilizado
T - Valor Total a Pagar
P - Peso ou Quantidade
DV- Digito Verificador EAN-13
Obs: Estes modelos podem ser configurados no programa que acompanha as balanças.
Para que o seu sistema saiba qual o tipo do código de barras que será impresso na balança, ou seja, para que você saiba como tratar no seu sistema, temos que criar uma tela de configuração para armazenar as posições da etiqueta.
Veja na figura abaixo um exemplo que serve para configurarmos corretamente as variações que podem acontecer nas etiquetas.
Com isso tudo configurado, quando o caixa (funcionário), por exemplo, passar um código de barras de um produto no PDV, o mesmo vai saber em qual posição está o peso do produto, código, preço e etc.
O que poderíamos fazer é verificar se o código de barras começa com o digito 2. Se for, já sabemos que trata-se de um código vindo da balança. Agora só nos resta pegarmos as informações que a etiqueta traz.

Ex:
Codigo de balança -> 2 0 1 0 1 0 0 0 0 0   9  0  7
Posições          -> 1 2 3 4 5 6 7 8 9 10 11 12 13
1 - prefixo balança;
2 a 5 ou 7 - código produto;
8 a 12 - preço na etiqueta


Conclusão

Bom, esse artigo foi para explicar como funcionam os códigos de barras impressos pelas balanças eletrônicas. Nos próximos artigos vou apresentar o componente acbrBal, que é para Leitura de informações de Balanças eletrônicas (Filizola, Toledo, Urano, etc) e que faz parte do projeto open source Acbr.
Para realizar a comunicação serial o projeto acbr usa a classe SynaSer
Projeto: Ararat Synapse (http://www.ararat.cz/synapse/)

Sérgio de Castro Guedes 
Contato: sergio_rj45@yahoo.com.br 
Programador com experiência em banco de dados MSSQL, Firebird e 
Oracle. Moderador de Conteúdo do Fórum, Editor Técnico e Colunista do Portal Active Delphi.
Administrador do Projeto Open Source Sia Consult (Sistema Comercial ERP).
Membro do projeto Acbr (Automação Comercial Brasil) e Desenvolvedor da VAP Informática, umas das maiores empresas de software de Manaus AM.

Referencias
- André Ferreira de Moraes - Membro do Projeto Acbr (http://acbr.sourceforge.net)
- Fórum do Projeto ACbr (http://www.forumweb.com.br/foruns/index.php?showforum=465)

Protegendo seu sistema contra cópias ilegais


Apresentarei aqui uma técnica que utiliza o serial lógico de uma unidade de disco do computador, que apesar de poder ser modificada após uma formatação, por exemplo, já é um bom começo!
Bom, vamos lá. Tendo em vista a preocupação dos colegas com pirataria e cópias ilegais, desenvolvi um código que verifica o número de série de um determinado dispositivo conectado ao computador e faz uma comparação.
Abaixo há uma função que pega o número de serie do CD que esta na respectiva unidade. O sistema só irá rodar se aquele determinado CD estiver no drive. Tanto faz se vc colocar o CD na unidade D, E ou F, a função irá verificar cada unidade a procura do número de serie correto e caso não o ache retorna false. Apesar da idéia ser criada com base em um CD, pode ser adaptada para o uso com HD's.
Para testar o código, você precisará acrescenta rum label e um button em seu form. Em seguida, implementar a funçãotbVolSerial, a procedure VerificalSerialH e o evento onClick do botão, conforme abaixo:
// função que pega o numero serial do dispositivo 
function Tform2.tbVolSerial(const Drive: Char; Path: PChar): Cardinal; 
var 
  MaxCompLen, FileSysFlag, PrevErrorMode: Cardinal;
begin
  if Path = nil then 
    Path := PChar(Drive + ':');
  PrevErrorMode := SetErrorMode(SEM_FAILCRITICALERRORS);
  try
    if not GetVolumeInformation(PChar(Path), nil, 0, @Result, MaxCompLen,
      FileSysFlag, nil, 0) then 
      Result := 0;
  finally 
    SetErrorMode(PrevErrorMode);
  end; 
end;
 
//Esta função faz a varredura em todos os dispositivos a
//procura do serial valido. retorna true ou false. 
procedure Tform2.VerificaSerialH(Driver: char); 
const 
  drive: array[0..25] of char = (
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
    'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
    'r', 's', 't', 'u', 'w', 'v', 'x', 'y', 'z'); 
var 
  j: integer;
  ini: TIniFile;
  SerialHardware, AdquireSerial: string;
  erro: Boolean;
begin
  if not FileExists('.serial.ini') then
  begin 
    AdquireSerial := IntToStr(tbVolSerial(Driver, nil));
    ini := TIniFile.Create('.serial.ini');
    ini.WriteString('serial', 'Hardware', AdquireSerial);
    ini.Free;
  end
  else
  begin 
    ini := TIniFile.Create('.serial.ini');
    SerialHardware := ini.ReadString('serial', 'Hardware', '');
    ini.free;
  end;
 
  for j := 0 to 25 do
  begin
    if (IntToStr(tbVolSerial(Drive[j], nil))) = SerialHardware then
    begin 
      Erro := false;
      Label1.Caption := 'Original';
      break;
    end
    else 
      Erro := true;
  end;
 
  if Erro then    Label1.Caption := 'Pirata' 
end;
 
procedure Tform2.Button1Click(Sender: TObject); 
begin 
  Label1.Caption := 'Este sistema é...';
  Label1.Repaint;
  VerificaSerialH('G'); 
end;

quinta-feira, 6 de janeiro de 2011

Convertendo a 1ª letra de um edit para maiúsculas

Esta dica é útil para ser utilizada no cadastramanto de nomes.

Se o operador do sistema esquecer de colocar a primeira letra de um nome em minúscula, automaticamente a letra ficará maiúscula.

Para que isto ocorra, coloque um objeto edit no formulário e no seu evento OnChange insira o seguinte código:

var
Variavel : Integer;

begin
With Edit1 do
if Text <> '' then
begin
OnChange := NIL;
Variavel := SelStart;
Text := UpperCase(Copy(Text,1,1))+LowerCase(Copy(Text,2,Length(Text)));
SelStart := Variavel;
OnChange := Edit1Change;
end;
end;

Como gerar senhas aleatórias

Segue abaixo rotina para gerar senhas aleatórias.

procedure TForm1.Button1Click(Sender: TObject);
var
i:integer;
const
str='1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ';
max=6;
begin
Edit1.Text:='';
for i:=1 to max do
begin
   Edit1.Text:=Edit1.Text+str[random(length(str))+1];
end;
end.

Checagem de CEP por Estado

Verifica se um CEP é valido ou não, passando o Estado e o Cep como parâmetro.



function ChecaCEP(cCep:String ; cEstado:String): Boolean;
var
cCEP1 : Integer;
begin
cCep := copy(cCep,1,5) + copy(cCep,7,3);
cCEP1 := StrToInt(copy(cCep,1,3));
if Length(trim(cCep)) > 0 then
   begin
   if (StrToInt(cCep) <= 1000000.0) then
       begin
       MessageDlg('CEP tem que ser maior que [01000-000]',mtError,[mbOk],0);
       Result := False
       end
   else
   begin
     if Length(trim(copy(cCep,6,3))) < 3 then Result := False else
     if (cEstado = 'SP') and (cCEP1 >= 10 ) and (cCEP1 <= 199) then Result := True else
     if (cEstado = 'RJ') and (cCEP1 >= 200) and (cCEP1 <= 289) then Result := True else
     if (cEstado = 'ES') and (cCEP1 >= 290) and (cCEP1 <= 299) then Result := True else
     if (cEstado = 'MG') and (cCEP1 >= 300) and (cCEP1 <= 399) then Result := True else
     if (cEstado = 'BA') and (cCEP1 >= 400) and (cCEP1 <= 489) then Result := True else
     if (cEstado = 'SE') and (cCEP1 >= 490) and (cCEP1 <= 499) then Result := True else
     if (cEstado = 'PE') and (cCEP1 >= 500) and (cCEP1 <= 569) then Result := True else
     if (cEstado = 'AL') and (cCEP1 >= 570) and (cCEP1 <= 579) then Result := True else
     if (cEstado = 'PB') and (cCEP1 >= 580) and (cCEP1 <= 589) then Result := True else
     if (cEstado = 'RN') and (cCEP1 >= 590) and (cCEP1 <= 599) then Result := True else
     if (cEstado = 'CE') and (cCEP1 >= 600) and (cCEP1 <= 639) then Result := True else
     if (cEstado = 'PI') and (cCEP1 >= 640) and (cCEP1 <= 649) then Result := True else
     if (cEstado = 'MA') and (cCEP1 >= 650) and (cCEP1 <= 659) then Result := True else
     if (cEstado = 'PA') and (cCEP1 >= 660) and (cCEP1 <= 688) then Result := True else
     if (cEstado = 'AM') and ((cCEP1 >= 690) and (cCEP1 <= 692) or (cCEP1 >= 694) and 
(cCEP1 <= 698)) then Result := True else
     if (cEstado = 'AP') and (cCEP1 = 689) then Result := True else
     if (cEstado = 'RR') and (cCEP1 = 693) then Result := True else
     if (cEstado = 'AC') and (cCEP1 = 699) then Result := True else
     if ((cEstado = 'DF') or (cEstado = 'GO')) and (cCEP1 >= 700)and(cCEP1 <= 769)then 
Result := True else
     if (cEstado = 'TO') and (cCEP1 >= 770) and (cCEP1 <= 779) then Result := True else
     if (cEstado = 'MT') and (cCEP1 >= 780) and (cCEP1 <= 788) then Result := True else
     if (cEstado = 'MS') and (cCEP1 >= 790) and (cCEP1 <= 799) then Result := True else
     if (cEstado = 'RO') and (cCEP1 = 789) then Result := True else
     if (cEstado = 'PR') and (cCEP1 >= 800) and (cCEP1 <= 879) then Result := True else
     if (cEstado = 'SC') and (cCEP1 >= 880) and (cCEP1 <= 899) then Result := True else
     if (cEstado = 'RS') and (cCEP1 >= 900) and (cCEP1 <= 999) then Result := True else
Result := False
   end;
 end
else
   begin
   Result := True;
   end
end;

Controle sobre a digitação

Quando alguém esta  digitando  algum valor que posteriormente será utilizado para calculo alguns cuidados são necessários, esse procedimento  ValidaKey  deve ser ligado no OnChange do TDBEdit para checar qual foi a tecla digitada.

procedure ValidaKey(Const Sender:TObject; var key: char);
begin
       if not(key in ['0'..'9','.',',',#8,#13]) then key := #0;
       if key in [',','.'] then key := DecimalSeparator;
       if key = DecimalSeparator then
                  if pos(key,TEdit(Sender).Text) <> 0 then key := #0;
end;


       if not(key in ['0'..'9','.',',',#8,#13]) then key := #0;

Se algum numero, ponto, virgula, BackSpace ou Enter for digitado então pode passar normalmente, caso contrario a tecla pressionada é ignorada.

       if key in [',','.'] then key := DecimalSeparator;

Se ponto ou virgula, assume como separador decimal.

        if key = DecimalSeparator then
          if pos(key,TEdit(Sender).Text) <> 0 then key := #0;

O separador decimal so pode ser digitado uma unica vez, na tentativa de uma segunda digitação ignora-se o símbolo.

Observem que o mais importante aqui é o conceito utilizado, o fato de se interceptar os caracteres digitados pelo usuário e poder filtrar esses caracteres para evitar uma entrada de  dados inconsistente. O exemplo de numero e símbolos não é conclusivo, uma vez que o mesmo efeito poderia ter sido obtido com a aplicação de uma mascara.

Curso de Delphi: 7.Consultas SQL