mongoosejs / mongoose-double

Provides Double support for mongoose.js
MIT License
18 stars 13 forks source link

Fail casting to Double while updating document #14

Open leandrocaplan opened 2 years ago

leandrocaplan commented 2 years ago

Hi there! Greetings form Argentina. First of all, I'm not a native english speaker. So, if there's something in the post that it isn't clear or I'm making some grammar mistakes, please let me know.

I'm developing a very simple app for the college (UTN, spanish acronym for "National Technological University"), which consist of a supermarket administration system. The app works for, among another things, control the stock of the products sold in the store, and also for keeping a very basic accounting of it. Here, we have only three ledger accounts (instead of hundreds) , which are updated when we make a sell or a purchase. A sale (to a client), increases the value of the ledger account associated with the payment form (cash, debit card or credit card), at once decreases the stock of the sold products, while a purchase (to providers), decreases the value of a ledger account, at once increases the stock of purchased products. Also, the system allows to deposit or withdraw money from an account, without doing any sale or purchase.

The issue is this one: I've created a MongoDB collection named "cuentas" ("accounts" in spanish), which has only one document. This document has an _id field of String type with the value "unico" (means "only one" in spanish), and another three fields. Each one of this fields corresponds to a ledger account ("caja" means "cash", "cuentaCorriente" means "current account", an "creditoAFavor" means "credit in favor"). Of course, the type of these three fields must be Double, since we're handling monetary amounts. When we make a sale, a purchase, or just depositing or withdrawing money, the account associated with that operation should be updated. Since the collection must have only one document with a fixed size, I thought it would be a good idea to make the collection capped. I've used moongose-double to define the schema on the model of "Cuentas". So on, when you run the app for the first time (having initially an empty database), a script is executed, which create the collection and initialize all values of the accounts in 0. When I open the database with MongoDB Compass, I can see all that values in 0, with type "Double", which is fine. Then, when I deposit (or withdraw) money in an account, if the updated value is not an integer, stills works fine, since I still get a Double type in Compass in that field. However, if I make an operation with some account that result in an integer value (example: previously I had $ 2.4 on the account, and I deposit $ 0.6 in it, resulting in a value of $3), when I check MongoDB Compass, I can see that the value has been casted to Int32. If the collection it's not capped, this is not a big issue, since I can operate between both types without any problem. However, if the collection is capped, that undesired cast would be changing the size of the document, which is not allowed in a capped collection. By the way, it doesn't seem good to me to the app to make that cast to Int32, since the purpose of mongoose-double is just avoid it, storing all those values with type Double, regardless they're integer or not. I just don't understand why, when I initialize the ledger accounts with value 0, the app cast them automatically to Double, but when I make an operation which results in an integer value, doesn't do it.

Here's my code, with some lines commented which I've tried but didn't work:

modelo-cuentas.js (model):


const mongoose = require('mongoose');
require('@mongoosejs/double');
const Schema = mongoose.Schema;

const cuentasSchema = new Schema(
{
    _id: { type: String,required: true, unique: true, trim: true, minlength: 1},
    caja: { type: mongoose.Schema.Types.Double, required: true },
    cuentaCorriente: { type: mongoose.Schema.Types.Double, required: true },
    creditoAFavor: { type: mongoose.Schema.Types.Double, required: true },

},
{
    //capped: { max: 1, size: 1024 }
});

const Cuentas = mongoose.model('Cuentas', cuentasSchema);

module.exports = Cuentas;

inicializarDb.js (required on server.js, "primeraEjecucion" called into connection.once, here is where I initialize all accounts in 0):


let Usuario = require('./modelos/modelo.usuario');
let Cuentas = require('./modelos/modelo.cuentas');

const primeraEjecucion = function(){

    //...

    Cuentas.findById("unico")
    .then(cuentas =>{

            if(!cuentas){

          const _id = "unico";
          const caja = 0;
          const cuentaCorriente = 0;
          const creditoAFavor = 0;

          const inicialCuentas = new Cuentas({
            _id,
            caja,
            cuentaCorriente,
            creditoAFavor,

          });
          console.log("Inicializo las cuentas en 0");
          console.log(inicialCuentas);
          inicialCuentas.save();
        }
    })
     .catch(err => { console.log(err);return res.status(400).json('Error: ' + err)});

};

exports.primeraEjecucion = primeraEjecucion;

cuentas.js (express routing for handling accounts, here is where I update the fields, by adding the amount (monto) defined in frontend, to the previous value of the account):

const router = require('express').Router();
const mongoose = require('mongoose');
require('@mongoosejs/double');
const Schema = mongoose.Schema;
const Double = require('@mongoosejs/double');

let Cuentas = require('../modelos/modelo.cuentas');

//Devuelvo el estado de las cuentas
router.route('/').get((req, res) => {
  Cuentas.findById("unico")
    .then(cuentas =>{ 
      return res.json(cuentas);
    })
    .catch(err => {return res.status(400).json('Error: ' + err)});
});

//Ingreso o retiro dinero de una cuenta
router.route('/update/').post((req, res) => {
    //Busco el único documento contenido en la colección Cuentas
    Cuentas.findById("unico")
    //Si lo encuentro, entro al then
    .then(cuentas => {

        cuentas[req.body.cuenta] +=  Number(req.body.monto);
        console.log(cuentas);
       //cuentas= new Cuentas(cuentas);
       //cuentas.overwrite(cuentas);
       cuentas.save()

        .then(() => {
                    console.log("Entro al then de save");
                    return res.json('¡Cuenta actualizada!');

                })
                .catch(err => {console.log("Catch de save" + err); return res.status(400).json('Error: ' + err);});

          })
        .catch(err => {console.log(err);return res.status(400).json('Error: ' + err);});
});

module.exports = router;

Here are some screenshots:

MongoDB Compass view, just after value initialization (all account fields in 0): Inicializaion

Depositing $ 1,3 in "Caja": ValorReal

MongoDB Compass view, after that deposit (0 + 1.3 = 1.3), not integer result: CompassReal

Depositing $ 0,7 in "Caja": ValorEntero

MongoDB Compass view, after that deposit (1.3 + 0,7 = 2), integer result: CompassEntero

We can see, in the last screenshot, mongoose casted the value to Int32, what is undesirable

I've been wondering, why if the "console.log(inicialCuentas)" line in "inicializarDb.js", has the the same format at the output to the "console.log(cuentas)" in "cuentas.js", I'm getting different results on both files.

Does anyone have an idea of what I'm doing wrong? I wonder if the code I'm posting here is enough to explain the problem or if more details are needed.

Thanks a lot!

guptaashwanee commented 1 year ago

Hi, This issue is still there, I am using this mongodb for my C++ IOT application with Node server. While creating a new document this saves as Double, but while updating it save as Int32. I think we need to find another way, please let me know if you got any fix for the same.