Newer
Older
Warehouse / src / SampleData / Samples / SampleTransactions.cs
@Derek Comartin Derek Comartin on 22 Aug 2023 2 KB Init
using MyWarehouse.Domain.Partners;
using MyWarehouse.Domain.Products;
using MyWarehouse.Domain.Transactions;

namespace MyWarehouse.TestData.Samples;

internal static class SampleTransactions
{
    // Determines the minimum & maximum number of transaction lines to include in each transaction.
    private const int MinTransactionLines = 1;
    private const int MaxTransactionLines = 8;
    
    // Determines the minimum & maximum number of products to include in each transaction line.
    private const int MinProcurementQuantity = 1;
    private const int MaxProcurementQuantity = 30;

    // Determines the maximum ratio of stock to sell for any given product while creating a sales transaction line.
    private const float MaximumSellingRatio = 0.4f;

    // Determines how many products to procure initially before starting to sell.
    private const int StockPreloadProductCount = 15;

    private static readonly Random _rnd = new(21395443);

    internal static Transaction GenerateTransaction(IReadOnlyList<Partner> partners, IReadOnlyList<Product> allProducts)
    {
        var availableProducts = allProducts.Where(x => x.NumberInStock > 0);
        var unavailableProducts = allProducts.Where(x => x.NumberInStock == 0);

        var shouldBeProcurement = availableProducts.Count() < StockPreloadProductCount || _rnd.Next(0, 2) == 0;
        var randomPartner = partners[_rnd.Next(0, partners.Count)];

        Func<IEnumerable<(Product, int)>, Transaction> transactionMethod = shouldBeProcurement 
            ? randomPartner.ProcureFrom 
            : randomPartner.SellTo;

        var productSelection = shouldBeProcurement 
            ? unavailableProducts.Any()
                ? unavailableProducts
                : allProducts
            : availableProducts;
        var productSelectionCount = productSelection.Count();

        // Select some random products, and project them to transaction lines with random quantities based on the transaction type.
        var transactionLines = productSelection
            .SelectRandom(_rnd.Next(MinTransactionLines, MaxTransactionLines + 1))
            .Select(p => (p, _rnd.Next(MinProcurementQuantity, shouldBeProcurement
                ? MaxProcurementQuantity + 1
                : Math.Max(MinProcurementQuantity, (int)(p.NumberInStock * MaximumSellingRatio)))));

        return transactionMethod(transactionLines);
    }

    internal static IEnumerable<T> SelectRandom<T>(this IEnumerable<T> list, int needed)
    {
        var count = list.Count();
        if (needed >= count)
            return list;

        var selectedItems = new HashSet<T>();
        while (needed > 0)
            if (selectedItems.Add(list.ElementAt(_rnd.Next(count))))
                needed--;

        return selectedItems;
    }
}