[{"data":1,"prerenderedAt":1034},["ShallowReactive",2],{"pf:\u002Fportfolio\u002Fcomarch-erp-optima-ksef-integration":3,"page-globals":144,"pf-related-src":199},{"id":4,"title":5,"body":6,"canonical":107,"client":108,"date":109,"description":97,"extension":110,"featured":111,"image":112,"meta":113,"navigation":114,"og":115,"path":118,"seo":119,"stack":130,"stem":133,"summary":134,"tags":135,"twitter":140,"type":142,"__hash__":143},"portfolio\u002Fportfolio\u002Fcomarch-erp-optima-ksef-integration.md","Automatyczna integracja Comarch ERP Optima z KSeF",{"type":7,"value":8,"toc":96},"minimark",[9,14,18,21,25,28,31,35,64,68,71,75,78,81,85],[10,11,13],"h2",{"id":12},"problem","Problem",[15,16,17],"p",{},"Proces wysyłania faktur z Comarch ERP Optima do KSeF wymagał automatyzacji bez pomijania mechanizmów biznesowych i ewidencyjnych systemu ERP.",[15,19,20],{},"Rozwiązanie musiało poprawnie przetwarzać dokumenty, przekazywać je do KSeF, odbierać Urzędowe Poświadczenie Odbioru oraz zapisywać rezultat w taki sposób, aby cały proces pozostawał widoczny dla użytkowników Optimy.",[10,22,24],{"id":23},"podejście","Podejście",[15,26,27],{},"Zaprojektowałem i wdrożyłem usługę Windows działającą w tle. Co 10 sekund wyszukuje ona w bazie Microsoft SQL Server faktury oczekujące na przetworzenie.",[15,29,30],{},"Dostęp do danych został zaimplementowany przy użyciu Dappera, natomiast właściwa wysyłka dokumentów odbywa się przez bibliotekę DLL API Comarch ERP Optima. Dzięki temu faktury są obsługiwane zgodnie z mechanizmami Optimy, a informacje o wysyłce i odebranym UPO pozostają dostępne bezpośrednio w systemie ERP.",[10,32,34],{"id":33},"architektura","Architektura",[36,37,38,46,52,58],"ul",{},[39,40,41,45],"li",{},[42,43,44],"strong",{},"Windows Service"," realizujący cykliczne przetwarzanie dokumentów.",[39,47,48,51],{},[42,49,50],{},"Dapper i SQL Server"," do szybkiego wyszukiwania faktur oczekujących na wysłanie.",[39,53,54,57],{},[42,55,56],{},"Comarch ERP Optima API"," do przetwarzania dokumentów i komunikacji z KSeF.",[39,59,60,63],{},[42,61,62],{},"Obsługa UPO"," wraz z zapisaniem wyniku procesu w Optimie.",[10,65,67],{"id":66},"stack","Stack",[15,69,70],{},".NET Framework 4.7.2, Windows Service, Microsoft SQL Server, Dapper oraz DLL API Comarch ERP Optima.",[10,72,74],{"id":73},"rezultat","Rezultat",[15,76,77],{},"Proces wysyłania faktur do KSeF został całkowicie zautomatyzowany. Dokumenty są regularnie wykrywane, poprawnie przetwarzane przez API Optimy i wysyłane do KSeF, a następnie uzupełniane o odebrane UPO.",[15,79,80],{},"Użytkownicy mogą kontrolować status faktur bezpośrednio w Comarch ERP Optima, bez korzystania z dodatkowego panelu lub ręcznego uruchamiania wysyłki.",[10,82,84],{"id":83},"lessons-learned","Lessons learned",[36,86,87,90,93],{},[39,88,89],{},"Integracja przez oficjalne API systemu ERP pozwala zachować spójność jego procesów biznesowych.",[39,91,92],{},"Usługa działająca w tle dobrze sprawdza się przy regularnym przetwarzaniu niewielkich kolejek dokumentów.",[39,94,95],{},"Rezultat integracji powinien być widoczny w systemie, z którego użytkownicy korzystają na co dzień.",{"title":97,"searchDepth":98,"depth":98,"links":99},"",3,[100,102,103,104,105,106],{"id":12,"depth":101,"text":13},2,{"id":23,"depth":101,"text":24},{"id":33,"depth":101,"text":34},{"id":66,"depth":101,"text":67},{"id":73,"depth":101,"text":74},{"id":83,"depth":101,"text":84},"https:\u002F\u002Fjetstreamaviation.pl\u002Fportfolio\u002Fcomarch-optima-ksef-integration","Confidential","2026-04-01","md",false,"\u002Fimages\u002Fportfolio\u002Foptima-ksef.webp",{},true,{"title":5,"description":116,"type":117},"Automatyczna wysyłka faktur do KSeF i odbieranie UPO bezpośrednio w środowisku Comarch ERP Optima.","article","\u002Fportfolio\u002Fcomarch-erp-optima-ksef-integration",{"title":120,"description":121,"keywords":122,"robots":129},"Integracja Comarch ERP Optima z KSeF | Case study","Usługa Windows automatyzująca wysyłkę faktur z Comarch ERP Optima do KSeF oraz pobieranie UPO.",[123,124,125,126,127,128],"Comarch ERP Optima","KSeF",".NET Framework","SQL Server","Dapper","integracja ERP","index,follow",[131,56,132,127,44,124],".NET Framework 4.7.2","Microsoft SQL Server","portfolio\u002Fcomarch-erp-optima-ksef-integration","Usługa Windows automatyzująca przetwarzanie faktur z Comarch ERP Optima, wysyłkę do KSeF oraz odbieranie Urzędowego Poświadczenia Odbioru.",[136,137,138,139],"backend","integration","erp","automation",{"card":141},"summary_large_image","Software \u002F ERP Integration","pLQ3RV8OBlrJ-g4BY6Lv1mG64n_dKjWPXIPhvt0PHy0",{"id":145,"title":146,"actions":147,"areas":147,"aviationList":147,"blogChrome":148,"body":163,"canonical":147,"cards":147,"contact":147,"cta":175,"description":182,"extension":110,"eyebrow":147,"hero":147,"heroImage":147,"list":147,"meta":183,"navigation":114,"og":147,"partners":147,"path":184,"perspective":147,"portfolioChrome":185,"process":147,"sectionHeader":147,"sections":147,"seo":195,"serviceLabels":147,"services":147,"stem":197,"twitter":147,"__hash__":198},"pages\u002Fpages\u002Fglobals.md","Globalne teksty UI",null,{"readingTimeSuffix":149,"tocTitle":150,"cta":151,"related":155,"tag":158},"min czytania","Spis treści",{"eyebrow":152,"title":153,"text":154},"Współpraca","Ten temat dotyczy Twojego systemu?","Jeśli mierzysz się z podobnym problemem w praktyce — napisz. Spojrzę na konkret i podpowiem kierunek.",{"eyebrow":156,"title":157},"Czytaj dalej","Powiązane wpisy",{"eyebrow":159,"bodyPrefix":160,"backLabel":161,"emptyText":162},"Tag","Wpisy powiązane z tematem","Wróć do wszystkich wpisów →","Brak wpisów dla tego tagu.",{"type":7,"value":164,"toc":173},[165],[15,166,167,168,172],{},"Ten dokument nie ma własnej strony (",[169,170,171],"code",{},"noindex","). Służy wyłącznie jako źródło współdzielonych tekstów interfejsu, które pojawiają się na wielu stronach naraz — dzięki temu można je edytować w jednym miejscu, bez wchodzenia w kod komponentów.",{"title":97,"searchDepth":98,"depth":98,"links":174},[],{"eyebrow":152,"title":176,"text":177,"primaryLabel":178,"primaryTo":179,"secondaryLabel":180,"secondaryTo":181},"Porozmawiajmy o projekcie","Opisz problem albo cel — odezwę się z konkretną propozycją następnego kroku: rozmowy, audytu, zakresu prac lub ofertą.","Napisz do mnie","\u002Fkontakt","Zobacz usługi IT","\u002Fuslugi-it","Współdzielone etykiety i domyślne treści renderowane na wielu stronach (CtaBlock, strony szczegółów blog\u002Fportfolio, tagi). Edytowane tutaj, bez zmian w kodzie.",{},"\u002Fpages\u002Fglobals",{"caseStudyEyebrow":186,"meta":187,"stackTitle":67,"areasTitle":191,"related":192},"Case study",{"type":188,"client":189,"date":190},"Typ","Klient","Data","Obszary",{"eyebrow":193,"title":194},"Więcej","Powiązane projekty",{"robots":196,"title":146,"description":182},"noindex,nofollow","pages\u002Fglobals","_s0SDmcqMxpaZUVz5yYm0sZItpGh6_JwnFv3nOsfADs",[200,635,705,826],{"id":201,"title":202,"body":203,"canonical":608,"client":108,"date":609,"description":97,"extension":110,"featured":114,"image":610,"meta":611,"navigation":114,"og":612,"path":614,"seo":615,"stack":624,"stem":628,"summary":629,"tags":630,"twitter":632,"type":633,"__hash__":634},"portfolio\u002Fportfolio\u002Fproxmox-provider-vps-platform.md","Proxmox Provider dla platformy VPS",{"type":7,"value":204,"toc":598},[205,207,210,214,217,219,239,259,261,287,291,294,568,571,573,576,578,581,583,594],[10,206,13],{"id":12},[15,208,209],{},"Platforma sprzedająca serwery VPS zdecydowała się wdrożyć nowe środowisko wirtualizacji - PVE (Proxmox Virtual Environment). W związku z tym należało zaprojektować usługę, która weźmnie całą odpowiedzialność za zarządzanie cyklem życia maszyn wirtualnych klientów oraz ich monitorowaniem. Wymagana była niezawodność, idempotencja oraz spójność przy jednocześnie rosnącej liczbie klientów.",[10,211,213],{"id":212},"kontekst","Kontekst",[15,215,216],{},"Warstwa wykonawcza opierała się na Proxmox VE. Panel klienta i panel administracyjny potrzebowały jednego, spójnego sposobu zlecania operacji na maszynach oraz wglądu w ich aktualny stan, a także możliwości monitorowania klastra — najlepiej w czasie rzeczywistym, bez ręcznego odświeżania. W celach administracyjnych wymagane było zarządzanie takimi funkcjami jak sieci wirtualne lub polityki migawek.",[10,218,24],{"id":23},[15,220,221,222,225,226,225,229,225,232,225,235,238],{},"Zaprojektowałem osobny moduł — „provider\" — odpowiedzialny wyłącznie za komunikację z Proxmox i tłumaczenie intencji biznesowych (",[169,223,224],{},"utwórz maszynę",", ",[169,227,228],{},"zmień plan",[169,230,231],{},"usuń",[169,233,234],{},"uruchom",[169,236,237],{},"wykonaj migawkę",") na konkretne operacje w infrastrukturze. Panel nigdy nie rozmawia z Proxmox bezpośrednio; zawsze przez jawny kontrakt providera.",[15,240,241,242,225,245,225,248,225,251,254,255,258],{},"Operacje długotrwałe potraktowałem jako zadania asynchroniczne z jawnym cyklem życia: ",[169,243,244],{},"zlecone",[169,246,247],{},"w toku",[169,249,250],{},"zakończone",[169,252,253],{},"błąd",". Za ich orkiestrację w obrębie modułu odpowiada mechanizm ",[169,256,257],{},"internal commands processor",". Dzięki temu interfejs mógł pokazywać rzeczywisty postęp, a nie tylko „proszę czekać\".",[10,260,34],{"id":33},[36,262,263,269,275,281],{},[39,264,265,268],{},[42,266,267],{},"Provider"," jako granica: cała wiedza o Proxmox zamknięta za jednym kontraktem.",[39,270,271,274],{},[42,272,273],{},"Kolejka zadań"," dla operacji długotrwałych, z idempotentnymi krokami.",[39,276,277,280],{},[42,278,279],{},"PostgreSQL"," jako źródło prawdy o stanie zamówień i maszyn.",[39,282,283,286],{},[42,284,285],{},"Redis"," do koordynacji zadań i cache'owania stanu odpytywanego często.",[10,288,290],{"id":289},"implementacja","Implementacja",[15,292,293],{},"Kluczowa była idempotentność. Ponowienie zadania po awarii nie mogło tworzyć drugiej maszyny ani pozostawiać systemu w stanie niespójnym. Każdy krok sprawdzał najpierw, czy jego efekt już nie istnieje.",[295,296,300],"pre",{"className":297,"code":298,"language":299,"meta":97,"style":97},"language-csharp shiki shiki-themes one-dark-pro one-dark-pro","public async Task\u003CResult> Handle(ProvisionVm task, CancellationToken ct)\n{\n    var existing = await proxmox.FindByTagAsync(task.CorrelationId, ct);\n    if (existing is not null)\n        return Result.AlreadyDone(existing.VmId); \u002F\u002F idempotentnie: brak duplikatu\n\n    var vm = await proxmox.CreateAsync(task.Spec, task.CorrelationId, ct);\n    await progress.PublishAsync(task.OrderId, VmState.Provisioned, ct);\n    return Result.Ok(vm.VmId);\n}\n","csharp",[169,301,302,352,357,400,421,451,457,499,538,562],{"__ignoreMap":97},[303,304,307,311,314,318,322,325,328,332,335,338,341,343,346,349],"span",{"class":305,"line":306},"line",1,[303,308,310],{"class":309},"sCyHM","public",[303,312,313],{"class":309}," async",[303,315,317],{"class":316},"sZsuq"," Task",[303,319,321],{"class":320},"shasQ","\u003C",[303,323,324],{"class":316},"Result",[303,326,327],{"class":320},"> ",[303,329,331],{"class":330},"sw6ri","Handle",[303,333,334],{"class":320},"(",[303,336,337],{"class":316},"ProvisionVm",[303,339,340],{"class":316}," task",[303,342,225],{"class":320},[303,344,345],{"class":316},"CancellationToken",[303,347,348],{"class":316}," ct",[303,350,351],{"class":320},")\n",[303,353,354],{"class":305,"line":101},[303,355,356],{"class":320},"{\n",[303,358,359,362,366,370,373,376,379,382,384,387,389,392,394,397],{"class":305,"line":98},[303,360,361],{"class":309},"    var",[303,363,365],{"class":364},"sh-OU"," existing",[303,367,369],{"class":368},"seqx8"," =",[303,371,372],{"class":320}," await ",[303,374,375],{"class":316},"proxmox",[303,377,378],{"class":320},".",[303,380,381],{"class":330},"FindByTagAsync",[303,383,334],{"class":320},[303,385,386],{"class":316},"task",[303,388,378],{"class":320},[303,390,391],{"class":316},"CorrelationId",[303,393,225],{"class":320},[303,395,396],{"class":364},"ct",[303,398,399],{"class":320},");\n",[303,401,403,406,409,412,415,419],{"class":305,"line":402},4,[303,404,405],{"class":309},"    if",[303,407,408],{"class":320}," (",[303,410,411],{"class":364},"existing",[303,413,414],{"class":320}," is not ",[303,416,418],{"class":417},"sDrLb","null",[303,420,351],{"class":320},[303,422,424,427,430,432,435,437,439,441,444,447],{"class":305,"line":423},5,[303,425,426],{"class":309},"        return",[303,428,429],{"class":316}," Result",[303,431,378],{"class":320},[303,433,434],{"class":330},"AlreadyDone",[303,436,334],{"class":320},[303,438,411],{"class":316},[303,440,378],{"class":320},[303,442,443],{"class":316},"VmId",[303,445,446],{"class":320},"); ",[303,448,450],{"class":449},"sIbxt","\u002F\u002F idempotentnie: brak duplikatu\n",[303,452,454],{"class":305,"line":453},6,[303,455,456],{"emptyLinePlaceholder":114},"\n",[303,458,460,462,465,467,469,471,473,476,478,480,482,485,487,489,491,493,495,497],{"class":305,"line":459},7,[303,461,361],{"class":309},[303,463,464],{"class":364}," vm",[303,466,369],{"class":368},[303,468,372],{"class":320},[303,470,375],{"class":316},[303,472,378],{"class":320},[303,474,475],{"class":330},"CreateAsync",[303,477,334],{"class":320},[303,479,386],{"class":316},[303,481,378],{"class":320},[303,483,484],{"class":316},"Spec",[303,486,225],{"class":320},[303,488,386],{"class":316},[303,490,378],{"class":320},[303,492,391],{"class":316},[303,494,225],{"class":320},[303,496,396],{"class":364},[303,498,399],{"class":320},[303,500,502,505,508,510,513,515,517,519,522,524,527,529,532,534,536],{"class":305,"line":501},8,[303,503,504],{"class":320},"    await ",[303,506,507],{"class":316},"progress",[303,509,378],{"class":320},[303,511,512],{"class":330},"PublishAsync",[303,514,334],{"class":320},[303,516,386],{"class":316},[303,518,378],{"class":320},[303,520,521],{"class":316},"OrderId",[303,523,225],{"class":320},[303,525,526],{"class":316},"VmState",[303,528,378],{"class":320},[303,530,531],{"class":316},"Provisioned",[303,533,225],{"class":320},[303,535,396],{"class":364},[303,537,399],{"class":320},[303,539,541,544,546,548,551,553,556,558,560],{"class":305,"line":540},9,[303,542,543],{"class":309},"    return",[303,545,429],{"class":316},[303,547,378],{"class":320},[303,549,550],{"class":330},"Ok",[303,552,334],{"class":320},[303,554,555],{"class":316},"vm",[303,557,378],{"class":320},[303,559,443],{"class":316},[303,561,399],{"class":320},[303,563,565],{"class":305,"line":564},10,[303,566,567],{"class":320},"}\n",[15,569,570],{},"Stan każdego zadania był publikowany na bieżąco, więc panel administracyjny i panel klienta widziały te same, aktualne informacje.",[10,572,67],{"id":66},[15,574,575],{},".NET, ASP.NET Core, PostgreSQL, Redis, Docker oraz API Proxmox VE.",[10,577,74],{"id":73},[15,579,580],{},"Provisioning maszyn stał się w pełni automatyczny i powtarzalny. Czas realizacji typowego zamówienia skrócił się z minut liczonych ręcznie do operacji wykonywanej bez udziału człowieka. Panele pokazują rzeczywisty stan maszyn na żywo, a operacje można bezpiecznie ponawiać po awarii.",[10,582,84],{"id":83},[36,584,585,588,591],{},[39,586,587],{},"Idempotentność nie jest dodatkiem — przy operacjach na infrastrukturze to warunek bezpieczeństwa.",[39,589,590],{},"Zamknięcie integracji za jednym kontraktem opłaciło się przy każdej późniejszej zmianie w Proxmox.",[39,592,593],{},"Komunikacja w czasie rzeczywistym najbardziej pomogła nie klientom, lecz administracji — skróciła czas reakcji na problemy.",[595,596,597],"style",{},"html pre.shiki code .sCyHM, html code.shiki .sCyHM{--shiki-default:#C678DD;--shiki-dark:#C678DD}html pre.shiki code .sZsuq, html code.shiki .sZsuq{--shiki-default:#E5C07B;--shiki-dark:#E5C07B}html pre.shiki code .shasQ, html code.shiki .shasQ{--shiki-default:#ABB2BF;--shiki-dark:#ABB2BF}html pre.shiki code .sw6ri, html code.shiki .sw6ri{--shiki-default:#61AFEF;--shiki-dark:#61AFEF}html pre.shiki code .sh-OU, html code.shiki .sh-OU{--shiki-default:#E06C75;--shiki-dark:#E06C75}html pre.shiki code .seqx8, html code.shiki .seqx8{--shiki-default:#56B6C2;--shiki-dark:#56B6C2}html pre.shiki code .sDrLb, html code.shiki .sDrLb{--shiki-default:#D19A66;--shiki-dark:#D19A66}html pre.shiki code .sIbxt, html code.shiki .sIbxt{--shiki-default:#7F848E;--shiki-default-font-style:italic;--shiki-dark:#7F848E;--shiki-dark-font-style:italic}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":97,"searchDepth":98,"depth":98,"links":599},[600,601,602,603,604,605,606,607],{"id":12,"depth":101,"text":13},{"id":212,"depth":101,"text":213},{"id":23,"depth":101,"text":24},{"id":33,"depth":101,"text":34},{"id":289,"depth":101,"text":290},{"id":66,"depth":101,"text":67},{"id":73,"depth":101,"text":74},{"id":83,"depth":101,"text":84},"https:\u002F\u002Fjetstreamaviation.pl\u002Fportfolio\u002Fproxmox-provider-vps-platform","2026-07-13","\u002Fimages\u002Fportfolio\u002Fproxmox-provider.webp",{},{"title":202,"description":613,"type":117},"Automatyzacja cyklu życia maszyn wirtualnych na Proxmox.","\u002Fportfolio\u002Fproxmox-provider-vps-platform",{"title":616,"description":617,"keywords":618,"robots":129},"Proxmox Provider — case study integracji platformy VPS","Case study systemu automatyzującego provisioning maszyn wirtualnych na Proxmox: architektura, kolejki zadań i komunikacja w czasie rzeczywistym.",[619,620,621,622,623,279],"Proxmox",".NET","VPS","infrastructure","Docker",[625,619,623,279,285,626,627],".NET Core","YARP","Grafana\u002FPrometheus\u002FLoki","portfolio\u002Fproxmox-provider-vps-platform","System integrujący panel platformy VPS z infrastrukturą Proxmox — automatyczne provisionowanie, cykl życia maszyn i komunikacja w czasie rzeczywistym.",[622,136,631],"devops",{"card":141},"Software \u002F Infrastructure","XoEMuTt59FnMAoRcEmjDmSb_DQZQwFyH1Ag8vW7t7Z0",{"id":4,"title":5,"body":636,"canonical":107,"client":108,"date":109,"description":97,"extension":110,"featured":111,"image":112,"meta":698,"navigation":114,"og":699,"path":118,"seo":700,"stack":702,"stem":133,"summary":134,"tags":703,"twitter":704,"type":142,"__hash__":143},{"type":7,"value":637,"toc":690},[638,640,642,644,646,648,650,652,670,672,674,676,678,680,682],[10,639,13],{"id":12},[15,641,17],{},[15,643,20],{},[10,645,24],{"id":23},[15,647,27],{},[15,649,30],{},[10,651,34],{"id":33},[36,653,654,658,662,666],{},[39,655,656,45],{},[42,657,44],{},[39,659,660,51],{},[42,661,50],{},[39,663,664,57],{},[42,665,56],{},[39,667,668,63],{},[42,669,62],{},[10,671,67],{"id":66},[15,673,70],{},[10,675,74],{"id":73},[15,677,77],{},[15,679,80],{},[10,681,84],{"id":83},[36,683,684,686,688],{},[39,685,89],{},[39,687,92],{},[39,689,95],{},{"title":97,"searchDepth":98,"depth":98,"links":691},[692,693,694,695,696,697],{"id":12,"depth":101,"text":13},{"id":23,"depth":101,"text":24},{"id":33,"depth":101,"text":34},{"id":66,"depth":101,"text":67},{"id":73,"depth":101,"text":74},{"id":83,"depth":101,"text":84},{},{"title":5,"description":116,"type":117},{"title":120,"description":121,"keywords":701,"robots":129},[123,124,125,126,127,128],[131,56,132,127,44,124],[136,137,138,139],{"card":141},{"id":706,"title":707,"body":708,"canonical":796,"client":108,"date":797,"description":97,"extension":110,"featured":114,"image":798,"meta":799,"navigation":114,"og":800,"path":803,"seo":804,"stack":813,"stem":818,"summary":819,"tags":820,"twitter":823,"type":824,"__hash__":825},"portfolio\u002Fportfolio\u002Faviation-training-systems.md","Cyfryzacja procesów w organizacji szkolenia lotniczego",{"type":7,"value":709,"toc":786},[710,712,715,717,720,722,725,728,732,758,760,763,765,768,770,773,775],[10,711,13],{"id":12},[15,713,714],{},"Organizacja szkoleniowa prowadziła ewidencję szkoleń i dokumentację operacyjną w sposób rozproszony: część w arkuszach, część w plikach, część w pamięci konkretnych osób. Przy audytach i codziennej pracy oznaczało to ryzyko niespójności oraz duży nakład czasu na odtworzenie stanu faktycznego.",[10,716,213],{"id":212},[15,718,719],{},"W lotnictwie dokumentacja nie jest formalnością — to element bezpieczeństwa i zgodności. Rozwiązanie musiało więc nie tylko usprawnić pracę, ale przede wszystkim być zgodne z wymaganiami i odporne na błędy wynikające z rutyny.",[10,721,24],{"id":23},[15,723,724],{},"Zamiast od razu wdrażać narzędzie, zacząłem od procesów. Zmapowałem realny przebieg szkolenia i obiegu dokumentów: gdzie powstają dane, kto za nie odpowiada i w którym momencie stają się wiążące. Dopiero na tej podstawie projektowałem uporządkowanie.",[15,726,727],{},"Przeniosłem podejście znane z operacji lotniczych: jasne role, checklisty dla kroków krytycznych oraz jeden, wiarygodny stan zamiast wielu równoległych wersji prawdy.",[10,729,731],{"id":730},"architektura-procesu","Architektura procesu",[36,733,734,740,746,752],{},[39,735,736,739],{},[42,737,738],{},"Jedno źródło prawdy"," dla ewidencji szkoleń i statusów.",[39,741,742,745],{},[42,743,744],{},"Jawne role i odpowiedzialności"," na każdym etapie obiegu.",[39,747,748,751],{},[42,749,750],{},"Checklisty"," dla kroków, w których błąd jest kosztowny.",[39,753,754,757],{},[42,755,756],{},"Ślad zmian"," — kto, co i kiedy zatwierdził.",[10,759,290],{"id":289},[15,761,762],{},"Wdrożenie było etapowe. Najpierw uporządkowaliśmy dane i ustaliliśmy pojedyncze źródło prawdy, potem sformalizowaliśmy procedury, a na końcu — dopiero na końcu — dobraliśmy narzędzia wspierające. Taka kolejność ograniczyła ryzyko, że narzędzie wymusi zły proces.",[10,764,67],{"id":66},[15,766,767],{},"Praca koncepcyjna i procesowa: projektowanie procesów, dokumentacja, procedury oraz podejście do compliance. Warstwa narzędziowa była wtórna wobec uporządkowanego procesu.",[10,769,74],{"id":73},[15,771,772],{},"Dokumentacja i ewidencja stały się spójne i możliwe do szybkiego odtworzenia. Przygotowanie do audytu przestało być awaryjnym zrywem, a codzienna praca zyskała jasne punkty odpowiedzialności. Organizacja zyskała stan, któremu można zaufać.",[10,774,84],{"id":83},[36,776,777,780,783],{},[39,778,779],{},"Narzędzie nie naprawi złego procesu — najpierw proces, potem software.",[39,781,782],{},"Checklisty sprawdzają się w dokumentacji tak samo dobrze jak w kokpicie.",[39,784,785],{},"Jedno źródło prawdy to najtańsza inwestycja w spokój podczas audytu.",{"title":97,"searchDepth":98,"depth":98,"links":787},[788,789,790,791,792,793,794,795],{"id":12,"depth":101,"text":13},{"id":212,"depth":101,"text":213},{"id":23,"depth":101,"text":24},{"id":730,"depth":101,"text":731},{"id":289,"depth":101,"text":290},{"id":66,"depth":101,"text":67},{"id":73,"depth":101,"text":74},{"id":83,"depth":101,"text":84},"https:\u002F\u002Fjetstreamaviation.pl\u002Fportfolio\u002Faviation-training-systems","2024-03-2","\u002Fimages\u002Fportfolio\u002Faviation-training-systems.jpg",{},{"title":707,"description":801,"image":802,"type":117},"Zgodność, powtarzalność i użyteczność operacyjna.","\u002Fimages\u002Fog\u002Faviation-training-systems.jpg","\u002Fportfolio\u002Faviation-training-systems",{"title":805,"description":806,"keywords":807,"robots":129},"Cyfryzacja procesów w ATO — case study","Case study porządkowania dokumentacji i procedur w organizacji szkolenia lotniczego: zgodność, powtarzalność i użyteczność operacyjna.",[808,809,810,811,812],"organizacja szkoleniowa","ATO","procedury lotnicze","compliance","dokumentacja",[814,815,816,817],"Aviation","Compliance","Process design","Documentation","portfolio\u002Faviation-training-systems","Uporządkowanie dokumentacji, ewidencji szkoleń i procedur w organizacji szkoleniowej — z naciskiem na zgodność, powtarzalność i realną użyteczność.",[821,811,822],"aviation","process",{"card":141},"Consulting \u002F Aviation IT","ivhmSUqIp6bgyU8lFp7Vuqv7lri6gNLjstlQqG0Tpcc",{"id":827,"title":828,"body":829,"canonical":1006,"client":108,"date":1007,"description":97,"extension":110,"featured":111,"image":1008,"meta":1009,"navigation":114,"og":1010,"path":1013,"seo":1014,"stack":1023,"stem":1025,"summary":1026,"tags":1027,"twitter":1031,"type":1032,"__hash__":1033},"portfolio\u002Fportfolio\u002Femvo-notification-system.md","EMVO EVI — system powiadomień o zdarzeniach EMVS",{"type":7,"value":830,"toc":996},[831,833,836,839,841,844,847,850,852,855,858,878,881,883,926,928,931,934,955,958,961,963,966,968,971,974,977,979],[10,832,13],{"id":12},[15,834,835],{},"Europejska infrastruktura weryfikacji leków składa się z wielu współpracujących ze sobą systemów krajowych i centralnych. Wystąpienie awarii, planowanej przerwy technicznej lub znanego problemu wymagało szybkiego przekazania precyzyjnej informacji do zainteresowanych organizacji i użytkowników.",[15,837,838],{},"Dotychczasowa obsługa takich komunikatów była trudna do skalowania. Odbiorcy potrzebowali informacji dotyczących wyłącznie wybranych krajów, systemów i rodzajów zdarzeń, natomiast operatorzy musieli kontrolować cały cykl życia zgłoszenia — od jego utworzenia, przez aktualizacje, aż po zamknięcie incydentu.",[10,840,213],{"id":212},[15,842,843],{},"EVI — European Medicines Verification System Information — pełniło rolę centralnego źródła informacji o zdarzeniach operacyjnych w ramach EMVS.",[15,845,846],{},"System musiał obsługiwać zdarzenia dotyczące wielu krajów, środowisk i komponentów infrastruktury. Każda publikacja mogła zmieniać się w czasie: otrzymywać aktualizacje, nowe przewidywane terminy rozwiązania, informacje o przyczynach lub potwierdzenie przywrócenia prawidłowego działania.",[15,848,849],{},"Równocześnie użytkownicy powinni otrzymywać tylko te wiadomości, które odpowiadały ich subskrypcjom, bez konieczności ręcznego śledzenia wszystkich komunikatów.",[10,851,24],{"id":23},[15,853,854],{},"Zaprojektowałem i rozwijałem backend systemu jako Web API w ASP.NET Core. Aplikacja rozdzielała odpowiedzialność za zarządzanie zgłoszeniami, publikowanie zdarzeń, obsługę subskrypcji oraz wysyłkę powiadomień.",[15,856,857],{},"Zdarzenie było traktowane jako obiekt posiadający własny cykl życia. Operator mógł określić między innymi:",[36,859,860,863,866,869,872,875],{},[39,861,862],{},"kraj lub grupę krajów,",[39,864,865],{},"system i środowisko, którego dotyczy zdarzenie,",[39,867,868],{},"rodzaj oraz poziom wpływu,",[39,870,871],{},"czas rozpoczęcia i zakończenia,",[39,873,874],{},"aktualny status,",[39,876,877],{},"opis problemu i kolejne aktualizacje.",[15,879,880],{},"Po opublikowaniu lub zmianie zgłoszenia system identyfikował właściwych subskrybentów i przygotowywał dla nich odpowiednie powiadomienia.",[10,882,34],{"id":33},[36,884,885,891,896,902,908,914,920],{},[39,886,887,890],{},[42,888,889],{},"ASP.NET Core Web API"," jako warstwa udostępniająca operacje związane ze zgłoszeniami, użytkownikami i subskrypcjami.",[39,892,893,895],{},[42,894,132],{}," jako źródło prawdy o zdarzeniach, odbiorcach, konfiguracji subskrypcji i historii wysyłki.",[39,897,898,901],{},[42,899,900],{},"SSO"," zapewniające kontrolowany dostęp do funkcji administracyjnych.",[39,903,904,907],{},[42,905,906],{},"Quartz.NET"," do realizacji zadań cyklicznych i przetwarzania operacji wykonywanych poza żądaniem HTTP.",[39,909,910,913],{},[42,911,912],{},"SendGrid"," jako usługa odpowiedzialna za dystrybucję wiadomości e-mail.",[39,915,916,919],{},[42,917,918],{},"Microsoft Azure"," jako środowisko uruchomieniowe aplikacji i jej infrastruktury.",[39,921,922,925],{},[42,923,924],{},"GitOps"," do kontrolowanego, powtarzalnego wdrażania zmian pomiędzy środowiskami.",[10,927,290],{"id":289},[15,929,930],{},"Jednym z kluczowych elementów był mechanizm dopasowywania zgłoszeń do subskrypcji. Użytkownik mógł określić zakres interesujących go informacji, a system oceniał każde nowe lub zaktualizowane zdarzenie na podstawie kilku kryteriów jednocześnie.",[15,932,933],{},"Proces obejmował:",[935,936,937,940,943,946,949,952],"ol",{},[39,938,939],{},"zapisanie lub aktualizację zgłoszenia,",[39,941,942],{},"określenie krajów, systemów i środowisk objętych zdarzeniem,",[39,944,945],{},"wyszukanie pasujących subskrypcji,",[39,947,948],{},"utworzenie powiadomień dla właściwych odbiorców,",[39,950,951],{},"przekazanie wiadomości do SendGrid,",[39,953,954],{},"zapisanie wyniku wysyłki i historii komunikacji.",[15,956,957],{},"Operacje wysyłania zostały oddzielone od podstawowej obsługi żądań API. Dzięki temu publikowanie zgłoszenia nie było uzależnione od czasu odpowiedzi zewnętrznego dostawcy poczty, a nieudane próby mogły być bezpiecznie ponawiane przez zadania Quartz.NET.",[15,959,960],{},"Istotnym elementem była również audytowalność. System przechowywał historię zmian zgłoszeń oraz informacje o wygenerowanych i wysłanych powiadomieniach, co ułatwiało analizowanie przebiegu komunikacji.",[10,962,67],{"id":66},[15,964,965],{},"ASP.NET Core Web API, Microsoft SQL Server, Microsoft Azure, Quartz.NET, SendGrid, SSO oraz proces wdrożeniowy oparty na GitOps.",[10,967,74],{"id":73},[15,969,970],{},"Powstał centralny system umożliwiający uporządkowane zarządzanie informacjami o zdarzeniach operacyjnych dotyczących wielu krajów i komponentów EMVS.",[15,972,973],{},"Operatorzy mogli prowadzić zgłoszenie przez cały jego cykl życia i publikować kolejne aktualizacje, natomiast odbiorcy automatycznie otrzymywali tylko te informacje, które odpowiadały zakresowi ich subskrypcji.",[15,975,976],{},"Rozdzielenie obsługi zgłoszeń od mechanizmu dystrybucji wiadomości zwiększyło odporność rozwiązania na błędy usług zewnętrznych i umożliwiło bezpieczne ponawianie niedostarczonych powiadomień.",[10,978,84],{"id":83},[36,980,981,984,987,990,993],{},[39,982,983],{},"W systemach wielokrajowych filtrowanie odbiorców jest równie istotne jak samo publikowanie komunikatów.",[39,985,986],{},"Powiadomienie powinno być osobnym elementem domeny z własnym statusem, historią i możliwością ponowienia wysyłki.",[39,988,989],{},"Operacje zależne od zewnętrznych dostawców nie powinny blokować podstawowego procesu publikowania zgłoszenia.",[39,991,992],{},"Historia zmian i wysyłki jest niezbędna w systemach, w których liczy się możliwość odtworzenia pełnego przebiegu komunikacji.",[39,994,995],{},"GitOps ułatwia utrzymanie spójnej konfiguracji aplikacji działającej w kilku środowiskach.",{"title":97,"searchDepth":98,"depth":98,"links":997},[998,999,1000,1001,1002,1003,1004,1005],{"id":12,"depth":101,"text":13},{"id":212,"depth":101,"text":213},{"id":23,"depth":101,"text":24},{"id":33,"depth":101,"text":34},{"id":289,"depth":101,"text":290},{"id":66,"depth":101,"text":67},{"id":73,"depth":101,"text":74},{"id":83,"depth":101,"text":84},"https:\u002F\u002Fjetstreamaviation.pl\u002Fportfolio\u002Femvo-notification-system","2022-05-01","\u002Fimages\u002Fportfolio\u002Femvo-evi.webp",{},{"title":1011,"description":1012,"type":117},"EMVO EVI — powiadomienia o zdarzeniach EMVS","System obsługi zgłoszeń, subskrypcji i wielokrajowej dystrybucji powiadomień dla infrastruktury weryfikacji leków.","\u002Fportfolio\u002Femvo-notification-system",{"title":1015,"description":1016,"keywords":1017,"robots":129},"EMVO EVI — system powiadomień EMVS | Case study","Case study systemu EMVO EVI do obsługi zdarzeń operacyjnych, subskrypcji i automatycznych powiadomień dla europejskiej infrastruktury weryfikacji leków.",[1018,1019,1020,1021,1022,126,912,906],"EMVO","EVI","EMVS","ASP.NET Core","Azure",[889,1024,1022,906,912,900,924],"MSSQL","portfolio\u002Femvo-notification-system","Wielokrajowy system obsługi zdarzeń operacyjnych i powiadomień dla europejskiej infrastruktury weryfikacji leków — od rejestracji zgłoszenia po dystrybucję informacji do subskrybentów.",[136,1028,1029,1030,631],"healthcare","notifications","cloud",{"card":141},"Software \u002F Healthcare","N28g6OVh1jB1P1osWC_T6GYRKJxpvVqGOMbQvwz4G-c",1784052984497]