Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate Duplicate Object ID #9386

Closed
surajpra opened this issue Sep 4, 2020 · 3 comments
Closed

Generate Duplicate Object ID #9386

surajpra opened this issue Sep 4, 2020 · 3 comments
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation
Milestone

Comments

@surajpra
Copy link

surajpra commented Sep 4, 2020

Do you want to request a feature or report a bug?
Bug
What is the current behavior?
Generate duplicate object id
If the current behavior is a bug, please provide the steps to reproduce.

let updateInventory = {
	'$push': { 'batches.$.committed' : { 'sales':orderId, 'item':lineItemId, 'quantity': item.quantity } },
	'batches.$.committed_stock': item.quantity
}
await Item.findOneAndUpdate({ '_id': itemId, 'batches._id': item.batch }, updateInventory, { new: true }, function(err, data) {
		if (err) {
            let error = __errorCodes.DATABASE_ERROR;
            error.errors = process.env.NODE_ENV === 'production' ? undefined : err.stack;
            console.timeEnd("dbsave");
            return res.status(error.status_code).json(error)
        } else {
        	console.timeEnd("dbsave");
            return res.json({
                status: "success",
                data: data
            });
        }
});

image

What is the expected behavior?
Unique Object ID
What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.
Node.js: 12.18.3
Mongoose: 5.10.2
MongoDB : 4.4.0Enterprise

@vkarpov15
Copy link
Collaborator

What does the schema for the Item model look like?

@vkarpov15 vkarpov15 added the needs clarification This issue doesn't have enough information to be actionable. Close after 14 days of inactivity label Sep 7, 2020
@surajpra
Copy link
Author

surajpra commented Sep 8, 2020

const itemSchema = mongoose.Schema({
    name: { trim: true, type: String, required: [true, 'name is required.'], maxLength: [100, 'length must be equal to or less than 100'] },
    sku: { trim: true, type: String, unique: [true, 'sku already exists'], sparse: true, default: null, maxLength: [100, 'length must be equal to or less than 100'] },
    brand: { trim: true, type: Schema.Types.ObjectId, ref:'brands', default: null },
    manufacturer: { trim: true, type: Schema.Types.ObjectId, ref:'manufacturers', default: null },
    category: { trim: true, type: Schema.Types.ObjectId, ref:'item-categories', default: null },
    source: { trim: true, type: String, enum: ['internal', 'api'], default: 'internal', required: [true, 'source is required.'] },
    accounting: { trim: true, type: String, enum: ['fifo', 'fefo', 'none'], default: 'none', required: [true, 'accounting is required.'] },
    item_type: { trim: true, type: String, enum: ['single', 'composite'], default: 'single', required: [true, 'item type is required.'] },
    inventory_type: { trim: true, type: String, enum: ['inventory', 'non-inventory', 'non-inventory-sales', 'non-inventory-purchase'], default: 'inventory', required: [true, 'inventory type is required.'] },
    unit: {
        type: Schema.Types.ObjectId, 
        ref: 'uoms', 
        trim: true,
        default: null,
        validate: {
            validator: function(value) {
                let unit = value.toString();
                return (this.product_type === 'goods' && validator.isEmpty(unit)) ? false : true; 
            },
            message: props => 'unit is required.'
        }
    },
    mapped_items:[{
        item: { trim: true, type: Schema.Types.ObjectId, ref:'items', default: null },
        initial_stock: { trim: true, type: Number, default: '0' },
        stock_on_hand: { trim: true, type: Number, default: '0' },
        incomming_stock: { trim: true, type: Number, default: '0' },
        committed_stock: { trim: true, type: Number, default: '0' },
        available_stock: { trim: true, type: Number, default: '0' },
    }],
    description: { trim: true, type: String, default: null, maxLength: [255, 'length must be equal to or less than 255'] },
    rate: { trim: true, type: Number, default: '0', required: [true, 'selling price is required.'] },
    documents: [{
        path: { trim: true, type: String, default: null, maxLength: [255, 'length must be equal to or less than 255'] },
    }],
    images: [{
        featured: { trim: true, type: Boolean, default: false },
        path: { trim: true, type: String, default: null, maxLength: [255, 'length must be equal to or less than 255'] },
    }],
    tax: { trim: true, type: Schema.Types.ObjectId, ref:'taxes', default: null },
    purchase_description: { trim: true, type: String, default: null, maxLength: [255, 'length must be equal to or less than 255'] },
    purchase_rate: { trim: true, type: Number, default: '0', required: [true, 'cost price is required.'] },
    product_type: { trim: true, type: String, enum: ['goods', 'service'], default: 'goods' },
    is_returnable: { trim: true, type: Boolean, default: false },
    confirmed: { trim: true, type: Boolean, default: true },
    reorder_level: { trim: true, type: Number, default: '0' },
    initial_stock: { trim: true, type: Number, default: '0' },
    supplier: { trim: true, type: Schema.Types.ObjectId, ref:'contacts', default: null },
    asset_value: { trim: true, type: Number, default: '0' },
    over_stock: { trim: true, type: Number, default: '0' },
    stock_on_hand: { trim: true, type: Number, default: '0' },
    incomming_stock: { trim: true, type: Number, default: '0' },
    committed_stock: { trim: true, type: Number, default: '0' },
    available_stock: { trim: true, type: Number, default: '0' },
    upc: { trim: true, unique: [true, 'upc already exists'], sparse: true, type: String, default: null, maxLength: [100, 'length must be equal to or less than 100'] },
    ean: { trim: true, unique: [true, 'ean already exists'], sparse: true, type: String, default: null, maxLength: [100, 'length must be equal to or less than 100'] },
    isbn: { trim: true, unique: [true, 'isbn already exists'], sparse: true, type: String, default: null, maxLength: [100, 'length must be equal to or less than 100'] },
    part_number: { trim: true, type: String, default: null, maxLength: [100, 'length must be equal to or less than 100'] },
    batches: [{
        warehouse: { trim: true, type: Schema.Types.ObjectId, ref:'warehouses', required: [true, 'warehouse is required.'] },
        reference: { trim: true, type: String, unique: [true, 'batch already exists'], required: [true, 'batch is required.'], maxLength: [100, 'length must be equal to or less than 100'] }, // unique reference batch
        manufacturer: { trim: true, type: String, default: null, maxLength: [100, 'length must be equal to or less than 100'] }, //manufacturer batch
        sales: { trim: true, type: Schema.Types.ObjectId, ref: 'orders', default: null }, // sales order - in case of return
        transfer: { trim: true, type: Schema.Types.ObjectId, default: null }, // transfer order
        purchase: { trim: true, type: Schema.Types.ObjectId, default: null }, // purchase order
        adjustment: { trim: true, type: Schema.Types.ObjectId, ref: 'item-adjustments', default: null }, // adjustment
        location: { trim: true, type: Schema.Types.ObjectId, ref: 'locations', default: null },
        initial_stock: { trim: true, type: Number, default: '0' },
        stock_on_hand: { trim: true, type: Number, default: '0' },
        incomming_stock: { trim: true, type: Number, default: '0' },
        incomming: [{
            purchase: { type: Schema.Types.ObjectId, required: [true, 'purchase order is required.'] },
            quantity: { type: Number, trim: true, required: [true, 'quantity is required.'] },
            datetime: { type: Date, default: Date.now }
        }],
        committed_stock: { trim: true, type: Number, default: '0' },
        committed: [{
            sales: { type: Schema.Types.ObjectId, ref: 'orders', required: [true, 'sales order is required.'] },
            item: { type: Schema.Types.ObjectId, required: [true, 'order item is required.'] },
            quantity: { type: Number, trim: true, required: [true, 'quantity is required.'] },
            datetime: { type: Date, default: Date.now }
        }],
        available_stock: { trim: true, type: Number, default: '0' },
        expiry_date: { 
            trim: true,
            type: Date,
            default: null,
            get: formatDate
        },
        manufacturer_date: { type: Date, default: null },
        sales_channels: [{ trim: true, type: Schema.Types.ObjectId, ref: 'sales-channels', default: null }]
    }],
    history: [{
        description: { trim: true, type: String, default: null, maxLength: [255, 'length must be equal to or less than 255'] },
        user: { trim: true, type: Schema.Types.ObjectId, ref: 'users', required: [true, 'created user is required.'] },
        datetime: { type: Date, default: Date.now }
    }],
    package_details: {
        length: { trim: true, type: Number, default: null, maxLength: [10, 'length must be equal to or less than 10'] },
        width: { trim: true, type: Number, default: null, maxLength: [10, 'length must be equal to or less than 10'] },
        height: { trim: true, type: Number, default: null, maxLength: [10, 'length must be equal to or less than 10'] },
        weight: { trim: true, type: Number, default: null, maxLength: [10, 'length must be equal to or less than 10'] }
    },
    active: {
        status: { type: Boolean, default: false },
        datetime: { type: Date, default: null }
    },
    created: {
        user: { type: Schema.Types.ObjectId, ref: 'users', required: [true, 'created user is required.'] },
        datetime: { type: Date, default: Date.now }        
    }, 
    modified: {
        user: { type: Schema.Types.ObjectId, ref: 'users', default: null },
        datetime: { type: Date, default: null }           
    }
});

@vkarpov15 vkarpov15 added needs repro script Maybe a bug, but no repro script. The issue reporter should create a script that demos the issue and removed needs clarification This issue doesn't have enough information to be actionable. Close after 14 days of inactivity labels Sep 10, 2020
@vkarpov15 vkarpov15 modified the milestones: 5.10.5, 5.10.6 Sep 10, 2020
@vkarpov15
Copy link
Collaborator

The issue is that you're executing the same query twice. Because you're passing a callback and using await, your update executes twice and pushes the same data to batches.$.committed twice. Passing a callback to findOneAndUpdate() executes the query once, and then await triggers a 2nd execution.

This is why we recommend not mixing promises and callbacks.

We will add some more documentation about this for now. We're planning on making executing the same query twice an error in 6.0 with #7398.

@vkarpov15 vkarpov15 added docs This issue is due to a mistake or omission in the mongoosejs.com documentation and removed needs repro script Maybe a bug, but no repro script. The issue reporter should create a script that demos the issue labels Sep 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
docs This issue is due to a mistake or omission in the mongoosejs.com documentation
Projects
None yet
Development

No branches or pull requests

2 participants