Académique Documents
Professionnel Documents
Culture Documents
INTRODUCCIÓN
¿Cómo podemos conseguir un arqueo de caja con Access?
El problema con que me enfrento al realizar este ejemplo es
que hay muchos sistemas diferentes de gestionar las ventas
y cobros (lo cual es bueno, porque se demuestra así vuestra
inventiva para “solucionar una necesidad”).
A eso hay que añadirle que quizá quien mire este ejemplo
sea español, que tiene sus monedas y billetes, pero
también puede mirarlo una persona de Argentina, o de
Perú, o de México... con otra moneda.
Dicho lo anterior veamos cómo podemos conseguir ese arqueo diario de caja.
La explico por el hecho de que quizá alguien quiera desarrollar antes este ejemplo para
“entenderlo” antes de aplicarlo a su BD.
1
Visítame en http://siliconproject.com.ar/neckkito/
Vamos a crearnos ahora una tabla, que llamaremos TArqueo, que nos servirá para ir
guardando los totales de los arqueos para ir guardando el saldo acumulado. La estructura de
esta tabla será la siguiente:
El campo [RegArq] nos servirá para regularizar el arqueo en el caso en que haya discrepancias
entre lo vendido y lo cobrado, y que no haya manera de cuadrar la caja. Teóricamente ese
valor debería ser cero, lo que implicaría que la caja siempre cuadra.
Si hay discrepancias ese valor servirá para regularizar el saldo de caja. Teóricamente un día
puede que nos falte dinero y otro que nos sobre, pero mirando su evolución en el tiempo
obtendríamos lo que se denomina “esperanza matemática tendente a cero”; es decir, que si
sumamos esas regularizaciones el resultado final debería ser, siempre en términos teóricos,
cero.
Nos servirá además para comprobar que no haya “cosas extrañas” en la caja, dado que si la
regularización muestra que siempre hay faltante de caja es que “algo” pasa...
Podríamos añadir los billetes y las monedas como campos en nuestra tabla, pero en este
ejemplo no lo haremos así porque considero que lo importante son los saldos finales, y no
aporta gran información saber que el día tal teníamos 2 billetes de xxx unidades monetarias.
…
Private Sub cmdAbreFArqueo_Click()
DoCmd.Close acForm, Me.Name
DoCmd.OpenForm "FArqueo", , , , acFormAdd
End Sub
…
La primera línea de código lo que hace es cerrar FMenu; la segunda abre FArqueo preparado ya
para la introducción de un nuevo registro.
2 Para generar código nos ponemos en la parte en blanco junto al evento que queramos, y veremos cómo nos aparece un pequeño
botón de puntos suspensivos. Si hacemos click sobre él nos aparecerá una ventana. Le decimos que queremos generar código.
Se nos abrirá el editor de VB, con dos líneas por defecto (Private Sub... y End Sub). Esas dos líneas no deben tocarse. El código lo
escribimos entre dichas líneas
2
Visítame en http://siliconproject.com.ar/neckkito/
PREPARANDO EL FORMULARIO DE ARQUEO
Para este ejemplo voy a suponer que nuestro hipotético país tiene:
txtXXX
txtTotalXX
X
3
Visítame en http://siliconproject.com.ar/neckkito/
de billetes y monedas.
Si habéis hecho copy-paste los nuevos controles habrán duplicado también las propiedades de
cada control. Si lo habéis hecho manual deberéis configurar las propiedades de los controles
como os indicaba en “Nº BILLETES DE 100”.
Evidentemente este textbox nos reflejará las salidas que se hayan producido en la caja por
conceptos tales como pequeños pagos de material, ingreso de una cantidad de caja en el
banco, etc.
Debemos también configurar algunas propiedades de nuestro formulario. Para ello sacamos
esas propiedades de formulario, nos vamos a Pestaña Formato y:
• Selectores de registro: NO
• Botones de desplazamiento: NO
• Cuadro de control: NO
No podemos limitarnos a salir al pulsarlo porque se habrán escrito valores en el formulario (lo
veremos en un código posterior). Si saliéramos sin más esos valores quedarían guardados, lo
que nos provocaría tener un registro incompleto (y, de rebote, nos fastidiaría las
comprobaciones del código).
4
Visítame en http://siliconproject.com.ar/neckkito/
Para solucionar este problema vamos a borrar el registro
antes de salir. Así nuestra tabla no tendrá información
fragmentada o errónea.
…
Private Sub cmdCancelar_Click()
'Establecemos un control de errores por si intentamos borrar un registro
vacío
On Error Resume Next
'Declaramos las variables
Dim resp As Integer
'Solicitamos confirmación al usuario
resp = MsgBox("Si cancela y sale se borrará la información del registro actual" _
& vbCrLf & vbCrLf & "¿Seguro que desea cancelar?", vbQuestion + vbYesNo, "CONFIRMACIÓN")
'Si el usuario pulsa NO salimos del proceso sin hacer nada
If resp = vbNo Then Exit Sub
'Si pulsa SÍ seguimos con el proceso
'Desactivamos los avisos
DoCmd.SetWarnings False
'Borrlamos el registro
DoCmd.RunCommand acCmdDeleteRecord
'Reactivamos los avisos
DoCmd.SetWarnings True
'Cerramos el formulario actual
DoCmd.Close acForm, Me.Name
'Abrimos FMenu
DoCmd.OpenForm "FMenu"
End Sub
…
Sigamos con el proceso. Un segundo frente es disponer de los valores iniciales para poder
realizar el arqueo. Esos valores iniciales los vamos a programar aprovechando el evento “Al
cargar” del formulario.
Lo que debemos tener en cuenta es que: a) nos ponga le fecha de hoy por defecto, aunque
después la podamos modificar, b) que nos traiga el último saldo del arqueo anterior y c) que
nos traiga la suma de las ventas realizadas ese día (esto lo haremos a través de una función
en un módulo, que veremos más adelante).
Así pues, vamos a sacar las propiedades del formulario y en el evento “Al cargar” vamos a
generar el siguiente código:
…
Private Sub Form_Load()
'Requiere registro de la referencia "Microsoft DAO 3.6 Object Library o módulo equivalente
'Declaramos las variables
Dim vFecha As Date
Dim vUltimoSaldo As Currency
Dim vVentas As Currency
Dim miSql As String
Dim rst As DAO.Recordset
'Asignamos la fecha del sistema a la variable vFecha
vFecha = Date
5
Visítame en http://siliconproject.com.ar/neckkito/
'Escribimos dicho valor en el campo correspondiente del formulario
Me.FechaArq.Value = vFecha
'Nos creamos una SQL que nos filtrará por el último saldo del arqueo anterior,
'representado por el valor del campo [SaldoCaja]
miSql = "SELECT TOP 1 TArqueo.SaldoCaja FROM TArqueo" _
& " ORDER BY TArqueo.FechaArq DESC"
'Creamos un recordset sobre dicha SQL
Set rst = CurrentDb.OpenRecordset(miSql)
'Si la consulta devuelve un valor nulo es porque aún no se ha realizado
'ningún arqueo. En este caso establecemos vUltimoSaldo como cero
If rst.RecordCount = 0 Then
vUltimoSaldo = 0
Else
'En caso contrario le asignamos el valor que devuelve la SQL
vUltimoSaldo = Nz(rst.Fields(0).Value, 0)
End If
'Escribimos ese valor en el formulario
Me.txtSaldoAnterior.Value = vUltimoSaldo
'Llamamos a la función fncVentas para que nos traiga las ventas de esa fecha
vVentas = fncVentas(vFecha)
'Escribimos las ventas en el formulario
Me.txtTotalVentas.Value = vVentas
'Cerramos conexiones y liberamos memoria
rst.Close
Set rst = Nothing
End Sub
…
Vemos que el código requiere el registro de la referencia “Microsoft DAO 3.6 Object Library”. Si
no sabemos cómo se registra una referencia lo que tenemos que hacer, en el editor de VB, es
irnos a Menú → Herramientas → Referencias... Nos saldrá una ventana con las referencias que
tenemos disponibles. Buscamos “Microsoft DAO 3.6 Object Library”, marcamos su check y
aceptamos.
Como ya os comentaba antes, el total de ventas de ese día nos vendrá dado por una función,
que programaremos en un módulo aparte.
Vamos a escribir primero la función que nos devolverá las ventas diarias. El código que nos
haría eso sería.
…
Public Function fncVentas(ByVal laFecha As Date) As Currency
'Declaramos las variables
Dim miSql As String
Dim rstVtas As DAO.Recordset
'Creamos la SQL que nos dará el total de ventas
miSql = "SELECT SUMA(TVentas.Importe * TVentas.Unidades) AS TotalVenta FROM TVentas" _
& " WHERE TVentas.FechaVta=#" & Format(laFecha, "mm/dd/yy") & "#"
'Creamos el recordset sobre la consulta
Set rstVtas = CurrentDb.OpenRecordset(miSql)
'Si no hay registros es que no ha habido ventas ese día. Hacemos que la función
'sea cero en este caso
If rstVtas.RecordCount = 0 Then
fncVentas = 0
Else
'Si ha habido ventas igualamos la función al resultado devuelvto por la SQL
fncVentas = rstVtas.Fields(0).Value
6
Visítame en http://siliconproject.com.ar/neckkito/
End If
'Cerramos conexiones y liberamos memoria
rstVtas.Close
Set rstVtas = Nothing
End Function
…
…
Public Sub calculoDatos( _
fSaldoAnterior As Currency, _
fBilletes100 As Currency, _
fBilletes10 As Currency, _
fMonedas5 As Currency, _
fMonedas1 As Currency, _
fVales As Currency, _
fSalidas As Currency, _
fRegulariz As Currency, _
fTotalVentas As Currency _
)
'Declaramos las variables
Dim vSaldoCaja As Currency
Dim vDif As Currency
'Realizamos el cálculo para determinar el saldo de caja
vSaldoCaja = fSaldoAnterior + fBilletes100 + fBilletes10 + fMonedas5 + fMonedas1 _
- fVales - fSalidas + fRegulariz
'Realizamos el cálculo para determinar la diferencia del arqueo
vDif = vSaldoCaja - fTotalVentas
'Escribimos esos valores en el formulario
With Forms!FArqueo
.SaldoCaja.Value = vSaldoCaja
.txtDiferencia.Value = vDif
End With
End Sub
…
Siguiente frente: la fecha de arqueo. Ya hemos visto que, por defecto, se nos escribirá la fecha
actual, pero podemos modificarla si nos interesa. Pero ello conlleva ciertos peligros. Vamos a
ver cómo podemos controlar dichos “peligros” a través de código.
…
Private Sub FechaArq_AfterUpdate()
'Declaramos las variables
Dim vFecha As Variant
Dim miSql As String
7
Visítame en http://siliconproject.com.ar/neckkito/
Dim vFechaUlt As Date
Dim rstFech As DAO.Recordset
'Cogemos el valor de la fecha introducida
vFecha = Nz(Me.FechaArq.Value, 0)
'Si vFecha es igual a cero es que el campo está en blanco. Lanzamos
un mensaje,
'volvemos a poner la fecha de hoy, recalculamos las ventas y salimos
If vFecha = 0 Then
MsgBox "La fecha no puede quedar en blanco", vbCritical, "AVISO"
Me.FechaArq.Value = Date
Me.txtTotalVentas.Value = fncVentas(Date)
Exit Sub
End If
'Si la fecha es superior al día actual avisamos de que eso no es
posible,
'volvemos a poner la fecha de hoy, recalculamos las ventas y salimos
If vFecha > Date Then
MsgBox "No puede introducir una fecha superior al día de hoy", vbCritical, "AVISO"
Me.FechaArq.Value = Date
Me.txtTotalVentas.Value = fncVentas(Date)
Exit Sub
End If
'Si la fecha es anterior al día de hoy comprobamos que no pueda ser igual o
'inferior a la fecha del último arqueo
'Creamos la SQL que nos dará esta información
miSql = "SELECT TArqueo.Id FROM TArqueo" _
& " WHERE TArqueo.FechaArq>#" & Format(vFecha, "mm/dd/yy") & "#"
'Creamos el recordset sobre la SQL
Set rstFech = CurrentDb.OpenRecordset(miSql)
'Si la consulta no devuelve ningún valor es que todo es correcto
If rstFech.RecordCount = 0 Then
'Correcto. No hago nada
Else
'Si devuelve valores es que ya existe una fecha igual o superior a la
'introducida. Avisamos, situamos la fecha en el día de hoy, recalculamos
'las ventas y salimos
MsgBox "La fecha introducida no es correcta. No puede ser menor o igual" _
& " a la fecha del último arqueo", vbCritical, "FECHA ERRÓNEA"
Me.FechaArq.Value = Date
Me.txtTotalVentas.Value = fncVentas(Date)
GoTo Salida
End If
'Como si hemos llegado hasta aquí es que la fecha es correcta recalculamos
'las ventas del día seleccionado
Me.txtTotalVentas.Value = fncVentas(vFecha)
Salida:
'Cerramos conexiones y liberamos memoria
rstFech.Close
Set rstFech = Nothing
End Sub
…
Otro punto menos. Vamos a por las actualizaciones de la información en el formulario. Antes
de que una vez más parezca que es un proceso complicadísimo os puedo decir que, en
realidad, se trata de un simple copy-paste una vez hayamos escrito la primera línea de código.
Como mecánica común os diré que el código debe ir en el evento “Después de actualizar” de
todos los controles en los que debamos escribir datos.
Empecemos: el usuario empezará a escribir el número de billetes de 100 que hay en la caja.
Luego lo que debemos hacer es sacar el evento antes citado (“Después de actualizar”) del
control txt100 y generar el siguiente código:
8
Visítame en http://siliconproject.com.ar/neckkito/
…
Private Sub txt100_AfterUpdate()
'Declaramos las variables
Dim vTotal As Currency
'Como son billetes de 100 deberemos multiplicar por
100 las unidades
vTotal = Nz(Me.txt100.Value, 0) * 100
'Asignamos el resultado a txtTotal100
Me.txtTotal100.Value = vTotal
'Recalculamos los resultados
Call calculoDatos( _
Me.txtSaldoAnterior.Value, _
Me.txtTotal100.Value, _
Me.txtTotal10.Value, _
Me.txtTotal5, _
Me.txtTotal1.Value, _
Me.txtValesCaja.Value, _
Me.txtSalidasCaja.Value, _
Me.RegArq.Value, _
Me.txtTotalVentas)
End Sub
…
Como veis, la llamada a la función lo que hace es pasarle toda la información al procedimiento
para que nos recalcule los datos en función de la nueva información introducida.
…
Private Sub txt10_AfterUpdate()
'Declaramos las variables
Dim vTotal As Currency
'Como son billetes de 10 deberemos multiplicar por 10 las unidades
vTotal = Nz(Me.txt10.Value, 0) * 10
'Asignamos el resultado a txtTotal10
Me.txtTotal10.Value = vTotal
'Recalculamos los resultados
Call calculoDatos( _
Me.txtSaldoAnterior.Value, _
Me.txtTotal100.Value, _
Me.txtTotal10.Value, _
Me.txtTotal5, _
Me.txtTotal1.Value, _
Me.txtValesCaja.Value, _
Me.txtSalidasCaja.Value, _
Me.RegArq.Value, _
Me.txtTotalVentas)
End Sub
…
Como veis, me he limitado a hacer un copy-paste del anterior código, adaptándolo a los
valores 10 en lugar de 100.
9
Visítame en http://siliconproject.com.ar/neckkito/
…
Private Sub txt5_AfterUpdate()
'Declaramos las variables
Dim vTotal As Currency
'Como son monedas de 5 deberemos multiplicar por 5
las unidades
vTotal = Nz(Me.txt5.Value, 0) * 5
'Asignamos el resultado a txtTotal5
Me.txtTotal5.Value = vTotal
'Recalculamos los resultados
Call calculoDatos( _
Me.txtSaldoAnterior.Value, _
Me.txtTotal100.Value, _
Me.txtTotal10.Value, _
Me.txtTotal5, _
Me.txtTotal1.Value, _
Me.txtValesCaja.Value, _
Me.txtSalidasCaja.Value, _
Me.RegArq.Value, _
Me.txtTotalVentas)
End Sub
…
…
Private Sub txt1_AfterUpdate()
'Asignamos el resultado a txtTotal1
Me.txtTotal1.Value = Me.txt1.Value
'Recalculamos los resultados
Call calculoDatos( _
Me.txtSaldoAnterior.Value, _
Me.txtTotal100.Value, _
Me.txtTotal10.Value, _
Me.txtTotal5, _
Me.txtTotal1.Value, _
Me.txtValesCaja.Value, _
Me.txtSalidasCaja.Value, _
Me.RegArq.Value, _
Me.txtTotalVentas)
End Sub
…
Como las monedas son de 1 u.m no necesito multiplicar, por lo que igualo valores
directamente.
…
Private Sub txtValesCaja_AfterUpdate()
If IsNull(Me.txtValesCaja.Value) Then
Me.txtValesCaja.Value = 0
10
Visítame en http://siliconproject.com.ar/neckkito/
End If
Call calculoDatos( _
Me.txtSaldoAnterior.Value, _
Me.txtTotal100.Value, _
Me.txtTotal10.Value, _
Me.txtTotal5, _
Me.txtTotal1.Value, _
Me.txtValesCaja.Value, _
Me.txtSalidasCaja.Value, _
Me.RegArq.Value, _
Me.txtTotalVentas)
End Sub
…
…
Private Sub txtSalidasCaja_AfterUpdate()
If IsNull(Me.txtSalidasCaja.Value) Then
Me.txtSalidasCaja.Value = 0
End If
Call calculoDatos( _
Me.txtSaldoAnterior.Value, _
Me.txtTotal100.Value, _
Me.txtTotal10.Value, _
Me.txtTotal5, _
Me.txtTotal1.Value, _
Me.txtValesCaja.Value, _
Me.txtSalidasCaja.Value, _
Me.RegArq.Value, _
Me.txtTotalVentas)
End Sub
…
Y el control RegArq:
…
Private Sub RegArq_AfterUpdate()
If IsNull(Me.RegArq.Value) Then
Me.RegArq.Value = 0
End If
Call calculoDatos( _
Me.txtSaldoAnterior.Value, _
Me.txtTotal100.Value, _
Me.txtTotal10.Value, _
Me.txtTotal5, _
Me.txtTotal1.Value, _
Me.txtValesCaja.Value, _
Me.txtSalidasCaja.Value, _
Me.RegArq.Value, _
Me.txtTotalVentas)
End Sub
…
Y ya casi acabamos. Sólo nos falta programar el botón cmdConfirmaArqueo. Lo que haremos
con este botón, antes de salir, es comprobar que el control txtDiferencia está a cero, lo que
11
Visítame en http://siliconproject.com.ar/neckkito/
implica que la caja está cuadrada. Si no está a cero avisaremos y no dejaremos salir hasta que
se cuadre.
…
Private Sub cmdConfirmaArqueo_Click()
'Declaramos las variables
Dim vDif As Currency
'Cogemos el valor de la diferencia
vDif = Me.txtDiferencia.Value
'Si la diferencia no es cero no dejamos salir
If vDif <> 0 Then
MsgBox "El arqueo no está cuadrado. Existe una diferencia", vbExclamation, "SIN
CUADRAR"
Exit Sub
End If
'Si la diferencia ha sido cero salimos
DoCmd.Close acForm, Me.Name
DoCmd.OpenForm "FMenu"
End Sub
…
Sólo me queda desear que os pueda ser útil y que hayáis captado la mecánica para adaptarlo
sin dificultad a vuestras aplicaciones.
Un saludo y...
¡suerte!
12
Visítame en http://siliconproject.com.ar/neckkito/