Histórico da Página
Este documento é material de especificação dos requisitos de inovação, trata-se de conteúdo extremamente técnico. |
---|
Informações Gerais
Especificação | |||
Produto | Microsiga Protheus | Módulo | SIGAWMS - Gestão de Armazenagem |
Segmento Executor |
Supply Chain - Logística | ||
Projeto |
M_DL_WMS002 | IRM |
PCREQ- |
9339 |
Requisito |
PCREQ- |
9340 | Subtarefa |
PDRDL- |
Chamado2
2452 |
Release de Entrega Planejada |
11.80.14
Réplica |
País | ( |
X ) Brasil ( ) Argentina ( ) |
México ( ) Chile ( ) Paraguai ( ) Equador |
( ) USA ( ) |
Colômbia ( ) Outro _____________. |
Outros
Legenda: 1 – Inovação 2 – Manutenção (Os demais campos devem ser preenchidos para ambos os processos).
Objetivo
Permitir a junção (aglutinação) de quantidades iguais de um mesmo produto dentro de uma mesma carga ou, prevendo alterações futuras, de um mesmo plano de movimentação a fim de otimizar as movimentações
Objetivo
O plano de expedição consiste na aglutinação dos mesmos produtos de diferentes cargas/pedidos ao executar uma ordem de serviço de expedição. A ideia é que seja possível selecionar as ordens de serviço que serão consideradas na aglutinação. Neste primeiro momento, é levado em consideração apenas produtos de pedidos da mesma montagem de carga, posteriormente será criado uma tela para selecionar inclusive pedidos de cargas diferentes. Esta ação otimiza a busca de saldo no armazém priorizando a expedição de endereços de tipo pulmão quando a junção dos pedidos que compõe o a carga ou plano solicitarem uma quantidade maior que uma norma do produto.
Esta alteração, diferente da aglutinação dos movimentos das atividades, configurada pelo cadastro de tarefa x atividade, aglutina as quantidade das ordens de serviço que geram um movimento de uma norma, priorizando a retirado do pulmão de forma mais eficiente.
Adequar o processo de aglutinação para permitir as movimentações por onda (plano de movimentação), que possibilitará a aglutinação de serviços de entrada e saída independente da carga ou cliente, será criado na ordem de serviço a indicação de qual plano a ordem de serviço pertence e assim juntar as quantidades dos produtos.
Definição da Regra de Negócio
Alteração:
A execução das ordens de serviço pelo WMSA150 realiza uma busca das DCF selecionadas e uma por uma posiciona no registro e chama o método ExecuteDCF, a ideia é logo após o posicionamento utilizar das características do registro para buscar as ordens que podem ser aglutinadas. Encontrado os dados, será somado a variável da quantidade do DCF posicionado no objeto com as quantidades de todas as ordens de serviço do array
e a partir desse ponto segue o fluxo normal, mas será precisa alterar também o status de cada DCF, para que não seja executada em seguida, e a criação da DCR para respeitar o array de ordens aglutinadas encontradas, o que mantém o
funcionamento do estorno.Detalhes Alteração:
Primeiramente, deve conterrelacionamento com a ordem de serviço original.
Primeiramente, é de conhecimento que a alteração é aplicável somente para o novo WMS, ou seja, o parâmetro MV_WMSNEW precisa estar igual a .T., assim como o ambiente de desenvolvimento.
Conter no dicionário o parâmetro MV_WMSACEX que pode ser criado aplicando o update WMSU0033, acessado via WMSUMAIN.
Alterar a classe WMSDTCOrdemServicoA alteração inicia-se com a criação
dosde dois atributos da classe WMSDTCOrdemServico:
cPlnExp- cCodPln > Código do plano de
- movimentação
- aOrdAglu > Array de ordens de serviço aglutinadas
*Lembrando que a criação de cada atributo precisa vir com os métodos Getters e Setters correspondentes.
Como o atributo
cPlnExpcCodPln da classe corresponde ao campo DCF_
PLNEXPCODPLN que será criado, ele precisa ser considerado nos métodos LoadData (utilizar FieldPos ao atribuir o valor) , RecordDCF e UpdateDCF (utilizar o método Getter e Setter criado).
Quando o parâmetro MV_WMSACEX for diferente de '0' e o serviço em execução for de separação (Self:oServico:ChkSepara()), dentro
Dentro do método ExecuteDCF da classe WMSDTCOrdemServicoExecute
logo após a validação se o 'Self:cIdDCF' está preenchidoapós as validações e antes de iniciar o bloco transaction, criar a chamada de um método que realizará a busca das ordens de serviço semelhantes e adicionará ao array aOrdAglu. Este método precisa ser criado na WMSDTCOrdemServico para futuras aplicações.
O select de busca precisa prever a informação do campo DCF_
PLNEXPCODPLN e para o filtro deve ser utilizado os atributos da classe.
Quando o atributo cPlnExp possuir valor diferente de vazio, oO filtro precisa respeitar
o plano de expedição,serviço, armazém, produto, lote, sub-lote, endereço
e, status diferente de '3=Executado' e quando o atributo cCodPln estiver preenchido, o plano, quando estiver vazio,
precisaconsiderar
a cargae a
função WMSCargafunção WMSCarga() e a carga ao invés do plano
de expedição. Quando MV_WMSACEX for '2', considerar no filtro o cliente e loja somente quando o plano estiver vazio.
Prevendo alterações futuras referente ao plano de movimentação, o endereço que precisa ser considerado no select varia dependendo do serviço que está sendo executado. Quando for um serviço de separação (Self:oServico:ChkSepara()), considerar armazém e endereço origem, pelo contrário, armazém e endereço destino, conforme exemplo:
" SELECT DCF.DCF_ID, DCF.DCF_QUANT, DCF.R_E_C_N_O_ RECNODCF"
" FROM "+RetSqlName("DCF")+" DCF"
" WHERE DCF.DCF_FILIAL = '"+xFilial("DCF")+"'"
" AND DCF.DCF_SERVIC = '"+Self:oServico:GetServico()+"'"
" AND DCF.DCF_CODPRO = '"+Self:oProdLote:GetProduto()+"'"
If !Empty(Self:GetPlnExp())
" AND DCF.DCF_PLNEXP = '"+Self:GetPlnExp()+"'"
ElseIf WmsCarga(Self:GetCarga())
If SuperGetMv("MV_
WMSACEX",.F.,"0") == '2' // Se aglutina por cliente
" AND DCF.DCF_CLIFOR = '"+Self:GetCliFor()+"'"
" AND DCF.DCF_
LOJA = '"+Self:GetLoja()+"'"
EndIf
" AND DCF.DCF_CARGA = '"+Self:GetCarga()+"'"
EndIf
If
Self:oServico:
GetPlnExpChkSepara(
))
" AND DCF.DCF_
LOCAL = '"+Self:oOrdEndOri:
GetPlnExpGetArmazem()+"'
ElseIf WmsCarga(Self:GetCarga())
"
" AND DCF.DCF_
ENDER = '"+Self:oOrdEndOri:
GetCargaGetEnder()+"'"
Else
" AND DCF.DCF_
LOCDES = '"+Self:
oOrdEndOrioOrdEndDes:GetArmazem()+"'"
" AND DCF.DCF_
ENDDES = '"+Self:
oOrdEndOrioOrdEndDes:GetEnder()+"'"
EndIf
" AND DCF.DCF_LOTECT = '"+Self:oProdLote:GetLoteCtl()+"'"
" AND DCF.DCF_NUMLOT = '"+Self:oProdLote:GetNumLote()+"'"
" AND DCF.DCF_STSERV <> '3'"
" AND DCF.R_E_C_N_O_ <> "+Self:GetRecno()
" AND DCF.D_E_L_E_T_ = ' '"
*Importante manter esta ordem no filtro do select para que o banco de dados utilize do índice mais apropriado
. Deixar um comentário a respeito.
Retornar os campos DCF_ID
, DCF_NUMSEQ, DCF_QUANT, R_E_C_N_O_
e, adicionar cada linha no array
aOrdAglu.aOrdAglu e criar na ultima posição um vetor vazio que posteriormente será carregado com as atividades do serviço e suas quantidades.
Este array sempre precisará conter pelo menos um registro, que será o DCF atual. Para isso, preencher a primeira posição os valores do objeto da DCF atual e depois com o que encontrar no select. No inicio do método, certificar que o array inicialize como vazio.
Inicializar uma variável nQtdOrdSer, que conterá a quantidade sumarizada da aglutinação, com o Self:nQuant do objeto atual e incrementar na mesma variável para cada DCF_QUANT retornado do select de ordens de serviço possíveis de aglutinação. Após isso, substituir o valor do Self:nQuant por nQtdOrdSer.
Ordenar o array por IDDCF com a função aSort para garantir a ordem na execução assim como as quantidades na hora da criação dos movimentos em passos posteriores.
No
Incrementar em uma variável o DCF_QUANT para que depois seja substituído a quantidade do objeto Self:nQuant pela quantidade sumarizada.
Voltando para o
método ExecuteDCF da classe WMSDTCOrdemServicoExecute, alterar o bloco que atualiza o status da DCF posicionada no objeto para repetir para cada recno do aOrdAglu
quando Len(aOrdAglu) > 0. Criar um laço de repetição pelo tamanho do array aOrdAglu.
O bloco que precisa repetir para atualizar o status é:
// Atualiza status
Self:SetStServ('3')
Self:SetOk("")
Self:UpdateDCF()
Self:UpdStatus()
*Importante manter o funcionamento atual do método quando o sistema não utilizar a funcionalidade, ou seja, quando o parâmetro MV_WMSACEX for igual a '0'.Porém será preciso reposicionar o objeto para atualizar cada DCF. Utilizar o método GoToDCF(nRecno) quando Len(aOrdAglu) > 1, passando o recno de cada DCF do array por parâmetro. Ao terminar o laço de atualização do status, é preciso voltar a DCF posicionada original para que o Self:UnLockDCF() realize sua função corretamente, assim como o restante do processo.
No método ExecutePrd, antes do comando 'For' de aProdutos, definir os campos na ultima posição do array aOrdAglu. Percorrer o tamanho de aOrdAglu e adicionar mais espaço para quando o produto for partes e então adicionar a quantidade multiplicada pelo múltiplo do componente neste sub-array de aOrdAglu.
Antes de chamar a execução da função (oFuncao:ExecFuncao) atribuir o array aOrdAglu ao objeto do movimento, assim como aRecD12 e aLibD12
Em WMSDTCMovimentosServicoArmazem método AssignD12 antes do comando 'For' de aAtividade, realizar o rateio para a tarefa da quantidade total solicitada dentre os ID DCF que estão no array, isso determinará quanto cada ID DCF será atendido pela quantidade total do movimento, conforme exemplo:
nQtSol := Self:nQtdMovto
nOrdPrd := Self:oOrdServ:nProduto
nOrdTar := Self:oOrdServ:nTarefa
For nI := 1 To Len(Self:aOrdAglu)
aAgluMov := Self:aOrdAglu[nI][POSTAREFA][nOrdPrd][nOrdTar]
If (aAgluMov[POSQTDSOL] - aAgluMov[POSQTDATD]) > 0
aAgluMov[POSIDDCF] := Self:oOrdServ:GetIdDCF()
If nQtSol > (aAgluMov[POSQTDSOL] - aAgluMov[POSQTDATD])
nQtSol -= (aAgluMov[POSQTDSOL] - aAgluMov[POSQTDATD])
aAgluMov[POSQTDMOV] := (aAgluMov[POSQTDSOL] - aAgluMov[POSQTDATD]) //Utiliza todo o saldo
Else
aAgluMov[POSQTDMOV] := nQtSol
nQtSol := 0
EndIf
// Grava o total atendido
aAgluMov[POSQTDATD] += aAgluMov[POSQTDMOV]
If nQtSol == 0
Exit
EndIf
EndIf
Next nI
Ao final da execução das atividades será preciso zerar as quantidades distribuídas da movimentação, ou seja, zerar a posição com a quantidade da movimentação de todos os sub-arrays de aOrdAglu.
Assim como a atualização do status para cada ordem de serviço, é preciso criar os registros DCR das ordens aglutinadas. Na classe WMSDTCMovimentosServicoArmazem método RecordD12, alterar o bloco "Grava relacionamento movimento serviço armazém" para cada ordem de serviço do array
quando Len(aOrdAglu) > 0, substituindo...conforme exemplo:
// Grava relacionamento movimento servico armazem
nOrdPrd := Self:oOrdServ:nProduto
nOrdTar := Self:oOrdServ:nTarefa
For nI := 1 To Len(Self:aOrdAglu)
aAgluMov := Self:aOrdAglu[nI][POSTAREFA][nOrdPrd][nOrdTar]
If aAgluMov[POSQTDMOV] > 0
cIdDCF := aAgluMov[POSIDDCF]
nQtdMovto := aAgluMov[POSQTDMOV]
nQtdMovto2 := ConvUm(Self:oMovPrdLot:GetProduto(),nQtdMovto,0,2)
oRelacMov:SetIdOrig
oRelacMov:SetIdDCF(cIdDCF)
oRelacMov:
SetSequen(cSequen)Pelos valores do array aOrdAglu, assim como a quantidade de cada DCF. Utilizar de uma variável auxiliar para atribuir a quantidade do array para não alterar a variável utilizada atualmente.
Os registros DCR desse movimento precisam ter o mesmo DCR_IDORI, que seria o iddcf do próprio objeto, não precisando ser alterado. DCR_IDMOV e DCR_IDOPER com o mesmo valor do original. DCR_QUANT, DCF_IDDCF e DCR_SEQUEN correspondente a cada registro do array.
Neste passo não é preciso voltar o objeto oRelacMov para o valor posicionado original, mas ele deve continuar com o funcionamento atual quando Len(aOrdAglu) == 0 e, como não estamos na classe WMSDTCOrdemServico, validar se o array tem sua definição correta (Type("Self:oOrdServ:aAgluDCF") == "A").
SetIdDCF(Self:aOrdAglu[nI][1])
oRelacMov:SetIdMovto(IIF(lAglutina,D12->D12_IDMOV,cIdMovto))
oRelacMov:SetIdOpera(IIF(lAglutina,D12->D12_IDOPER,cIdOpera))
oRelacMov:SetSequen(cSequen)
If oRelacMov:LoadData()
oRelacMov:SetQuant(oRelacMov:GetQuant()+nQtdMovto)
oRelacMov:SetQuant2(oRelacMov:GetQuant2()+nQtdMovto2)
oRelacMov:UpdateDCR()
Else
oRelacMov:SetQuant(nQtdMovto)
oRelacMov:SetQuant2(nQtdMovto2)
oRelacMov:RecordDCR()
EndIf
nQtdTotal += nQtdMovto
EndIf
Next nI
nQtdMovto := nQtdTotal
nQtdMovto2 := ConvUm(Self:oMovPrdLot:GetProduto(),nQtdMovto,0,2)
Finalmente, será preciso alterar o processo de conferência de saída para considerar o array de ordens de serviço aglutinadas. A conferência de saída (DLConfSai()) não deve gerar movimentos aglutinados, por isso é preciso considerar todos os identificados da DCF para que cada movimento seja criado com a quantidade correta. Em WMSBCCConferenciaSaida método ProcConfSai alterar o select das movimentações buscando por todos os identificadores da DCF do array ao invés do posicionado, conforme exemplo:
" AND DCR.DCR_IDDCF IN ('000001','000002','000003','000004') "
Criar no update do projeto (WMSNEWUPD) o campo com as informações abaixo em Dicionário de Dados.
Rotina | Tipo de Operação | Opção de Menu | Regras de Negócio |
WMSDTCOrdemServico | Alteração |
WMSDTCOrdemServicoExecute | Alteração |
WMSDTCMovimentosServicoArmazem | Alteração |
WMSNEWUPD |
Alteração |
Exemplo de Aplicação
:Configurar
o
parâmetro
MV_WMSACEX
com
o
valor
'1=Aglutina
por
Carga'
ou
'2=Aglutina
por
Carga/Cliente'.
Criar
vários
pedidos
de
venda
que
realizam
montagem
de
carga
e
que
geram
o
serviço
WMS
na
montagem
de
carga,
com
os
mesmos
produtos
entre
eles.
Criar
uma
ou
mais
cargas
com
os
pedidos
criados.
Ao
executar
os
serviços,
os
movimentos
da
D12
devem
juntar
a
quantidade
dos
produtos
iguais
em
um
único
movimento
respeitando
a
norma.
Verificar
se
os
dados
da
DCR
consistem.
Estornar
e
executar
várias
vezes
para
garantir
que o estorno não deixe lixo na baseo saldo do produto.
Tabelas Utilizadas
DCF – Ordem de Serviço
D12 – Movimentos Serviço WMS
DCR – Relacionamento Movimentos Distribuição
Dicionário de Dados
Arquivo : DCF – Ordem de Serviço
Campo | DCF_ |
CODPLN | |
Tipo | C |
Tamanho | 6 |
Valor Inicial |
Mandatório | Sim ( ) Não (x) |
Descrição |
Código do plano de movimentação. | |
Título | Cód. Plano |
Picture | @! |
Help de Campo |
Código do plano de expedição.
Utilizado para indicar as ordens de serviço que terão seus produtos aglutinados. |
|
---|
Este documento é material de especificação dos requisitos de inovação, trata-se de conteúdo extremamente técnico. |
---|