Preventing over-purchasing products by managing Knex.js inventory levels

Currently, I am in the process of developing an online store for my school's guild organization.

I must admit that I lack experience working with databases and Knex.js is still a bit challenging for me.

An issue arises when multiple users simultaneously add items to their carts - sometimes resulting in more items being added than what is available in the database. It has led me to believe that I might not have fully grasped the concept of transactions and their functionality. Selling excess tickets or products due to this error could potentially put us in a difficult situation. Any guidance on how to handle this scenario better would be greatly appreciated.

The following code snippet exemplifies my attempt at incorporating the transaction:

 private async inventoryToCartTransaction(
    cart: sql.Cart,
    inventoryId: UUID,
    quantity: number = 1,
  ) {
    return this.knex.transaction(async (trx) => {
      const inventory = await trx<sql.ProductInventory>(TABLE.PRODUCT_INVENTORY)
        .where({ id: inventoryId }).first();
      if (!inventory) throw new Error(`Inventory with id ${inventoryId} not found`);
      if (inventory.quantity < quantity) throw new Error('Not enough inventory');
      // More code follows...
    });
  }

This is how the data is structured within the database:

export interface Product {
  id: UUID,
  name: string,
  description: string,
  SKU: string,
  price: number,
  image_url: string,
  max_per_user: number,
  category_id: UUID,
  created_at: Date,
  updated_at: Date,
  deleted_at?: Date,
}
// Additional data structure interfaces listed below...

Answer №1

It looks like the issue lies in your code: you deduct 1 from your inventory (

quantity: inventory.quantity - 1,
), but then you add quantity to the total cart quantity (
total_quantity: cart.total_quantity + quantity,
)

      await trx<sql.ProductInventory>(TABLE.PRODUCT_INVENTORY).where({ id: inventory.id }).update({
        // this is where it happens
        quantity: inventory.quantity - 1,
      });
      await trx<sql.Cart>(TABLE.CART).where({ id: cart.id }).update({
        total_price: cart.total_price + product.price,
        // and also here
        total_quantity: cart.total_quantity + quantity,
      });

Solution

      await trx<sql.ProductInventory>(TABLE.PRODUCT_INVENTORY).where({ id: inventory.id }).update({
        quantity: inventory.quantity - quantity,
      });
      await trx<sql.Cart>(TABLE.CART).where({ id: cart.id }).update({
        // Consider adding `* quantity` for accuracy
        total_price: cart.total_price + product.price * quantity,
        total_quantity: cart.total_quantity + quantity,
      });

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The data structure '{ variableName: string; }' cannot be directly assigned to a variable of type 'string'

When I see this error, it seems to make perfect sense based on what I am reading. However, the reason why I am getting it is still unclear to me. In the following example, myOtherVariable is a string and variableName should be too... Or at least that&apos ...

Is there a way to extract the HTMLElement[] type from the DOM?

In my TypeScript project, I am trying to gather all the top-level elements of a page using the code snippet below: const getHTMLElement() : HTMLElement[] { const doc = document.body.children; const list : HTMLElement[] = []; for (let c of Array.f ...

Using type as an argument in a hook in a similar fashion to how it is

My custom hook utilizes Zustand and is capable of storing various data types. However, I am looking to specify the type of data that will be stored similar to how it is done with the useState hook. import { Profile } from "@/types"; import { crea ...

Every time I clear the information, it seems to be instantly replaced with new data. How can I prevent it from constantly refilling?

When I press the remove button on my application, it successfully deletes the data in a field. However, it also automatically adds new data to the field which was not intended. I am seeking assistance on how to keep those fields empty after removing the ...

Include a string in every tuple

I am trying to define a new type: type myNewType = 'value-1' | 'value-2' | 'value-3' Is there a way to create another type like this? type myNewType2 = '1' | '2' | '3' However, I want the outpu ...

When compiling my TypeScript file, I encountered an error stating that a block-scoped variable cannot be redeclared

In my Visual Studio Code, I have written just one line of code in my ex1.ts file: let n: number = 10; Upon compiling using the command tsc ex1.ts, the compiler successfully generates the ex1.js file. However, VSC promptly displays an error in the .ts file ...

Issue encountered when attempting to run "ng test" in Angular (TypeScript) | Karma/Jasmine reports an AssertionError stating that Compilation cannot be undefined

My development setup includes Angular with TypeScript. Angular version: 15.1.0 Node version: 19.7.0 npm version: 9.5.1 However, I encountered an issue while running ng test: The error message displayed was as follows: ⠙ Generating browser application ...

Learn the process of sending a delete request to a REST API with Angular

Is there a way to effectively send a delete request to a REST API using Angular? I am attempting to send a delete request with an ID of 1 My current approach is as follows: this.http.delete(environment.apiUrl+"id="+1).subscribe(data => { }); The va ...

When using the .concat method on an array, props may display as unidentified

When I log the items array in my props, it contains items. However, when I try to add to the array using .concat, I encounter an error stating Cannot read property 'concat' of undefined export default (props) => { const { items } = props; ...

How can I use TypeScript to wrap a component in Vue 3?

Looking to customize a PrimeVue component (Calendar) by styling it differently and then re-exporting it. Here's an example in React: const WrappedCalendar: React.FC<CalendarProps> = (props)=> <div style={{background:'green'}}&g ...

Error encountered in Node.js OpenAI wrapper: BadRequestError (400) - The uploaded image must be in PNG format and cannot exceed 4 MB

Attempting to utilize the OpenAI Dall-e 2 to modify one of my images using the official Nodejs SDK. However, encountering an issue: This is the snippet of code: const image = fs.createReadStream(`./dist/lab/${interaction.user.id}.png`) const mask = fs.c ...

What is the simplest way to incorporate Vue with Typescript, without the need for a complex build setup?

I've been spending the last couple of days experimenting with my basic ASP.NET Core website set up for Typescript 2.9, but unfortunately, I haven't made much progress. My main goal is to keep the front-end simple, with just single Vue apps on eac ...

PostgreSQL is indicating that a relation is not present, even though the table itself does exist

I'm currently working on integrating my express app with a Postgres database. Below is the snippet of my code: var express = require('express'); var app = express(); var pg = require('pg').native; var connectionString = process.en ...

Implementing computed properties: A guide to incorporating type setting

I currently have two separate interfaces defined for Person and Dog. interface Person { name: string; weight: number; } interface Dog { name: string; mass: number } const specificAttribute = isDog ? 'mass' : 'weight'; ...

Change the type declaration of a list of elements to a list containing those elements organized within a container - Array<Wrapper<T>>

Is there a way to convert a plain array into an array of wrapped items in JavaScript? declare type MyGenericArray = [number, string, boolean] declare type WrappedGeneraicArray = Wrap<MyGenericArray> // WrappedGeneraicArr ...

The variable type 'editor.IStandaloneCodeEditor' does not match the parameter type 'monaco.editor.IStandaloneCodeEditor'

After installing the "monaco-editor" package using the command npm i monaco-editor, I encountered a type error in my source code. Can someone help me understand why this error is happening and how I can resolve it? This is the package file content: { "p ...

Countdown component in Ant Design failing to display correct date

I’m currently working on developing a specific date component using react in conjunction with antd. Below is the code snippet I am utilizing: import { Statistic, Col, Row } from 'antd'; const { Countdown } = Statistic; const deadline = Date.pa ...

Is it possible to refresh the webpage in Angular when the tab is clicked?

Can someone help me find a solution to reload an Angular app's page when the user selects the browser tab? I've been exploring using window.location.reload() for this purpose, but I need guidance on triggering it specifically when the tab is sel ...

The file is missing the required fields in the Firestore document

I've been facing a challenge while attempting to update specific fields within a firebase document. Even though the cloud function triggers and performs an upload on the document, the values of the fields I am trying to update never seem to get upload ...

Deciphering key-value pairs that are separated by commas

I am looking to convert the following format: realm="https://api.digitalocean.com/v2/registry/auth",service="registry.digitalocean.com",scope="registry:catalog:*" Into this JSON object: { realm: "https://api.digitaloce ...