01. DATOS GENERALES
| Producto | |
|---|
| Línea de producto: | |
|---|
| Segmento: | |
|---|
| Módulo: | SIGAFIN - Financiero |
|---|
| Función: | | Rutina | Nombre Técnico | Fecha |
|---|
| FINA998.App | Front de Totvs Recibos | 27/12/2024 |
|
|---|
| País: | Todos |
|---|
| Ticket: | N/A |
|---|
| Requisito/Story/Issue (informe el requisito vinculado): | DMINA-25574 |
|---|
|
02. SITUACIÓN/REQUISITO
En la rutina FINA998, cuando se realiza un descuento por pronto pago utilizando el PE F998VALBX, al colocar un pago parcial de una factura y después un porcentaje de descuento, no toma el valor ingresado en la moneda correspondiente, toma el valor total del titulo limitando la funcionalidad de un pago parcial.
03. SOLUCIÓN
Cambios realizados en los fuentes del aplicativo:
- Front-end de Totvs Recibo (FINA99.APP): Se modificó el JSON de retorno al punto de entrada F998VALBX donde se agrega un objeto de monedas el cual lleva el monto ingresado en cada moneda, donde el usuario puede elegir si utiliza valor del titulo completo o el ingresado en la moneda.
Por medio del módulo configurador (SIGACFG) se dan de alta los siguientes campos
- Realizar un respaldo del repositorio (RPO).
- Aplicar el parche correspondiente al issue DMINA-25574.
- Validar que las rutinas actualizadas en el repositorio, coincidan con las descritas en el encabezado del presente Documento Técnico, así como las fechas.
- Crear la función de usuario F998VALBX definiendo las propiedades para la vista , cálculos para los campos descuento, multa, interés y total, así como la función a ejecutar para guardar los datos.
- Dentro del módulo Financiero ejecutar la rutina TOTVS Recibo (Actualizaciones | Cuentas por cobrar).
En la rutina de Clientes (MATA030), ubicada en el módulo Financiero (Actualizaciones | Archivos) registre un cliente.
En la rutina de Productos (MATA010), ubicada en el módulo Facturación (Actualizaciones | Archivos), incluir un nuevo producto.
- En la rutina de Tipos de Entrada y Salida (MATA080), ubicada en el módulo Facturación (Actualizaciones | Archivos), incluir una TES de salida.
- En la rutina de Facturaciones (MATA467N), ubicada en el módulo de Facturación (Actualizaciones | Facturaciones) se realiza una factura que genere un título financiero al cliente configurado anteriormente.
Ingresar a la rutina TOTVS Recibo; SIGAFIN - Actualizaciones | Cuentas por Cobrar | TOTVS Recibo.
Ingresar a la opción Nuevo Recibo.
Informe el encabezado del recibo con los datos configurados anteriormente y dar clic en "Extraer Títulos".
- Localizar el título generado anteriormente y dar clic en "Editar cobro".
- Verificar que se visualice el nuevo campo configurado "Descuento por pronto pago".
- Verificar que se realice la lógica matemática adecuadamente proporcionada al descuento.
- Agregar una forma de pago por el valor restante del título financiero.
- Guardar el recibo:
- Verificar que el valor se haya guardado correctamente en el campo configurado en el PE.
| Descripción | Actualizaciones |
|---|
| Punto de entrada: | F998VALBX |
| Parámetros recibidos | | Nombre | Tipo | Descripción |
|---|
| PARAMIXB[1] | O | Definición de la vista del campo "Descuento por pronto pago" Descripción del objeto PARAMIXB[1] cuando en el Json la bandera PARAMIXB[2] es Verdadera (.T.) y la bandera ["recalculate" ] este como falsa (.F.) Objeto Titulo | Nombre en Json | Equivalencia | Descripción |
|---|
| ['recalculate´] | N/A | Si es True (.T.) realizará los calculos, si es False (.F.) realizara la vista | | titulos[nX]['branch'] | E1_FILIAL | Filial | | titulos[nX]['billnumber'] | E1_NUMERO | Numero de Titulo | | titulos[nX]['type'] | E1_TIPO | Tipo de Titulo | | titulos[nX]['actualduedt'] | E1_VENCREA | Vencimiento Real | | titulos[nX]['currency'] | E1_MOEDA | Moneda | | titulos[nX]['unit'] | E1_LOJA | Tienda | | titulos[nX]['installment'] | E1_PARCELA | Cuota | | titulos[nX]['billvalue'] | E1_VALOR | Valor | | titulos[nX]['customer'] | E1_CLIENTE | Cliente | | titulos[nX]['class'] | E1_NATUREZ | Modalidad | | titulos[nX]['custname'] | A1_NOME | Nombre del cliente | | titulos[nX]['balance'] | E1_SALDO | Saldo del titulo | | titulos[nX]['prefix'] | E1_PREFIXO | Prefijo del titulo | | titulos[nX]['issuedt'] | E1_EMISSAO | Fecha de emisión | | titulos[nX]['writeoffne'] | E1_VALLIQ | Valor neto de la baja | | titulos[nX]['discount'] | E1_DESCONT | Descuento | | titulos[nX]['interest'] | E1_JUROS | Intereses | | titulos[nX]['fine'] | E1_MULTA | Multas | | titulos[nX]['series'] | E1_SERIE | Serie | | titulos[nX]['origin'] | E1_ORIGEM | Ruta origen del titulo | | titulos[nX]['recno'] | R_E_C_N_O |
| | titulos[nX]['client'] | E1_CLIENTE | Cliente | | titulos[nX]['originbranch'] | E1_FILORIG | Filial Original | | titulos[nX]['paycondf2'] | F2_COND | Condición SF2 | | titulos[nX]['paycondf1'] | F1_COND | Condición SF1 | | titulos[nX]['serie2f2'] | F2_SERIE2 | Serie SF2 | | titulos[nX]['serie2f1'] | F1_SERIE2 | Serie SF1 | | titulos[nX]['paymentcondition'] |
| Condición de pago | | titulos[nX]['cobrar'] |
| Monto a cobrar en Numeric | | titulos[nX]['billvalueString'] |
| Total o saldo (Dependiendo la pantalla) | | titulos[nX]['balanceString'] |
| Saldo en String | | titulos[nX]['cobrarString'] |
| Monto a cobrar en String | | titulos[nX]['discountString'] |
| Descuento del titulo en String | | titulos[nX]['interestString'] |
| Interés del titulo en String | | titulos[nX]['fineString'] |
| Multa del titulo en String |
Definición de la lógica del campo "Descuento por pronto pago" Descripción del objeto PARAMIXB[1] cuando en el JSON la bandera PARAMIXB[2] es Verdadera (.T.) y la bandera ["recalculate" ] este como True (.T.) Objeto | Nombre en Json | Descripción |
|---|
| ['recalculate´] | Si es True (.T.) realizará los cálculos, si es False (.F.) realizara la vista | | bind['discountInput'] | Descuento ingresado en el campo Descuento Pronto Pago | | bind['descuento'] | Tipo de Titulo | | bind['multa'] | Interés del titulo en String | | bind['interes'] | Multa del titulo en String | | bind['saldo'] | Saldo total (Ya tomando en cuenta, descuento, multa e interés) | | coins[nX]['property'] | Nombre de la moneda | | coins[nX]['coin'] | Moneda | | coins[nX]['value'] | Valor ingresado |
Definición de la grabación del campo "Descuento por pronto pago" Descripción de los objetos (PARAMIXB[1]) mandados en el JSON cuando la bandera PARAMIXB[2] es Falsa (.F.) Objeto Encabezado | Nombre en Json | Equivalencia | Descripción |
|---|
| encabezado['serie'] | FJT_SERIE | Serie del recibo | | encabezado['recibo'] | FJT_RECIBO | Numero de recibo | | encabezado['emissa'] | FJT_EMISSA | Emisión | | encabezado['nature'] | FJT_NATURE | Cobrador | | encabezado['client'] | FJT_CLIENT | Cliente | | encabezado['loja'] | FJT_LOJA | Tienda | | encabezado['cobrad'] | FJT_COBRAD | Cobrador | | encabezado['recprov'] | FJT_RECPRV | Recibo Provisorio |
Objeto Titulos | Nombre en Json | Equivalencia | Descripción |
|---|
| titulos[nX]['branch'] | E1_FILIAL | Filial | | titulos[nX]['billnumber'] | E1_NUMERO | Número de Titulo | | titulos[nX]['type'] | E1_TIPO | Tipo de Titulo | | titulos[nX]['actualduedt'] | E1_VENCREA | Vencimiento Real | | titulos[nX]['currency'] | E1_MOEDA | Moneda | | titulos[nX]['unit'] | E1_LOJA | Tienda | | titulos[nX]['installment'] | E1_PARCELA | Cuota | | titulos[nX]['billvalue'] | E1_VALOR | Valor | | titulos[nX]['customer'] | E1_CLIENTE | Cliente | | titulos[nX]['class'] | E1_NATUREZ | Modalidad | | titulos[nX]['custname'] | A1_NOME | Nombre del cliente | | titulos[nX]['balance'] | E1_SALDO | Saldo del titulo | | titulos[nX]['prefix'] | E1_PREFIXO | Prefijo del titulo | | titulos[nX]['issuedt'] | E1_EMISSAO | Fecha de emisión | | titulos[nX]['writeoffne'] | E1_VALLIQ | Valor neto de la baja | | titulos[nX]['discount'] | E1_DESCONT | Descuento | | titulos[nX]['interest'] | E1_JUROS | Intereses | | titulos[nX]['fine'] | E1_MULTA | Multas | | titulos[nX]['series'] | E1_SERIE | Serie | | titulos[nX]['origin'] | E1_ORIGEM | Ruta origen del titulo | | titulos[nX]['recno'] | R_E_C_N_O |
| | titulos[nX]['client'] | E1_CLIENTE | Cliente | | titulos[nX]['originbranch'] | E1_FILORIG | Filial Original | | titulos[nX]['paycondf2'] | F2_COND | Condición SF2 | | titulos[nX]['paycondf1'] | F1_COND | Condición SF1 | | titulos[nX]['serie2f2'] | F2_SERIE2 | Serie SF2 | | titulos[nX]['serie2f1'] | F1_SERIE2 | Serie SF1 | | titulos[nX]['paymentcondition'] |
| Condición de pago | | titulos[nX]['cobrar'] |
| Monto a cobrar en Numeric | | titulos[nX]['billvalueString'] |
| Total o saldo (Dependiendo la pantalla) | | titulos[nX]['balanceString'] |
| Saldo en String | | titulos[nX]['cobrarString'] |
| Monto a cobrar en String | | titulos[nX]['discountString'] |
| Descuento del titulo en String | | titulos[nX]['interestString'] |
| Interés del titulo en String | | titulos[nX]['fineString'] |
| Multa del titulo en String | | titulos[nX]['quickSelected'] |
| Titulo seleccionado en el GRID | | titulos[nX]['reasonBx'] |
| Motivos de baja | | titulos[nX]['peso'] |
| Total en moneda |
Objeto monedas | Nombre en Json | Descripción |
|---|
| monedas[nX]['coin'] | Nombre Moneda | | monedas[nX]['tasa'] | Tasa | | monedas[nX]['received'] | Cantidad recibida en esta moneda | | monedas[nX]['balance'] | Saldo en Numeric | | monedas[nX]['type'] | Nombre de Moneda | | monedas[nX]['moneda'] | Numero de Moneda | | monedas[nX]['stringTasa'] | Tasa de moneda | | monedas[nX]['balanceString'] | Saldo en String | | monedas[nX]['receivedString'] | Cantidad recibida en esta moneda |
Objeto formas de pago | Nombre en JSON | Descripción |
|---|
| formaspago[nX]['property'] | Nombre de la propiedad (Trae el mismo nombre registrado en la SX3) | | formaspago[nX]['value'] | Valor de la propiedad |
Objeto parametros | Nombre en JSON | Descripción |
|---|
| params[nX]['mv_par01'] | Valor del parámetro 01 | params[nX]['mv_par01'] . . . params[nX]['mv_parxx'] |
|
| | PARAMIXB[2] | L | True (.T.) si es momento del guardado y False (.F.) si es configuración de la vista |
|
| Respuesta: | Descripción del objeto de respuesta cuando en el JSON la bandera PARAMIXB[2] es Verdadera (.T.) y la bandera ["recalculate" ] este como falsa (.F.) Objeto de respuesta Nombre | Tipo | Descripción | Obligatorio |
|---|
| oJson['label'] | L | Nombre del campo nuevo a mostrar. | Si | | oJson['disabledDiscount'] | L | Deshabilitar o habilitar el campo de descuento. | Si | | oJson['disabledInterest'] | L | Deshabilitar o habilitar el campo de Intereses. | Si | | oJson['disabledFine'] | L | Deshabilitar o habilitar el campo de Multa. | Si | | oJson['disabledInput'] | L | Deshabilitar o habilitar el input nuevo | Si | | oJson['maxDiscount'] | N | Se informa el descuento permitido debe tener un rango mayor a 0 y menor a 100 | Si | | oJson['warningMaxDiscount'] | C | Mensaje personalizado por si se excede el descuento permitido, si no se informa el front-end mandará uno por default | No |
Descripción del objeto de respuesta cuando en el JSON la bandera PARAMIXB[2] es Verdadera (.T.) y la bandera ["recalculate" ] este como True (.T.) Objeto de respuesta | Nombre en Json | Descripción |
|---|
| oJson['descuento'] | Valor que será mostrado en el campo descuento | | oJson['residuo'] | Valor que será mostrado como total del titulo (Después de descuentos, multa e intereses) | | oJson['multa'] | Valor que será mostrado como multa del titulo | | oJson['interes'] | Valor que será mostrado como interés del titulo |
Descripción de los objetos de respuesta en el JSON cuando la bandera PARAMIXB[2] sea Falsa (.F.) N/A |
Ejemplo donde el descuento se calcula con el saldo total del titulo
#Include 'Protheus.ch'
User Function F998VALBX()
Local oResponse := JsonObject():New() as Object
IF PARAMIXB[2] //Vista
IF PARAMIXB[1]['recalculate']
oResponse:=setValues(PARAMIXB[1])
ELSE
oResponse['label'] := "% Descuento por pronto pago" //Nombre del campo nuevo a mostrar.
oResponse['readonlyDiscount'] := .T. //Deshabilitar o habilitar el campo de descuento.
oResponse['readonlyInterest'] := .T. //Deshabilitar o habilitar el campo de Intereses.
oResponse['readonlyFine'] := .T. //Deshabilitar o habilitar el campo de Multa.
oResponse['disabledInput'] := .F.//isDisable(PARAMIXB[1]) //Deshabilitar o habilitar el input nuevo
oResponse['maxDiscount'] := 50//GetLimite(PARAMIXB[1]) //Se informa el descuento permitido debe tener un rango mayor a 0 y menor a 100
oResponse['warningMaxDiscount'] := "Descuento por Pronto Pago mayor al permitido. En esta condición el tope es:"+ALLTRIM(STR(oResponse['maxDiscount'])) //Mensaje personalizado por si se excede el descuento permitido, si no se informa el front mandara uno por default
ENDIF
ELSE
SaveData(PARAMIXB[1]) //Grabación
ENDIF
Return oResponse
/*/{Protheus.doc} setValues
Esta funcion determina los valores a mostrar en los campos de descuento, residuo, multa e interés
@type function
@version
@author luis.aboytes
@since 25/7/2024
@param oJson, object, param_description
@return variant, return_description
/*/
Static Function setValues(oJson)
Local oRes := JsonObject():New() }
// El cálculo se realiza por el total del título si se utiliza el dato PARAMIXB[1]['bind']['saldo']
oRes['descuento'] := Round(PARAMIXB[1]['bind']['discountInput']/100*PARAMIXB[1]['bind']['saldo'],2) //Calculo con el total del titulo
oRes['residuo'] := Round(PARAMIXB[1]['bind']['saldo']-oRes['descuento'],MsDecimais(SE1->E1_MOEDA)) //Calculo con el total del titulo
oRes['multa'] := 0
oRes['interes'] := 0
Return oRes
/*/{Protheus.doc} SaveData
Guardamos el dato en un campo en especifico
@type function
@author luis.aboytes
@since 18/7/2024
/*/
Static Function SaveData(oJsonData)
Local nCont as Numeric
For nCont := 1 to LEN(oJsonData['titulos'])
SE1->(dbSetOrder(1))
SE1->(dbSeek(xFilial("SE1")+PADR(oJsonData['titulos'][nCont]['prefix'],GetSx3Cache("E1_PREFIXO","X3_TAMANHO"))+PADR(oJsonData['titulos'][nCont]['billnumber'],GetSx3Cache("E1_NUM","X3_TAMANHO"))+PADR(oJsonData['titulos'][nCont]['installment'],GetSx3Cache("E1_PARCELA","X3_TAMANHO"))))
RecLock("SE1",.F.)
SE1->E1_XPERDES := oJsonData['titulos'][nCont]['percentDiscount']
MsUnLock()
Next
Return
/*/{Protheus.doc} GetLimite
Se retorna el limite de descuento
@type function
@author luis.aboytes
@since 18/7/2024
/*/
Static Function GetLimite(oJsonData)
Local nTope := 0
SE1->(dbSetOrder(1))
SE1->(dbSeek(xFilial("SE1")+PADR(oJsonData['prefix'],GetSx3Cache("E1_PREFIXO","X3_TAMANHO"))+PADR(oJsonData['billnumber'],GetSx3Cache("E1_NUM","X3_TAMANHO"))+PADR(oJsonData['installment'],GetSx3Cache("E1_PARCELA","X3_TAMANHO"))))
nTope:=IIF(VAZIO(Posicione("SE4", 1,xFilial("SE4")+SE1->E1_XCONDPP,"E4_XDESCON")),0,Posicione("SE4", 1,xFilial("SE4")+SE1->E1_XCONDPP,"E4_XDESCON"))
Return nTope
/*/{Protheus.doc} isDisable
Funcion que retorna si es editable o no el valor del input
@type function
@author luis.aboytes
@since 18/7/2024
/*/
Static Function isDisable(oJsonData)
Local lRet := .t.
SE1->(dbSetOrder(1))
SE1->(dbSeek(xFilial("SE1")+PADR(oJsonData['prefix'],GetSx3Cache("E1_PREFIXO","X3_TAMANHO"))+PADR(oJsonData['billnumber'],GetSx3Cache("E1_NUM","X3_TAMANHO"))+PADR(oJsonData['installment'],GetSx3Cache("E1_PARCELA","X3_TAMANHO"))))
IF SE1->E1_XCONDPP == '999'
lRet := .f.
ENDIF
Return lRet
Ejemplo donde el descuento se calcula con el valor ingresado en la moneda 1
#Include 'Protheus.ch'
User Function F998VALBX()
Local oResponse := JsonObject():New() as Object
IF PARAMIXB[2] //Vista
IF PARAMIXB[1]['recalculate']
oResponse:=setValues(PARAMIXB[1])
ELSE
oResponse['label'] := "% Descuento por pronto pago" //Nombre del campo nuevo a mostrar.
oResponse['readonlyDiscount'] := .T. //Deshabilitar o habilitar el campo de descuento.
oResponse['readonlyInterest'] := .T. //Deshabilitar o habilitar el campo de Intereses.
oResponse['readonlyFine'] := .T. //Deshabilitar o habilitar el campo de Multa.
oResponse['disabledInput'] := .F.//isDisable(PARAMIXB[1]) //Deshabilitar o habilitar el input nuevo
oResponse['maxDiscount'] := 50//GetLimite(PARAMIXB[1]) //Se informa el descuento permitido debe tener un rango mayor a 0 y menor a 100
oResponse['warningMaxDiscount'] := "Descuento por Pronto Pago mayor al permitido. En esta condición el tope es:"+ALLTRIM(STR(oResponse['maxDiscount'])) //Mensaje personalizado por si se excede el descuento permitido, si no se informa el front mandara uno por default
ENDIF
ELSE
SaveData(PARAMIXB[1]) //Grabación
ENDIF
Return oResponse
/*/{Protheus.doc} setValues
Esta funcion determina los valores a mostrar en los campos de descuento, residuo,multa e interes
@type function
@version
@author luis.aboytes
@since 25/7/2024
@param oJson, object, param_description
@return variant, return_description
/*/
Static Function setValues(oJson)
Local oRes := JsonObject():New()
// El cálculo se realiza con el dato ingresado en la moneda 1 o en cualquier moneda puede
// utilizar por ejemplo PARAMIXB[1]["coins"][1]['value'] para la moneda 1.
oRes['descuento'] := Round(PARAMIXB[1]['bind']['discountInput']/100*PARAMIXB[1]["coins"][1]['value'],2) //Calculo con el valor ingresado a pagar
oRes['residuo'] := Round(PARAMIXB[1]["coins"][1]['value']-oRes['descuento'],MsDecimais(SE1->E1_MOEDA)) //Calculo con el valor ingresado a pagar
oRes['multa'] := 0
oRes['interes'] := 0
Return oRes
/*/{Protheus.doc} SaveData
Guardamos el dato en un campo en especifico
@type function
@author luis.aboytes
@since 18/7/2024
/*/
Static Function SaveData(oJsonData)
Local nCont as Numeric
For nCont := 1 to LEN(oJsonData['titulos'])
SE1->(dbSetOrder(1))
SE1->(dbSeek(xFilial("SE1")+PADR(oJsonData['titulos'][nCont]['prefix'],GetSx3Cache("E1_PREFIXO","X3_TAMANHO"))+PADR(oJsonData['titulos'][nCont]['billnumber'],GetSx3Cache("E1_NUM","X3_TAMANHO"))+PADR(oJsonData['titulos'][nCont]['installment'],GetSx3Cache("E1_PARCELA","X3_TAMANHO"))))
RecLock("SE1",.F.)
SE1->E1_XPERDES := oJsonData['titulos'][nCont]['percentDiscount']
MsUnLock()
Next
Return
/*/{Protheus.doc} GetLimite
Se retorna el limite de descuento
@type function
@author luis.aboytes
@since 18/7/2024
/*/
Static Function GetLimite(oJsonData)
Local nTope := 0
SE1->(dbSetOrder(1))
SE1->(dbSeek(xFilial("SE1")+PADR(oJsonData['prefix'],GetSx3Cache("E1_PREFIXO","X3_TAMANHO"))+PADR(oJsonData['billnumber'],GetSx3Cache("E1_NUM","X3_TAMANHO"))+PADR(oJsonData['installment'],GetSx3Cache("E1_PARCELA","X3_TAMANHO"))))
nTope:=IIF(VAZIO(Posicione("SE4", 1,xFilial("SE4")+SE1->E1_XCONDPP,"E4_XDESCON")),0,Posicione("SE4", 1,xFilial("SE4")+SE1->E1_XCONDPP,"E4_XDESCON"))
Return nTope
/*/{Protheus.doc} isDisable
Funcion que retorna si es editable o no el valor del input
@type function
@author luis.aboytes
@since 18/7/2024
/*/
Static Function isDisable(oJsonData)
Local lRet := .t.
SE1->(dbSetOrder(1))
SE1->(dbSeek(xFilial("SE1")+PADR(oJsonData['prefix'],GetSx3Cache("E1_PREFIXO","X3_TAMANHO"))+PADR(oJsonData['billnumber'],GetSx3Cache("E1_NUM","X3_TAMANHO"))+PADR(oJsonData['installment'],GetSx3Cache("E1_PARCELA","X3_TAMANHO"))))
IF SE1->E1_XCONDPP == '999'
lRet := .f.
ENDIF
Return lRet
¡IMPORTANTE!
La presente solución aplica para versión 12.1.2210 o superior, siempre y cuando se tengan las rutinas actualizadas a la fecha indicada en la sección 01 - Datos Generales.
05. ASUNTOS RELACIONADOS