The function sometimes skip some steps/expressions

I have made a logic that matches users whom have made requests to donors. I wrote the logic to accept partial donation. Lets say a user wants $5000 and there are two donors looking at give $3000 and $4000, it will take the $3000 and then $2000 from the second user to complete the receivers donation request.

The logic works a fine but the issue sometimes after matching 4k user and 3k user to our recipient it still accept other matches.

Heres my codebase:

async function fetchOpenRequestsAndTransactions(transactionRepository: TransactionRepository, currency: string) {
  const [openRequests, availableTransactions] = await Promise.all([
    transactionRepository.getOpenRequestsByOldest(currency),
    transactionRepository.getAvailableTransactionsByOldest(currency),
  ]);

  return [openRequests, availableTransactions];
}

function prepareUpdates(
  updatesRequests: any[],
  updatesTransactions: any[],
  request: IRequest,
  transaction: ITransaction,
  isRequestMatchComplete: boolean,
  isTxMatchComplete: boolean,
) {
  updatesRequests.push({
    id: request.id,
    matchedAmount: request.matchedAmount,
    isMatchComplete: isRequestMatchComplete,
    lastUpdatedAt: request.updatedAt,
  });

  updatesTransactions.push({
    id: transaction.id,
    unMatchedAmount: transaction.unMatchedAmount,
    isMatchComplete: isTxMatchComplete,
    lastUpdatedAt: transaction.updatedAt,
  });
}

async function handleDonationRequest(job, dbInstance) {
  const { currency } = job.data;

  // load dependencies
  await dependencyInjectorLoader(dbInstance);

  const transactionRepository = Container.get<TransactionRepository>(TransactionRepository);

  const [openRequests, availableTransactions] = await fetchOpenRequestsAndTransactions(transactionRepository, currency);

  const updatesRequests = [];
  const updatesTransactions = [];
  const matchesToCreate = [];

  for (let i = 0; i < openRequests.length; i++) {
    const request = openRequests[i] as IRequest;

    console.log('Processing request: ', i, 'Request ID:', request.id, 'Transactions: ', availableTransactions.length);

    let amountNeeded = request.amount - request.matchedAmount;
    let isRequestMatchComplete = false;

    for (const transaction of availableTransactions as ITransaction[]) {
      if (amountNeeded <= 0 || isRequestMatchComplete) break;

      let isTxMatchComplete = false;

      if (transaction.unMatchedAmount > 0) {
        const contribution = Math.min(amountNeeded, transaction.unMatchedAmount);

        request.matchedAmount += contribution;
        transaction.unMatchedAmount -= contribution;

        amountNeeded -= contribution;

        isRequestMatchComplete = amountNeeded === 0;
        isTxMatchComplete = transaction.unMatchedAmount === 0;

        prepareUpdates(
          updatesRequests,
          updatesTransactions,
          request,
          transaction,
          isRequestMatchComplete,
          isTxMatchComplete,
        );

        matchesToCreate.push({
          amount: contribution,
          currency: currency,
          request: request.id,
          transaction: transaction.id,
        });

        console.log(`Request ${request.id} received ${contribution} from transaction ${transaction.id}`);
      }
    }

    if (isRequestMatchComplete) {
      request.isMatchComplete = true;
    }
  }

  if(!matchesToCreate.length) return;
  await transactionRepository.startTxCall(async tx => {
    await Promise.all([
      transactionRepository.createMatches(tx, matchesToCreate),
      transactionRepository.batchUpdateRequests(tx, updatesRequests),
      transactionRepository.batchUpdateTransactions(tx, updatesTransactions),
    ]);
  });
}

The batch update calls all implement optimistic locking with the updatedAt field.

It behaves like the checks get skipped sometimes. This mostly happens when I have multiple people testing it