Direkt zum Inhalt
KAMOUniverse

Changelog

Was ist neu?

Jede Verbesserung an der Plattform, wie sie geliefert wird.

  1. · kamo-internal

    Eine Gruppe, die man aufstellen und bezahlen kann

    Erstellen einer Gruppe produziert einen Namen und nichts anderes - keine Möglichkeit, jemanden hinzufügen, keine Möglichkeit, eine Möglichkeit, eine Möglichkeit, eine Möglichkeit für seinen Manager einen Plan zu wählen, nirgendwo zu zahlen. Eröffnung einer Gruppe zeigt jetzt, wer zahlt, wer ist darauf, wer wartet noch auf einen Sitzplatz, und der Plan kontrolliert, dass Diese Sitze kaufen. Erstellen Sie eine Frage, wer zahlt, mit dem kanonischen Mitglied Picker. Es verwendet, um zu senden das Konto der Organisation uid, das ist genau, warum jede Gruppe in Rechnung gestellt wurde die Organisation; der Zahler wird nun vom Manager auf Serverseite aufgelöst und accountUid ist überhaupt nicht mehr auf dem Draht. Der Dienstplan unterscheidet, dass man einen Sitzplatz von Wartezeiten auf einen hat. Ein Manager, der setzt acht Personen auf fünf Sitzplätzen wird gesagt, drei Kollegen haben keinen Zugang statt von ihnen erfahren.

  2. · BillingService

    Machen Sie Abrechnungsgruppen arbeiten Ende zu Ende, und stoppen Selbstzahlung Gebühren für nichts

    Eine Gruppe kann jetzt Leute sammeln, einen Plan auf der eigenen Karte ihres Managers kaufen und seine Sitze aus. Nichts davon existierte: die einzigen Operationen wurden erstellt, genehmigen und löschen, so dass ein Besitzer, der eine Gruppe gemacht hat ein Label und keine Möglichkeit, es zu verwenden. BillingGroupSeatService ist, wo Geld bewegt und Anspruch ausgestellt wird, gehalten abgesehen von der Aufzeichnung, so dass keine neue Route hinzugefügt werden kann, die die Tore überspringt. abonnement() refuses before create() wird aufgerufen und nicht danach, weil create() öffnet ein Live Stripe-Abonnement - eine nicht genehmigte Gruppe, eine Vermächtnis, einen Manager mit keiner Karte oder einer Sitzkappe brechen alle dort zu stoppen. Zwei Mängel machten die Gruppenrechnung zur falschen Person: - Der Zahler war, was auch immer KontoUid die Anfrage Körper benannt, unkontrolliert, und die Konsole schickte die Organisation. Eine Gruppe, die der Organisation in Rechnung gestellt wird, ist kein Billing-Gruppe; es ist ein zweiter Name für die Organisation eigenen Abonnement. Der Zahler wird nun vom Manager abgeleitet und die beiden werden zustimmen bevor etwas angeklagt wird. Das Senden von KontoUid wird komplett abgelehnt. - sicherstellenAccountForMember verwenden die älteste Kontomitglied-Link des Mitglieds, die für Jeder, der jemals auf das Konto der Organisation aufgenommen wurde, ist das Konto der Organisation. sicherzustellen, dassPersonalAccountForMember von den primären Mitgliedsgruppen identifiziert wird, was das einzige Feld, das sagt, dass ein Konto einer Person gehört. Wo ein Eigentümer verwaltet seine eigene Gruppe, "ihre Karte" und "die Organisation ist Karte" sind wirklich die gleiche Konto-Reihe und der gleiche Stripe-Kunde, und keine Code kann sie auseinander halten. Das wird abgelehnt, es sei denn bestätigt, anstatt Papiere über. Getrennt, zwei Live-Bugs gefunden, während in hier: - /self/seat hat das Mitglied belastet und ihm nie einen Sitz erteilt. Es heißt create() und zurückgekehrt; es gab keine Abonnementmitglied-Reihe und keine Kontolizenz, so Der Käufer blieb ohne Lizenz, während seine Karte jeden Monat in Rechnung gestellt wurde. - @Transactional in AccountBillingService hatte sich auf eine private Methode gedriftet, so Spring ignorierte es und stellte sicher, dassAccountForMember ohne Transaktion lief. A Ausfall zwischen seinen beiden spart verwaist ein Konto die nächste Suche konnte nicht siehe, und der folgende Aufruf erzeugte einen zweiten. /self/coverage ist neu: Sie antwortet "wer für meinen Sitz bezahlt", was nichts getan hat. Es ist auch, was verhindert, dass jemand bereits abgedeckt wird ein zweiter Sitz angeboten angeboten ihre eigene Karte.

  3. · InitializerService

    Schema für Gruppen, die tatsächlich zahlen können

    Drei Läufer: BillingGroupRosterMigration erstellt billing_pay_group_members, die eine Gruppe bedeutet, dafür zu bezahlen, bevor es etwas gekauft hat. org_id ist darauf denormalisiert so kann die Datenbank durchsetzen "ein Mitglied ist auf höchstens eine aktive Abrechnungsgruppe pro Organisation" als teilweise unique. Zwei Gruppen, die für eine Person zahlen, sind zwei Gebühren für einen Sitz, und nichts anderes im Schema würde es fangen: Die Einzigartigkeit von Abonnements sind pro Abonnement und AccountLicense hat keine. **************** fügt billing_group_uid zu account_subscriptions hinzu und ersetzt die dreispaltige Unikat mit zwei Teil-Unikaten. Verbreitet es auf vier Spalten würden nicht funktionieren - NULLs sind in einem einzigartigen Index hier deutlich, so Jede gruppenunabhängige Reihe würde aufhören, eingeschränkt zu werden. BillingPayGroupsMigration fügt market_id, seat_limit und Status und Noten hinzu bestehende Gruppen LEGACY. Diese wurden auf jedes Konto uid die Anfrage in Rechnung gestellt Körper getragen, und die Konsole schickte die Organisation, so dass ihr Konto_uid ist fast sicher falsch. Es gibt keinen sicheren Weg zu erraten, welche Karte gemeint war, und Erraten falsche Rechnungen die falsche Person, so dass sie an der Ladung Punkt verweigert werden statt repariert. Der erste Lauf zeigte, warum der Retry zählt: Beide Ersatzindizes scheiterten an 40001 konnte den Zugang nicht serialisieren, der Läufer loggte "gesprungen" und weiter, und die DROP der alten Beschränkung dann gelungen - Konto_Abonnements verlassen ohne Einzigartigkeit, was streng schlimmer ist als die Kollision, die dies repariert. DDL-Ankündigungen versuchen Sie jetzt auf Serialisierungsfehler, und der Tropfen wird übersprungen ausschließlich, sofern beide Ersatzstoffe nicht bestätigt sind.

  4. · kamo-shared-library

    Lassen Sie eine Abrechnungsgruppe ihr eigener Zahler sein

    Eine Gruppe könnte genannt werden und nichts anderes. Sein Abonnement wurde nie festgelegt nichts in der Plattform namens setSubscription - so subscription_uid blieb für immer und die Gruppe konnte keinen einzigen Sitz kaufen oder halten. Drei Dinge mussten sich im Modell ändern: BillingGroupMember ist neu. Sitz Wahrheit bleibt, wo es war, auf Abonnement Mitglied und Kontolizenz, aber Abonnement-Mitglied.Abonnement ist NICHT NULL, also bis a Gruppe kauft einen Plan gibt es nirgendwo, um die Menschen, die es existiert, um zu decken aufnehmen und Leute zu sammeln, bevor sie Sitze für sie kaufen, ist der springende Punkt. Das ist Absicht, das ist Anspruch. AccountAbonnement gewinnt BillingGroupUid, und die (Konto, Markt, Ziel Org) einzigartige Beschränkung bewegt sich aus dem Enter zu kommen. Eine Gruppe stellt ihren Manager, auf die Eigenkonto des Managers, die die gleiche Org im gleichen Markt wie jeder Sitzplatz dass der Manager bereits für sich selbst gekauft - die gleiche tuple. Also die erste Gruppe Kauf nicht gescheitert, fand es das persönliche Abonnement des Managers und umgeschrieben es: Plan, Sitze, Stripe id. Keine Ausnahme, keine zweite Reihe. Die Regel lebt jetzt als zwei teilweise einzigartige Indizes, die JPA nicht ausdrücken kann, erstellt von KamoInitializer; die Entity-Deklaration zu gehen oder ddl-auto würde die alte Beschränkung neu erstellen vor dem Läufer, der ihn fallen lässt. ************ wird gelöscht statt veraltet. Es kehrt optional auf die Stärke einer Einschränkung, die nicht mehr existiert, so dass, sobald ein Konto hält sowohl ein persönliches und ein Gruppen-Abonnement es passt zwei Reihen und wirft an allen fünf Call-Sites in drei Dienststellen. Es zu lassen, um von Autocomplete gefunden zu werden, war die gefährlichere Option. Außerdem: erlaubtGroups() enthält jetzt REQUIRES_APPROVAL. Ohne es machte die Genehmigungsweg unerreichbar - die Schaffung einer Gruppe wurde direkt abgelehnt, während die Code Einstellung der genehmigten Flagge gefragt erfordert Approval() und so konnte immer nur Antwort "bereits genehmigt". Ein Besitzer, der sich für das Versehen entschied, bekam überhaupt keine Gruppen.

  5. · kamo-internal

    Der HR Kurs Editor, Aufgaben Grid und Mitglied Kursspieler

    /hr/training ersetzt seinen Platzhalter durch die beiden Sub-Tabs, nach denen der Auftrag gebeten wurde, und dem des Mitglieds Assigned Training Grid gewinnt die Action-Spalte, die es mangels eines Spielers zurückhielt. KURSE. Ein Katalograster mit Veröffentlichung, Zuweisung, Duplikat, Archivierung und Löschung von Inline. Löschen ist deaktiviert, sobald jemand eine Zuordnung hält und der Tooltip zeigt stattdessen auf Archive Kurs jemanden wurde Waisen ihre Beweise zugewiesen, da die Zuordnung eine Version pins Die Eltern wären weg. "Nichts hat sich seit der Version N geändert" wird als INFO und nicht als Fehler gezeigt: der Autor drückte Publish auf etwas bereits veröffentlicht, das ist ein Erfolg, den sie missverstanden. EDITOR. Outline · canvas · Inspector als ganzseitig verschachtelte Route und nicht als Dialog - eine Folie braucht eine Leinwand neben einem Umriss und einen Inspektor, Autoring ist Multi-Session-Arbeit, die zu überleben eine Auffrischung, und hrTabMatches wurde geschrieben, so dass eine verschachtelte Route hält die Eltern Registerkarte beleuchtet und MANAGE_TRAINING-gated ohne neue Registrierung. Speichern sind pro-ITEM und debounced; ein Ganz-Kurs PUT mit Ersatz-Set-Semantik verwandelt zwei Admins Bearbeitung zwei verschiedene Einheiten in eine still verlieren ihre Arbeit. Nach jeder Spülung wird der Kurs erneut gelesen, weil saveItemContent Kinderreihen neu erstellt und eine abgestandene lokale Kopie würde Phantom-IDs auf die nächste speichern senden. Nachbestellung ist Pfeiltasten angetrieben von moveInOrder, die CLAMPS statt Verpackung . Verpackung leise reordordnet einen Kurs der Autor hat nicht bedeuten, neu zu ordnen, und die Reihenfolge IST die Reihenfolge. MEDIA kommt von /hr/resources durch ein neues LibraryPickerDialog, verallgemeinert aus MaterialPickerDialog durch Heben seiner zwei festcodierten Konstanten zu Requisiten. assocType ist der ORDINAL (13), nie das enum id (15): das enum überspringt id 6, und Senden einer id landet auf einem anderen Regeln der Sammlung. Alt-Text ist auf jedem Medien-Dia erforderlich und Blöcke veröffentlichen . das Autorenwerkzeug ist der einzige Durchsetzungspunkt, der tatsächlich hält, weil eine Überprüfung Checkliste einmal ausgeführt wird und alt Text wird nie hinzugefügt. PLAYER. Wiederaufnahme bei der ersten GAP, nie der am weitesten erreichte Punkt, und jedes Tor Gewerkschaften die Server-Rekord mit THIS-Sitzung - Lesen nur die Server-Reihe macht Next unerreichbar den Moment eine Zecke hat nicht rund-tripped, das ist der genaue Fehler der legale Assistent ausgeliefert. Video-Vervollständigung wird als WATCHED COVERAGE gemessen, nicht currentTime. /api/imaging/stream ehrt HTTP Reichweite Ende zu Ende, so dass ein Lernender kann bis zur letzten Sekunde in einer Anfrage und jede Position-basierte suchen Gate wird durch einen einzigen Drag besiegt; accumulate() behandelt einen Sprung größer als die Zeckenlücke als eine Suche und öffnet ein neues Intervall, anstatt die Spanne über gesprungen zu machen. videoCoverage.test.ts behauptet "seek zum Ende Renditen unter 5%" als erster Fall. Berichterstattung ist Medienzeit, so dass 2x Wiedergabe noch zu die Zeitachse abdecken, weshalb es fair und nicht um ein Schlupfloch geht, sondern fair ist. Eine sichtbare Fluchtluke erscheint, sobald die volle Länge des Videos in der Wand-Uhr-Zeit verstrichen ist und zeichnet überspringenReason auf der Fortschrittsreihe, weil ein Tor ohne Entzug zu einem Support-Ticket wird, dessen Antwort immer lautet "wir werden markieren Sie es komplett für Sie" - das gleiche Ergebnis, nicht verfolgt. Der Testläufer hat keinerlei Korrektheitsdaten: Die Nutzlast wird von PlayerQuestionDto aufgebaut, die kein solches Feld hat, und der Server entscheidet. Antworten autosave so ein geschlossener Laptop ist nicht verloren Versuch, und Leerzeichen sind erlaubt, müssen aber bestätigt werden. npm Test: 3 Wachen sauber, 102 Dateien, 1257 Tests. next build compiles.

  6. · DaemonService

    Die stündnlich trainieren Erinnerung tick

    NUR um :10, im Gegensatz zu der legalen Sweep ist täglich 13:00. Training erinnert jeden org an seinem OWN lokalen Senden Stunde, und Organization.timezone überspannt 21 Stunden (Pacific/Honolu nach Australien/Sydney) - eine Single tägliche UTC-Tick bedeutet "heute" ist falsch für die meisten der Plattform, das ist genau, warum die rechtliche Sweep erzählt ein Sydney Mieter "due today" am falschen Tag. Jede Zecke besucht nur die Orgs, deren lokale Stunde Matches, in der Regel ein bis drei, so dass dies scannt LESS als eine tägliche Cross-Ordnung Job statt 24x mehr. Der Job lebt hier und nicht als @Scheduled in DocsService, denn dieser Einsatz ist Repliken: 1 mit Strategie: Rekstellen, so dass die alte Hülse weg ist, bevor die neue beginnt und ein Einsatzfenster nicht Doppel-Feuer. DocsService ist RollingUpdate mit maxSurge: 1, also für die Länge jedes Rollouts dort sind zwei Hülsen und beide glauben, es ist 09:00 - und für einen Job, dessen Ausgabe ist Mail zu jedem überfällig Mitarbeiter auf der Plattform, "in der Regel einmal" ist keine Eigenschaft. Explizite endliche Timeouts, weil eine Standard-Rest-Template wartet ewig und, kombiniert mit @DisallowConcurrentExecution, ein verkeilt Downstream stoppt den Job läuft wieder, bis die Hülse Neustarts - leise, und ununterscheidbar von "nichts war fällig". Nie wieder wirbelt bei Quartz: ein Flucht Ausnahme kann sofort wieder Feuer, Drehen einer übersprungenen Stunde in einen Wiedereintritt Sturm.

  7. · DocsService

    Das komplette HR Training Backend

    Kurs-Authoring, Veröffentlichung, Zuweisung, der Mitgliedsspieler, serverseitige Abstufung, WORM-Beweis und der Erinnerungs-Sweep - das ganze Modul hinter /api/docs/training. AUTHORING. Kurse führen eine geordnete Liste von Gegenständen, bei denen ein TEST ein PEER eines UNIT ist, nie ein Kind: dass eine Entscheidung ein einzelnes Modell sowohl "einen Test am Ende jeder Trainingsshow" als auch "a Abschlussprüfung" ohne eine zweite Struktur. Inhaltsersparnisse werden ersetzt, weil der Editor die ganze geordnete Liste; eine pro-Rrow-Patch-API benötigt ein Löschver plus eine Neuauflage, die drei Runden ist Reisen und drei Chancen für den Umriss, den der Autor sieht, von der gespeicherten abzuweichen. PUBLISHING friert eine unveränderliche Version ein, die Pin zuweist. Jede Regel, an die ein Mitglied gehalten wird, ist auf sie in THE SAME STATEMENT kopiert, die die Zeile schreibt - das Rechtsmodul erklärt diese Spalten und überließ sie einem späteren Anrufer, der nie kam, weshalb jede legale Version in der Produktion trägt eine Null-Bescheinigung. TRFP1 ist Länge--Pepetiert und nicht begrenzer-zusammen, so dass ein handwerklicher Wert kann eine Feldgrenze nicht imitieren, und isCorrect ist INSIDE der hash: das legale Modul ausgeliefert LPFP1 ohne das entsprechende Feld, veröffentlichen beantwortet "nichts hat sich geändert", und eine korrigierte Layout könnte Nie eingefroren werden. Veröffentlichen Sie weigert sich mit einem 409 Benennung der beanstandeten Element - einschließlich einer Medien-Rutsche mit kein Alt-Text, weil das Autorenwerkzeug der einzige Durchsetzungspunkt ist, der tatsächlich gilt. GRADING IST SERVER-SIDE, und dies ist eine Sicherheitseigenschaft und keine Funktion. Der Spieler wird bedient ****************, die verschiedene Klassen von den DTOs des Autors sind und haben einfach keine Korrektheit Feld zu bevölkern - eine gemeinsame Klasse mit einem Nullfeld kommt zurück die das erste Mal, dass jemand einen Mapper wiederverwendet, und das Leck ist still, während die Compliance-Rekord hält suchen gültig. Shuffle und die Frage ziehen werden hier berechnet und auf dem Versuch aufgezeichnet, so dass eine wieder aufgenommen Sitzung sieht die gleichen Fragen; eine Client-Seite shuffle über eine Client-gehaltene Antwort-Taste ist Theater. Richtige Antworten kommen erst zurück, wenn Show-Antworten es für diesen Versuch erlauben, so dass ein Mitglied nicht scheitern kann absichtlich, um den Schlüssel zu ernten. Teilkredit ist bei Null gedeakt (ohne sie, Auswahl alles punktet negativ und zieht nach unten der Rest des Tests). Prozent ist halb-up genau einmal abgerundet, so dass die Ergebnisse Bildschirm, die E-Mail, die Zertifikat und CSV können etwa 79,5% gegen einen 80-Prozent-St. Ein Kurs ohne Prüfungen nach Abschluss - möglich == 0 darf sich niemals teilen und darf niemals "gescheitert" bedeuten. EVIDENCE. TRMF1 (was sie sahen) und TRQZ1 (was sie beantwortet haben) werden als Digest UND wörtlich gespeichert kanonische Saiten, weil ein Digest aus gespeicherten Beweisen wiederersuchen kann, ist kein Beweis. Die Beglaubigung ist für jedes Endergebnis geschrieben, Pass oder scheitern: "Sie nahmen es und nicht bestanden" ist genau so eine Compliance-Faktum wie das Vorbeigehen. REMINDERS fegen ORG-MAJOR und feuern jede org an seiner OWN lokalen Stunde. Organisation.timezone umfasst 21 Stunden, so dass die legale Sweep' ZoneId.systemDefault() sagt ein Sydney Mieter "due today" am falschen Tag. Der Eimer bleibt eine reine Funktion von (dueDate, heute), das ist, was macht Extend Due Date kostenlos und die Sweektion wieder ausgespielt; Nachholbedarf sendet nur die jüngste nicht gesendete Sprosse nach einem Ausfall und zeichnet die Ältere als verdrängt. Idempotenz ist eine echte Tabelle mit einem einzigartigen Anspruchsindex statt eines Redis SET NX, so "haben wir Ada am 4. März gesagt" überlebt einen FLUSHDB und ist abfragenbar - die Tabelle legal eigenen javadoc sagt, ist richtig und aufgeschoben. Ein Trockenlauf behauptet NICHTS; behaupten, würde jeden Eimer verbrennen inspiziert und unterdrücke den realen Lauf dahinter. DocsInternalAuthFilter gewinnt den Trainings-Innenweg. Ohne sie ist der Cross-Org-Sweep weltweit erreichbar, denn ResourceServerConfig ist anyRequest().permitAll() und OTKPreAuthFilter führt die Kette fort, wenn kein OTK vorhanden ist. 455 Tests bestehen.

  8. · kamo-shared-library

    Die vier Schulungs-E-Mail-Vorlagen

    Dieselbe Konstante/dott-Schlüssel-Split wie die HR-Rechtsfamilie: der CONSTANT ist der Dateiname-Stamm, templateKey die Plattform SENDS ist der gepunktete Wert in jedem .json. Das Falsche zu machen ist das Leben QUOTE_SENT/commerce.quote_sent Bug - jeder sendet 404s für immer gegen eine Vorlage, die gesät so HrTrainingTemplateTest pins beide Hälften in beide Richtungen. Eine Erinnerungsvorlage deckt jede Sprosse der Leiter ab (T-14/7/3/1, fällig heute, und jede vergangene-fällige Zeitraum). Ton wird von einer vorgerenderten Augenbraue und duePhrase statt von separaten Dateien getragen, weil ein halbaddiertes kanonisches Paar wirft innerhalb loadAll() - das ist, was SamenForOrg iterates und würde Aussaat für JEDE Org auf der Plattform brechen, anstatt nur seine eigene. Jede Vorlage ist eine 600px inline-styled Karte mit einem kugelsicheren VML + Anker CTA, weil Outlook Word Engine ignoriert den Anker völlig. Der Test behauptet ctaUrl erscheint genau dreimal (VML href, anchor href, Copy-Paste Fallback), dass keine Vorlage {{#if" verwendet (der Renderer ist eine flache regex ersetzen und als sichtbaren Text darstellen würden), dass keine Vorlage eine externe Ressource zieht, und dass Jeder trägt die drei Variablen EmailTemplateService injiziert, so dass White-Label-Branding funktioniert. Probanden verwenden natürlichTitlePlain und Körper verwenden natürlichTitel: die beiden Render aus einer variablen Karte, so ein entgangener Wert in einem Betreff zeigt "Mitarbeiter's Handbook" im Mail-Client-Header während einer unentflogen in einem Körper Schiffe Live-Markup von der org eigenen DKIM-signed Domain. Bei einem FINAL-Fehler wird keine Rückeroberung-E-Mails gesendet. Jemandem per E-Mail sagen, dass er dauerhaft hat einen Pflichtkurs, der keinen Weg nach vorn und ohne Menschenanliegen, verfehlt hat, ist das falsche Medium - der HR Grid trägt es stattdessen. 1186 Tests bestehen.

  9. · kamo-internal

    Ein Mitgliedsbelag für beide Verpflichtungen, ein Abzeichen für beide

    Die Einhaltung der Vorschriften des Mitglieds Register hatte zwei Sub-Tabs, Pending und Completed, für eine Art von Verpflichtung. Diese sind ein STATE, keine Art - so dass sie in einen Status-Filter zusammenbrechen (mit "alle", die zwei Registerkarten hatte nirgendwo zu setzen, und das bedeutete zuvor Überprüfung sowohl zu beantworten "war es diese I signiert?"), und der Tab-Streifen ist frei, um die beiden Dinge zu trennen, die wirklich unterschiedlich sind: Legal Packages - Instrumente zum Unterzeichnen, unverändertes Verhalten hinter einem Filter Zugeordnete Schulungen - Kurse zu nehmen Jedes Panel behält seinen eigenen Filter in seinem eigenen Param. Ein gemeinsamer ?filter= würde einen legalen Eimer tragen in das Schulungsfeld, wo das gleiche Wort eine andere Reihe von Statusen bedeutet, und Tabs wechseln würde die andere Liste stillschweigend neu gefiltert. Ein fremdes ?sub= wird auf dem Bergwerk normalisiert, weil EinstellungenView.writeTabToUrl setzt ?tab= und löscht ?configure, aber Blätter ?sub= allein . bereits korrekt über den Index Fallback, aber die URL behauptete, ein Zustand dieser Komponente nie hatte, und die URL ist das, was gemerkt und in ein Support-Ticket einfügt. NavTop zeigt jetzt EINE Abzeichen für BOTH. Ein Mitglied denkt nicht an "Pakete zu unterschreiben" und "Kurse zu nehmen" als zwei Systeme, denken sie an Dinge, die sie schulden; zwei fast identische Markierungen nebeneinander in Fix Chrom würde sie arbeiten, welche man beleuchtet ist. Die Zählung ist die Summe, und die Ikone gewinnt eine echter Aufmerksamkeitszustand - zwei Ringe auf ::before/::after eines Wrappers, so dass sich die Glyphe selbst nie bewegt und kämpft nie gegen den Badge Anker. Bewegung ist ein ADDITIONAL-Signal, nicht das einzige (die Zählung und die Fehlerfarbe trägt sie immer noch), weshalb bevorzugt-reduzierte Bewegung sie mit nichts verloren fallen lässt. MemberAssignedTraining versendet bewusst NO-Action-Kolumne. trainingActionFor bereits entscheidet start/resume/retake/certificate und wird einheitsgeprüft, aber der Kursspieler ist nicht gebaut - und Sichtlich deaktiviert "Start" ist ein Versprechen, das das Produkt nicht halten kann, das sich eher als gebrochen liest unvollendet. Die Spalte kommt mit dem Player an. trainingStatusesFor sendet jeden Status EXPLICITLY für "alle", anstatt den Param wegzuwerfen: Der No-Filter-Fallback des Servers existiert, um ein abgestandenes Lesezeichen zu retten, nicht um eine Anrufkonvention zu sein. Statusmeldungen werden als wiederholte Params, niemals CSV - ein Komma wird vom APIService-Gateway doppelt codiert. check-i18n-Tasten haben die fehlenden Schlüssel genau so eingefangen, wie geplant; hr.training.member.* wurde dahingeleitet kamo-translation-dictionary zuerst und verifiziert auf seiner Herkunft/Hauptseite vor dieser Übergabe. npm Test: 3 Wachen sauber, 98 Dateien, 1221 Tests bestanden.

  10. · DocsService

    Die Member-Facing Training API

    Registriert com.kamo.z.shared.hr.training in @EnableJpaRepositories . @EntityScan ist breit gerückt, so dass die Entitäten werden so oder so gefunden, aber Repositorien sind es nicht. Zwei Berechtigungsentzugsentrichter, absichtlich getrennt, Spiegelung des rechtlichen Moduls: TrainingAccess-Gatter /api/docs/training/** auf MANAGE_TRAINING (RoleRightType 185) lesen UND schreiben, die genau die richtige app/hr/hrTabs.ts verbietet die Registerkarte. Ein Server, der schmaler eingestellt ist als tab's lässt ein Mitglied in der Lage, die Registerkarte zu öffnen und sammeln Sie eine 403 über ein leeres Gitter; breiter, und es ist ein endpoint erreichbar durch jemanden, dem die UI es nie angeboten hat. Rechte werden gelesen member_rights_applied, nie aus dem Token, weil die OTK-Sitzungskarte keine Rechte hat. TrainingAssignmentOwnership autorisiert die eigene Oberfläche des Mitglieds durch IDENTITY. TrainingMemberController hat daher überhaupt kein Schulungszugriffsfeld - ein Mitglied, das einen ihm zugewiesenen Kurs hat, keinen HR richtig, und sie durch den HR-Trichter zu leiten würde bedeuten, entweder jeden Mitarbeiter auszusperren ihre eigene Ausbildung oder Erweiterung der HR-Rechts-Set, bis der Katalog und Publikum Dienstplan offen für die ganze Org. Eigentumsantworten 404, nie 403: ein 403 bestätigt, dass die Uid echt ist und dreht den Endpunkt in ein Orakel für die, die was zugewiesen wurde, und eine Bewertung Aufzeichnung darf nicht von einem Peer aufzählen. TrainingMemberSurfaceAuthTest pinnt alle drei Eigenschaften durch Reflexion, weil der Ausfallmodus ist Jemand, der das Feld später hinzufügt, "so ist auch der Endpunkt gesichert". GET /zusenmächtigungen und GET /zuweisen/Ausgaben-Zählung. Keine Mapping nimmt ein Mitglied oder org id - beide kommen von der Sitzung. Status ist ein REPEATABLE Param, nie CSV: APIService gibt seine upstream URL an Resttemplate as a String, der %2C bis %252C neu kodiert, so dass ein Komma als ein buchstäbliches Zeichen ankommt passend keine enum Konstante. Ein unerkennzeichneter Filter bedeutet "kein Filter", nie "nichts passen", also Abgestandenes Lesezeichen zeigt alles und nicht ein leeres Raster, das sich als Datenverlust liest. Die gespendete Seite ist ein DocsService-Lokal-Repository über die gemeinsame Entität, sortiert (gereicht an DESC, uid DESC) - übt sich auf ein nicht-unique Key Duplikate und überspringt Zeilen, und eine 200-köpfige Zuordnung schreibt geliefertIm Inneren der gleichen Millisekunde. Bündel-Zähler gibt die gleiche JSON-Form wie der Druckrahmen der Plakette, so dass ein Client-Pser serviert beides. Reihen werden innerhalb der Transaktion abgebildet; Kurs und Version sind LAZY. 413 Tests bestehen.

  11. · InitializerService

    Erstellen Sie das HR Training Schema

    Registriert com.kamo.z.shared.hr.training in der expliziten @EnableJpaRepositories Liste @EntityScan ist breit, so dass die Entitäten gefunden werden, so oder so, aber Repositorien sind nicht, und ohne dies werden die Tabellen nie erstellt. TrainingSchemaMigration (@Bestellung(43); 42 ist die gesetzliche) fügt hinzu, was das Entity Mapping nicht kann express. Fünf UNIQUE-Indizes werden als fatal behandelt, weil jeder eine Korrektheitsgarantie ist nichts anderes bietet: - (course_uid, version_number): zwei gleichzeitige Veröffentlichungen würden sonst die gleiche Zahl prägen und jede Aufgabe, die auf "Version 3" geheftet wird, wird mehrdeutig. - eine Bescheinigung pro Einsatz: die WORM-Wache blockiert UPDATE und DELETE, kein Duplikat INSERT. - die Benachrichtigung Anspruch Index IST die idempotenz der gesamten Erinnerungspipeline - die Forderung ist ein INSERT dagegen, so dass ohne es ein neu verhandelter Sweep jeden überfälligen Mitarbeiter von jeder Mieter. - eine Rezertifizierung pro (Mitglied, Kurs, Fälligkeit); eine Fortschrittszeile pro Folie. Der Kurs-item CHECK ist absichtlich als zweispaltiges Prädikat geschrieben: SchemaCleanupRunner @Order(1) lässt jede einzelne Kolumne IN-Liste CHECK auf jedem Lauf fallen, denn das ist die Form Hibernate emittiert für @Enumerated(STRING). CockroachDB/Yugabyte haben keine ADD CONSTRAINT IF NICHT EXISTEN für CHECKs, so "bereit existiert" wird gefangen und als Erfolg behandelt. Der Test ist nur Reflexion-only. mvn Test in diesem repo MIGRATES PRODUCTION . DemoApplicationTests ist ein @SpringBootTest ohne Testprofil und ohne src/test/resources - so behauptet es das SQL Konsistenten gegen den @Table-Namen jedes Entity-Unternehmens ohne Kontext zu starten, der der Ausfall ist Das passiert tatsächlich: eine Tabelle, die das handgeschriebene SQL hinterlässt.

  12. · kamo-shared-library

    Streamen Sie Dokument-Uploads und fügen Sie das HR-Training-Schema hinzu

    ImageService gewinnt UploadDocumentStreaming, das Streaming-Pendant von uploadDokument. Das gepufferte Formular kann überhaupt keinen großen Upload ausdrücken: readInputStream allocates new byte[size], ein Java-Array top outet bei Integer.MAX_VALUE und MinIOStorageService.upload legt dann eine ZWEITE Vollkopie für Cross-Node-Retry zu. Gegen einen 2Gi-Container, der ein ein paar hundert Megabyte die praktische Decke - weshalb die bildgebende mehrteilige Kappe saß bei 500MB während der Chat-Anhängepfad, der bereits streamt, auf 3 GiB lief. Die neue Methode hat einmal (beide verdaut einen Puffer), schnüffelt den MIME-Typ VOR der hash pass so eine falsch etikettierte 3 GiB-Datei abgelehnt wird, ohne eine vollständige Lektüre auf sie, und Delegierte Lagerung zu einem neuen StoreContentAddressed Überlastung, die vorbestellte Verdauungen akzeptiert so wird die Quelle zweimal statt dreimal gelesen. Bewusst @Transaktional: lagern Gigabytes dauert Minuten und eine Transaktion über sie gehalten Pins eine gepoolte Verbindung. Auch com.kamo.z.shared.hr.training hinzugefügt 20 Einheiten, 11 enums und 20 Repositories für die HR-Trainingsmodul, Spiegelbild com.kamo.z.shared.hr.legal's Conventions: BaseUuidEntity, an explizit last_modified with touch() because BaseUuidEntity.dateUpdated ist tot, angepinnt columnDefinition auf jeder optionalen @JoinColumn und @OnDelete(CASCADE) auf dem Publikum FKs weil CascadeType.* bei DDL nichts aussendet. Zwei Abweichungen von der rechtlichen Blaupause, beide bewusst: - TrainingAssignmentStatus teilt FAILED in FAILED_RETAKE_ALLOWED (offen) und FAILED_FINAL (geschlossen), und REMINDABLE_STATES ist streng schmaler als OPEN_STATES, so dass ein Mitglied wartet Die Einstufung wird nie gejagt. Pinned by TrainingAssignmentStatusTest. - hr_training_notification ist eine echte Tabelle mit einem UNIQUE-Anspruchsindex statt eines Redis SET NX, also "haben wir es ihnen an diesem Datum gesagt" überlebt einen FLUSHDB und ist abrufbar. Antwort und Bescheinigung Repositorys erweitern die nackte Repository Marker, nicht JpaRepository, so die Batch-Löschmethoden, die @PreRemove umgehen, existieren nicht auf der Schnittstelle. Erfordert einen KamoInitializerService, bevor ein Dienst, der diese Tabellen liest, eingesetzt wird.

  13. · InitializerService

    Erstellen Sie billing_pay_groups explizit und rückgängig machen Sie die Alt-Kollision

    Erstellen Sie es in einer Migration, anstatt es zu überlassen, ddl-auto, die eine halb gebaute Tabelle, wenn das Unternehmen auf die bereits bestehenden billing_groups hingewiesen wurde.

  14. · EmailService

    Größe jeder Briefkasten gegen die Speicherung der Organisation

    Dovecot erzwingt jetzt eine Per-Mailbox-Limit, und nichts war die Spalte es liest. Dies schreibt es nachts, nach dem Speicher-Schnappschuss, so dass die Kapazität ist Die geteilte ist die Zahl von heute und nicht die von gestern. Jeder Briefkasten erhält einen gleichen Anteil am gesamten Pool. Die Eigenschaft, die zählt, ist dass die Aktien Summe auf die Kapazität, so dass die Post kann nie auf eigene nehmen eine Organisation über seine Decke hinaus. Es ist auch die Version, die ein Besitzer vorhersagen kann "Ihr Anteil an der Lagerung der Organisation" - und es bewegt sich nicht, wenn ein Unzusammenhängende Domäne wächst, was jedem Briefkasten die verbleibende Kopffreiheit geben würde. Unbegrenzte Pläne und Organisationen ohne Live-Abonnement schreiben 0, die die user_query wird zu einem NULL quota_rule: no limit. Diese Richtung ist gewollt, weil die Alternative ist die Verweigerung der Post für eine Organisation, deren Abrechnung Reihen fehlen oder mitten in der Migration.

  15. · KlusterServices

    Ersetzen Sie Speicherquote bei der Ein-Lieferung und stoppen Sie den Versand des DB-Passworts

    Objektspeicherung verweigert bereits Inhalte, die eine Organisation vorbei an seinem nehmen würde Decke, die jede Domäne, die durch den Objektspeicher geht abdeckt. Einflugpost war die Lücke: LMTP schreibt direkt in den maildir ohne Java im Pfad, so Dovecot Quoten-Plugin ist das einzige, was es stoppen kann. Das Plugin lädt global, so dass LMTP-Lieferung überprüft wird - das ist der Weg, der Angelegenheiten mit imap_quota für Clients, die fragen. Es gibt absichtlich keine Standardquote_rule: Für jede Mailbox, die der Userdb macht, würde eine globale Regel gelten nicht für die Antwort, das ist das Gegenteil von scheitern sicher. Die pro-User-Regel von user_query ist die einzige Quelle eines Limits, und es ist NULL, bis quota_bytes ist gesetzt, so dass jede der 18 vorhandenen Mailboxen genauso unbegrenzt ist wie zuvor. virtual_users.quota_bytes wurde vor diesem hinzugefügt, standardmäßig auf 0, weil die neue user_query Referenzen es: Versand der Konfiguration zuerst hätte jede ausgefallen userdb lookup und nimmt Mail auth nach unten. Die generierte dovecot.conf wurde mit Taubenconf gegen den Live 2.3.20 bestätigt. vor dem Drücken; es beschließt "quota sieve" für LMTP und "quota imap_quota" für IMAP. Unabhängig davon trägt die ConfigMap nicht mehr das MySQL-Passwort. Es versendet in plaintext, hier engagiert und lesbar von .kubectl get cm -o yaml'. Die ConfigMap hat jetzt einen Platzhalter und der Anmeldeschein kommt von der bestehenden mysql-secret durch die Umgebung; der Container weigert sich zu starten, wenn es leer statt mit einem unbliebbaren Userdb zu laufen. Beachten Sie, dass dies das einfache Paar bearbeitet, was CI anwendet und was tatsächlich ist laufen. dovecot-configmap.yaml und dovecot-deployment.yaml werden angewendet von nichts und beschreiben ein ganz anderes Bild.

  16. · KlusterServices

    Ersetzen Sie Speicherquote bei der Ein-Lieferung und stoppen Sie den Versand des DB-Passworts

    Objektspeicherung verweigert bereits Inhalte, die eine Organisation vorbei an seinem nehmen würde Decke, die jede Domäne, die durch den Objektspeicher geht abdeckt. Einflugpost war die Lücke: LMTP schreibt direkt in den maildir ohne Java im Pfad, so Dovecot Quoten-Plugin ist das einzige, was es stoppen kann. Das Plugin lädt global, so dass LMTP-Lieferung überprüft wird - das ist der Weg, der Angelegenheiten mit imap_quota für Clients, die fragen. Es gibt absichtlich keine Standardquote_rule: Für jede Mailbox, die der Userdb macht, würde eine globale Regel gelten nicht für die Antwort, das ist das Gegenteil von scheitern sicher. Die pro-User-Regel von user_query ist die einzige Quelle eines Limits, und es ist NULL, bis quota_bytes ist gesetzt, so dass jede der 18 vorhandenen Mailboxen genauso unbegrenzt ist wie zuvor. virtual_users.quota_bytes wurde vor diesem hinzugefügt, standardmäßig auf 0, weil die neue user_query Referenzen es: Versand der Konfiguration zuerst hätte jede ausgefallen userdb lookup und nimmt Mail auth nach unten. Die generierte dovecot.conf wurde mit Taubenconf gegen den Live 2.3.20 bestätigt. vor dem Drücken; es beschließt "quota sieve" für LMTP und "quota imap_quota" für IMAP. Unabhängig davon trägt die ConfigMap nicht mehr das MySQL-Passwort. Es versendet in plaintext, hier engagiert und lesbar von .kubectl get cm -o yaml'. Die ConfigMap hat jetzt einen Platzhalter und der Anmeldeschein kommt von der bestehenden mysql-secret durch die Umgebung; der Container weigert sich zu starten, wenn es leer statt mit einem unbliebbaren Userdb zu laufen. Beachten Sie, dass dies das einfache Paar bearbeitet, was CI anwendet und was tatsächlich ist laufen. dovecot-configmap.yaml und dovecot-deployment.yaml werden angewendet von nichts und beschreiben ein ganz anderes Bild.

  17. · kamo-internal

    Lassen Sie ein Mitglied ihren eigenen Sitzplatz von der Konsole aus lizenzieren

    Das Gremium, das die Schleife auf Selbstbezahlung schließt. Es erscheint nur, wo die Organisationsrichtlinien erlauben es, und nur für Leute, die nicht bereits kaufen im Namen der Organisation - der Server überprüft die gleiche Politik, so versteckt ist es eine Höflichkeit und nicht die Kontrolle. Es sagt, wer in Rechnung gestellt wird, bevor für eine Karte fragen, und unter Genehmigungsmodus sagt Es ist klar, dass nichts berechnet wird, bis der Eigentümer zustimmt. Nicht lesen Die Förderfähigkeit verbirgt sich über die Option, anstatt eine zu zeigen, die abgelehnt würde. Wenn die Politik nicht erlaubt Selbstzahler wählen einen Plan, nehmen sie die Organisation ist an und der Picker wird überhaupt nicht angezeigt - ein Eigentümer delegiert Kosten in der Regel nicht delegieren die Produktentscheidung.

  18. · BillingService

    Sagen Sie den Leuten, dass ihre Zahlung fehlgeschlagen ist, und stellen Sie das Abonnement herunter

    Die Dünung, die nicht existierte. bill.payment_failed schrieb eine Reihe und kehrte zurück: keine E-Mail, keine Flagge, keine Änderung des Staates, und billing.email-Service-url konfiguriert wurde und referenziert durch keinen Java-Code. Der Webhook startet nun die Delinquenzuhr auf den ersten Ausfall und löscht ihn in dem Moment, in dem eine Rechnung bezahlt wird, so dass ein zurückgewonnener Abonnement frisch beginnt, anstatt als eine abgestandene Uhr in den nächsten Ausfall zu tragen. Nur der erste Ausfall beginnt es - ein erneuter Versuch eine Woche später muss nicht neu starten Gnade und Hand eine nicht zahlende Abonnement eine weitere Woche Service bei jedem Versuch. Der Sweep entscheidet absichtlich nicht, wann die Ansprüche aufhören. Das passiert auf die Uhr, in isLive, so gibt es kein Fenster, wo ein abgelaufenes Abonnement noch arbeitet, weil ein Job nicht ausgeführt hat. Was der Sweep besitzt, ist das, was eine Uhr nicht kann: sagen Sie einmal, und Stornierung nach autoCancelAfterDays - dreißig Tage von Standard, lange nachdem der Dienst stoppt. Diese Lücke ist absichtlich wieder zu erreichen, weil Kunde, der seine Karte am Tag zwanzig aktualisiert, bekommt alles zurück, indem er bezahlt, und ein gekündigtes Abonnement ist viel schwerer rückgängig zu machen als ein ausgesetztes Abonnement. Benachrichtigung ist Best-Anstrengung durch Design: Streifen retries ein Webhook, der antwortet non-2xx, und ein unerreichbarer Mail-Dienst ist kein Grund, eine Zahlung wieder aufzuarbeiten Veranstaltung.

  19. · InitializerService

    Fügen Sie die Dünenuhr Spalten

    past_due_ since und suspension_notified_at, beide nullbar. Null bedeutet zahlen und nicht-noch-erzählt.

  20. · kamo-shared-library

    Machen Sie eine fehlgeschlagene Zahlung bedeuten etwas

    PAST_DUE gezählt als voll live in beiden Anspruchsentlösern, ohne Zeitstempel und keine Kappe, so dass ein Kunde, dessen Karte versagt hielt jede Funktion so lange wie Stripe verließ das Abonnement in diesem Zustand - unbegrenzt in der Praxis - und Niemand, Kunde oder Betreiber, wurde informiert. Ein Abonnement trägt jetzt, wenn die Sammlung zum ersten Mal gescheitert, und PAST_DUE ist live nur während innerhalb seiner Gnadenfrist. Berechnen auf lesen, genau wie Probe Ablauf, so Service stoppt auf der Uhr, anstatt zu warten, bis ein Job zu bemerken: geplante Entscheidung würde ein Fenster hinterlassen, in dem ein abgelaufenes Abonnement noch funktionierte weil der Sweep noch nicht gelaufen war. Zwei bewusste Entscheidungen darüber, falsch zu liegen. Grace säumt auf eine Woche statt Null, weil eine fehlgeschlagene Zahlung ist in der Regel eine abgelaufene Karte und nicht eine Entscheidung zu Aufhören zu zahlen. Und eine vergangene Reihe ohne Stempel liest sich wie innere Anmut, weil jedes Abonnement vor der Spalte hat ein - Behandlung von Null wie verfallen würde haben abgeschnitten Kunden in dem Moment, die diese versendet, für eine Strafe niemand hatte gemessen.

  21. · BillingService

    Lassen Sie ein Mitglied einen eigenen Sitzplatz kaufen und ein Body-Parameter-Loch schließen

    Self-Pay hatte keine Route innerhalb der Konsole. Alles auf der Abrechnungsfläche ist Konto-förmig - Sie müssen bereits eine Kontomitglied-Reihe vor Plänen & Billing zeigt Ihnen alles, und der einzige Weg, um eine war ein Eigentümer Aktion Ein Mitglied, das für sich selbst bezahlen wollte, wurde gesagt, zu gehen und zu fragen. Der eine echte Selbstbezahler-Pfad, der existierte, die Verbraucherkasse, die von White-Label-Produkten verwendet wird, ist unerreichbar von der Konsole, weil die Abrechnung Proxy baut Header aus kratzen und nie weiterleiten das Cookie seine Sitzungsentwider braucht. Der Käufer ist, wer auch immer die Sitzung sagt, sie sind. X-Member-Id wird durch den Proxy eingestellt von *** und nie durch den Anrufer, so im Gegensatz zu den Kontorouten gibt es keine ID im Pfad zum Tausch - das ist der ganze Grund, warum dies eher seine eigene Route ist als ein anderer Handler unter /accounts/{accountUid'. Die Politik der Organisation ist Server-Seite überprüft, so versteckt die Option anderswo ist mehr als Dekoration, und die Sitzobergrenze pro Anzahlung wird hier durchgesetzt und nicht vom Kunden vertraut. Schließt auch ein Cross-Tenant-Loch auf POST /accounts. Diese Route hat keine Account-ID in seinem PATH, so AccountOrgScopeInterceptor - welche Schlüssel auf diesem Segment nie sah es, und jedes Mitglied einer Organisation könnte eine andere Organisation nennen Konto im BODY und schreiben Sie seinen Rechnungskontakt um, umleiten seine Rechnungen und Quittungen. Die Benennung eines willkürlichen MitgliedsId tat dasselbe aus der anderen Richtung. Beide müssen nun der Organisation des Anrufers angehören.

  22. · kamo-internal

    Lassen Sie den Eigentümer entscheiden, wer zahlen kann, und verwalten die Gruppen, die tun

    Der Besitzer hatte genau einen Hebel über Selbstzahlung und es war die falsche Form: ein irreversible Förderung Geste, ohne Möglichkeit, im Voraus zu sagen, ob es war erlaubt und keine Möglichkeit, es rückgängig zu machen - während ein separater Verbraucher-Checkout-Pfad lassen Mitglied selbst abonnieren Sie sich selbst. Das ist die präventive Kontrolle, und es ist, was Der Rest der Konsole versteckt sich. Vier Modi, beschriftet durch das, was mit anderen Menschen passiert, anstatt durch das enum: ein Besitzer Kommissionierung eine Politik ist zu entscheiden, was ihre Mitarbeiter sehen können, so dass ist, was jeder Option beschreibt. Gruppen erscheinen erst, wenn der Modus sie zulässt, weil eine Gruppe sind mehrere Leute, die das tun, was eine Person bereits tun kann. Nicht-Besitzer sehen die Einstellung eher lese als gar nicht. "Die Organisation pays zentral" ist die Antwort darauf, warum sie keine Kaufoptionen haben und sich verstecken Sie macht das nur zu einer Unterstützungsfrage. Ein Versagen Laden dieser Abschnitt lässt die Plan Picker arbeiten - eine Politik der Konsole kann nicht lesen sollte die Kauffläche nicht mitnehmen.

  23. · BillingService

    Org Politik und Abrechnung Gruppe APIs

    Wer innerhalb einer Organisation Kamo bezahlen kann, ist nun verantwortlich und sorglos. Beide Routen sind org-scoped und daher absichtlich außerhalb /api/billing/accounts/** und sein Abfangjäger: "Möchde meine Mitarbeiter ihre eigenen Sitze kaufen" muss verantwortbar sein bevor ein zweites Konto existiert, was genau dann eine kontobestätigte Einstellung ist kann nicht helfen. Lesen Sie die Politik ist offen für jedes Mitglied, weil die Oberflächen, die sich verstecken Selbstbezahlungskontrollen brauchen die Antwort und sie offenbart nichts Handlungsfähiges. Alles das ändert, wer für das bezahlt, was nur Eigentümer ist, überprüft mit der Datenbank eher als der Anrufer-überschützte Header. Gruppengründung weigert sich, wenn die Organisation keine Gruppen erlaubt hat - eine Gruppe ist mehrere Menschen tun, was eine Person kann bereits tun, so eine Organisation, die hat die Selbstzahlung auch nicht erlaubt. Unter REQUIRES_APPROVAL eine Gruppe startet nicht genehmigt; jeder andere Modus hat bereits eine pauschale Erlaubnis erteilt, so beginnt es genehmigt. Das Löschen einer Gruppe löst die Namensgebung auf und nie die Abrechnung: das, wofür es bezahlt, zu stornieren, ist eine separate, vorsätzliche Handlung. Der Dienstplan kommt aus den Abonnement-eigenen Abonnement-Mitglieder-Reihen statt wird in der Gruppe dupliziert, so dass eine Gruppe nicht mit den Sitzen nicht widersprechen kann Bezahlen.

  24. · kamo-internal

    Eine Plan- & Abrechnungskonsole, die ein Besitzer tatsächlich finden kann

    Es gab keine Möglichkeit, ein erstes Abonnement irgendwo in dem Produkt zu kaufen. Planlisten waren nur über ein Abonnement erreichbar, das Sie bereits besaßen, so dass eine Organisation mit keiner sah eine leere Platte und einen Hinweis, der auf eine andere org. Der eine echte Abrechnung Bildschirm lebte auf der Mitglieds-Profilseite, auf den Einstellungen eingereicht Landung unter einer Überschrift mit dem Titel "Persönlich", und war ganz für jede Org versteckt ohne Eltern. Die Hausverwaltung Bar hatte überhaupt keinen Rechnungseingang. Die Konsole beantwortet die Frage, mit der ein Besitzer ankommt - was sind wir, was kostet es, wie ändere ich es - und Preise jede Kontrolle, wie sie es bewegen, so die Summe ist sichtbar, bevor etwas gebunden wird. Sitzplätze, Add-ons und Stauraum blockiert jede Aktualisierung einer laufenden Figur, und ein Jahresplan sagt, was eigentlich lässt das Konto einmal im Jahr, anstatt eine monatliche Zahl zu verlassen multipliziert im Kopf des Lesers. Die Arithmetik ist planQuote, gegen den Ladungspfad getestet, weil beide Orte die Plattform zuvor zitiert einen Preis nicht einverstanden mit dem, was Stripe in Rechnung gestellt. Erreichbar von der Home Admin Bar und die Einstellungen Menü, gated auf der Finanzierung Rechte statt CONFIGURE_SYSTEM: Entscheiden, was die Organisation bezahlt ist eine Finanzierungsentscheidung.

  25. · BillingService

    Standardieren Sie das Ziel eines neuen Abonnements oder an den Anrufer

    Ein erster Kauf hat kein bestehendes Abonnement, um targetOrganizationId von kopieren, und erfordert es bedingungslos ist, was die Schaffung einer von der Konsole gemacht unmöglich. Eine Organisation, die Sitze für sich selbst kauft, ist der übliche Fall; Suborg-Regelung geht es immer noch explizit.

  26. · kamo-internal

    Zeige die Decke, nicht nur die Bytes

    Das Panel berichtet gespeichert, hinzugefügt, entfernt und Lebensdauer Bytes gegen keine Referenz überhaupt - so ein Besitzer Blick auf 400 GB hatte keine Möglichkeit zu sagen, ob war bequem oder über ihre nächste Upload zu verweigern, und ihr erstes Wissen eines Problems wäre gescheitert gewesen. Die Planzulage und die Blöcke gekauft sind, was diese Zahlen in eine Position zu verwandeln, so dass die Kapazitätsleiste geht zuerst und alles unten liest sich dagegen. Die Bar bleibt grün, bis es darauf ankommt: Bernstein bei 90%, rot voll. Eine Bar, die Drehung Bernstein bei 60% lehrt die Menschen, es zu ignorieren. Es sagt auch deutlich, dass liest sich nicht betroffen sind. "Storage ist voll" lautet wie "Ich haben den Zugriff auf meine Dateien verloren", es sei denn, Sie sagen etwas anderes, und die eine Sache Füllen Sie Ihre Lagerung nie tut, nehmen Sie etwas weg. Blockgröße und Preis kommen aus dem Katalog, anstatt Hardkodiert, so dass die "Zu 10 GB für $2.50 pro Monat" Linie kann nicht von dem driften, was tatsächlich berechnet wird.

  27. · SecurityService

    Gate Förderung auf der Politik des Eigentümers, machen es idempotent, fügen Sie seine inverse

    Die Förderung eines Mitglieds, um für sich selbst zu bezahlen, war bedingungslos, einseitig und unsicher zu wiederholen. Es weigert sich jetzt, wenn die Politik der Organisation sagt, die Organisation zahlt für Jeder - bisher war die einzige Kontrolle über die Selbstbezahlung die Unumkehrbarkeit der Geste selbst. Es ist idempotent. Förderung des gleichen Mitglieds zweimal eingefügt ein zweites Konto und eine zweite AccountMember-Reihe, die das Unikat auf member_id stolpert und 500 beantwortet; ein Doppelklick, ein erneutes Versuch nach einem Netzwerk-Blip, oder ein Besitzer, der den Fluss wiederholt, um mehr Lizenznehmer alle treffen es. Es nutzt nun das Konto und Abonnement der Mitglied hat und macht weiter zum Transfer. deoteBillingOwner ist das Umgekehrt, das nie existiert hat. Ein Besitzer, der gefördert jemand konnte es nicht rückgängig machen, und wenn dieses Mitglied ging, ihre persönliche Besessene Konto gehalten Sitze Ziel der Organisation ohne Möglichkeit, zurückzufordern sie. Lizenznehmer bewegen sich zurück auf das Abonnement der Organisation und die Selbstzahler werden abgesetzt; ihre Konto- und Zahlungsmethoden werden in Ruhe gelassen, weil die Rückgewinnung von Sitzen, von denen die Organisation abhängt, das Geschäft des Eigentümers ist und Die Abrechnungsgeschichte von jemandem zu löschen, ist es nicht. Passen ist eher auf den Shared User als die Mitglieder-ID, da die Promotion das Mitglied auf der obersten Ebene org spiegelt. Behebt auch die Endpunkt-Ratsche, die ALREADY FAILING auf Hauptvoransatz war Arbeit - so SecurityService Build war rot und nichts wurde eingesetzt. Der Scan konnte nicht zwei echte Wachen sehen: extractUserId liest die Benutzerkennung aus dem *** Sitzung, und requireTopLevelOwner geht durch CallerSessionResolver und weiter verlangt, dass der Anrufer die Top-Level-Rig besitzt. Lehre es lassen Sie beide lassen 25 abgestanden Basis-Einträge werden gelöscht, das ist genau das, was die Datei verlangt endpoint bedeutet, seine Linie zu entfernen, und das ist es, was die Zahl sinken lässt. runSweep ist mit seinem wahren Grund basiert: intern-auth durch eine Header Parameter, den der Body-Scan nicht erreichen kann.

  28. · InitializerService

    Lassen Sie die Einschränkung, die ein Mitglied auf ein Abrechnungskonto beschränkt

    Findet die Einschränkung durch Form statt nach Namen. Hibernate erzeugte sie und der Name ist nicht stabil über Umgebungen, so dass jede einzigartige Einschränkung auf account_members, deren Spalte genau member_id ist, ist diejenige, die fallen soll. Die Composite on (account_uid, member_id) hat zwei Spalten und passt nie zusammen.

  29. · kamo-shared-library

    Lassen Sie sich von Mitgliedern und Gruppen bezahlen, nach den Regeln des Eigentümers

    Drei Dinge fehlten und eines war aktiv schädlich. account_members trug ein globales Unikat auf member_id - ein Mitglied könnte verwalten genau EIN Abrechnungskonto Plattform-weit. Das ist die Obergrenze, die jeden gemacht hat Multi-Payer-Vereinbarung unmöglich, und es war bereits Schaden anrichten, anstatt rein einschränkend: accountMemberService.add() arbeitete sich um ihn herum, indem er schweigend DELETING der Mitgliedszeile auf ihrem vorherigen Konto, so dass die Gewährung von Rechnungszugang zu einem widerrufen es von einem anderen ohne Warnung; und fördernToBillingOwner warf eine Einschränkungsverletzung im zweiten Durchgang, der einen Doppelklick in einen 500 verwandelt. Die Composite unique on (account_uid, member_id) existierte bereits und drückt die aus echte Regel. Dropping es macht eine abgeleitete findByMemberId Rückkehr Optional werfen den Moment Jeder hält zwei Reihen, und neun Anrufer verlassen sich darauf. Es ist jetzt eine begrenzte Abfrage durch Schaffung angeordnet, so dass diese Anrufer halten die Antwort, die sie anstelle von je nachdem, was der Planer zuerst zurückgibt; findeAllByMemberId ist für Code, der alle von ihnen will. OrgBillingPolicy ist die präventive Kontrolle, die der Besitzer nie hatte. Der 2026-04-16 Design spezifiziert billing_delegation_mode und Implementierung ließ es fallen, verlassen eine irreversible fördern Geste und keine Möglichkeit, im Voraus zu sagen, ob Selbstzahlung war überhaupt erlaubt - während ein separater Verbraucher-Checkout-Pfad vermiet ist Mitglieder abonnieren sich trotzdem selbst. Wo ein Modus etwas verbietet, verstecken sich die Oberflächen es anstatt es anzubieten und zu scheitern. BillingGroup benennt eine Vereinbarung, die das bereits erlaubte Modell: AccountSubscription ist einzigartig auf (Konto, Markt, Ziel org), so dass mehrere Konten immer auf eine Organisation abzielen. Es trägt keine Dienstplan seiner eigenen - wer hält einen Sitz ist Abonnement-Mitglied plus Kontolizenz, die gleiche Antwort wie für alle anderen, und ein Zweiter Dienstplan wäre eine zweite Wahrheit im Gleichschritt zu halten.

  30. · SecurityService

    Entlare die ausfällt-offene Wirkung dem Betreiber

    Die App-Katalog-Seite beantwortet bereits die org-unabhängige Frage planen erwähnen, diese App - und wirft eine Warnung, wenn keiner tut. Das beantwortet die Mieter-für-Tenant eins dahinter, das ist, was tatsächlich Tore Flip das Ventil.

Euer Universum wartet.

Fügen Sie Produkte hinzu, wann immer Sie möchten – keine Migration, keine neuen Anmeldungen.

Für immer kostenlos – keine Kreditkarte erforderlich