public static async Task <BetriebsmittelStamm> BetriebsmittelstammKopieren( IStammApi quellStammApi, IStammApi zielStammApi, Guid quellStammId, string zielStammBezeichnung, Action <BetriebsmittelStammKopierEventArgs> messageCallback) { var quellStamm = await quellStammApi.GetBetriebsmittelStamm(quellStammId).ConfigureAwait(false); var zielStämme = await zielStammApi.GetBetriebsmittelStämme(); // Erzeuge Zielstamm, falls noch nicht vorhanden var zielStamm = zielStämme.SingleOrDefault(s => s.Bezeichnung == zielStammBezeichnung); if (zielStamm == null) { messageCallback?.Invoke(new BetriebsmittelStammKopierEventArgs($"Erzeuge Betriebsmittelstamm '{zielStammBezeichnung}', da noch nicht vorhanden.")); zielStamm = await zielStammApi.CreateBetriebsmittelStamm(new NewBetriebsmittelStammInfo { Art = quellStamm.Art, Bezeichnung = zielStammBezeichnung }); } else if (zielStamm.Id == quellStammId) { throw new InvalidOperationException("Quell- und Zielstamm sind identisch."); } messageCallback?.Invoke(new BetriebsmittelStammKopierEventArgs($"Abgleich der Basis-Eigenschaften von Stamm '{quellStamm.Bezeichnung}' nach Stamm '{zielStammBezeichnung}'")); // Anstatt den zielStamm von Hand mit den quellStamm-Properties zu befüllen, // klonen wir einfach den Quellstamm und setzen nur die abweichenden Properties neu. var clonedQuellStamm = Clone(quellStamm); clonedQuellStamm.Art = zielStamm.Art; clonedQuellStamm.Nummer = zielStamm.Nummer; clonedQuellStamm.Bezeichnung = zielStamm.Bezeichnung; // Schreibe geänderten Zielstamm zurück. Die Betriebsmittel sind hier // nicht enthalten. Die werden weiter unten kopiert. await zielStammApi.UpdateBetriebsmittelStamm(zielStamm.Id, clonedQuellStamm); // Hole Zielstamm neu, um die tatsächlichen IDs (z.B. für die Kostenkataloge) zu erhalten zielStamm = await zielStammApi.GetBetriebsmittelStamm(zielStamm.Id); // Jetzt sind die Betriebsmittel zu kopieren. Da diese z.T. ID-Verweise auf den zugehörigen Kosten-/Zuschlagskatalog enthalten, // brauchen wir ein Mapping der IDs, um diese Verweise bei der Übernahme in den neuen Zielstamm entsprechend umzubiegen. // dazu erzeugen wir Mappings von Kosten-/Zuschlagskatalogen (Quelle => Ziel) auf Basis von Nummer + Bezeichnung // (ein anderes Kriterium haben wir leider nicht). var kostenkatalogIds = quellStamm.Kostenkataloge.Select(qk => new { QuellId = qk.Id, ZielId = zielStamm.Kostenkataloge.Single(zk => zk.Nummer == qk.Nummer && zk.Bezeichnung == qk.Bezeichnung).Id }); var zuschlagkatalogIds = quellStamm.Zuschlagskataloge.Select(qk => new { QuellId = qk.Id, ZielId = zielStamm.Zuschlagskataloge.Single(zk => zk.Nummer == qk.Nummer && zk.Bezeichnung == qk.Bezeichnung).Id }); var katalogIdMap = kostenkatalogIds.Concat(zuschlagkatalogIds).ToDictionary(k => k.QuellId, k => k.ZielId); AbgleichInfo abgleichInfo = new AbgleichInfo { QuellStamm = quellStamm, ZielStamm = zielStamm, KatalogIdMap = katalogIdMap }; // Für jede Betriebsmittelart: Gleiche Betriebsmittel ab (ohne weitere Kosten). foreach (var betriebsmittelArt in new[] { BetriebsmittelArt.Lohn, BetriebsmittelArt.Material, BetriebsmittelArt.Gerät, BetriebsmittelArt.SonstigeKosten, BetriebsmittelArt.Nachunternehmer, BetriebsmittelArt.Baustein }) { messageCallback?.Invoke(new BetriebsmittelStammKopierEventArgs($"Abgleich der Betriebsmittel der Art '{betriebsmittelArt}'")); // Hole für den Quell- und Zielstamm den Betriebsmittelbaum in strukturierter Form. Dieser Aufruf liefert ein // Array mit einem Element, und zwar der Wurzelgruppe, die alle anderen Elemente in strukturierter Form enthält. var quellWurzelGruppe = (await quellStammApi.GetAllBetriebsmittel(quellStamm.Id, betriebsmittelArt)).Single(); var zielWurzelGruppe = (await zielStammApi.GetAllBetriebsmittel(zielStamm.Id, betriebsmittelArt)).Single(); // Gleiche Betriebsmittel ab (auf Basis der Nummer) await BetriebsmittelAbgleichen(quellStammApi, zielStammApi, abgleichInfo, quellWurzelGruppe, zielWurzelGruppe, messageCallback); } // Alle Betriebsmittel wurden übernommen. Jetzt können die weiteren Kosten übernommen werden. Dictionary <Guid, Guid> quellToZielBetriebsmittelIds = abgleichInfo.QuellToZielBetriebsmittel.ToDictionary(t => t.Key.Id, t => t.Value.Id); foreach (var quellZielBetriebsmittel in abgleichInfo.QuellToZielBetriebsmittel) { var quellBetriebsmittel = quellZielBetriebsmittel.Key; var zielBetriebsmittel = quellZielBetriebsmittel.Value; if (quellBetriebsmittel.WeitereKosten?.Count > 0) { messageCallback?.Invoke(new BetriebsmittelStammKopierEventArgs($"Abgleich der weiteren Kosten für das Betriebsmittel {quellBetriebsmittel.GetBetriebsmittelDescription()}")); zielBetriebsmittel.WeitereKosten = Clone(quellBetriebsmittel.WeitereKosten); void TransformBetriebsmittelLinks(IEnumerable <KalkulationsZeile> kalkZeilen) { foreach (var kalkZeile in kalkZeilen) { if (kalkZeile.BetriebsmittelDetails != null) { kalkZeile.BetriebsmittelDetails.BetriebsmittelId = quellToZielBetriebsmittelIds[kalkZeile.BetriebsmittelDetails.BetriebsmittelId]; } else if (kalkZeile.UnterpositionDetails != null) { TransformBetriebsmittelLinks(kalkZeile.UnterpositionDetails.Zeilen); } } } TransformBetriebsmittelLinks(zielBetriebsmittel.WeitereKosten); zielBetriebsmittel.Kosten = null; zielBetriebsmittel.Zuschläge = null; await zielStammApi.UpdateBetriebsmittel(zielBetriebsmittel.Id, zielBetriebsmittel); } } return(zielStamm); }
static async Task BetriebsmittelAbgleichen( IStammApi quellStammApi, IStammApi zielStammApi, AbgleichInfo abgleichInfo, Betriebsmittel quellGruppe, Betriebsmittel zielGruppe, Action <BetriebsmittelStammKopierEventArgs> messageCallback) { var quellChildren = quellGruppe.GruppeDetails.BetriebsmittelList.Where(b => b.Nummer != null).ToList(); var zielChildrenByNumber = zielGruppe.GruppeDetails.BetriebsmittelList.Where(b => b.Nummer != null).ToDictionary(b => b.Nummer); foreach (var quellChildItem in quellChildren) { // Lade Quell-Betriebsmittel inkl. Details var quellChild = await quellStammApi.GetBetriebsmittel(quellChildItem.Id, quellChildItem.Art); messageCallback?.Invoke(new BetriebsmittelStammKopierEventArgs($"Abgleich des Betriebsmittels {quellChild.GetBetriebsmittelDescription()}")); zielChildrenByNumber.TryGetValue(quellChild.Nummer, out var zielChild); // Falls das Betriebsmittel noch keine Entsprechung im Ziel hat => neu erzeugen (gilt auch für Gruppen) if (zielChild == null) { zielChild = await zielStammApi.CreateBetriebsmittel(abgleichInfo.ZielStamm.Id, new NewBetriebsmittelInfo { Art = quellChild.Art, Bezeichnung = quellChild.Bezeichnung, Nummer = quellChild.Nummer, ParentGruppeId = zielGruppe.Id }); if (zielChild.GruppeDetails != null) { zielChild.GruppeDetails.BetriebsmittelList = new List <Betriebsmittel>(); } } abgleichInfo.QuellToZielBetriebsmittel.Add(quellChild, zielChild); zielChild.Bezeichnung = quellChild.Bezeichnung; zielChild.Leistungsfähig = quellChild.Leistungsfähig; zielChild.Einheit = quellChild.Einheit; zielChild.Kosten = quellChild.Kosten?.Select(k => new BetriebsmittelKosten { KostenebeneId = abgleichInfo.KatalogIdMap[k.KostenebeneId], LohnDetails = Clone(k.LohnDetails), GerätDetails = Clone(k.GerätDetails), MaterialDetails = Clone(k.MaterialDetails), SonstigeKostenDetails = Clone(k.SonstigeKostenDetails), NachunternehmerDetails = Clone(k.NachunternehmerDetails) }).ToList(); zielChild.Zuschläge = quellChild.Zuschläge?.Select(bz => new BetriebsmittelZuschlag { ArtIndex = bz.ArtIndex, ZuschlagsgruppenNummer = bz.ZuschlagsgruppenNummer, ZuschlagsebeneId = abgleichInfo.KatalogIdMap[bz.ZuschlagsebeneId], }).ToList(); zielChild.Details = Clone(quellChild.Details); zielChild.LohnDetails = Clone(quellChild.LohnDetails); zielChild.MaterialDetails = Clone(quellChild.MaterialDetails); zielChild.GerätDetails = Clone(quellChild.GerätDetails); zielChild.SonstigeKostenDetails = Clone(quellChild.SonstigeKostenDetails); zielChild.NachunternehmerDetails = Clone(quellChild.NachunternehmerDetails); zielChild.BausteinDetails = Clone(quellChild.BausteinDetails); zielChild.WeitereKosten = null; await zielStammApi.UpdateBetriebsmittel(zielChild.Id, zielChild); // Falls das zielChild eine Gruppe ist => rekursiv die Kinder abgleichen if (zielChild.GruppeDetails != null) { await BetriebsmittelAbgleichen(quellStammApi, zielStammApi, abgleichInfo, quellChildItem, zielChild, messageCallback); } } }