desnecessário. Se for uma cotação de 10 itens, até que vai, mas se forem aquelas megacotações de 1000 a 2000 itens?
Eu fiz uma rotina que dispensa o método usado pelo RM e o faz diretamente via uma customização direta aonde geramos uma planilha para cada fornecedor, enviamos por E-mail para ele preencher e depois ficamos apenas por conta de importarmos a supra-citada pra dentro do RM. Processo bem mais agil que pode
automatizar cotações de infinitas quantidades de intens.
Os passos a serem adotados serão: Montar a lista dos intens cotados, montar a lista dos fornecesdores destino, montar a planilha a ser preenchida pelo fornecedor e depois importar a planilha e gravar na tabela de cotação.
O primeiro passo a ser adotado é montarmos uma sentença SQL que irá buscar os dados da cotação em questão. Para isto a tabela alvo do Nucleus vai ser a TCITMORCAMENTO aonde estão os itens a serem orçados e seus respectivos fornecedores vinculados.
Para este processo funcionar, seus fornecedores deverão estar todos cadastrados na tabela FCFO e com seus respectivos E-Mails vigentes.
Na Unit do Plugin, iremos adicionar os dois itens do menu da customização:
MenuInfo : array [0..MAXMENU-1] of TPlugMenuInfo =
(
(Caption: '&Enviar orçamentos para fornecedores'; Hint: 'Envia itens orçados
na cotação para os fornecedores cadastrados';
Tag: 999992; Event: nil; Process: false; Bitmap: ''),
(Caption: '&Importar orçamentos cotados'; Hint: 'Importa itens orçados pelos
fornecedores cadastrados para a cotação';
Tag: 999992; Event: nil; Process: false; Bitmap: ''),
(Caption: '-'; Hint: '';
);
No Initialize do plugin, iremos declará-las:
i := 0; MenuInfo[i].Event := mnuEnviaCotacaoOrcadaClick;
inc(i); MenuInfo[i].Event := mnuImportaCotacaoOrcadaClick;
inc(i);
Feito isto, você deverá montar uma tela aonde colocaremos dois
DBLookUpComboBox, um progressbar e dois buttons.
Um DBLookup irá receber a lista de cotações em aberto (Todas diferentes do
flag 7 (Cancelado)). A sentença a ser usada na query, deve ser algo parecido
com:
select I.CODCOMPRADOR, I.CODCOTACAO, I.DATLIMRESPTA, P.EMAIL, P.NOME
from TCCOTACAO as I, PPESSOA as P where I.CODCOMPRADOR = P.CODIGO and I.STSCOTACAO
= 1 and I.CODCOLIGADA = 2
Um dos Buttons é para fechar o form o outro irémos inserir as rotinas de montagem da planilha e envio. Nesta rotina, você precisará primeiro criar uma query que busque a relação de fornecedores da cotação e seus
respectívos itens a serem orçados. Lembrando pela ordem que o registro mestre será o fornecedor, e os filhos serão seus itens. Sendo assim, a sentença SQL para listar os fornecedores será a seguinte:
select DISTINCT F.CODCFO, F.NOME, F.EMAIL from TCITMORCAMENTO AS C,
FCFO AS F WHERE F.CODCFO = C.CODCFO AND F.CODCOLIGADA = :CODCOLIGADA AND C.CODCOTACAO
= :CODCOTACAO and F.EMAIL is not null
A lista dos itens destinadas a cada fornecedor, será constituída da seguinte
sentença SQL:
select C.CODCFO,P.CODIGOPRD, P.DESCRICAO, C.CODUND, SUM(I.QUANTIDADE)
QUANTIDADE from TCITMORCAMENTO as C left outer join TPRD as P on P.IDPRD = C.IDPRD,
TMOV as M, TITMMOV as I Where C.CODCFO = :CODCFO AND C.CODCOTACAO = :CODCOTACAO
AND M.IDMOV = C.IDMOV
AND I.IDMOV = M.IDMOV
AND I.IDPRD = C.IDPRD
AND M.STATUS <> 'C'
GROUP BY C.CODCFO,P.CODIGOPRD, P.DESCRICAO, C.CODUND
ORDER BY P.DESCRICAO
Os códigos de status das cotações são:
1 - Composição (Ainda não foi enviado o orçamento para o fornecedor)
2 - Aguardando Resposta dos fornecedores
3 - Ordem de compra Parcialmente gerada
4 - Não usado
5 - Em negociação
6 - Composição
7 - Cancelada
Bom. De posse então dos dados para prepararmos a planilha, você monta uma rotina de escrita em uma planilha excel e mande salvá-la em disco (A Web tá cheia de exemplos de como gravar dados em uma planilha excel). Então criariamos uma rotina que iriia lendo fornecedor por fornecedor, gera a planilha, envia para o destinatrio e, em seguida, apaga a planilha do disco
FOR i := 0 TO DadosFornecedor.Count - 1 DO
BEGIN
DadosFornecedor.items[i];
IF CreateCotacaoSheet(DadosFornecedor,i,Cotacao,DadosItemCotacao, sNameFile) THEN
BEGIN
IF DadosFornecedor.Items[i].EMailUsuario <> '' THEN
BEGIN
SendSMTPMail(PChar(Cotacao.Operador), PChar(Cotacao.EMailOperador), PChar(DadosFornecedor.Items[i].Nome), PChar(DadosFornecedor.Items[i].EMailUsuario),NIL,NIL,pChar('PEDIDO DE ORÇAMENTO Nº ' + Cotacao.CodCotacao + '-' + DadosFornecedor.Items[i].CodFornecedor),PChar(BodyMessage),[PChar(sNameFile)]);
DeleteFile(sNameFile);
END
ELSE
MessageDlg('Fornecedor ' + DadosFornecedor.Items[i].Nome + ' Não possui conta de E-Mail. A planilha será salva no disco!', mtWarning, [mbOk], 0);
END;
END;
MessageDlg('Processo de envio de orçamentos para os fornecedores processado com sucesso!', mtInformation, [mbOk], 0);
SetStatusCotacao(Cotacao.CodCotacao,2);
FreeAndNil(DadosFornecedor);
FreeAndNil(DadosItemCotacao);
//
END;
Note você que esta rotina somente apaga a planilha do disco, se o fornecedor
tiver E-mail. Caso não o tenha ele não apaga pois você pode pegar esta
planilha, imprimir e enviar ela por Fax pro cara.
Depois disto, você deverá mudar o status da cotação para "Aguardando resposta
dos fornecedores". Para isto, você cria uma rotina usando a seguinte sentença
SQL:
UPDATE TCCOTACAO SET STSCOTACAO = :STSCOTACAO WHERE CODCOTACAO = :CODCOTACAO
Para mudar o status para "Aguardando resposta dos fornecedores" use o código 2
no parametro CODCOTACAO
Importando os dados.
Quanto a questão da importação desta planilha, é mais simples ainda.
Geralmente eu salvo a planilha como Texto separado por tabulações e uso a
rotina abaixo para importar os dados para o RM:
procedure TfImpOrcamento.btnImportClick(Sender: TObject);
var
fi : textfile;
BackupName, line, CodCotacao, CodPrd, ValFrete, CodCfo, ValPrazoEntrega,ValPrazoValidade,
NumeroParcelas, ValLimiteResposta, CondicoesPagto, CodCpg, FreteCIFouFob,
Preco, Unitario, IPI, Desconto, ValorDescontado, ValorFinal,
Quantidade, Total, ValTrb, ValCotacao, ValICMS: String;
LimiteResposta, DataEmissao: TDateTime;
Frete, ICMS: Extended;
i, k: integer;
done, decimal : boolean;
iCount, PrazoEntrega, PrazoValidade: integer;
Orcamento : TOrcamento;
DadosOrcamento : TDadosOrcamento;
Item : TItem;
DadosItem : TDadosItem;
begin
if eFileName.Text = '' then
begin
MessageDlg('Você deverá selecionar um arquivo para proceder com a importação', mtError, [mbOk], 0);
eFileName.Setfocus;
Exit;
end
else if IsFileInUse(eFileName.text) then
begin
MessageDlg('Erro ao abrir o arquivo ' + ExtractFileName(eFileName.text)
+ '. O arquivo está sendo usadado por outro processo', mtError, [mbOk], 0);
eFileName.Setfocus;
exit;
end;
try
//
assignfile(fi, eFileName.text);
{$i-} Reset(fi); {$i+}
//
pBar.Max := 40;
//
if ioresult <> 0 then
begin
MessageDlg('Erro ao abrir arquivo ' +
eFileName.text, mtInformation, [mbOk], 0);
exit;
end;
done := False;
//
if not eof(fi) then
begin
//
DadosOrcamento := TDadosOrcamento.Create;
DadosItem := TDadosItem.Create;
//
repeat
if not eof(fi) then
readln(fi, line);
until eof(fi) or (copy(line, 1,10)=
'Fornecedor');
pBar.StepIt;
line := copy(line, 12, length(line));
line := RetiraCaracteres(#$09, line);
CodCfo := copy(Line,1,6);
//
repeat
if not eof(fi) then
readln(fi, line);
until eof(fi) or (copy(line, 1,19)= 'Pedido de Orçamento');
pBar.StepIt;
line := copy(line, 21, length(line));
CodCotacao := LTrim(RetiraPalavra(line, #$09));
//
repeat
if not eof(fi) then
readln(fi, line);
until eof(fi) or (copy(line, 1, 14)= 'Data Lim. Resp');
pBar.StepIt;
line := copy(line, 16, length(line));
ValLimiteResposta := RetiraCaracteres(#$09, line);
//
repeat
if not eof(fi) then
readln(fi, line);
until eof(fi) or (copy(line, 1, 22)= 'Condições de Pagamento');
pBar.StepIt;
line := copy(line, 24, length(line));
CondicoesPagto := RetiraPalavra(line, #$09);
//
repeat
if not eof(fi) then
readln(fi, line);
until eof(fi) or (copy(line, 1, 18)= 'Número de parcelas');
pBar.StepIt;
line := copy(line, 20, length(line));
line := RetiraPalavra(line, #$09);
NumeroParcelas := SomenteDigitos(line,2);
//
repeat
if not eof(fi) then
readln(fi, line);
until eof(fi) or (copy(line, 1, 16)= 'Prazo de entrega');
pBar.StepIt;
line := copy(line, 18, length(line));
ValPrazoEntrega := RetiraPalavra(line, #$09);
ValPrazoEntrega := SomenteDigitos(ValPrazoEntrega,2);
//
repeat
if not eof(fi) then
readln(fi, line);
until eof(fi) or (copy(line, 1, 21)= 'Alíquota ICMS Inclusa');
pBar.StepIt;
line := copy(line, 23, length(line));
line := RetiraPalavra(line, #$09);
ValICMS := SomenteDigitos(line,2);
//
repeat
if not eof(fi) then
readln(fi, line);
until eof(fi) or (copy(line, 1, 21)= 'Validade do Orçamento');
pBar.StepIt;
line := copy(line, 23, length(line));
ValPrazoValidade := RetiraPalavra(line, #$09);
ValPrazoValidade := SomenteDigitos(ValPrazoValidade,2);
//
repeat
if not eof(fi) then
readln(fi, line);
until eof(fi) or (copy(line, 1, 14)= 'Valor do Frete');
pBar.StepIt;
line := copy(line, 16, length(line));
ValFrete := RetiraPalavra(line, #$09);
//
repeat
if not eof(fi) then
readln(fi, line);
until eof(fi) or (copy(line, 1, 13)= 'Tipo de Frete');
pBar.StepIt;
line := copy(line, 15, length(line));
FreteCIFouFob := RetiraPalavra(line, #$09);
//
if not DadosOrcamento.Locate(CodCfo)
then
begin
Orcamento := DadosOrcamento.Add;
//
Orcamento.CodCfo := CodCfo;
Orcamento.CodCotacao := CodCotacao;
Orcamento.CondicoesPagto := CondicoesPagto;
Orcamento.FreteCIFouFob := FreteCIFouFob;
Orcamento.PrazoEntrega := PrazoEntrega;
Orcamento.PrazoValidade := PrazoValidade;
Orcamento.NumeroParcelas := StrToInt(NumeroParcelas);
try
LimiteResposta := StrToDate(ValLimiteResposta);
Orcamento.LimiteResposta := LimiteResposta;
except
end;
Orcamento.DataEmissao := DataEmissao;
try
ICMS := StrToFloat(ValICMS);
Orcamento.ICMS := ICMS;
except
end;
try
Frete := StrToFloat(ValFrete);
Orcamento.Frete := Frete;
except
end;
end;
//
readln(fi, line);
readln(fi, line);
readln(fi, line);
readln(fi, line);
readln(fi, line);
readln(fi, line);
readln(fi, line);
readln(fi, line);
readln(fi, line);
readln(fi, line);
//
while not Eof(fi) do
begin
readln(fi, line);
CodPrd := '';
i := 1;
k := 0;
//
while (i <= length(line))
and (line[i] <> #$09) do
begin
CodPrd := CodPrd + line[i];
inc(i);
end;
inc(i);
if Copy(CodPrd,1,11) = 'Observações' then
Break;
if CodPrd <> '' then
begin
//
Unitario := '';
Quantidade := '';
IPI := '';
Desconto := '';
ValorDescontado := '';
ValorFinal := '';
decimal := false;
k := 0;
//
while (i <= length(line)) and (line[i] <> #$09) do
inc(i);
inc(i);
while (i <= length(line)) and (line[i] <> #$09) do
begin
case line[i] of
'0'..'9':
begin
inc(k);
if not decimal or (k <= 4) then
Quantidade := Quantidade + line[i];
end;
',':
begin
Quantidade := Quantidade + ',';
decimal := true;
k := 0;
end;
end;
inc(i);
end;
try
StrToFloat(Quantidade);
except
Quantidade := '0';
end;
inc(i);
while (i <=
length(line)) and (line[i] <> #$09) do
inc(i);
inc(i);
while (i <= length(line)) and (line[i] <> #$09) do
begin
case line[i] of
'0'..'9':
begin
inc(k);
if not decimal or (k <= 4) then
Unitario := Unitario + line[i];
end;
',':
begin
Unitario := Unitario + ',';
decimal := true;
k := 0;
end;
end;
inc(i);
end;
try
StrToFloat(Unitario);
except
Unitario := '0';
end;
inc(i);
while (i <= length(line)) and (line[i] <> #$09) do
begin
IPI := IPI + line[i];
inc(i);
end;
try
StrToFloat(IPI);
except
IPI := '0';
end;
inc(i);
while (i <= length(line)) and (line[i] <> #$09) do
begin
case
line[i] of
'0'..'9':
begin
inc(k);
if not decimal or (k <= 4) then
Desconto := Desconto + line[i];
end;
',':
begin
Desconto := Desconto + ',';
decimal := true;
k := 0;
end;
end;
inc(i);
end;
try
StrToFloat(Desconto);
except
Desconto := '0';
end;
inc(i);
while (i <=length(line)) and (line[i] <> #$09) do
begin
ValorDescontado := ValorDescontado + line[i];
inc(i);
end;
try
StrToFloat(ValorDescontado);
except
ValorDescontado := '0';
end;
inc(i);
while (i <=length(line)) and (line[i] <> #$09) do
begin
ValorFinal := ValorFinal + line[i];
inc(i);
end;
try
StrToFloat(ValorFinal);
except
ValorFinal := '0';
end;
inc(i);
//
Item := DadosItem.Add;
Item.CodCfo := CodCfo;
Item.CodPrd := CodPrd;
if Unitario <> '0'
then
Item.Status := 0
else
Item.Status := 1;
Item.Unitario := StrToFloat(Unitario);
Item.Quantidade := StrToFloat(Quantidade);
Item.IPI := StrTofloat(IPI);
Item.Desconto := StrToFloat(Desconto);
Item.ValorDescontado := StrToFloat(ValorDescontado);
Item.Valortotal := StrToFloat(ValorFinal);
//
end;
end;
//
try
qryGravaCotacao.ParamByName('VALPRAZOENTREGA').AsInteger := Orcamento.PrazoEntrega;
qryGravaCotacao.ParamByName('DATENTREGA').AsDateTime := Date;
qryGravaCotacao.ParamByName('CODCPG').AsString := BuscaPrazoPagamento(Orcamento.CondicoesPagto, Orcamento.NumeroParcelas);
qryGravaCotacao.ParamByName('VALFRETE').AsFloat := Orcamento.Frete;
qryGravaCotacao.ParamByName('FRETECIFOUFOB').AsInteger := BuscaTipoFrete(Orcamento.FreteCIFouFob);
qryGravaCotacao.ParamByName('VALTRB').AsFloat := Orcamento.ICMS;
qryGravaCotacao.ParamByName('CODCOTACAO').AsString := Orcamento.CodCotacao;
qryGravaCotacao.ParamByName('CODCOLIGADA').AsInteger := CorporeRM.Coligada;
qryGravaCotacao.ParamByName('CODCFO').AsString := Orcamento.CodCfo;
qryGravaCotacao.ExecSQL;
try
for k := 0 to DadosItem.Count-1 do
begin
DadosItem.Items[k];
if
(DadosItem.Items[k].CodCfo = Orcamento.CodCfo) then
begin
try
CodCpg := BuscaPrazoPagamento(Orcamento.CondicoesPagto, Orcamento.NumeroParcelas);
qryGravaItemCotacao.ParamByName('IDPRD').AsInteger := BuscaIDProduto(DadosItem.Items[k].CodPrd);
qryGravaItemCotacao.ParamByName('CODCFO').AsString := DadosItem.Items[k].CodCfo;
qryGravaItemCotacao.ParamByName('VALCOTACAO').AsFloat := DadosItem.Items[k].Unitario;
qryGravaItemCotacao.ParamByName('VALNEGOCIADO').AsFloat := DadosItem.Items[k].Unitario;
qryGravaItemCotacao.ParamByName('VALTRB').AsFloat := DadosItem.Items[k].IPI;
qryGravaItemCotacao.ParamByName('DESCONTO').AsFloat := DadosItem.Items[k].ValorDescontado;
qryGravaItemCotacao.ParamByName('PERCDESCONTO').AsFloat := DadosItem.Items[k].Desconto;
qryGravaItemCotacao.ParamByName('VALTOTCOTACAONEG').AsFloat := 0;
qryGravaItemCotacao.ParamByName('CODCOTACAO').AsString := Orcamento.CodCotacao;
qryGravaItemCotacao.ParamByName('CODCOLIGADA').AsInteger := CorporeRM.Coligada;
qryGravaItemCotacao.ParamByName('STSITEM').AsInteger := DadosItem.Items[k].Status;
//
qryGravaItemCotacao.ParamByName('CFOVENCEDOR').AsFloat := DadosItem.Items[k].Quantidade;
qryGravaItemCotacao.ParamByName('VALTOTCOTACAO').AsFloat := 0; //
(DadosItem.Items[k].Unitario * DadosItem.Items[k].Quantidade);
qryGravaItemCotacao.ParamByName('CODCPGNEGOCIADA').AsString := CodCpg;
qryGravaItemCotacao.ParamByName('CODCPG').AsString := CodCpg;
qryGravaItemCotacao.ExecSQL;
except
MessageDlg('Erro ao tentar gravar o item "' + IntToStr(K) + '" desta
Cotação: '+ Exception(exceptobject).message, mtError, [mbOk], 0);
end;
end
end;
except
MessageDlg('Erro ao tentar gravar os dados nesta Cotação: '+ Exception(exceptobject).message, mtError, [mbOk], 0);
end;
except
MessageDlg('Erro ao tentar ler os dados para esta Cotação: '+ Exception(exceptobject).message, mtError, [mbOk], 0);
end;
//
end;
finally
MessageDlg('Importação realizada com sucesso!', mtInformation, [mbOk], 0);
CloseFile(fi);
SetStatusCotacao(Orcamento.CodCotacao,5);
//
BackupName := ExtractFileName(eFileName.text);
BackupName := ChangeFileExt(BackupName, '.old');
if not RenameFile(eFileName.text, BackupName) then
raise Exception.Create('Não foi possível renomear o arquivo já importado!');
//
end;
end;
cotação. Para isto iremos mexer em duas tabelas: TCORCAMENTO e TCITMORCAMENTO.
Você deve usar a seguinte sentença SQL para gravar os dados na TCORCAMENTO:
update TCORCAMENTO SET VALPRAZOENTREGA = :VALPRAZOENTREGA, DATENTREGA = :DATENTREGA, CODCPG = :CODCPG, VALFRETE = :VALFRETE, FRETECIFOUFOB = :FRETECIFOUFOB, VALTRB = :VALTRB where CODCOTACAO = :CODCOTACAO and CODCOLIGADA = :CODCOLIGADA and CODCFO = :CODCFO
E, para gravar na TCITMORCAMENTO:
update TCITMORCAMENTO set VALCOTACAO = :VALCOTACAO,VALNEGOCIADO = :VALNEGOCIADO, VALTRB = :VALTRB,DESCONTO = :DESCONTO, PERCDESCONTO = :PERCDESCONTO, VALTOTCOTACAONEG =:VALTOTCOTACAONEG, CODMOEDA = 'R$', CODCPG = :CODCPG, CODCPGNEGOCIADA = :CODCPGNEGOCIADA, STSITEM = 0, VALTOTCOTACAO = :VALTOTCOTACAO where CODCOTACAO = :CODCOTACAO and CODCOLIGADA = :CODCOLIGADA and CODCFO = :CODCFO and IDPRD = :IDPRD
Atente para o campo STSITEM da Tabela. Você pode gravar o valor 0 para ele ser atualizado na cotação ou então usar uma regra conforme o que você importar na planilha.
0 = Cotado
1 = Não fornece
2 = Não cotou
4 = Desqualificado
Depois você deve usar aquela mesma rotina de mudança de status da cotação e muda para o status 5 (Em negociação). E pronto!
É isso aí. Peixe mais bem pescado que este não vão encontrar em lugar algum :)
Um comentário:
Excelente rotina parabéns! Estou tentando fazer no delphi 7 cotação para fornecedores de uma relação de produtos que gere relatório de fornecedores com menor preço. Está com alguns erros. Você tem essa rotina em delphi para tirar algumas dúvidas.
Grato,
Valmir
Postar um comentário