public void Test_facade_to_wrap_InvAss_Wholesaler_pairs_to_a_data_load_format()
        {
            // C: create a facade to go from InvAss, wholesaler pairs to a data load format
            // Data load fields (ICommonFields + InvUnique, Cat, and PartNumber): InvUnique, Cat, PartNumber
            // ICommonFields: SupplierName, SupplierPartNumber, SupplierCode WholesaleCost, PriceSchedule1_MSRP
            //      , PriceSchedule2_MinPrice
            const int    commonId = 3;
            const string matching_supplier_code = "matching_code";
            const string supp_part_num          = "SamplePartNum";
            const string supp_name = "Some Supplier Name";

            S5Inventory InvRaw = Get_Sample_S5Inventory_for_one_Inventory_Item();

            IEnumerable <IS5InvAssembled> InvAss = InvRaw.ToAssembled();
            var First = InvAss.First();

            First.Inv.Supplier = commonId;
            First.Inv.SuppPart = supp_part_num;

            QryAccount acct = new QryAccount();

            acct.AName    = supp_name;
            acct.AUnique  = commonId;
            acct.BankInfo = matching_supplier_code;

            (IS5InvAssembled InvAss, QryAccount ExtraInfo)pair = new ValueTuple <IS5InvAssembled, QryAccount>(First, acct);

            IDataLoadFormat       DataLoadRecord;
            AdaptToDataLoadFormat adapter = new AdaptToDataLoadFormat();

            adapter.Init(pair);
            DataLoadRecord = adapter;

            Assert.AreEqual(matching_supplier_code, adapter.SupplierCode);
            Assert.AreEqual("211545", adapter.PartNumber);
            Assert.AreEqual(12018, adapter.InvUnique);
            //confirm that the prices below are correct by geting real data to sample
            Assert.AreEqual((decimal)59.99, adapter.PriceSchedule1_MSRP);
            Assert.AreEqual((decimal)69.99, adapter.PriceSchedule2_MinPrice);
            Assert.AreEqual(supp_name, adapter.SupplierName);
            Assert.AreEqual(supp_part_num, adapter.SupplierPartNumber);
            Assert.AreEqual(29, adapter.WholesaleCost);
        }
        static void Main(string[] args)
        {
            Console.WriteLine("Begin");

            IConfiguration config = new ConfigurationBuilder()
                                    .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                                    .Build();

            IEnumerable <IPriceFile> BevNetRecords = new List <PriceFile>();
            string     PathAndFile = config["SourceDirectory"] + config["FileName"];
            const bool DontSuppressRecordErrors = false;

            BevNetRecords = FileReaderGeneric <PriceFile> .ReadFile
                                (PathAndFile : PathAndFile
                                , SupressRecordErrors : DontSuppressRecordErrors
                                );

            int MaxRecordsToDisplay = 10;

            for (int i = 0; i < MaxRecordsToDisplay; i++)
            {
                Console.WriteLine((BevNetRecords.Skip(i).FirstOrDefault() ?? new PriceFile()).PROD_ITEM);
            }

            Console.WriteLine($"Path: {config["SourceDirectory"]}");

            Console.WriteLine("Loading Inventory From Real Windward");

            IEnumerable <IS5InvAssembled> InvAss = GetDataFromSystemFive(config);

            Console.WriteLine("Finished - Loading Inventory From Real Windward");

            // A: Get XRef from Account for AUnique,BankInfo pairs
            IEnumerable <QryAccount> accts = GetXRefFromSupplierRecords(config);

            // B: Attach BankInfo / wholesaler to Assembled Inventory to make InvAss, wholesaler pairs
            //   GenericJoins service will do this
            IEnumerable <Tuple <IS5InvAssembled, QryAccount> > result = MakePairs(InvAss, accts);

            // C: create a facade to go from InvAss, wholesaler pairs to a data load format
            IEnumerable <AdaptToDataLoadFormat> adapters = result.Select(p =>
            {
                AdaptToDataLoadFormat tmp = new AdaptToDataLoadFormat();
                tmp.Init(new ValueTuple <IS5InvAssembled, QryAccount>(p.Item1, p.Item2));
                return(tmp);
            }
                                                                         );

            // At this point I believe I can focus on putting into CSV file. So implement ICopyable<T>
            IEnumerable <DataLoadFormat> DataLoad = adapters.Select(a =>
            {
                DataLoadFormat tmp = new DataLoadFormat();
                tmp.CopyFrom(a); // .CopyFrom is from ICopyable
                tmp.SupplierPartNumber   = tmp.SupplierPartNumber.TrimEnd();
                tmp.S5Orig_ListPrice     = a.PriceSchedule1_MSRP;
                tmp.S5Orig_MinPrice      = a.PriceSchedule2_MinPrice;
                tmp.S5Orig_WholesaleCost = a.WholesaleCost;
                return(tmp);
            });


            // D: create an adapter to go from the data load format to Common fields - PriceFileAdapter

            // E: get common fields from BevNets PriceFileAdapter


            // F: apply rehydrator to step Ds adapter and step Es common fields
            Rehydrater <PriceFile_Clean, IPriceFile, CommonFields, ICommonFields, PriceFileAdapter
                        , (string SupplierCode, string SupplierPart)> rehydrater;

            rehydrater = new Rehydrater <PriceFile_Clean, IPriceFile, CommonFields, ICommonFields, PriceFileAdapter
                                         , (string SupplierCode, string SupplierPart)>();

            List <(IPriceFile IFrom, ICommonFields ITo)> pairs = new List <(IPriceFile, ICommonFields)>();
            //  - Assemble pairs of matching records that need to be updated from each other.
            //      - DataLoadFormat is ICommonFields
            //      - BevNetRecords is IPriceFile
            //      - Join them together into pairs and perorm the update
            Func <ICommonFields, (string SupplierCode, string SupplierPart)> keyDataLoad = (common) =>
            {
                return(new ValueTuple <string, string>(common.SupplierCode.ToUpper(), common.SupplierPartNumber));
            };
            Func <IPriceFile, ValueTuple <string, string> > keyBevNet = (priceRecord) =>
            {
                return(new ValueTuple <string, string>(priceRecord.WHOLESALER.ToUpper(), priceRecord.PROD_ITEM));
            };

            IEnumerable <Tuple <IPriceFile, ICommonFields> > updatePairs
                = GenericJoins <IPriceFile, ICommonFields, (string SupplierCode, string SupplierPart)>
                  .InnerJoin(BevNetRecords, DataLoad, keyBevNet, keyDataLoad);

            pairs = updatePairs.Select(p => new ValueTuple <IPriceFile, ICommonFields>(p.Item1, p.Item2)).ToList();

            rehydrater.From_To_Pairs = pairs;
            rehydrater.UpdateIntegrationRecords();
            DataLoad = rehydrater.From_To_Pairs.Select(p => (DataLoadFormat)p.Item2);
            // Write the file
            var engine = new FileHelperAsyncEngine <DataLoadFormat>();

            using (engine.BeginWriteFile(config["SourceDirectory"] + "S5InventoryDataLoad.csv"))
            {
                foreach (var record in DataLoad)
                {
                    record.Change_ListPrice     = record.PriceSchedule1_MSRP - record.S5Orig_ListPrice;
                    record.Change_MinPrice      = record.PriceSchedule2_MinPrice - record.S5Orig_MinPrice;
                    record.Change_WholesaleCost = record.WholesaleCost - record.S5Orig_WholesaleCost;
                    engine.WriteNext(record);
                }
            }

            // G: export the resulting data load object to CSV

            Console.WriteLine("Done");
            Console.ReadKey();
        }