No artigo de hoje, vou mostrar como deixar informações pré salvas antes de efetivar um cadastro.
Em um cliente, tem um usuário que fazia pedidos de compras, e digitava cerca de 300 a 400 itens, e as vezes ele precisava ter que resolver alguma coisa em outro setor, e o timeout da rede da empresa às vezes encerrava a conexão dele.
Cogitamos em aumentar o timeout, mas como na parte da rede eles não podiam mexer, então solicitaram para que a alternativa fosse feita no ERP.
Para isso, foi criado a seguinte lógica:
- Criar uma ação no Outras Ações do Pedidos de Compras
- Essa função irá abrir uma tela com uma grid, e aqui começa nossa mágica
- Se existir um arquivo texto na temporária do Windows, irá carregar as informações desse arquivo para a grid, se não existir, irá criar o arquivo em branco
- Na alteração entre as linhas para cima ou para baixo, será feito um laço de repetição, copiando todas as informações e jogando para esse arquivo texto
Dessa forma, se cair a conexão do usuário, ou até mesmo ele fechar propositalmente o Protheus, ao abrir novamente, o arquivo texto será lido, processado e a grid será atualizada com as informações.
Abaixo o exemplo do código fonte desenvolvido:
//Bibliotecas #Include "TOTVS.ch" /*/{Protheus.doc} User Function MT121BRW Ponto de entrada para adicionar outras ações no Pedido de Compras @type Function @author Atilio @since 10/03/2018 @version version /*/ User Function MT121BRW() Local aArea := GetArea() aAdd(aRotina,{"* Incluir Pedido" ,"U_ACOMM01" , 0, 2, 0, Nil }) RestArea(aArea) Return /*/{Protheus.doc} User Function ACOMM01 Facilitador para inclusão de pedidos de compras que ficam sempre na memória os itens @type Function @author Atilio @since 10/03/2018 @version version /*/ User Function ACOMM01() Local aArea := GetArea() Local oDlgPed Local cMaskQtd := PesqPict('SC7', 'C7_QUANT') //Fontes Local cFontPad := "Tahoma" Local oFontMod := TFont():New(cFontPad, , -38) Local oFontSub := TFont():New(cFontPad, , -20) Local oFontSubN := TFont():New(cFontPad, , -20, , .T.) //Tamanho de componentes Local aTamanho := MsAdvSize() Local nJanLarg := aTamanho[5] - 100 Local nJanAltu := aTamanho[6] - 200 Local nLargBtn := 50 //Fornecedor e loja Private cFornPad := "F00001" Private cLojaPad := "01" Private cTabePad := "001" //Variaveis privadas da tela Private oSayModulo, cSayModulo := 'COM' Private oSayTitulo, cSayTitulo := 'Inclusão de' Private oSaySubTit, cSaySubTit := 'Pedidos de Compras' Private oMsGetCom Private aHeaderCom := {} Private aColsCom := {} Private aColsEdit := {"XX_PRODUTO", "XX_PRAZO", "XX_QTDE", "XX_OBSERV"} //Posições das colunas Private nPosProd := 1 Private nPosDesc := 2 Private nPosQtde := 3 Private nPosObse := 4 Private nPosBlan := 5 Private nPosDele := 6 //Diretório e arquivos temporários Private cPastaTmp := GetTempPath() Private cArquiTmp := "pedido_compra_ACOMM01.txt" //Monta o aHeader das Interações // Titulo Campo Picture Tamanho Dec Valid Usado Tipo F3 aAdd(aHeaderCom,{ "Produto", "XX_PRODUTO", "", TamSX3('C7_PRODUTO')[01], 0, "u_zVldCo01(1)", ".T.", "C", "SB1", ""} ) aAdd(aHeaderCom,{ "Descricao", "XX_DESCRIC", "", 30, 0, ".F.", ".F.", "C", "", ""} ) aAdd(aHeaderCom,{ "Quantidade", "XX_QTDE", cMaskQtd, TamSX3('C7_QUANT')[01], TamSX3('C7_QUANT')[02], "Positivo()", ".T.", "N", "", ""} ) aAdd(aHeaderCom,{ "Observação", "XX_OBSERV", "", TamSX3('C7_OBS')[01], 0, ".T.", ".T.", "C", "", ""} ) aAdd(aHeaderCom,{ " ", "XX_BLANK", "", 1, 0, ".F.", ".F.", "C", "", ""} ) If File(cPastaTmp + cArquiTmp) Processa( {|| fBuscaDad()}, "Buscando informações...") EndIf oDlgPed := TDialog():New(0, 0, nJanAltu, nJanLarg, "Novo Pedido de Compra", , , , , CLR_BLACK, RGB(250, 250, 250), , , .T.) //Títulos e SubTítulos oSayModulo := TSay():New(004, 003, {|| cSayModulo}, oDlgPed, "", oFontMod, , , , .T., RGB(149, 179, 215), , 200, 30, , , , , , .F., , ) oSayTitulo := TSay():New(004, 045, {|| cSayTitulo}, oDlgPed, "", oFontSub, , , , .T., RGB(031, 073, 125), , 200, 30, , , , , , .F., , ) oSaySubTit := TSay():New(014, 045, {|| cSaySubTit}, oDlgPed, "", oFontSubN, , , , .T., RGB(031, 073, 125), , 300, 30, , , , , , .F., , ) //Botões oBtnFech := TButton():New(006, (nJanLarg/2-001)-((nLargBtn+2)*01), "Fechar", oDlgPed, {|| oDlgPed:End()}, nLargBtn, 018, , , , .T., , , , , , ) oBtnConf := TButton():New(006, (nJanLarg/2-001)-((nLargBtn+2)*02), "Confirmar", oDlgPed, {|| fConfirmar()}, nLargBtn, 018, , , , .T., , , , , , ) oBtnFech := TButton():New(006, (nJanLarg/2-001)-((nLargBtn+2)*03), "Limpar", oDlgPed, {|| fLimpar()}, nLargBtn, 018, , , , .T., , , , , , ) //Grid dos produtos oMsGetCom := MsNewGetDados():New(; 027,; //nTop 003,; //nLeft (nJanAltu/2) - 3,; //nBottom (nJanLarg/2) - 3,; //nRight GD_INSERT + GD_UPDATE + GD_DELETE,; //nStyle "AllwaysTrue()",; //cLinhaOk ,; //cTudoOk "",; //cIniCpos aColsEdit,; //aAlter ,; //nFreeze 99999999,; //nMax ,; //cFieldOK ,; //cSuperDel ,; //cDelOk oDlgPed,; //oWnd aHeaderCom,; //aHeader aColsCom) //aCols oMsGetCom:bChange := {|| fGravaTemp() } oDlgPed:Activate(, , , .T., {|| .T.}, , {|| } ) RestArea(aArea) Return /*/{Protheus.doc} User Function zVldCo01 Função para validar as colunas da grid @type Function @author Atilio @since 31/03/2021 @version version /*/ User Function zVldCo01(nTipo) Local aArea := GetArea() Local lRet := .T. Local nLinha := oMsGetCom:nAt Local aColsAux := oMsGetCom:aCols Local cCodProd := "" Default nTipo := 1 //Se for o código do produto If nTipo == 1 cCodProd := Alltrim(&(ReadVar())) DbSelectArea('SB1') SB1->(DbSetOrder(1)) // B1_FILIAL + B1_COD //Valida se o produto existe If SB1->(DbSeek(FWxFilial('SB1') + cCodProd)) DbSelectArea('AIB') AIB->(DbSetOrder(2)) // AIB_FILIAL + AIB_CODFOR + AIB_LOJFOR + AIB_CODTAB + AIB_CODPRO //Se existir na tabela do fornecedor If AIB->(DbSeek(FWxFilial('AIB') + cFornPad + cLojaPad + cTabePad + SB1->B1_COD)) aColsAux[nLinha][nPosProd] := SB1->B1_COD aColsAux[nLinha][nPosDesc] := SubStr(SB1->B1_DESC, 1, 30) Else lRet := .F. MsgStop("Produto não encontrado na tabela de preço - " + cFornPad + "!", "Atenção") EndIf Else lRet := .F. MsgStop("Produto não encontrado!", "Atenção") EndIf EndIf RestArea(aArea) Return lRet Static Function fGravaTemp() Local cTexto := "" Local nAtual := 0 Local aColsAux := oMsGetCom:aCols //Percorre as linhas, e se não tiver excluida, grava na variável For nAtual := 1 To Len(aColsAux) If ! aColsAux[nAtual][nPosDele] cTexto += aColsAux[nAtual][nPosProd]+";" cTexto += aColsAux[nAtual][nPosDesc]+";" cTexto += cValToChar(aColsAux[nAtual][nPosQtde])+";" cTexto += aColsAux[nAtual][nPosObse]+";" cTexto += CRLF EndIf Next //Gera o arquivo texto MemoWrite(cPastaTmp + cArquiTmp, cTexto) Return Static Function fBuscaDad() Local nAtual := 0 Local cArquivo := cPastaTmp + cArquiTmp oFile := FWFileReader():New(cArquivo) //Se o arquivo pode ser aberto If (oFile:Open()) //Se não for fim do arquivo If ! (oFile:EoF()) //Definindo o tamanho da régua aLinhas := oFile:GetAllLines() ProcRegua(Len(aLinhas)) //Método GoTop não funciona, deve fechar e abrir novamente o arquivo oFile:Close() oFile := FWFileReader():New(cArquivo) oFile:Open() While (oFile:HasLine()) //Incrementando a régua nAtual++ IncProc("Analisando linha " + cValToChar(nAtual) + " de " + cValToChar(Len(aLinhas)) + "...") //Buscando o texto da linha atual cLinAtu := oFile:GetLine() //Se tiver conteúdo na linha atual If ! Empty(cLinAtu) aDadosAux := StrTokArr(cLinAtu, ";") If ! Empty(aDadosAux[nPosProd]) aAdd(aColsCom, {; aDadosAux[nPosProd],; aDadosAux[nPosDesc],; Val(aDadosAux[nPosQtde]),; aDadosAux[nPosObse],; "",; .F.; }) EndIf EndIf EndDo EndIf //Fecha o arquivo e finaliza o processamento oFile:Close() EndIf Return Static Function fLimpar() //Zera o array e apaga o arquivo temporário If MsgYesNo("Tem certeza que deseja limpar os itens?", "Atenção") aColsCom := {} aAdd(aColsCom, {; Space(TamSX3('C7_PRODUTO')[01]),; Space(30),; 0,; Space(TamSX3('C7_OBS')[01]),; "",; .F.; }) oMsGetCom:SetArray(aColsCom) FErase(cPastaTmp + cArquiTmp) EndIf Return Static Function fConfirmar() Local aColsAux := oMsGetCom:aCols Local nTotalItens := Len(aColsAux) Local nMinutos := Round(nTotalItens/15, 0) //média de 15 itens por minuto If MsgYesNo("Esse procedimento, levará em média " + cValToChar(nMinutos) + " minutos. Deseja continuar?", "Atenção") Processa( {|| fInclui()}, "Incluindo pedido...") EndIf Return Static Function fInclui() Local aCabC7 := {} Local aItensC7 := {} Local aColsAux := oMsGetCom:aCols Local nAtual Local aLinAux Local lAborta := .F. Local cProdutos := "" Private lMSErroAuto := .F. ProcRegua(0) IncProc("Adicionando o cabeçalho") //Monta o cabeçalho aCabC7 := {; {'C7_FILIAL', cFilAnt, Nil},; {'C7_FORNECE', cFornPad, Nil},; {'C7_LOJA', cLojaPad, Nil},; {'C7_COND', "026", Nil},; {'C7_EMISSAO', Date(), Nil},; {'C7_MOEDA', 1, Nil},; {'C7_CONTATO', "Teste", Nil},; {'C7_FILENT', cFilAnt, Nil},; {'C7_TXMOEDA', 1, Nil}; } //Insere os itens ProcRegua(Len(aColsAux)) For nAtual := 1 To Len(aColsAux) IncProc("Adicionando produto " + cValToChar(nAtual) + " de " + cValToChar(Len(aColsAux)) + "...") aLinAux := {} //Se a linha não estiver excluída If ! aColsAux[nAtual][nPosDele] //Faz a validação para ver se o produto tem preço If fVldPreco(aColsAux[nAtual][nPosProd], nAtual) aAdd(aLinAux, {"C7_ITEM", StrZero(nAtual, 4), Nil} ) aAdd(aLinAux, {"C7_PRODUTO", aColsAux[nAtual][nPosProd], Nil} ) aAdd(aLinAux, {"C7_QUANT", aColsAux[nAtual][nPosQtde], Nil} ) aAdd(aLinAux, {"C7_CODTAB", cTabePad, Nil} ) aAdd(aLinAux, {"C7_OBS", aColsAux[nAtual][nPosObse], Nil} ) aAdd(aItensC7, aClone(aLinAux)) Else cProdutos += "<li>" + Alltrim(aColsAux[nAtual][nPosProd]) + "</li>" lAborta := .T. EndIf EndIf Next //Se for abortar, irá mostrar a mensagem If lAborta MsgStop("Os seguintes produtos estão com o preço zerado na tabela de preço '" + cTabePad + "' do fornecedor '" + cFornPad + "': <br><ul>" + cProdutos + "</ul>", "Atenção") //Se não tiver sido abortado Else //Inicia a transação e chama a inclusão do pedido Begin Transaction ProcRegua(2) IncProc("Efetivando a gravação...") MSExecAuto({|v, x, y, z, w| MATA120(v, x, y, z, w) }, 1, aCabC7, aItensC7, 3, .F.) If lMsErroAuto MostraErro() Else MsgInfo("Pedido de compras gerado, código: " + SC7->C7_NUM, "Atenção") EndIf //DisarmTransaction() End Transaction EndIf Return Static Function fVldPreco(cCodProd, nLinAtu) Local aArea := GetArea() Local lRet := .F. Local cQuery := "" //Monta a consulta buscando o preço na tabela de preço do fornecedor cQuery += " SELECT " + CRLF cQuery += " AIB_PRCCOM " + CRLF cQuery += " FROM " + CRLF cQuery += " " + RetSQLName("AIB") + " AIB " + CRLF cQuery += " WHERE " + CRLF cQuery += " AIB_CODFOR = '" + cFornPad + "' " + CRLF cQuery += " AND AIB_LOJFOR = '" + cLojaPad + "' " + CRLF cQuery += " AND AIB_CODTAB = '" + cTabePad + "' " + CRLF cQuery += " AND AIB_CODPRO = '" + cCodProd + "' " + CRLF cQuery += " AND AIB.D_E_L_E_T_ = ' ' " + CRLF TCQuery cQuery New Alias "QRY_AIB" //Se não tiver no fim do arquivo If ! QRY_AIB->(EoF()) //Se tiver preço If QRY_AIB->AIB_PRCCOM > 0 lRet := .T. Else lRet := .F. //MsgStop("Produto '" + Alltrim(cCodProd) + "' na linha '" + cValToChar(nLinAtu) + "' está com o preço zerado na tabela de preço '" + cTabePad + "' do fornecedor '" + cFornPad + "'!", "Atenção") EndIf //Senão, não foi encontrado registro Else lRet := .F. //MsgStop("Produto '" + Alltrim(cCodProd) + "' na linha '" + cValToChar(nLinAtu) + "' não encontrado na tabela de preço '" + cTabePad + "' do fornecedor '" + cFornPad + "'!", "Atenção") EndIf QRY_AIB->(DbCloseArea()) RestArea(aArea) Return lRet
Obs.: A classe MsNewGetDados foi descontinuada, não recomendo criar rotinas do zero com ela, para isso tente usar por exemplo a FwBrowse e/ou MVC, nesse link tem exemplos – Migrando do MSNewGetDados para FWBrowse e MVC.
Bom pessoal, por hoje é só.
Abraços e até a próxima.