Changelog
Co nowego.
Każde ulepszenie platformy, jak to wysyła.
· kamo-internal
Grupa, na którą możesz włożyć ludzi i zapłacić za
Tworzenie grupy wytworzyło nazwę i nic więcej – nie ma sposobu, aby dodawać kogokolwiek, nie ma mowy Za menedżera wybrać plan, nie ma nic do zapłacenia. Otwarcie grupy pokazuje teraz, kto Płaci, kto na nim jest, kto wciąż czeka na miejsce, a plan kontroluje to. Kup te miejsca. Stworzenie jednego pyta, kto płaci, za pomocą kanonicznego zbieracza członków. Kiedyś wysyłał Konto organizacji uid, i właśnie dlatego każda grupa została obciążona Organizacja; płatnik jest teraz rozwiązany z serwerem od menedżera i KontoUid w ogóle nie jest już na drutach. W składzie wyróżnia się siedzeniem od czekania na niego. Menedżer, który stawia Osiem osób na pięciu miejscach mówi się, że trzem kolegom nie ma dostępu, a nie Dowiedziać się od nich.
· BillingService
Spraw, aby grupy rozliczeniowe działały na koniec i przestań za darmo za darmo za nic
Grupa może teraz gromadzić ludzi, kupować plan na własnej karcie swojego menedżera i ręcznie - Na zewnątrz. Nic z tego nie istniało: jedyne operacje były tworzone, aprobujące i usunąć, więc właściciel, który stworzył grupę, dostał etykietę i nie ma możliwości jej użycia. BillingGroupSeatService to miejsce, w którym pieniądze są wydawane, przechowywane Poza rejestrowaniem, więc nie można dodać nowej trasy, która przeskakiwała bramy. Subtad() odrzuca przed create() jest nazywany raczej niż po, ponieważ create() Otwiera subskrypcję na żywo Stripe — niezatwierdzona grupa, spuścizna, menedżer Bez karty lub nasadki siedzenia wszystko zatrzymuje się. Dwie wady sprawiły, że grupa uznała niewłaściwą osobę: - Płatnik był jakimkolwiek kontemUid, który organ żądający o nazwie, niesprawdzony, i Konsola wysłała organizację. Grupa rozliczana organizacji nie jest Grupa rozliczeniowa; jest to druga nazwa własnej subskrypcji organizacji. Płatnik jest teraz wywodzący się od menedżera i obaj są dochodzeniowi, aby się zgodzić Zanim cokolwiek zostanie naliczone. Wysyłanie accountUid jest odrzucane wprost. - gwar]KonfliktMamber ponownie wykorzystuje najstarszy link członkini członka, dla którego Każdy, kto kiedykolwiek został dodany do konta organizacji, jest kontem organizacji. Upewnij się, żePersonalAccountForMember identyfikuje się przez pierwotnego Członka, który jest Jedyne pole, które mówi, że konto należy do osoby. Tam, gdzie właściciel zarządza własną grupą, "swoją kartą" i "organizacją" Karta" jest naprawdę tym samym wierszem konta i tym samym klientem Stripe, i nie Kod może je rozróżnić. Odmawia się to, chyba że zostanie to potwierdzone, a nie papierowane. Nad koniec. Oddzielnie, dwa żywe robaki znalezione tutaj: - /self/seat obciążył członka i nigdy nie wydał mu miejsca. Nazywa się create() I zwrócono; nie było żadnego wiersza Subskrypcji i nie było AccountLicense, więc Kupujący pozostał nielicencjonowany, podczas gdy ich karta była rozliczana co miesiąc. - Transakcyjne w AccountBillingService dryfowało na metodę prywatną, więc Wiosna zignorowała to i zapewnia, żeAccountForMember działa bez żadnej transakcji. A A (t) Awaria między dwoma oszczędnościami osierocony Konto, następna odsłona nie mogła Zobacz, a następujące wezwanie stworzyło drugie. /self/coverage to nowe: odpowiada "kto płaci za moje miejsce", co nic nie zrobił. To również powstrzymuje kogoś już zakrytego, oferowanie drugiego miejsca na Własna karta.
· InitializerService
Schemat dla grup, które faktycznie mogą zapłacić
Trzech biegaczy: BillingGroupRosterMigration tworzy billing_pay_group_members — kogo grupa oznacza za to, że zanim cokolwiek kupi. org_id jest na to denormalizowany Tak więc baza danych może egzekwować "członek jest w większości jednej aktywnej grupy rozliczeniowej na osobę Organizacja" jako częściowa unikalna. Dwie grupy płacące za jedną osobę to dwie Zarzuty za jedno miejsce, a nic innego w schemacie by go nie złapało: Unikat unikalnych subskrypcji są w przypadku subskrypcji, a AccountLicense nie ma żadnego. Dodaje billing_group_uid do account_subscriptions I zastępuje trzykolumn unikalny dwoma częściowymi unikalnymi. Poszerzenie go do Cztery kolumny nie zadziałałyby - NULL są tutaj odrębne w unikalnym indeksie, więc Każdy niegrupowy rząd przestałby być ograniczony. BillingPayGroupColumration dodaje market_id, seat_limit i status oraz znamion Istniejące grupy LEGACY. Zostały one obciążone na każde konto, które jest zgodne z prośbą Ciało przenoszone, a konsola wysłała organizację, więc ich account_uid jest Prawie na pewno źle. Nie ma bezpiecznego sposobu, aby zgadnąć, która karta była przeznaczona, i Odgadywanie błędnych rachunków niewłaściwą osobę, więc odmawia się im w punkcie zarzutu Zamiast naprawiać. Pierwsza runda pokazała, dlaczego ponowne wykonanie ma znaczenie: oba indeksy zastępcze nie powiodły się 40001 nie mógł serializować dostępu, biegacz zarejestrował "pominięty" i kontynuował, oraz DROP starego przymusu odniósł sukces – pozostawiając account_subscriptions Bez żadnej wyjątkowości, co jest surowo gorsze niż kolizja, która to rozwiązuje. Oświadczenia DDL ponownie próbują awarii serializacji, a spadek jest pomijany Całkowicie, chyba że oba zamienniki są potwierdzone.
· kamo-shared-library
Niech grupa rozliczeniowa będzie swoim własnym płatnikiem
Grupę można nazwać i nic więcej. Jego subskrypcja nigdy nie została ustawiona – Nic na platformie o nazwie setSubscription — więc subskrypcja_uid pozostał Null na zawsze, a grupa nie mogła kupić ani usiąść. W modelu trzeba było zmienić trzy rzeczy: BillingGroupMember jest nowy. Tużna pozostaje tam, gdzie była, na SubscriptionMemberze KonkontaLicencja, ale subskrypcja subskrypcja NIE jest NULL, więc aż do Grupa kupuje plan, w którym nie ma gdzie nagrać ludzi, których istnieje, aby pokryć – Zbieranie ludzi przed zakupem miejsc dla nich jest dla nich wszystkim. To jest Intencja, to jest uprawnienie. Konksięgowanie Zyski rozliczenioweGroupUid i (konto, rynek, wyznaczają org) Unikalne ograniczenia wyjdą z jednostki. Grupa rozlicza swojego menedżera, na temat Własne konto menedżera, ukierunkowane na ten sam org na tym samym rynku, co dowolne miejsce Ten menedżer już kupił dla siebie - ta sama trójka. Więc pierwsza grupa Zakup nie zawiódł, znalazł osobistą subskrypcję menedżera i przepisał It: plan, miejsca, Stripe id. Nie ma wyjątku, bez drugiego rzędu. Reguła żyje teraz jako dwa częściowe unikalne indeksy, których WZŚP nie może wyrazić, stworzone przez KamoInitializer; Deklaracja jednostki musiała przejść lub ddl-auto odtworzyłby stare ograniczenie Przed biegaczem, który go upuszcza. - jest usuwany, a nie Zmartwychwrócił się. Zwraca opcjonalnie siłę ograniczenia, które już nie Istnieje, więc gdy jedno konto zawiera zarówno subskrypcję osobistą, jak i grupową Mecze dwa rzędy i rzuca we wszystkie pięć stron połączeń w trzech serwisach. Pozostawienie go do odnalezienia przez autouzupełnianie było bardziej niebezpieczną opcją. Również: pozwala na to, abyGroups() zawiera teraz REQUIRES_APPROVAL. Wykluczając to, uczyniło Ścieżka zatwierdzania nieosiągalna – utworzenie grupy zostało odrzucone wprost, podczas gdy Ustawienie kodu, o którą pytana jest zatwierdzona flaga wymaga zatwierdzenia () i tak samo może być tylko zawsze Odpowiedź "już zatwierdzone". Właściciel, który wybrał nadzór, nie miał żadnych grup.
· kamo-internal
Edytor kursu HR, gracz w zakresie zadań i gracz kursu
/hr/szkolenie zastępuje swojego zastępstwa dwoma sub-tabami, o które prosiłem, oraz członkiem Przydzielona siatka treningowa zyskuje kolumnę akcji, którą wstrzymywała z braku gracza. COURSES. Siatka katalogowa z publikacją, przypisaniem, powielaniem, archiwizowaniem i usuwaniem inline. Usuń jest Niepełnosprawny, gdy ktoś ma przydział i punkty przyczółkowe w Archive zamiast tego – usuwając Oczywiście, że ktoś został przydzielony do sieroty, ponieważ zadanie przypieczętuje wersję, której Rodzic by zniknął. "Nic się nie zmieniło od czasu wersji N" jest pokazywany jako INFO, a nie błąd: Autor naciskał na Publikację na temat czegoś już opublikowanego, co jest sukcesem, który źle odczytali. Odcztliwy. Zarys - płótno - inspektor jako pełnostronicowa trasa zagnieżdżona, a nie dialog — slajd Potrzebuje płótna obok konturu, a inspektor, autorstwo jest wielosesyjnym dziełem, które musi być Przeżyj odświeżenie, a hrTabMatches został napisany, więc zagnieżdżona trasa utrzymuje podświetloną zakładkę dla rodziców. MANAGE_TRAINING-gated bez nowej rejestracji. Zapisy są per-ITEM i odrzucone; PUT całego kursu Z zasilaną semantyką zamienia dwie administratorzy redagującą dwie różne jednostki w jedną cicho przegrywającą - Jego praca. Po każdym spłukaniu kurs jest ponownie odczytywany, ponieważ saveItemContent odtwarza dziecięce rzędy i Nieaktualna lokalna kopia wysyłałaby fantomowe id na następny zapis. Reordering to przyciski strzałek napędzane PrzesuńInOrder, który CLAMPS zamiast owijać - owijając ciche zarządzenie kursu, który zrobił autor Nie oznacza to, że zmienia się kolejność, a porządek czytania jest nakazem czytania. MEDIA pochodzi z /hr/resources za pośrednictwem nowej bibliotekiPickerDialog, uogólnionej MateriałPickerDialog poprzez podnoszenie dwóch twardych stałych w rekwizyty. assocType jest ORDINALNY (13), nigdy enum id (15): enum przeskoki id 6 i posyłanie ziemi i dna na innym Zasady zbiórki. Alt tekst jest wymagany na każdym liniom nośnikowym i publikowanych blokach — narzędzie autorskie Jest to jedyny punkt egzekucyjny, który faktycznie posiada, ponieważ lista kontrolna jest uruchamiana raz i alt Tekst jest dodawany nigdy. Wieczor. Wrewiduje na pierwszym GAP, nigdy najdalej osiągniętym punkcie, a każda brama związek Rekord serwera z sesją THIS - czytanie tylko wiersza serwera sprawia, że Następny nieosiągalny moment kleszcz nie został wycięty, co jest dokładnym błędem, który legalny czarodziej wysłał. Ukończenie wideo jest mierzone jako WATCHED COVERAGE, a nie currentTime. /api/imaging/stream honors HTTP Rozstaw do końca, więc uczeń może szukać ostatniej sekundy w jednym wniosku i dowolnym pozycyjnym Brama jest pokonana przez pojedynczy opór; akumulacja () traktuje skok większy niż lukę kleszczową jako poszukiwanie i poszukiwanie Otwiera nowy interwał zamiast przypisać przeskok. videoCoverage.test.ts twierdzi "szukanie Do końca plony poniżej 5%" jako pierwszy przypadek. Relacja to czas medialny, więc 2x odtwarzanie wciąż musi Obejmuje oś czasu – dlatego pozwalanie na to, aby była sprawiedliwa, a nie luka. Widoczny właz ewakuacyjny Po pojawieniu się po upływie pełnej długości filmu w czasie zegara ściany i zapisach skipReason na Postęp, ponieważ brama bez ucieczki staje się biletem wsparcia, którego odpowiedź zawsze brzmi "będziemy Oznacz go kompletny dla siebie" - ten sam wynik, nienamierzony. Biegacz testowy nie ma żadnych danych dotyczących poprawności: ładunek jest zbudowany z PlayerQuestionDto, Który nie ma takiego pola, a serwer decyduje. Odpowiedzi autoza to kasowanie, więc zamknięty laptop nie jest zagubiony Próba i puste miejsca są dozwolone, ale muszą być potwierdzone. Test npm: 3 osłony czyste, 102 pliki, 1257 testów. Następne kompilacje.
· DaemonService
Godzinowe przypomnienie o treningu kleszcz
RZEczo o :10, w przeciwieństwie do legalnego zamiatania codziennie o 13:00. Szkolenie przypomina każdy org w jego OWN local send Godzina i strefa organizacyjna obejmuje 21 godzin (Pacific/Honolulu do Australii / Sydney) — jeden Codzienny znacznik UTC oznacza, że "dzisiaj" jest błędny dla większości platformy, dlatego właśnie jest to legalne zamiatanie Mówi do najemcy z Sydney "należy dziś" w niewłaściwym dniu. Każdy kleszcz odwiedza tylko orgs, których lokalna godzina Mecze, zazwyczaj jeden do trzech, więc to skanuje mniej niż codzienną pracę cross-org, a nie 24x więcej. Praca żyje tutaj, a nie jako „Schedplaned” w DocsService, ponieważ to wdrożenie jest replikami: 1 Zintegrować: Odtwórz, więc stara kapsuła zniknęła, zanim rozpocznie się nowa, a okno wdrożeniowe nie może Podwójne ogień. DocsService to RollingUpdate with maxSurge: 1, więc na długość każdego wdrożenia Są to dwie kapsuły i obaj uważają, że jest godzina 09:00 - a dla pracy, której wydajność jest poczta do każdego zaległego Pracownik na platformie, "zwykle raz" nie jest własnością. Wyraźne limity czasowe, ponieważ domyślny RestTemplate czeka na zawsze i, w połączeniu z DisallowConcurrentExecution, jeden zaklinowany w dole rzeki zatrzymuje pracę ponownie, dopóki strąk restarty – po cichu i nieodróżniając się od „nic nie było należne”. Nigdy nie rzucaj w Quartz: a Uciekając z wyjątku może natychmiast strzelić, zamieniając skąpaną godzinę w burzę próbną.
· DocsService
Kompletny backend HR Training
Autoryzowanie kursu, publikowanie, przypisanie, gracz członkowski, ocena po stronie serwera, Dowody WORM i Zamiatanie przypomnienia — cały moduł za /api/docs/szkolenia. AUTORING. Kursy posiadają zamówiony wykaz przedmiotów, w których TEST jest PEER UNIT, nigdy dzieckiem: Ta jedna decyzja pozwala pojedynczemu modelowi wyrazić zarówno „test na końcu każdego pokazu treningowego”, jak i „a) Egzamin końcowy" bez drugiej struktury. Zapisy treści są zastępowane, ponieważ redaktor jest właścicielem Cała zamówiona lista; łatka na rzędy API wymaga podania verbplus reorder, który jest trzy rund Wycieczki i trzy szanse na zarys, który autor widzi, aby odbiegać od tego przechowywanego. PUBLISHING zamraża niezmienną wersję, która przypisuje pin. Każda reguła, do której członek jest utrzymywana, jest skopiowane na niego w tym samym PRAWIE, który pisze wiersz — moduł prawny zadeklarował te kolumny i zostawił je późniejszemu rozmówcy, który nigdy nie przyszedł, dlatego każda wersja prawna w produkcji Przenosi nieważny certyfikat. TRFP1 jest pionowo na długość, a nie przyłączony do graniczy, więc wartość spreparowana Nie można naśladować granicy pola, a jest poprawne jest INSIDE hash: moduł prawny dostarczony LPFP1 Bez równoważnego pola, opublikowanie odpowiedzieło "nic się nie zmieniło", a poprawiony układ może Nigdy nie bądź zamrożona. Publikacja odmawia z 409 nazwaniem obraźliwego przedmiotu - w tym slajdem medialnym z Brak tekstu altowego, ponieważ narzędzie autorskie jest jedynym punktem egzekucyjnym, który faktycznie posiada. GRADING JEST SERVER-SIDE, a to raczej własność zabezpieczenia niż funkcja. Gracz jest obsługiwany - które są różnymi klasami niż autorskie DTO I po prostu nie ma pola poprawności do zaludnienia - wspólna klasa z nieważnym polem wraca Po raz pierwszy ktoś ponownie używa mappera, a przeciek milczy, podczas gdy rejestr zgodności wygląda Właściwie to. Shuffle i losowanie pytań są tu obliczane i nagrywane przy próbie, więc wznowiony Sesja przedstawia te same pytania; przepychanie po stronie klienta przez klucz odpowiedzi jest teatrem. Prawidłowe odpowiedzi wracają tylko wtedy, gdy wspowoduje to na tę próbę, więc członek nie może zawieść Celowo zbierz klucz. Częściowy kredyt jest podłożony na zero (bez niego, wybierając wszystko, co wynikuje negatywnie, i przeciąga się w dół Reszta testu). Procent jest zaokrąglany dokładnie w połowie razy, więc ekran wyników, e-mail, Certyfikat i CSV nie mogą się nie zgodzić na 79,5% wobec 80% baru. Kurs bez egzaminów Po ukończeniu – możliwe - 0 nigdy nie może dzielić i nigdy nie może oznaczać "nieudanego". - Nieustępliwe. TRMF1 (to, co widzieli) i TRQZ1 (odebrane) są przechowywane jako trawniki i dosłownie Ciągi kanoniczne, ponieważ trawienie, którego nikt nie może rekomponować z przechowywanych dowodów, nie są dowodem. A. w tym, w tym, w tym, w tym, w tym, w tym, w tym, w tym, w tym, w tym, z w tym, w tym, w tym, w tym, w tym, Świadectw jest pisany dla każdego końcowego wyniku, przejścia lub porażki: "zajęli to i nie przeszli" Dokładnie tak samo, jak fakt zgodności, co przechodzenie. REMINDERS zamiata ORG-MAJOR i strzelaj do org w jej własnej godzinie lokalnej. Organizacja.timezone obejmuje 21 Godziny, więc legalny zamiatanie ZoneId.systemDefault() mówi najemcy z Sydney "należy" w niewłaściwym dniu. Wiadro pozostaje czystą funkcją (dueDate, dziś), co sprawia, że Extend Due Date jest darmowy i Zamiatający odtwarzalny; nadrabianie zaległości wysyła tylko najnowszy niewysłany kategor po awarii i rejestruje Starsze, jak się zastąpił. Idempotencja to prawdziwy stół z unikalnym indeksem roszczeń, a nie Redis SET NX, więc "mówiliśmy Ada 4 marca" przetrwał FLUSHDB i jest do zapytania - stół prawny Javadoc mówi, że jest słuszny i odroczony. Suchy bieg nie twierdzi, że twierdzi, że spaliłby każde wiadro Skontrolował i tłumił prawdziwy bieg za nim. DocsInternalAuthFilter zyskuje ścieżkę wewnętrzną szkolenia. Bez tego cross-org jest Ogromne światy, ponieważ ResourceServerConfig jest dowolną żądzcznością(). uspokojenieAll() i OTKPreAuthFilter Kontynuuje łańcuch, gdy nie ma OTK. Przejście do egzaminów 455 testów.
· kamo-shared-library
Cztery szablony e-mail treningów
Ten sam podział stałego / kropkowanego klucza, co rodzina prawna HR: CONSTANT to nazwa pliku, templateKey platforma SENDS to wartość kropkowana w każdym .json. Unieważnienie tego jest na żywo QUOTE_SENT/commerce.quote_sent bug — każdy wysyła 404 na zawsze przeciwko szablonowi, który został rozsiewany Idealnie — tak HrTrainingTemplateTest szpilki obie połówki w obu kierunkach. Jeden szablon przypominający obejmuje każdy szczebel drabiny (T-14/7/3/1, który ma być dzisiaj, a każda przeszłość zależna Okres). Ton jest noszony przez pre-rendered eye brwi i duePhrase zamiast przez oddzielne pliki, Ponieważ w połowie dodana para kanoniczna rzuca wewnątrz obciążeniaAll() - co jest tym, co nasiona Ortoką - I rozbijałby rozstawienie dla każdego orgii na platformie, a nie tylko dla własnej. Każdy szablon jest kartą w stylu liniowym 600px z kuloodpornym VML + kotwicą CTA, ponieważ Outlook's Silnik Word całkowicie ignoruje kotwicę. Test twierdzi, że ctaUrl pojawia się dokładnie trzy razy (VML href, anchor href, copy-paste fallback), że żaden szablon nie używa "if" (renderer jest płaskim regexem Zastępuje i renderowałby go jako widoczny tekst), że żaden szablon nie pociąga zasobu zewnętrznego i który Każdy z nich nosi trzy zmienne e-mailTemplateService wstrzykuje tak, że biało-labelkowe branding działa. Przedmioty korzystają z kursuTitlePlain, a ciała korzystają z kursuTytel: dwa rendery z jednej zmiennej mapy, więc Wartość zubożałej w temacie pokazuje "Employee & 39;s Handbook" w nagłówku klienta poczty, podczas gdy Nieuciekli jeden na statku ciała na żywo z własnej domeny podpisanej przez DKIM. Wiadomość e-mail nie jest wysyłana w związku z niepowodzeniem. Powiedzieć komuś przez e-mail, że mają na stałe Nie powiódł się obowiązkowy kurs, bez ścieżki do przodu i bez człowieka, jest niewłaściwym medium - HR Zamiast tego siatkę ją przenosi. 1186 egzaminów przechodzi.
· kamo-internal
Jedna powierzchnia dla obu obowiązków, jedna odznaka dla obu
Zakładka zgodności z członkami miała dwa pod-tabs, oczekujące i kompletne, dla jednego rodzaju zobowiązania. Są to państwo, a nie rodzaj – więc zapadają się w filtr statusu (z "wszystkim", który dwie zakładki Nie było gdzie się umieścić, a co wcześniej oznaczało sprawdzenie obu, aby odpowiedzieć "to było to, że to ja Podpisano?"), a pasek karty jest wolny, aby oddzielić dwie rzeczy, które są naprawdę różne: Pakiety prawne – instrumenty do podpisu, niezmienione zachowanie za filtrem Szkolenie przypisane — kursy do podjęcia Każdy panel przechowuje własny filtr na własnym param. Jeden wspólny ?filter miałby w sobie wiadro prawnym do panelu szkoleniowego, gdzie to samo słowo oznacza inny zestaw statusów i przełączanie zakładek Po cichu przefiltrowałoby drugą listę. Zagraniczny „sub” jest normalizowany na górze, ponieważ SettingsView.writeTabToUrl zestawy ?tab i usuwa ?configuruj, ale pozostawianie ?sub - ekran był Już poprawia się poprzez spadek indeksu, ale adres URL twierdził, że stan ten komponent nigdy nie miał, A adres URL jest tym, co otrzymuje zakładkę i wklejany do biletu pomocy technicznej. NavTop pokazuje teraz jedną odznakę dla obu. Członek nie myśli o "pakietach do podpisania" i "kursach do Bierz się "" jako dwa systemy, myślą o rzeczach, które są winni; dwa prawie identyczne znaki obok siebie w Stały chrom sprawi, że będą ustalać, który z nich jest zapalony. Liczenie jest sumą, a ikona zyskuje Prawdziwy stan uwagi — dwa pierścienie na ::przed/:: po owijaniu, więc sam glif nigdy się nie rusza I nigdy nie walczy z kotwicą odznaki. Ruch jest sygnałem DODATKOJE, a nie jedynym (liczbą i Kolor błędu nadal go nosi), dlatego preferuje-redukowane ruchy upuszcza go z niczym nie stracą. MemberAssignedTraining celowo wysyła kolumnę akcji NIE. trainingActionFor już decyduje Start/reume/retake/certificate i jest testowany w jednostce, ale gracz kursu nie jest zbudowany - i Widocznie wyłączony "Start" to obietnica, której produkt nie może dotrzymać, która brzmi raczej jako zepsuta, niż Niedokończony. Kolumna pojawia się z graczem. TreningStatusesFor wysyła każdy status EXPLICITLY dla "wszystkich", zamiast pominąć param: Nieprzepaść serwera nie ma, aby uratować nieaktualną zakładkę, aby nie być konwencją wzywającą. Statusy podróżują jako powtarzające się params, nigdy CSV – przecinek jest podwójnie kodowany przez bramę APIService. check-i18n-keys złapały brakujące klucze dokładnie zgodnie z zaprojektowaniem; hr.training.member. Najpierw i zweryfikowany na swoim pochodzeniu/głównym przed tym commitem. Test npm: 3 osłony czyste, 98 plików, 1221 testów.
· DocsService
Interfejs API szkolenia z członkostwom
com.kamo.z.shared.hr.training in .EnableJpaRepositories — EntityScan jest szeroki, więc Podmioty są w ten sposób znajdowane, ale repozytoria nie są. Dwa lejki autoryzacyjne, celowo oddzielające, odzwierciedlające moduł prawny: TrainingAccess gates /api/docs/training/ na MANAGE_TRAINING (RoleRightType 185) przeczytaj i napisz, Który musi pozostać dokładnie w odpowiedniej aplikacji/hrgada/hrTabs.ts, zamyka kartę. Zestaw serwera węższy niż Zakładka pozostawia członka możliwość otwarcia zakładki i zebranie 403 na pustej siatce; szerszy i jest to Punkt końcowy dostępny dla kogoś, komu UI nigdy mu nie zaoferował. Prawa są odczytywane od member_rights_applied, nigdy z tokena, ponieważ mapa sesji OTK nie ma żadnych praw. TrainingAssignOwnership autoryzuje własną powierzchnię członka przez IDENTITY. SzkoleniaMemberController W związku z tym nie ma żadnego pola TrainingAccess – członek biorący udział w kursie, który został przydzielony, nie ma Właściwy HR, a przekierowanie ich przez lejek HR oznaczałoby albo zamknięcie każdego pracownika. Własne szkolenie lub poszerzenie odpowiedniego zestawu HR, aż katalog i skład publiczności nie zostaną otwarte na Cały org. Właściciele odpowiedzi 404, nigdy 403: 403 potwierdza, że uid jest prawdziwy i obraca punkt końcowy Do wyczyściu dla tego, kto został przydzielony, a zapis oceny nie może być wyliczany przez rówieśnika. TrainingMemberSurPowierzchnieAjtTest pinsuje wszystkie trzy właściwości przez odbicie, ponieważ tryb awarii jest Ktoś później dodał pole "tak, aby punkt końcowy był również zabezpieczony". GET / Przydziały i GET / Przydziały/liczba oczekująca. Brak mapowania bierze członka lub org id - oba przychodzą Od sesji. status jest parm REPEATABLE, nigdy CSV: APIService przekazuje swój adres URL RestTemplate as a String, który koduje od% 2C do% 252C, więc przecinek przybywa jako jeden dosłowny token Dopasowanie enum nie jest stałe. Nierozpoznany filtr oznacza "bez filtra", nigdy "nic nie należy dopasowywać", więc a więc Niezwykłe zakładki pokazują wszystko, a nie pustą siatkę, która odczytuje jako utratę danych. W odczytywane strony jest repozytorium miejscowego DocsService nad współdzielonym podmiotem, posortowanym (dostarczenieAt DESC, uid DESC) — paging na nieuniknionych key duplikatach i przeskakiwania rzędów, a 200-osobowe zadanie pisze Dostarczone wewnątrz tej samej milisekundy. Oczekująca liczba zwrotów z powróci taki sam kształt JSON, jak klatka naciśniętej odznaki, więc służy jeden parser klienta Obydwa. Wiertury są odwzorowane wewnątrz transakcji; kurs i wersja to LAZY. Przejście do egzaminów na 413 testów.
· InitializerService
Stwórz schemat szkolenia HR
Rejestry com.kamo.z.shared.hr.training w jawnej liście „EnableJpaRepositories) — EntityScan jest szeroki, więc jednostki są znajdowane w obie strony, ale repozytoria nie są, i Bez tego tabele nigdy nie są tworzone. TrainingSchemaMigration (Dział(43); 42 jest legalny) dodaje to, czego jednostka nie może express. Pięć indeksów UNIQUE traktuje się jako śmiertelne, ponieważ każdy z nich jest gwarancją poprawności Nic innego nie przewiduje: - (course_uid, version_number): dwa równoległe publikacje w przeciwnym razie wykrocząłyby tę samą liczbę A każde zadanie przypięte do "wersja 3" staje się niejednoznaczne. - jeden atest na zlecenie: Strażnik WORM blokuje AKTUALIZACJA i DELETE, a nie duplikat INSERT. - wskaźnik zgłoszenia jest idempotencja całego rurociągu przypominającego — roszczenie Jest INSERT przeciwko niemu, więc bez tego ponownie przeczesuje ponowne wysyłanie wiadomości każdego zaległego pracownika Każdy lokator. - jedna recertyfikacja na (członek, kurs, termin); jeden wiersz postępu na slajd. Kurs-deks jest napisany jako dwukolumnowy predykat w celu: SchemaCleanupRunner Zamówienie(1) upuszcza każdą pojedynczą kolumnę IN-list CHECK na każdym biegu, ponieważ taki jest kształt Hiberntan emituje dla ’Enumerated(STRING). CoacarachDB/Yugabyte nie ma DZIAłANIA TREŚCIINT IF NOT WYSTAWY dla CHECK, więc "już istnieje" zostaje złapany i traktowany jako sukces. Test jest tylko na odbicie. mvn test w tym repo MIGRATES PRODUKCJI — DemoApplicationTests Jest to "SpringBootTest bez profilu testowego i bez src/testu / zasobów — tak więc zapewnia SQL Stawami w stosunku do nazwy tabeli każdej jednostki bez rozpoczynania kontekstu, który jest porażką Tak się dzieje: zmienia nazwę stołu, pozostawiając za sobą ręcznie pisany SQL.
· kamo-shared-library
Przesyłaj dokumenty i dodaj schemat szkolenia HR
ImageService zyskuje uploadDocumentStreaming, odpowiednik przesyłania strumieniowego przesyłaniaDokumentu. Formularz buforowany nie może w ogóle wyrazić dużego przesłania: readInputStream przydziela Nowy bajt[size], Java macierz Java góruje na Integer.MAX_VALUE i MinIOStorageService.upload Następnie przydziela drugą pełną kopię do ponownego prób cross-node. Przeciw pojemnikowi 2Gi, który stworzył Kilkaset megabajtów praktycznego sufitu – dlatego wieloczęściowa czapka do obrazowania usiadła 500MB, podczas gdy ścieżka zamocowania czatu, która już przesyła, biegła do 3 GiB. Nowa metoda ma się raz (oba trawi jeden bufor), wącha typ MIME PRZED hash pass, więc błędnie oznaczony plik 3 GiB jest odrzucany bez wydawania pełnej informacji na nim, i Deleguje przechowywanie do nowego sklepuZaspokojone przeciążeniem, które akceptuje wstępnie liść trawników Tak więc źródło jest odczytywane dwa razy, a nie trzy razy. Celowo nie ?Transagowe: przechowywanie gigabajty trwają kilka minut, a transakcja trzymana na nim przypina połączone połączenie. Dodaje również com.kamo.shared.hr.training — 20 podmiotów, 11 ens i 20 repozytoriów dla Moduł szkoleniowy HR, mirroring com.kamo.z.shared.hr.legal's Conventions: BaseUuidEntity, an Wyraźnie last_modified with touch() ponieważ BaseUuidEntity.dateUpdated jest martwy, przypięty KolumnaDefinicja na temat każdej opcjonalnej kolumny i "OnDelete(CASCADE) na widowni FK Ponieważ CascadeType. nie emituje nic w DDL. Dwa odejścia od prawnego planu, oba celowe: - TrainingAssignmentStatus dzieli FAILED na FAILED_ETAKE_ALLOWED (open) i FAILED_FINAL (zamknięte) i REMINDABLE_STATES są ściśle węższe niż OPEN_STATES, więc członek czekający Oklaski nigdy nie są ścigane. Przypisane przez TrainingAssignmentStatusTest. - hr_training_notification to prawdziwa tabela z indeksem roszczeń UNIQUE zamiast Redis SET NX, więc "czy powiedzieliśmy im w tym dniu" przetrwają FLUSHDB i są do zapytania. Repozytoria odpowiedzi i atestacji rozszerzają nagie znacznik repozytorium, a nie JpaRepository, więc Wsad usuwaj metody usuwania, które omijają PreReusteve nie istnieją w interfejsie. Wymaga usługi KamoInitializerService uruchomionej przed wdrożeniem jakiejkolwiek usługi, która odczytuje te tabele.
· InitializerService
Utwórz billing_pay_groups wyraźnie i cofnie starą kolizją
Tworzenie go w migracji, a nie pozostawienie go ddl-auto, który wytworzył W połowie zbudowany stół, gdy jednostka została wycelowana w istniejących wcześniej billing_groups.
· EmailService
Rozmiar każdej skrzynki pocztowej w stosunku do miejsca przechowywania organizacji
Dovecot wymusza teraz limit na skrzynkę pocztową i nic nie ustawiało jej kolumny Czytam. To pisze to co noc, po migawce magazynu, więc pojemność jest Podzielona jest raczej dzisiejszą figurą niż wczorajszą. Każda skrzynka pocztowa otrzymuje równy udział w całym puli. Liczy się nieruchomość, która ma znaczenie, to Że akcje sumują się do pojemności, więc poczta nigdy nie może sama wziąć Organizacja poza swoim sufitem. Jest to również wersja, którą właściciel może przewidzieć – "Twój udział w przechowywaniu organizacji" - i nie porusza się, gdy Niezwiązana ze sobą domena rośnie, co daje każdej skrzynce pocztowej pozostałą stronę. Nieograniczone plany i organizacje bez subskrypcji na żywo piszą 0, które user_query zamienia się w NULL kwot_rule: brak limitu. Ten kierunek jest celowy, Ponieważ alternatywą jest odmawianie poczty dla organizacji, której wiersze rozliczeniowe Brakuje lub migracja.
· KlusterServices
Wzmocnij limit przechowywania na dostawie przychodzące i przestań wysyłać hasło DB
Przechowywanie obiektów już odrzuca treści, które przenoszą organizację poza nią sufit, który obejmuje każdą domenę przechodzącą przez sklep obiektowy. Poczta dopływowa Była luka: LMTP pisze prosto do maildir bez Javy na ścieżce, więc Wtyczka kwot Dovecot jest jedyną rzeczą, która może ją zatrzymać. Wtyczka ładuje się globalnie, więc dostawa LMTP jest sprawdzana - to jest ścieżka, która Sprawy - z imap_quota dodanym dla klientów, którzy pytają. Celowo nie ma Domyślny limit_rule: globalna reguła miałaby zastosowanie do każdej skrzynki pocztowej, którą robi użytkownikdb Nie odpowiada, co jest przeciwieństwem niepowodzenia. Zasada na użytkownika z user_query jest jedynym źródłem limitu i jest to NULL, dopóki nie jest kwot_bajtes Zestaw, więc każda z 18 istniejących skrzynek pocztowych jest nieograniczona dokładnie tak, jak wcześniej. Virtual_users.quota_bytes został dodany przed tym, domyślnie do 0, ponieważ Nowe odniesienie user_query: wysyłka konfiskaty najpierw zawiodłaby każdego userdb lookup i poczta w dół. Wygenerowany dovecot.conf został zatwierdzony z gołąbą przeciwko live 2.3.20 Przed pchnięciem; postanawia "przesieć kwotową" dla LMTP i "quota imap_quota" Dla IMAP. Oddzielnie ConfigMap nie ma już hasła MySQL. On został wysłany do zwykły tekst, zaangażowany tutaj i czytelny od .kubectl get cm -o yaml. A w poty, wt., w pot wt., w tym, w tym, w tym, że posą o., w tym, w tym, a tym, z ws., w tym, w tym, a w tym, a tym ConfigMap posiada teraz zastępczy symbol, a poświadczenia pochodzą z istniejącego Mysql-secre przez środowisko; pojemnik odmawia rozpoczęcia, jeśli jest Próżnia zamiast działać z nieużyteczną userdb. Zauważ, że to edytuje -prosta para, która jest tym, co CI ma zastosowanie i co tak naprawdę jest Bieganie. dovecot-configmap.yaml i dovecot-deployment.yaml są stosowane przez Nic i zupełnie nie opisz innego obrazu.
· KlusterServices
Wzmocnij limit przechowywania na dostawie przychodzące i przestań wysyłać hasło DB
Przechowywanie obiektów już odrzuca treści, które przenoszą organizację poza nią sufit, który obejmuje każdą domenę przechodzącą przez sklep obiektowy. Poczta dopływowa Była luka: LMTP pisze prosto do maildir bez Javy na ścieżce, więc Wtyczka kwot Dovecot jest jedyną rzeczą, która może ją zatrzymać. Wtyczka ładuje się globalnie, więc dostawa LMTP jest sprawdzana - to jest ścieżka, która Sprawy - z imap_quota dodanym dla klientów, którzy pytają. Celowo nie ma Domyślny limit_rule: globalna reguła miałaby zastosowanie do każdej skrzynki pocztowej, którą robi użytkownikdb Nie odpowiada, co jest przeciwieństwem niepowodzenia. Zasada na użytkownika z user_query jest jedynym źródłem limitu i jest to NULL, dopóki nie jest kwot_bajtes Zestaw, więc każda z 18 istniejących skrzynek pocztowych jest nieograniczona dokładnie tak, jak wcześniej. Virtual_users.quota_bytes został dodany przed tym, domyślnie do 0, ponieważ Nowe odniesienie user_query: wysyłka konfiskaty najpierw zawiodłaby każdego userdb lookup i poczta w dół. Wygenerowany dovecot.conf został zatwierdzony z gołąbą przeciwko live 2.3.20 Przed pchnięciem; postanawia "przesieć kwotową" dla LMTP i "quota imap_quota" Dla IMAP. Oddzielnie ConfigMap nie ma już hasła MySQL. On został wysłany do zwykły tekst, zaangażowany tutaj i czytelny od .kubectl get cm -o yaml. A w poty, wt., w pot wt., w tym, w tym, w tym, że posą o., w tym, w tym, a tym, z ws., w tym, w tym, a w tym, a tym ConfigMap posiada teraz zastępczy symbol, a poświadczenia pochodzą z istniejącego Mysql-secre przez środowisko; pojemnik odmawia rozpoczęcia, jeśli jest Próżnia zamiast działać z nieużyteczną userdb. Zauważ, że to edytuje -prosta para, która jest tym, co CI ma zastosowanie i co tak naprawdę jest Bieganie. dovecot-configmap.yaml i dovecot-deployment.yaml są stosowane przez Nic i zupełnie nie opisz innego obrazu.
· kamo-internal
Pozwól członkowi licencjonuj własne miejsce z konsoli
Panel, który zamyka pętlę na samopłatnie. Pojawia się tylko tam, gdzie Polityka organizacji na to pozwala, a tylko dla osób, które jeszcze nie kupują W imieniu organizacji — serwer sprawdza tę samą politykę, więc ukrywając ją Jest to raczej uprzejmość niż kontrola. Mówi, kto zostanie obciążony przed poproszeniem o kartę, a w trybie zatwierdzenia mówi Oczywicie, że nic nie jest obciążone, dopóki właściciel się nie zgodzi. Niezbędziony czytania Kwalifikacja kryje w sobie opcję, a nie pokazuje takiej, która zostałaby odrzucona. Kiedy polityka nie pozwala samo-płatnym na wybór planu, biorą ten, który jest Organizacja jest w toku, a zbieracz nie jest pokazywany w ogóle - właściciel delegujący koszty Zazwyczaj nie deleguje decyzji dotyczącej produktu.
· BillingService
Powiedz ludziom, że ich płatność nie powiodła się i zachwić subskrypcję
Wyniszczenie, które nie istniało. invoice.payment_failed napisało wiersz i zwróciło: Nie skonfigurowano e-maila, bez flagi, brak zmiany stanu, a billing.email-service-url Odwołane jest w ogóle kod Java. Webhook teraz uruchamia zegar zawierciowy przy pierwszej awarii i go usuwa w momencie płatności faktury, więc odzyskana subskrypcja zaczyna się od nowa niż noszenie nieświeżego zegara w następnej awarii. Tylko pierwsza porażka zaczyna się – próba tydzień później nie może ponownie uruchomić wdzięku i ręki, nie płacąc Subskrypcja kolejnego tygodnia usług przy każdej próbie. Zamiatanie celowo nie decyduje, kiedy uprawnienia się zatrzymują. To się dzieje na Zegar, w isLive, więc nie ma okna, w którym nadal wygasła subskrypcja Działa dlatego, że praca nie działa. To, co posiada, to to, czego zegar nie może zrobić: Mówiąc to raz i anulując po autoCancelAfterDays — trzydzieści dni po Umyślnie, długo po zatrzymaniu usługi. Ta luka jest odzyskiwalna celowo, ponieważ klient, który aktualizuje swoją kartę w dniu dwudziestym, otrzymuje wszystko z powrotem, płacąc, A anulowana subskrypcja jest znacznie trudniejsza do cofnięcia niż zawieszona. Powiadomienie jest najlepszym wysiłkiem według projektu: Stripe retries a webhook, który odpowiada Nie-2xx, a nieosiągalna usługa poczty nie jest powodem do ponownego przetwarzania płatności Impreza.
· InitializerService
Dodaj kolumny zegara ociekającej
past_due_since i suspension_notified_at, oba zniweczone. Null oznacza płacenie i Nie tylko opowiedziany.
· kamo-shared-library
Wykonaj nieudaną płatność oznacza coś
PAST_DUE liczony jako w pełni żywy w obu delegujących uprawnieniach, bez znacznika czasu I brak czapki, więc klient, którego karta nie powiodła się, zachował każdą funkcję tak długo, jak Stripe opuścił subskrypcję w tym stanie – w nieskończoność w praktyce – oraz Nikomu, klientowi ani operatorowi, nie powiedziano. Subskrypcja teraz nosi, gdy kolekcja po raz pierwszy nie powiodła się, a PAST_DUE jest na żywo Tylko w okresie jego łaski. Informowany o czytaniu, dokładnie tak jak próbne wygaśnięcie, Tak więc obsługa zatrzymuje się na zegarze, zamiast czekać, aż zadanie zostanie zauważone: a Zaplanowana decyzja pozostawiłaby okno, w którym niewygasła subskrypcja nadal działała Bo zamiatanie jeszcze nie uciekło. Dwa świadome wybory dotyczące bycia w błędzie. Grace nie domyślnie do tygodnia, a nie Zero, ponieważ nieudana płatność jest zwykle kartą wygasłą, a nie decyzją o Przestawaj płacić. I przeszły rząd bez stempli nieczytanych jako wewnętrzna gracja, ponieważ Każda subskrypcja poprzedzająca kolumnę ma jedną - traktowanie zerowego jako wygasłego Odcięliśmy klientów w momencie, gdy to zostało wysłane, za przestępczość, której nikt nie miał Zmierzone.
· BillingService
Pozwól członkowi kupić własne miejsce i zamknąć otwór na karpas.
Samopłacenie nie miało drogi wewnątrz konsoli. Wszystko na powierzchni rozliczeniowej jest W kształcie konta – musisz już posiadać wiersz kontaMemior, zanim Plans & Billing pokazuje wszystko, a jedynym sposobem, aby go zdobyć, była akcja właściciela - więc Członek, który chciał zapłacić za siebie, został poproszony o pójście i poproszenie. Ten prawdziwy ścieżka samopłatna, która istniała, kasa konsumencka używana przez produkty z etykietą białą, Jest nieosiągalny z konsoli, ponieważ proxy rozliczeniowego odbudowuje nagłówki od Zadrapanie i nigdy nie przekazuje ciastka, których potrzebuje jego resolwator sesji. Nabywcą jest każdy, kto powie, że jest. X-Member-Id jest ustawiany przez proxy Od - i nigdy przez dzwonijącego, więc w przeciwieństwie do tras kont nie ma id Na drodze wymiany – co jest całym powodem, dla którego jest to raczej jego własna droga. w przypadku innego prowadnicy pod /konto/konkontoUid. Polityka organizacji jest Sprawdzona część serwera, więc ukrywanie opcji w innym miejscu to coś więcej niż dekoracja, i Nasadka do fotela jest egzekwowana tutaj, a nie zaufana od klienta. Również zamyka otwór cross-tenant na POST /kony. Ta droga nie ma anid konta W swoim PATH, więc AccountOrgScopeInceptor — który klucze w tym segmencie — nigdy Zobaczył go, a każdy członek jakiejkolwiek organizacji mógł wymienić inną organizację Konto w BODY i przepisać swój kontakt rozliczeniowy, przekierowując faktury i Pokwitowania. Nazwanie arbitralnego członkaId zrobiło to samo z innego kierunku. Obaj są teraz zobowiązani do przynależności do organizacji dzwoniącej.
· kamo-internal
Pozwól właścicielowi zdecydować, kto może zapłacić i zarządzaj grupami, które to robią
Właściciel miał dokładnie jedną dźwignię nad samopłatnością i był to niewłaściwy kształt: Nieodwracalny gest promujący, bez możliwości wcześniejszego powiedzenia, czy tak było Zezwolona i niemoże jej cofnąć – podczas gdy osobna ścieżka wyboru konsumencka pozwala Samodzielny subskrybuje się niezależnie. To jest kontrola prewencyjne i to jest to, co Reszta konsoli ukrywa się. Cztery tryby, oznaczone przez to, co dzieje się z innymi ludźmi, a nie przez enum: Właścicielem wybierającym polisy decyduje o tym, co ich pracownicy mogą zobaczyć, więc to jest to, co każdy z nich Opcja opisuje. Grupy pojawiają się tylko wtedy, gdy tryb im na to pozwala, ponieważ grupa Jest to kilka osób, które robią to, co jedna osoba może już zrobić. Nie-właściciele widzą ustawienie tylko do odczytu, a nie w ogóle. "Organizacja Płace centralnie" jest odpowiedzią na pytanie, dlaczego nie mają opcji zakupu i ukrywają się To tylko zamienia to w pytanie wspierające. Załadunek awarii tej sekcji sprawia, że zbieracz planu działa – polityka Konsola nie może odczytać nie powinna z nią spuścić powierzchni kupna.
· BillingService
Org polityka i grupy rozliczeniowe APIs
Kto w organizacji może zapłacić Kamo jest teraz odpowiedzialny i ustawiony. Oba Trasy są lubg-scoped i dlatego celowo na zewnątrz /api/billing/konta/ . I jego przechwytujący: "Niech mój personel kupuje własne miejsca" musi być odpowiedzialny Zanim pojawi się drugie konto, czyli dokładnie wtedy, gdy ustawienie konta Nie mogę pomóc. Czytanie polityki jest otwarte dla każdego członka, ponieważ powierzchnie, które się ukrywają Kontrola samopłatności potrzebuje odpowiedzi i nie ujawnia niczego, co mogłoby działać. Wszystko To zmienia się, kto płaci za to, co jest tylko dla właścicieli, sprawdzane w bazie danych Zamiast nagłówka dostarczana przez dzwoniciela. Tworzenie grup odmawia, gdy organizacja nie zezwala na grupy – grupa Jest to kilka osób, które robią to, co jedna osoba może już zrobić, więc organizacja, która Nie zezwala na to samopłatność również tego nie pozwala. W obszarze REQUIRES_APPROVAL grupa zaczyna niezatwierdzona; każdy inny tryb już dał pozwolenie na kocyk, Tak więc zaczyna się zatwierdzać. Uniemożliwienie grupy rozpuszcza nazewnictwo, a nigdy Rachunek: anulowanie tego, za co płaci, jest oddzielnym, celowym działaniem. Lista pochodzi z własnych rzędów subskrybentów subskrypcji, a nie z rzędów Jest powielany w grupie, więc grupa nie może nie zgodzić się z miejscami, które jest Płacę za to.
· kamo-internal
Konsola Planów i rozliczenia, która właściciel może znaleźć
Nie było sposobu, aby kupić pierwszą subskrypcję w dowolnym miejscu w produkcie. Listy planów Były dostępne tylko dzięki subskrypcji, którą już posiadałeś, więc organizacja Żaden z nich nie widział pustego panelu i wskazówkę wskazującą na inny org. Ten jeden Prawdziwy ekran rozliczeniowy był na stronie profilu członka, zlecony w ustawieniach Lądowanie pod nagłówkiem zatytułowanym "Osobista" i było całkowicie ukryte dla każdego orgu Bez rodzica. Domowy bar administratora w ogóle nie miał wejścia do rozliczeń. Konsola odpowiada na pytanie, z którym przychodzi właściciel – na czym jesteśmy, co Czy to kosztuje, jak to zmienić – i cenuje każdą kontrolę, gdy ją poruszają, więc Suma jest widoczna, zanim cokolwiek się powściągnie. Miejsca, dodatki i przechowywanie blokuje każdą aktualizację bieżącej liczby, a roczny plan określa, co tak naprawdę Zostawia konto raz w roku, zamiast zostawiać numer miesięczny, aby być Pomnożył się w głowie czytelnika. Irytmetyka jest planQuote, testowana na ścieżce ładowania, ponieważ oba miejsca Platforma wcześniej podała cenę, która nie zgadzała się z tym, co ogłoszono Stripe. Dotarte do paska administratora domu i menu ustawień, zamknięte na finansowanie Prawa zamiast CONFIGURE_SYSTEM: decydowanie o tym, za co organizacja płaci Decyzja finansowa.
· BillingService
Domyślić docelową org nowej subskrypcji do dzwoniący
Pierwszy zakup nie ma istniejącej subskrypcji, aby kopiować targetOrganizationId od, I wymaganie go bezwarunkowo jest tym, co stworzyło jeden z konsoli Niemożliwe. Organizacja kupująca miejsca dla siebie jest częstym przypadkiem; Układ pod-org nadal przechodzi ją wyraźnie.
· kamo-internal
Pokaż sufit, nie tylko bajty
Panel przechowywany, dodany, dodano, usunięty i bajtów życia na brak W ogóle odniesienie - więc właściciel patrząc na 400 GB nie miał możliwości, aby stwierdzić, czy to Było wygodne lub zamierzając odmówić ich następnego przesłania, a ich pierwsza wiedza Problem byłby zagubiony. Dodatek planowy i bloki Kupione są to, co zamienia te liczby w pozycję, więc pasek pojemnościowy idzie Najpierw i wszystko poniżej czyta się przeciwko. Bar pozostaje zielony, dopóki nie ma znaczenia: bursztyn w 90%, czerwony w pełni. Bar, który Wznosi się, że bursztyn w 60% uczy ludzi ignorować to. Mówi również wyraźnie, że odczyty nie są naruszone. "Storage jest pełny" - czytamy w "I Stracił dostęp do moich plików" chyba, że powiesz inaczej, i jedno Wypełnienie przechowywania nigdy nie jest zabieraniem czegokolwiek. Rozmiar bloku i cena pochodzą z katalogu, a nie z zakodowania, więc "Dodać 10 GB za 2,50 USD miesięcznie" linia nie może dryfować z tego, co faktycznie jest obciążone.
· SecurityService
Promocja bramy na politykę właściciela, uczynij go idempotentem, dodaj jej odwrotną
Promowanie członka do płacenia za siebie było bezwarunkowe, jednokierunkowe i niebezpieczne. Powtarzam. Teraz odmawia, gdy polityka organizacji mówi, że organizacja płaci za Wszyscy – wcześniej jedyną kontrolą nad samopłatnością była nieodwracalna O samym geście. Jest to idempotentne. Promowanie tego samego członka dwukrotnie wstawił drugie konto i drugi wiersz AccountMember, potykając unikalny na member_id i odpowiadając na 500; Kliknij dwukrotnie, próba po przesunięciu sieci lub właściciel ponownie uruchamiający przepływ do Przesuń więcej licencjobiorców, którzy uderzą w niego. Teraz ponownie wykorzystuje konto i subskrypcję Członek ma i kontynuuje transfer. demoteBillingOwner jest odwrotnością, która nigdy nie istniała. Właściciel, który awansował Ktoś nie mógł tego cofnąć, a jeśli ten członek odejdzie, to jego osobista Konto nadal posiadało miejsca skierowane do organizacji bez możliwości odzyskania - Idją. Licencjobiorcy wracają do subskrypcji organizacji i Samopłatnik zostaje zatrzymany; ich Konto i metody płatności są pozostawione same sobie, Ponieważ odzyskiwanie miejsc, od których zależy organizacja, jest sprawą firmy właściciela i Usuwanie czyjejś historii rozliczeniowej nie jest. Dopasowanie jest na współdzielonym użytkowniku, a raczej Id, ponieważ awans odzwierciedla członka na orgę najwyższego poziomu. Naprawia również grzechotkę o punkty końcowe, która była już nieufana na głównej pozycji. Praca – więc budowa SecurityService była czerwona i nic się nie rozmieszczało. Skan Nie mógł zobaczyć dwóch prawdziwych ochraniaczy: extractUserId odczytuje userID z ;) Sesja i wymagaTopLevelOwner przechodzi przez CallerSessionResolver i dalej Wymagający dzwoniący jest posiadający orgi najwyższego poziomu. Nauczanie go oba pozwoliło 25 nieświeżym Wpisy bazowe są usuwane, co jest dokładnie tym, o co prosi plik – naprawiający Punkt końcowy oznacza usunięcie jego linii, a to właśnie powoduje spadek liczby. runSweep jest bazowy z jego prawdziwym powodem: wewnętrzny dźwięk strzeżony przez Parametr nagłówka, do którego nie może dotrzeć ciało-skan.
· InitializerService
Zrezygnuj z ograniczenia, które ograniczyło członka do jednego konta rozliczeniowego
Ustala ograniczenie przez kształt, a nie z imienia i nazwiska - Hibernate go wygenerował i Nazwa nie jest stabilna w różnych środowiskach, więc wszelkie unikalne ograniczenie Członkowie account_members, których lista jest dokładnie member_id, jest tym, który upuszcza. The Pot w, w której poncie, ws., w tym, w tym, w tym, w tym, w tym, w tym, w tym, w tym, w tym, w tym Kompozyt on (account_uid, member_id) ma dwie kolumny i nigdy nie pasuje.
· kamo-shared-library
Niech członkowie i grupy płacą za siebie, zgodnie z zasadami właściciela
Brakowało trzech rzeczy, a jedna była aktywnie szkodliwa. Członkowie account_members mieli globalny unikalny namember_id — członek mógłby administrować Dokładnie jedna platforma rozliczeniowa w całym kraju. To jest sufit, który stworzył każdy Rozkład wielu płatników jest niemożliwy i to już wyrządzał szkody, a nie Po prostu ograniczenie: AccountMemberService.add() pracował wokół niego po cichu DELETING wiersz członka na poprzednim koncie, więc udzielając dostępu do rozliczeń Aby jeden unieważnił go od drugiego bez ostrzeżenia; i promowaćToBillingOwner rzucił Naruszenie ograniczenia w drugim biegu, zamieniając podwójne kliknięcie w 500. A w pog, w pog w, pon, w pocjach, w pog w, w pot w tym, w pon, w pos o., w pog w Kompozyt unikalny na (account_uid, member_id) już istniał i wyraża: Prawdziwa reguła. Upuszczenie go sprawia, że pochodzi znaleziskByMemId zwraca opcjonalny rzut w momencie Każdy trzyma dwa rzędy, a dziewięciu dzwoniących na to polega. Jest to teraz ograniczone zapytanie Zamówione przez stworzenie, więc ci dzwoniący zachowują odpowiedź, którą mieli, zamiast W zależności od tego, co najpierw powróci planista; znajdźAllByMemId Kod, który chce ich wszystkich. OrgBillingPolicy jest kontrolą prewencyjną, której właściciel nigdy nie miał. 2026-04-16 Zaprojektuj określony billing_delegation_mode i wdrożenie go upuścił, pozostawiając Jeden nieodwracalny promuj gest i nie ma sposobu, aby powiedzieć z wyprzedzeniem, czy Samopłatność była w ogóle dozwolona – podczas gdy oddzielna ścieżka wyboru konsumenta pozwala Członkowie i tak samo się zsubskrybują. W przypadku, gdy tryb zabrania czegoś, co ukrywają powierzchnie Zamiast oferować go i zawodzić. BillingGroup nazywa umowę, którą model już dozwolony: KontoSubskrypcja Jest wyjątkowy (konto, rynek, cel lubg), więc kilka kont zawsze może Celuje w jedną organizację. Nie nosi własnego składu – kto ma miejsce siedzący Subskrypcja plus AccountLicense, taka sama odpowiedź jak dla każdego innego, a także Drugi skład byłby drugą prawdą, aby utrzymać się na kroku.
· SecurityService
Wyrażanie otwartego wpływu na operatora
Strona katalogu aplikacji już odpowiada na pytanie niezależne od orgów — robi dowolne Plan wspomina tę aplikację - i podnosi ostrzeżenie, gdy tego nie robi. To odpowiada na Najemca jeden za nim, który tak naprawdę odwraca zawór.

Twój wszechświat czeka.
Rozpocznij bezpłatnie w kilka minut.Dodaj produkty, kiedy tylko będziesz gotowy — bez migracji, bez nowych logowań.
Bezpłatne na zawsze — nie wymaga karty kredytowej