Javascript Algorithm for Linked Fifo for inventory allocation

Viewed 15

I have a Fifo kind of problem, which I solved already but I feel like my implementation is very basic and could definitely learn a better way to do this. Given 2 array of objects, one for the sales that my customers are demanding, and one for the purchases that I'm doing to my inventory provider, I need to be able to allocate the orders and know when I will be able to satisfy them. I will only work with one product, to make it simpler. I'm very new at coding this case of problems, so I would really apreciate a point in the right direction. Maybe there's a data structure that I haven't used that could help here.

Properties: 'created': when the sales order was created 'quantity': how many items the customer wants

const salesOrders = [{
  'id': 'S1',
  'created': '2020-01-02',
  'quantity': 6
}, {
  'id': 'S2',  
  'created': '2020-11-05',
  'quantity': 2  
}, {
  'id': 'S3',  
  'created': '2019-12-04',
  'quantity': 3  
}, {
  'id': 'S4',  
  'created': '2020-01-20',
  'quantity': 2  
}, {
  'id': 'S5',  
  'created': '2019-12-15',
  'quantity': 9  
}];

Properties: 'receiving': when we expect to receive the product 'quantity': how many we will be receiving

const purchaseOrders = [{
  'id': 'P1',  
  'receiving': '2020-01-04',
  'quantity': 4
}, {
  'id': 'P2',  
  'receiving': '2020-01-05',
  'quantity': 3  
}, {
  'id': 'P3',  
  'receiving': '2020-02-01',
  'quantity': 5  
}, {
  'id': 'P4',  
  'receiving': '2020-03-05',
  'quantity': 1  
}, {
  'id': 'P5',  
  'receiving': '2020-02-20',
  'quantity': 7
}];

My code so far. I'm returnign an array that for reach sales, it shows when I will be able to satisfy it. The problem that I'm running with the current implementation is that I cannot cover all the cases.

function allocate(salesOrders, purchaseOrders) {
    //ordering sales and purchases by date
    const orderedSales = salesOrders.sort((a, b) => a.created.localeCompare(b.created));
    const orderedPurchases = purchaseOrders.sort((a, b) => a.receiving.localeCompare(b.receiving));

    console.log(orderedSales)
    console.log(orderedPurchases)
    let stock = 0;
    const result = [];
    purchaseIndex = 0;
    orderedSales.forEach((sale, index) => {
        const order = orderedPurchases[purchaseIndex];
        if (order) {
            console.log("Processing order", sale.id)
            console.log(`Leftover stock = ${stock}`)
            stock += order.quantity
            console.log(`new stock = ${stock}`)
            stock = stock - sale.quantity;
            console.log(`Sustracting = ${sale.quantity}`)
            console.log(`Remaining = ${stock}`)
            while (stock < 0) {
                purchaseIndex++
                console.log(`Demand NOT satified, moving to next purchase order with index ${purchaseIndex}`)

                stock += order.quantity
                console.log(`Current stock = ${stock}`)
                increaseOrder = false;
            }

            //demand has been satisfied
            console.log(`Demand for ${sale.id} was satified with purchase ${order.id}, time is ${order.receiving}, moving to next purchase order`)
            result.push({
                id: sale.id,
                availabilityDate: order.receiving
            })
            purchaseIndex++

            console.log("Next sale ++++++++")
            console.log(" ++++++++")
        }
    });

    console.log(result);
}

allocate(salesOrders, purchaseOrders)

1 Answers

I think your approach is mostly ok. I would just have some remarks/questions:

  • is it ok to always have stock start at 0? They are never left over? According to me, it should be one of the parameters of the allocate function
  • you should handle the case where the purchases are not enough to satisfy the sales (which is the case in your example data set) => with your current code your while loop could go over the max allowed index of the purchases array and throw an exception
  • if a sale can be satisfied before its creation date, should the availability date be before the creation date or should it be forced to the creation date? (if sales are always in the past and purchases in the future, this question doesn't make sense)

Here is how I would tackle the problem:

console.log(allocate(salesOrders, purchaseOrders));

function allocate(_sales, _purchases) {
  const sales = structureData(_sales, "created");
  const purchases = structureData(_purchases, "receiving");
  const LAST_PURCHASE_INDEX = purchases.length - 1;

  let stock = 0; // in real life, maybe this should be an input as well since it might not always start from 0?
  let availabilityDate = sales[0].date; // timestamp of stock availability, initialized to first sale timestamp
  let availabilityDateString = sales[0].created; // date in string format of stock availability, initialized to first sale created date 

  let purchaseIndex = 0; // index of the next purchase to process

  const result = [];

  // loop on sales
  for (let sale of sales) {
    const requiredQuantity = sale.quantity;
    const saleId = sale.id;

    // As long as we don't have enough stock, process the next purchase if there is any
    while (stock < requiredQuantity && purchaseIndex <= LAST_PURCHASE_INDEX) {
      const purchase = purchases[purchaseIndex];
      stock += purchase.quantity;
      availabilityDate = purchase.date;
      availabilityDateString = purchase.receiving;
      purchaseIndex++;
    }

    if (stock >= requiredQuantity) { // we have enough stock and push the availability date
      result.push({
        id: saleId,
        availabilityDate:
          availabilityDate > sale.date ? availabilityDateString : sale.created, // It could be simplified to availabilityDate if it's ok to have an availabilityDate before the sales creation
      });
      stock -= sale.quantity;
    } else { // we don't have enough stock and there are no purchases left, so we need more purchases
      result.push({ id: saleId , availabilityDateString: "Not anytime soon, need more purchases"});
    }
  }

  return result;
}

// utils to sort orders and add a date timesteamp for easier date comparison
function structureData(orders, dateField) {
  return orders
    .map((order) => ({ ...order, date: new Date(order[dateField]).getTime() }))
    .sort((o1, o2) => o1.date - o2.date);
}

Related